Handle inReplyTo in a way that is compatible with bookwyrm

merge-requests/30/head
Bob Mottram 2023-12-24 23:42:38 +00:00
parent 85f03e69c4
commit db4f77e52d
7 changed files with 100 additions and 67 deletions

View File

@ -16,6 +16,7 @@ from utils import locate_post
from utils import load_json
from utils import harmless_markup
from utils import get_attributed_to
from utils import get_reply_to
from keys import get_instance_actor_key
from session import get_json
from session import get_json_valid
@ -190,11 +191,11 @@ def download_conversation_posts(authorized: bool, session,
if not authorized:
# only show a single post to non-authorized viewers
break
if not post_json_object['object'].get('inReplyTo'):
post_id = get_reply_to(post_json_object['object'])
if not post_id:
if debug:
print(post_id + ' is not a reply')
break
post_id = post_json_object['object']['inReplyTo']
post_id = remove_id_ending(post_id)
post_filename = \
locate_post(base_dir, nickname, domain, post_id)

View File

@ -33,6 +33,7 @@ from utils import get_nickname_from_actor
from utils import get_domain_from_actor
from utils import is_pgp_encrypted
from utils import local_actor_url
from utils import get_reply_to
from session import create_session
from speaker import speakable_text
from speaker import get_speaker_pitch
@ -897,8 +898,9 @@ def _read_local_box_post(session, nickname: str, domain: str,
system_language, espeak, name_str, gender)
print('')
if post_json_object['object'].get('inReplyTo'):
print('Replying to ' + post_json_object['object']['inReplyTo'] + '\n')
reply_id = get_reply_to(post_json_object['object'])
if reply_id:
print('Replying to ' + reply_id + '\n')
if screenreader:
time.sleep(2)
@ -1181,7 +1183,8 @@ def _desktop_show_box(indent: str,
# append icons to the end of the name
space_added = False
if post_json_object['object'].get('inReplyTo'):
reply_id = get_reply_to(post_json_object['object'])
if reply_id:
if not space_added:
space_added = True
name += ' '

View File

@ -80,6 +80,7 @@ from utils import local_actor_url
from utils import has_object_string_type
from utils import valid_hash_tag
from utils import get_attributed_to
from utils import get_reply_to
from categories import get_hashtag_categories
from categories import set_hashtag_category
from httpsig import get_digest_algorithm_from_headers
@ -623,8 +624,9 @@ def inbox_permitted_message(domain: str, message_json: {},
if message_json['type'] not in always_allowed_types:
if not has_object_dict(message_json):
return True
if message_json['object'].get('inReplyTo'):
in_reply_to = message_json['object']['inReplyTo']
reply_id = get_reply_to(message_json['object'])
if reply_id:
in_reply_to = reply_id
if not isinstance(in_reply_to, str):
return False
if not url_permitted(in_reply_to, federation_list):
@ -737,10 +739,10 @@ def save_post_to_inbox_queue(base_dir: str, http_prefix: str,
return None
# is this a reply to a blocked domain or account?
if post_json_object['object'].get('inReplyTo'):
if isinstance(post_json_object['object']['inReplyTo'], str):
in_reply_to = \
post_json_object['object']['inReplyTo']
reply_id = get_reply_to(post_json_object['object'])
if reply_id:
if isinstance(reply_id, str):
in_reply_to = reply_id
reply_domain, _ = \
get_domain_from_actor(in_reply_to)
if reply_domain:
@ -1528,9 +1530,10 @@ def _valid_post_content(base_dir: str, nickname: str, domain: str,
if message_json.get('id'):
print('REJECT: content filtered ' + str(message_json['id']))
return False
if message_json['object'].get('inReplyTo'):
if isinstance(message_json['object']['inReplyTo'], str):
original_post_id = message_json['object']['inReplyTo']
reply_id = get_reply_to(message_json['object'])
if reply_id:
if isinstance(reply_id, str):
original_post_id = reply_id
post_post_filename = locate_post(base_dir, nickname, domain,
original_post_id)
if post_post_filename:
@ -2370,7 +2373,8 @@ def _receive_zot_reaction(recent_posts_cache: {},
print('DEBUG: ' + message_json['object']['type'] +
' has no "content"')
return False
if not message_json['object'].get('inReplyTo'):
reply_id = get_reply_to(message_json['object'])
if not reply_id:
if debug:
print('DEBUG: ' + message_json['object']['type'] +
' has no "inReplyTo"')
@ -2384,7 +2388,7 @@ def _receive_zot_reaction(recent_posts_cache: {},
if debug:
print('DEBUG: content is too long to be an emoji reaction')
return False
if not isinstance(message_json['object']['inReplyTo'], str):
if not isinstance(reply_id, str):
if debug:
print('DEBUG: ' + message_json['object']['type'] +
' inReplyTo is not string')
@ -2399,7 +2403,7 @@ def _receive_zot_reaction(recent_posts_cache: {},
print('DEBUG: "users" or "profile" missing from actor in ' +
message_json['object']['type'])
return False
if '/statuses/' not in message_json['object']['inReplyTo']:
if '/statuses/' not in reply_id:
if debug:
print('DEBUG: "statuses" missing from inReplyTo in ' +
message_json['object']['type'])
@ -2415,7 +2419,7 @@ def _receive_zot_reaction(recent_posts_cache: {},
handle_name = handle.split('@')[0]
handle_dom = handle.split('@')[1]
post_reaction_id = message_json['object']['inReplyTo']
post_reaction_id = get_reply_to(message_json['object'])
emoji_content = remove_html(message_json['object']['content'])
if not emoji_content:
if debug:
@ -3417,11 +3421,11 @@ def populate_replies(base_dir: str, http_prefix: str, domain: str,
return False
if not has_object_dict(message_json):
return False
if not message_json['object'].get('inReplyTo'):
reply_to = get_reply_to(message_json['object'])
if not reply_to:
return False
if not message_json['object'].get('to'):
return False
reply_to = message_json['object']['inReplyTo']
if not isinstance(reply_to, str):
return False
if debug:
@ -3509,10 +3513,11 @@ def _obtain_avatar_for_reply_post(session, base_dir: str, http_prefix: str,
if not has_object_dict(post_json_object):
return
if not post_json_object['object'].get('inReplyTo'):
reply_id = get_reply_to(post_json_object['object'])
if not reply_id:
return
lookup_actor = post_json_object['object']['inReplyTo']
lookup_actor = reply_id
if not lookup_actor:
return
@ -4225,7 +4230,7 @@ def _is_valid_dm(base_dir: str, nickname: str, domain: str, port: int,
# replies to bounce messages
obj = post_json_object['object']
if obj_has_dict and \
not obj.get('inReplyTo'):
not get_reply_to(obj):
bounced_id = \
remove_id_ending(post_json_object['id'])
bounce_chat = False
@ -4392,9 +4397,7 @@ def _create_reply_notification_file(base_dir: str, nickname: str, domain: str,
elif post_json_object['object'].get('context'):
conversation_id = post_json_object['object']['context']
if not post_json_object['object'].get('inReplyTo'):
return is_reply_to_muted_post
in_reply_to = post_json_object['object']['inReplyTo']
in_reply_to = get_reply_to(post_json_object['object'])
if not in_reply_to:
return is_reply_to_muted_post
if not isinstance(in_reply_to, str):

View File

@ -86,6 +86,7 @@ from utils import remove_html
from utils import dangerous_markup
from utils import acct_dir
from utils import local_actor_url
from utils import get_reply_to
from media import get_music_metadata
from media import attach_media
from media import replace_you_tube
@ -684,17 +685,17 @@ def _get_posts(session, outbox_url: str, max_posts: int,
if this_item['summary']:
summary = this_item['summary']
if this_item.get('inReplyTo'):
if this_item['inReplyTo']:
if isinstance(this_item['inReplyTo'], str):
reply_id = get_reply_to(this_item)
if reply_id:
if isinstance(reply_id, str):
# No replies to non-permitted domains
if not url_permitted(this_item['inReplyTo'],
if not url_permitted(reply_id,
federation_list):
if debug:
print('url not permitted ' +
this_item['inReplyTo'])
reply_id)
continue
in_reply_to = this_item['inReplyTo']
in_reply_to = reply_id
if this_item.get('attachment'):
if len(this_item['attachment']) > max_attachments:
@ -836,10 +837,11 @@ def get_post_domains(session, outbox_url: str, max_posts: int, debug: bool,
content_str = get_base_content_from_post(item, system_language)
if content_str:
_update_word_frequency(content_str, word_frequency)
if item['object'].get('inReplyTo'):
if isinstance(item['object']['inReplyTo'], str):
reply_id = get_reply_to(item['object'])
if reply_id:
if isinstance(reply_id, str):
post_domain, _ = \
get_domain_from_actor(item['object']['inReplyTo'])
get_domain_from_actor(reply_id)
if post_domain:
if post_domain not in post_domains:
post_domains.append(post_domain)
@ -900,10 +902,11 @@ def _get_posts_for_blocked_domains(base_dir: str,
break
if not has_object_dict(item):
continue
if item['object'].get('inReplyTo'):
if isinstance(item['object']['inReplyTo'], str):
reply_id = get_reply_to(item['object'])
if reply_id:
if isinstance(reply_id, str):
post_domain, _ = \
get_domain_from_actor(item['object']['inReplyTo'])
get_domain_from_actor(reply_id)
if not post_domain:
continue
if is_blocked_domain(base_dir, post_domain):

View File

@ -14,6 +14,7 @@ from utils import save_json
from utils import has_object_dict
from utils import text_in_file
from utils import dangerous_markup
from utils import get_reply_to
def is_vote(base_dir: str, nickname: str, domain: str,
@ -24,9 +25,10 @@ def is_vote(base_dir: str, nickname: str, domain: str,
if has_object_dict(post_json_object):
post_obj = post_json_object['object']
if not post_obj.get('inReplyTo'):
reply_id = get_reply_to(post_obj)
if not reply_id:
return False
if not isinstance(post_obj['inReplyTo'], str):
if not isinstance(reply_id, str):
return False
if not post_obj.get('name'):
return False
@ -35,7 +37,7 @@ def is_vote(base_dir: str, nickname: str, domain: str,
print('VOTE: ' + str(post_obj))
# is the replied to post a Question?
in_reply_to = post_obj['inReplyTo']
in_reply_to = reply_id
question_post_filename = \
locate_post(base_dir, nickname, domain, in_reply_to)
if not question_post_filename:
@ -105,7 +107,7 @@ def question_update_votes(base_dir: str, nickname: str, domain: str,
post_obj = reply_json['object']
reply_vote = post_obj['name']
in_reply_to = post_obj['inReplyTo']
in_reply_to = get_reply_to(post_obj)
question_post_filename = \
locate_post(base_dir, nickname, domain, in_reply_to)
if not question_post_filename:

View File

@ -2138,15 +2138,16 @@ def _is_reply_to_blog_post(base_dir: str, nickname: str, domain: str,
"""
if not has_object_dict(post_json_object):
return False
if not post_json_object['object'].get('inReplyTo'):
reply_id = get_reply_to(post_json_object['object'])
if not reply_id:
return False
if not isinstance(post_json_object['object']['inReplyTo'], str):
if not isinstance(reply_id, str):
return False
blogs_index_filename = \
acct_dir(base_dir, nickname, domain) + '/tlblogs.index'
if not os.path.isfile(blogs_index_filename):
return False
post_id = remove_id_ending(post_json_object['object']['inReplyTo'])
post_id = remove_id_ending(reply_id)
post_id = post_id.replace('/', '#')
if text_in_file(post_id, blogs_index_filename):
return True
@ -3477,9 +3478,10 @@ def is_reply(post_json_object: {}, actor: str) -> bool:
'EncryptedMessage',
'ChatMessage', 'Article'):
return False
if post_json_object['object'].get('inReplyTo'):
if isinstance(post_json_object['object']['inReplyTo'], str):
if post_json_object['object']['inReplyTo'].startswith(actor):
reply_id = get_reply_to(post_json_object['object'])
if reply_id:
if isinstance(reply_id, str):
if reply_id.startswith(actor):
return True
if not post_json_object['object'].get('tag'):
return False
@ -4840,3 +4842,13 @@ def get_media_url_from_video(post_json_object: {}) -> (str, str, str, str):
media_type = media_link['mediaType']
media_url = remove_html(media_link['href'])
return media_type, media_url, media_torrent, media_magnet
def get_reply_to(post_json_object: {}) -> str:
"""Returns the reply to link from a post
"""
if post_json_object.get('inReplyTo'):
return post_json_object['inReplyTo']
if post_json_object.get('inReplyToBook'):
return post_json_object['inReplyToBook']
return ''

View File

@ -73,6 +73,7 @@ from utils import local_actor_url
from utils import is_unlisted_post
from utils import language_right_to_left
from utils import get_attributed_to
from utils import get_reply_to
from content import format_mixed_right_to_left
from content import replace_remote_hashtags
from content import detect_dogwhistles
@ -678,8 +679,9 @@ def _get_edit_icon_html(base_dir: str, nickname: str, domain_full: str,
return edit_str
reply_to = ''
if post_json_object['object'].get('inReplyTo'):
reply_to = ';replyTo=' + post_json_object['object']['inReplyTo']
reply_id = get_reply_to(post_json_object['object'])
if reply_id:
reply_to = ';replyTo=' + reply_id
first_post_str = ''
if first_post_id:
@ -1482,7 +1484,7 @@ def _reply_to_unknown_html(translate: {},
"""Returns the html title for a reply to an unknown handle
"""
replying_to_str = _replying_to_with_scope(post_json_object, translate)
post_id = post_json_object['object']['inReplyTo']
post_id = get_reply_to(post_json_object['object'])
post_link = '/users/' + nickname + '?convthread=' + \
post_id.replace('/', '--')
return ' <img loading="lazy" decoding="async" title="' + \
@ -1512,7 +1514,7 @@ def _reply_with_unknown_path_html(translate: {},
eg. does not contain /statuses/
"""
replying_to_str = _replying_to_with_scope(post_json_object, translate)
post_id = post_json_object['object']['inReplyTo']
post_id = get_reply_to(post_json_object['object'])
post_link = '/users/' + nickname + '?convthread=' + \
post_id.replace('/', '--')
return ' <img loading="lazy" decoding="async" title="' + \
@ -1577,7 +1579,8 @@ def _get_post_title_reply_html(base_dir: str,
obj_json = post_json_object['object']
# not a reply
if not obj_json.get('inReplyTo'):
reply_id = get_reply_to(obj_json)
if not reply_id:
return (title_str, reply_avatar_image_in_post,
container_class_icons, container_class)
@ -1585,7 +1588,7 @@ def _get_post_title_reply_html(base_dir: str,
container_class = 'container darker'
# reply to self
if obj_json['inReplyTo'].startswith(post_actor):
if reply_id.startswith(post_actor):
title_str += _reply_to_yourself_html(translate)
return (title_str, reply_avatar_image_in_post,
container_class_icons, container_class)
@ -1593,8 +1596,8 @@ def _get_post_title_reply_html(base_dir: str,
# has a reply
reply_actor = None
in_reply_to = None
if '/statuses/' not in obj_json['inReplyTo']:
reply_url = obj_json['inReplyTo']
if '/statuses/' not in reply_id:
reply_url = reply_id
post_domain = reply_url
prefixes = get_protocol_prefixes()
for prefix in prefixes:
@ -1637,9 +1640,10 @@ def _get_post_title_reply_html(base_dir: str,
return (title_str, reply_avatar_image_in_post,
container_class_icons, container_class)
if obj_json.get('inReplyTo'):
if isinstance(obj_json['inReplyTo'], str):
in_reply_to = obj_json['inReplyTo']
reply_id = get_reply_to(obj_json)
if reply_id:
if isinstance(reply_id, str):
in_reply_to = reply_id
if in_reply_to and not reply_actor:
reply_actor = in_reply_to.split('/statuses/')[0]
reply_nickname = get_nickname_from_actor(reply_actor)
@ -3081,10 +3085,15 @@ def html_individual_post(recent_posts_cache: {}, max_recent_posts: int,
# show the previous posts
if has_object_dict(post_json_object):
while post_json_object['object'].get('inReplyTo'):
post_id = True
while post_id:
if not post_json_object:
break
post_id = get_reply_to(post_json_object['object'])
if not post_id:
break
post_filename = \
locate_post(base_dir, nickname, domain,
post_json_object['object']['inReplyTo'])
locate_post(base_dir, nickname, domain, post_id)
if not post_filename:
break
post_json_object = load_json(post_filename)