2021-08-12 10:26:24 +00:00
|
|
|
__filename__ = "conversation.py"
|
|
|
|
__author__ = "Bob Mottram"
|
|
|
|
__license__ = "AGPL3+"
|
2023-01-21 23:03:30 +00:00
|
|
|
__version__ = "1.4.0"
|
2021-08-12 10:26:24 +00:00
|
|
|
__maintainer__ = "Bob Mottram"
|
2021-09-10 16:14:50 +00:00
|
|
|
__email__ = "bob@libreserver.org"
|
2021-08-12 10:26:24 +00:00
|
|
|
__status__ = "Production"
|
|
|
|
__module_group__ = "Timeline"
|
|
|
|
|
|
|
|
import os
|
2021-12-26 10:57:03 +00:00
|
|
|
from utils import has_object_dict
|
2021-12-26 12:02:29 +00:00
|
|
|
from utils import acct_dir
|
2021-12-27 11:20:57 +00:00
|
|
|
from utils import remove_id_ending
|
2022-06-10 11:43:33 +00:00
|
|
|
from utils import text_in_file
|
2022-12-28 10:18:13 +00:00
|
|
|
from utils import locate_post
|
|
|
|
from utils import load_json
|
2023-01-07 11:45:19 +00:00
|
|
|
from utils import harmless_markup
|
2022-12-28 10:18:13 +00:00
|
|
|
from keys import get_instance_actor_key
|
|
|
|
from session import get_json
|
2021-08-12 10:26:24 +00:00
|
|
|
|
|
|
|
|
2021-12-29 21:55:09 +00:00
|
|
|
def _get_conversation_filename(base_dir: str, nickname: str, domain: str,
|
|
|
|
post_json_object: {}) -> str:
|
2021-10-14 15:12:35 +00:00
|
|
|
"""Returns the conversation filename
|
2021-08-12 10:26:24 +00:00
|
|
|
"""
|
2021-12-26 10:57:03 +00:00
|
|
|
if not has_object_dict(post_json_object):
|
2021-10-14 15:12:35 +00:00
|
|
|
return None
|
2023-01-09 11:38:05 +00:00
|
|
|
if not post_json_object['object'].get('conversation') and \
|
|
|
|
not post_json_object['object'].get('context'):
|
2021-10-14 15:12:35 +00:00
|
|
|
return None
|
2021-12-25 22:09:19 +00:00
|
|
|
if not post_json_object['object'].get('id'):
|
2021-10-14 15:12:35 +00:00
|
|
|
return None
|
2021-12-30 20:32:19 +00:00
|
|
|
conversation_dir = acct_dir(base_dir, nickname, domain) + '/conversation'
|
|
|
|
if not os.path.isdir(conversation_dir):
|
|
|
|
os.mkdir(conversation_dir)
|
2023-01-09 11:38:05 +00:00
|
|
|
if post_json_object['object'].get('conversation'):
|
|
|
|
conversation_id = post_json_object['object']['conversation']
|
|
|
|
else:
|
|
|
|
conversation_id = post_json_object['object']['context']
|
2021-12-30 20:32:19 +00:00
|
|
|
conversation_id = conversation_id.replace('/', '#')
|
|
|
|
return conversation_dir + '/' + conversation_id
|
2021-10-14 15:12:35 +00:00
|
|
|
|
|
|
|
|
2021-12-29 21:55:09 +00:00
|
|
|
def update_conversation(base_dir: str, nickname: str, domain: str,
|
|
|
|
post_json_object: {}) -> bool:
|
2022-12-23 18:27:27 +00:00
|
|
|
"""Adds a post to a conversation index in the /conversation subdirectory
|
2021-10-14 15:12:35 +00:00
|
|
|
"""
|
2021-12-30 20:32:19 +00:00
|
|
|
conversation_filename = \
|
2021-12-29 21:55:09 +00:00
|
|
|
_get_conversation_filename(base_dir, nickname, domain,
|
|
|
|
post_json_object)
|
2021-12-30 20:32:19 +00:00
|
|
|
if not conversation_filename:
|
2021-10-14 15:12:35 +00:00
|
|
|
return False
|
2021-12-27 11:20:57 +00:00
|
|
|
post_id = remove_id_ending(post_json_object['object']['id'])
|
2021-12-30 20:32:19 +00:00
|
|
|
if not os.path.isfile(conversation_filename):
|
2021-08-12 10:26:24 +00:00
|
|
|
try:
|
2022-06-09 14:46:30 +00:00
|
|
|
with open(conversation_filename, 'w+',
|
|
|
|
encoding='utf-8') as conv_file:
|
2021-12-30 20:32:19 +00:00
|
|
|
conv_file.write(post_id + '\n')
|
2021-08-12 10:26:24 +00:00
|
|
|
return True
|
2021-11-25 18:42:38 +00:00
|
|
|
except OSError:
|
2021-12-29 21:55:09 +00:00
|
|
|
print('EX: update_conversation ' +
|
2021-12-30 20:32:19 +00:00
|
|
|
'unable to write to ' + conversation_filename)
|
2022-06-10 11:43:33 +00:00
|
|
|
elif not text_in_file(post_id + '\n', conversation_filename):
|
2021-08-12 10:26:24 +00:00
|
|
|
try:
|
2022-06-09 14:46:30 +00:00
|
|
|
with open(conversation_filename, 'a+',
|
|
|
|
encoding='utf-8') as conv_file:
|
2021-12-30 20:32:19 +00:00
|
|
|
conv_file.write(post_id + '\n')
|
2021-08-12 10:26:24 +00:00
|
|
|
return True
|
2021-11-25 18:42:38 +00:00
|
|
|
except OSError:
|
2021-12-29 21:55:09 +00:00
|
|
|
print('EX: update_conversation 2 ' +
|
2021-12-30 20:32:19 +00:00
|
|
|
'unable to write to ' + conversation_filename)
|
2021-08-12 10:26:24 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
|
2021-12-29 21:55:09 +00:00
|
|
|
def mute_conversation(base_dir: str, nickname: str, domain: str,
|
2021-12-30 20:32:19 +00:00
|
|
|
conversation_id: str) -> None:
|
2021-08-12 10:26:24 +00:00
|
|
|
"""Mutes the given conversation
|
|
|
|
"""
|
2021-12-30 20:32:19 +00:00
|
|
|
conversation_dir = acct_dir(base_dir, nickname, domain) + '/conversation'
|
|
|
|
conversation_filename = \
|
|
|
|
conversation_dir + '/' + conversation_id.replace('/', '#')
|
|
|
|
if not os.path.isfile(conversation_filename):
|
2021-08-12 10:26:24 +00:00
|
|
|
return
|
2021-12-30 20:32:19 +00:00
|
|
|
if os.path.isfile(conversation_filename + '.muted'):
|
2021-08-12 10:26:24 +00:00
|
|
|
return
|
2021-11-25 18:42:38 +00:00
|
|
|
try:
|
2022-06-09 14:46:30 +00:00
|
|
|
with open(conversation_filename + '.muted', 'w+',
|
|
|
|
encoding='utf-8') as conv_file:
|
2021-12-30 20:32:19 +00:00
|
|
|
conv_file.write('\n')
|
2021-11-25 18:42:38 +00:00
|
|
|
except OSError:
|
2021-12-30 20:32:19 +00:00
|
|
|
print('EX: unable to write mute ' + conversation_filename)
|
2021-08-12 10:26:24 +00:00
|
|
|
|
|
|
|
|
2021-12-29 21:55:09 +00:00
|
|
|
def unmute_conversation(base_dir: str, nickname: str, domain: str,
|
2021-12-30 20:32:19 +00:00
|
|
|
conversation_id: str) -> None:
|
2021-08-12 10:26:24 +00:00
|
|
|
"""Unmutes the given conversation
|
|
|
|
"""
|
2021-12-30 20:32:19 +00:00
|
|
|
conversation_dir = acct_dir(base_dir, nickname, domain) + '/conversation'
|
|
|
|
conversation_filename = \
|
|
|
|
conversation_dir + '/' + conversation_id.replace('/', '#')
|
|
|
|
if not os.path.isfile(conversation_filename):
|
2021-08-12 10:26:24 +00:00
|
|
|
return
|
2021-12-30 20:32:19 +00:00
|
|
|
if not os.path.isfile(conversation_filename + '.muted'):
|
2021-08-12 10:26:24 +00:00
|
|
|
return
|
|
|
|
try:
|
2021-12-30 20:32:19 +00:00
|
|
|
os.remove(conversation_filename + '.muted')
|
2021-11-25 18:42:38 +00:00
|
|
|
except OSError:
|
2021-12-29 21:55:09 +00:00
|
|
|
print('EX: unmute_conversation unable to delete ' +
|
2021-12-30 20:32:19 +00:00
|
|
|
conversation_filename + '.muted')
|
2022-12-28 10:18:13 +00:00
|
|
|
|
|
|
|
|
2023-03-01 14:34:41 +00:00
|
|
|
def download_conversation_posts(authorized: bool, session,
|
|
|
|
http_prefix: str, base_dir: str,
|
2022-12-28 10:18:13 +00:00
|
|
|
nickname: str, domain: str,
|
|
|
|
post_id: str, debug: bool) -> []:
|
|
|
|
"""Downloads all posts for a conversation and returns a list of the
|
|
|
|
json objects
|
|
|
|
"""
|
|
|
|
if '://' not in post_id:
|
|
|
|
return []
|
|
|
|
profile_str = 'https://www.w3.org/ns/activitystreams'
|
|
|
|
as_header = {
|
|
|
|
'Accept': 'application/ld+json; profile="' + profile_str + '"'
|
|
|
|
}
|
2022-12-28 10:18:58 +00:00
|
|
|
conversation_view = []
|
2022-12-28 10:18:13 +00:00
|
|
|
signing_priv_key_pem = get_instance_actor_key(base_dir, domain)
|
|
|
|
post_id = remove_id_ending(post_id)
|
|
|
|
post_filename = \
|
|
|
|
locate_post(base_dir, nickname, domain, post_id)
|
2023-03-01 14:34:41 +00:00
|
|
|
post_json_object = None
|
2022-12-28 10:18:13 +00:00
|
|
|
if post_filename:
|
2023-01-07 18:13:15 +00:00
|
|
|
post_json_object = load_json(post_filename)
|
2022-12-28 10:18:13 +00:00
|
|
|
else:
|
2023-03-01 14:34:41 +00:00
|
|
|
if authorized:
|
|
|
|
post_json_object = \
|
|
|
|
get_json(signing_priv_key_pem, session, post_id,
|
|
|
|
as_header, None, debug, __version__,
|
|
|
|
http_prefix, domain)
|
2022-12-28 10:18:13 +00:00
|
|
|
if debug:
|
2023-01-07 18:13:15 +00:00
|
|
|
if not post_json_object:
|
2022-12-28 10:18:13 +00:00
|
|
|
print(post_id + ' returned no json')
|
2023-01-07 18:13:15 +00:00
|
|
|
while post_json_object:
|
|
|
|
if not isinstance(post_json_object, dict):
|
2022-12-28 10:28:15 +00:00
|
|
|
break
|
2023-01-07 18:13:15 +00:00
|
|
|
if not has_object_dict(post_json_object):
|
|
|
|
if not post_json_object.get('attributedTo'):
|
|
|
|
print(str(post_json_object))
|
2022-12-28 10:18:13 +00:00
|
|
|
if debug:
|
|
|
|
print(post_id + ' has no attributedTo')
|
|
|
|
break
|
2023-01-07 18:13:15 +00:00
|
|
|
if not isinstance(post_json_object['attributedTo'], str):
|
2022-12-28 10:18:13 +00:00
|
|
|
break
|
2023-01-07 18:13:15 +00:00
|
|
|
if not post_json_object.get('published'):
|
2022-12-28 10:18:13 +00:00
|
|
|
if debug:
|
|
|
|
print(post_id + ' has no published date')
|
|
|
|
break
|
2023-01-07 18:13:15 +00:00
|
|
|
if not post_json_object.get('to'):
|
2022-12-28 10:18:13 +00:00
|
|
|
if debug:
|
|
|
|
print(post_id + ' has no "to" list')
|
|
|
|
break
|
2023-01-07 18:13:15 +00:00
|
|
|
if not isinstance(post_json_object['to'], list):
|
2022-12-28 10:18:13 +00:00
|
|
|
break
|
2023-01-07 18:13:15 +00:00
|
|
|
if 'cc' not in post_json_object:
|
2022-12-28 10:18:13 +00:00
|
|
|
if debug:
|
|
|
|
print(post_id + ' has no "cc" list')
|
|
|
|
break
|
2023-01-07 18:13:15 +00:00
|
|
|
if not isinstance(post_json_object['cc'], list):
|
2022-12-28 10:18:13 +00:00
|
|
|
break
|
|
|
|
wrapped_post = {
|
|
|
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
|
|
'id': post_id + '/activity',
|
|
|
|
'type': 'Create',
|
2023-01-07 18:13:15 +00:00
|
|
|
'actor': post_json_object['attributedTo'],
|
|
|
|
'published': post_json_object['published'],
|
|
|
|
'to': post_json_object['to'],
|
|
|
|
'cc': post_json_object['cc'],
|
|
|
|
'object': post_json_object
|
2022-12-28 10:18:13 +00:00
|
|
|
}
|
2023-01-07 18:13:15 +00:00
|
|
|
post_json_object = wrapped_post
|
|
|
|
if not post_json_object['object'].get('published'):
|
2022-12-30 17:32:49 +00:00
|
|
|
break
|
2023-01-07 11:07:34 +00:00
|
|
|
|
2023-01-07 11:45:19 +00:00
|
|
|
# render harmless any dangerous markup
|
2023-01-07 18:13:15 +00:00
|
|
|
harmless_markup(post_json_object)
|
2023-01-07 11:07:34 +00:00
|
|
|
|
2023-01-07 18:13:15 +00:00
|
|
|
conversation_view = [post_json_object] + conversation_view
|
2023-03-01 14:34:41 +00:00
|
|
|
if not authorized:
|
|
|
|
# only show a single post to non-authorized viewers
|
|
|
|
break
|
2023-01-07 18:13:15 +00:00
|
|
|
if not post_json_object['object'].get('inReplyTo'):
|
2022-12-28 10:18:13 +00:00
|
|
|
if debug:
|
|
|
|
print(post_id + ' is not a reply')
|
|
|
|
break
|
2023-01-07 18:13:15 +00:00
|
|
|
post_id = post_json_object['object']['inReplyTo']
|
2022-12-28 10:18:13 +00:00
|
|
|
post_id = remove_id_ending(post_id)
|
|
|
|
post_filename = \
|
|
|
|
locate_post(base_dir, nickname, domain, post_id)
|
2023-03-01 14:34:41 +00:00
|
|
|
post_json_object = None
|
2022-12-28 10:18:13 +00:00
|
|
|
if post_filename:
|
2023-01-07 18:13:15 +00:00
|
|
|
post_json_object = load_json(post_filename)
|
2022-12-28 10:18:13 +00:00
|
|
|
else:
|
2023-03-01 14:34:41 +00:00
|
|
|
if authorized:
|
|
|
|
post_json_object = \
|
|
|
|
get_json(signing_priv_key_pem, session, post_id,
|
|
|
|
as_header, None, debug, __version__,
|
|
|
|
http_prefix, domain)
|
2022-12-28 10:18:13 +00:00
|
|
|
if debug:
|
2023-01-07 18:13:15 +00:00
|
|
|
if not post_json_object:
|
2022-12-28 10:18:13 +00:00
|
|
|
print(post_id + ' returned no json')
|
2022-12-28 10:18:58 +00:00
|
|
|
return conversation_view
|