__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 += \ '

' + \ translate['Account Information'] + ': ' + searchHandle + '


\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 += ' ' + \ blockedPostsHtml + '\n' else: infoForm += \ '' if postDomain != domain: infoForm += '' 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 += \ '

' + \ translate['Moderation Information'] + \ '


' 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' for col in range(cols): infoForm += ' \n' infoForm += ' \n' infoForm += '\n' col = 0 for acct in accounts: acctNickname = acct.split('@')[0] accountDir = os.path.join(base_dir + '/accounts', acct) actor_json = load_json(accountDir + '.json') if not actor_json: continue actor = actor_json['id'] avatarUrl = '' ext = '' if actor_json.get('icon'): if actor_json['icon'].get('url'): avatarUrl = actor_json['icon']['url'] if '.' in avatarUrl: ext = '.' + avatarUrl.split('.')[-1] acctUrl = \ '/users/' + nickname + '?options=' + actor + ';1;' + \ '/members/' + acctNickname + ext infoForm += '\n' col += 1 if col == cols: # new row of accounts infoForm += '\n\n' infoForm += '\n
\n' infoForm += '' infoForm += '
' if is_moderator(base_dir, acctNickname): infoForm += '' + acctNickname + '' else: infoForm += acctNickname if is_editor(base_dir, acctNickname): infoForm += ' ✍' infoForm += '
\n
\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