diff --git a/content.py b/content.py
index e5b1af646..5d0a54a6b 100644
--- a/content.py
+++ b/content.py
@@ -1008,6 +1008,147 @@ def _auto_tag(base_dir: str, nickname: str, domain: str,
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,
nickname: str, domain: str, content: str,
recipients: [], hashtags: {},
@@ -1022,12 +1163,7 @@ def add_html_tags(base_dir: str, http_prefix: str,
content = content.replace('\r', '')
content = content.replace('\n', ' --linebreak-- ')
content = _add_music_tag(content, 'nowplaying')
- content_simplified = \
- 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(' ')
+ words = _get_simplified_content(content).split(' ')
# remove . for words which are not mentions
new_words = []
diff --git a/daemon.py b/daemon.py
index df2daa8ef..7f60035aa 100644
--- a/daemon.py
+++ b/daemon.py
@@ -326,6 +326,7 @@ from utils import has_group_type
from manualapprove import manual_deny_follow_request_thread
from manualapprove import manual_approve_follow_request_thread
from announce import create_announce
+from content import load_dogwhistles
from content import valid_url_lengths
from content import contains_invalid_local_links
from content import get_price_from_string
@@ -1595,7 +1596,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.max_recent_posts,
self.server.cw_lists,
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,
max_outbox_threads_per_account: int) -> int:
@@ -3182,7 +3184,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled,
self.server.default_timeline,
reply_is_chat,
- bold_reading).encode('utf-8')
+ bold_reading,
+ self.server.dogwhistles).encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
cookie, calling_domain, False)
@@ -3326,7 +3329,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled,
self.server.default_timeline,
reply_is_chat,
- bold_reading).encode('utf-8')
+ bold_reading,
+ self.server.dogwhistles).encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
cookie, calling_domain, False)
@@ -3888,7 +3892,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.dogwhistles)
if hashtag_str:
msg = hashtag_str.encode('utf-8')
msglen = len(msg)
@@ -3993,7 +3998,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.dogwhistles)
if history_str:
msg = history_str.encode('utf-8')
msglen = len(msg)
@@ -4073,7 +4079,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.dogwhistles)
if bookmarks_str:
msg = bookmarks_str.encode('utf-8')
msglen = len(msg)
@@ -4243,7 +4250,8 @@ class PubServer(BaseHTTPRequestHandler):
timezone,
self.server.onion_domain,
self.server.i2p_domain,
- bold_reading)
+ bold_reading,
+ self.server.dogwhistles)
if profile_str:
msg = profile_str.encode('utf-8')
msglen = len(msg)
@@ -5098,6 +5106,27 @@ class PubServer(BaseHTTPRequestHandler):
print('EX: _newswire_update unable to delete ' +
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
hashtag_rules_filename = \
base_dir + '/accounts/hashtagrules.txt'
@@ -8471,7 +8500,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.dogwhistles)
if hashtag_str:
msg = hashtag_str.encode('utf-8')
msglen = len(msg)
@@ -8719,7 +8749,8 @@ class PubServer(BaseHTTPRequestHandler):
False, True, False,
self.server.cw_lists,
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_path_str = \
@@ -9265,7 +9296,8 @@ class PubServer(BaseHTTPRequestHandler):
False, True, False,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, mitm, bold_reading)
+ timezone, mitm, bold_reading,
+ self.server.dogwhistles)
else:
print('WARN: Liked post not found: ' + liked_post_filename)
# clear the icon from the cache so that it gets updated
@@ -9450,7 +9482,8 @@ class PubServer(BaseHTTPRequestHandler):
False, True, False,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, mitm, bold_reading)
+ timezone, mitm, bold_reading,
+ self.server.dogwhistles)
else:
print('WARN: Unliked post not found: ' + liked_post_filename)
# clear the icon from the cache so that it gets updated
@@ -9664,7 +9697,8 @@ class PubServer(BaseHTTPRequestHandler):
False, True, False,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, mitm, bold_reading)
+ timezone, mitm, bold_reading,
+ self.server.dogwhistles)
else:
print('WARN: Emoji reaction post not found: ' +
reaction_post_filename)
@@ -9868,7 +9902,8 @@ class PubServer(BaseHTTPRequestHandler):
False, True, False,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, mitm, bold_reading)
+ timezone, mitm, bold_reading,
+ self.server.dogwhistles)
else:
print('WARN: Unreaction post not found: ' +
reaction_post_filename)
@@ -9974,7 +10009,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists,
self.server.lists_enabled,
timeline_str, page_number,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.dogwhistles)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@@ -10116,7 +10152,8 @@ class PubServer(BaseHTTPRequestHandler):
False, True, False,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, mitm, bold_reading)
+ timezone, mitm, bold_reading,
+ self.server.dogwhistles)
else:
print('WARN: Bookmarked post not found: ' + bookmark_filename)
# self._post_to_outbox(bookmark_json,
@@ -10265,7 +10302,8 @@ class PubServer(BaseHTTPRequestHandler):
False, True, False,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, mitm, bold_reading)
+ timezone, mitm, bold_reading,
+ self.server.dogwhistles)
else:
print('WARN: Unbookmarked post not found: ' +
bookmark_filename)
@@ -10375,7 +10413,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.max_like_count,
self.server.signing_priv_key_pem,
self.server.cw_lists,
- self.server.lists_enabled)
+ self.server.lists_enabled,
+ self.server.dogwhistles)
if delete_str:
delete_str_len = len(delete_str)
self._set_headers('text/html', delete_str_len,
@@ -10502,7 +10541,8 @@ class PubServer(BaseHTTPRequestHandler):
use_cache_only,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, mitm, bold_reading)
+ timezone, mitm, bold_reading,
+ self.server.dogwhistles)
else:
print('WARN: Muted post not found: ' + mute_filename)
@@ -10629,7 +10669,8 @@ class PubServer(BaseHTTPRequestHandler):
use_cache_only,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, mitm, bold_reading)
+ timezone, mitm, bold_reading,
+ self.server.dogwhistles)
else:
print('WARN: Unmuted post not found: ' + mute_filename)
if calling_domain.endswith('.onion') and onion_domain:
@@ -10758,7 +10799,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.dogwhistles)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@@ -10862,7 +10904,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.dogwhistles)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@@ -11316,7 +11359,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists,
self.server.lists_enabled,
'inbox', self.server.default_timeline,
- bold_reading)
+ bold_reading, self.server.dogwhistles)
if not msg:
self._404()
return True
@@ -11383,7 +11426,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists,
self.server.lists_enabled,
'inbox', self.server.default_timeline,
- bold_reading, 'shares')
+ bold_reading, self.server.dogwhistles,
+ 'shares')
if not msg:
self._404()
return True
@@ -11472,7 +11516,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, mitm, bold_reading)
+ timezone, mitm, bold_reading,
+ self.server.dogwhistles)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@@ -11788,7 +11833,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.dogwhistles)
if getreq_start_time:
fitness_performance(getreq_start_time,
self.server.fitness,
@@ -11959,7 +12005,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.dogwhistles)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@@ -12119,7 +12166,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.replies)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@@ -12277,7 +12325,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.dogwhistles)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@@ -12435,7 +12484,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.dogwhistles)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@@ -12602,7 +12652,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.dogwhistles)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@@ -12772,7 +12823,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.dogwhistles)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@@ -12892,7 +12944,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled, timezone,
- bold_reading)
+ bold_reading, self.server.dogwhistles)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@@ -12986,7 +13038,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.dogwhistles)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@@ -13123,7 +13176,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.dogwhistles)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@@ -13274,7 +13328,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.dogwhistles)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@@ -13422,7 +13477,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
- timezone, bold_reading)
+ timezone, bold_reading,
+ self.server.dogwhistles)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@@ -14799,7 +14855,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled,
self.server.default_timeline,
reply_is_chat,
- bold_reading).encode('utf-8')
+ bold_reading,
+ self.server.dogwhistles).encode('utf-8')
if not msg:
print('Error replying to ' + in_reply_to_url)
self._404()
@@ -14942,7 +14999,8 @@ class PubServer(BaseHTTPRequestHandler):
http_prefix,
self.server.default_timeline,
self.server.theme_name,
- access_keys)
+ access_keys,
+ self.server.dogwhistles)
if msg:
msg = msg.encode('utf-8')
msglen = len(msg)
@@ -20967,6 +21025,12 @@ def run_daemon(preferred_podcast_formats: [],
# scan the theme directory for any svg files containing scripts
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
# eg ['audio/opus', 'audio/mp3']
httpd.preferred_podcast_formats = preferred_podcast_formats
diff --git a/default_dogwhistles.txt b/default_dogwhistles.txt
new file mode 100644
index 000000000..4860e5081
--- /dev/null
+++ b/default_dogwhistles.txt
@@ -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
+
diff --git a/inbox.py b/inbox.py
index 8b64d4a1b..091c6bbd4 100644
--- a/inbox.py
+++ b/inbox.py
@@ -131,6 +131,7 @@ from conversation import update_conversation
from webapp_hashtagswarm import html_hash_tag_swarm
from person import valid_sending_actor
from fitnessFunctions import fitness_performance
+from content import load_dogwhistles
from content import valid_url_lengths
from content import remove_script
@@ -408,7 +409,8 @@ def _inbox_store_post_to_html_cache(recent_posts_cache: {},
lists_enabled: str,
timezone: str,
mitm: bool,
- bold_reading: bool) -> None:
+ bold_reading: bool,
+ dogwhistles: {}) -> None:
"""Converts the json post into html and stores it in a cache
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,
not_dm, True, True, False, True, False,
cw_lists, lists_enabled, timezone, mitm,
- bold_reading)
+ bold_reading, dogwhistles)
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,
peertube_instances: [],
theme_name: str, max_like_count: int,
- cw_lists: {}) -> bool:
+ cw_lists: {}, dogwhistles: {}) -> bool:
"""A post was edited
"""
if not has_object_dict(message_json):
@@ -1230,7 +1232,7 @@ def _receive_edit_to_post(recent_posts_cache: {}, message_json: {},
manually_approve_followers,
False, True, False, cw_lists,
lists_enabled, timezone, mitm,
- bold_reading)
+ bold_reading, dogwhistles)
return True
@@ -1250,7 +1252,7 @@ def _receive_update_activity(recent_posts_cache: {}, session, base_dir: str,
show_published_date_only: bool,
peertube_instances: [],
theme_name: str, max_like_count: int,
- cw_lists: {}) -> bool:
+ cw_lists: {}, dogwhistles: {}) -> bool:
"""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,
peertube_instances,
theme_name, max_like_count,
- cw_lists):
+ cw_lists, dogwhistles):
print('EDITPOST: received ' + message_json['object']['id'])
return True
else:
@@ -1340,7 +1342,7 @@ def _receive_like(recent_posts_cache: {},
theme_name: str, system_language: str,
max_like_count: int, cw_lists: {},
lists_enabled: str,
- bold_reading: bool) -> bool:
+ bold_reading: bool, dogwhistles: {}) -> bool:
"""Receives a Like activity within the POST section of HTTPServer
"""
if message_json['type'] != 'Like':
@@ -1449,7 +1451,7 @@ def _receive_like(recent_posts_cache: {},
manually_approve_followers,
False, True, False, cw_lists,
lists_enabled, timezone, mitm,
- bold_reading)
+ bold_reading, dogwhistles)
return True
@@ -1469,7 +1471,7 @@ def _receive_undo_like(recent_posts_cache: {},
theme_name: str, system_language: str,
max_like_count: int, cw_lists: {},
lists_enabled: str,
- bold_reading: bool) -> bool:
+ bold_reading: bool, dogwhistles: {}) -> bool:
"""Receives an undo like activity within the POST section of HTTPServer
"""
if message_json['type'] != 'Undo':
@@ -1568,7 +1570,7 @@ def _receive_undo_like(recent_posts_cache: {},
manually_approve_followers,
False, True, False, cw_lists,
lists_enabled, timezone, mitm,
- bold_reading)
+ bold_reading, dogwhistles)
return True
@@ -1588,7 +1590,8 @@ def _receive_reaction(recent_posts_cache: {},
allow_local_network_access: bool,
theme_name: str, system_language: str,
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
"""
if message_json['type'] != 'EmojiReact':
@@ -1719,7 +1722,7 @@ def _receive_reaction(recent_posts_cache: {},
manually_approve_followers,
False, True, False, cw_lists,
lists_enabled, timezone, mitm,
- bold_reading)
+ bold_reading, dogwhistles)
return True
@@ -1741,7 +1744,7 @@ def _receive_undo_reaction(recent_posts_cache: {},
theme_name: str, system_language: str,
max_like_count: int, cw_lists: {},
lists_enabled: str,
- bold_reading: bool) -> bool:
+ bold_reading: bool, dogwhistles: {}) -> bool:
"""Receives an undo emoji reaction within the POST section of HTTPServer
"""
if message_json['type'] != 'Undo':
@@ -1858,7 +1861,7 @@ def _receive_undo_reaction(recent_posts_cache: {},
manually_approve_followers,
False, True, False, cw_lists,
lists_enabled, timezone, mitm,
- bold_reading)
+ bold_reading, dogwhistles)
return True
@@ -1876,7 +1879,8 @@ def _receive_bookmark(recent_posts_cache: {},
allow_local_network_access: bool,
theme_name: str, system_language: str,
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
"""
if not message_json.get('type'):
@@ -1973,7 +1977,7 @@ def _receive_bookmark(recent_posts_cache: {},
manually_approve_followers,
False, True, False, cw_lists,
lists_enabled, timezone, mitm,
- bold_reading)
+ bold_reading, dogwhistles)
return True
@@ -1993,7 +1997,8 @@ def _receive_undo_bookmark(recent_posts_cache: {},
allow_local_network_access: bool,
theme_name: str, system_language: str,
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
"""
if not message_json.get('type'):
@@ -2090,7 +2095,8 @@ def _receive_undo_bookmark(recent_posts_cache: {},
show_individual_post_icons,
manually_approve_followers,
False, True, False, cw_lists, lists_enabled,
- timezone, mitm, bold_reading)
+ timezone, mitm, bold_reading,
+ dogwhistles)
return True
@@ -2186,7 +2192,8 @@ def _receive_announce(recent_posts_cache: {},
allow_deletion: bool,
peertube_instances: [],
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
"""
if message_json['type'] != 'Announce':
@@ -2309,7 +2316,7 @@ def _receive_announce(recent_posts_cache: {},
manually_approve_followers,
False, True, False, cw_lists,
lists_enabled, timezone, mitm,
- bold_reading)
+ bold_reading, dogwhistles)
if not announce_html:
print('WARN: Unable to generate html for announce ' +
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,
max_like_count: int,
cw_lists: {}, lists_enabled: bool,
- bold_reading: bool) -> None:
+ bold_reading: bool, dogwhistles: {}) -> None:
"""Updates the votes on a Question/poll
"""
# 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,
False, True, False, cw_lists,
lists_enabled, timezone, mitm,
- bold_reading)
+ bold_reading, dogwhistles)
# add id to inbox index
inbox_update_index('inbox', base_dir, handle,
@@ -3676,7 +3683,8 @@ def _inbox_after_initial(server, inbox_start_time,
cw_lists: {}, lists_enabled: str,
content_license_url: str,
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
"""
# 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,
theme_name, system_language,
max_like_count, cw_lists, lists_enabled,
- bold_reading):
+ bold_reading, dogwhistles):
if debug:
print('DEBUG: Like accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness,
@@ -3749,7 +3757,7 @@ def _inbox_after_initial(server, inbox_start_time,
allow_local_network_access,
theme_name, system_language,
max_like_count, cw_lists, lists_enabled,
- bold_reading):
+ bold_reading, dogwhistles):
if debug:
print('DEBUG: Undo like accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness,
@@ -3775,7 +3783,7 @@ def _inbox_after_initial(server, inbox_start_time,
allow_local_network_access,
theme_name, system_language,
max_like_count, cw_lists, lists_enabled,
- bold_reading):
+ bold_reading, dogwhistles):
if debug:
print('DEBUG: Reaction accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness,
@@ -3802,7 +3810,7 @@ def _inbox_after_initial(server, inbox_start_time,
allow_local_network_access,
theme_name, system_language,
max_like_count, cw_lists, lists_enabled,
- bold_reading):
+ bold_reading, dogwhistles):
if debug:
print('DEBUG: Undo reaction accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness,
@@ -3829,7 +3837,7 @@ def _inbox_after_initial(server, inbox_start_time,
allow_local_network_access,
theme_name, system_language,
max_like_count, cw_lists, lists_enabled,
- bold_reading):
+ bold_reading, dogwhistles):
if debug:
print('DEBUG: Bookmark accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness,
@@ -3856,7 +3864,7 @@ def _inbox_after_initial(server, inbox_start_time,
allow_local_network_access,
theme_name, system_language,
max_like_count, cw_lists, lists_enabled,
- bold_reading):
+ bold_reading, dogwhistles):
if debug:
print('DEBUG: Undo bookmark accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness,
@@ -3891,7 +3899,7 @@ def _inbox_after_initial(server, inbox_start_time,
allow_deletion,
peertube_instances,
max_like_count, cw_lists, lists_enabled,
- bold_reading):
+ bold_reading, dogwhistles):
if debug:
print('DEBUG: Announce accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness,
@@ -4033,7 +4041,7 @@ def _inbox_after_initial(server, inbox_start_time,
theme_name, system_language,
max_like_count,
cw_lists, lists_enabled,
- bold_reading)
+ bold_reading, dogwhistles)
fitness_performance(inbox_start_time, server.fitness,
'INBOX', '_receive_question_vote',
debug)
@@ -4277,7 +4285,8 @@ def _inbox_after_initial(server, inbox_start_time,
cw_lists,
lists_enabled,
timezone, mitm,
- bold_reading)
+ bold_reading,
+ dogwhistles)
fitness_performance(inbox_start_time,
server.fitness,
'INBOX',
@@ -5237,6 +5246,11 @@ def run_inbox_queue(server,
debug)
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
# This makes the filename and the id consistent
# if queue_json['post'].get('id'):
@@ -5328,7 +5342,7 @@ def run_inbox_queue(server,
show_published_date_only,
peertube_instances,
theme_name, max_like_count,
- cw_lists):
+ cw_lists, dogwhistles):
if debug:
print('Queue: Update accepted from ' + key_id)
if os.path.isfile(queue_filename):
@@ -5456,7 +5470,7 @@ def run_inbox_queue(server,
cw_lists, lists_enabled,
content_license_url,
languages_understood, mitm,
- bold_reading)
+ bold_reading, dogwhistles)
fitness_performance(inbox_start_time, server.fitness,
'INBOX', 'handle_after_initial',
debug)
diff --git a/outbox.py b/outbox.py
index 878897a0c..9698f1a11 100644
--- a/outbox.py
+++ b/outbox.py
@@ -213,7 +213,8 @@ def post_message_to_outbox(session, translate: {},
max_like_count: int,
max_recent_posts: int, cw_lists: {},
lists_enabled: str,
- content_license_url: str) -> bool:
+ content_license_url: str,
+ dogwhistles: {}) -> bool:
"""post is received by the outbox
Client to server message post
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,
cw_lists, lists_enabled,
timezone, mitm,
- bold_reading)
+ bold_reading, dogwhistles)
if outbox_announce(recent_posts_cache,
base_dir, message_json, debug):
diff --git a/schedule.py b/schedule.py
index 52460847b..e93a056bb 100644
--- a/schedule.py
+++ b/schedule.py
@@ -142,7 +142,8 @@ def _update_post_schedule(base_dir: str, handle: str, httpd,
httpd.max_recent_posts,
httpd.cw_lists,
httpd.lists_enabled,
- httpd.content_license_url):
+ httpd.content_license_url,
+ httpd.dogwhistles):
index_lines.remove(line)
try:
os.remove(post_filename)
diff --git a/tests.py b/tests.py
index e6ed196a1..990299132 100644
--- a/tests.py
+++ b/tests.py
@@ -132,6 +132,7 @@ from inbox import valid_inbox
from inbox import valid_inbox_filenames
from inbox import cache_svg_images
from categories import guess_hashtag_category
+from content import detect_dogwhistles
from content import remove_script
from content import create_edits_html
from content import content_diff
@@ -7326,6 +7327,30 @@ def _test_remove_end_of_line():
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():
base_dir = os.getcwd()
print('Running tests...')
@@ -7343,6 +7368,7 @@ def run_all_tests():
_test_checkbox_names()
_test_thread_functions()
_test_functions()
+ _test_dogwhistles()
_test_remove_end_of_line()
_test_translation_labels()
_test_color_contrast_value(base_dir)
diff --git a/translations/ar.json b/translations/ar.json
index 5340cc17b..cc1c0383c 100644
--- a/translations/ar.json
+++ b/translations/ar.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "قم بالتبديل إلى عرض الاعتدال",
"Minimize attached images": "تصغير الصور المرفقة",
"SHOW MEDIA": "عرض الوسائط",
- "ActivityPub Specification": "مواصفات ActivityPub"
+ "ActivityPub Specification": "مواصفات ActivityPub",
+ "Dogwhistle words": "كلمات Dogwhistle",
+ "Content warnings will be added for the following": "ستتم إضافة تحذيرات المحتوى لما يلي"
}
diff --git a/translations/bn.json b/translations/bn.json
index 553d29482..7fb7c941d 100644
--- a/translations/bn.json
+++ b/translations/bn.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "সংযম দৃশ্যে স্যুইচ করুন",
"Minimize attached images": "সংযুক্ত ছবি ছোট করুন",
"SHOW MEDIA": "মিডিয়া দেখান",
- "ActivityPub Specification": "ActivityPub স্পেসিফিকেশন"
+ "ActivityPub Specification": "ActivityPub স্পেসিফিকেশন",
+ "Dogwhistle words": "কুকুরের হুইসেল শব্দ",
+ "Content warnings will be added for the following": "নিম্নলিখিত জন্য বিষয়বস্তু সতর্কতা যোগ করা হবে"
}
diff --git a/translations/ca.json b/translations/ca.json
index 67933223f..bfd994db7 100644
--- a/translations/ca.json
+++ b/translations/ca.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "Canvia a la visualització de moderació",
"Minimize attached images": "Minimitzar les imatges adjuntes",
"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"
}
diff --git a/translations/cy.json b/translations/cy.json
index 7247eee33..ff2ab6fc4 100644
--- a/translations/cy.json
+++ b/translations/cy.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "Newid i wedd safoni",
"Minimize attached images": "Lleihau delweddau sydd ynghlwm",
"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"
}
diff --git a/translations/de.json b/translations/de.json
index f19a7411d..962cfcbb9 100644
--- a/translations/de.json
+++ b/translations/de.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "Wechseln Sie zur Moderationsansicht",
"Minimize attached images": "Angehängte Bilder minimieren",
"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"
}
diff --git a/translations/el.json b/translations/el.json
index ea496a1fc..8a3cee2df 100644
--- a/translations/el.json
+++ b/translations/el.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "Μετάβαση σε προβολή εποπτείας",
"Minimize attached images": "Ελαχιστοποιήστε τις συνημμένες εικόνες",
"SHOW MEDIA": "ΔΕΙΤΕ ΜΕΣΑ",
- "ActivityPub Specification": "Προδιαγραφές ActivityPub"
+ "ActivityPub Specification": "Προδιαγραφές ActivityPub",
+ "Dogwhistle words": "Σφυρίχτρα λέξεις",
+ "Content warnings will be added for the following": "Θα προστεθούν προειδοποιήσεις περιεχομένου για τα ακόλουθα"
}
diff --git a/translations/en.json b/translations/en.json
index d283394d6..6b19ef3b2 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -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"
+ "ActivityPub Specification": "ActivityPub Specification",
+ "Dogwhistle words": "Dogwhistle words",
+ "Content warnings will be added for the following": "Content warnings will be added for the following"
}
diff --git a/translations/es.json b/translations/es.json
index c021c76a8..2a0f58af5 100644
--- a/translations/es.json
+++ b/translations/es.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "Cambiar a la vista de moderación",
"Minimize attached images": "Minimizar imágenes adjuntas",
"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"
}
diff --git a/translations/fr.json b/translations/fr.json
index 02adc075e..493cc8346 100644
--- a/translations/fr.json
+++ b/translations/fr.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "Passer en mode modération",
"Minimize attached images": "Réduire les images jointes",
"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"
}
diff --git a/translations/ga.json b/translations/ga.json
index 89e4839e1..d980c9432 100644
--- a/translations/ga.json
+++ b/translations/ga.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "Athraigh go dtí an t-amharc modhnóireachta",
"Minimize attached images": "Íoslaghdaigh íomhánna ceangailte",
"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"
}
diff --git a/translations/hi.json b/translations/hi.json
index 25da4a044..95f4c8a66 100644
--- a/translations/hi.json
+++ b/translations/hi.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "मॉडरेशन दृश्य पर स्विच करें",
"Minimize attached images": "संलग्न छवियों को छोटा करें",
"SHOW MEDIA": "मीडिया दिखाएं",
- "ActivityPub Specification": "गतिविधिपब विशिष्टता"
+ "ActivityPub Specification": "गतिविधिपब विशिष्टता",
+ "Dogwhistle words": "कुत्ते की सीटी शब्द",
+ "Content warnings will be added for the following": "निम्नलिखित के लिए सामग्री चेतावनियां जोड़ दी जाएंगी"
}
diff --git a/translations/it.json b/translations/it.json
index b120d93ea..f5b7cb4e6 100644
--- a/translations/it.json
+++ b/translations/it.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "Passa alla visualizzazione moderazione",
"Minimize attached images": "Riduci al minimo le immagini allegate",
"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"
}
diff --git a/translations/ja.json b/translations/ja.json
index 2ecea1bcb..5210d836c 100644
--- a/translations/ja.json
+++ b/translations/ja.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "モデレートビューに切り替えます",
"Minimize attached images": "添付画像を最小限に抑える",
"SHOW MEDIA": "メディアを表示",
- "ActivityPub Specification": "ActivityPubの仕様"
+ "ActivityPub Specification": "ActivityPubの仕様",
+ "Dogwhistle words": "犬笛の言葉",
+ "Content warnings will be added for the following": "以下のコンテンツ警告が追加されます"
}
diff --git a/translations/ko.json b/translations/ko.json
index 8dbfbb256..b0a9e2b55 100644
--- a/translations/ko.json
+++ b/translations/ko.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "검토 보기로 전환",
"Minimize attached images": "첨부된 이미지 최소화",
"SHOW MEDIA": "미디어 표시",
- "ActivityPub Specification": "ActivityPub 사양"
+ "ActivityPub Specification": "ActivityPub 사양",
+ "Dogwhistle words": "개 휘파람 단어",
+ "Content warnings will be added for the following": "다음에 대한 콘텐츠 경고가 추가됩니다."
}
diff --git a/translations/ku.json b/translations/ku.json
index fbbe26b22..7df2289a8 100644
--- a/translations/ku.json
+++ b/translations/ku.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "Biguherîne bo dîtina moderatoriyê",
"Minimize attached images": "Wêneyên pêvekirî kêm bikin",
"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"
}
diff --git a/translations/nl.json b/translations/nl.json
index 775c8d284..8a94c779d 100644
--- a/translations/nl.json
+++ b/translations/nl.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "Overschakelen naar moderatieweergave",
"Minimize attached images": "Bijgevoegde afbeeldingen minimaliseren",
"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:"
}
diff --git a/translations/oc.json b/translations/oc.json
index 98d73eedc..42e21d93b 100644
--- a/translations/oc.json
+++ b/translations/oc.json
@@ -561,5 +561,7 @@
"Switch to moderation view": "Switch to moderation view",
"Minimize attached images": "Minimize attached images",
"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"
}
diff --git a/translations/pl.json b/translations/pl.json
index feb0e953e..75da3fdba 100644
--- a/translations/pl.json
+++ b/translations/pl.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "Przełącz na widok moderacji",
"Minimize attached images": "Zminimalizuj załączone obrazy",
"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"
}
diff --git a/translations/pt.json b/translations/pt.json
index b2725ce50..2adeebaa0 100644
--- a/translations/pt.json
+++ b/translations/pt.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "Mudar para a visualização de moderação",
"Minimize attached images": "Minimizar imagens anexadas",
"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"
}
diff --git a/translations/ru.json b/translations/ru.json
index bf21f25f3..e12bb1799 100644
--- a/translations/ru.json
+++ b/translations/ru.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "Перейти в режим модерации",
"Minimize attached images": "Свернуть прикрепленные изображения",
"SHOW MEDIA": "ПОКАЗАТЬ МЕДИА",
- "ActivityPub Specification": "Спецификация ActivityPub"
+ "ActivityPub Specification": "Спецификация ActivityPub",
+ "Dogwhistle words": "Собачий свисток",
+ "Content warnings will be added for the following": "Предупреждения о содержании будут добавлены для следующих"
}
diff --git a/translations/sw.json b/translations/sw.json
index 21d05b2b8..02682a5b5 100644
--- a/translations/sw.json
+++ b/translations/sw.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "Badili hadi mwonekano wa udhibiti",
"Minimize attached images": "Punguza picha zilizoambatishwa",
"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"
}
diff --git a/translations/tr.json b/translations/tr.json
index 95b1bba51..2eadf668a 100644
--- a/translations/tr.json
+++ b/translations/tr.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "Denetleme görünümüne geç",
"Minimize attached images": "Ekli resimleri simge durumuna küçült",
"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"
}
diff --git a/translations/uk.json b/translations/uk.json
index 86e92abaa..58723e75a 100644
--- a/translations/uk.json
+++ b/translations/uk.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "Перейти до режиму модерації",
"Minimize attached images": "Мінімізуйте вкладені зображення",
"SHOW MEDIA": "ПОКАЗАТИ ЗМІ",
- "ActivityPub Specification": "Специфікація ActivityPub"
+ "ActivityPub Specification": "Специфікація ActivityPub",
+ "Dogwhistle words": "Собачі слова",
+ "Content warnings will be added for the following": "Попередження про вміст буде додано для наступних"
}
diff --git a/translations/yi.json b/translations/yi.json
index 1ab354557..1a3f2fcf8 100644
--- a/translations/yi.json
+++ b/translations/yi.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "באַשטימען צו מאַדעריישאַן מיינונג",
"Minimize attached images": "מינאַמייז אַטאַטשט בילדער",
"SHOW MEDIA": "ווייַז מעדיע",
- "ActivityPub Specification": "ActivityPub באַשרייַבונג"
+ "ActivityPub Specification": "ActivityPub באַשרייַבונג",
+ "Dogwhistle words": "דאָגווהיסטלע ווערטער",
+ "Content warnings will be added for the following": "אינהאַלט וואָרנינגז וועט זיין מוסיף פֿאַר די פאלגענדע"
}
diff --git a/translations/zh.json b/translations/zh.json
index 0f1f7d9ae..e953072ec 100644
--- a/translations/zh.json
+++ b/translations/zh.json
@@ -565,5 +565,7 @@
"Switch to moderation view": "切换到审核视图",
"Minimize attached images": "最小化附加图像",
"SHOW MEDIA": "展示媒体",
- "ActivityPub Specification": "ActivityPub 规范"
+ "ActivityPub Specification": "ActivityPub 规范",
+ "Dogwhistle words": "狗哨的话",
+ "Content warnings will be added for the following": "将为以下内容添加内容警告"
}
diff --git a/webapp_column_right.py b/webapp_column_right.py
index 5e41fe570..5339f63f9 100644
--- a/webapp_column_right.py
+++ b/webapp_column_right.py
@@ -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,
domain: str, port: int, http_prefix: str,
default_timeline: str, theme: str,
- access_keys: {}) -> str:
+ access_keys: {}, dogwhistles: {}) -> str:
"""Shows the edit newswire screen
"""
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" ' + \
'spellcheck="true">' + filter_str + '\n'
+ dogwhistle_str = ''
+ for whistle, category in dogwhistles.items():
+ if not category:
+ continue
+ dogwhistle_str += whistle + ' -> ' + category + '\n'
+
+ edit_newswire_form += \
+ '
\n'
+ edit_newswire_form += '
'
+ edit_newswire_form += ' \n'
+
hashtag_rules_str = ''
hashtag_rules_filename = \
base_dir + '/accounts/hashtagrules.txt'
diff --git a/webapp_confirm.py b/webapp_confirm.py
index 63dce3c4f..5b8a13106 100644
--- a/webapp_confirm.py
+++ b/webapp_confirm.py
@@ -38,7 +38,8 @@ def html_confirm_delete(server, css_cache: {},
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) -> str:
+ cw_lists: {}, lists_enabled: str,
+ dogwhistles: {}) -> str:
"""Shows a screen asking to confirm the deletion of a post
"""
if '/statuses/' not in message_id:
@@ -90,7 +91,7 @@ def html_confirm_delete(server, css_cache: {},
theme_name, system_language, max_like_count,
False, False, False, False, False, False,
cw_lists, lists_enabled, timezone, mitm,
- bold_reading)
+ bold_reading, dogwhistles)
delete_post_str += '
' + \ diff --git a/webapp_create_post.py b/webapp_create_post.py index 51dfcc54e..f8653a685 100644 --- a/webapp_create_post.py +++ b/webapp_create_post.py @@ -224,7 +224,8 @@ def html_new_post(css_cache: {}, media_instance: bool, translate: {}, max_like_count: int, signing_priv_key_pem: str, cw_lists: {}, lists_enabled: str, box_name: str, - reply_is_chat: bool, bold_reading: bool) -> str: + reply_is_chat: bool, bold_reading: bool, + dogwhistles: {}) -> str: """New post screen """ reply_str = '' @@ -303,7 +304,7 @@ def html_new_post(css_cache: {}, media_instance: bool, translate: {}, False, False, False, cw_lists, lists_enabled, timezone, False, - bold_reading) + bold_reading, dogwhistles) reply_str = '\n' diff --git a/webapp_frontscreen.py b/webapp_frontscreen.py index f0996dc50..1e961d698 100644 --- a/webapp_frontscreen.py +++ b/webapp_frontscreen.py @@ -38,7 +38,8 @@ def _html_front_screen_posts(recent_posts_cache: {}, max_recent_posts: int, max_like_count: int, signing_priv_key_pem: str, cw_lists: {}, lists_enabled: str, - bold_reading: bool) -> str: + bold_reading: bool, + dogwhistles: {}) -> str: """Shows posts on the front screen of a news instance These should only be public blog posts from the features timeline 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, cw_lists, lists_enabled, timezone, False, - bold_reading) + bold_reading, dogwhistles) if post_str: profile_str += post_str + separator_str ctr += 1 @@ -120,7 +121,8 @@ def html_front_screen(signing_priv_key_pem: str, extra_json: {}, page_number: 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 """ bold_reading = False @@ -195,7 +197,7 @@ def html_front_screen(signing_priv_key_pem: str, max_like_count, signing_priv_key_pem, cw_lists, lists_enabled, - bold_reading) + license_str + bold_reading, dogwhistles) + license_str # Footer which is only used for system accounts profile_footer_str = ' \n' diff --git a/webapp_likers.py b/webapp_likers.py index 680c78b7d..6ff5970f0 100644 --- a/webapp_likers.py +++ b/webapp_likers.py @@ -41,7 +41,7 @@ def html_likers_of_post(base_dir: str, nickname: str, max_like_count: int, signing_priv_key_pem: str, cw_lists: {}, lists_enabled: str, box_name: str, default_timeline: str, - bold_reading: bool, + bold_reading: bool, dogwhistles: {}, dict_name: str = 'likes') -> str: """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, cw_lists, lists_enabled, - timezone, mitm, bold_reading) + timezone, mitm, bold_reading, + dogwhistles) # show likers beneath the post obj = post_json_object diff --git a/webapp_moderation.py b/webapp_moderation.py index a3914ce7b..50db5883e 100644 --- a/webapp_moderation.py +++ b/webapp_moderation.py @@ -57,7 +57,8 @@ def html_moderation(css_cache: {}, default_timeline: str, shared_items_federated_domains: [], signing_priv_key_pem: 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 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, max_like_count, shared_items_federated_domains, signing_priv_key_pem, cw_lists, lists_enabled, - timezone, bold_reading) + timezone, bold_reading, dogwhistles) def html_account_info(css_cache: {}, translate: {}, diff --git a/webapp_post.py b/webapp_post.py index f42207fb6..cc34a0fb3 100644 --- a/webapp_post.py +++ b/webapp_post.py @@ -62,6 +62,7 @@ from utils import get_domain_from_actor from utils import acct_dir from utils import local_actor_url from utils import is_unlisted_post +from content import detect_dogwhistles from content import create_edits_html from content import bold_reading_string from content import limit_repeated_words @@ -1555,6 +1556,30 @@ def _substitute_onion_domains(base_dir: str, content: str) -> str: 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, allow_downloads: bool, recent_posts_cache: {}, max_recent_posts: int, @@ -1583,7 +1608,8 @@ def individual_post_as_html(signing_priv_key_pem: str, cw_lists: {}, lists_enabled: str, timezone: str, - mitm: bool, bold_reading: bool) -> str: + mitm: bool, bold_reading: bool, + dogwhistles: {}) -> str: """ Shows a single post as html """ if not post_json_object: @@ -2142,6 +2168,10 @@ def individual_post_as_html(signing_priv_key_pem: str, if content_str: summary_str = get_summary_from_post(post_json_object, system_language, 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 # does an emoji indicate a no boost preference? # 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, cw_lists: {}, lists_enabled: str, timezone: str, mitm: bool, - bold_reading: bool) -> str: + bold_reading: bool, dogwhistles: {}) -> str: """Show an individual post as html """ 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, False, authorized, False, False, False, False, cw_lists, lists_enabled, timezone, mitm, - bold_reading) + bold_reading, dogwhistles) message_id = remove_id_ending(post_json_object['id']) # show the previous posts @@ -2488,7 +2518,8 @@ def html_individual_post(recent_posts_cache: {}, max_recent_posts: int, False, False, False, False, cw_lists, lists_enabled, timezone, mitm, - bold_reading) + post_str + bold_reading, + dogwhistles) + post_str # show the following posts 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, cw_lists, lists_enabled, timezone, False, - bold_reading) + bold_reading, dogwhistles) css_filename = base_dir + '/epicyon-profile.css' if os.path.isfile(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, signing_priv_key_pem: str, cw_lists: {}, 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 """ replies_str = '' @@ -2582,7 +2614,7 @@ def html_post_replies(recent_posts_cache: {}, max_recent_posts: int, False, False, cw_lists, lists_enabled, timezone, False, - bold_reading) + bold_reading, dogwhistles) css_filename = base_dir + '/epicyon-profile.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, cw_lists: {}, lists_enabled: str, box_name: str, page_number: int, - timezone: str, bold_reading: bool) -> str: + timezone: str, bold_reading: bool, + dogwhistles: {}) -> str: """Returns the emoji picker screen """ reacted_to_post_str = \ @@ -2635,7 +2668,7 @@ def html_emoji_reaction_picker(recent_posts_cache: {}, max_recent_posts: int, max_like_count, False, False, False, False, False, False, cw_lists, lists_enabled, timezone, False, - bold_reading) + bold_reading, dogwhistles) reactions_filename = base_dir + '/emoji/reactions.json' if not os.path.isfile(reactions_filename): diff --git a/webapp_profile.py b/webapp_profile.py index e024cb659..9da1602bb 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -148,7 +148,7 @@ def html_profile_after_search(recent_posts_cache: {}, max_recent_posts: int, cw_lists: {}, lists_enabled: str, timezone: 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 """ http = False @@ -388,7 +388,7 @@ def html_profile_after_search(recent_posts_cache: {}, max_recent_posts: int, False, False, False, cw_lists, lists_enabled, timezone, False, - bold_reading) + bold_reading, dogwhistles) i += 1 if i >= 8: break @@ -636,7 +636,7 @@ def html_profile(signing_priv_key_pem: str, system_language, max_like_count, shared_items_federated_domains, None, page_number, max_items_per_page, cw_lists, - lists_enabled) + lists_enabled, {}) domain, port = get_domain_from_actor(profile_json['id']) if not domain: @@ -1031,7 +1031,7 @@ def html_profile(signing_priv_key_pem: str, max_like_count, signing_priv_key_pem, cw_lists, lists_enabled, - timezone, bold_reading) + license_str + timezone, bold_reading, {}) + license_str if not is_group: if selected == 'following': profile_str += \ @@ -1104,7 +1104,8 @@ def _html_profile_posts(recent_posts_cache: {}, max_recent_posts: int, max_like_count: int, signing_priv_key_pem: 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 These should only be public posts """ @@ -1154,7 +1155,7 @@ def _html_profile_posts(recent_posts_cache: {}, max_recent_posts: int, True, False, False, cw_lists, lists_enabled, timezone, False, - bold_reading) + bold_reading, dogwhistles) if post_str: profile_str += post_str + separator_str ctr += 1 diff --git a/webapp_search.py b/webapp_search.py index 72cc0058a..c30b78e97 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -621,7 +621,8 @@ def html_history_search(translate: {}, base_dir: str, signing_priv_key_pem: 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 your post history """ if historysearch.startswith("'"): @@ -709,7 +710,8 @@ def html_history_search(translate: {}, base_dir: str, show_individual_post_icons, False, False, False, False, cw_lists, lists_enabled, - timezone, False, bold_reading) + timezone, False, bold_reading, + dogwhistles) if post_str: history_search_form += separator_str + post_str index += 1 @@ -734,7 +736,8 @@ def html_hashtag_search(nickname: str, domain: str, port: int, max_like_count: int, signing_priv_key_pem: 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 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, store_to_sache, False, cw_lists, lists_enabled, timezone, False, - bold_reading) + bold_reading, dogwhistles) if post_str: hashtag_search_form += separator_str + post_str index += 1 diff --git a/webapp_timeline.py b/webapp_timeline.py index c6826b9c7..651a290f2 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -493,7 +493,8 @@ def html_timeline(css_cache: {}, default_timeline: str, shared_items_federated_domains: [], signing_priv_key_pem: str, cw_lists: {}, lists_enabled: str, - timezone: str, bold_reading: bool) -> str: + timezone: str, bold_reading: bool, + dogwhistles: {}) -> str: """Show the timeline as html """ enable_timing_log = False @@ -1000,7 +1001,7 @@ def html_timeline(css_cache: {}, default_timeline: str, False, True, use_cache_only, cw_lists, lists_enabled, timezone, mitm, - bold_reading) + bold_reading, dogwhistles) _log_timeline_timing(enable_timing_log, timeline_start_time, box_name, '12') @@ -1238,7 +1239,8 @@ def html_shares(css_cache: {}, default_timeline: str, shared_items_federated_domains: [], signing_priv_key_pem: 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 """ manually_approve_followers = \ @@ -1269,7 +1271,7 @@ def html_shares(css_cache: {}, default_timeline: str, shared_items_federated_domains, signing_priv_key_pem, cw_lists, lists_enabled, timezone, - bold_reading) + bold_reading, dogwhistles) def html_wanted(css_cache: {}, default_timeline: str, @@ -1298,7 +1300,8 @@ def html_wanted(css_cache: {}, default_timeline: str, shared_items_federated_domains: [], signing_priv_key_pem: 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 """ manually_approve_followers = \ @@ -1329,7 +1332,7 @@ def html_wanted(css_cache: {}, default_timeline: str, shared_items_federated_domains, signing_priv_key_pem, cw_lists, lists_enabled, timezone, - bold_reading) + bold_reading, dogwhistles) def html_inbox(css_cache: {}, default_timeline: str, @@ -1359,7 +1362,8 @@ def html_inbox(css_cache: {}, default_timeline: str, shared_items_federated_domains: [], signing_priv_key_pem: str, cw_lists: {}, lists_enabled: str, - timezone: str, bold_reading: bool) -> str: + timezone: str, bold_reading: bool, + dogwhistles: {}) -> str: """Show the inbox as html """ manually_approve_followers = \ @@ -1390,7 +1394,7 @@ def html_inbox(css_cache: {}, default_timeline: str, shared_items_federated_domains, signing_priv_key_pem, cw_lists, lists_enabled, timezone, - bold_reading) + bold_reading, dogwhistles) def html_bookmarks(css_cache: {}, default_timeline: str, @@ -1420,7 +1424,8 @@ def html_bookmarks(css_cache: {}, default_timeline: str, shared_items_federated_domains: [], signing_priv_key_pem: str, cw_lists: {}, lists_enabled: str, - timezone: str, bold_reading: bool) -> str: + timezone: str, bold_reading: bool, + dogwhistles: {}) -> str: """Show the bookmarks as html """ manually_approve_followers = \ @@ -1450,7 +1455,7 @@ def html_bookmarks(css_cache: {}, default_timeline: str, access_keys, system_language, max_like_count, shared_items_federated_domains, signing_priv_key_pem, cw_lists, lists_enabled, timezone, - bold_reading) + bold_reading, dogwhistles) 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: [], signing_priv_key_pem: 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 """ artist = is_artist(base_dir, nickname) @@ -1506,7 +1512,7 @@ def html_inbox_dms(css_cache: {}, default_timeline: str, shared_items_federated_domains, signing_priv_key_pem, cw_lists, lists_enabled, timezone, - bold_reading) + bold_reading, dogwhistles) 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: [], signing_priv_key_pem: 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 """ 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, access_keys, system_language, max_like_count, 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, @@ -1590,7 +1598,8 @@ def html_inbox_media(css_cache: {}, default_timeline: str, shared_items_federated_domains: [], signing_priv_key_pem: 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 """ 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, access_keys, system_language, max_like_count, 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, @@ -1644,7 +1654,8 @@ def html_inbox_blogs(css_cache: {}, default_timeline: str, shared_items_federated_domains: [], signing_priv_key_pem: 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 """ 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, access_keys, system_language, max_like_count, 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, @@ -1699,7 +1711,8 @@ def html_inbox_features(css_cache: {}, default_timeline: str, shared_items_federated_domains: [], signing_priv_key_pem: 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 """ 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, access_keys, system_language, max_like_count, 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, @@ -1752,7 +1766,8 @@ def html_inbox_news(css_cache: {}, default_timeline: str, shared_items_federated_domains: [], signing_priv_key_pem: 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 """ 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, access_keys, system_language, max_like_count, 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, @@ -1805,7 +1821,8 @@ def html_outbox(css_cache: {}, default_timeline: str, shared_items_federated_domains: [], signing_priv_key_pem: str, cw_lists: {}, lists_enabled: str, - timezone: str, bold_reading: bool) -> str: + timezone: str, bold_reading: bool, + dogwhistles: {}) -> str: """Show the Outbox as html """ manually_approve_followers = \ @@ -1831,4 +1848,5 @@ def html_outbox(css_cache: {}, default_timeline: str, allow_local_network_access, text_mode_banner, access_keys, system_language, max_like_count, shared_items_federated_domains, signing_priv_key_pem, - cw_lists, lists_enabled, timezone, bold_reading) + cw_lists, lists_enabled, timezone, bold_reading, + dogwhistles)