__filename__ = "webapp_moderation.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.5.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@libreserver.org"
__status__ = "Production"
__module_group__ = "Moderation"
import os
from utils import get_url_from_post
from utils import remove_html
from utils import is_artist
from utils import is_account_dir
from utils import get_full_domain
from utils import is_editor
from utils import load_json
from utils import get_nickname_from_actor
from utils import get_domain_from_actor
from utils import get_config_param
from utils import local_actor_url
from utils import remove_eol
from posts import download_follow_collection
from posts import get_public_post_info
from posts import is_moderator
from webapp_timeline import html_timeline
# from webapp_utils import get_person_avatar_url
from webapp_utils import get_banner_file
from webapp_utils import get_content_warning_button
from webapp_utils import html_header_with_external_style
from webapp_utils import html_footer
from blocking import get_global_block_reason
from blocking import is_blocked_domain
from blocking import is_blocked
from session import create_session
def html_moderation(default_timeline: str,
recent_posts_cache: {}, max_recent_posts: int,
translate: {}, page_number: int, items_per_page: int,
session, base_dir: str, wf_request: {}, person_cache: {},
nickname: str, domain: str, port: int, inbox_json: {},
allow_deletion: bool,
http_prefix: str, project_version: str,
yt_replace_domain: str,
twitter_replacement_domain: str,
show_published_date_only: bool,
newswire: {}, positive_voting: bool,
show_publish_as_icon: bool,
full_width_tl_button_header: bool,
icons_as_buttons: bool,
rss_icon_at_top: bool,
publish_button_at_top: bool,
authorized: bool, moderation_action_str: str,
theme: str, peertube_instances: [],
allow_local_network_access: bool,
text_mode_banner: str,
access_keys: {}, system_language: str,
max_like_count: int,
shared_items_federated_domains: [],
signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str,
timezone: str, bold_reading: bool,
dogwhistles: {}, ua_str: str,
min_images_for_accounts: [],
reverse_sequence: bool,
buy_sites: {},
auto_cw_cache: {}) -> str:
"""Show the moderation feed as html
This is what you see when selecting the "mod" timeline
"""
artist = is_artist(base_dir, nickname)
return html_timeline(default_timeline,
recent_posts_cache, max_recent_posts,
translate, page_number,
items_per_page, session, base_dir,
wf_request, person_cache,
nickname, domain, port, inbox_json, 'moderation',
allow_deletion, http_prefix,
project_version, True, False,
yt_replace_domain,
twitter_replacement_domain,
show_published_date_only,
newswire, False, False, artist, positive_voting,
show_publish_as_icon,
full_width_tl_button_header,
icons_as_buttons, rss_icon_at_top,
publish_button_at_top,
authorized, moderation_action_str, theme,
peertube_instances, allow_local_network_access,
text_mode_banner, access_keys, system_language,
max_like_count, shared_items_federated_domains,
signing_priv_key_pem, cw_lists, lists_enabled,
timezone, bold_reading, dogwhistles, ua_str,
min_images_for_accounts, reverse_sequence, None,
buy_sites, auto_cw_cache)
def html_account_info(translate: {},
base_dir: str, http_prefix: str,
nickname: str, domain: str,
search_handle: str, debug: bool,
system_language: str, signing_priv_key_pem: str,
back_url: str,
block_federated: []) -> str:
"""Shows which domains a search handle interacts with.
This screen is shown if a moderator enters a handle and selects info
on the moderation screen
"""
signing_priv_key_pem = None
msg_str1 = 'This account interacts with the following instances'
info_form = ''
css_filename = base_dir + '/epicyon-profile.css'
if os.path.isfile(base_dir + '/epicyon.css'):
css_filename = base_dir + '/epicyon.css'
instance_title = \
get_config_param(base_dir, 'instanceTitle')
info_form = \
html_header_with_external_style(css_filename, instance_title, None)
search_nickname = get_nickname_from_actor(search_handle)
if not search_nickname:
return ''
search_domain, search_port = get_domain_from_actor(search_handle)
if not search_domain:
return ''
search_handle = search_nickname + '@' + search_domain
search_actor = \
local_actor_url(http_prefix, search_nickname, search_domain)
if not back_url:
back_url = '/users/' + nickname + '/moderation'
info_form += \
'
\n'
info_form += translate[msg_str1] + ' \n'
proxy_type = 'tor'
if not os.path.isfile('/usr/bin/tor'):
proxy_type = None
if domain.endswith('.i2p'):
proxy_type = None
session = create_session(proxy_type)
word_frequency = {}
origin_domain = None
domain_dict = get_public_post_info(session, base_dir,
search_nickname, search_domain,
origin_domain,
proxy_type, search_port,
http_prefix, debug,
__version__, word_frequency,
system_language,
signing_priv_key_pem)
# get a list of any blocked followers
followers_list = \
download_follow_collection(signing_priv_key_pem,
'followers', session,
http_prefix, search_actor, 1, 5, debug)
blocked_followers = []
for follower_actor in followers_list:
follower_nickname = get_nickname_from_actor(follower_actor)
if not follower_nickname:
return ''
follower_domain, follower_port = get_domain_from_actor(follower_actor)
if not follower_domain:
return ''
follower_domain_full = get_full_domain(follower_domain, follower_port)
if is_blocked(base_dir, nickname, domain,
follower_nickname, follower_domain_full,
None, block_federated):
blocked_followers.append(follower_actor)
# get a list of any blocked following
following_list = \
download_follow_collection(signing_priv_key_pem,
'following', session,
http_prefix, search_actor, 1, 5, debug)
blocked_following = []
for following_actor in following_list:
following_nickname = get_nickname_from_actor(following_actor)
if not following_nickname:
return ''
following_domain, following_port = \
get_domain_from_actor(following_actor)
if not following_domain:
return ''
following_domain_full = \
get_full_domain(following_domain, following_port)
if is_blocked(base_dir, nickname, domain,
following_nickname, following_domain_full,
None, block_federated):
blocked_following.append(following_actor)
info_form += '\n'
users_path = '/users/' + nickname + '/accountinfo'
ctr = 1
for post_domain, blocked_post_urls in domain_dict.items():
info_form += '
' + \
post_domain + ' '
if is_blocked_domain(base_dir, post_domain, None, block_federated):
blocked_posts_links = ''
url_ctr = 0
for url in blocked_post_urls:
if url_ctr > 0:
blocked_posts_links += '
'
blocked_posts_links += \
'
' + \
url + ' '
url_ctr += 1
blocked_posts_html = ''
if blocked_posts_links:
block_no_str = 'blockNumber' + str(ctr)
blocked_posts_html = \
get_content_warning_button(block_no_str,
translate,
blocked_posts_links)
ctr += 1
info_form += \
'
'
info_form += '' + \
translate['Unblock'] + ' ' + \
blocked_posts_html + '\n'
else:
info_form += \
'
'
if post_domain != domain:
info_form += '' + \
translate['Block'] + ' '
info_form += ' \n'
info_form += '
\n'
info_form += '
\n'
if blocked_following:
blocked_following.sort()
info_form += '\n'
info_form += '
' + translate['Blocked following'] + ' \n'
info_form += \
'
' + \
translate['Receives posts from the following accounts'] + \
':
\n'
for actor in blocked_following:
following_nickname = get_nickname_from_actor(actor)
if not following_nickname:
return ''
following_domain, following_port = get_domain_from_actor(actor)
if not following_domain:
return ''
following_domain_full = \
get_full_domain(following_domain, following_port)
info_form += '
' + \
following_nickname + '@' + following_domain_full + \
' \n'
info_form += '
\n'
if blocked_followers:
blocked_followers.sort()
info_form += '\n'
info_form += '
' + translate['Blocked followers'] + ' \n'
info_form += \
'
' + \
translate['Sends out posts to the following accounts'] + \
':
\n'
for actor in blocked_followers:
follower_nickname = get_nickname_from_actor(actor)
if not follower_nickname:
return ''
follower_domain, follower_port = get_domain_from_actor(actor)
if not follower_domain:
return ''
follower_domain_full = \
get_full_domain(follower_domain, follower_port)
info_form += '
' + \
follower_nickname + '@' + \
follower_domain_full + ' \n'
info_form += '
\n'
if word_frequency:
max_count = 1
for word, count in word_frequency.items():
if count > max_count:
max_count = count
minimum_word_count = int(max_count / 2)
if minimum_word_count >= 3:
info_form += '\n'
info_form += '
' + translate['Word frequencies'] + ' \n'
word_swarm = ''
ctr = 0
for word, count in word_frequency.items():
if count >= minimum_word_count:
if ctr > 0:
word_swarm += ' '
if count < max_count - int(max_count / 4):
word_swarm += word
else:
if count != max_count:
word_swarm += '' + word + ' '
else:
word_swarm += '' + word + ' '
ctr += 1
info_form += word_swarm
info_form += '\n'
info_form += html_footer()
return info_form
def html_moderation_info(translate: {}, base_dir: str,
nickname: str, domain: str, theme: str,
access_keys: {}) -> str:
msg_str1 = \
'These are globally blocked for all accounts on this instance'
msg_str2 = \
'Any blocks or suspensions made by moderators will be shown here.'
info_form = ''
css_filename = base_dir + '/epicyon-profile.css'
if os.path.isfile(base_dir + '/epicyon.css'):
css_filename = base_dir + '/epicyon.css'
instance_title = \
get_config_param(base_dir, 'instanceTitle')
info_form = html_header_with_external_style(css_filename,
instance_title, None)
# show banner
banner_file, _ = \
get_banner_file(base_dir, nickname, domain, theme)
moderation_link = '/users/' + nickname + '/moderation'
info_form += \
'\n \n'
info_form += \
' '
info_shown = False
accounts = []
for _, dirs, _ in os.walk(base_dir + '/accounts'):
for acct in dirs:
if not is_account_dir(acct):
continue
accounts.append(acct)
break
accounts.sort()
cols = 5
if len(accounts) > 10:
info_form += '' + translate['Show Accounts']
info_form += ' \n'
info_form += '\n'
info_form += '
\n'
info_form += '
\n'
if len(accounts) > 10:
info_form += ' \n'
suspended_filename = base_dir + '/accounts/suspended.txt'
if os.path.isfile(suspended_filename):
with open(suspended_filename, 'r', encoding='utf-8') as fp_sus:
suspended_str = fp_sus.read()
info_form += '\n'
info_form += ' ' + \
translate['Suspended accounts'] + ' '
info_form += ' ' + \
translate['These are currently suspended']
info_form += \
' \n'
info_form += '
\n'
info_shown = True
blocking_filename = base_dir + '/accounts/blocking.txt'
if os.path.isfile(blocking_filename):
blocking_reasons_filename = \
base_dir + '/accounts/blocking_reasons.txt'
blocking_reasons_exist = False
if os.path.isfile(blocking_reasons_filename):
blocking_reasons_exist = True
with open(blocking_filename, 'r', encoding='utf-8') as fp_block:
blocked_lines = fp_block.readlines()
blocked_str = ''
if blocked_lines:
blocked_lines.sort()
for line in blocked_lines:
if not line:
continue
line = remove_eol(line).strip()
if blocking_reasons_exist:
reason = \
get_global_block_reason(line,
blocking_reasons_filename)
if reason:
blocked_str += \
line + ' - ' + reason + '\n'
continue
blocked_str += line + '\n'
info_form += '\n'
info_form += \
' ' + \
translate['Blocked accounts and hashtags'] + ' '
info_form += \
' ' + \
translate[msg_str1]
info_form += \
' \n'
info_form += '
\n'
info_shown = True
filters_filename = base_dir + '/accounts/filters.txt'
if os.path.isfile(filters_filename):
with open(filters_filename, 'r', encoding='utf-8') as fp_filt:
filtered_str = fp_filt.read()
info_form += '\n'
info_form += \
' ' + \
translate['Filtered words'] + ' '
info_form += \
' \n'
info_form += '
\n'
info_shown = True
if not info_shown:
info_form += \
'' + \
translate[msg_str2] + \
'
\n'
info_form += html_footer()
return info_form