__filename__ = "webapp_moderation.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.2.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@libreserver.org"
__status__ = "Production"
__module_group__ = "Moderation"
import os
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 posts import downloadFollowCollection
from posts import getPublicPostInfo
from posts import is_moderator
from webapp_timeline import htmlTimeline
# from webapp_utils import getPersonAvatarUrl
from webapp_utils import getContentWarningButton
from webapp_utils import htmlHeaderWithExternalStyle
from webapp_utils import htmlFooter
from blocking import isBlockedDomain
from blocking import isBlocked
from session import create_session
def htmlModeration(css_cache: {}, defaultTimeline: str,
recent_posts_cache: {}, max_recent_posts: int,
translate: {}, pageNumber: int, itemsPerPage: int,
session, base_dir: str, wfRequest: {}, person_cache: {},
nickname: str, domain: str, port: int, inboxJson: {},
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, moderationActionStr: str,
theme: str, peertube_instances: [],
allow_local_network_access: bool,
text_mode_banner: str,
accessKeys: {}, system_language: str,
max_like_count: int,
shared_items_federated_domains: [],
signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str) -> str:
"""Show the moderation feed as html
This is what you see when selecting the "mod" timeline
"""
artist = is_artist(base_dir, nickname)
return htmlTimeline(css_cache, defaultTimeline,
recent_posts_cache, max_recent_posts,
translate, pageNumber,
itemsPerPage, session, base_dir,
wfRequest, person_cache,
nickname, domain, port, inboxJson, '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, moderationActionStr, theme,
peertube_instances, allow_local_network_access,
text_mode_banner, accessKeys, system_language,
max_like_count, shared_items_federated_domains,
signing_priv_key_pem, cw_lists, lists_enabled)
def htmlAccountInfo(css_cache: {}, translate: {},
base_dir: str, http_prefix: str,
nickname: str, domain: str, port: int,
searchHandle: str, debug: bool,
system_language: str, signing_priv_key_pem: str) -> 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
msgStr1 = 'This account interacts with the following instances'
infoForm = ''
cssFilename = base_dir + '/epicyon-profile.css'
if os.path.isfile(base_dir + '/epicyon.css'):
cssFilename = base_dir + '/epicyon.css'
instanceTitle = \
get_config_param(base_dir, 'instanceTitle')
infoForm = htmlHeaderWithExternalStyle(cssFilename, instanceTitle, None)
searchNickname = get_nickname_from_actor(searchHandle)
searchDomain, searchPort = get_domain_from_actor(searchHandle)
searchHandle = searchNickname + '@' + searchDomain
searchActor = \
local_actor_url(http_prefix, searchNickname, searchDomain)
infoForm += \
'
\n'
infoForm += translate[msgStr1] + ' \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)
wordFrequency = {}
originDomain = None
domainDict = getPublicPostInfo(session,
base_dir, searchNickname, searchDomain,
originDomain,
proxy_type, searchPort,
http_prefix, debug,
__version__, wordFrequency, system_language,
signing_priv_key_pem)
# get a list of any blocked followers
followersList = \
downloadFollowCollection(signing_priv_key_pem,
'followers', session,
http_prefix, searchActor, 1, 5, debug)
blockedFollowers = []
for followerActor in followersList:
followerNickname = get_nickname_from_actor(followerActor)
followerDomain, followerPort = get_domain_from_actor(followerActor)
followerDomainFull = get_full_domain(followerDomain, followerPort)
if isBlocked(base_dir, nickname, domain,
followerNickname, followerDomainFull):
blockedFollowers.append(followerActor)
# get a list of any blocked following
followingList = \
downloadFollowCollection(signing_priv_key_pem,
'following', session,
http_prefix, searchActor, 1, 5, debug)
blockedFollowing = []
for followingActor in followingList:
followingNickname = get_nickname_from_actor(followingActor)
followingDomain, followingPort = get_domain_from_actor(followingActor)
followingDomainFull = get_full_domain(followingDomain, followingPort)
if isBlocked(base_dir, nickname, domain,
followingNickname, followingDomainFull):
blockedFollowing.append(followingActor)
infoForm += '\n'
usersPath = '/users/' + nickname + '/accountinfo'
ctr = 1
for postDomain, blockedPostUrls in domainDict.items():
infoForm += '
' + \
postDomain + ' '
if isBlockedDomain(base_dir, postDomain):
blockedPostsLinks = ''
urlCtr = 0
for url in blockedPostUrls:
if urlCtr > 0:
blockedPostsLinks += '
'
blockedPostsLinks += \
'
' + \
url + ' '
urlCtr += 1
blockedPostsHtml = ''
if blockedPostsLinks:
blockNoStr = 'blockNumber' + str(ctr)
blockedPostsHtml = \
getContentWarningButton(blockNoStr,
translate, blockedPostsLinks)
ctr += 1
infoForm += \
'
'
infoForm += '' + \
translate['Unblock'] + ' ' + \
blockedPostsHtml + '\n'
else:
infoForm += \
'
'
if postDomain != domain:
infoForm += '' + \
translate['Block'] + ' '
infoForm += ' \n'
infoForm += '
\n'
infoForm += '
\n'
if blockedFollowing:
blockedFollowing.sort()
infoForm += '\n'
infoForm += '
' + translate['Blocked following'] + ' \n'
infoForm += \
'
' + \
translate['Receives posts from the following accounts'] + \
':
\n'
for actor in blockedFollowing:
followingNickname = get_nickname_from_actor(actor)
followingDomain, followingPort = get_domain_from_actor(actor)
followingDomainFull = \
get_full_domain(followingDomain, followingPort)
infoForm += '
' + \
followingNickname + '@' + followingDomainFull + \
' \n'
infoForm += '
\n'
if blockedFollowers:
blockedFollowers.sort()
infoForm += '\n'
infoForm += '
' + translate['Blocked followers'] + ' \n'
infoForm += \
'
' + \
translate['Sends out posts to the following accounts'] + \
':
\n'
for actor in blockedFollowers:
followerNickname = get_nickname_from_actor(actor)
followerDomain, followerPort = get_domain_from_actor(actor)
followerDomainFull = get_full_domain(followerDomain, followerPort)
infoForm += '
' + \
followerNickname + '@' + followerDomainFull + ' \n'
infoForm += '
\n'
if wordFrequency:
maxCount = 1
for word, count in wordFrequency.items():
if count > maxCount:
maxCount = count
minimumWordCount = int(maxCount / 2)
if minimumWordCount >= 3:
infoForm += '\n'
infoForm += '
' + translate['Word frequencies'] + ' \n'
wordSwarm = ''
ctr = 0
for word, count in wordFrequency.items():
if count >= minimumWordCount:
if ctr > 0:
wordSwarm += ' '
if count < maxCount - int(maxCount / 4):
wordSwarm += word
else:
if count != maxCount:
wordSwarm += '' + word + ' '
else:
wordSwarm += '' + word + ' '
ctr += 1
infoForm += wordSwarm
infoForm += '\n'
infoForm += htmlFooter()
return infoForm
def htmlModerationInfo(css_cache: {}, translate: {},
base_dir: str, http_prefix: str,
nickname: str) -> str:
msgStr1 = \
'These are globally blocked for all accounts on this instance'
msgStr2 = \
'Any blocks or suspensions made by moderators will be shown here.'
infoForm = ''
cssFilename = base_dir + '/epicyon-profile.css'
if os.path.isfile(base_dir + '/epicyon.css'):
cssFilename = base_dir + '/epicyon.css'
instanceTitle = \
get_config_param(base_dir, 'instanceTitle')
infoForm = htmlHeaderWithExternalStyle(cssFilename, instanceTitle, None)
infoForm += \
' '
infoShown = False
accounts = []
for subdir, dirs, files 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:
infoForm += '' + translate['Show Accounts']
infoForm += ' \n'
infoForm += '\n'
infoForm += '
\n'
infoForm += '
\n'
if len(accounts) > 10:
infoForm += ' \n'
suspendedFilename = base_dir + '/accounts/suspended.txt'
if os.path.isfile(suspendedFilename):
with open(suspendedFilename, 'r') as f:
suspendedStr = f.read()
infoForm += '\n'
infoForm += ' ' + \
translate['Suspended accounts'] + ' '
infoForm += ' ' + \
translate['These are currently suspended']
infoForm += \
' \n'
infoForm += '
\n'
infoShown = True
blockingFilename = base_dir + '/accounts/blocking.txt'
if os.path.isfile(blockingFilename):
with open(blockingFilename, 'r') as f:
blockedStr = f.read()
infoForm += '\n'
infoForm += \
' ' + \
translate['Blocked accounts and hashtags'] + ' '
infoForm += \
' ' + \
translate[msgStr1]
infoForm += \
' \n'
infoForm += '
\n'
infoShown = True
filtersFilename = base_dir + '/accounts/filters.txt'
if os.path.isfile(filtersFilename):
with open(filtersFilename, 'r') as f:
filteredStr = f.read()
infoForm += '\n'
infoForm += \
' ' + \
translate['Filtered words'] + ' '
infoForm += \
' \n'
infoForm += '
\n'
infoShown = True
if not infoShown:
infoForm += \
'' + \
translate[msgStr2] + \
'
\n'
infoForm += htmlFooter()
return infoForm