From edb01af961550654988471622a45b66de72f2371 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 8 Feb 2022 10:33:13 +0000 Subject: [PATCH 1/3] Support for receiving ChatMessage activities Equivalent to a Note DM --- outbox.py | 2 +- posts.py | 8 +++++--- utils.py | 14 ++++++++------ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/outbox.py b/outbox.py index db6e6a690..371d7ae20 100644 --- a/outbox.py +++ b/outbox.py @@ -408,7 +408,7 @@ def post_message_to_outbox(session, translate: {}, # The following activity types get added to the index files indexed_activities = ( 'Create', 'Question', 'Note', 'EncryptedMessage', 'Article', - 'Patch', 'Announce' + 'Patch', 'Announce', 'ChatMessage' ) if message_json['type'] in indexed_activities: indexes = [outbox_name, "inbox"] diff --git a/posts.py b/posts.py index 96b827a2c..eae572545 100644 --- a/posts.py +++ b/posts.py @@ -484,9 +484,9 @@ def _is_public_feed_post(item: {}, person_posts: {}, debug: bool) -> bool: this_item = item['object'] # check that this is a public post # #Public should appear in the "to" list - itemIsNote = False + item_is_note = False if item['type'] == 'Note' or item['type'] == 'Page': - itemIsNote = True + item_is_note = True if isinstance(this_item, dict): if this_item.get('to'): @@ -497,7 +497,7 @@ def _is_public_feed_post(item: {}, person_posts: {}, debug: bool) -> bool: break if not is_public: return False - elif isinstance(this_item, str) or itemIsNote: + elif isinstance(this_item, str) or item_is_note: if item.get('to'): is_public = False for recipient in item['to']: @@ -3561,6 +3561,7 @@ def is_image_media(session, base_dir: str, http_prefix: str, if post_json_object['object']['type'] != 'Note' and \ post_json_object['object']['type'] != 'Page' and \ post_json_object['object']['type'] != 'Event' and \ + post_json_object['object']['type'] != 'ChatMessage' and \ post_json_object['object']['type'] != 'Article': return False if not post_json_object['object'].get('attachment'): @@ -3583,6 +3584,7 @@ def _add_post_string_to_timeline(post_str: str, boxname: str, # must be a recognized ActivityPub type if ('"Note"' in post_str or '"EncryptedMessage"' in post_str or + '"ChatMessage"' in post_str or '"Event"' in post_str or '"Article"' in post_str or '"Patch"' in post_str or diff --git a/utils.py b/utils.py index 5def9ec8f..c70c4fd28 100644 --- a/utils.py +++ b/utils.py @@ -2661,12 +2661,13 @@ def is_dm(post_json_object: {}) -> bool: return False if not has_object_dict(post_json_object): return False - if post_json_object['object']['type'] != 'Note' and \ - post_json_object['object']['type'] != 'Page' and \ - post_json_object['object']['type'] != 'Patch' and \ - post_json_object['object']['type'] != 'EncryptedMessage' and \ - post_json_object['object']['type'] != 'Article': - return False + if post_json_object['object']['type'] != 'ChatMessage': + if post_json_object['object']['type'] != 'Note' and \ + post_json_object['object']['type'] != 'Page' and \ + post_json_object['object']['type'] != 'Patch' and \ + post_json_object['object']['type'] != 'EncryptedMessage' and \ + post_json_object['object']['type'] != 'Article': + return False if post_json_object['object'].get('moderationStatus'): return False fields = ('to', 'cc') @@ -2693,6 +2694,7 @@ def is_reply(post_json_object: {}, actor: str) -> bool: if post_json_object['object']['type'] != 'Note' and \ post_json_object['object']['type'] != 'Page' and \ post_json_object['object']['type'] != 'EncryptedMessage' and \ + post_json_object['object']['type'] != 'ChatMessage' and \ post_json_object['object']['type'] != 'Article': return False if post_json_object['object'].get('inReplyTo'): From 51a666df39ea4091fad71b998b291daf37675f97 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 8 Feb 2022 10:52:03 +0000 Subject: [PATCH 2/3] Replying to a chat message has a different path --- content.py | 2 +- daemon.py | 12 ++++++++---- utils.py | 13 +++++++++++++ webapp_post.py | 6 +++++- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/content.py b/content.py index aaa1545a3..5bebdb0de 100644 --- a/content.py +++ b/content.py @@ -48,7 +48,7 @@ INVALID_CONTENT_STRINGS = ( 'graph', 'showshare', 'category', 'showwanted', 'rmshare', 'rmwanted', 'repeatprivate', 'unrepeatprivate', 'replyto', - 'replyfollowers', 'replydm', 'editblogpost', + 'replyfollowers', 'replydm', 'replychat', 'editblogpost', 'handle', 'blockdomain' ) diff --git a/daemon.py b/daemon.py index c3a19c9ea..3ab138298 100644 --- a/daemon.py +++ b/daemon.py @@ -15932,8 +15932,11 @@ class PubServer(BaseHTTPRequestHandler): # replying as a direct message, # for moderation posts or the dm timeline - if '?replydm=' in self.path: - in_reply_to_url = self.path.split('?replydm=')[1] + if '?replydm=' in self.path or '?replychat=' in self.path: + reply_type = 'replydm' + if '?replychat=' in self.path: + reply_type = 'replychat' + in_reply_to_url = self.path.split('?' + reply_type + '=')[1] in_reply_to_url = urllib.parse.unquote_plus(in_reply_to_url) if '?' in in_reply_to_url: # multiple parameters @@ -15970,9 +15973,10 @@ class PubServer(BaseHTTPRequestHandler): share_description = \ share_description.replace('_', ' ') - self.path = self.path.split('?replydm=')[0] + '/newdm' + self.path = \ + self.path.split('?' + reply_type + '=')[0] + '/newdm' if self.server.debug: - print('DEBUG: replydm path ' + self.path) + print('DEBUG: ' + reply_type + ' path ' + self.path) # Edit a blog post if authorized and \ diff --git a/utils.py b/utils.py index c70c4fd28..b03a719df 100644 --- a/utils.py +++ b/utils.py @@ -2654,6 +2654,19 @@ def reject_post_id(base_dir: str, nickname: str, domain: str, reject_file.write('\n') +def is_chat_message(post_json_object: {}) -> bool: + """Returns true if the given post is a chat message + Note that is_dm should be checked before calling this + """ + if post_json_object['type'] != 'Create': + return False + if not has_object_dict(post_json_object): + return False + if post_json_object['object']['type'] != 'ChatMessage': + return False + return True + + def is_dm(post_json_object: {}) -> bool: """Returns true if the given post is a DM """ diff --git a/webapp_post.py b/webapp_post.py index 01b1c1675..c645a30fe 100644 --- a/webapp_post.py +++ b/webapp_post.py @@ -33,6 +33,7 @@ from utils import has_object_dict from utils import update_announce_collection from utils import is_pgp_encrypted from utils import is_dm +from utils import is_chat_message from utils import reject_post_id from utils import is_recent_post from utils import get_config_param @@ -445,10 +446,13 @@ def _get_reply_icon_html(base_dir: str, nickname: str, domain: str, '" title="' + reply_to_this_post_str + '">\n' else: if is_dm(post_json_object): + reply_type = 'replydm' + if is_chat_message(post_json_object): + reply_type = 'replychat' reply_str += \ ' ' + \ '\n' From 823b4c331dcebabeddc9ee96e86dbea89d7fa910 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 8 Feb 2022 12:08:20 +0000 Subject: [PATCH 3/3] When replying to or bouncing a chat message the post should have ChatMessage type --- daemon.py | 28 ++++++++++++++++++++++------ inbox.py | 12 +++++++++--- posts.py | 5 ++++- webapp_create_post.py | 11 +++++++++-- 4 files changed, 44 insertions(+), 12 deletions(-) diff --git a/daemon.py b/daemon.py index 3ab138298..7b8a40715 100644 --- a/daemon.py +++ b/daemon.py @@ -2838,6 +2838,7 @@ class PubServer(BaseHTTPRequestHandler): custom_submit_text = get_config_param(base_dir, 'customSubmitText') conversation_id = None + reply_is_chat = False msg = html_new_post(self.server.css_cache, False, self.server.translate, base_dir, @@ -2872,7 +2873,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.signing_priv_key_pem, self.server.cw_lists, self.server.lists_enabled, - self.server.default_timeline).encode('utf-8') + self.server.default_timeline, + reply_is_chat).encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, cookie, calling_domain, False) @@ -2973,6 +2975,7 @@ class PubServer(BaseHTTPRequestHandler): custom_submit_text = get_config_param(base_dir, 'customSubmitText') conversation_id = None + reply_is_chat = False msg = html_new_post(self.server.css_cache, False, self.server.translate, base_dir, @@ -3006,7 +3009,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.signing_priv_key_pem, self.server.cw_lists, self.server.lists_enabled, - self.server.default_timeline).encode('utf-8') + self.server.default_timeline, + reply_is_chat).encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, cookie, calling_domain, False) @@ -13188,6 +13192,7 @@ class PubServer(BaseHTTPRequestHandler): media_instance: bool, translate: {}, base_dir: str, http_prefix: str, in_reply_to_url: str, reply_to_list: [], + reply_is_chat: bool, share_description: str, reply_page_number: int, reply_category: str, domain: str, domain_full: str, @@ -13214,7 +13219,7 @@ class PubServer(BaseHTTPRequestHandler): str(reply_interval_hours) + ' hours') self._403() return True - elif self.server.debug: + if self.server.debug: print('Reply is within time interval: ' + str(reply_interval_hours) + ' hours') @@ -13267,7 +13272,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.signing_priv_key_pem, self.server.cw_lists, self.server.lists_enabled, - self.server.default_timeline).encode('utf-8') + self.server.default_timeline, + reply_is_chat).encode('utf-8') if not msg: print('Error replying to ' + in_reply_to_url) self._404() @@ -15932,10 +15938,12 @@ class PubServer(BaseHTTPRequestHandler): # replying as a direct message, # for moderation posts or the dm timeline + reply_is_chat = False if '?replydm=' in self.path or '?replychat=' in self.path: reply_type = 'replydm' if '?replychat=' in self.path: reply_type = 'replychat' + reply_is_chat = True in_reply_to_url = self.path.split('?' + reply_type + '=')[1] in_reply_to_url = urllib.parse.unquote_plus(in_reply_to_url) if '?' in in_reply_to_url: @@ -16071,6 +16079,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.base_dir, self.server.http_prefix, in_reply_to_url, reply_to_list, + reply_is_chat, share_description, reply_page_number, reply_category, self.server.domain, @@ -16919,6 +16928,7 @@ class PubServer(BaseHTTPRequestHandler): city = get_spoofed_city(self.server.city, self.server.base_dir, nickname, self.server.domain) + conversation_id = None if fields.get('conversationId'): conversation_id = fields['conversationId'] @@ -17305,6 +17315,10 @@ class PubServer(BaseHTTPRequestHandler): self.server.domain_full, self.server.person_cache) + reply_is_chat = False + if fields.get('replychatmsg'): + reply_is_chat = fields['replychatmsg'] + message_json = \ create_direct_message_post(self.server.base_dir, nickname, @@ -17333,7 +17347,8 @@ class PubServer(BaseHTTPRequestHandler): conversation_id, self.server.low_bandwidth, content_license_url, - languages_understood) + languages_understood, + reply_is_chat) if message_json: if fields['schedulePost']: return 1 @@ -17396,7 +17411,8 @@ class PubServer(BaseHTTPRequestHandler): conversation_id, self.server.low_bandwidth, self.server.content_license_url, - languages_understood) + languages_understood, + False) if message_json: if fields['schedulePost']: return 1 diff --git a/inbox.py b/inbox.py index e8afe0df4..b4a97e296 100644 --- a/inbox.py +++ b/inbox.py @@ -2826,7 +2826,8 @@ def _bounce_dm(senderPostId: str, session, http_prefix: str, last_bounce_message: [], system_language: str, signing_priv_key_pem: str, content_license_url: str, - languages_understood: []) -> bool: + languages_understood: [], + bounce_is_chat: bool) -> bool: """Sends a bounce message back to the sending handle if a DM has been rejected """ @@ -2888,7 +2889,7 @@ def _bounce_dm(senderPostId: str, session, http_prefix: str, system_language, conversation_id, low_bandwidth, content_license_url, - languages_understood) + languages_understood, bounce_is_chat) if not post_json_object: print('WARN: unable to create bounce message to ' + sending_handle) return False @@ -2984,6 +2985,10 @@ def _is_valid_dm(base_dir: str, nickname: str, domain: str, port: int, if not obj.get('inReplyTo'): bounced_id = \ remove_id_ending(post_json_object['id']) + bounce_chat = False + if obj.get('type'): + if obj['type'] == 'ChatMessage': + bounce_chat = True _bounce_dm(bounced_id, session, http_prefix, base_dir, @@ -2998,7 +3003,8 @@ def _is_valid_dm(base_dir: str, nickname: str, domain: str, port: int, system_language, signing_priv_key_pem, content_license_url, - languages_understood) + languages_understood, + bounce_chat) return False # dm index will be updated diff --git a/posts.py b/posts.py index eae572545..7137e4ede 100644 --- a/posts.py +++ b/posts.py @@ -2113,7 +2113,8 @@ def create_direct_message_post(base_dir: str, location: str, system_language: str, conversation_id: str, low_bandwidth: bool, content_license_url: str, - languages_understood: []) -> {}: + languages_understood: [], + dm_is_chat: bool) -> {}: """Direct Message post """ content = resolve_petnames(base_dir, nickname, domain, content) @@ -2144,6 +2145,8 @@ def create_direct_message_post(base_dir: str, message_json['object']['to'] = message_json['to'] message_json['cc'] = [] message_json['object']['cc'] = [] + if dm_is_chat: + message_json['object']['type'] = 'ChatMessage' if schedule_post: post_id = remove_id_ending(message_json['object']['id']) save_post_to_box(base_dir, http_prefix, post_id, diff --git a/webapp_create_post.py b/webapp_create_post.py index b96ff2cda..dd39ad58e 100644 --- a/webapp_create_post.py +++ b/webapp_create_post.py @@ -210,7 +210,8 @@ def html_new_post(css_cache: {}, media_instance: bool, translate: {}, system_language: str, max_like_count: int, signing_priv_key_pem: str, cw_lists: {}, lists_enabled: str, - boxName: str) -> str: + boxName: str, + reply_is_chat: bool) -> str: """New post screen """ reply_str = '' @@ -691,7 +692,10 @@ def html_new_post(css_cache: {}, media_instance: bool, translate: {}, dropdown_new_blog_suffix += '?replyto=' + inReplyTo dropdown_unlisted_suffix += '?replyto=' + inReplyTo dropdown_followers_suffix += '?replyfollowers=' + inReplyTo - dropdown_dm_suffix += '?replydm=' + inReplyTo + if reply_is_chat: + dropdown_dm_suffix += '?replychat=' + inReplyTo + else: + dropdown_dm_suffix += '?replydm=' + inReplyTo for mentioned_actor in mentions: dropdown_new_post_suffix += '?mention=' + mentioned_actor dropdown_new_blog_suffix += '?mention=' + mentioned_actor @@ -732,6 +736,9 @@ def html_new_post(css_cache: {}, media_instance: bool, translate: {}, '
\n' + if reply_is_chat: + new_post_form += \ + ' \n' if conversationId: new_post_form += \ '