merge-requests/30/head
Bob Mottram 2023-01-13 22:36:39 +00:00
commit 4dc66c1b78
66 changed files with 769 additions and 260 deletions

236
daemon.py
View File

@ -178,6 +178,7 @@ from webapp_podcast import html_podcast_episode
from webapp_theme_designer import html_theme_designer
from webapp_minimalbutton import set_minimal
from webapp_minimalbutton import is_minimal
from webapp_utils import load_buy_sites
from webapp_utils import get_default_path
from webapp_utils import get_avatar_image_url
from webapp_utils import html_hashtag_blocked
@ -483,7 +484,8 @@ class PubServer(BaseHTTPRequestHandler):
theme_name: str, max_like_count: int,
cw_lists: {}, dogwhistles: {},
min_images_for_accounts: [],
max_hashtags: int) -> None:
max_hashtags: int,
buy_sites: {}) -> None:
"""When an edited post is created this assigns
a published and updated date to it, and uses
the previous id
@ -531,7 +533,7 @@ class PubServer(BaseHTTPRequestHandler):
theme_name, max_like_count,
cw_lists, dogwhistles,
min_images_for_accounts,
max_hashtags)
max_hashtags, buy_sites)
# update the index
id_str = edited_postid.split('/')[-1]
@ -665,6 +667,7 @@ class PubServer(BaseHTTPRequestHandler):
event_end_time = None
location = None
conversation_id = None
buy_url = ''
city = get_spoofed_city(self.server.city,
self.server.base_dir,
nickname, self.server.domain)
@ -698,7 +701,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.low_bandwidth,
self.server.content_license_url,
languages_understood, False,
self.server.translate)
self.server.translate, buy_url)
if message_json:
# NOTE: content and contentMap are not required, but we will keep
# them in there so that the post does not get filtered out by
@ -1876,7 +1879,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled,
self.server.content_license_url,
self.server.dogwhistles,
self.server.min_images_for_accounts)
self.server.min_images_for_accounts,
self.server.buy_sites)
def _get_outbox_thread_index(self, nickname: str,
max_outbox_threads_per_account: int) -> int:
@ -3240,7 +3244,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.i2p_domain,
bold_reading,
self.server.dogwhistles,
min_images_for_accounts)
min_images_for_accounts,
self.server.buy_sites)
if profile_str:
msg = profile_str.encode('utf-8')
msglen = len(msg)
@ -3667,6 +3672,7 @@ class PubServer(BaseHTTPRequestHandler):
if self.server.default_post_language.get(nickname):
default_post_language = \
self.server.default_post_language[nickname]
default_buy_site = ''
msg = \
html_new_post({}, False, self.server.translate,
base_dir,
@ -3707,7 +3713,9 @@ class PubServer(BaseHTTPRequestHandler):
bold_reading,
self.server.dogwhistles,
self.server.min_images_for_accounts,
None, None, default_post_language)
None, None, default_post_language,
self.server.buy_sites,
default_buy_site)
if msg:
msg = msg.encode('utf-8')
msglen = len(msg)
@ -3827,6 +3835,7 @@ class PubServer(BaseHTTPRequestHandler):
if self.server.default_post_language.get(nickname):
default_post_language = \
self.server.default_post_language[nickname]
default_buy_site = ''
msg = \
html_new_post({}, False, self.server.translate,
base_dir,
@ -3866,7 +3875,9 @@ class PubServer(BaseHTTPRequestHandler):
bold_reading,
self.server.dogwhistles,
self.server.min_images_for_accounts,
None, None, default_post_language)
None, None, default_post_language,
self.server.buy_sites,
default_buy_site)
if msg:
msg = msg.encode('utf-8')
msglen = len(msg)
@ -4444,7 +4455,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.map_format,
self.server.access_keys,
'search',
self.server.min_images_for_accounts)
self.server.min_images_for_accounts,
self.server.buy_sites)
if hashtag_str:
msg = hashtag_str.encode('utf-8')
msglen = len(msg)
@ -4555,7 +4567,8 @@ class PubServer(BaseHTTPRequestHandler):
timezone, bold_reading,
self.server.dogwhistles,
self.server.access_keys,
self.server.min_images_for_accounts)
self.server.min_images_for_accounts,
self.server.buy_sites)
if history_str:
msg = history_str.encode('utf-8')
msglen = len(msg)
@ -4638,7 +4651,8 @@ class PubServer(BaseHTTPRequestHandler):
timezone, bold_reading,
self.server.dogwhistles,
self.server.access_keys,
self.server.min_images_for_accounts)
self.server.min_images_for_accounts,
self.server.buy_sites)
if bookmarks_str:
msg = bookmarks_str.encode('utf-8')
msglen = len(msg)
@ -4817,7 +4831,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.i2p_domain,
bold_reading,
self.server.dogwhistles,
min_images_for_accounts)
min_images_for_accounts,
self.server.buy_sites)
if profile_str:
msg = profile_str.encode('utf-8')
msglen = len(msg)
@ -7867,6 +7882,44 @@ class PubServer(BaseHTTPRequestHandler):
set_config_param(base_dir, 'crawlersAllowed',
crawlers_allowed_str)
# save allowed buy domains
buy_sites = {}
if fields.get('buySitesStr'):
buy_sites_str = \
fields['buySitesStr']
buy_sites_list = \
buy_sites_str.split('\n')
for site_url in buy_sites_list:
if ' ' in site_url:
site_url = site_url.split(' ')[-1]
buy_icon_text = \
site_url.replace(site_url, '').strip()
if not buy_icon_text:
buy_icon_text = site_url
else:
buy_icon_text = site_url
if buy_sites.get(buy_icon_text):
continue
if '<' in site_url:
continue
if not site_url.strip():
continue
buy_sites[buy_icon_text] = site_url.strip()
if str(self.server.buy_sites) != \
str(buy_sites):
self.server.buy_sites = buy_sites
buy_sites_filename = \
base_dir + '/accounts/buy_sites.json'
if buy_sites:
save_json(buy_sites, buy_sites_filename)
else:
if os.path.isfile(buy_sites_filename):
try:
os.remove(buy_sites_filename)
except OSError:
print('EX: unable to delete ' +
buy_sites_filename)
# save peertube instances list
peertube_instances_file = \
base_dir + '/accounts/peertube.txt'
@ -9201,7 +9254,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.map_format,
self.server.access_keys,
'search',
self.server.min_images_for_accounts)
self.server.min_images_for_accounts,
self.server.buy_sites)
if hashtag_str:
msg = hashtag_str.encode('utf-8')
msglen = len(msg)
@ -9522,7 +9576,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled,
timezone, mitm, bold_reading,
self.server.dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
self.server.buy_sites)
actor_absolute = self._get_instance_url(calling_domain) + actor
@ -10088,7 +10143,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled,
timezone, mitm, bold_reading,
self.server.dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
self.server.buy_sites)
else:
print('WARN: Liked post not found: ' + liked_post_filename)
# clear the icon from the cache so that it gets updated
@ -10292,7 +10348,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled,
timezone, mitm, bold_reading,
self.server.dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
self.server.buy_sites)
else:
print('WARN: Unliked post not found: ' + liked_post_filename)
# clear the icon from the cache so that it gets updated
@ -10525,7 +10582,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled,
timezone, mitm, bold_reading,
self.server.dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
self.server.buy_sites)
else:
print('WARN: Emoji reaction post not found: ' +
reaction_post_filename)
@ -10748,7 +10806,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled,
timezone, mitm, bold_reading,
self.server.dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
self.server.buy_sites)
else:
print('WARN: Unreaction post not found: ' +
reaction_post_filename)
@ -10857,7 +10916,8 @@ class PubServer(BaseHTTPRequestHandler):
timeline_str, page_number,
timezone, bold_reading,
self.server.dogwhistles,
self.server.min_images_for_accounts)
self.server.min_images_for_accounts,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -11016,7 +11076,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled,
timezone, mitm, bold_reading,
self.server.dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
self.server.buy_sites)
else:
print('WARN: Bookmarked post not found: ' + bookmark_filename)
# self._post_to_outbox(bookmark_json,
@ -11184,7 +11245,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled,
timezone, mitm, bold_reading,
self.server.dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
self.server.buy_sites)
else:
print('WARN: Unbookmarked post not found: ' +
bookmark_filename)
@ -11298,7 +11360,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.min_images_for_accounts)
self.server.min_images_for_accounts,
self.server.buy_sites)
if delete_str:
delete_str_len = len(delete_str)
self._set_headers('text/html', delete_str_len,
@ -11439,7 +11502,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled,
timezone, mitm, bold_reading,
self.server.dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
self.server.buy_sites)
else:
print('WARN: Muted post not found: ' + mute_filename)
@ -11583,7 +11647,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled,
timezone, mitm, bold_reading,
self.server.dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
self.server.buy_sites)
else:
print('WARN: Unmuted post not found: ' + mute_filename)
if calling_domain.endswith('.onion') and onion_domain:
@ -11714,7 +11779,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled,
timezone, bold_reading,
self.server.dogwhistles,
self.server.min_images_for_accounts)
self.server.min_images_for_accounts,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -11828,7 +11894,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled,
timezone, bold_reading,
self.server.dogwhistles,
self.server.min_images_for_accounts)
self.server.min_images_for_accounts,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -11948,7 +12015,8 @@ class PubServer(BaseHTTPRequestHandler):
None, None, self.server.cw_lists,
self.server.lists_enabled,
self.server.content_license_url,
timezone, bold_reading)
timezone, bold_reading,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -12074,7 +12142,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists,
self.server.lists_enabled,
content_license_url,
timezone, bold_reading)
timezone, bold_reading,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -12173,7 +12242,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.dogwhistles,
self.server.access_keys,
self.server.min_images_for_accounts,
self.server.debug)
self.server.debug,
self.server.buy_sites)
if conv_str:
msg = conv_str.encode('utf-8')
msglen = len(msg)
@ -12336,7 +12406,8 @@ class PubServer(BaseHTTPRequestHandler):
'inbox', self.server.default_timeline,
bold_reading,
self.server.dogwhistles,
self.server.min_images_for_accounts)
self.server.min_images_for_accounts,
self.server.buy_sites)
if not msg:
self._404()
return True
@ -12402,6 +12473,7 @@ class PubServer(BaseHTTPRequestHandler):
'inbox', self.server.default_timeline,
bold_reading, self.server.dogwhistles,
self.server.min_images_for_accounts,
self.server.buy_sites,
'shares')
if not msg:
self._404()
@ -12491,7 +12563,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.lists_enabled,
timezone, mitm, bold_reading,
self.server.dogwhistles,
self.server.min_images_for_accounts)
self.server.min_images_for_accounts,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -12817,7 +12890,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.dogwhistles,
ua_str,
self.server.min_images_for_accounts,
reverse_sequence, last_post_id)
reverse_sequence, last_post_id,
self.server.buy_sites)
if getreq_start_time:
fitness_performance(getreq_start_time,
self.server.fitness,
@ -13000,7 +13074,8 @@ class PubServer(BaseHTTPRequestHandler):
timezone, bold_reading,
self.server.dogwhistles, ua_str,
self.server.min_images_for_accounts,
reverse_sequence, last_post_id)
reverse_sequence, last_post_id,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -13174,7 +13249,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.dogwhistles,
ua_str,
self.server.min_images_for_accounts,
reverse_sequence, last_post_id)
reverse_sequence, last_post_id,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -13344,7 +13420,8 @@ class PubServer(BaseHTTPRequestHandler):
timezone, bold_reading,
self.server.dogwhistles, ua_str,
self.server.min_images_for_accounts,
reverse_sequence, last_post_id)
reverse_sequence, last_post_id,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -13514,7 +13591,8 @@ class PubServer(BaseHTTPRequestHandler):
timezone, bold_reading,
self.server.dogwhistles, ua_str,
self.server.min_images_for_accounts,
reverse_sequence, last_post_id)
reverse_sequence, last_post_id,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -13687,7 +13765,8 @@ class PubServer(BaseHTTPRequestHandler):
timezone, bold_reading,
self.server.dogwhistles, ua_str,
self.server.min_images_for_accounts,
reverse_sequence)
reverse_sequence,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -13864,7 +13943,8 @@ class PubServer(BaseHTTPRequestHandler):
timezone, bold_reading,
self.server.dogwhistles, ua_str,
min_images_for_accounts,
reverse_sequence)
reverse_sequence,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -13992,7 +14072,8 @@ class PubServer(BaseHTTPRequestHandler):
bold_reading, self.server.dogwhistles,
ua_str,
self.server.min_images_for_accounts,
reverse_sequence)
reverse_sequence,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -14093,7 +14174,8 @@ class PubServer(BaseHTTPRequestHandler):
timezone, bold_reading,
self.server.dogwhistles, ua_str,
self.server.min_images_for_accounts,
reverse_sequence)
reverse_sequence,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -14235,7 +14317,8 @@ class PubServer(BaseHTTPRequestHandler):
timezone, bold_reading,
self.server.dogwhistles, ua_str,
self.server.min_images_for_accounts,
reverse_sequence)
reverse_sequence,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -14395,7 +14478,8 @@ class PubServer(BaseHTTPRequestHandler):
timezone, bold_reading,
self.server.dogwhistles, ua_str,
self.server.min_images_for_accounts,
reverse_sequence)
reverse_sequence,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -14552,7 +14636,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.dogwhistles,
ua_str,
min_images_for_accounts,
reverse_sequence)
reverse_sequence,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -14696,7 +14781,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists,
self.server.lists_enabled,
self.server.content_license_url,
timezone, bold_reading)
timezone, bold_reading,
self.server.buy_sites)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -14838,7 +14924,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists,
self.server.lists_enabled,
content_license_url,
timezone, bold_reading).encode('utf-8')
timezone, bold_reading,
self.server.buy_sites).encode('utf-8')
msglen = len(msg)
self._set_headers('text/html',
msglen, cookie, calling_domain, False)
@ -14974,7 +15061,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists,
self.server.lists_enabled,
content_license_url,
timezone, bold_reading).encode('utf-8')
timezone, bold_reading,
self.server.buy_sites).encode('utf-8')
msglen = len(msg)
self._set_headers('text/html',
msglen, cookie, calling_domain, False)
@ -15114,7 +15202,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists,
self.server.lists_enabled,
content_license_url,
timezone, bold_reading).encode('utf-8')
timezone, bold_reading,
self.server.buy_sites).encode('utf-8')
msglen = len(msg)
self._set_headers('text/html',
msglen, cookie, calling_domain, False)
@ -15255,7 +15344,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists,
self.server.lists_enabled,
content_license_url,
timezone, bold_reading).encode('utf-8')
timezone, bold_reading,
self.server.buy_sites).encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
cookie, calling_domain, False)
@ -15422,7 +15512,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists,
self.server.lists_enabled,
self.server.content_license_url,
timezone, bold_reading).encode('utf-8')
timezone, bold_reading,
self.server.buy_sites).encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
cookie, calling_domain, False)
@ -16220,7 +16311,7 @@ class PubServer(BaseHTTPRequestHandler):
nickname,
self.server.domain_full,
self.server.person_cache)
default_buy_site = ''
msg = \
html_new_post(edit_post_params, media_instance,
translate,
@ -16264,7 +16355,9 @@ class PubServer(BaseHTTPRequestHandler):
self.server.dogwhistles,
self.server.min_images_for_accounts,
new_post_month, new_post_year,
default_post_language)
default_post_language,
self.server.buy_sites,
default_buy_site)
if not msg:
print('Error replying to ' + in_reply_to_url)
self._404()
@ -16343,7 +16436,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.system_language,
self.server.min_images_for_accounts,
self.server.max_recent_posts,
self.server.reverse_sequence)
self.server.reverse_sequence,
self.server.buy_sites)
if msg:
msg = msg.encode('utf-8')
msglen = len(msg)
@ -18634,7 +18728,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.bold_reading,
self.server.dogwhistles,
self.server.min_images_for_accounts,
self.server.debug)
self.server.debug,
self.server.buy_sites)
if msg:
msg = msg.encode('utf-8')
msglen = len(msg)
@ -20625,6 +20720,10 @@ class PubServer(BaseHTTPRequestHandler):
else:
comments_enabled = True
buy_url = ''
if fields.get('buyUrl'):
buy_url = fields['buyUrl']
if post_type == 'newpost':
if not fields.get('pinToProfile'):
pin_to_profile = False
@ -20675,7 +20774,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.low_bandwidth,
self.server.content_license_url,
languages_understood,
self.server.translate)
self.server.translate, buy_url)
if message_json:
if edited_postid:
recent_posts_cache = self.server.recent_posts_cache
@ -20722,7 +20821,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists,
self.server.dogwhistles,
min_images_for_accounts,
self.server.max_hashtags)
self.server.max_hashtags,
self.server.buy_sites)
print('DEBUG: sending edited public post ' +
str(message_json))
if fields['schedulePost']:
@ -20814,7 +20914,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.low_bandwidth,
self.server.content_license_url,
languages_understood,
self.server.translate)
self.server.translate, buy_url)
if message_json:
if fields['schedulePost']:
return 1
@ -20979,7 +21079,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.low_bandwidth,
self.server.content_license_url,
languages_understood,
self.server.translate)
self.server.translate, buy_url)
if message_json:
if edited_postid:
recent_posts_cache = self.server.recent_posts_cache
@ -21026,7 +21126,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists,
self.server.dogwhistles,
min_images_for_accounts,
self.server.max_hashtags)
self.server.max_hashtags,
self.server.buy_sites)
print('DEBUG: sending edited unlisted post ' +
str(message_json))
@ -21089,7 +21190,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.low_bandwidth,
self.server.content_license_url,
languages_understood,
self.server.translate)
self.server.translate,
buy_url)
if message_json:
if edited_postid:
recent_posts_cache = self.server.recent_posts_cache
@ -21136,7 +21238,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists,
self.server.dogwhistles,
min_images_for_accounts,
self.server.max_hashtags)
self.server.max_hashtags,
self.server.buy_sites)
print('DEBUG: sending edited followers post ' +
str(message_json))
@ -21211,7 +21314,8 @@ class PubServer(BaseHTTPRequestHandler):
content_license_url,
languages_understood,
reply_is_chat,
self.server.translate)
self.server.translate,
buy_url)
if message_json:
print('DEBUG: posting DM edited_postid ' +
str(edited_postid))
@ -21260,7 +21364,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists,
self.server.dogwhistles,
min_images_for_accounts,
self.server.max_hashtags)
self.server.max_hashtags,
self.server.buy_sites)
print('DEBUG: sending edited dm post ' +
str(message_json))
@ -21326,7 +21431,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.low_bandwidth,
self.server.content_license_url,
languages_understood,
False, self.server.translate)
False, self.server.translate,
buy_url)
if message_json:
if fields['schedulePost']:
return 1
@ -21378,7 +21484,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cw_lists,
self.server.dogwhistles,
min_images_for_accounts,
self.server.max_hashtags)
self.server.max_hashtags,
self.server.buy_sites)
print('DEBUG: sending edited reminder post ' +
str(message_json))
if self._post_to_outbox(message_json,
@ -22928,6 +23035,9 @@ def run_daemon(max_hashtags: int,
# scan the theme directory for any svg files containing scripts
assert not scan_themes_for_scripts(base_dir)
# permitted sites from which the buy button may be displayed
httpd.buy_sites = load_buy_sites(base_dir)
# which accounts should minimize all attached images by default
httpd.min_images_for_accounts = load_min_images_for_accounts(base_dir)

View File

@ -528,6 +528,7 @@ def _desktop_reply_to_post(session, post_id: str,
event_time = None
event_end_time = None
location = None
buy_url = ''
_say_command(say_str, say_str, screenreader, system_language, espeak)
if send_post_via_server(signing_priv_key_pem, __version__,
base_dir, session, nickname, password,
@ -540,7 +541,7 @@ def _desktop_reply_to_post(session, post_id: str,
system_language, languages_understood,
low_bandwidth, content_license_url,
event_date, event_time, event_end_time, location,
translate, debug, post_id, post_id,
translate, buy_url, debug, post_id, post_id,
conversation_id, subject) == 0:
say_str = 'Reply sent'
else:
@ -602,6 +603,7 @@ def _desktop_new_post(session,
event_time = None
event_end_time = None
location = None
buy_url = ''
_say_command(say_str, say_str, screenreader, system_language, espeak)
if send_post_via_server(signing_priv_key_pem, __version__,
base_dir, session, nickname, password,
@ -614,7 +616,7 @@ def _desktop_new_post(session,
system_language, languages_understood,
low_bandwidth, content_license_url,
event_date, event_time, event_end_time, location,
translate, debug, None, None,
translate, buy_url, debug, None, None,
conversation_id, subject) == 0:
say_str = 'Post sent'
else:
@ -1345,6 +1347,7 @@ def _desktop_new_dm_base(session, to_handle: str,
event_time = None
event_end_time = None
location = None
buy_url = ''
say_str = 'Sending'
_say_command(say_str, say_str, screenreader, system_language, espeak)
@ -1359,7 +1362,7 @@ def _desktop_new_dm_base(session, to_handle: str,
system_language, languages_understood,
low_bandwidth, content_license_url,
event_date, event_time, event_end_time, location,
translate, debug, None, None,
translate, buy_url, debug, None, None,
conversation_id, subject) == 0:
say_str = 'Direct message sent'
else:

View File

@ -149,6 +149,9 @@ def _command_options() -> None:
parser.add_argument('--eventLocation', type=str,
default=None,
help='Location for an event when sending a c2s post')
parser.add_argument('--buyUrl', type=str,
default=None,
help='Link for buying something')
parser.add_argument('--content_license_url', type=str,
default='https://creativecommons.org/' +
'licenses/by-nc/4.0',
@ -1702,7 +1705,7 @@ def _command_options() -> None:
argb.low_bandwidth,
argb.content_license_url,
argb.eventDate, argb.eventTime, argb.eventEndTime,
argb.eventLocation, translate,
argb.eventLocation, translate, argb.buyUrl,
argb.debug,
reply_to, reply_to, argb.conversationId, subject)
for _ in range(10):
@ -3375,6 +3378,7 @@ def _command_options() -> None:
low_bandwidth = False
languages_understood = [argb.language]
translate = {}
buy_url = ''
create_public_post(base_dir, nickname, domain, port, http_prefix,
"like this is totally just a #test man",
@ -3389,7 +3393,7 @@ def _command_options() -> None:
test_event_end_time, test_location,
test_is_article, argb.language, conversation_id,
low_bandwidth, argb.content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
create_public_post(base_dir, nickname, domain, port, http_prefix,
"Zoiks!!!",
test_save_to_file,
@ -3403,7 +3407,7 @@ def _command_options() -> None:
test_event_end_time, test_location,
test_is_article, argb.language, conversation_id,
low_bandwidth, argb.content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
create_public_post(base_dir, nickname, domain, port, http_prefix,
"Hey scoob we need like a hundred more #milkshakes",
test_save_to_file,
@ -3417,7 +3421,7 @@ def _command_options() -> None:
test_event_end_time, test_location,
test_is_article, argb.language, conversation_id,
low_bandwidth, argb.content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
create_public_post(base_dir, nickname, domain, port, http_prefix,
"Getting kinda spooky around here",
test_save_to_file,
@ -3431,7 +3435,7 @@ def _command_options() -> None:
test_event_end_time, test_location,
test_is_article, argb.language, conversation_id,
low_bandwidth, argb.content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
create_public_post(base_dir, nickname, domain, port, http_prefix,
"And they would have gotten away with it too" +
"if it wasn't for those pesky hackers",
@ -3446,7 +3450,7 @@ def _command_options() -> None:
test_event_end_time, test_location,
test_is_article, argb.language, conversation_id,
low_bandwidth, argb.content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
create_public_post(base_dir, nickname, domain, port, http_prefix,
"man these centralized sites are like the worst!",
test_save_to_file,
@ -3460,7 +3464,7 @@ def _command_options() -> None:
test_event_end_time, test_location,
test_is_article, argb.language, conversation_id,
low_bandwidth, argb.content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
create_public_post(base_dir, nickname, domain, port, http_prefix,
"another mystery solved #test",
test_save_to_file,
@ -3474,7 +3478,7 @@ def _command_options() -> None:
test_event_end_time, test_location,
test_is_article, argb.language, conversation_id,
low_bandwidth, argb.content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
create_public_post(base_dir, nickname, domain, port, http_prefix,
"let's go bowling",
test_save_to_file,
@ -3488,7 +3492,7 @@ def _command_options() -> None:
test_event_end_time, test_location,
test_is_article, argb.language, conversation_id,
low_bandwidth, argb.content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
domain_full = domain + ':' + str(port)
clear_follows(base_dir, nickname, domain)
follow_person(base_dir, nickname, domain, 'maxboardroom', domain_full,

107
inbox.py
View File

@ -452,7 +452,8 @@ def _inbox_store_post_to_html_cache(recent_posts_cache: {},
mitm: bool,
bold_reading: bool,
dogwhistles: {},
min_images_for_accounts: []) -> None:
min_images_for_accounts: [],
buy_sites: {}) -> None:
"""Converts the json post into html and stores it in a cache
This enables the post to be quickly displayed later
"""
@ -482,7 +483,7 @@ def _inbox_store_post_to_html_cache(recent_posts_cache: {},
not_dm, True, True, False, True, False,
cw_lists, lists_enabled, timezone, mitm,
bold_reading, dogwhistles, minimize_all_images,
None)
None, buy_sites)
def valid_inbox(base_dir: str, nickname: str, domain: str) -> bool:
@ -1257,7 +1258,8 @@ def receive_edit_to_post(recent_posts_cache: {}, message_json: {},
theme_name: str, max_like_count: int,
cw_lists: {}, dogwhistles: {},
min_images_for_accounts: [],
max_hashtags: int) -> bool:
max_hashtags: int,
buy_sites: {}) -> bool:
"""A post was edited
"""
if not has_object_dict(message_json):
@ -1392,7 +1394,8 @@ def receive_edit_to_post(recent_posts_cache: {}, message_json: {},
False, True, False, cw_lists,
lists_enabled, timezone, mitm,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
return True
@ -1414,7 +1417,8 @@ def _receive_update_activity(recent_posts_cache: {}, session, base_dir: str,
theme_name: str, max_like_count: int,
cw_lists: {}, dogwhistles: {},
min_images_for_accounts: [],
max_hashtags: int) -> bool:
max_hashtags: int,
buy_sites: {}) -> bool:
"""Receives an Update activity within the POST section of HTTPServer
"""
@ -1456,7 +1460,7 @@ def _receive_update_activity(recent_posts_cache: {}, session, base_dir: str,
theme_name, max_like_count,
cw_lists, dogwhistles,
min_images_for_accounts,
max_hashtags):
max_hashtags, buy_sites):
print('EDITPOST: received ' + message_json['object']['id'])
return True
else:
@ -1507,7 +1511,8 @@ def _receive_like(recent_posts_cache: {},
max_like_count: int, cw_lists: {},
lists_enabled: str,
bold_reading: bool, dogwhistles: {},
min_images_for_accounts: []) -> bool:
min_images_for_accounts: [],
buy_sites: {}) -> bool:
"""Receives a Like activity within the POST section of HTTPServer
"""
if message_json['type'] != 'Like':
@ -1621,7 +1626,7 @@ def _receive_like(recent_posts_cache: {},
False, True, False, cw_lists,
lists_enabled, timezone, mitm,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None, buy_sites)
return True
@ -1642,7 +1647,8 @@ def _receive_undo_like(recent_posts_cache: {},
max_like_count: int, cw_lists: {},
lists_enabled: str,
bold_reading: bool, dogwhistles: {},
min_images_for_accounts: []) -> bool:
min_images_for_accounts: [],
buy_sites: {}) -> bool:
"""Receives an undo like activity within the POST section of HTTPServer
"""
if message_json['type'] != 'Undo':
@ -1746,7 +1752,8 @@ def _receive_undo_like(recent_posts_cache: {},
False, True, False, cw_lists,
lists_enabled, timezone, mitm,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
return True
@ -1768,7 +1775,8 @@ def _receive_reaction(recent_posts_cache: {},
max_like_count: int, cw_lists: {},
lists_enabled: str, bold_reading: bool,
dogwhistles: {},
min_images_for_accounts: []) -> bool:
min_images_for_accounts: [],
buy_sites: {}) -> bool:
"""Receives an emoji reaction within the POST section of HTTPServer
"""
if message_json['type'] != 'EmojiReact':
@ -1903,7 +1911,7 @@ def _receive_reaction(recent_posts_cache: {},
False, True, False, cw_lists,
lists_enabled, timezone, mitm,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None, buy_sites)
return True
@ -1925,7 +1933,8 @@ def _receive_zot_reaction(recent_posts_cache: {},
max_like_count: int, cw_lists: {},
lists_enabled: str, bold_reading: bool,
dogwhistles: {},
min_images_for_accounts: []) -> bool:
min_images_for_accounts: [],
buy_sites: {}) -> bool:
"""Receives an zot-style emoji reaction within the POST section of
HTTPServer A zot style emoji reaction is an ordinary reply Note whose
content is exactly one emoji
@ -2084,7 +2093,8 @@ def _receive_zot_reaction(recent_posts_cache: {},
False, True, False, cw_lists,
lists_enabled, timezone, mitm,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
return True
@ -2107,7 +2117,8 @@ def _receive_undo_reaction(recent_posts_cache: {},
max_like_count: int, cw_lists: {},
lists_enabled: str,
bold_reading: bool, dogwhistles: {},
min_images_for_accounts: []) -> bool:
min_images_for_accounts: [],
buy_sites: {}) -> bool:
"""Receives an undo emoji reaction within the POST section of HTTPServer
"""
if message_json['type'] != 'Undo':
@ -2229,7 +2240,8 @@ def _receive_undo_reaction(recent_posts_cache: {},
False, True, False, cw_lists,
lists_enabled, timezone, mitm,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
return True
@ -2249,7 +2261,8 @@ def _receive_bookmark(recent_posts_cache: {},
max_like_count: int, cw_lists: {},
lists_enabled: {}, bold_reading: bool,
dogwhistles: {},
min_images_for_accounts: []) -> bool:
min_images_for_accounts: [],
buy_sites: {}) -> bool:
"""Receives a bookmark activity within the POST section of HTTPServer
"""
if not message_json.get('type'):
@ -2350,7 +2363,8 @@ def _receive_bookmark(recent_posts_cache: {},
False, True, False, cw_lists,
lists_enabled, timezone, mitm,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
return True
@ -2372,7 +2386,8 @@ def _receive_undo_bookmark(recent_posts_cache: {},
max_like_count: int, cw_lists: {},
lists_enabled: str, bold_reading: bool,
dogwhistles: {},
min_images_for_accounts: []) -> bool:
min_images_for_accounts: [],
buy_sites: {}) -> bool:
"""Receives an undo bookmark activity within the POST section of HTTPServer
"""
if not message_json.get('type'):
@ -2473,7 +2488,8 @@ def _receive_undo_bookmark(recent_posts_cache: {},
manually_approve_followers,
False, True, False, cw_lists, lists_enabled,
timezone, mitm, bold_reading,
dogwhistles, minimize_all_images, None)
dogwhistles, minimize_all_images, None,
buy_sites)
return True
@ -2572,7 +2588,8 @@ def _receive_announce(recent_posts_cache: {},
max_like_count: int, cw_lists: {},
lists_enabled: str, bold_reading: bool,
dogwhistles: {}, mitm: bool,
min_images_for_accounts: []) -> bool:
min_images_for_accounts: [],
buy_sites: {}) -> bool:
"""Receives an announce activity within the POST section of HTTPServer
"""
if message_json['type'] != 'Announce':
@ -2714,7 +2731,8 @@ def _receive_announce(recent_posts_cache: {},
False, True, False, cw_lists,
lists_enabled, timezone, mitm,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
if not announce_html:
print('WARN: Unable to generate html for announce ' +
str(message_json))
@ -3727,6 +3745,7 @@ def _bounce_dm(sender_post_id: str, session, http_prefix: str,
location = None
conversation_id = None
low_bandwidth = False
buy_url = ''
post_json_object = \
create_direct_message_post(base_dir, nickname, domain, port,
http_prefix, content,
@ -3741,7 +3760,7 @@ def _bounce_dm(sender_post_id: str, session, http_prefix: str,
low_bandwidth,
content_license_url,
languages_understood, bounce_is_chat,
translate)
translate, buy_url)
if not post_json_object:
print('WARN: unable to create bounce message to ' + sending_handle)
return False
@ -3903,7 +3922,8 @@ def _receive_question_vote(server, base_dir: str, nickname: str, domain: str,
max_like_count: int,
cw_lists: {}, lists_enabled: bool,
bold_reading: bool, dogwhistles: {},
min_images_for_accounts: []) -> None:
min_images_for_accounts: [],
buy_sites: {}) -> None:
"""Updates the votes on a Question/poll
"""
# if this is a reply to a question then update the votes
@ -3961,7 +3981,8 @@ def _receive_question_vote(server, base_dir: str, nickname: str, domain: str,
False, True, False, cw_lists,
lists_enabled, timezone, mitm,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
# add id to inbox index
inbox_update_index('inbox', base_dir, handle,
@ -4136,7 +4157,7 @@ def _inbox_after_initial(server, inbox_start_time,
languages_understood: [],
mitm: bool, bold_reading: bool,
dogwhistles: {},
max_hashtags: int) -> bool:
max_hashtags: int, buy_sites: {}) -> bool:
""" Anything which needs to be done after initial checks have passed
"""
# if this is a clearnet instance then replace any onion/i2p
@ -4185,7 +4206,8 @@ def _inbox_after_initial(server, inbox_start_time,
theme_name, system_language,
max_like_count, cw_lists, lists_enabled,
bold_reading, dogwhistles,
server.min_images_for_accounts):
server.min_images_for_accounts,
buy_sites):
if debug:
print('DEBUG: Like accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness,
@ -4211,7 +4233,8 @@ def _inbox_after_initial(server, inbox_start_time,
theme_name, system_language,
max_like_count, cw_lists, lists_enabled,
bold_reading, dogwhistles,
server.min_images_for_accounts):
server.min_images_for_accounts,
buy_sites):
if debug:
print('DEBUG: Undo like accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness,
@ -4238,7 +4261,8 @@ def _inbox_after_initial(server, inbox_start_time,
theme_name, system_language,
max_like_count, cw_lists, lists_enabled,
bold_reading, dogwhistles,
server.min_images_for_accounts):
server.min_images_for_accounts,
buy_sites):
if debug:
print('DEBUG: Reaction accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness,
@ -4265,7 +4289,8 @@ def _inbox_after_initial(server, inbox_start_time,
theme_name, system_language,
max_like_count, cw_lists, lists_enabled,
bold_reading, dogwhistles,
server.min_images_for_accounts):
server.min_images_for_accounts,
buy_sites):
if debug:
print('DEBUG: Zot reaction accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness,
@ -4293,7 +4318,8 @@ def _inbox_after_initial(server, inbox_start_time,
theme_name, system_language,
max_like_count, cw_lists, lists_enabled,
bold_reading, dogwhistles,
server.min_images_for_accounts):
server.min_images_for_accounts,
buy_sites):
if debug:
print('DEBUG: Undo reaction accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness,
@ -4321,7 +4347,8 @@ def _inbox_after_initial(server, inbox_start_time,
theme_name, system_language,
max_like_count, cw_lists, lists_enabled,
bold_reading, dogwhistles,
server.min_images_for_accounts):
server.min_images_for_accounts,
server.buy_sites):
if debug:
print('DEBUG: Bookmark accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness,
@ -4349,7 +4376,8 @@ def _inbox_after_initial(server, inbox_start_time,
theme_name, system_language,
max_like_count, cw_lists, lists_enabled,
bold_reading, dogwhistles,
server.min_images_for_accounts):
server.min_images_for_accounts,
server.buy_sites):
if debug:
print('DEBUG: Undo bookmark accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness,
@ -4385,7 +4413,8 @@ def _inbox_after_initial(server, inbox_start_time,
peertube_instances,
max_like_count, cw_lists, lists_enabled,
bold_reading, dogwhistles, mitm,
server.min_images_for_accounts):
server.min_images_for_accounts,
server.buy_sites):
if debug:
print('DEBUG: Announce accepted from ' + actor)
fitness_performance(inbox_start_time, server.fitness,
@ -4462,7 +4491,8 @@ def _inbox_after_initial(server, inbox_start_time,
max_like_count,
cw_lists, lists_enabled,
bold_reading, dogwhistles,
server.min_images_for_accounts)
server.min_images_for_accounts,
server.buy_sites)
fitness_performance(inbox_start_time, server.fitness,
'INBOX', '_receive_question_vote',
debug)
@ -4779,7 +4809,8 @@ def _inbox_after_initial(server, inbox_start_time,
timezone, mitm,
bold_reading,
dogwhistles,
min_img_for_accounts)
min_img_for_accounts,
buy_sites)
fitness_performance(inbox_start_time,
server.fitness,
'INBOX',
@ -5840,7 +5871,7 @@ def run_inbox_queue(server,
theme_name, max_like_count,
cw_lists, dogwhistles,
server.min_images_for_accounts,
max_hashtags):
max_hashtags, server.buy_sites):
if debug:
print('Queue: Update accepted from ' + key_id)
if os.path.isfile(queue_filename):
@ -5969,7 +6000,7 @@ def run_inbox_queue(server,
content_license_url,
languages_understood, mitm,
bold_reading, dogwhistles,
max_hashtags)
max_hashtags, server.buy_sites)
fitness_performance(inbox_start_time, server.fitness,
'INBOX', 'handle_after_initial',
debug)

Binary file not shown.

View File

@ -17,6 +17,7 @@ defaults</a></li>
<li><a href="#media-timeline">Media timeline</a></li>
<li><a href="#moderation">Moderation</a></li>
<li><a href="#themes">Themes</a></li>
<li><a href="#buying-and-selling">Buying and selling</a></li>
<li><a href="#sharing-economy">Sharing economy</a></li>
<li><a href="#search">Search</a></li>
<li><a href="#browsing-in-a-command-shell">Browsing in a command
@ -65,7 +66,11 @@ hardware. Think many small communicating nodes rather than a small
number of large servers. Also, in spite of the prevailing great
obsession with scale, not everything needs to. You can federate with a
small number of servers for a particular purpose - such as running a
club or hackspace - and thats ok.</p>
club or hackspace - and thats ok. It supports both the server-to-server
(S2S) and client-to-server (C2S) versions of the ActivityPub protocol,
with <a
href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication">basic
auth</a> for C2S authentication.</p>
<p><a
href="https://uxdesign.cc/mastodon-is-antiviral-design-42f090ab8d51?gi=9baf6195c60b">Anti-virality</a>
is a common design approach in the fediverse, and Epicyon also follows
@ -429,7 +434,7 @@ preferable, so that it matches your typical pattern of daily posting
activity without giving away your real location.</p>
<h3 id="verifying-your-website-or-blog">Verifying your website or
blog</h3>
<p>It is possible to indicate that a website of blog belongs to you by
<p>It is possible to indicate that a website or blog belongs to you by
linking it to your profile screen. Within the <em>head</em> html section
of your website or blog index page include a line similar to:</p>
<div class="sourceCode" id="cb18"><pre
@ -571,6 +576,13 @@ post.</p>
<p>Selecting the <em>location</em> header will open the last known
geolocation, so if your current location is near this makes it quicker
to find.</p>
<h2 id="scientific-references">Scientific references</h2>
<p>It is possible to have references to scientific papers linked
automatically, such that they are readable with one click/press.
Supported references are <a href="https://arxiv.org">arXiv</a> and <a
href="https://en.wikipedia.org/wiki/Digital_object_identifier">Digital
object identifier (DOI)</a>. For example:</p>
<pre class="text"><code>This is a reference to a paper: arxiv:2203.15752</code></pre>
<h1 id="the-timeline">The Timeline</h1>
<h2 id="layout">Layout</h2>
<figure>
@ -902,6 +914,18 @@ you to change colors and values for user interface components.</p>
<img src="manual-theme-designer.png" alt="Theme designer screen" />
<figcaption aria-hidden="true">Theme designer screen</figcaption>
</figure>
<h1 id="buying-and-selling">Buying and selling</h1>
<p>When creating a new post you have the option of specifying a <em>buy
link</em> This is a link to a web page where you can buy some particular
item. When someone receives the post if they have a compatible instance
then a small shopping cart icon will appear at the bottom of the post
along with the other icons. Clicking or pressing the shopping cart will
then take you to the buying site. Its a predictable and machine
parsable way indicating that something is for sale, separate from the
post content.</p>
<p>To avoid spam, it is possible for the shopping icon to only appear if
it links to one of an allowed list of seller domains. In this way you
can be confident that you are only navigating to approved sites.</p>
<h1 id="sharing-economy">Sharing economy</h1>
<p>This is intended to add <a
href="https://en.wikipedia.org/wiki/The_Freecycle_Network">Freecycle</a>

View File

@ -15,10 +15,11 @@
14. [Media timeline](#media-timeline)
15. [Moderation](#moderation)
16. [Themes](#themes)
17. [Sharing economy](#sharing-economy)
18. [Search](#search)
19. [Browsing in a command shell](#browsing-in-a-command-shell)
20. [Building fediverse communities](#building-fediverse-communities)
17. [Buying and selling](#buying-and-selling)
18. [Sharing economy](#sharing-economy)
19. [Search](#search)
20. [Browsing in a command shell](#browsing-in-a-command-shell)
21. [Building fediverse communities](#building-fediverse-communities)
# Introduction
*"Every new beginning comes from some other beginnings end."*
@ -655,6 +656,11 @@ If you have the *artist* role then from the top of the left column of the main t
![Theme designer screen](manual-theme-designer.png)
# Buying and selling
When creating a new post you have the option of specifying a *buy link* This is a link to a web page where you can buy some particular item. When someone receives the post if they have a compatible instance then a small shopping cart icon will appear at the bottom of the post along with the other icons. Clicking or pressing the shopping cart will then take you to the buying site. It's a predictable and machine parsable way indicating that something is for sale, separate from the post content.
To avoid spam, it is possible for the shopping icon to only appear if it links to one of an allowed list of seller domains. In this way you can be confident that you are only navigating to approved sites.
# Sharing economy
This is intended to add [Freecycle](https://en.wikipedia.org/wiki/The_Freecycle_Network) type functionality within a social network context, leveraging your social connections on the instance, or between participating instances, to facilitate sharing and reduce wasteful consumerism.

View File

@ -636,6 +636,7 @@ def _convert_rss_to_activitypub(base_dir: str, http_prefix: str,
city = 'London, England'
conversation_id = None
languages_understood = [system_language]
buy_url = ''
blog = create_news_post(base_dir,
domain, port, http_prefix,
rss_description,
@ -645,7 +646,8 @@ def _convert_rss_to_activitypub(base_dir: str, http_prefix: str,
rss_title, system_language,
conversation_id, low_bandwidth,
content_license_url,
languages_understood, translate)
languages_understood, translate,
buy_url)
if not blog:
continue

View File

@ -238,7 +238,8 @@ def post_message_to_outbox(session, translate: {},
lists_enabled: str,
content_license_url: str,
dogwhistles: {},
min_images_for_accounts: []) -> bool:
min_images_for_accounts: [],
buy_sites: {}) -> bool:
"""post is received by the outbox
Client to server message post
https://www.w3.org/TR/activitypub/#client-to-server-outbox-delivery
@ -593,7 +594,8 @@ def post_message_to_outbox(session, translate: {},
cw_lists, lists_enabled,
timezone, mitm,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
if is_edited_post:
message_json['type'] = 'Update'

View File

@ -1092,6 +1092,29 @@ def _attach_post_license(post_json_object: {},
})
def _attach_buy_link(post_json_object: {},
buy_url: str, translate: {}) -> None:
"""Attaches a link for buying something
"""
if not buy_url:
return
if '://' not in buy_url:
return
if ' ' in buy_url or '<' in buy_url:
return
buy_str = 'Buy'
if translate.get(buy_str):
buy_str = translate[buy_str]
if 'attachment' not in post_json_object:
post_json_object['attachment'] = []
post_json_object['attachment'].append({
"type": "Link",
"name": buy_str,
"href": buy_url,
"mediaType": "text/html"
})
def _create_post_s2s(base_dir: str, nickname: str, domain: str, port: int,
http_prefix: str, content: str, status_number: str,
published: str, new_post_id: str, post_context: {},
@ -1102,7 +1125,8 @@ def _create_post_s2s(base_dir: str, nickname: str, domain: str, port: int,
post_object_type: str, summary: str,
in_reply_to_atom_uri: str, system_language: str,
conversation_id: str, low_bandwidth: bool,
content_license_url: str) -> {}:
content_license_url: str, buy_url: str,
translate: {}) -> {}:
"""Creates a new server-to-server post
"""
actor_url = local_actor_url(http_prefix, nickname, domain)
@ -1166,6 +1190,7 @@ def _create_post_s2s(base_dir: str, nickname: str, domain: str, port: int,
media_type, image_description, city, low_bandwidth,
content_license_url)
_attach_post_license(new_post['object'], content_license_url)
_attach_buy_link(new_post['object'], buy_url, translate)
return new_post
@ -1179,7 +1204,8 @@ def _create_post_c2s(base_dir: str, nickname: str, domain: str, port: int,
post_object_type: str, summary: str,
in_reply_to_atom_uri: str, system_language: str,
conversation_id: str, low_bandwidth: str,
content_license_url: str) -> {}:
content_license_url: str, buy_url: str,
translate: {}) -> {}:
"""Creates a new client-to-server post
"""
domain_full = get_full_domain(domain, port)
@ -1233,6 +1259,7 @@ def _create_post_c2s(base_dir: str, nickname: str, domain: str, port: int,
media_type, image_description, city, low_bandwidth,
content_license_url)
_attach_post_license(new_post, content_license_url)
_attach_buy_link(new_post, buy_url, translate)
return new_post
@ -1433,7 +1460,8 @@ def _create_post_base(base_dir: str,
system_language: str,
conversation_id: str, low_bandwidth: bool,
content_license_url: str,
languages_understood: [], translate: {}) -> {}:
languages_understood: [], translate: {},
buy_url: str) -> {}:
"""Creates a message
"""
content = remove_invalid_chars(content)
@ -1591,7 +1619,7 @@ def _create_post_base(base_dir: str,
post_object_type, summary,
in_reply_to_atom_uri, system_language,
conversation_id, low_bandwidth,
content_license_url)
content_license_url, buy_url, translate)
else:
new_post = \
_create_post_c2s(base_dir, nickname, domain, port,
@ -1604,7 +1632,7 @@ def _create_post_base(base_dir: str,
post_object_type, summary,
in_reply_to_atom_uri, system_language,
conversation_id, low_bandwidth,
content_license_url)
content_license_url, buy_url, translate)
_create_post_mentions(cc_url, new_post, to_recipients, tags)
@ -1845,7 +1873,8 @@ def create_public_post(base_dir: str,
location: str, is_article: bool, system_language: str,
conversation_id: str, low_bandwidth: bool,
content_license_url: str,
languages_understood: [], translate: {}) -> {}:
languages_understood: [], translate: {},
buy_url: str) -> {}:
"""Public post
"""
domain_full = get_full_domain(domain, port)
@ -1879,7 +1908,7 @@ def create_public_post(base_dir: str,
event_status, ticket_url, system_language,
conversation_id, low_bandwidth,
content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
def _append_citations_to_blog_post(base_dir: str,
@ -1924,7 +1953,8 @@ def create_blog_post(base_dir: str,
location: str, system_language: str,
conversation_id: str, low_bandwidth: bool,
content_license_url: str,
languages_understood: [], translate: {}) -> {}:
languages_understood: [], translate: {},
buy_url: str) -> {}:
blog_json = \
create_public_post(base_dir,
nickname, domain, port, http_prefix,
@ -1937,7 +1967,7 @@ def create_blog_post(base_dir: str,
event_date, event_time, event_end_time, location,
True, system_language, conversation_id,
low_bandwidth, content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
blog_json['object']['url'] = \
blog_json['object']['url'].replace('/@', '/users/')
_append_citations_to_blog_post(base_dir, nickname, domain, blog_json)
@ -1953,7 +1983,8 @@ def create_news_post(base_dir: str,
subject: str, system_language: str,
conversation_id: str, low_bandwidth: bool,
content_license_url: str,
languages_understood: [], translate: {}) -> {}:
languages_understood: [], translate: {},
buy_url: str) -> {}:
client_to_server = False
in_reply_to = None
in_reply_to_atom_uri = None
@ -1974,7 +2005,7 @@ def create_news_post(base_dir: str,
event_date, event_time, event_end_time, location,
True, system_language, conversation_id,
low_bandwidth, content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
blog['object']['type'] = 'Article'
return blog
@ -1995,6 +2026,7 @@ def create_question_post(base_dir: str,
"""
domain_full = get_full_domain(domain, port)
local_actor = local_actor_url(http_prefix, nickname, domain_full)
buy_url = ''
message_json = \
_create_post_base(base_dir, nickname, domain, port,
'https://www.w3.org/ns/activitystreams#Public',
@ -2008,7 +2040,7 @@ def create_question_post(base_dir: str,
None, None, None,
None, None, None, None, None, system_language,
None, low_bandwidth, content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
message_json['object']['type'] = 'Question'
message_json['object']['oneOf'] = []
message_json['object']['votersCount'] = 0
@ -2043,7 +2075,8 @@ def create_unlisted_post(base_dir: str,
location: str, system_language: str,
conversation_id: str, low_bandwidth: bool,
content_license_url: str,
languages_understood: [], translate: {}) -> {}:
languages_understood: [], translate: {},
buy_url: str) -> {}:
"""Unlisted post. This has the #Public and followers links inverted.
"""
domain_full = get_full_domain(domain, port)
@ -2063,7 +2096,7 @@ def create_unlisted_post(base_dir: str,
None, None, None, None, None, system_language,
conversation_id, low_bandwidth,
content_license_url, languages_understood,
translate)
translate, buy_url)
def create_followers_only_post(base_dir: str,
@ -2082,7 +2115,7 @@ def create_followers_only_post(base_dir: str,
conversation_id: str, low_bandwidth: bool,
content_license_url: str,
languages_understood: [],
translate: {}) -> {}:
translate: {}, buy_url: str) -> {}:
"""Followers only post
"""
domain_full = get_full_domain(domain, port)
@ -2100,7 +2133,7 @@ def create_followers_only_post(base_dir: str,
None, None, None, None, None, system_language,
conversation_id, low_bandwidth,
content_license_url, languages_understood,
translate)
translate, buy_url)
def get_mentioned_people(base_dir: str, http_prefix: str,
@ -2157,7 +2190,8 @@ def create_direct_message_post(base_dir: str,
conversation_id: str, low_bandwidth: bool,
content_license_url: str,
languages_understood: [],
dm_is_chat: bool, translate: {}) -> {}:
dm_is_chat: bool, translate: {},
buy_url: str) -> {}:
"""Direct Message post
"""
content = resolve_petnames(base_dir, nickname, domain, content)
@ -2183,7 +2217,7 @@ def create_direct_message_post(base_dir: str,
None, None, None, None, None, system_language,
conversation_id, low_bandwidth,
content_license_url, languages_understood,
translate)
translate, buy_url)
# mentioned recipients go into To rather than Cc
message_json['to'] = message_json['object']['cc']
message_json['object']['to'] = message_json['to']
@ -2269,11 +2303,11 @@ def create_report_post(base_dir: str,
post_to = moderators_list
post_cc = None
post_json_object = None
buy_url = ''
for to_url in post_to:
# who is this report going to?
to_nickname = to_url.split('/users/')[1]
handle = to_nickname + '@' + domain
post_json_object = \
_create_post_base(base_dir, nickname, domain, port,
to_url, post_cc,
@ -2286,7 +2320,7 @@ def create_report_post(base_dir: str,
None, None, None,
None, None, None, None, None, system_language,
None, low_bandwidth, content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
if not post_json_object:
continue
@ -2411,7 +2445,7 @@ def send_post(signing_priv_key_pem: str, project_version: str,
shared_items_federated_domains: [],
shared_item_federation_tokens: {},
low_bandwidth: bool, content_license_url: str,
translate: {},
translate: {}, buy_url: str,
debug: bool = False, in_reply_to: str = None,
in_reply_to_atom_uri: str = None, subject: str = None) -> int:
"""Post to another inbox. Used by unit tests.
@ -2479,7 +2513,7 @@ def send_post(signing_priv_key_pem: str, project_version: str,
None, None, None, None, None, system_language,
conversation_id, low_bandwidth,
content_license_url, languages_understood,
translate)
translate, buy_url)
# get the senders private key
private_key_pem = get_person_key(nickname, domain, base_dir, 'private')
@ -2576,7 +2610,7 @@ def send_post_via_server(signing_priv_key_pem: str, project_version: str,
low_bandwidth: bool,
content_license_url: str,
event_date: str, event_time: str, event_end_time: str,
location: str, translate: {},
location: str, translate: {}, buy_url: str,
debug: bool = False,
in_reply_to: str = None,
in_reply_to_atom_uri: str = None,
@ -2668,7 +2702,7 @@ def send_post_via_server(signing_priv_key_pem: str, project_version: str,
None, None, None, None, None, system_language,
conversation_id, low_bandwidth,
content_license_url, languages_understood,
translate)
translate, buy_url)
auth_header = create_basic_auth_header(from_nickname, password)

View File

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

View File

@ -773,6 +773,7 @@ def create_server_alice(path: str, domain: str, port: int,
conversation_id = None
translate = {}
content_license_url = 'https://creativecommons.org/licenses/by-nc/4.0'
buy_url = ''
create_public_post(path, nickname, domain, port, http_prefix,
"No wise fish would go anywhere without a porpoise",
test_save_to_file,
@ -787,7 +788,7 @@ def create_server_alice(path: str, domain: str, port: int,
test_event_end_time, test_location,
test_is_article, system_language, conversation_id,
low_bandwidth, content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
create_public_post(path, nickname, domain, port, http_prefix,
"Curiouser and curiouser!",
test_save_to_file,
@ -802,7 +803,7 @@ def create_server_alice(path: str, domain: str, port: int,
test_event_end_time, test_location,
test_is_article, system_language, conversation_id,
low_bandwidth, content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
create_public_post(path, nickname, domain, port, http_prefix,
"In the gardens of memory, in the palace " +
"of dreams, that is where you and I shall meet",
@ -818,7 +819,7 @@ def create_server_alice(path: str, domain: str, port: int,
test_event_end_time, test_location,
test_is_article, system_language, conversation_id,
low_bandwidth, content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
regenerate_index_for_box(path, nickname, domain, 'outbox')
global TEST_SERVER_ALICE_RUNNING
TEST_SERVER_ALICE_RUNNING = True
@ -937,6 +938,7 @@ def create_server_bob(path: str, domain: str, port: int,
conversation_id = None
content_license_url = 'https://creativecommons.org/licenses/by-nc/4.0'
translate = {}
buy_url = ''
create_public_post(path, nickname, domain, port, http_prefix,
"It's your life, live it your way.",
test_save_to_file,
@ -951,7 +953,7 @@ def create_server_bob(path: str, domain: str, port: int,
test_event_end_time, test_location,
test_is_article, system_language, conversation_id,
low_bandwidth, content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
create_public_post(path, nickname, domain, port, http_prefix,
"One of the things I've realised is that " +
"I am very simple",
@ -967,7 +969,7 @@ def create_server_bob(path: str, domain: str, port: int,
test_event_end_time, test_location,
test_is_article, system_language, conversation_id,
low_bandwidth, content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
create_public_post(path, nickname, domain, port, http_prefix,
"Quantum physics is a bit of a passion of mine",
test_save_to_file,
@ -982,7 +984,7 @@ def create_server_bob(path: str, domain: str, port: int,
test_event_end_time, test_location,
test_is_article, system_language, conversation_id,
low_bandwidth, content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
regenerate_index_for_box(path, nickname, domain, 'outbox')
global TEST_SERVER_BOB_RUNNING
TEST_SERVER_BOB_RUNNING = True
@ -1321,6 +1323,7 @@ def test_post_message_between_servers(base_dir: str) -> None:
low_bandwidth = False
signing_priv_key_pem = None
translate = {}
buy_url = ''
send_result = \
send_post(signing_priv_key_pem, __version__,
session_alice, alice_dir, 'alice', alice_domain, alice_port,
@ -1335,7 +1338,7 @@ def test_post_message_between_servers(base_dir: str) -> None:
languages_understood,
alice_shared_items_federated_domains,
alice_shared_item_federation_tokens, low_bandwidth,
content_license_url, translate,
content_license_url, translate, buy_url,
in_reply_to, in_reply_to_atom_uri, subject)
print('send_result: ' + str(send_result))
@ -1402,6 +1405,8 @@ def test_post_message_between_servers(base_dir: str) -> None:
assert 'यह एक परीक्षण है' in received_json['object']['content']
print('Check that message received from Alice contains an attachment')
assert received_json['object']['attachment']
if len(received_json['object']['attachment']) != 2:
pprint(received_json['object']['attachment'])
assert len(received_json['object']['attachment']) == 2
attached = received_json['object']['attachment'][0]
pprint(attached)
@ -1693,6 +1698,7 @@ def test_follow_between_servers(base_dir: str) -> None:
low_bandwidth = False
signing_priv_key_pem = None
translate = {}
buy_url = ''
send_result = \
send_post(signing_priv_key_pem, __version__,
session_alice, alice_dir, 'alice', alice_domain, alice_port,
@ -1705,7 +1711,7 @@ def test_follow_between_servers(base_dir: str) -> None:
languages_understood,
alice_shared_items_federated_domains,
alice_shared_item_federation_tokens, low_bandwidth,
content_license_url, translate,
content_license_url, translate, buy_url,
in_reply_to, in_reply_to_atom_uri, subject)
print('send_result: ' + str(send_result))
@ -2060,6 +2066,7 @@ def test_shared_items_federation(base_dir: str) -> None:
low_bandwidth = False
signing_priv_key_pem = None
translate = {}
buy_url = ''
send_result = \
send_post(signing_priv_key_pem, __version__,
session_alice, alice_dir, 'alice', alice_domain, alice_port,
@ -2072,7 +2079,7 @@ def test_shared_items_federation(base_dir: str) -> None:
languages_understood,
alice_shared_items_federated_domains,
alice_shared_item_federation_tokens, low_bandwidth,
content_license_url, translate, True,
content_license_url, translate, buy_url, True,
in_reply_to, in_reply_to_atom_uri, subject)
print('send_result: ' + str(send_result))
@ -2490,6 +2497,7 @@ def test_group_follow(base_dir: str) -> None:
if os.path.isfile(os.path.join(outbox_path, name))])
translate = {}
buy_url = ''
send_result = \
send_post(signing_priv_key_pem, __version__,
session_alice, alice_dir, 'alice', alice_domain, alice_port,
@ -2502,7 +2510,7 @@ def test_group_follow(base_dir: str) -> None:
languages_understood,
alice_shared_items_federated_domains,
alice_shared_item_federation_tokens, low_bandwidth,
content_license_url, translate,
content_license_url, translate, buy_url,
in_reply_to, in_reply_to_atom_uri, subject)
print('send_result: ' + str(send_result))
@ -2877,6 +2885,7 @@ def _test_create_person_account(base_dir: str):
"(yawn)\n\n...then it's not really independent.\n\n" + \
"Politicians will threaten to withdraw funding if you do " + \
"anything which challenges middle class sensibilities or incomes."
buy_url = ''
test_post_json = \
create_public_post(base_dir, nickname, domain, port, http_prefix,
content, save_to_file,
@ -2889,7 +2898,7 @@ def _test_create_person_account(base_dir: str):
test_event_end_time, test_location,
test_is_article, system_language, conversation_id,
low_bandwidth, content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
assert test_post_json
assert test_post_json.get('object')
assert test_post_json['object']['content']
@ -2915,7 +2924,7 @@ def _test_create_person_account(base_dir: str):
test_event_end_time, test_location,
test_is_article, system_language, conversation_id,
low_bandwidth, content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
assert test_post_json
assert test_post_json.get('object')
assert test_post_json['object']['content']
@ -3116,6 +3125,7 @@ def test_client_to_server(base_dir: str):
event_end_time = '12:30'
location = "Kinshasa"
translate = {}
buy_url = ''
send_result = \
send_post_via_server(signing_priv_key_pem, __version__,
alice_dir, session_alice, 'alice', password,
@ -3128,7 +3138,7 @@ def test_client_to_server(base_dir: str):
system_language, languages_understood,
low_bandwidth, content_license_url,
event_date, event_time, event_end_time, location,
translate, True, None, None,
translate, buy_url, True, None, None,
conversation_id, None)
print('send_result: ' + str(send_result))
@ -4728,6 +4738,7 @@ def _test_reply_to_public_post(base_dir: str) -> None:
low_bandwidth = True
content_license_url = 'https://creativecommons.org/licenses/by-nc/4.0'
translate = {}
buy_url = ''
reply = \
create_public_post(base_dir, nickname, domain, port, http_prefix,
content, save_to_file,
@ -4740,7 +4751,7 @@ def _test_reply_to_public_post(base_dir: str) -> None:
test_event_end_time, test_location,
test_is_article, system_language, conversation_id,
low_bandwidth, content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
# print(str(reply))
expected_str = \
'<p><span class=\"h-card\">' + \
@ -5678,6 +5689,7 @@ def _test_links_within_post(base_dir: str) -> None:
low_bandwidth = True
content_license_url = 'https://creativecommons.org/licenses/by-nc/4.0'
translate = {}
buy_url = ''
post_json_object = \
create_public_post(base_dir, nickname, domain, port, http_prefix,
@ -5691,7 +5703,7 @@ def _test_links_within_post(base_dir: str) -> None:
test_event_end_time, test_location,
test_is_article, system_language, conversation_id,
low_bandwidth, content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
expected_str = \
'<p>This is a test post with links.<br><br>' + \
@ -5735,7 +5747,7 @@ def _test_links_within_post(base_dir: str) -> None:
test_event_end_time, test_location,
test_is_article, system_language, conversation_id,
low_bandwidth, content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
assert post_json_object['object']['content'] == content
assert post_json_object['object']['contentMap'][system_language] == content
@ -6787,6 +6799,7 @@ def _test_can_replyto(base_dir: str) -> None:
low_bandwidth = True
content_license_url = 'https://creativecommons.org/licenses/by-nc/4.0'
translate = {}
buy_url = ''
post_json_object = \
create_public_post(base_dir, nickname, domain, port, http_prefix,
@ -6800,7 +6813,7 @@ def _test_can_replyto(base_dir: str) -> None:
test_event_end_time, test_location,
test_is_article, system_language, conversation_id,
low_bandwidth, content_license_url,
languages_understood, translate)
languages_understood, translate, buy_url)
# set the date on the post
curr_date_str = "2021-09-08T20:45:00Z"
post_json_object['published'] = curr_date_str

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -608,5 +608,9 @@
"Inactive": "غير نشط",
"Registrations open": "التسجيلات مفتوحة",
"Registrations remaining": "التسجيلات المتبقية",
"Edit reminder": "تحرير التذكير"
"Edit reminder": "تحرير التذكير",
"Purchase": "شراء",
"Subscribe": "الإشتراك",
"Buy link": "رابط شراء",
"Buy links are allowed from the following domains": "روابط الشراء مسموح بها من المجالات التالية"
}

View File

@ -608,5 +608,9 @@
"Inactive": "নিষ্ক্রিয়",
"Registrations open": "রেজিস্ট্রেশন খোলা",
"Registrations remaining": "রেজিস্ট্রেশন বাকি",
"Edit reminder": "অনুস্মারক সম্পাদনা করুন"
"Edit reminder": "অনুস্মারক সম্পাদনা করুন",
"Purchase": "ক্রয়",
"Subscribe": "সাবস্ক্রাইব",
"Buy link": "সংযোগ কেনা",
"Buy links are allowed from the following domains": "নিম্নলিখিত ডোমেনগুলি থেকে লিঙ্কগুলি কেনার অনুমতি দেওয়া হয়েছে"
}

View File

@ -608,5 +608,9 @@
"Inactive": "Inactiu",
"Registrations open": "Inscripcions obertes",
"Registrations remaining": "Inscripcions restants",
"Edit reminder": "Edita el recordatori"
"Edit reminder": "Edita el recordatori",
"Purchase": "Compra",
"Subscribe": "Subscriu-te",
"Buy link": "Enllaç de compra",
"Buy links are allowed from the following domains": "Els enllaços de compra es permeten des dels dominis següents"
}

View File

@ -608,5 +608,9 @@
"Inactive": "Anactif",
"Registrations open": "Cofrestriadau yn agor",
"Registrations remaining": "Cofrestriadau ar ôl",
"Edit reminder": "Golygu nodyn atgoffa"
"Edit reminder": "Golygu nodyn atgoffa",
"Purchase": "Prynu",
"Subscribe": "Tanysgrifio",
"Buy link": "Prynu dolen",
"Buy links are allowed from the following domains": "Caniateir dolenni prynu o'r parthau canlynol"
}

View File

@ -608,5 +608,9 @@
"Inactive": "Inaktiv",
"Registrations open": "Anmeldungen geöffnet",
"Registrations remaining": "Anmeldungen verbleiben",
"Edit reminder": "Erinnerung bearbeiten"
"Edit reminder": "Erinnerung bearbeiten",
"Purchase": "Kaufen",
"Subscribe": "Abonnieren",
"Buy link": "Link kaufen",
"Buy links are allowed from the following domains": "Kauflinks sind von den folgenden Domains erlaubt"
}

View File

@ -608,5 +608,9 @@
"Inactive": "Αδρανής",
"Registrations open": "Οι εγγραφές ανοίγουν",
"Registrations remaining": "Απομένουν οι εγγραφές",
"Edit reminder": "Επεξεργασία υπενθύμισης"
"Edit reminder": "Επεξεργασία υπενθύμισης",
"Purchase": "Αγορά",
"Subscribe": "Εγγραφείτε",
"Buy link": "Σύνδεσμος αγοράς",
"Buy links are allowed from the following domains": "Οι σύνδεσμοι αγοράς επιτρέπονται από τους παρακάτω τομείς"
}

View File

@ -608,5 +608,9 @@
"Inactive": "Inactive",
"Registrations open": "Registrations open",
"Registrations remaining": "Registrations remaining",
"Edit reminder": "Edit reminder"
"Edit reminder": "Edit reminder",
"Purchase": "Purchase",
"Subscribe": "Subscribe",
"Buy link": "Buy link",
"Buy links are allowed from the following domains": "Buy links are allowed from the following domains"
}

View File

@ -608,5 +608,9 @@
"Inactive": "Αδρανής",
"Registrations open": "Inscripciones abiertas",
"Registrations remaining": "Registros restantes",
"Edit reminder": "Editar recordatorio"
"Edit reminder": "Editar recordatorio",
"Purchase": "Compra",
"Subscribe": "Suscribir",
"Buy link": "Enlace de compra",
"Buy links are allowed from the following domains": "Se permiten enlaces de compra de los siguientes dominios"
}

View File

@ -608,5 +608,9 @@
"Inactive": "غیر فعال",
"Registrations open": "ثبت نام ها باز شد",
"Registrations remaining": "ثبت نام باقی مانده است",
"Edit reminder": "ویرایش یادآوری"
"Edit reminder": "ویرایش یادآوری",
"Purchase": "خرید",
"Subscribe": "اشتراک در",
"Buy link": "لینک خرید",
"Buy links are allowed from the following domains": "لینک خرید از دامنه های زیر مجاز است"
}

View File

@ -608,5 +608,9 @@
"Inactive": "Inactive",
"Registrations open": "Inscriptions ouvertes",
"Registrations remaining": "Inscriptions restantes",
"Edit reminder": "Modifier le rappel"
"Edit reminder": "Modifier le rappel",
"Purchase": "Acheter",
"Subscribe": "S'abonner",
"Buy link": "Acheter un lien",
"Buy links are allowed from the following domains": "Les liens d'achat sont autorisés à partir des domaines suivants"
}

View File

@ -608,5 +608,9 @@
"Inactive": "Neamhghníomhach",
"Registrations open": "Clárúcháin oscailte",
"Registrations remaining": "Clárúcháin fágtha",
"Edit reminder": "Cuir meabhrúchán in eagar"
"Edit reminder": "Cuir meabhrúchán in eagar",
"Purchase": "Ceannach",
"Subscribe": "Liostáil",
"Buy link": "Ceannaigh nasc",
"Buy links are allowed from the following domains": "Ceadaítear naisc cheannaigh ó na fearainn seo a leanas"
}

View File

@ -608,5 +608,9 @@
"Inactive": "निष्क्रिय",
"Registrations open": "पंजीकरण खुले हैं",
"Registrations remaining": "रजिस्ट्रेशन बाकी हैं",
"Edit reminder": "रिमाइंडर संपादित करें"
"Edit reminder": "रिमाइंडर संपादित करें",
"Purchase": "खरीदना",
"Subscribe": "सदस्यता लेने के",
"Buy link": "लिंक खरीदें",
"Buy links are allowed from the following domains": "निम्नलिखित डोमेन से खरीदें लिंक की अनुमति है"
}

View File

@ -608,5 +608,9 @@
"Inactive": "Non attivo",
"Registrations open": "Aperte le iscrizioni",
"Registrations remaining": "Iscrizioni rimanenti",
"Edit reminder": "Modifica promemoria"
"Edit reminder": "Modifica promemoria",
"Purchase": "Acquistare",
"Subscribe": "Sottoscrivi",
"Buy link": "Link per l'acquisto",
"Buy links are allowed from the following domains": "I link di acquisto sono consentiti dai seguenti domini"
}

View File

@ -608,5 +608,9 @@
"Inactive": "非活性",
"Registrations open": "登録開始",
"Registrations remaining": "残りの登録数",
"Edit reminder": "リマインダーを編集"
"Edit reminder": "リマインダーを編集",
"Purchase": "購入",
"Subscribe": "申し込む",
"Buy link": "購入リンク",
"Buy links are allowed from the following domains": "次のドメインからの購入リンクが許可されています"
}

View File

@ -608,5 +608,9 @@
"Inactive": "비활성",
"Registrations open": "등록 시작",
"Registrations remaining": "남은 등록",
"Edit reminder": "알림 수정"
"Edit reminder": "알림 수정",
"Purchase": "구입",
"Subscribe": "구독하다",
"Buy link": "구매 링크",
"Buy links are allowed from the following domains": "다음 도메인에서 구매 링크가 허용됩니다."
}

View File

@ -608,5 +608,9 @@
"Inactive": "Bêkar",
"Registrations open": "Qeydkirin vedibin",
"Registrations remaining": "Registrations maye",
"Edit reminder": "Bîranîna biguherîne"
"Edit reminder": "Bîranîna biguherîne",
"Purchase": "Kirrîn",
"Subscribe": "Subscribe",
"Buy link": "Girêdanê bikirin",
"Buy links are allowed from the following domains": "Zencîreyên kirînê ji domên jêrîn têne destûr kirin"
}

View File

@ -608,5 +608,9 @@
"Inactive": "Inactief",
"Registrations open": "Inschrijvingen geopend",
"Registrations remaining": "Resterende inschrijvingen",
"Edit reminder": "Herinnering bewerken"
"Edit reminder": "Herinnering bewerken",
"Purchase": "Aankoop",
"Subscribe": "Abonneren",
"Buy link": "koop link",
"Buy links are allowed from the following domains": "Kooplinks zijn toegestaan vanaf de volgende domeinen"
}

View File

@ -604,5 +604,9 @@
"Inactive": "Inactive",
"Registrations open": "Registrations open",
"Registrations remaining": "Registrations remaining",
"Edit reminder": "Edit reminder"
"Edit reminder": "Edit reminder",
"Purchase": "Purchase",
"Subscribe": "Subscribe",
"Buy link": "Buy link",
"Buy links are allowed from the following domains": "Buy links are allowed from the following domains"
}

View File

@ -608,5 +608,9 @@
"Inactive": "Nieaktywny",
"Registrations open": "Rejestracje otwarte",
"Registrations remaining": "Pozostały zapisy",
"Edit reminder": "Edytuj przypomnienie"
"Edit reminder": "Edytuj przypomnienie",
"Purchase": "Zakup",
"Subscribe": "Subskrybuj",
"Buy link": "Kup Link",
"Buy links are allowed from the following domains": "Kupuj linki są dozwolone z następujących domen"
}

View File

@ -608,5 +608,9 @@
"Inactive": "Inativa",
"Registrations open": "inscrições abertas",
"Registrations remaining": "Inscrições restantes",
"Edit reminder": "Editar lembrete"
"Edit reminder": "Editar lembrete",
"Purchase": "Comprar",
"Subscribe": "Se inscrever",
"Buy link": "Link de compra",
"Buy links are allowed from the following domains": "Links de compra são permitidos nos seguintes domínios"
}

View File

@ -608,5 +608,9 @@
"Inactive": "Неактивный",
"Registrations open": "Регистрация открыта",
"Registrations remaining": "Осталось регистраций",
"Edit reminder": "Изменить напоминание"
"Edit reminder": "Изменить напоминание",
"Purchase": "Покупка",
"Subscribe": "Подписаться",
"Buy link": "Купить ссылку",
"Buy links are allowed from the following domains": "Ссылки на покупку разрешены со следующих доменов"
}

View File

@ -608,5 +608,9 @@
"Inactive": "Isiyotumika",
"Registrations open": "Usajili umefunguliwa",
"Registrations remaining": "Usajili uliosalia",
"Edit reminder": "Badilisha kikumbusho"
"Edit reminder": "Badilisha kikumbusho",
"Purchase": "Nunua",
"Subscribe": "Jisajili",
"Buy link": "Nunua kiungo",
"Buy links are allowed from the following domains": "Viungo vya kununua vinaruhusiwa kutoka kwa vikoa vifuatavyo"
}

View File

@ -608,5 +608,9 @@
"Inactive": "etkin değil",
"Registrations open": "kayıtlar açık",
"Registrations remaining": "kalan kayıtlar",
"Edit reminder": "Hatırlatıcıyı düzenle"
"Edit reminder": "Hatırlatıcıyı düzenle",
"Purchase": "Satın alma",
"Subscribe": "Abone",
"Buy link": "Bağlantı satın al",
"Buy links are allowed from the following domains": "Aşağıdaki alanlardan satın alma bağlantılarına izin verilir"
}

View File

@ -608,5 +608,9 @@
"Inactive": "Неактивний",
"Registrations open": "Реєстрація відкрита",
"Registrations remaining": "Залишилось реєстрацій",
"Edit reminder": "Редагувати нагадування"
"Edit reminder": "Редагувати нагадування",
"Purchase": "Купівля",
"Subscribe": "Підпишіться",
"Buy link": "Купити посилання",
"Buy links are allowed from the following domains": "Посилання на купівлю дозволено з таких доменів"
}

View File

@ -608,5 +608,9 @@
"Inactive": "ינאַקטיוו",
"Registrations open": "רעדזשיסטריישאַנז עפענען",
"Registrations remaining": "רעדזשיסטריישאַנז רוען",
"Edit reminder": "רעדאַגירן דערמאָנונג"
"Edit reminder": "רעדאַגירן דערמאָנונג",
"Purchase": "קויפן",
"Subscribe": "אַבאָנירן",
"Buy link": "קויפן לינק",
"Buy links are allowed from the following domains": "קויפן פֿאַרבינדונגען זענען ערלויבט פֿון די פאלגענדע דאָומיינז"
}

View File

@ -608,5 +608,9 @@
"Inactive": "无效",
"Registrations open": "注册开放",
"Registrations remaining": "剩余名额",
"Edit reminder": "编辑提醒"
"Edit reminder": "编辑提醒",
"Purchase": "购买",
"Subscribe": "订阅",
"Buy link": "购买链接",
"Buy links are allowed from the following domains": "允许来自以下域的购买链接"
}

View File

@ -4166,7 +4166,7 @@ def save_reverse_timeline(base_dir: str, reverse_sequence: []) -> []:
def is_quote_toot(post_json_object: str, content: str) -> bool:
"""Returns true if the given post is a quote toot
"""Returns true if the given post is a quote toot / quote tweet
"""
# Pleroma/Misskey implementations
if post_json_object['object'].get('quoteUri') or \

View File

@ -40,7 +40,8 @@ def html_confirm_delete(server,
max_like_count: int, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str,
dogwhistles: {},
min_images_for_accounts: []) -> str:
min_images_for_accounts: [],
buy_sites: {}) -> str:
"""Shows a screen asking to confirm the deletion of a post
"""
if '/statuses/' not in message_id:
@ -96,7 +97,7 @@ def html_confirm_delete(server,
False, False, False, False, False, False,
cw_lists, lists_enabled, timezone, mitm,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None, buy_sites)
delete_post_str += '<center>'
delete_post_str += \
' <p class="followText">' + \

View File

@ -42,7 +42,7 @@ def html_conversation_view(post_id: str,
timezone: str, bold_reading: bool,
dogwhistles: {}, access_keys: {},
min_images_for_accounts: [],
debug: bool) -> str:
debug: bool, buy_sites: {}) -> str:
"""Show a page containing a conversation thread
"""
conv_posts = \
@ -96,7 +96,8 @@ def html_conversation_view(post_id: str,
cw_lists, lists_enabled,
timezone, False, bold_reading,
dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
if post_str:
# check for "HTTP/1.1 303 See Other Server"
if 'X-AP-Instance-ID' not in post_str:

View File

@ -24,6 +24,7 @@ from utils import get_currencies
from utils import get_category_types
from utils import get_account_timezone
from utils import get_supported_languages
from webapp_utils import get_buy_links
from webapp_utils import html_following_data_list
from webapp_utils import html_common_emoji
from webapp_utils import begin_edit_section
@ -239,7 +240,9 @@ def html_new_post(edit_post_params: {},
dogwhistles: {},
min_images_for_accounts: [],
default_month: int, default_year: int,
default_post_language: str) -> str:
default_post_language: str,
buy_sites: {},
default_buy_site: str) -> str:
"""New post screen
"""
# get the json if this is an edited post
@ -256,6 +259,12 @@ def html_new_post(edit_post_params: {},
return ''
if not edited_post_json:
return ''
buy_links = \
get_buy_links(edited_post_json, translate, buy_sites)
if buy_links:
for _, buy_url in buy_links.items():
default_buy_site = buy_url
break
if edited_post_json['object'].get('conversation'):
conversation_id = edited_post_json['object']['conversation']
elif edited_post_json['object'].get('context'):
@ -390,7 +399,8 @@ def html_new_post(edit_post_params: {},
cw_lists, lists_enabled,
timezone, False,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
reply_str = '<input type="hidden" ' + \
'name="replyTo" value="' + in_reply_to + '">\n'
@ -479,7 +489,6 @@ def html_new_post(edit_post_params: {},
' <textarea id="imageDescription" name="imageDescription" ' + \
'style="height:' + str(image_description_height) + \
'px" spellcheck="true" autocomplete="on"></textarea>\n'
new_post_image_section += end_edit_section()
new_post_emoji_section = ''
@ -766,6 +775,9 @@ def html_new_post(edit_post_params: {},
' <label class="labels">' + \
translate['Language used'] + '</label>\n'
replies_section += languages_dropdown
buy_link_str = '🛒 ' + translate['Buy link']
replies_section += edit_text_field(buy_link_str, 'buyUrl',
default_buy_site, 'https://...')
replies_section += '</div>\n'
date_and_location = \

View File

@ -40,7 +40,8 @@ def _html_front_screen_posts(recent_posts_cache: {}, max_recent_posts: int,
lists_enabled: str,
bold_reading: bool,
dogwhistles: {},
min_images_for_accounts: []) -> str:
min_images_for_accounts: [],
buy_sites: {}) -> 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
@ -95,7 +96,8 @@ def _html_front_screen_posts(recent_posts_cache: {}, max_recent_posts: int,
cw_lists, lists_enabled,
timezone, False,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
if post_str:
profile_str += post_str + separator_str
ctr += 1
@ -128,7 +130,8 @@ def html_front_screen(signing_priv_key_pem: str,
max_items_per_page: int,
cw_lists: {}, lists_enabled: str,
dogwhistles: {},
min_images_for_accounts: []) -> str:
min_images_for_accounts: [],
buy_sites: {}) -> str:
"""Show the news instance front screen
"""
bold_reading = False
@ -204,7 +207,8 @@ def html_front_screen(signing_priv_key_pem: str,
signing_priv_key_pem,
cw_lists, lists_enabled,
bold_reading, dogwhistles,
min_images_for_accounts) + license_str
min_images_for_accounts,
buy_sites) + license_str
# Footer which is only used for system accounts
profile_footer_str = ' </td>\n'

View File

@ -43,6 +43,7 @@ def html_likers_of_post(base_dir: str, nickname: str,
box_name: str, default_timeline: str,
bold_reading: bool, dogwhistles: {},
min_images_for_accounts: [],
buy_sites: {},
dict_name: str = 'likes') -> str:
"""Returns html for a screen showing who liked a post
"""
@ -113,7 +114,8 @@ def html_likers_of_post(base_dir: str, nickname: str,
cw_lists, lists_enabled,
timezone, mitm, bold_reading,
dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
# show likers beneath the post
obj = post_json_object

View File

@ -61,7 +61,8 @@ def html_moderation(default_timeline: str,
timezone: str, bold_reading: bool,
dogwhistles: {}, ua_str: str,
min_images_for_accounts: [],
reverse_sequence: bool) -> str:
reverse_sequence: bool,
buy_sites: {}) -> str:
"""Show the moderation feed as html
This is what you see when selecting the "mod" timeline
"""
@ -88,7 +89,8 @@ def html_moderation(default_timeline: str,
max_like_count, shared_items_federated_domains,
signing_priv_key_pem, cw_lists, lists_enabled,
timezone, bold_reading, dogwhistles, ua_str,
min_images_for_accounts, reverse_sequence, None)
min_images_for_accounts, reverse_sequence, None,
buy_sites)
def html_account_info(translate: {},

View File

@ -81,6 +81,7 @@ from content import get_mentions_from_html
from content import switch_words
from person import is_person_snoozed
from person import get_person_avatar_url
from webapp_utils import get_buy_links
from webapp_utils import language_right_to_left
from webapp_utils import get_banner_file
from webapp_utils import get_avatar_image_url
@ -1648,6 +1649,7 @@ def _get_footer_with_icons(show_icons: bool,
like_str: str, reaction_str: str,
bookmark_str: str,
delete_str: str, mute_str: str, edit_str: str,
buy_str: str,
post_json_object: {}, published_link: str,
time_class: str, published_str: str,
nickname: str, content_license_url: str,
@ -1661,7 +1663,7 @@ def _get_footer_with_icons(show_icons: bool,
footer_str += ' <div class="' + container_class_icons + '">\n'
footer_str += \
reply_str + announce_str + like_str + bookmark_str + reaction_str
footer_str += delete_str + mute_str + edit_str
footer_str += delete_str + mute_str + edit_str + buy_str
if not is_news_post(post_json_object):
footer_str += ' '
if content_license_url:
@ -1836,6 +1838,26 @@ def _get_copyright_footer(content_license_url: str,
return copyright_str
def _get_buy_footer(buy_links: {}, translate: {}) -> str:
"""Returns the footer buy link
"""
if not buy_links:
return ''
icon_filename = 'buy.png'
description = translate['Buy']
buy_str = ''
for buy_title, buy_url in buy_links.items():
buy_str = \
' ' + \
'<a class="imageAnchor" href="' + buy_url + \
'" title="' + description + '" tabindex="10">' + \
'<img loading="lazy" decoding="async" title="' + \
description + '" alt="' + description + \
' |" src="/icons/' + icon_filename + '"/></a>\n'
break
return buy_str
def individual_post_as_html(signing_priv_key_pem: str,
allow_downloads: bool,
recent_posts_cache: {}, max_recent_posts: int,
@ -1867,7 +1889,8 @@ def individual_post_as_html(signing_priv_key_pem: str,
mitm: bool, bold_reading: bool,
dogwhistles: {},
minimize_all_images: bool,
first_post_id: str) -> str:
first_post_id: str,
buy_sites: {}) -> str:
""" Shows a single post as html
"""
if not post_json_object:
@ -2475,12 +2498,24 @@ def individual_post_as_html(signing_priv_key_pem: str,
if disallow_reply(content_all_str):
reply_str = ''
is_patch = is_git_patch(base_dir, nickname, domain,
post_json_object['object']['type'],
summary_str, content_str)
# html for the buy icon
buy_str = ''
if 'attachment' not in post_json_object['object']:
post_json_object['object']['attachment'] = []
if not is_patch:
buy_links = get_buy_links(post_json_object, translate, buy_sites)
buy_str = _get_buy_footer(buy_links, translate)
new_footer_str = \
_get_footer_with_icons(show_icons,
container_class_icons,
reply_str, announce_str,
like_str, reaction_str, bookmark_str,
delete_str, mute_str, edit_str,
delete_str, mute_str, edit_str, buy_str,
post_json_object, published_link,
time_class, published_str, nickname,
content_license_url, translate)
@ -2495,9 +2530,6 @@ def individual_post_as_html(signing_priv_key_pem: str,
if not summary_str:
summary_str = get_summary_from_post(post_json_object, system_language,
languages_understood)
is_patch = is_git_patch(base_dir, nickname, domain,
post_json_object['object']['type'],
summary_str, content_str)
_log_post_timing(enable_timing_log, post_start_time, '16')
@ -2582,13 +2614,14 @@ def individual_post_as_html(signing_priv_key_pem: str,
_log_post_timing(enable_timing_log, post_start_time, '17')
map_str = ''
buy_links = {}
if post_json_object['object'].get('tag'):
if not is_patch:
content_str = \
replace_emoji_from_tags(session, base_dir, content_str,
post_json_object['object']['tag'],
'content', False, True)
buy_links = get_buy_links(post_json_object, translate, buy_sites)
# show embedded map if the location contains a map url
location_str = \
get_location_from_tags(post_json_object['object']['tag'])
@ -2707,7 +2740,8 @@ def html_individual_post(recent_posts_cache: {}, max_recent_posts: int,
cw_lists: {}, lists_enabled: str,
timezone: str, mitm: bool,
bold_reading: bool, dogwhistles: {},
min_images_for_accounts: []) -> str:
min_images_for_accounts: [],
buy_sites: {}) -> str:
"""Show an individual post as html
"""
original_post_json = post_json_object
@ -2791,7 +2825,7 @@ def html_individual_post(recent_posts_cache: {}, max_recent_posts: int,
False, authorized, False, False, False, False,
cw_lists, lists_enabled, timezone, mitm,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None, buy_sites)
message_id = remove_id_ending(post_json_object['id'])
# show the previous posts
@ -2835,7 +2869,7 @@ def html_individual_post(recent_posts_cache: {}, max_recent_posts: int,
bold_reading,
dogwhistles,
minimize_all_images,
None) + post_str
None, buy_sites) + post_str
# show the following posts
post_filename = locate_post(base_dir, nickname, domain, message_id)
@ -2875,7 +2909,8 @@ def html_individual_post(recent_posts_cache: {}, max_recent_posts: int,
cw_lists, lists_enabled,
timezone, False,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
css_filename = base_dir + '/epicyon-profile.css'
if os.path.isfile(base_dir + '/epicyon.css'):
css_filename = base_dir + '/epicyon.css'
@ -2906,7 +2941,8 @@ def html_post_replies(recent_posts_cache: {}, max_recent_posts: int,
lists_enabled: str,
timezone: str, bold_reading: bool,
dogwhistles: {},
min_images_for_accounts: []) -> str:
min_images_for_accounts: [],
buy_sites: {}) -> str:
"""Show the replies to an individual post as html
"""
replies_str = ''
@ -2937,7 +2973,8 @@ def html_post_replies(recent_posts_cache: {}, max_recent_posts: int,
cw_lists, lists_enabled,
timezone, False,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
css_filename = base_dir + '/epicyon-profile.css'
if os.path.isfile(base_dir + '/epicyon.css'):
@ -2968,7 +3005,8 @@ def html_emoji_reaction_picker(recent_posts_cache: {}, max_recent_posts: int,
box_name: str, page_number: int,
timezone: str, bold_reading: bool,
dogwhistles: {},
min_images_for_accounts: []) -> str:
min_images_for_accounts: [],
buy_sites: {}) -> str:
"""Returns the emoji picker screen
"""
minimize_all_images = False
@ -2996,7 +3034,7 @@ def html_emoji_reaction_picker(recent_posts_cache: {}, max_recent_posts: int,
False, False, False, False, False, False,
cw_lists, lists_enabled, timezone, False,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None, buy_sites)
reactions_filename = base_dir + '/emoji/reactions.json'
if not os.path.isfile(reactions_filename):

View File

@ -164,7 +164,8 @@ def html_profile_after_search(recent_posts_cache: {}, max_recent_posts: int,
timezone: str,
onion_domain: str, i2p_domain: str,
bold_reading: bool, dogwhistles: {},
min_images_for_accounts: []) -> str:
min_images_for_accounts: [],
buy_sites: {}) -> str:
"""Show a profile page after a search for a fediverse address
"""
http = False
@ -416,7 +417,8 @@ def html_profile_after_search(recent_posts_cache: {}, max_recent_posts: int,
cw_lists, lists_enabled,
timezone, False,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
i += 1
if i >= 8:
break
@ -646,7 +648,8 @@ def html_profile(signing_priv_key_pem: str,
max_items_per_page: int,
cw_lists: {}, lists_enabled: str,
content_license_url: str,
timezone: str, bold_reading: bool) -> str:
timezone: str, bold_reading: bool,
buy_sites: {}) -> str:
"""Show the profile page as html
"""
show_moved_accounts = False
@ -678,7 +681,7 @@ def html_profile(signing_priv_key_pem: str,
shared_items_federated_domains, None,
page_number, max_items_per_page, cw_lists,
lists_enabled, {},
min_images_for_accounts)
min_images_for_accounts, buy_sites)
domain, port = get_domain_from_actor(profile_json['id'])
if not domain:
@ -1169,7 +1172,8 @@ def html_profile(signing_priv_key_pem: str,
cw_lists, lists_enabled,
timezone, bold_reading, {},
min_images_for_accounts,
max_profile_posts) + license_str
max_profile_posts,
buy_sites) + license_str
if not is_group:
if selected == 'following':
profile_str += \
@ -1269,7 +1273,8 @@ def _html_profile_posts(recent_posts_cache: {}, max_recent_posts: int,
timezone: str, bold_reading: bool,
dogwhistles: {},
min_images_for_accounts: [],
max_profile_posts: int) -> str:
max_profile_posts: int,
buy_sites: {}) -> str:
"""Shows posts on the profile screen
These should only be public posts
"""
@ -1327,7 +1332,8 @@ def _html_profile_posts(recent_posts_cache: {}, max_recent_posts: int,
cw_lists, lists_enabled,
timezone, False,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
if post_str and item_id not in shown_items:
profile_str += post_str + separator_str
shown_items.append(item_id)
@ -1878,7 +1884,8 @@ def _html_edit_profile_filtering(base_dir: str, nickname: str, domain: str,
user_agents_blocked: str,
crawlers_allowed: str,
translate: {}, reply_interval_hours: int,
cw_lists: {}, lists_enabled: str) -> str:
cw_lists: {}, lists_enabled: str,
buy_sites: {}) -> str:
"""Filtering and blocking section of edit profile screen
"""
filter_str = ''
@ -2065,6 +2072,20 @@ def _html_edit_profile_filtering(base_dir: str, nickname: str, domain: str,
'crawlersAllowedStr', crawlers_allowed_str,
200, '', False)
buy_domains_list_str = ''
for buy_icon_text, buy_url in buy_sites.items():
if buy_icon_text != buy_url:
buy_domains_list_str += \
buy_icon_text + ' ' + buy_url.strip() + '\n'
else:
buy_domains_list_str += buy_url.strip() + '\n'
buy_domains_str = \
"Buy links are allowed from the following domains"
edit_profile_form += \
edit_text_area(translate[buy_domains_str], None,
'buySitesStr', buy_domains_list_str,
200, '', False)
cw_lists_str = ''
for name, _ in cw_lists.items():
variablename = get_cw_list_variable(name)
@ -2507,7 +2528,8 @@ def html_edit_profile(server, translate: {},
system_language: str,
min_images_for_accounts: [],
max_recent_posts: int,
reverse_sequence: []) -> str:
reverse_sequence: [],
buy_sites: {}) -> str:
"""Shows the edit profile screen
"""
path = path.replace('/inbox', '').replace('/outbox', '')
@ -2748,7 +2770,7 @@ def html_edit_profile(server, translate: {},
_html_edit_profile_filtering(base_dir, nickname, domain,
user_agents_blocked, crawlers_allowed,
translate, reply_interval_hours,
cw_lists, lists_enabled)
cw_lists, lists_enabled, buy_sites)
# git projects section
edit_profile_form += \

View File

@ -702,7 +702,8 @@ def html_history_search(translate: {}, base_dir: str,
lists_enabled: str,
timezone: str, bold_reading: bool,
dogwhistles: {}, access_keys: {},
min_images_for_accounts: []) -> str:
min_images_for_accounts: [],
buy_sites: {}) -> str:
"""Show a page containing search results for your post history
"""
if historysearch.startswith("'"):
@ -812,7 +813,8 @@ def html_history_search(translate: {}, base_dir: str,
cw_lists, lists_enabled,
timezone, False, bold_reading,
dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
if post_str:
history_search_form += separator_str + post_str
index += 1
@ -840,7 +842,8 @@ def html_hashtag_search(nickname: str, domain: str, port: int,
timezone: str, bold_reading: bool,
dogwhistles: {}, map_format: str,
access_keys: {}, box_name: str,
min_images_for_accounts: []) -> str:
min_images_for_accounts: [],
buy_sites: {}) -> str:
"""Show a page containing search results for a hashtag
or after selecting a hashtag from the swarm
"""
@ -1032,7 +1035,8 @@ def html_hashtag_search(nickname: str, domain: str, port: int,
store_to_sache, False, cw_lists,
lists_enabled, timezone, False,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
if post_str:
hashtag_search_form += \
text_mode_separator + separator_str + post_str
@ -1075,7 +1079,7 @@ def html_hashtag_search_remote(nickname: str, domain: str, port: int,
timezone: str, bold_reading: bool,
dogwhistles: {},
min_images_for_accounts: [],
debug: bool) -> str:
debug: bool, buy_sites: {}) -> str:
"""Show a page containing search results for a remote hashtag
"""
hashtag = hashtag_url.split('/')[-1]
@ -1235,7 +1239,8 @@ def html_hashtag_search_remote(nickname: str, domain: str, port: int,
store_to_sache, False, cw_lists,
lists_enabled, timezone, False,
bold_reading, dogwhistles,
minimize_all_images, None)
minimize_all_images, None,
buy_sites)
if post_str:
hashtag_search_form += \
text_mode_separator + separator_str + post_str

View File

@ -501,7 +501,8 @@ def html_timeline(default_timeline: str,
dogwhistles: {}, ua_str: str,
min_images_for_accounts: [],
reverse_sequence: bool,
last_post_id: str) -> str:
last_post_id: str,
buy_sites: {}) -> str:
"""Show the timeline as html
"""
enable_timing_log = False
@ -1037,7 +1038,7 @@ def html_timeline(default_timeline: str,
timezone, mitm,
bold_reading, dogwhistles,
minimize_all_images,
first_post_id)
first_post_id, buy_sites)
_log_timeline_timing(enable_timing_log,
timeline_start_time, box_name, '12')
@ -1306,7 +1307,8 @@ def html_shares(default_timeline: str,
timezone: str, bold_reading: bool,
dogwhistles: {}, ua_str: str,
min_images_for_accounts: [],
reverse_sequence: bool) -> str:
reverse_sequence: bool,
buy_sites: {}) -> str:
"""Show the shares timeline as html
"""
manually_approve_followers = \
@ -1339,7 +1341,7 @@ def html_shares(default_timeline: str,
cw_lists, lists_enabled, timezone,
bold_reading, dogwhistles, ua_str,
min_images_for_accounts,
reverse_sequence, None)
reverse_sequence, None, buy_sites)
def html_wanted(default_timeline: str,
@ -1371,7 +1373,8 @@ def html_wanted(default_timeline: str,
timezone: str, bold_reading: bool,
dogwhistles: {}, ua_str: str,
min_images_for_accounts: [],
reverse_sequence: bool) -> str:
reverse_sequence: bool,
buy_sites: {}) -> str:
"""Show the wanted timeline as html
"""
manually_approve_followers = \
@ -1404,7 +1407,7 @@ def html_wanted(default_timeline: str,
cw_lists, lists_enabled, timezone,
bold_reading, dogwhistles, ua_str,
min_images_for_accounts,
reverse_sequence, None)
reverse_sequence, None, buy_sites)
def html_inbox(default_timeline: str,
@ -1438,7 +1441,8 @@ def html_inbox(default_timeline: str,
dogwhistles: {}, ua_str: str,
min_images_for_accounts: [],
reverse_sequence: bool,
last_post_id: str) -> str:
last_post_id: str,
buy_sites: {}) -> str:
"""Show the inbox as html
"""
manually_approve_followers = \
@ -1471,7 +1475,8 @@ def html_inbox(default_timeline: str,
cw_lists, lists_enabled, timezone,
bold_reading, dogwhistles, ua_str,
min_images_for_accounts,
reverse_sequence, last_post_id)
reverse_sequence, last_post_id,
buy_sites)
def html_bookmarks(default_timeline: str,
@ -1504,7 +1509,8 @@ def html_bookmarks(default_timeline: str,
timezone: str, bold_reading: bool,
dogwhistles: {}, ua_str: str,
min_images_for_accounts: [],
reverse_sequence: bool) -> str:
reverse_sequence: bool,
buy_sites: {}) -> str:
"""Show the bookmarks as html
"""
manually_approve_followers = \
@ -1536,7 +1542,7 @@ def html_bookmarks(default_timeline: str,
cw_lists, lists_enabled, timezone,
bold_reading, dogwhistles, ua_str,
min_images_for_accounts,
reverse_sequence, None)
reverse_sequence, None, buy_sites)
def html_inbox_dms(default_timeline: str,
@ -1570,7 +1576,8 @@ def html_inbox_dms(default_timeline: str,
dogwhistles: {}, ua_str: str,
min_images_for_accounts: [],
reverse_sequence: bool,
last_post_id: str) -> str:
last_post_id: str,
buy_sites: {}) -> str:
"""Show the DM timeline as html
"""
artist = is_artist(base_dir, nickname)
@ -1598,7 +1605,8 @@ def html_inbox_dms(default_timeline: str,
cw_lists, lists_enabled, timezone,
bold_reading, dogwhistles, ua_str,
min_images_for_accounts,
reverse_sequence, last_post_id)
reverse_sequence, last_post_id,
buy_sites)
def html_inbox_replies(default_timeline: str,
@ -1632,7 +1640,8 @@ def html_inbox_replies(default_timeline: str,
dogwhistles: {}, ua_str: str,
min_images_for_accounts: [],
reverse_sequence: bool,
last_post_id: str) -> str:
last_post_id: str,
buy_sites: {}) -> str:
"""Show the replies timeline as html
"""
artist = is_artist(base_dir, nickname)
@ -1658,7 +1667,7 @@ def html_inbox_replies(default_timeline: str,
shared_items_federated_domains, signing_priv_key_pem,
cw_lists, lists_enabled, timezone, bold_reading,
dogwhistles, ua_str, min_images_for_accounts,
reverse_sequence, last_post_id)
reverse_sequence, last_post_id, buy_sites)
def html_inbox_media(default_timeline: str,
@ -1692,7 +1701,8 @@ def html_inbox_media(default_timeline: str,
dogwhistles: {}, ua_str: str,
min_images_for_accounts: [],
reverse_sequence: bool,
last_post_id: str) -> str:
last_post_id: str,
buy_sites: {}) -> str:
"""Show the media timeline as html
"""
artist = is_artist(base_dir, nickname)
@ -1718,7 +1728,7 @@ def html_inbox_media(default_timeline: str,
shared_items_federated_domains, signing_priv_key_pem,
cw_lists, lists_enabled, timezone, bold_reading,
dogwhistles, ua_str, min_images_for_accounts,
reverse_sequence, last_post_id)
reverse_sequence, last_post_id, buy_sites)
def html_inbox_blogs(default_timeline: str,
@ -1752,7 +1762,8 @@ def html_inbox_blogs(default_timeline: str,
dogwhistles: {}, ua_str: str,
min_images_for_accounts: [],
reverse_sequence: bool,
last_post_id: str) -> str:
last_post_id: str,
buy_sites: {}) -> str:
"""Show the blogs timeline as html
"""
artist = is_artist(base_dir, nickname)
@ -1778,7 +1789,7 @@ def html_inbox_blogs(default_timeline: str,
shared_items_federated_domains, signing_priv_key_pem,
cw_lists, lists_enabled, timezone, bold_reading,
dogwhistles, ua_str, min_images_for_accounts,
reverse_sequence, last_post_id)
reverse_sequence, last_post_id, buy_sites)
def html_inbox_features(default_timeline: str,
@ -1812,7 +1823,8 @@ def html_inbox_features(default_timeline: str,
timezone: str, bold_reading: bool,
dogwhistles: {}, ua_str: str,
min_images_for_accounts: [],
reverse_sequence: bool) -> str:
reverse_sequence: bool,
buy_sites: {}) -> str:
"""Show the features timeline as html
"""
return html_timeline(default_timeline,
@ -1837,7 +1849,7 @@ def html_inbox_features(default_timeline: str,
shared_items_federated_domains, signing_priv_key_pem,
cw_lists, lists_enabled, timezone, bold_reading,
dogwhistles, ua_str, min_images_for_accounts,
reverse_sequence, None)
reverse_sequence, None, buy_sites)
def html_inbox_news(default_timeline: str,
@ -1870,7 +1882,8 @@ def html_inbox_news(default_timeline: str,
timezone: str, bold_reading: bool,
dogwhistles: {}, ua_str: str,
min_images_for_accounts: [],
reverse_sequence: bool) -> str:
reverse_sequence: bool,
buy_sites: {}) -> str:
"""Show the news timeline as html
"""
return html_timeline(default_timeline,
@ -1895,7 +1908,7 @@ def html_inbox_news(default_timeline: str,
shared_items_federated_domains, signing_priv_key_pem,
cw_lists, lists_enabled, timezone, bold_reading,
dogwhistles, ua_str, min_images_for_accounts,
reverse_sequence, None)
reverse_sequence, None, buy_sites)
def html_outbox(default_timeline: str,
@ -1928,7 +1941,8 @@ def html_outbox(default_timeline: str,
timezone: str, bold_reading: bool,
dogwhistles: {}, ua_str: str,
min_images_for_accounts: [],
reverse_sequence: bool) -> str:
reverse_sequence: bool,
buy_sites: {}) -> str:
"""Show the Outbox as html
"""
manually_approve_followers = \
@ -1956,4 +1970,4 @@ def html_outbox(default_timeline: str,
shared_items_federated_domains, signing_priv_key_pem,
cw_lists, lists_enabled, timezone, bold_reading,
dogwhistles, ua_str, min_images_for_accounts,
reverse_sequence, None)
reverse_sequence, None, buy_sites)

View File

@ -2080,3 +2080,77 @@ def html_following_dropdown(base_dir: str, nickname: str,
following_address + '</option>\n'
list_str += '</select>\n'
return list_str
def get_buy_links(post_json_object: str, translate: {}, buy_sites: {}) -> {}:
"""Returns any links to buy something from an external site
"""
if not post_json_object['object'].get('attachment'):
return {}
if not isinstance(post_json_object['object']['attachment'], list):
return {}
links = {}
buy_strings = []
for buy_str in ('Buy', 'Purchase', 'Subscribe'):
if translate.get(buy_str):
buy_str = translate[buy_str]
buy_strings += buy_str.lower()
for item in post_json_object['object']['attachment']:
if not isinstance(item, dict):
continue
if not item.get('name'):
continue
if not isinstance(item['name'], str):
continue
if not item.get('type'):
continue
if not item.get('href'):
continue
if not isinstance(item['type'], str):
continue
if not isinstance(item['href'], str):
continue
if item['type'] != 'Link':
continue
if not item.get('mediaType'):
continue
if not isinstance(item['mediaType'], str):
continue
if 'html' not in item['mediaType']:
continue
item_name = item['name']
# The name should not be excessively long
if len(item_name) > 32:
continue
# there should be no html in the name
if remove_html(item_name) != item_name:
continue
# there should be no html in the link
if '<' in item['href'] or \
'://' not in item['href'] or \
' ' in item['href']:
continue
if buy_sites:
# limited to an allowlist of buying sites
for site, buy_domain in buy_sites.items():
if buy_domain in item['href']:
links[site.title()] = item['href']
continue
else:
# The name only needs to indicate that this is a buy link
for buy_str in buy_strings:
if buy_str in item_name.lower():
links[item_name] = item['href']
continue
return links
def load_buy_sites(base_dir: str) -> {}:
"""Loads domains from which buying is permitted
"""
buy_sites_filename = base_dir + '/accounts/buy_sites.json'
if os.path.isfile(buy_sites_filename):
buy_sites_json = load_json(buy_sites_filename)
if buy_sites_json:
return buy_sites_json
return {}