Merge branch 'main' of gitlab.com:bashrc2/epicyon

main
Bob Mottram 2022-12-23 22:35:56 +00:00
commit 93dfb820c8
6 changed files with 304 additions and 5 deletions

View File

@ -34,7 +34,7 @@ def _get_conversation_filename(base_dir: str, nickname: str, domain: str,
def update_conversation(base_dir: str, nickname: str, domain: str,
post_json_object: {}) -> bool:
"""Ads a post to a conversation index in the /conversation subdirectory
"""Adds a post to a conversation index in the /conversation subdirectory
"""
conversation_filename = \
_get_conversation_filename(base_dir, nickname, domain,

View File

@ -221,6 +221,7 @@ from webapp_suspended import html_suspended
from webapp_tos import html_terms_of_service
from webapp_confirm import html_confirm_follow
from webapp_confirm import html_confirm_unfollow
from webapp_post import html_conversation_thread
from webapp_post import html_emoji_reaction_picker
from webapp_post import html_post_replies
from webapp_post import html_individual_post
@ -11801,6 +11802,72 @@ class PubServer(BaseHTTPRequestHandler):
self._redirect_headers(actor_absolute, cookie, calling_domain)
return True
def _show_conversation_thread(self, authorized: bool,
calling_domain: str, path: str,
base_dir: str, http_prefix: str,
domain: str, port: int,
debug: str, curr_session) -> bool:
"""get conversation thread from the date link on a post
"""
if not authorized:
return False
if not path.startswith('/users/'):
return False
if '?convthread=' not in path:
return False
post_id = path.split('?convthread=')[1].strip()
post_id = post_id.replace('--', '/')
nickname = path.split('/users/')[1]
if '?convthread=' in nickname:
nickname = nickname.split('?convthread=')[0]
if '/' in nickname:
nickname = nickname.split('/')[0]
timezone = None
if self.server.account_timezone.get(nickname):
timezone = \
self.server.account_timezone.get(nickname)
bold_reading = False
if self.server.bold_reading.get(nickname):
bold_reading = True
conv_str = \
html_conversation_thread(post_id, self.server.translate,
base_dir,
http_prefix,
nickname,
domain,
self.server.project_version,
self.server.recent_posts_cache,
self.server.max_recent_posts,
curr_session,
self.server.cached_webfingers,
self.server.person_cache,
port,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
timezone, bold_reading,
self.server.dogwhistles,
self.server.access_keys,
self.server.min_images_for_accounts,
self.server.debug)
if conv_str:
msg = conv_str.encode('utf-8')
msglen = len(msg)
self._login_headers('text/html',
msglen, calling_domain)
self._write(msg)
self._404()
self.server.getreq_busy = False
return True
def _show_individual_at_post(self, ssml_getreq: bool, authorized: bool,
calling_domain: str, referer_domain: str,
path: str,
@ -16419,6 +16486,19 @@ class PubServer(BaseHTTPRequestHandler):
self.server.debug)
return
if self._show_conversation_thread(authorized,
calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
self.server.debug,
self.server.session):
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', '_show_conversation_thread',
self.server.debug)
return
# shared items catalog for this instance
# this is only accessible to instance members or to
# other instances which present an authorization token

View File

@ -26,6 +26,7 @@ from roles import set_role
from webfinger import webfinger_handle
from bookmarks import send_bookmark_via_server
from bookmarks import send_undo_bookmark_via_server
from posts import download_conversation_posts
from posts import set_post_expiry_days
from posts import get_instance_actor_key
from posts import send_mute_via_server
@ -183,6 +184,9 @@ def _command_options() -> None:
parser.add_argument('-n', '--nickname', dest='nickname', type=str,
default=None,
help='Nickname of the account to use')
parser.add_argument('--conversation', dest='conversation', type=str,
default=None,
help='Download a conversation for the given post id')
parser.add_argument('--screenreader', dest='screenreader', type=str,
default=None,
help='Name of the screen reader: ' +
@ -1112,7 +1116,12 @@ def _command_options() -> None:
sys.exit()
if argb.json:
session = create_session(None)
proxy_type = None
if '.onion/' in argb.json:
proxy_type = 'tor'
elif '.i2p/' in argb.json:
proxy_type = 'i2p'
session = create_session(proxy_type)
profile_str = 'https://www.w3.org/ns/activitystreams'
as_header = {
'Accept': 'application/ld+json; profile="' + profile_str + '"'
@ -1136,6 +1145,38 @@ def _command_options() -> None:
pprint(test_json)
sys.exit()
if argb.conversation:
post_id = argb.conversation
if '://' not in post_id:
print('--conversation should be the url of a post')
sys.exit()
proxy_type = None
if '.onion/' in post_id:
proxy_type = 'tor'
elif '.i2p/' in post_id:
proxy_type = 'i2p'
session = create_session(proxy_type)
if not argb.domain:
argb.domain = get_config_param(base_dir, 'domain')
domain = ''
if argb.domain:
domain = argb.domain
if not domain:
print('Please specify a domain with the --domain option')
sys.exit()
nickname = ''
if argb.nickname:
nickname = argb.nickname
if not nickname:
print('Please specify a nickname with the --nickname option')
sys.exit()
conv_json = download_conversation_posts(session, http_prefix,
base_dir, nickname, domain,
post_id, argb.debug)
if conv_json:
pprint(conv_json)
sys.exit()
if argb.ssml:
session = create_session(None)
profile_str = 'https://www.w3.org/ns/activitystreams'

View File

@ -6035,3 +6035,86 @@ def set_max_profile_posts(base_dir: str, nickname: str, domain: str,
max_posts_filename)
return False
return True
def download_conversation_posts(session, http_prefix: str, base_dir: str,
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 + '"'
}
conversation_thread = []
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)
if post_filename:
post_json = load_json(post_filename)
else:
post_json = get_json(signing_priv_key_pem, session, post_id,
as_header, None, debug, __version__,
http_prefix, domain)
if debug:
if not post_json:
print(post_id + ' returned no json')
while post_json:
if not has_object_dict(post_json):
if not post_json.get('attributedTo'):
print(str(post_json))
if debug:
print(post_id + ' has no attributedTo')
break
if not isinstance(post_json['attributedTo'], str):
break
if not post_json.get('published'):
if debug:
print(post_id + ' has no published date')
break
if not post_json.get('to'):
if debug:
print(post_id + ' has no "to" list')
break
if not isinstance(post_json['to'], list):
break
if 'cc' not in post_json:
if debug:
print(post_id + ' has no "cc" list')
break
if not isinstance(post_json['cc'], list):
break
wrapped_post = {
"@context": "https://www.w3.org/ns/activitystreams",
'id': post_id + '/activity',
'type': 'Create',
'actor': post_json['attributedTo'],
'published': post_json['published'],
'to': post_json['to'],
'cc': post_json['cc'],
'object': post_json
}
post_json = wrapped_post
conversation_thread = [post_json] + conversation_thread
if not post_json['object'].get('inReplyTo'):
if debug:
print(post_id + ' is not a reply')
break
post_id = post_json['object']['inReplyTo']
post_id = remove_id_ending(post_id)
post_filename = \
locate_post(base_dir, nickname, domain, post_id)
if post_filename:
post_json = load_json(post_filename)
else:
post_json = get_json(signing_priv_key_pem, session, post_id,
as_header, None, debug, __version__,
http_prefix, domain)
if debug:
if not post_json:
print(post_id + ' returned no json')
return conversation_thread

View File

@ -21,6 +21,7 @@ from announce import no_of_announces
from like import liked_by_person
from like import no_of_likes
from follow import is_following_actor
from posts import download_conversation_posts
from posts import post_is_muted
from posts import get_person_box
from posts import download_announce
@ -91,6 +92,7 @@ from webapp_utils import get_post_attachments_as_html
from webapp_utils import html_header_with_external_style
from webapp_utils import html_footer
from webapp_utils import get_broken_link_substitute
from webapp_utils import html_post_separator
from webapp_media import add_embedded_elements
from webapp_question import insert_question
from devices import e2e_edecrypt_message_from_device
@ -1541,7 +1543,8 @@ def _get_footer_with_icons(show_icons: bool,
bookmark_str: str,
delete_str: str, mute_str: str, edit_str: str,
post_json_object: {}, published_link: str,
time_class: str, published_str: str) -> str:
time_class: str, published_str: str,
nickname: str) -> str:
"""Returns the html for a post footer containing icons
"""
if not show_icons:
@ -1553,7 +1556,11 @@ def _get_footer_with_icons(show_icons: bool,
reply_str + announce_str + like_str + bookmark_str + reaction_str
footer_str += delete_str + mute_str + edit_str
if not is_news_post(post_json_object):
footer_str += ' <a href="' + published_link + '" class="' + \
date_link = published_link
if post_json_object['object'].get('inReplyTo'):
date_link = '/users/' + nickname + '?convthread=' + \
published_link.replace('/', '--')
footer_str += ' <a href="' + date_link + '" class="' + \
time_class + '" tabindex="10"><span itemprop="datePublished">' + \
published_str + '</span></a>\n'
else:
@ -2289,7 +2296,7 @@ def individual_post_as_html(signing_priv_key_pem: str,
like_str, reaction_str, bookmark_str,
delete_str, mute_str, edit_str,
post_json_object, published_link,
time_class, published_str)
time_class, published_str, nickname)
if new_footer_str:
footer_str = new_footer_str
@ -2841,3 +2848,90 @@ def html_emoji_reaction_picker(recent_posts_cache: {}, max_recent_posts: int,
'</header>\n'
return header_str + reacted_to_post_str + emoji_picks_str + html_footer()
def html_conversation_thread(post_id: str,
translate: {}, base_dir: str,
http_prefix: str,
nickname: str, domain: str,
project_version: str,
recent_posts_cache: {},
max_recent_posts: int,
session,
cached_webfingers,
person_cache: {},
port: int,
yt_replace_domain: str,
twitter_replacement_domain: str,
show_published_date_only: bool,
peertube_instances: [],
allow_local_network_access: bool,
theme_name: str,
system_language: str,
max_like_count: int,
signing_priv_key_pem: str,
cw_lists: {},
lists_enabled: str,
timezone: str, bold_reading: bool,
dogwhistles: {}, access_keys: {},
min_images_for_accounts: [],
debug: bool) -> str:
"""Show a page containing a conversation thread
"""
conv_posts = \
download_conversation_posts(session, http_prefix, base_dir,
nickname, domain,
post_id, debug)
css_filename = base_dir + '/epicyon-profile.css'
if os.path.isfile(base_dir + '/epicyon.css'):
css_filename = base_dir + '/epicyon.css'
instance_title = \
get_config_param(base_dir, 'instanceTitle')
conv_str = \
html_header_with_external_style(css_filename, instance_title, None)
if not conv_posts:
conv_str += html_footer()
return conv_str
separator_str = html_post_separator(base_dir, None)
minimize_all_images = False
if nickname in min_images_for_accounts:
minimize_all_images = True
for post_json_object in conv_posts:
show_individual_post_icons = False
allow_deletion = False
post_str = \
individual_post_as_html(signing_priv_key_pem,
True, recent_posts_cache,
max_recent_posts,
translate, None,
base_dir, session, cached_webfingers,
person_cache,
nickname, domain, port,
post_json_object,
None, True, allow_deletion,
http_prefix, project_version,
'search',
yt_replace_domain,
twitter_replacement_domain,
show_published_date_only,
peertube_instances,
allow_local_network_access,
theme_name, system_language,
max_like_count,
show_individual_post_icons,
show_individual_post_icons,
False, False, False, False,
cw_lists, lists_enabled,
timezone, False, bold_reading,
dogwhistles,
minimize_all_images)
if post_str:
conv_str += separator_str + post_str
conv_str += html_footer()
return conv_str

View File

@ -749,6 +749,7 @@ def html_history_search(translate: {}, base_dir: str,
history_search_form += \
'<center><h5>' + translate['No results'] + \
'</h5></center>'
history_search_form += html_footer()
return history_search_form
separator_str = html_post_separator(base_dir, None)