Merge pull request 'Top-level Post Replies' (#120) from freamon/pyfedi:23c into main

Reviewed-on: https://codeberg.org/rimu/pyfedi/pulls/120
This commit is contained in:
rimu 2024-03-25 08:19:52 +00:00
commit 6762d9a87b
7 changed files with 61 additions and 17 deletions

View file

@ -569,7 +569,7 @@ def actor_json_to_model(activity_json, address, server):
ap_followers_url=activity_json['followers'],
ap_inbox_url=activity_json['endpoints']['sharedInbox'],
ap_outbox_url=activity_json['outbox'],
ap_featured_url=activity_json['featured'],
ap_featured_url=activity_json['featured'] if 'featured' in activity_json else '',
ap_moderators_url=mods_url,
ap_fetched_at=utcnow(),
ap_domain=server,

View file

@ -627,7 +627,7 @@ def admin_users_add():
private_key, public_key = RsaKeys.generate_keypair()
user.private_key = private_key
user.public_key = public_key
user.ap_profile_id = f"https://{current_app.config['SERVER_NAME']}/u/{user.user_name}"
user.ap_profile_id = f"https://{current_app.config['SERVER_NAME']}/u/{user.user_name}".lower()
user.ap_public_url = f"https://{current_app.config['SERVER_NAME']}/u/{user.user_name}"
user.ap_inbox_url = f"https://{current_app.config['SERVER_NAME']}/u/{user.user_name}/inbox"
user.roles.append(Role.query.get(form.role.data))

View file

@ -49,7 +49,8 @@ def add_local():
rules=form.rules.data, nsfw=form.nsfw.data, private_key=private_key,
public_key=public_key, description_html=markdown_to_html(form.description.data),
rules_html=markdown_to_html(form.rules.data), local_only=form.local_only.data,
ap_profile_id='https://' + current_app.config['SERVER_NAME'] + '/c/' + form.url.data,
ap_profile_id='https://' + current_app.config['SERVER_NAME'] + '/c/' + form.url.data.lower(),
ap_public_url='https://' + current_app.config['SERVER_NAME'] + '/c/' + form.url.data,
ap_followers_url='https://' + current_app.config['SERVER_NAME'] + '/c/' + form.url.data + '/followers',
ap_domain=current_app.config['SERVER_NAME'],
subscriptions_count=1, instance_id=1, low_quality='memes' in form.url.data)
@ -102,9 +103,9 @@ def add_remote():
flash(_('Community not found.'), 'warning')
else:
flash(_('Community not found. If you are searching for a nsfw community it is blocked by this instance.'), 'warning')
if new_community.banned:
flash(_('That community is banned from %(site)s.', site=g.site.name), 'warning')
else:
if new_community.banned:
flash(_('That community is banned from %(site)s.', site=g.site.name), 'warning')
return render_template('community/add_remote.html',
title=_('Add remote community'), form=form, new_community=new_community,
@ -962,4 +963,4 @@ def community_moderate_banned(actor):
else:
abort(401)
else:
abort(404)
abort(404)

View file

@ -96,7 +96,7 @@ def retrieve_mods_and_backfill(community_id: int):
if outbox_request.status_code == 200:
outbox_data = outbox_request.json()
outbox_request.close()
if outbox_data['type'] == 'OrderedCollection' and 'orderedItems' in outbox_data:
if 'type' in outbox_data and outbox_data['type'] == 'OrderedCollection' and 'orderedItems' in outbox_data:
activities_processed = 0
for activity in outbox_data['orderedItems']:
user = find_actor_or_create(activity['object']['actor'])

View file

@ -440,6 +440,10 @@ class Community(db.Model):
retval = self.ap_profile_id if self.ap_profile_id else f"https://{current_app.config['SERVER_NAME']}/c/{self.name}"
return retval.lower()
def public_url(self):
result = self.ap_public_url if self.ap_public_url else f"https://{current_app.config['SERVER_NAME']}/c/{self.name}"
return result
def is_local(self):
return self.ap_id is None or self.profile_id().startswith('https://' + current_app.config['SERVER_NAME'])
@ -466,6 +470,14 @@ class Community(db.Model):
instances = instances.filter(Instance.id != 1, Instance.gone_forever == False)
return instances.all()
def has_followers_from_domain(self, domain: str) -> bool:
instances = Instance.query.join(User, User.instance_id == Instance.id).join(CommunityMember, CommunityMember.user_id == User.id)
instances = instances.filter(CommunityMember.community_id == self.id, CommunityMember.is_banned == False)
for instance in instances:
if instance.domain == domain:
return True
return False
def delete_dependencies(self):
for post in self.posts:
post.delete_dependencies()
@ -750,6 +762,10 @@ class User(UserMixin, db.Model):
result = self.ap_profile_id if self.ap_profile_id else f"https://{current_app.config['SERVER_NAME']}/u/{self.user_name}"
return result
def public_url(self):
result = self.ap_public_url if self.ap_public_url else f"https://{current_app.config['SERVER_NAME']}/u/{self.user_name}"
return result
def created_recently(self):
return self.created and self.created > utcnow() - timedelta(days=7)
@ -803,6 +819,12 @@ class User(UserMixin, db.Model):
reply.body = reply.body_html = ''
db.session.commit()
def mention_tag(self):
if self.ap_domain is None:
return '@' + self.user_name + '@' + current_app.config['SERVER_NAME']
else:
return '@' + self.user_name + '@' + self.ap_domain
class ActivityLog(db.Model):
id = db.Column(db.Integer, primary_key=True)

View file

@ -118,12 +118,12 @@ def show_post(post_id: int):
reply_json = {
'type': 'Note',
'id': reply.profile_id(),
'attributedTo': current_user.profile_id(),
'attributedTo': current_user.public_url(),
'to': [
'https://www.w3.org/ns/activitystreams#Public'
],
'cc': [
community.profile_id(),
community.public_url(), post.author.public_url()
],
'content': reply.body_html,
'inReplyTo': post.profile_id(),
@ -134,20 +134,30 @@ def show_post(post_id: int):
},
'published': ap_datetime(utcnow()),
'distinguished': False,
'audience': community.profile_id()
'audience': community.public_url(),
'tag': [{
'href': post.author.public_url(),
'name': post.author.mention_tag(),
'type': 'Mention'
}]
}
create_json = {
'type': 'Create',
'actor': current_user.profile_id(),
'audience': community.profile_id(),
'actor': current_user.public_url(),
'audience': community.public_url(),
'to': [
'https://www.w3.org/ns/activitystreams#Public'
],
'cc': [
community.ap_profile_id
community.public_url(), post.author.public_url()
],
'object': reply_json,
'id': f"https://{current_app.config['SERVER_NAME']}/activities/create/{gibberish(15)}"
'id': f"https://{current_app.config['SERVER_NAME']}/activities/create/{gibberish(15)}",
'tag': [{
'href': post.author.public_url(),
'name': post.author.mention_tag(),
'type': 'Mention'
}]
}
if not community.is_local(): # this is a remote community, send it to the instance that hosts it
success = post_request(community.ap_inbox_url, create_json, current_user.private_key,
@ -161,7 +171,7 @@ def show_post(post_id: int):
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"actor": community.ap_profile_id,
"actor": community.public_url(),
"cc": [
community.ap_followers_url
],
@ -173,6 +183,17 @@ def show_post(post_id: int):
if instance.inbox and not current_user.has_blocked_instance(instance.id) and not instance_banned(instance.domain):
send_to_remote_instance(instance.id, community.id, announce)
# send copy of Note to post author (who won't otherwise get it if no-one else on their instance is subscribed to the community)
if not post.author.is_local() and post.author.ap_domain != community.ap_domain:
if not community.is_local() or (community.is_local and not community.has_followers_from_domain(post.author.ap_domain)):
success = post_request(post.author.ap_inbox_url, create_json, current_user.private_key,
current_user.ap_profile_id + '#main-key')
if not success:
# sending to shared inbox is good enough for Mastodon, but Lemmy will reject it the local community has no followers
personal_inbox = post.author.public_url() + '/inbox'
post_request(personal_inbox, create_json, current_user.private_key,
current_user.ap_profile_id + '#main-key')
return redirect(url_for('activitypub.post_ap', post_id=post_id)) # redirect to current page to avoid refresh resubmitting the form
else:
replies = post_replies(post.id, sort)

View file

@ -670,7 +670,7 @@ def finalize_user_setup(user, application_required=False):
private_key, public_key = RsaKeys.generate_keypair()
user.private_key = private_key
user.public_key = public_key
user.ap_profile_id = f"https://{current_app.config['SERVER_NAME']}/u/{user.user_name}"
user.ap_profile_id = f"https://{current_app.config['SERVER_NAME']}/u/{user.user_name}".lower()
user.ap_public_url = f"https://{current_app.config['SERVER_NAME']}/u/{user.user_name}"
user.ap_inbox_url = f"https://{current_app.config['SERVER_NAME']}/u/{user.user_name}/inbox"
db.session.commit()