add instance ban popover for admins

This commit is contained in:
Izalia Mae 2022-11-23 13:36:45 -05:00
parent eabc8084de
commit 33cdd8f7fc
6 changed files with 580 additions and 25 deletions

View file

@ -1,12 +1,16 @@
import asyncio
import json
from izzylib.misc import random_str
from mastodon.Mastodon import MastodonAPIError
from pprint import pprint
from threading import Thread
from .. import cache, var
from ..base import ComponentBase
from ..database import default_permissions
from ..exceptions import AccountNotFoundError, NoAccountsError
from ..functions import connect, get_buffer_text
from ..functions import connect, get_buffer_text, run_in_gui_thread
from ..objects import SavedLoginRow
@ -32,11 +36,13 @@ class StatusBar(ComponentBase):
self.bookmark_row = None
self.theme_enabled = False
self.toot_acct = None
self.toot_account = None
self.toot_post = None
self.toot_max_len = 500
self.unsaved_logins = []
self.fediban_bans = dict()
self.fediban_current = None
self.setup()
@ -85,6 +91,57 @@ class StatusBar(ComponentBase):
self.bookmark_fields[k].set_text(value)
def fediban_get(self, domain):
self.fediban_refresh(check_only=True)
try:
return self.fediban_bans[domain]
except KeyError:
pass
for row in self.fediban_bans.values():
if row.domain in domain or domain in row.domain:
return row
def fediban_get_fields(self):
return DotDict(
domain = self['fediban-domain'].get_text(),
severity = self['fediban-severity'].get_active_id(),
private_comment = get_buffer_text(self['fediban-private']),
public_comment = get_buffer_text(self['fediban-public']),
nomedia = self['fediban-media'].get_active(),
noreports = self['fediban-reports'].get_active(),
obfuscate = self['fediban-obfuscate'].get_active()
)
def fediban_set_fields(self, row=None):
self['fediban-domain'].set_text(row.domain if row else self.tab.url.domain)
self['fediban-public'].get_buffer().set_text(row.public_comment if row else '')
self['fediban-private'].get_buffer().set_text(row.private_comment if row else '')
self['fediban-severity'].set_active_id(row.severity if row else 'silence')
self['fediban-media'].set_active(row.reject_media if row else False)
self['fediban-reports'].set_active(row.reject_reports if row else False)
self['fediban-obfuscate'].set_active(row.obfuscate if row else False)
self['fediban-domain'].set_sensitive(False if row else True)
self['fediban-delete'].set_sensitive(True if row else False)
self['fediban-save'].set_label('Update' if row else 'Save')
def fediban_refresh(self, force=False, check_only=False):
if not self.fediban_bans or force:
self.fediban_bans = self.toot_account.api.admin_domains_all(200)
if check_only:
return
self.fediban_current = row = self.fediban_get(self.tab.url.domain)
run_in_gui_thread(self.fediban_set_fields, row)
def login_unsaved_del_row(self, row):
self.logins.unsaved.remove(row['container'])
self.unsaved_logins.remove(row)
@ -218,14 +275,12 @@ class StatusBar(ComponentBase):
tab = self.tab
if name == 'debug':
#self.window.notification('Merp!', 'INFO', timeout=0, system=True)
print(asyncio.get_running_loop())
#if self.window.themes.current:
#self.window.themes.unset()
#else:
#self.window.themes.set('test')
self.toot_account = self.app.get_default_account()
data = self.toot_account.api.admin_domains_all()
print(len(data))
# self.fediban_refresh()
# for row in self.fediban_bans:
# print('gab.com' in row.domain, row.domain)
elif name == 'bookmark':
if not tab.url:
@ -287,6 +342,24 @@ class StatusBar(ComponentBase):
self['toot-content'].grab_focus()
elif name == 'fediban':
try:
self.toot_account = self.app.get_default_account()
except NoAccountsError:
self.window.notification('No active fedi accounts', 'error')
return self['fediban-popover'].popdown()
try:
Thread(target=self.fediban_refresh).start()
except MastodonAPIError as e:
if e.args[0] == 'Mastodon API returned error' and e.args[1] == 403:
self.window.notification('Admin actions not available on this account')
return self['fediban-popover'].popdown()
raise e
def handle_bookmark_button(self, name):
with self.db.session as s:
@ -369,6 +442,43 @@ class StatusBar(ComponentBase):
cache.posts.store(self.tab.url, post)
def handle_fediban(self, action):
if not self.toot_account:
return
acct = self.toot_account
row = self.fediban_current
if action == 'hide':
self.fediban_set_fields()
elif action == 'save':
new_data = self.fediban_get_fields()
new_row = None
if row == None:
new_row = acct.api.admin_domains_block(**new_data)
self.window.notification(f'Blocked domain: {new_row.domain}')
else:
del new_data['domain']
new_row = acct.api.admin_domains_update(row.id, **new_data)
self.window.notification(f'Updated domain block: {row.domain}')
self.fediban_bans[new_row.domain] = new_row
elif action == 'delete':
acct.api.admin_domains_unblock(row.id)
del row.domain
self.window.notification(f'Unblocked domain: {row.domain}')
elif action == 'refresh':
Thread(target=self.fediban_refresh, kwargs={'force': True}).start()
if action in {'save', 'delete', 'close'}:
self['fediban-popover'].popdown()
def handle_siteoptions_switch(self, name):
active = self[f'siteoptions-{name}'].get_active()
try:
@ -484,6 +594,14 @@ class StatusBar(ComponentBase):
self.connect('toot-content', 'key-press-event', self.handle_toot_key_press)
connect(self['toot-content'].get_buffer(), 'changed', self.toot_update_count)
## FediBan
self.connect('fediban-popover', 'show', self.handle_status_button, 'fediban')
self.connect('fediban-popover', 'hide', self.handle_fediban, 'hide')
self.connect('fediban-delete', 'clicked', self.handle_fediban, 'delete')
self.connect('fediban-save', 'clicked', self.handle_fediban, 'save')
self.connect('fediban-refresh', 'clicked', self.handle_fediban, 'refresh')
self.connect('fediban-close', 'clicked', self.handle_fediban, 'close')
## Logins
self.connect('logins-close', 'clicked', self.handle_logins_button, 'close')
self.connect('logins-unsaved-clear', 'clicked', self.handle_logins_button, 'clear')

View file

@ -80,6 +80,7 @@ class WebviewHandler(ComponentBase):
def handle_context_menu(self, webview, context_menu, event, hit):
account = self.app.get_default_account()
menu = ContextMenuClass(context_menu, self.tab)
url = DotDict(
page = webview.get_uri(),
@ -103,6 +104,8 @@ class WebviewHandler(ComponentBase):
url[key] = Url(value)
with self.app.db.session as s:
permissions = s.get_permission(url.page.hostname())
if data.link:
menu.new_action('link_open', 'Open Link', self.tab.load_url, url.link)

View file

@ -3,11 +3,12 @@ import re, traceback
from datetime import datetime, timedelta
from io import BytesIO
from izzylib_sql import Row
from mastodon import Mastodon
# from mastodon import Mastodon
from PIL import Image
from urllib.parse import quote_plus
from ..functions import TimeoutCallback, get_app, run_in_gui_thread
from ..mastodon_temp import Mastodon
row_classes = {}
@ -37,7 +38,8 @@ class Account(RowBase):
@property
def api(self):
if not self._api:
self._set_api()
self._api = Mastodon(access_token=self.token, api_base_url=f'https://{self.domain}')
logging.debug(f'logged into {self.fullhandle}')
return self._api
@ -80,11 +82,6 @@ class Account(RowBase):
return f'{self.handle}@{self.domain}'
def _set_api(self):
self._api = Mastodon(access_token=self.token, api_base_url=f'https://{self.domain}')
logging.debug(f'logged into {self.fullhandle}')
def fetch_avatar(self):
byte = BytesIO()
http_client = get_app().http_client

View file

@ -94,6 +94,9 @@ def get_app():
def get_buffer_text(text_buffer):
if not isinstance(text_buffer, Gtk.TextBuffer):
text_buffer = text_buffer.get_buffer()
return text_buffer.get_text(text_buffer.get_start_iter(), text_buffer.get_end_iter(), True)

View file

@ -0,0 +1,129 @@
from mastodon import Mastodon as MastodonPy
from mastodon.Mastodon import api_version
class Mastodon(MastodonPy):
__DICT_VERSION_ADMIN_DOMAIN = '4.0.0'
def __init__(self, *args, **kwargs):
MastodonPy.__init__(self, *args, **kwargs)
self._patch()
def _patch(self):
self._Mastodon__SCOPE_SETS['admin:read'].extend([
'admin:read:domain_allows',
'admin:read:domain_blocks',
'admin:read:ip_blocks',
'admin:read:email_domain_blocks',
'admin:read:cononical_email_blocks'
])
self._Mastodon__SCOPE_SETS['admin:write'].extend([
'admin:read:domain_allows',
'admin:read:domain_blocks',
'admin:read:ip_blocks',
'admin:read:email_domain_blocks',
'admin:read:cononical_email_blocks'
])
def __api_request(self, *args, **kwargs):
data = MastodonPy.__api_request(self, *args, **kwargs)
if isinstance(data, list):
return [DotDict(row) for row in data]
elif isinstance(data, dict):
return DotDict(data)
return data
@api_version('4.0.0', '4.0.0', __DICT_VERSION_ADMIN_DOMAIN)
def admin_domains(self, limit=100, max_id=None):
'heck'
params = {'limit': limit}
if max_id != None:
params['max_id'] = max_id
return self.__api_request('GET', '/api/v1/admin/domain_blocks', params, use_json=True)
@api_version('4.0.0', '4.0.0', __DICT_VERSION_ADMIN_DOMAIN)
def admin_domains_blocked(self, id):
'heck'
return self.__api_request('GET', f'/api/v1/admin/domain_blocks/{id}')
@api_version('4.0.0', '4.0.0', __DICT_VERSION_ADMIN_DOMAIN)
def admin_domains_block(self, domain, severity='silence', nomedia=False, noreports=False, private_comment=None, public_comment=None, obfuscate=False):
'heck'
params = {
'domain': domain,
'severity': severity,
'reject_media': nomedia,
'reject_reports': noreports,
'private_comment': private_comment,
'public_comment': public_comment,
'obfuscate': obfuscate
}
return self.__api_request('POST', '/api/v1/admin/domain_blocks', params, use_json=True)
@api_version('4.0.0', '4.0.0', __DICT_VERSION_ADMIN_DOMAIN)
def admin_domains_update(self, id, severity='silence', nomedia=None, noreports=None, private_comment=None, public_comment=None, obfuscate=None):
'heck'
params = {}
if severity:
params['severity'] = severity
if nomedia != None:
params['reject_media'] = nomedia
if noreports != None:
params['reject_reports'] = noreports
if obfuscate != None:
params['obfuscate'] = obfuscate
if private_comment != None:
params['private_comment'] = private_comment
if public_comment != None:
params['public_comment'] = public_comment
return self.__api_request('PUT', f'/api/v1/admin/domain_blocks/{id}', params, use_json=True)
@api_version('4.0.0', '4.0.0', __DICT_VERSION_ADMIN_DOMAIN)
def admin_domains_unblock(self, id):
'heck'
return self.__api_request('DELETE', f'/api/v1/admin/domain_blocks/{id}')
def admin_domains_all(self, limit=100):
bans = dict()
new_bans = []
offset = None
while True:
if offset == None:
new_bans = self.admin_domains(limit)
else:
new_bans = self.admin_domains(limit, offset)
for row in new_bans:
bans[row.domain] = row
offset = new_bans[-1].id
if len(new_bans) < limit:
break
logging.verbose(f'Fetched {len(bans)} domain ban(s)')
return bans

View file

@ -411,6 +411,16 @@
<property name="pixel-size">20</property>
<property name="icon-name">mail-mark-notjunk</property>
</object>
<object class="GtkImage" id="statusbar-fediban-close-icon">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">window-close</property>
</object>
<object class="GtkImage" id="statusbar-fediban-refresh-icon">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">view-refresh</property>
</object>
<object class="GtkImage" id="statusbar-logins-close-icon">
<property name="visible">True</property>
<property name="can-focus">False</property>
@ -1576,6 +1586,24 @@
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkMenuButton" id="statusbar-fediban">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="focus-on-click">False</property>
<property name="receives-default">True</property>
<property name="relief">none</property>
<property name="popover">statusbar-fediban-popover</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkMenuButton" id="statusbar-toot">
<property name="visible">True</property>
@ -1595,7 +1623,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
<property name="position">4</property>
</packing>
</child>
<child>
@ -1615,7 +1643,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
<property name="position">5</property>
</packing>
</child>
<child>
@ -1635,7 +1663,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
<property name="position">6</property>
</packing>
</child>
<child>
@ -1655,7 +1683,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">6</property>
<property name="position">7</property>
</packing>
</child>
<child>
@ -1666,7 +1694,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">7</property>
<property name="position">8</property>
</packing>
</child>
<child>
@ -1687,7 +1715,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">8</property>
<property name="position">9</property>
</packing>
</child>
<child>
@ -1709,7 +1737,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">9</property>
<property name="position">10</property>
</packing>
</child>
<child>
@ -1731,7 +1759,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">10</property>
<property name="position">11</property>
</packing>
</child>
<style>
@ -1800,4 +1828,281 @@
</object>
</child>
</object>
<object class="GtkPopover" id="statusbar-fediban-popover">
<property name="can-focus">False</property>
<property name="relative-to">statusbar-fediban</property>
<child>
<!-- n-columns=2 n-rows=11 -->
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="has-tooltip">True</property>
<property name="margin-start">5</property>
<property name="margin-end">5</property>
<property name="margin-top">5</property>
<property name="margin-bottom">5</property>
<property name="row-spacing">5</property>
<property name="column-spacing">5</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">5</property>
<child>
<object class="GtkButton" id="statusbar-fediban-refresh">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Do a full refresh of banned accounts</property>
<property name="image">statusbar-fediban-refresh-icon</property>
<property name="relief">none</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Instance Ban</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="statusbar-fediban-close">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Close popover</property>
<property name="image">statusbar-fediban-close-icon</property>
<property name="relief">none</property>
<property name="always-show-image">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="statusbar-fediban-domain">
<property name="width-request">300</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="tooltip-text" translatable="yes">Domain to ban. Remove sub-domain to ban the domain and all sub-domains.</property>
<property name="placeholder-text" translatable="yes">Domain</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkTextView" id="statusbar-fediban-private">
<property name="height-request">75</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="tooltip-text" translatable="yes">Comment that will only be visible to other admins and mods</property>
<property name="left-margin">5</property>
<property name="right-margin">5</property>
<property name="top-margin">5</property>
<property name="bottom-margin">5</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">6</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkTextView" id="statusbar-fediban-public">
<property name="height-request">75</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="tooltip-text" translatable="yes">Comment that will be visible to everybody</property>
<property name="left-margin">5</property>
<property name="right-margin">5</property>
<property name="top-margin">5</property>
<property name="bottom-margin">5</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">4</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="statusbar-fediban-severity">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">The severity of the ban</property>
<property name="active">1</property>
<items>
<item id="suspend" translatable="yes">Suspend</item>
<item id="silence" translatable="yes">Silence</item>
<item id="noop" translatable="yes">None</item>
</items>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Obfuscate</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">9</property>
</packing>
</child>
<child>
<object class="GtkSwitch" id="statusbar-fediban-obfuscate">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="tooltip-text" translatable="yes">Replace some characters in the domain name with *'s when displayed on the about page</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">9</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Reject Reports</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">8</property>
</packing>
</child>
<child>
<object class="GtkSwitch" id="statusbar-fediban-reports">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="tooltip-text" translatable="yes">Ignore any reports coming from the domain</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">8</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Reject Media</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">7</property>
</packing>
</child>
<child>
<object class="GtkSwitch" id="statusbar-fediban-media">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="tooltip-text" translatable="yes">Don't cache any media coming from the domain</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">7</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Public Comment</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Private Comment</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">5</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">5</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkButton" id="statusbar-fediban-delete">
<property name="label" translatable="yes">Delete</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="statusbar-fediban-save">
<property name="label" translatable="yes">Save</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">10</property>
<property name="width">2</property>
</packing>
</child>
</object>
</child>
</object>
</interface>