subscribe to public ban lists!

This commit is contained in:
William Kray
2024-10-07 13:13:07 -07:00
parent ce830e29e8
commit cdd957cca5
3 changed files with 107 additions and 33 deletions
+7
View File
@@ -82,3 +82,10 @@ censor_files: true
censor_wordlist: censor_wordlist:
- 'effword' - 'effword'
- 'essword' - 'essword'
# list of banlists that should be subscribed to, such as #community-moderation-effort-bl:neko.dev
# when users join any room managed by this bot, they are compared against these existing banlists
# if found, they will immediately be banned.
# your bot MUST be in the banlist room already!
banlists:
- '#community-moderation-effort-bl:neko.dev'
+99 -32
View File
@@ -4,6 +4,7 @@ from typing import Awaitable, Type, Optional, Tuple
import json import json
import time import time
import re import re
import fnmatch
from mautrix.client import Client, InternalEventType, MembershipEventDispatcher, SyncStream from mautrix.client import Client, InternalEventType, MembershipEventDispatcher, SyncStream
from mautrix.types import (Event, StateEvent, EventID, UserID, FileInfo, EventType, from mautrix.types import (Event, StateEvent, EventID, UserID, FileInfo, EventType,
@@ -38,6 +39,7 @@ class Config(BaseProxyConfig):
helper.copy("censor") helper.copy("censor")
helper.copy("censor_wordlist") helper.copy("censor_wordlist")
helper.copy("censor_files") helper.copy("censor_files")
helper.copy("banlists")
class CommunityBot(Plugin): class CommunityBot(Plugin):
@@ -126,7 +128,7 @@ class CommunityBot(Plugin):
for w in self.config['censor_wordlist']: for w in self.config['censor_wordlist']:
try: try:
if bool(re.search(w, msg.content.body, re.IGNORECASE)): if bool(re.search(w, msg.content.body, re.IGNORECASE)):
self.log.debug(f"DEBUG message flagged for censorship") #self.log.debug(f"DEBUG message flagged for censorship")
return True return True
else: else:
pass pass
@@ -135,21 +137,105 @@ class CommunityBot(Plugin):
def censor_room(self, msg): def censor_room(self, msg):
if isinstance(self.config['censor'], bool): if isinstance(self.config['censor'], bool):
self.log.debug(f"DEBUG message will be redacted because censoring is enabled") #self.log.debug(f"DEBUG message will be redacted because censoring is enabled")
return self.config['censor'] return self.config['censor']
elif isinstance(self.config['censor'], list): elif isinstance(self.config['censor'], list):
if msg.room_id in self.config['censor']: if msg.room_id in self.config['censor']:
self.log.debug(f"DEBUG message will be redacted because censoring is enabled for THIS room") #self.log.debug(f"DEBUG message will be redacted because censoring is enabled for THIS room")
return True return True
else: else:
return False return False
async def check_if_banned(self, userid):
# fetch banlist data
is_banned = False
myrooms = await self.client.get_joined_rooms()
for l in self.config['banlists']:
#self.log.debug(f"DEBUG getting banlist {l}")
if l.startswith('#'):
try:
l_id = await self.client.resolve_room_alias(l)
list_id = l_id["room_id"]
#self.log.debug(f"DEBUG banlist id resolves to: {list_id}")
except:
evt.reply("i don't recognize that list, sorry")
return
else:
list_id = l
if list_id not in myrooms:
self.log.error(f"Bot must be in {l} before attempting to use it as a banlist.")
pass
#self.log.debug(f"DEBUG looking up state in {list_id}")
list_state = await self.client.get_state(list_id)
#self.log.debug(f"DEBUG state found: {list_state}")
try:
user_policies = list(filter(lambda p : p.type.t=='m.policy.rule.user', list_state))
#self.log.debug(f"DEBUG user policies found: {user_policies}")
except Exception as e:
self.log.error(e)
for rule in user_policies:
#self.log.debug(f"Checking match of user {userid} in banlist {l} for {rule['content']}")
try:
if bool(fnmatch.fnmatch(userid, rule["content"]["entity"])) and \
bool(re.search('ban$', rule["content"]["recommendation"])):
#self.log.debug(f"DEBUG user {userid} matches ban rule {rule['content']['entity']}!")
return True
else:
pass
except Exception as e:
self.log.error(f"Found something funny in the banlist {l} for {rule['content']}: {e}")
pass
# if we haven't exited by now, we must not be banned!
return is_banned
async def ban_this_user(self, user):
#self.log.debug(f"DEBUG getting list of rooms")
roomlist = await self.get_space_roomlist()
# don't forget to kick from the space itself
roomlist.append(self.config["parent_room"])
#self.log.debug(f"DEBUG list of rooms acquired")
ban_event_map = {'ban_list':{}, 'error_list':{}}
ban_event_map['ban_list'][user] = []
#self.log.debug(f"DEBUG banning {user} from rooms...")
for room in roomlist:
try:
roomname = None
roomnamestate = await self.client.get_state_event(room, 'm.room.name')
roomname = roomnamestate['name']
# ban user even if they're not in the room!
#await self.client.get_state_event(room, EventType.ROOM_MEMBER, user)
await self.client.ban_user(room, user, reason='banned')
if roomname:
ban_event_map['ban_list'][user].append(roomname)
else:
ban_event_map['ban_list'][user].append(room)
time.sleep(0.5)
except MNotFound:
pass
except Exception as e:
self.log.warning(e)
ban_event_map['error_list'][user] = []
ban_event_map['error_list'][user].append(roomname or room)
return ban_event_map
@event.on(InternalEventType.JOIN) @event.on(InternalEventType.JOIN)
async def newjoin(self, evt:StateEvent) -> None: async def newjoin(self, evt:StateEvent) -> None:
if evt.source & SyncStream.STATE: if evt.source & SyncStream.STATE:
return return
else: else:
on_banlist = await self.check_if_banned(evt.sender)
if on_banlist:
#self.log.debug(f"DEBUG user is on banlist!")
# ban this account in managed rooms, don't bother with anything else
await self.ban_this_user(evt.sender)
return
# passive sync of tracking db # passive sync of tracking db
if evt.room_id == self.config['parent_room']: if evt.room_id == self.config['parent_room']:
await self.do_sync() await self.do_sync()
@@ -213,6 +299,12 @@ class CommunityBot(Plugin):
async def community(self) -> None: async def community(self) -> None:
pass pass
@community.subcommand("bancheck", help="check subscribed banlists for a user's mxid")
@command.argument("mxid", "full matrix ID", required=True)
async def check_banlists(self, evt: MessageEvent, mxid: UserID) -> None:
ban_status = await self.check_if_banned(mxid)
await evt.reply(f"user on banlist: {ban_status}")
@community.subcommand("sync", help="update the activity tracker with the current space members \ @community.subcommand("sync", help="update the activity tracker with the current space members \
in case they are missing") in case they are missing")
@@ -388,37 +480,12 @@ class CommunityBot(Plugin):
if evt.sender in self.config["admins"]: if evt.sender in self.config["admins"]:
user = mxid user = mxid
msg = await evt.respond("starting the ban...") msg = await evt.respond("starting the ban...")
roomlist = await self.get_space_roomlist() results_map = await self.ban_this_user(user)
# don't forget to kick from the space itself
roomlist.append(self.config["parent_room"])
ban_list = {}
error_list = {}
ban_list[user] = []
for room in roomlist:
try:
roomname = None
roomnamestate = await self.client.get_state_event(room, 'm.room.name')
roomname = roomnamestate['name']
# ban user even if they're not in the room!
#await self.client.get_state_event(room, EventType.ROOM_MEMBER, user)
await self.client.ban_user(room, user, reason='banned')
if roomname:
ban_list[user].append(roomname)
else:
ban_list[user].append(room)
time.sleep(0.5)
except MNotFound:
pass
except Exception as e:
self.log.warning(e)
error_list[user] = []
error_list[user].append(roomname or room)
results = "the following users were kicked and banned:<p><code>{ban_list}</code></p>the following errors were \ results = "the following users were kicked and banned:<p><code>{ban_list}</code></p>the following errors were \
recorded:<p><code>{error_list}</code></p>".format(ban_list=ban_list, error_list=error_list) recorded:<p><code>{error_list}</code></p>".format(ban_list=results_map['ban_list'],
error_list=results_map['error_list'])
await evt.respond(results, allow_html=True, edits=msg) await evt.respond(results, allow_html=True, edits=msg)
# sync our database after we've made changes to room memberships # sync our database after we've made changes to room memberships
@@ -641,7 +708,7 @@ class CommunityBot(Plugin):
new_pl = current_pl new_pl = current_pl
new_pl['users'] = updated_user_map new_pl['users'] = updated_user_map
try: try:
self.log.debug(f"DEBUG sending finalized PL map to room {room}: {updated_user_map}") #self.log.debug(f"DEBUG sending finalized PL map to room {room}: {updated_user_map}")
await self.client.send_state_event(room, 'm.room.power_levels', new_pl) await self.client.send_state_event(room, 'm.room.power_levels', new_pl)
success_list.append(roomname or room) success_list.append(roomname or room)
except Exception as e: except Exception as e:
+1 -1
View File
@@ -1,6 +1,6 @@
maubot: 0.1.0 maubot: 0.1.0
id: org.jobmachine.communitybot id: org.jobmachine.communitybot
version: 0.1.8 version: 0.1.9
license: MIT license: MIT
modules: modules:
- community - community