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

main
Bob Mottram 2022-07-05 21:01:56 +01:00
commit ab9257fbb5
42 changed files with 546 additions and 162 deletions

View File

@ -1008,6 +1008,147 @@ def _auto_tag(base_dir: str, nickname: str, domain: str,
append_tags.append('#' + tag_name) append_tags.append('#' + tag_name)
def _get_simplified_content(content: str) -> str:
"""Returns a simplified version of the content suitable for
splitting up into individual words
"""
content_simplified = \
content.replace(',', ' ').replace(';', ' ').replace('- ', ' ')
content_simplified = content_simplified.replace('. ', ' ').strip()
if content_simplified.endswith('.'):
content_simplified = content_simplified[:len(content_simplified)-1]
return content_simplified
def detect_dogwhistles(content: str, dogwhistles: {}) -> {}:
"""Returns a dict containing any detected dogwhistle words
"""
content = remove_html(content).lower()
result = {}
words = _get_simplified_content(content).split(' ')
for whistle, category in dogwhistles.items():
if not category:
continue
ending = False
starting = False
whistle = whistle.lower()
if whistle.startswith('x-'):
whistle = whistle[2:]
ending = True
elif (whistle.startswith('*') or
whistle.startswith('~') or
whistle.startswith('-')):
whistle = whistle[1:]
ending = True
if ending:
prev_wrd = ''
for wrd in words:
wrd2 = (prev_wrd + ' ' + wrd).strip()
if wrd.endswith(whistle) or wrd2.endswith(whistle):
if not result.get(whistle):
result[whistle] = {
"count": 1,
"category": category
}
else:
result[whistle]['count'] += 1
prev_wrd = wrd
continue
if whistle.lower().endswith('-x'):
whistle = whistle[:len(whistle)-2]
starting = True
elif (whistle.endswith('*') or
whistle.endswith('~') or
whistle.endswith('-')):
whistle = whistle[:len(whistle)-1]
starting = True
if starting:
prev_wrd = ''
for wrd in words:
wrd2 = (prev_wrd + ' ' + wrd).strip()
if wrd.startswith(whistle) or wrd2.startswith(whistle):
if not result.get(whistle):
result[whistle] = {
"count": 1,
"category": category
}
else:
result[whistle]['count'] += 1
prev_wrd = wrd
continue
if '*' in whistle:
whistle_start = whistle.split('*', 1)[0]
whistle_end = whistle.split('*', 1)[1]
prev_wrd = ''
for wrd in words:
wrd2 = (prev_wrd + ' ' + wrd).strip()
if ((wrd.startswith(whistle_start) and
wrd.endswith(whistle_end)) or
(wrd2.startswith(whistle_start) and
wrd2.endswith(whistle_end))):
if not result.get(whistle):
result[whistle] = {
"count": 1,
"category": category
}
else:
result[whistle]['count'] += 1
prev_wrd = wrd
continue
prev_wrd = ''
for wrd in words:
wrd2 = (prev_wrd + ' ' + wrd).strip()
if whistle in (wrd, wrd2):
if not result.get(whistle):
result[whistle] = {
"count": 1,
"category": category
}
else:
result[whistle]['count'] += 1
prev_wrd = wrd
return result
def load_dogwhistles(filename: str) -> {}:
"""Loads a list of dogwhistles from file
"""
if not os.path.isfile(filename):
return {}
dogwhistle_lines = []
try:
with open(filename, 'r', encoding='utf-8') as fp_dogwhistles:
dogwhistle_lines = fp_dogwhistles.readlines()
except OSError:
print('EX: unable to load dogwhistles from ' + filename)
return {}
separators = ('->', '=>', ',', ';', '|', '=')
dogwhistles = {}
for line in dogwhistle_lines:
line = remove_eol(line).strip()
if not line:
continue
if line.startswith('#'):
continue
whistle = None
category = None
for sep in separators:
if sep in line:
whistle = line.split(sep, 1)[0].strip()
category = line.split(sep, 1)[1].strip()
break
if not whistle:
whistle = line
dogwhistles[whistle] = category
return dogwhistles
def add_html_tags(base_dir: str, http_prefix: str, def add_html_tags(base_dir: str, http_prefix: str,
nickname: str, domain: str, content: str, nickname: str, domain: str, content: str,
recipients: [], hashtags: {}, recipients: [], hashtags: {},
@ -1022,12 +1163,7 @@ def add_html_tags(base_dir: str, http_prefix: str,
content = content.replace('\r', '') content = content.replace('\r', '')
content = content.replace('\n', ' --linebreak-- ') content = content.replace('\n', ' --linebreak-- ')
content = _add_music_tag(content, 'nowplaying') content = _add_music_tag(content, 'nowplaying')
content_simplified = \ words = _get_simplified_content(content).split(' ')
content.replace(',', ' ').replace(';', ' ').replace('- ', ' ')
content_simplified = content_simplified.replace('. ', ' ').strip()
if content_simplified.endswith('.'):
content_simplified = content_simplified[:len(content_simplified)-1]
words = content_simplified.split(' ')
# remove . for words which are not mentions # remove . for words which are not mentions
new_words = [] new_words = []

140
daemon.py
View File

@ -326,6 +326,7 @@ from utils import has_group_type
from manualapprove import manual_deny_follow_request_thread from manualapprove import manual_deny_follow_request_thread
from manualapprove import manual_approve_follow_request_thread from manualapprove import manual_approve_follow_request_thread
from announce import create_announce from announce import create_announce
from content import load_dogwhistles
from content import valid_url_lengths from content import valid_url_lengths
from content import contains_invalid_local_links from content import contains_invalid_local_links
from content import get_price_from_string from content import get_price_from_string
@ -1595,7 +1596,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.max_recent_posts, self.server.max_recent_posts,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
self.server.content_license_url) self.server.content_license_url,
self.server.dogwhistles)
def _get_outbox_thread_index(self, nickname: str, def _get_outbox_thread_index(self, nickname: str,
max_outbox_threads_per_account: int) -> int: max_outbox_threads_per_account: int) -> int:
@ -3182,7 +3184,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled, self.server.lists_enabled,
self.server.default_timeline, self.server.default_timeline,
reply_is_chat, reply_is_chat,
bold_reading).encode('utf-8') bold_reading,
self.server.dogwhistles).encode('utf-8')
msglen = len(msg) msglen = len(msg)
self._set_headers('text/html', msglen, self._set_headers('text/html', msglen,
cookie, calling_domain, False) cookie, calling_domain, False)
@ -3326,7 +3329,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled, self.server.lists_enabled,
self.server.default_timeline, self.server.default_timeline,
reply_is_chat, reply_is_chat,
bold_reading).encode('utf-8') bold_reading,
self.server.dogwhistles).encode('utf-8')
msglen = len(msg) msglen = len(msg)
self._set_headers('text/html', msglen, self._set_headers('text/html', msglen,
cookie, calling_domain, False) cookie, calling_domain, False)
@ -3888,7 +3892,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, bold_reading) timezone, bold_reading,
self.server.dogwhistles)
if hashtag_str: if hashtag_str:
msg = hashtag_str.encode('utf-8') msg = hashtag_str.encode('utf-8')
msglen = len(msg) msglen = len(msg)
@ -3993,7 +3998,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, bold_reading) timezone, bold_reading,
self.server.dogwhistles)
if history_str: if history_str:
msg = history_str.encode('utf-8') msg = history_str.encode('utf-8')
msglen = len(msg) msglen = len(msg)
@ -4073,7 +4079,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, bold_reading) timezone, bold_reading,
self.server.dogwhistles)
if bookmarks_str: if bookmarks_str:
msg = bookmarks_str.encode('utf-8') msg = bookmarks_str.encode('utf-8')
msglen = len(msg) msglen = len(msg)
@ -4243,7 +4250,8 @@ class PubServer(BaseHTTPRequestHandler):
timezone, timezone,
self.server.onion_domain, self.server.onion_domain,
self.server.i2p_domain, self.server.i2p_domain,
bold_reading) bold_reading,
self.server.dogwhistles)
if profile_str: if profile_str:
msg = profile_str.encode('utf-8') msg = profile_str.encode('utf-8')
msglen = len(msg) msglen = len(msg)
@ -5098,6 +5106,27 @@ class PubServer(BaseHTTPRequestHandler):
print('EX: _newswire_update unable to delete ' + print('EX: _newswire_update unable to delete ' +
filter_newswire_filename) filter_newswire_filename)
# save dogwhistle words list
dogwhistles_filename = base_dir + '/accounts/dogwhistles.txt'
if fields.get('dogwhistleWords'):
try:
with open(dogwhistles_filename, 'w+',
encoding='utf-8') as fp_dogwhistles:
fp_dogwhistles.write(fields['dogwhistleWords'])
except OSError:
print('EX: unable to write ' + dogwhistles_filename)
self.server.dogwhistles = \
load_dogwhistles(dogwhistles_filename)
else:
# save an empty file
try:
with open(dogwhistles_filename, 'w+',
encoding='utf-8') as fp_dogwhistles:
fp_dogwhistles.write('')
except OSError:
print('EX: unable to write ' + dogwhistles_filename)
self.server.dogwhistles = {}
# save news tagging rules # save news tagging rules
hashtag_rules_filename = \ hashtag_rules_filename = \
base_dir + '/accounts/hashtagrules.txt' base_dir + '/accounts/hashtagrules.txt'
@ -8471,7 +8500,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, bold_reading) timezone, bold_reading,
self.server.dogwhistles)
if hashtag_str: if hashtag_str:
msg = hashtag_str.encode('utf-8') msg = hashtag_str.encode('utf-8')
msglen = len(msg) msglen = len(msg)
@ -8719,7 +8749,8 @@ class PubServer(BaseHTTPRequestHandler):
False, True, False, False, True, False,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, mitm, bold_reading) timezone, mitm, bold_reading,
self.server.dogwhistles)
actor_absolute = self._get_instance_url(calling_domain) + actor actor_absolute = self._get_instance_url(calling_domain) + actor
actor_path_str = \ actor_path_str = \
@ -9265,7 +9296,8 @@ class PubServer(BaseHTTPRequestHandler):
False, True, False, False, True, False,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, mitm, bold_reading) timezone, mitm, bold_reading,
self.server.dogwhistles)
else: else:
print('WARN: Liked post not found: ' + liked_post_filename) print('WARN: Liked post not found: ' + liked_post_filename)
# clear the icon from the cache so that it gets updated # clear the icon from the cache so that it gets updated
@ -9450,7 +9482,8 @@ class PubServer(BaseHTTPRequestHandler):
False, True, False, False, True, False,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, mitm, bold_reading) timezone, mitm, bold_reading,
self.server.dogwhistles)
else: else:
print('WARN: Unliked post not found: ' + liked_post_filename) print('WARN: Unliked post not found: ' + liked_post_filename)
# clear the icon from the cache so that it gets updated # clear the icon from the cache so that it gets updated
@ -9664,7 +9697,8 @@ class PubServer(BaseHTTPRequestHandler):
False, True, False, False, True, False,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, mitm, bold_reading) timezone, mitm, bold_reading,
self.server.dogwhistles)
else: else:
print('WARN: Emoji reaction post not found: ' + print('WARN: Emoji reaction post not found: ' +
reaction_post_filename) reaction_post_filename)
@ -9868,7 +9902,8 @@ class PubServer(BaseHTTPRequestHandler):
False, True, False, False, True, False,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, mitm, bold_reading) timezone, mitm, bold_reading,
self.server.dogwhistles)
else: else:
print('WARN: Unreaction post not found: ' + print('WARN: Unreaction post not found: ' +
reaction_post_filename) reaction_post_filename)
@ -9974,7 +10009,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timeline_str, page_number, timeline_str, page_number,
timezone, bold_reading) timezone, bold_reading,
self.server.dogwhistles)
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
self._set_headers('text/html', msglen, self._set_headers('text/html', msglen,
@ -10116,7 +10152,8 @@ class PubServer(BaseHTTPRequestHandler):
False, True, False, False, True, False,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, mitm, bold_reading) timezone, mitm, bold_reading,
self.server.dogwhistles)
else: else:
print('WARN: Bookmarked post not found: ' + bookmark_filename) print('WARN: Bookmarked post not found: ' + bookmark_filename)
# self._post_to_outbox(bookmark_json, # self._post_to_outbox(bookmark_json,
@ -10265,7 +10302,8 @@ class PubServer(BaseHTTPRequestHandler):
False, True, False, False, True, False,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, mitm, bold_reading) timezone, mitm, bold_reading,
self.server.dogwhistles)
else: else:
print('WARN: Unbookmarked post not found: ' + print('WARN: Unbookmarked post not found: ' +
bookmark_filename) bookmark_filename)
@ -10375,7 +10413,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.max_like_count, self.server.max_like_count,
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled) self.server.lists_enabled,
self.server.dogwhistles)
if delete_str: if delete_str:
delete_str_len = len(delete_str) delete_str_len = len(delete_str)
self._set_headers('text/html', delete_str_len, self._set_headers('text/html', delete_str_len,
@ -10502,7 +10541,8 @@ class PubServer(BaseHTTPRequestHandler):
use_cache_only, use_cache_only,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, mitm, bold_reading) timezone, mitm, bold_reading,
self.server.dogwhistles)
else: else:
print('WARN: Muted post not found: ' + mute_filename) print('WARN: Muted post not found: ' + mute_filename)
@ -10629,7 +10669,8 @@ class PubServer(BaseHTTPRequestHandler):
use_cache_only, use_cache_only,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, mitm, bold_reading) timezone, mitm, bold_reading,
self.server.dogwhistles)
else: else:
print('WARN: Unmuted post not found: ' + mute_filename) print('WARN: Unmuted post not found: ' + mute_filename)
if calling_domain.endswith('.onion') and onion_domain: if calling_domain.endswith('.onion') and onion_domain:
@ -10758,7 +10799,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, bold_reading) timezone, bold_reading,
self.server.dogwhistles)
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
self._set_headers('text/html', msglen, self._set_headers('text/html', msglen,
@ -10862,7 +10904,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, bold_reading) timezone, bold_reading,
self.server.dogwhistles)
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
self._set_headers('text/html', msglen, self._set_headers('text/html', msglen,
@ -11316,7 +11359,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
'inbox', self.server.default_timeline, 'inbox', self.server.default_timeline,
bold_reading) bold_reading, self.server.dogwhistles)
if not msg: if not msg:
self._404() self._404()
return True return True
@ -11383,7 +11426,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
'inbox', self.server.default_timeline, 'inbox', self.server.default_timeline,
bold_reading, 'shares') bold_reading, self.server.dogwhistles,
'shares')
if not msg: if not msg:
self._404() self._404()
return True return True
@ -11472,7 +11516,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, mitm, bold_reading) timezone, mitm, bold_reading,
self.server.dogwhistles)
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
self._set_headers('text/html', msglen, self._set_headers('text/html', msglen,
@ -11788,7 +11833,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, bold_reading) timezone, bold_reading,
self.server.dogwhistles)
if getreq_start_time: if getreq_start_time:
fitness_performance(getreq_start_time, fitness_performance(getreq_start_time,
self.server.fitness, self.server.fitness,
@ -11959,7 +12005,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, bold_reading) timezone, bold_reading,
self.server.dogwhistles)
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
self._set_headers('text/html', msglen, self._set_headers('text/html', msglen,
@ -12119,7 +12166,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, bold_reading) timezone, bold_reading,
self.server.replies)
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
self._set_headers('text/html', msglen, self._set_headers('text/html', msglen,
@ -12277,7 +12325,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, bold_reading) timezone, bold_reading,
self.server.dogwhistles)
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
self._set_headers('text/html', msglen, self._set_headers('text/html', msglen,
@ -12435,7 +12484,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, bold_reading) timezone, bold_reading,
self.server.dogwhistles)
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
self._set_headers('text/html', msglen, self._set_headers('text/html', msglen,
@ -12602,7 +12652,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, bold_reading) timezone, bold_reading,
self.server.dogwhistles)
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
self._set_headers('text/html', msglen, self._set_headers('text/html', msglen,
@ -12772,7 +12823,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, bold_reading) timezone, bold_reading,
self.server.dogwhistles)
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
self._set_headers('text/html', msglen, self._set_headers('text/html', msglen,
@ -12892,7 +12944,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, timezone, self.server.lists_enabled, timezone,
bold_reading) bold_reading, self.server.dogwhistles)
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
self._set_headers('text/html', msglen, self._set_headers('text/html', msglen,
@ -12986,7 +13038,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, bold_reading) timezone, bold_reading,
self.server.dogwhistles)
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
self._set_headers('text/html', msglen, self._set_headers('text/html', msglen,
@ -13123,7 +13176,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, bold_reading) timezone, bold_reading,
self.server.dogwhistles)
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
self._set_headers('text/html', msglen, self._set_headers('text/html', msglen,
@ -13274,7 +13328,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, bold_reading) timezone, bold_reading,
self.server.dogwhistles)
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
self._set_headers('text/html', msglen, self._set_headers('text/html', msglen,
@ -13422,7 +13477,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem, self.server.signing_priv_key_pem,
self.server.cw_lists, self.server.cw_lists,
self.server.lists_enabled, self.server.lists_enabled,
timezone, bold_reading) timezone, bold_reading,
self.server.dogwhistles)
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
self._set_headers('text/html', msglen, self._set_headers('text/html', msglen,
@ -14799,7 +14855,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled, self.server.lists_enabled,
self.server.default_timeline, self.server.default_timeline,
reply_is_chat, reply_is_chat,
bold_reading).encode('utf-8') bold_reading,
self.server.dogwhistles).encode('utf-8')
if not msg: if not msg:
print('Error replying to ' + in_reply_to_url) print('Error replying to ' + in_reply_to_url)
self._404() self._404()
@ -14942,7 +14999,8 @@ class PubServer(BaseHTTPRequestHandler):
http_prefix, http_prefix,
self.server.default_timeline, self.server.default_timeline,
self.server.theme_name, self.server.theme_name,
access_keys) access_keys,
self.server.dogwhistles)
if msg: if msg:
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
@ -20967,6 +21025,12 @@ def run_daemon(preferred_podcast_formats: [],
# scan the theme directory for any svg files containing scripts # scan the theme directory for any svg files containing scripts
assert not scan_themes_for_scripts(base_dir) assert not scan_themes_for_scripts(base_dir)
# load a list of dogwhistle words
dogwhistles_filename = base_dir + '/accounts/dogwhistles.txt'
if not os.path.isfile(dogwhistles_filename):
dogwhistles_filename = base_dir + '/default_dogwhistles.txt'
httpd.dogwhistles = load_dogwhistles(dogwhistles_filename)
# list of preferred podcast formats # list of preferred podcast formats
# eg ['audio/opus', 'audio/mp3'] # eg ['audio/opus', 'audio/mp3']
httpd.preferred_podcast_formats = preferred_podcast_formats httpd.preferred_podcast_formats = preferred_podcast_formats

View File

@ -0,0 +1,15 @@
X-pilled, alt-right terminology
soy boy, alt-right terminology
soyboy, alt-right terminology
soyboi, alt-right terminology
kek, alt-right terminology
groyper, alt-right meme
chad, alt-right meme
globalist*, antisemitism
globalism, antisemitism
fren, alt-right terminology
cuck*, alt-right terminology
*1488, nazism
nazbol, nazism
*1290, antisemitism

View File

@ -131,6 +131,7 @@ from conversation import update_conversation
from webapp_hashtagswarm import html_hash_tag_swarm from webapp_hashtagswarm import html_hash_tag_swarm
from person import valid_sending_actor from person import valid_sending_actor
from fitnessFunctions import fitness_performance from fitnessFunctions import fitness_performance
from content import load_dogwhistles
from content import valid_url_lengths from content import valid_url_lengths
from content import remove_script from content import remove_script
@ -408,7 +409,8 @@ def _inbox_store_post_to_html_cache(recent_posts_cache: {},
lists_enabled: str, lists_enabled: str,
timezone: str, timezone: str,
mitm: bool, mitm: bool,
bold_reading: bool) -> None: bold_reading: bool,
dogwhistles: {}) -> None:
"""Converts the json post into html and stores it in a cache """Converts the json post into html and stores it in a cache
This enables the post to be quickly displayed later This enables the post to be quickly displayed later
""" """
@ -434,7 +436,7 @@ def _inbox_store_post_to_html_cache(recent_posts_cache: {},
theme_name, system_language, max_like_count, theme_name, system_language, max_like_count,
not_dm, True, True, False, True, False, not_dm, True, True, False, True, False,
cw_lists, lists_enabled, timezone, mitm, cw_lists, lists_enabled, timezone, mitm,
bold_reading) bold_reading, dogwhistles)
def valid_inbox(base_dir: str, nickname: str, domain: str) -> bool: def valid_inbox(base_dir: str, nickname: str, domain: str) -> bool:
@ -1106,7 +1108,7 @@ def _receive_edit_to_post(recent_posts_cache: {}, message_json: {},
show_published_date_only: bool, show_published_date_only: bool,
peertube_instances: [], peertube_instances: [],
theme_name: str, max_like_count: int, theme_name: str, max_like_count: int,
cw_lists: {}) -> bool: cw_lists: {}, dogwhistles: {}) -> bool:
"""A post was edited """A post was edited
""" """
if not has_object_dict(message_json): if not has_object_dict(message_json):
@ -1230,7 +1232,7 @@ def _receive_edit_to_post(recent_posts_cache: {}, message_json: {},
manually_approve_followers, manually_approve_followers,
False, True, False, cw_lists, False, True, False, cw_lists,
lists_enabled, timezone, mitm, lists_enabled, timezone, mitm,
bold_reading) bold_reading, dogwhistles)
return True return True
@ -1250,7 +1252,7 @@ def _receive_update_activity(recent_posts_cache: {}, session, base_dir: str,
show_published_date_only: bool, show_published_date_only: bool,
peertube_instances: [], peertube_instances: [],
theme_name: str, max_like_count: int, theme_name: str, max_like_count: int,
cw_lists: {}) -> bool: cw_lists: {}, dogwhistles: {}) -> bool:
"""Receives an Update activity within the POST section of HTTPServer """Receives an Update activity within the POST section of HTTPServer
""" """
@ -1290,7 +1292,7 @@ def _receive_update_activity(recent_posts_cache: {}, session, base_dir: str,
show_published_date_only, show_published_date_only,
peertube_instances, peertube_instances,
theme_name, max_like_count, theme_name, max_like_count,
cw_lists): cw_lists, dogwhistles):
print('EDITPOST: received ' + message_json['object']['id']) print('EDITPOST: received ' + message_json['object']['id'])
return True return True
else: else:
@ -1340,7 +1342,7 @@ def _receive_like(recent_posts_cache: {},
theme_name: str, system_language: str, theme_name: str, system_language: str,
max_like_count: int, cw_lists: {}, max_like_count: int, cw_lists: {},
lists_enabled: str, lists_enabled: str,
bold_reading: bool) -> bool: bold_reading: bool, dogwhistles: {}) -> bool:
"""Receives a Like activity within the POST section of HTTPServer """Receives a Like activity within the POST section of HTTPServer
""" """
if message_json['type'] != 'Like': if message_json['type'] != 'Like':
@ -1449,7 +1451,7 @@ def _receive_like(recent_posts_cache: {},
manually_approve_followers, manually_approve_followers,
False, True, False, cw_lists, False, True, False, cw_lists,
lists_enabled, timezone, mitm, lists_enabled, timezone, mitm,
bold_reading) bold_reading, dogwhistles)
return True return True
@ -1469,7 +1471,7 @@ def _receive_undo_like(recent_posts_cache: {},
theme_name: str, system_language: str, theme_name: str, system_language: str,
max_like_count: int, cw_lists: {}, max_like_count: int, cw_lists: {},
lists_enabled: str, lists_enabled: str,
bold_reading: bool) -> bool: bold_reading: bool, dogwhistles: {}) -> bool:
"""Receives an undo like activity within the POST section of HTTPServer """Receives an undo like activity within the POST section of HTTPServer
""" """
if message_json['type'] != 'Undo': if message_json['type'] != 'Undo':
@ -1568,7 +1570,7 @@ def _receive_undo_like(recent_posts_cache: {},
manually_approve_followers, manually_approve_followers,
False, True, False, cw_lists, False, True, False, cw_lists,
lists_enabled, timezone, mitm, lists_enabled, timezone, mitm,
bold_reading) bold_reading, dogwhistles)
return True return True
@ -1588,7 +1590,8 @@ def _receive_reaction(recent_posts_cache: {},
allow_local_network_access: bool, allow_local_network_access: bool,
theme_name: str, system_language: str, theme_name: str, system_language: str,
max_like_count: int, cw_lists: {}, max_like_count: int, cw_lists: {},
lists_enabled: str, bold_reading: bool) -> bool: lists_enabled: str, bold_reading: bool,
dogwhistles: {}) -> bool:
"""Receives an emoji reaction within the POST section of HTTPServer """Receives an emoji reaction within the POST section of HTTPServer
""" """
if message_json['type'] != 'EmojiReact': if message_json['type'] != 'EmojiReact':
@ -1719,7 +1722,7 @@ def _receive_reaction(recent_posts_cache: {},
manually_approve_followers, manually_approve_followers,
False, True, False, cw_lists, False, True, False, cw_lists,
lists_enabled, timezone, mitm, lists_enabled, timezone, mitm,
bold_reading) bold_reading, dogwhistles)
return True return True
@ -1741,7 +1744,7 @@ def _receive_undo_reaction(recent_posts_cache: {},
theme_name: str, system_language: str, theme_name: str, system_language: str,
max_like_count: int, cw_lists: {}, max_like_count: int, cw_lists: {},
lists_enabled: str, lists_enabled: str,
bold_reading: bool) -> bool: bold_reading: bool, dogwhistles: {}) -> bool:
"""Receives an undo emoji reaction within the POST section of HTTPServer """Receives an undo emoji reaction within the POST section of HTTPServer
""" """
if message_json['type'] != 'Undo': if message_json['type'] != 'Undo':
@ -1858,7 +1861,7 @@ def _receive_undo_reaction(recent_posts_cache: {},
manually_approve_followers, manually_approve_followers,
False, True, False, cw_lists, False, True, False, cw_lists,
lists_enabled, timezone, mitm, lists_enabled, timezone, mitm,
bold_reading) bold_reading, dogwhistles)
return True return True
@ -1876,7 +1879,8 @@ def _receive_bookmark(recent_posts_cache: {},
allow_local_network_access: bool, allow_local_network_access: bool,
theme_name: str, system_language: str, theme_name: str, system_language: str,
max_like_count: int, cw_lists: {}, max_like_count: int, cw_lists: {},
lists_enabled: {}, bold_reading: bool) -> bool: lists_enabled: {}, bold_reading: bool,
dogwhistles: {}) -> bool:
"""Receives a bookmark activity within the POST section of HTTPServer """Receives a bookmark activity within the POST section of HTTPServer
""" """
if not message_json.get('type'): if not message_json.get('type'):
@ -1973,7 +1977,7 @@ def _receive_bookmark(recent_posts_cache: {},
manually_approve_followers, manually_approve_followers,
False, True, False, cw_lists, False, True, False, cw_lists,
lists_enabled, timezone, mitm, lists_enabled, timezone, mitm,
bold_reading) bold_reading, dogwhistles)
return True return True
@ -1993,7 +1997,8 @@ def _receive_undo_bookmark(recent_posts_cache: {},
allow_local_network_access: bool, allow_local_network_access: bool,
theme_name: str, system_language: str, theme_name: str, system_language: str,
max_like_count: int, cw_lists: {}, max_like_count: int, cw_lists: {},
lists_enabled: str, bold_reading: bool) -> bool: lists_enabled: str, bold_reading: bool,
dogwhistles: {}) -> bool:
"""Receives an undo bookmark activity within the POST section of HTTPServer """Receives an undo bookmark activity within the POST section of HTTPServer
""" """
if not message_json.get('type'): if not message_json.get('type'):
@ -2090,7 +2095,8 @@ def _receive_undo_bookmark(recent_posts_cache: {},
show_individual_post_icons, show_individual_post_icons,
manually_approve_followers, manually_approve_followers,
False, True, False, cw_lists, lists_enabled, False, True, False, cw_lists, lists_enabled,
timezone, mitm, bold_reading) timezone, mitm, bold_reading,
dogwhistles)
return True return True
@ -2186,7 +2192,8 @@ def _receive_announce(recent_posts_cache: {},
allow_deletion: bool, allow_deletion: bool,
peertube_instances: [], peertube_instances: [],
max_like_count: int, cw_lists: {}, max_like_count: int, cw_lists: {},
lists_enabled: str, bold_reading: bool) -> bool: lists_enabled: str, bold_reading: bool,
dogwhistles: {}) -> bool:
"""Receives an announce activity within the POST section of HTTPServer """Receives an announce activity within the POST section of HTTPServer
""" """
if message_json['type'] != 'Announce': if message_json['type'] != 'Announce':
@ -2309,7 +2316,7 @@ def _receive_announce(recent_posts_cache: {},
manually_approve_followers, manually_approve_followers,
False, True, False, cw_lists, False, True, False, cw_lists,
lists_enabled, timezone, mitm, lists_enabled, timezone, mitm,
bold_reading) bold_reading, dogwhistles)
if not announce_html: if not announce_html:
print('WARN: Unable to generate html for announce ' + print('WARN: Unable to generate html for announce ' +
str(message_json)) str(message_json))
@ -3452,7 +3459,7 @@ def _receive_question_vote(server, base_dir: str, nickname: str, domain: str,
theme_name: str, system_language: str, theme_name: str, system_language: str,
max_like_count: int, max_like_count: int,
cw_lists: {}, lists_enabled: bool, cw_lists: {}, lists_enabled: bool,
bold_reading: bool) -> None: bold_reading: bool, dogwhistles: {}) -> None:
"""Updates the votes on a Question/poll """Updates the votes on a Question/poll
""" """
# if this is a reply to a question then update the votes # if this is a reply to a question then update the votes
@ -3505,7 +3512,7 @@ def _receive_question_vote(server, base_dir: str, nickname: str, domain: str,
manually_approve_followers, manually_approve_followers,
False, True, False, cw_lists, False, True, False, cw_lists,
lists_enabled, timezone, mitm, lists_enabled, timezone, mitm,
bold_reading) bold_reading, dogwhistles)
# add id to inbox index # add id to inbox index
inbox_update_index('inbox', base_dir, handle, inbox_update_index('inbox', base_dir, handle,
@ -3676,7 +3683,8 @@ def _inbox_after_initial(server, inbox_start_time,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
content_license_url: str, content_license_url: str,
languages_understood: [], languages_understood: [],
mitm: bool, bold_reading: bool) -> bool: mitm: bool, bold_reading: bool,
dogwhistles: {}) -> bool:
""" Anything which needs to be done after initial checks have passed """ Anything which needs to be done after initial checks have passed
""" """
# if this is a clearnet instance then replace any onion/i2p # if this is a clearnet instance then replace any onion/i2p
@ -3724,7 +3732,7 @@ def _inbox_after_initial(server, inbox_start_time,
allow_local_network_access, allow_local_network_access,
theme_name, system_language, theme_name, system_language,
max_like_count, cw_lists, lists_enabled, max_like_count, cw_lists, lists_enabled,
bold_reading): bold_reading, dogwhistles):
if debug: if debug:
print('DEBUG: Like accepted from ' + actor) print('DEBUG: Like accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness, fitness_performance(inbox_start_time, server.fitness,
@ -3749,7 +3757,7 @@ def _inbox_after_initial(server, inbox_start_time,
allow_local_network_access, allow_local_network_access,
theme_name, system_language, theme_name, system_language,
max_like_count, cw_lists, lists_enabled, max_like_count, cw_lists, lists_enabled,
bold_reading): bold_reading, dogwhistles):
if debug: if debug:
print('DEBUG: Undo like accepted from ' + actor) print('DEBUG: Undo like accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness, fitness_performance(inbox_start_time, server.fitness,
@ -3775,7 +3783,7 @@ def _inbox_after_initial(server, inbox_start_time,
allow_local_network_access, allow_local_network_access,
theme_name, system_language, theme_name, system_language,
max_like_count, cw_lists, lists_enabled, max_like_count, cw_lists, lists_enabled,
bold_reading): bold_reading, dogwhistles):
if debug: if debug:
print('DEBUG: Reaction accepted from ' + actor) print('DEBUG: Reaction accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness, fitness_performance(inbox_start_time, server.fitness,
@ -3802,7 +3810,7 @@ def _inbox_after_initial(server, inbox_start_time,
allow_local_network_access, allow_local_network_access,
theme_name, system_language, theme_name, system_language,
max_like_count, cw_lists, lists_enabled, max_like_count, cw_lists, lists_enabled,
bold_reading): bold_reading, dogwhistles):
if debug: if debug:
print('DEBUG: Undo reaction accepted from ' + actor) print('DEBUG: Undo reaction accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness, fitness_performance(inbox_start_time, server.fitness,
@ -3829,7 +3837,7 @@ def _inbox_after_initial(server, inbox_start_time,
allow_local_network_access, allow_local_network_access,
theme_name, system_language, theme_name, system_language,
max_like_count, cw_lists, lists_enabled, max_like_count, cw_lists, lists_enabled,
bold_reading): bold_reading, dogwhistles):
if debug: if debug:
print('DEBUG: Bookmark accepted from ' + actor) print('DEBUG: Bookmark accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness, fitness_performance(inbox_start_time, server.fitness,
@ -3856,7 +3864,7 @@ def _inbox_after_initial(server, inbox_start_time,
allow_local_network_access, allow_local_network_access,
theme_name, system_language, theme_name, system_language,
max_like_count, cw_lists, lists_enabled, max_like_count, cw_lists, lists_enabled,
bold_reading): bold_reading, dogwhistles):
if debug: if debug:
print('DEBUG: Undo bookmark accepted from ' + actor) print('DEBUG: Undo bookmark accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness, fitness_performance(inbox_start_time, server.fitness,
@ -3891,7 +3899,7 @@ def _inbox_after_initial(server, inbox_start_time,
allow_deletion, allow_deletion,
peertube_instances, peertube_instances,
max_like_count, cw_lists, lists_enabled, max_like_count, cw_lists, lists_enabled,
bold_reading): bold_reading, dogwhistles):
if debug: if debug:
print('DEBUG: Announce accepted from ' + actor) print('DEBUG: Announce accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness, fitness_performance(inbox_start_time, server.fitness,
@ -4033,7 +4041,7 @@ def _inbox_after_initial(server, inbox_start_time,
theme_name, system_language, theme_name, system_language,
max_like_count, max_like_count,
cw_lists, lists_enabled, cw_lists, lists_enabled,
bold_reading) bold_reading, dogwhistles)
fitness_performance(inbox_start_time, server.fitness, fitness_performance(inbox_start_time, server.fitness,
'INBOX', '_receive_question_vote', 'INBOX', '_receive_question_vote',
debug) debug)
@ -4277,7 +4285,8 @@ def _inbox_after_initial(server, inbox_start_time,
cw_lists, cw_lists,
lists_enabled, lists_enabled,
timezone, mitm, timezone, mitm,
bold_reading) bold_reading,
dogwhistles)
fitness_performance(inbox_start_time, fitness_performance(inbox_start_time,
server.fitness, server.fitness,
'INBOX', 'INBOX',
@ -5237,6 +5246,11 @@ def run_inbox_queue(server,
debug) debug)
inbox_start_time = time.time() inbox_start_time = time.time()
dogwhistles_filename = base_dir + '/accounts/dogwhistles.txt'
if not os.path.isfile(dogwhistles_filename):
dogwhistles_filename = base_dir + '/default_dogwhistles.txt'
dogwhistles = load_dogwhistles(dogwhistles_filename)
# set the id to the same as the post filename # set the id to the same as the post filename
# This makes the filename and the id consistent # This makes the filename and the id consistent
# if queue_json['post'].get('id'): # if queue_json['post'].get('id'):
@ -5328,7 +5342,7 @@ def run_inbox_queue(server,
show_published_date_only, show_published_date_only,
peertube_instances, peertube_instances,
theme_name, max_like_count, theme_name, max_like_count,
cw_lists): cw_lists, dogwhistles):
if debug: if debug:
print('Queue: Update accepted from ' + key_id) print('Queue: Update accepted from ' + key_id)
if os.path.isfile(queue_filename): if os.path.isfile(queue_filename):
@ -5456,7 +5470,7 @@ def run_inbox_queue(server,
cw_lists, lists_enabled, cw_lists, lists_enabled,
content_license_url, content_license_url,
languages_understood, mitm, languages_understood, mitm,
bold_reading) bold_reading, dogwhistles)
fitness_performance(inbox_start_time, server.fitness, fitness_performance(inbox_start_time, server.fitness,
'INBOX', 'handle_after_initial', 'INBOX', 'handle_after_initial',
debug) debug)

View File

@ -213,7 +213,8 @@ def post_message_to_outbox(session, translate: {},
max_like_count: int, max_like_count: int,
max_recent_posts: int, cw_lists: {}, max_recent_posts: int, cw_lists: {},
lists_enabled: str, lists_enabled: str,
content_license_url: str) -> bool: content_license_url: str,
dogwhistles: {}) -> bool:
"""post is received by the outbox """post is received by the outbox
Client to server message post Client to server message post
https://www.w3.org/TR/activitypub/#client-to-server-outbox-delivery https://www.w3.org/TR/activitypub/#client-to-server-outbox-delivery
@ -513,7 +514,7 @@ def post_message_to_outbox(session, translate: {},
False, True, use_cache_only, False, True, use_cache_only,
cw_lists, lists_enabled, cw_lists, lists_enabled,
timezone, mitm, timezone, mitm,
bold_reading) bold_reading, dogwhistles)
if outbox_announce(recent_posts_cache, if outbox_announce(recent_posts_cache,
base_dir, message_json, debug): base_dir, message_json, debug):

View File

@ -142,7 +142,8 @@ def _update_post_schedule(base_dir: str, handle: str, httpd,
httpd.max_recent_posts, httpd.max_recent_posts,
httpd.cw_lists, httpd.cw_lists,
httpd.lists_enabled, httpd.lists_enabled,
httpd.content_license_url): httpd.content_license_url,
httpd.dogwhistles):
index_lines.remove(line) index_lines.remove(line)
try: try:
os.remove(post_filename) os.remove(post_filename)

View File

@ -132,6 +132,7 @@ from inbox import valid_inbox
from inbox import valid_inbox_filenames from inbox import valid_inbox_filenames
from inbox import cache_svg_images from inbox import cache_svg_images
from categories import guess_hashtag_category from categories import guess_hashtag_category
from content import detect_dogwhistles
from content import remove_script from content import remove_script
from content import create_edits_html from content import create_edits_html
from content import content_diff from content import content_diff
@ -7326,6 +7327,30 @@ def _test_remove_end_of_line():
assert remove_eol(text) == expected assert remove_eol(text) == expected
def _test_dogwhistles():
print('dogwhistles')
dogwhistles = {
"X-hamstered": "hamsterism",
"gerbil": "rodent",
"*snake": "slither",
"start*end": "something"
}
content = 'This text does not contain any dogwhistles'
assert not detect_dogwhistles(content, dogwhistles)
content = 'A gerbil named joe'
assert detect_dogwhistles(content, dogwhistles)
content = 'A rattlesnake.'
assert detect_dogwhistles(content, dogwhistles)
content = 'A startthingend.'
assert detect_dogwhistles(content, dogwhistles)
content = 'This content is unhamstered and yhamstered.'
result = detect_dogwhistles(content, dogwhistles)
assert result
assert result.get('hamstered')
assert result['hamstered']['count'] == 2
assert result['hamstered']['category'] == "hamsterism"
def run_all_tests(): def run_all_tests():
base_dir = os.getcwd() base_dir = os.getcwd()
print('Running tests...') print('Running tests...')
@ -7343,6 +7368,7 @@ def run_all_tests():
_test_checkbox_names() _test_checkbox_names()
_test_thread_functions() _test_thread_functions()
_test_functions() _test_functions()
_test_dogwhistles()
_test_remove_end_of_line() _test_remove_end_of_line()
_test_translation_labels() _test_translation_labels()
_test_color_contrast_value(base_dir) _test_color_contrast_value(base_dir)

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "قم بالتبديل إلى عرض الاعتدال", "Switch to moderation view": "قم بالتبديل إلى عرض الاعتدال",
"Minimize attached images": "تصغير الصور المرفقة", "Minimize attached images": "تصغير الصور المرفقة",
"SHOW MEDIA": "عرض الوسائط", "SHOW MEDIA": "عرض الوسائط",
"ActivityPub Specification": "مواصفات ActivityPub" "ActivityPub Specification": "مواصفات ActivityPub",
"Dogwhistle words": "كلمات Dogwhistle",
"Content warnings will be added for the following": "ستتم إضافة تحذيرات المحتوى لما يلي"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "সংযম দৃশ্যে স্যুইচ করুন", "Switch to moderation view": "সংযম দৃশ্যে স্যুইচ করুন",
"Minimize attached images": "সংযুক্ত ছবি ছোট করুন", "Minimize attached images": "সংযুক্ত ছবি ছোট করুন",
"SHOW MEDIA": "মিডিয়া দেখান", "SHOW MEDIA": "মিডিয়া দেখান",
"ActivityPub Specification": "ActivityPub স্পেসিফিকেশন" "ActivityPub Specification": "ActivityPub স্পেসিফিকেশন",
"Dogwhistle words": "কুকুরের হুইসেল শব্দ",
"Content warnings will be added for the following": "নিম্নলিখিত জন্য বিষয়বস্তু সতর্কতা যোগ করা হবে"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "Canvia a la visualització de moderació", "Switch to moderation view": "Canvia a la visualització de moderació",
"Minimize attached images": "Minimitzar les imatges adjuntes", "Minimize attached images": "Minimitzar les imatges adjuntes",
"SHOW MEDIA": "MOSTRA ELS MITJANS", "SHOW MEDIA": "MOSTRA ELS MITJANS",
"ActivityPub Specification": "Especificació d'ActivityPub" "ActivityPub Specification": "Especificació d'ActivityPub",
"Dogwhistle words": "Paraules de xiulet",
"Content warnings will be added for the following": "S'afegiran advertències de contingut per al següent"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "Newid i wedd safoni", "Switch to moderation view": "Newid i wedd safoni",
"Minimize attached images": "Lleihau delweddau sydd ynghlwm", "Minimize attached images": "Lleihau delweddau sydd ynghlwm",
"SHOW MEDIA": "DANGOS CYFRYNGAU", "SHOW MEDIA": "DANGOS CYFRYNGAU",
"ActivityPub Specification": "Manyleb GweithgareddPub" "ActivityPub Specification": "Manyleb GweithgareddPub",
"Dogwhistle words": "Geiriau chwibanogl",
"Content warnings will be added for the following": "Bydd rhybuddion cynnwys yn cael eu hychwanegu ar gyfer y canlynol"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "Wechseln Sie zur Moderationsansicht", "Switch to moderation view": "Wechseln Sie zur Moderationsansicht",
"Minimize attached images": "Angehängte Bilder minimieren", "Minimize attached images": "Angehängte Bilder minimieren",
"SHOW MEDIA": "MEDIEN ZEIGEN", "SHOW MEDIA": "MEDIEN ZEIGEN",
"ActivityPub Specification": "ActivityPub-Spezifikation" "ActivityPub Specification": "ActivityPub-Spezifikation",
"Dogwhistle words": "Hundepfeife Worte",
"Content warnings will be added for the following": "Inhaltswarnungen werden für Folgendes hinzugefügt"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "Μετάβαση σε προβολή εποπτείας", "Switch to moderation view": "Μετάβαση σε προβολή εποπτείας",
"Minimize attached images": "Ελαχιστοποιήστε τις συνημμένες εικόνες", "Minimize attached images": "Ελαχιστοποιήστε τις συνημμένες εικόνες",
"SHOW MEDIA": "ΔΕΙΤΕ ΜΕΣΑ", "SHOW MEDIA": "ΔΕΙΤΕ ΜΕΣΑ",
"ActivityPub Specification": "Προδιαγραφές ActivityPub" "ActivityPub Specification": "Προδιαγραφές ActivityPub",
"Dogwhistle words": "Σφυρίχτρα λέξεις",
"Content warnings will be added for the following": "Θα προστεθούν προειδοποιήσεις περιεχομένου για τα ακόλουθα"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "Switch to moderation view", "Switch to moderation view": "Switch to moderation view",
"Minimize attached images": "Minimize attached images", "Minimize attached images": "Minimize attached images",
"SHOW MEDIA": "SHOW MEDIA", "SHOW MEDIA": "SHOW MEDIA",
"ActivityPub Specification": "ActivityPub Specification" "ActivityPub Specification": "ActivityPub Specification",
"Dogwhistle words": "Dogwhistle words",
"Content warnings will be added for the following": "Content warnings will be added for the following"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "Cambiar a la vista de moderación", "Switch to moderation view": "Cambiar a la vista de moderación",
"Minimize attached images": "Minimizar imágenes adjuntas", "Minimize attached images": "Minimizar imágenes adjuntas",
"SHOW MEDIA": "MOSTRAR MEDIOS", "SHOW MEDIA": "MOSTRAR MEDIOS",
"ActivityPub Specification": "Especificación de ActivityPub" "ActivityPub Specification": "Especificación de ActivityPub",
"Dogwhistle words": "Palabras de silbato para perros",
"Content warnings will be added for the following": "Se agregarán advertencias de contenido para lo siguiente"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "Passer en mode modération", "Switch to moderation view": "Passer en mode modération",
"Minimize attached images": "Réduire les images jointes", "Minimize attached images": "Réduire les images jointes",
"SHOW MEDIA": "AFFICHER LES MÉDIAS", "SHOW MEDIA": "AFFICHER LES MÉDIAS",
"ActivityPub Specification": "Spécification ActivityPub" "ActivityPub Specification": "Spécification ActivityPub",
"Dogwhistle words": "Mots de sifflet de chien",
"Content warnings will be added for the following": "Des avertissements de contenu seront ajoutés pour les éléments suivants"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "Athraigh go dtí an t-amharc modhnóireachta", "Switch to moderation view": "Athraigh go dtí an t-amharc modhnóireachta",
"Minimize attached images": "Íoslaghdaigh íomhánna ceangailte", "Minimize attached images": "Íoslaghdaigh íomhánna ceangailte",
"SHOW MEDIA": "Taispeáin MEÁIN", "SHOW MEDIA": "Taispeáin MEÁIN",
"ActivityPub Specification": "Sonraíocht ActivityPub" "ActivityPub Specification": "Sonraíocht ActivityPub",
"Dogwhistle words": "Focail feadóg mhadra",
"Content warnings will be added for the following": "Cuirfear rabhaidh ábhair leis maidir leis na nithe seo a leanas"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "मॉडरेशन दृश्य पर स्विच करें", "Switch to moderation view": "मॉडरेशन दृश्य पर स्विच करें",
"Minimize attached images": "संलग्न छवियों को छोटा करें", "Minimize attached images": "संलग्न छवियों को छोटा करें",
"SHOW MEDIA": "मीडिया दिखाएं", "SHOW MEDIA": "मीडिया दिखाएं",
"ActivityPub Specification": "गतिविधिपब विशिष्टता" "ActivityPub Specification": "गतिविधिपब विशिष्टता",
"Dogwhistle words": "कुत्ते की सीटी शब्द",
"Content warnings will be added for the following": "निम्नलिखित के लिए सामग्री चेतावनियां जोड़ दी जाएंगी"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "Passa alla visualizzazione moderazione", "Switch to moderation view": "Passa alla visualizzazione moderazione",
"Minimize attached images": "Riduci al minimo le immagini allegate", "Minimize attached images": "Riduci al minimo le immagini allegate",
"SHOW MEDIA": "MOSTRA MEDIA", "SHOW MEDIA": "MOSTRA MEDIA",
"ActivityPub Specification": "Specifica ActivityPub" "ActivityPub Specification": "Specifica ActivityPub",
"Dogwhistle words": "Parole da fischietto",
"Content warnings will be added for the following": "Verranno aggiunti avvisi sui contenuti per quanto segue"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "モデレートビューに切り替えます", "Switch to moderation view": "モデレートビューに切り替えます",
"Minimize attached images": "添付画像を最小限に抑える", "Minimize attached images": "添付画像を最小限に抑える",
"SHOW MEDIA": "メディアを表示", "SHOW MEDIA": "メディアを表示",
"ActivityPub Specification": "ActivityPubの仕様" "ActivityPub Specification": "ActivityPubの仕様",
"Dogwhistle words": "犬笛の言葉",
"Content warnings will be added for the following": "以下のコンテンツ警告が追加されます"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "검토 보기로 전환", "Switch to moderation view": "검토 보기로 전환",
"Minimize attached images": "첨부된 이미지 최소화", "Minimize attached images": "첨부된 이미지 최소화",
"SHOW MEDIA": "미디어 표시", "SHOW MEDIA": "미디어 표시",
"ActivityPub Specification": "ActivityPub 사양" "ActivityPub Specification": "ActivityPub 사양",
"Dogwhistle words": "개 휘파람 단어",
"Content warnings will be added for the following": "다음에 대한 콘텐츠 경고가 추가됩니다."
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "Biguherîne bo dîtina moderatoriyê", "Switch to moderation view": "Biguherîne bo dîtina moderatoriyê",
"Minimize attached images": "Wêneyên pêvekirî kêm bikin", "Minimize attached images": "Wêneyên pêvekirî kêm bikin",
"SHOW MEDIA": "MEDYA NÎŞAN DE", "SHOW MEDIA": "MEDYA NÎŞAN DE",
"ActivityPub Specification": "Specification ActivityPub" "ActivityPub Specification": "Specification ActivityPub",
"Dogwhistle words": "Peyvên kûçikê",
"Content warnings will be added for the following": "Hişyariyên naverokê dê ji bo jêrîn werin zêdekirin"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "Overschakelen naar moderatieweergave", "Switch to moderation view": "Overschakelen naar moderatieweergave",
"Minimize attached images": "Bijgevoegde afbeeldingen minimaliseren", "Minimize attached images": "Bijgevoegde afbeeldingen minimaliseren",
"SHOW MEDIA": "TOON MEDIA", "SHOW MEDIA": "TOON MEDIA",
"ActivityPub Specification": "ActivityPub-specificatie" "ActivityPub Specification": "ActivityPub-specificatie",
"Dogwhistle words": "Hondenfluitwoorden",
"Content warnings will be added for the following": "Er worden inhoudswaarschuwingen toegevoegd voor het volgende:"
} }

View File

@ -561,5 +561,7 @@
"Switch to moderation view": "Switch to moderation view", "Switch to moderation view": "Switch to moderation view",
"Minimize attached images": "Minimize attached images", "Minimize attached images": "Minimize attached images",
"SHOW MEDIA": "SHOW MEDIA", "SHOW MEDIA": "SHOW MEDIA",
"ActivityPub Specification": "ActivityPub Specification" "ActivityPub Specification": "ActivityPub Specification",
"Dogwhistle words": "Dogwhistle words",
"Content warnings will be added for the following": "Content warnings will be added for the following"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "Przełącz na widok moderacji", "Switch to moderation view": "Przełącz na widok moderacji",
"Minimize attached images": "Zminimalizuj załączone obrazy", "Minimize attached images": "Zminimalizuj załączone obrazy",
"SHOW MEDIA": "POKAŻ MEDIA", "SHOW MEDIA": "POKAŻ MEDIA",
"ActivityPub Specification": "Specyfikacja ActivityPub" "ActivityPub Specification": "Specyfikacja ActivityPub",
"Dogwhistle words": "Słowa gwizdka na psa",
"Content warnings will be added for the following": "Ostrzeżenia dotyczące treści zostaną dodane do następujących"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "Mudar para a visualização de moderação", "Switch to moderation view": "Mudar para a visualização de moderação",
"Minimize attached images": "Minimizar imagens anexadas", "Minimize attached images": "Minimizar imagens anexadas",
"SHOW MEDIA": "MOSTRAR MÍDIA", "SHOW MEDIA": "MOSTRAR MÍDIA",
"ActivityPub Specification": "Especificação do ActivityPub" "ActivityPub Specification": "Especificação do ActivityPub",
"Dogwhistle words": "Palavras de apito",
"Content warnings will be added for the following": "Avisos de conteúdo serão adicionados para os seguintes"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "Перейти в режим модерации", "Switch to moderation view": "Перейти в режим модерации",
"Minimize attached images": "Свернуть прикрепленные изображения", "Minimize attached images": "Свернуть прикрепленные изображения",
"SHOW MEDIA": "ПОКАЗАТЬ МЕДИА", "SHOW MEDIA": "ПОКАЗАТЬ МЕДИА",
"ActivityPub Specification": "Спецификация ActivityPub" "ActivityPub Specification": "Спецификация ActivityPub",
"Dogwhistle words": "Собачий свисток",
"Content warnings will be added for the following": "Предупреждения о содержании будут добавлены для следующих"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "Badili hadi mwonekano wa udhibiti", "Switch to moderation view": "Badili hadi mwonekano wa udhibiti",
"Minimize attached images": "Punguza picha zilizoambatishwa", "Minimize attached images": "Punguza picha zilizoambatishwa",
"SHOW MEDIA": "ONESHA VYOMBO VYA HABARI", "SHOW MEDIA": "ONESHA VYOMBO VYA HABARI",
"ActivityPub Specification": "Vipimo vya ActivityPub" "ActivityPub Specification": "Vipimo vya ActivityPub",
"Dogwhistle words": "Maneno ya mbwa",
"Content warnings will be added for the following": "Maonyo ya maudhui yataongezwa kwa yafuatayo"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "Denetleme görünümüne geç", "Switch to moderation view": "Denetleme görünümüne geç",
"Minimize attached images": "Ekli resimleri simge durumuna küçült", "Minimize attached images": "Ekli resimleri simge durumuna küçült",
"SHOW MEDIA": "MEDYA GÖSTER", "SHOW MEDIA": "MEDYA GÖSTER",
"ActivityPub Specification": "ActivityPub Spesifikasyonu" "ActivityPub Specification": "ActivityPub Spesifikasyonu",
"Dogwhistle words": "İtiraf sözleri",
"Content warnings will be added for the following": "Aşağıdakiler için içerik uyarıları eklenecek"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "Перейти до режиму модерації", "Switch to moderation view": "Перейти до режиму модерації",
"Minimize attached images": "Мінімізуйте вкладені зображення", "Minimize attached images": "Мінімізуйте вкладені зображення",
"SHOW MEDIA": "ПОКАЗАТИ ЗМІ", "SHOW MEDIA": "ПОКАЗАТИ ЗМІ",
"ActivityPub Specification": "Специфікація ActivityPub" "ActivityPub Specification": "Специфікація ActivityPub",
"Dogwhistle words": "Собачі слова",
"Content warnings will be added for the following": "Попередження про вміст буде додано для наступних"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "באַשטימען צו מאַדעריישאַן מיינונג", "Switch to moderation view": "באַשטימען צו מאַדעריישאַן מיינונג",
"Minimize attached images": "מינאַמייז אַטאַטשט בילדער", "Minimize attached images": "מינאַמייז אַטאַטשט בילדער",
"SHOW MEDIA": "ווייַז מעדיע", "SHOW MEDIA": "ווייַז מעדיע",
"ActivityPub Specification": "ActivityPub באַשרייַבונג" "ActivityPub Specification": "ActivityPub באַשרייַבונג",
"Dogwhistle words": "דאָגווהיסטלע ווערטער",
"Content warnings will be added for the following": "אינהאַלט וואָרנינגז וועט זיין מוסיף פֿאַר די פאלגענדע"
} }

View File

@ -565,5 +565,7 @@
"Switch to moderation view": "切换到审核视图", "Switch to moderation view": "切换到审核视图",
"Minimize attached images": "最小化附加图像", "Minimize attached images": "最小化附加图像",
"SHOW MEDIA": "展示媒体", "SHOW MEDIA": "展示媒体",
"ActivityPub Specification": "ActivityPub 规范" "ActivityPub Specification": "ActivityPub 规范",
"Dogwhistle words": "狗哨的话",
"Content warnings will be added for the following": "将为以下内容添加内容警告"
} }

View File

@ -549,7 +549,7 @@ def html_newswire_mobile(css_cache: {}, base_dir: str, nickname: str,
def html_edit_newswire(css_cache: {}, translate: {}, base_dir: str, path: str, def html_edit_newswire(css_cache: {}, translate: {}, base_dir: str, path: str,
domain: str, port: int, http_prefix: str, domain: str, port: int, http_prefix: str,
default_timeline: str, theme: str, default_timeline: str, theme: str,
access_keys: {}) -> str: access_keys: {}, dogwhistles: {}) -> str:
"""Shows the edit newswire screen """Shows the edit newswire screen
""" """
if '/users/' not in path: if '/users/' not in path:
@ -645,6 +645,22 @@ def html_edit_newswire(css_cache: {}, translate: {}, base_dir: str, path: str,
'name="filteredWordsNewswire" style="height:50vh" ' + \ 'name="filteredWordsNewswire" style="height:50vh" ' + \
'spellcheck="true">' + filter_str + '</textarea>\n' 'spellcheck="true">' + filter_str + '</textarea>\n'
dogwhistle_str = ''
for whistle, category in dogwhistles.items():
if not category:
continue
dogwhistle_str += whistle + ' -> ' + category + '\n'
edit_newswire_form += \
' <br><b><label class="labels">' + \
translate['Dogwhistle words'] + '</label></b>\n'
edit_newswire_form += ' <br><label class="labels">' + \
translate['Content warnings will be added for the following'] + \
':</label>'
edit_newswire_form += ' <textarea id="message" ' + \
'name="dogwhistleWords" style="height:50vh" ' + \
'spellcheck="true">' + dogwhistle_str + '</textarea>\n'
hashtag_rules_str = '' hashtag_rules_str = ''
hashtag_rules_filename = \ hashtag_rules_filename = \
base_dir + '/accounts/hashtagrules.txt' base_dir + '/accounts/hashtagrules.txt'

View File

@ -38,7 +38,8 @@ def html_confirm_delete(server, css_cache: {},
allow_local_network_access: bool, allow_local_network_access: bool,
theme_name: str, system_language: str, theme_name: str, system_language: str,
max_like_count: int, signing_priv_key_pem: str, max_like_count: int, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str) -> str: cw_lists: {}, lists_enabled: str,
dogwhistles: {}) -> str:
"""Shows a screen asking to confirm the deletion of a post """Shows a screen asking to confirm the deletion of a post
""" """
if '/statuses/' not in message_id: if '/statuses/' not in message_id:
@ -90,7 +91,7 @@ def html_confirm_delete(server, css_cache: {},
theme_name, system_language, max_like_count, theme_name, system_language, max_like_count,
False, False, False, False, False, False, False, False, False, False, False, False,
cw_lists, lists_enabled, timezone, mitm, cw_lists, lists_enabled, timezone, mitm,
bold_reading) bold_reading, dogwhistles)
delete_post_str += '<center>' delete_post_str += '<center>'
delete_post_str += \ delete_post_str += \
' <p class="followText">' + \ ' <p class="followText">' + \

View File

@ -224,7 +224,8 @@ def html_new_post(css_cache: {}, media_instance: bool, translate: {},
max_like_count: int, signing_priv_key_pem: str, max_like_count: int, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
box_name: str, box_name: str,
reply_is_chat: bool, bold_reading: bool) -> str: reply_is_chat: bool, bold_reading: bool,
dogwhistles: {}) -> str:
"""New post screen """New post screen
""" """
reply_str = '' reply_str = ''
@ -303,7 +304,7 @@ def html_new_post(css_cache: {}, media_instance: bool, translate: {},
False, False, False, False, False, False,
cw_lists, lists_enabled, cw_lists, lists_enabled,
timezone, False, timezone, False,
bold_reading) bold_reading, dogwhistles)
reply_str = '<input type="hidden" ' + \ reply_str = '<input type="hidden" ' + \
'name="replyTo" value="' + in_reply_to + '">\n' 'name="replyTo" value="' + in_reply_to + '">\n'

View File

@ -38,7 +38,8 @@ def _html_front_screen_posts(recent_posts_cache: {}, max_recent_posts: int,
max_like_count: int, max_like_count: int,
signing_priv_key_pem: str, cw_lists: {}, signing_priv_key_pem: str, cw_lists: {},
lists_enabled: str, lists_enabled: str,
bold_reading: bool) -> str: bold_reading: bool,
dogwhistles: {}) -> str:
"""Shows posts on the front screen of a news instance """Shows posts on the front screen of a news instance
These should only be public blog posts from the features timeline These should only be public blog posts from the features timeline
which is the blog timeline of the news actor which is the blog timeline of the news actor
@ -89,7 +90,7 @@ def _html_front_screen_posts(recent_posts_cache: {}, max_recent_posts: int,
True, False, False, True, False, False,
cw_lists, lists_enabled, cw_lists, lists_enabled,
timezone, False, timezone, False,
bold_reading) bold_reading, dogwhistles)
if post_str: if post_str:
profile_str += post_str + separator_str profile_str += post_str + separator_str
ctr += 1 ctr += 1
@ -120,7 +121,8 @@ def html_front_screen(signing_priv_key_pem: str,
extra_json: {}, extra_json: {},
page_number: int, page_number: int,
max_items_per_page: int, max_items_per_page: int,
cw_lists: {}, lists_enabled: str) -> str: cw_lists: {}, lists_enabled: str,
dogwhistles: {}) -> str:
"""Show the news instance front screen """Show the news instance front screen
""" """
bold_reading = False bold_reading = False
@ -195,7 +197,7 @@ def html_front_screen(signing_priv_key_pem: str,
max_like_count, max_like_count,
signing_priv_key_pem, signing_priv_key_pem,
cw_lists, lists_enabled, cw_lists, lists_enabled,
bold_reading) + license_str bold_reading, dogwhistles) + license_str
# Footer which is only used for system accounts # Footer which is only used for system accounts
profile_footer_str = ' </td>\n' profile_footer_str = ' </td>\n'

View File

@ -41,7 +41,7 @@ def html_likers_of_post(base_dir: str, nickname: str,
max_like_count: int, signing_priv_key_pem: str, max_like_count: int, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
box_name: str, default_timeline: str, box_name: str, default_timeline: str,
bold_reading: bool, bold_reading: bool, dogwhistles: {},
dict_name: str = 'likes') -> str: dict_name: str = 'likes') -> str:
"""Returns html for a screen showing who liked a post """Returns html for a screen showing who liked a post
""" """
@ -107,7 +107,8 @@ def html_likers_of_post(base_dir: str, nickname: str,
False, False, False, False, False, False,
False, False, False, False, False, False,
cw_lists, lists_enabled, cw_lists, lists_enabled,
timezone, mitm, bold_reading) timezone, mitm, bold_reading,
dogwhistles)
# show likers beneath the post # show likers beneath the post
obj = post_json_object obj = post_json_object

View File

@ -57,7 +57,8 @@ def html_moderation(css_cache: {}, default_timeline: str,
shared_items_federated_domains: [], shared_items_federated_domains: [],
signing_priv_key_pem: str, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Show the moderation feed as html """Show the moderation feed as html
This is what you see when selecting the "mod" timeline This is what you see when selecting the "mod" timeline
""" """
@ -83,7 +84,7 @@ def html_moderation(css_cache: {}, default_timeline: str,
text_mode_banner, access_keys, system_language, text_mode_banner, access_keys, system_language,
max_like_count, shared_items_federated_domains, max_like_count, shared_items_federated_domains,
signing_priv_key_pem, cw_lists, lists_enabled, signing_priv_key_pem, cw_lists, lists_enabled,
timezone, bold_reading) timezone, bold_reading, dogwhistles)
def html_account_info(css_cache: {}, translate: {}, def html_account_info(css_cache: {}, translate: {},

View File

@ -62,6 +62,7 @@ from utils import get_domain_from_actor
from utils import acct_dir from utils import acct_dir
from utils import local_actor_url from utils import local_actor_url
from utils import is_unlisted_post from utils import is_unlisted_post
from content import detect_dogwhistles
from content import create_edits_html from content import create_edits_html
from content import bold_reading_string from content import bold_reading_string
from content import limit_repeated_words from content import limit_repeated_words
@ -1555,6 +1556,30 @@ def _substitute_onion_domains(base_dir: str, content: str) -> str:
return content return content
def _add_dogwhistle_warnings(summary: str, content: str,
dogwhistles: {}, translate: {}) -> {}:
"""Adds dogwhistle warnings for the given content
"""
if not dogwhistles:
return summary
content_str = str(summary) + ' ' + content
detected = detect_dogwhistles(content_str, dogwhistles)
if not detected:
return summary
for _, item in detected.items():
if not item.get('category'):
continue
whistle_str = item['category']
if translate.get(whistle_str):
whistle_str = translate[whistle_str]
if summary:
summary += ', ' + whistle_str
else:
summary = whistle_str
return summary
def individual_post_as_html(signing_priv_key_pem: str, def individual_post_as_html(signing_priv_key_pem: str,
allow_downloads: bool, allow_downloads: bool,
recent_posts_cache: {}, max_recent_posts: int, recent_posts_cache: {}, max_recent_posts: int,
@ -1583,7 +1608,8 @@ def individual_post_as_html(signing_priv_key_pem: str,
cw_lists: {}, cw_lists: {},
lists_enabled: str, lists_enabled: str,
timezone: str, timezone: str,
mitm: bool, bold_reading: bool) -> str: mitm: bool, bold_reading: bool,
dogwhistles: {}) -> str:
""" Shows a single post as html """ Shows a single post as html
""" """
if not post_json_object: if not post_json_object:
@ -2142,6 +2168,10 @@ def individual_post_as_html(signing_priv_key_pem: str,
if content_str: if content_str:
summary_str = get_summary_from_post(post_json_object, system_language, summary_str = get_summary_from_post(post_json_object, system_language,
languages_understood) languages_understood)
# add dogwhistle warnings to summary
summary_str = _add_dogwhistle_warnings(summary_str, content_str,
dogwhistles, translate)
content_all_str = str(summary_str) + ' ' + content_str content_all_str = str(summary_str) + ' ' + content_str
# does an emoji indicate a no boost preference? # does an emoji indicate a no boost preference?
# if so then don't show the repeat/announce icon # if so then don't show the repeat/announce icon
@ -2378,7 +2408,7 @@ def html_individual_post(recent_posts_cache: {}, max_recent_posts: int,
max_like_count: int, signing_priv_key_pem: str, max_like_count: int, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
timezone: str, mitm: bool, timezone: str, mitm: bool,
bold_reading: bool) -> str: bold_reading: bool, dogwhistles: {}) -> str:
"""Show an individual post as html """Show an individual post as html
""" """
original_post_json = post_json_object original_post_json = post_json_object
@ -2447,7 +2477,7 @@ def html_individual_post(recent_posts_cache: {}, max_recent_posts: int,
system_language, max_like_count, system_language, max_like_count,
False, authorized, False, False, False, False, False, authorized, False, False, False, False,
cw_lists, lists_enabled, timezone, mitm, cw_lists, lists_enabled, timezone, mitm,
bold_reading) bold_reading, dogwhistles)
message_id = remove_id_ending(post_json_object['id']) message_id = remove_id_ending(post_json_object['id'])
# show the previous posts # show the previous posts
@ -2488,7 +2518,8 @@ def html_individual_post(recent_posts_cache: {}, max_recent_posts: int,
False, False, False, False, False, False, False, False,
cw_lists, lists_enabled, cw_lists, lists_enabled,
timezone, mitm, timezone, mitm,
bold_reading) + post_str bold_reading,
dogwhistles) + post_str
# show the following posts # show the following posts
post_filename = locate_post(base_dir, nickname, domain, message_id) post_filename = locate_post(base_dir, nickname, domain, message_id)
@ -2527,7 +2558,7 @@ def html_individual_post(recent_posts_cache: {}, max_recent_posts: int,
False, False, False, False, False, False, False, False,
cw_lists, lists_enabled, cw_lists, lists_enabled,
timezone, False, timezone, False,
bold_reading) bold_reading, dogwhistles)
css_filename = base_dir + '/epicyon-profile.css' css_filename = base_dir + '/epicyon-profile.css'
if os.path.isfile(base_dir + '/epicyon.css'): if os.path.isfile(base_dir + '/epicyon.css'):
css_filename = base_dir + '/epicyon.css' css_filename = base_dir + '/epicyon.css'
@ -2555,7 +2586,8 @@ def html_post_replies(recent_posts_cache: {}, max_recent_posts: int,
max_like_count: int, max_like_count: int,
signing_priv_key_pem: str, cw_lists: {}, signing_priv_key_pem: str, cw_lists: {},
lists_enabled: str, lists_enabled: str,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Show the replies to an individual post as html """Show the replies to an individual post as html
""" """
replies_str = '' replies_str = ''
@ -2582,7 +2614,7 @@ def html_post_replies(recent_posts_cache: {}, max_recent_posts: int,
False, False, False, False,
cw_lists, lists_enabled, cw_lists, lists_enabled,
timezone, False, timezone, False,
bold_reading) bold_reading, dogwhistles)
css_filename = base_dir + '/epicyon-profile.css' css_filename = base_dir + '/epicyon-profile.css'
if os.path.isfile(base_dir + '/epicyon.css'): if os.path.isfile(base_dir + '/epicyon.css'):
@ -2611,7 +2643,8 @@ def html_emoji_reaction_picker(recent_posts_cache: {}, max_recent_posts: int,
max_like_count: int, signing_priv_key_pem: str, max_like_count: int, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
box_name: str, page_number: int, box_name: str, page_number: int,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Returns the emoji picker screen """Returns the emoji picker screen
""" """
reacted_to_post_str = \ reacted_to_post_str = \
@ -2635,7 +2668,7 @@ def html_emoji_reaction_picker(recent_posts_cache: {}, max_recent_posts: int,
max_like_count, max_like_count,
False, False, False, False, False, False, False, False, False, False, False, False,
cw_lists, lists_enabled, timezone, False, cw_lists, lists_enabled, timezone, False,
bold_reading) bold_reading, dogwhistles)
reactions_filename = base_dir + '/emoji/reactions.json' reactions_filename = base_dir + '/emoji/reactions.json'
if not os.path.isfile(reactions_filename): if not os.path.isfile(reactions_filename):

View File

@ -148,7 +148,7 @@ def html_profile_after_search(recent_posts_cache: {}, max_recent_posts: int,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
timezone: str, timezone: str,
onion_domain: str, i2p_domain: str, onion_domain: str, i2p_domain: str,
bold_reading: bool) -> str: bold_reading: bool, dogwhistles: {}) -> str:
"""Show a profile page after a search for a fediverse address """Show a profile page after a search for a fediverse address
""" """
http = False http = False
@ -388,7 +388,7 @@ def html_profile_after_search(recent_posts_cache: {}, max_recent_posts: int,
False, False, False, False, False, False,
cw_lists, lists_enabled, cw_lists, lists_enabled,
timezone, False, timezone, False,
bold_reading) bold_reading, dogwhistles)
i += 1 i += 1
if i >= 8: if i >= 8:
break break
@ -636,7 +636,7 @@ def html_profile(signing_priv_key_pem: str,
system_language, max_like_count, system_language, max_like_count,
shared_items_federated_domains, None, shared_items_federated_domains, None,
page_number, max_items_per_page, cw_lists, page_number, max_items_per_page, cw_lists,
lists_enabled) lists_enabled, {})
domain, port = get_domain_from_actor(profile_json['id']) domain, port = get_domain_from_actor(profile_json['id'])
if not domain: if not domain:
@ -1031,7 +1031,7 @@ def html_profile(signing_priv_key_pem: str,
max_like_count, max_like_count,
signing_priv_key_pem, signing_priv_key_pem,
cw_lists, lists_enabled, cw_lists, lists_enabled,
timezone, bold_reading) + license_str timezone, bold_reading, {}) + license_str
if not is_group: if not is_group:
if selected == 'following': if selected == 'following':
profile_str += \ profile_str += \
@ -1104,7 +1104,8 @@ def _html_profile_posts(recent_posts_cache: {}, max_recent_posts: int,
max_like_count: int, max_like_count: int,
signing_priv_key_pem: str, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Shows posts on the profile screen """Shows posts on the profile screen
These should only be public posts These should only be public posts
""" """
@ -1154,7 +1155,7 @@ def _html_profile_posts(recent_posts_cache: {}, max_recent_posts: int,
True, False, False, True, False, False,
cw_lists, lists_enabled, cw_lists, lists_enabled,
timezone, False, timezone, False,
bold_reading) bold_reading, dogwhistles)
if post_str: if post_str:
profile_str += post_str + separator_str profile_str += post_str + separator_str
ctr += 1 ctr += 1

View File

@ -621,7 +621,8 @@ def html_history_search(translate: {}, base_dir: str,
signing_priv_key_pem: str, signing_priv_key_pem: str,
cw_lists: {}, cw_lists: {},
lists_enabled: str, lists_enabled: str,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Show a page containing search results for your post history """Show a page containing search results for your post history
""" """
if historysearch.startswith("'"): if historysearch.startswith("'"):
@ -709,7 +710,8 @@ def html_history_search(translate: {}, base_dir: str,
show_individual_post_icons, show_individual_post_icons,
False, False, False, False, False, False, False, False,
cw_lists, lists_enabled, cw_lists, lists_enabled,
timezone, False, bold_reading) timezone, False, bold_reading,
dogwhistles)
if post_str: if post_str:
history_search_form += separator_str + post_str history_search_form += separator_str + post_str
index += 1 index += 1
@ -734,7 +736,8 @@ def html_hashtag_search(nickname: str, domain: str, port: int,
max_like_count: int, max_like_count: int,
signing_priv_key_pem: str, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Show a page containing search results for a hashtag """Show a page containing search results for a hashtag
or after selecting a hashtag from the swarm or after selecting a hashtag from the swarm
""" """
@ -897,7 +900,7 @@ def html_hashtag_search(nickname: str, domain: str, port: int,
show_public_only, show_public_only,
store_to_sache, False, cw_lists, store_to_sache, False, cw_lists,
lists_enabled, timezone, False, lists_enabled, timezone, False,
bold_reading) bold_reading, dogwhistles)
if post_str: if post_str:
hashtag_search_form += separator_str + post_str hashtag_search_form += separator_str + post_str
index += 1 index += 1

View File

@ -493,7 +493,8 @@ def html_timeline(css_cache: {}, default_timeline: str,
shared_items_federated_domains: [], shared_items_federated_domains: [],
signing_priv_key_pem: str, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Show the timeline as html """Show the timeline as html
""" """
enable_timing_log = False enable_timing_log = False
@ -1000,7 +1001,7 @@ def html_timeline(css_cache: {}, default_timeline: str,
False, True, use_cache_only, False, True, use_cache_only,
cw_lists, lists_enabled, cw_lists, lists_enabled,
timezone, mitm, timezone, mitm,
bold_reading) bold_reading, dogwhistles)
_log_timeline_timing(enable_timing_log, _log_timeline_timing(enable_timing_log,
timeline_start_time, box_name, '12') timeline_start_time, box_name, '12')
@ -1238,7 +1239,8 @@ def html_shares(css_cache: {}, default_timeline: str,
shared_items_federated_domains: [], shared_items_federated_domains: [],
signing_priv_key_pem: str, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Show the shares timeline as html """Show the shares timeline as html
""" """
manually_approve_followers = \ manually_approve_followers = \
@ -1269,7 +1271,7 @@ def html_shares(css_cache: {}, default_timeline: str,
shared_items_federated_domains, shared_items_federated_domains,
signing_priv_key_pem, signing_priv_key_pem,
cw_lists, lists_enabled, timezone, cw_lists, lists_enabled, timezone,
bold_reading) bold_reading, dogwhistles)
def html_wanted(css_cache: {}, default_timeline: str, def html_wanted(css_cache: {}, default_timeline: str,
@ -1298,7 +1300,8 @@ def html_wanted(css_cache: {}, default_timeline: str,
shared_items_federated_domains: [], shared_items_federated_domains: [],
signing_priv_key_pem: str, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Show the wanted timeline as html """Show the wanted timeline as html
""" """
manually_approve_followers = \ manually_approve_followers = \
@ -1329,7 +1332,7 @@ def html_wanted(css_cache: {}, default_timeline: str,
shared_items_federated_domains, shared_items_federated_domains,
signing_priv_key_pem, signing_priv_key_pem,
cw_lists, lists_enabled, timezone, cw_lists, lists_enabled, timezone,
bold_reading) bold_reading, dogwhistles)
def html_inbox(css_cache: {}, default_timeline: str, def html_inbox(css_cache: {}, default_timeline: str,
@ -1359,7 +1362,8 @@ def html_inbox(css_cache: {}, default_timeline: str,
shared_items_federated_domains: [], shared_items_federated_domains: [],
signing_priv_key_pem: str, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Show the inbox as html """Show the inbox as html
""" """
manually_approve_followers = \ manually_approve_followers = \
@ -1390,7 +1394,7 @@ def html_inbox(css_cache: {}, default_timeline: str,
shared_items_federated_domains, shared_items_federated_domains,
signing_priv_key_pem, signing_priv_key_pem,
cw_lists, lists_enabled, timezone, cw_lists, lists_enabled, timezone,
bold_reading) bold_reading, dogwhistles)
def html_bookmarks(css_cache: {}, default_timeline: str, def html_bookmarks(css_cache: {}, default_timeline: str,
@ -1420,7 +1424,8 @@ def html_bookmarks(css_cache: {}, default_timeline: str,
shared_items_federated_domains: [], shared_items_federated_domains: [],
signing_priv_key_pem: str, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Show the bookmarks as html """Show the bookmarks as html
""" """
manually_approve_followers = \ manually_approve_followers = \
@ -1450,7 +1455,7 @@ def html_bookmarks(css_cache: {}, default_timeline: str,
access_keys, system_language, max_like_count, access_keys, system_language, max_like_count,
shared_items_federated_domains, signing_priv_key_pem, shared_items_federated_domains, signing_priv_key_pem,
cw_lists, lists_enabled, timezone, cw_lists, lists_enabled, timezone,
bold_reading) bold_reading, dogwhistles)
def html_inbox_dms(css_cache: {}, default_timeline: str, def html_inbox_dms(css_cache: {}, default_timeline: str,
@ -1480,7 +1485,8 @@ def html_inbox_dms(css_cache: {}, default_timeline: str,
shared_items_federated_domains: [], shared_items_federated_domains: [],
signing_priv_key_pem: str, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Show the DM timeline as html """Show the DM timeline as html
""" """
artist = is_artist(base_dir, nickname) artist = is_artist(base_dir, nickname)
@ -1506,7 +1512,7 @@ def html_inbox_dms(css_cache: {}, default_timeline: str,
shared_items_federated_domains, shared_items_federated_domains,
signing_priv_key_pem, signing_priv_key_pem,
cw_lists, lists_enabled, timezone, cw_lists, lists_enabled, timezone,
bold_reading) bold_reading, dogwhistles)
def html_inbox_replies(css_cache: {}, default_timeline: str, def html_inbox_replies(css_cache: {}, default_timeline: str,
@ -1536,7 +1542,8 @@ def html_inbox_replies(css_cache: {}, default_timeline: str,
shared_items_federated_domains: [], shared_items_federated_domains: [],
signing_priv_key_pem: str, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Show the replies timeline as html """Show the replies timeline as html
""" """
artist = is_artist(base_dir, nickname) artist = is_artist(base_dir, nickname)
@ -1560,7 +1567,8 @@ def html_inbox_replies(css_cache: {}, default_timeline: str,
allow_local_network_access, text_mode_banner, allow_local_network_access, text_mode_banner,
access_keys, system_language, max_like_count, access_keys, system_language, max_like_count,
shared_items_federated_domains, signing_priv_key_pem, shared_items_federated_domains, signing_priv_key_pem,
cw_lists, lists_enabled, timezone, bold_reading) cw_lists, lists_enabled, timezone, bold_reading,
dogwhistles)
def html_inbox_media(css_cache: {}, default_timeline: str, def html_inbox_media(css_cache: {}, default_timeline: str,
@ -1590,7 +1598,8 @@ def html_inbox_media(css_cache: {}, default_timeline: str,
shared_items_federated_domains: [], shared_items_federated_domains: [],
signing_priv_key_pem: str, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Show the media timeline as html """Show the media timeline as html
""" """
artist = is_artist(base_dir, nickname) artist = is_artist(base_dir, nickname)
@ -1614,7 +1623,8 @@ def html_inbox_media(css_cache: {}, default_timeline: str,
allow_local_network_access, text_mode_banner, allow_local_network_access, text_mode_banner,
access_keys, system_language, max_like_count, access_keys, system_language, max_like_count,
shared_items_federated_domains, signing_priv_key_pem, shared_items_federated_domains, signing_priv_key_pem,
cw_lists, lists_enabled, timezone, bold_reading) cw_lists, lists_enabled, timezone, bold_reading,
dogwhistles)
def html_inbox_blogs(css_cache: {}, default_timeline: str, def html_inbox_blogs(css_cache: {}, default_timeline: str,
@ -1644,7 +1654,8 @@ def html_inbox_blogs(css_cache: {}, default_timeline: str,
shared_items_federated_domains: [], shared_items_federated_domains: [],
signing_priv_key_pem: str, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Show the blogs timeline as html """Show the blogs timeline as html
""" """
artist = is_artist(base_dir, nickname) artist = is_artist(base_dir, nickname)
@ -1668,7 +1679,8 @@ def html_inbox_blogs(css_cache: {}, default_timeline: str,
allow_local_network_access, text_mode_banner, allow_local_network_access, text_mode_banner,
access_keys, system_language, max_like_count, access_keys, system_language, max_like_count,
shared_items_federated_domains, signing_priv_key_pem, shared_items_federated_domains, signing_priv_key_pem,
cw_lists, lists_enabled, timezone, bold_reading) cw_lists, lists_enabled, timezone, bold_reading,
dogwhistles)
def html_inbox_features(css_cache: {}, default_timeline: str, def html_inbox_features(css_cache: {}, default_timeline: str,
@ -1699,7 +1711,8 @@ def html_inbox_features(css_cache: {}, default_timeline: str,
shared_items_federated_domains: [], shared_items_federated_domains: [],
signing_priv_key_pem: str, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Show the features timeline as html """Show the features timeline as html
""" """
return html_timeline(css_cache, default_timeline, return html_timeline(css_cache, default_timeline,
@ -1722,7 +1735,8 @@ def html_inbox_features(css_cache: {}, default_timeline: str,
allow_local_network_access, text_mode_banner, allow_local_network_access, text_mode_banner,
access_keys, system_language, max_like_count, access_keys, system_language, max_like_count,
shared_items_federated_domains, signing_priv_key_pem, shared_items_federated_domains, signing_priv_key_pem,
cw_lists, lists_enabled, timezone, bold_reading) cw_lists, lists_enabled, timezone, bold_reading,
dogwhistles)
def html_inbox_news(css_cache: {}, default_timeline: str, def html_inbox_news(css_cache: {}, default_timeline: str,
@ -1752,7 +1766,8 @@ def html_inbox_news(css_cache: {}, default_timeline: str,
shared_items_federated_domains: [], shared_items_federated_domains: [],
signing_priv_key_pem: str, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Show the news timeline as html """Show the news timeline as html
""" """
return html_timeline(css_cache, default_timeline, return html_timeline(css_cache, default_timeline,
@ -1775,7 +1790,8 @@ def html_inbox_news(css_cache: {}, default_timeline: str,
allow_local_network_access, text_mode_banner, allow_local_network_access, text_mode_banner,
access_keys, system_language, max_like_count, access_keys, system_language, max_like_count,
shared_items_federated_domains, signing_priv_key_pem, shared_items_federated_domains, signing_priv_key_pem,
cw_lists, lists_enabled, timezone, bold_reading) cw_lists, lists_enabled, timezone, bold_reading,
dogwhistles)
def html_outbox(css_cache: {}, default_timeline: str, def html_outbox(css_cache: {}, default_timeline: str,
@ -1805,7 +1821,8 @@ def html_outbox(css_cache: {}, default_timeline: str,
shared_items_federated_domains: [], shared_items_federated_domains: [],
signing_priv_key_pem: str, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str, cw_lists: {}, lists_enabled: str,
timezone: str, bold_reading: bool) -> str: timezone: str, bold_reading: bool,
dogwhistles: {}) -> str:
"""Show the Outbox as html """Show the Outbox as html
""" """
manually_approve_followers = \ manually_approve_followers = \
@ -1831,4 +1848,5 @@ def html_outbox(css_cache: {}, default_timeline: str,
allow_local_network_access, text_mode_banner, allow_local_network_access, text_mode_banner,
access_keys, system_language, max_like_count, access_keys, system_language, max_like_count,
shared_items_federated_domains, signing_priv_key_pem, shared_items_federated_domains, signing_priv_key_pem,
cw_lists, lists_enabled, timezone, bold_reading) cw_lists, lists_enabled, timezone, bold_reading,
dogwhistles)