mirror of
https://codeberg.org/rimu/pyfedi
synced 2025-01-23 19:36:56 -08:00
Retrieve remote post from Search (no pre-supplied community required)
This commit is contained in:
parent
788cad653b
commit
ce1d01de09
4 changed files with 128 additions and 2 deletions
|
@ -2430,3 +2430,106 @@ def resolve_remote_post(uri: str, community_id: int, announce_actor=None) -> Uni
|
|||
return post
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def resolve_remote_post_from_search(uri: str) -> Union[Post, None]:
|
||||
post = Post.query.filter_by(ap_id=uri).first()
|
||||
if post:
|
||||
return post
|
||||
|
||||
site = Site.query.get(1)
|
||||
|
||||
parsed_url = urlparse(uri)
|
||||
uri_domain = parsed_url.netloc
|
||||
actor_domain = None
|
||||
actor = None
|
||||
post_request = get_request(uri, headers={'Accept': 'application/activity+json'})
|
||||
if post_request.status_code == 200:
|
||||
post_data = post_request.json()
|
||||
post_request.close()
|
||||
# check again that it doesn't already exist (can happen with different but equivalent URLs)
|
||||
post = Post.query.filter_by(ap_id=post_data['id']).first()
|
||||
if post:
|
||||
return post
|
||||
|
||||
# find the author of the post. Make sure their domain matches the site hosting it to migitage impersonation attempts
|
||||
if 'attributedTo' in post_data:
|
||||
if isinstance(post_data['attributedTo'], str):
|
||||
actor = post_data['attributedTo']
|
||||
parsed_url = urlparse(post_data['attributedTo'])
|
||||
actor_domain = parsed_url.netloc
|
||||
elif isinstance(post_data['attributedTo'], list):
|
||||
for a in post_data['attributedTo']:
|
||||
if a['type'] == 'Person':
|
||||
actor = a['id']
|
||||
parsed_url = urlparse(a['id'])
|
||||
actor_domain = parsed_url.netloc
|
||||
break
|
||||
if uri_domain != actor_domain:
|
||||
return None
|
||||
|
||||
# find the community the post was submitted to
|
||||
community = None
|
||||
if not community and post_data['type'] == 'Page': # lemmy
|
||||
if 'audience' in post_data:
|
||||
community_id = post_data['audience']
|
||||
community = Community.query.filter_by(ap_profile_id=community_id).first()
|
||||
|
||||
if not community and post_data['type'] == 'Video': # peertube
|
||||
if 'attributedTo' in post_data and isinstance(post_data['attributedTo'], list):
|
||||
for a in post_data['attributedTo']:
|
||||
if a['type'] == 'Group':
|
||||
community_id = a['id']
|
||||
community = Community.query.filter_by(ap_profile_id=community_id).first()
|
||||
if community:
|
||||
break
|
||||
|
||||
if not community: # mastodon, etc
|
||||
if 'inReplyTo' not in post_data or post_data['inReplyTo'] != None:
|
||||
return None
|
||||
|
||||
if not community and 'to' in post_data and isinstance(post_data['to'], str):
|
||||
community_id = post_data['to'].lower()
|
||||
if not community_id == 'https://www.w3.org/ns/activitystreams#Public' and not community_id.endswith('/followers'):
|
||||
community = Community.query.filter_by(ap_profile_id=community_id).first()
|
||||
if not community and 'cc' in post_data and isinstance(post_data['cc'], str):
|
||||
community_id = post_data['cc'].lower()
|
||||
if not community_id == 'https://www.w3.org/ns/activitystreams#Public' and not community_id.endswith('/followers'):
|
||||
community = Community.query.filter_by(ap_profile_id=community_id).first()
|
||||
if not community and 'to' in post_data and isinstance(post_data['to'], list):
|
||||
for t in post_data['to']:
|
||||
community_id = t.lower()
|
||||
if not community_id == 'https://www.w3.org/ns/activitystreams#Public' and not community_id.endswith('/followers'):
|
||||
community = Community.query.filter_by(ap_profile_id=community_id).first()
|
||||
if community:
|
||||
break
|
||||
if not community and 'cc' in post_data and isinstance(post_data['to'], list):
|
||||
for c in post_data['cc']:
|
||||
community_id = c.lower()
|
||||
if not community_id == 'https://www.w3.org/ns/activitystreams#Public' and not community_id.endswith('/followers'):
|
||||
community = Community.query.filter_by(ap_profile_id=community_id).first()
|
||||
if community:
|
||||
break
|
||||
|
||||
if not community:
|
||||
return None
|
||||
|
||||
activity_log = ActivityPubLog(direction='in', activity_id=post_data['id'], activity_type='Resolve Post', result='failure')
|
||||
if site.log_activitypub_json:
|
||||
activity_log.activity_json = json.dumps(post_data)
|
||||
db.session.add(activity_log)
|
||||
user = find_actor_or_create(actor)
|
||||
if user and community and post_data:
|
||||
request_json = {
|
||||
'id': f"https://{uri_domain}/activities/create/gibberish(15)",
|
||||
'object': post_data
|
||||
}
|
||||
post = create_post(activity_log, community, request_json, user)
|
||||
if post:
|
||||
if 'published' in post_data:
|
||||
post.posted_at=post_data['published']
|
||||
post.last_active=post_data['published']
|
||||
db.session.commit()
|
||||
return post
|
||||
|
||||
return None
|
||||
|
|
|
@ -7,6 +7,8 @@ from app.models import Post, Language, Community
|
|||
from app.search import bp
|
||||
from app.utils import moderating_communities, joined_communities, render_template, blocked_domains, blocked_instances, \
|
||||
communities_banned_from, recently_upvoted_posts, recently_downvoted_posts, blocked_users
|
||||
from app.community.forms import RetrieveRemotePost
|
||||
from app.activitypub.util import resolve_remote_post_from_search
|
||||
|
||||
|
||||
@bp.route('/search', methods=['GET', 'POST'])
|
||||
|
@ -87,3 +89,20 @@ def run_search():
|
|||
moderating_communities=moderating_communities(current_user.get_id()),
|
||||
joined_communities=joined_communities(current_user.get_id()),
|
||||
site=g.site)
|
||||
|
||||
|
||||
@bp.route('/retrieve_remote_post', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def retrieve_remote_post():
|
||||
if current_user.banned:
|
||||
return show_ban_message()
|
||||
form = RetrieveRemotePost()
|
||||
new_post = None
|
||||
if form.validate_on_submit():
|
||||
address = form.address.data.strip()
|
||||
new_post = resolve_remote_post_from_search(address)
|
||||
if new_post is None:
|
||||
flash(_('Post not found.'), 'warning')
|
||||
|
||||
return render_template('community/retrieve_remote_post.html',
|
||||
title=_('Retrieve Remote Post'), form=form, new_post=new_post)
|
||||
|
|
|
@ -11,7 +11,11 @@
|
|||
<div class="card mt-5">
|
||||
<div class="card-body p-6">
|
||||
<div class="card-title">{{ _('Retrieve Remote Post') }}</div>
|
||||
<p>Enter the full URL of the post submitted to {{ community.ap_id }}</p>
|
||||
{% if community %}
|
||||
<p>Enter the full URL of the post submitted to {{ community.ap_id }}</p>
|
||||
{% else %}
|
||||
<p>Enter the full URL of the post</p>
|
||||
{% endif %}
|
||||
<p>Note: URL needs to match the one from the post author's instance (which may be different than the community's instance)</p>
|
||||
{{ render_form(form) }}
|
||||
</div>
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
<h6 class="mt-5">{{ _('Trying to add a communitiy or post from another instance?') }} </h6>
|
||||
<p>{{ _('In many types of federated platforms you can put a URL of a post or community into the search in order to add it to your local instance. In PieFed the search is just for searching.') }}</p>
|
||||
<p><a href="{{ url_for('community.add_remote') }}" class="btn btn-primary">{{ _('Add remote community') }}</a></p>
|
||||
<p>{{ _('To add a post from a remote instance, find the community in PieFed then look for the "Retrieve a post from the original server" link.') }}</p>
|
||||
<p><a href="{{ url_for('search.retrieve_remote_post') }}" class="btn btn-primary">{{ _('Retrieve remote post') }}</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue