1
0
Fork 0
forked from sch/KaikOut

Add RTBL configuration file;

Add functionality to exclude specified RTBL lists.
This commit is contained in:
Schimon Jehudah, Adv. 2024-07-30 15:07:34 +03:00
parent 9e051fa0a6
commit 8df43fd327
7 changed files with 148 additions and 25 deletions

View file

@ -159,6 +159,14 @@ timer <number>
Timer value (in seconds) for countdown before committing an action. Timer value (in seconds) for countdown before committing an action.
""" """
[rtbl]
allow = """
ignore [+|-] <keyword>
Jabber IDs to ignore
comma-separated keywords
'+' appends to, '-' removes from.
"""
[statistics] [statistics]
score = """ score = """
scores <jid> scores <jid>

View file

@ -1,6 +1,7 @@
# This file lists default RTBL sources per database. # This file lists default RTBL sources per database.
# See file /usr/share/kaikout/rtbl.toml # See file /usr/share/kaikout/rtbl.toml
[[node]] [[sources]]
description = "A general block list focussing on spam and service abuse."
jabber_id = "xmppbl.org" jabber_id = "xmppbl.org"
node_name = "muc_bans_sha256" node_id = "muc_bans_sha256"

View file

@ -13,6 +13,7 @@ frequency_messages = 1 # The maximum allowed frequency (in seconds) of sent me
frequency_presence = 180 # The maximum allowed frequency (in seconds) of changed status messages. frequency_presence = 180 # The maximum allowed frequency (in seconds) of changed status messages.
inactivity_span = 30 # The maximum allowed time (in days) of inactivity. inactivity_span = 30 # The maximum allowed time (in days) of inactivity.
inactivity_warn = 300 # The time (in minutes) of inactivity to send a warning upon before action. Value can not be higher than of inactivity_span. inactivity_warn = 300 # The time (in minutes) of inactivity to send a warning upon before action. Value can not be higher than of inactivity_span.
rtbl_ignore = [] # A list of RTBL lists to exclude.
score_messages = 3 # The maximum allowed number of message faults to act upon. score_messages = 3 # The maximum allowed number of message faults to act upon.
score_presence = 10 # The maximum allowed number of presence faults to act upon. score_presence = 10 # The maximum allowed number of presence faults to act upon.
timer = 180 # Timer value (in seconds) for countdown before committing an action. timer = 180 # Timer value (in seconds) for countdown before committing an action.

View file

@ -199,6 +199,67 @@ class Log:
with open(filename, 'w') as f: f.write(content) with open(filename, 'w') as f: f.write(content)
class BlockList:
def get_filename():
"""
Get pathname of filename.
If filename does not exist, create it.
Parameters
----------
None.
Returns
-------
filename : str
Pathname.
"""
data_dir = Config.get_default_data_directory()
if not os.path.isdir(data_dir): os.mkdir(data_dir)
filename = os.path.join(data_dir, r"blocklist.toml")
if not os.path.exists(filename):
data = {'entries' : {}}
content = tomli_w.dumps(data)
with open(filename, 'w') as f: f.write(content)
return filename
def load_blocklist(self):
filename = BlockList.get_filename()
with open(filename, 'rb') as f:
self.blocklist = tomllib.load(f)
def add_entry_to_blocklist(self, jabber_id, node_id, item_id):
"""
Update blocklist file.
Parameters
----------
jabber_id : str
Jabber ID.
node_id : str
Node name.
item_id : str
Item ID.
Returns
-------
None.
"""
if jabber_id not in self.blocklist['entries']:
self.blocklist['entries'][jabber_id] = {}
if node_id not in self.blocklist['entries'][jabber_id]:
self.blocklist['entries'][jabber_id][node_id] = []
self.blocklist['entries'][jabber_id][node_id].append(item_id)
data = self.blocklist
content = tomli_w.dumps(data)
filename = BlockList.get_filename()
with open(filename, 'w') as f: f.write(content)
class Url: class Url:

View file

@ -260,7 +260,7 @@ class XmppChat:
else: else:
response = str(self.settings[room]['action']) response = str(self.settings[room]['action'])
case _ if command_lowercase.startswith('allow +'): case _ if command_lowercase.startswith('allow +'):
value = command[7:] value = command[7:].strip()
if value: if value:
response = XmppCommands.set_filter( response = XmppCommands.set_filter(
self, room, db_file, value, 'allow', True) self, room, db_file, value, 'allow', True)
@ -268,7 +268,7 @@ class XmppChat:
response = ('No action has been taken. ' response = ('No action has been taken. '
'Missing keywords.') 'Missing keywords.')
case _ if command_lowercase.startswith('allow -'): case _ if command_lowercase.startswith('allow -'):
value = command[7:] value = command[7:].strip()
if value: if value:
response = XmppCommands.set_filter( response = XmppCommands.set_filter(
self, room, db_file, value, 'allow', False) self, room, db_file, value, 'allow', False)
@ -386,6 +386,22 @@ class XmppChat:
await XmppCommands.muc_leave(self, room) await XmppCommands.muc_leave(self, room)
else: else:
response = 'This command is valid in groupchat only.' response = 'This command is valid in groupchat only.'
case _ if command_lowercase.startswith('ignore +'):
value = command[8:].strip()
if value:
response = XmppCommands.set_filter(
self, room, db_file, value, 'rtbl_ignore', True)
else:
response = ('No action has been taken. '
'Missing Jabber IDs.')
case _ if command_lowercase.startswith('ignore -'):
value = command[8:].strip()
if value:
response = XmppCommands.set_filter(
self, room, db_file, value, 'rtbl_ignore', False)
else:
response = ('No action has been taken. '
'Missing Jabber IDs.')
case 'inactivity off': case 'inactivity off':
XmppCommands.update_setting_value( XmppCommands.update_setting_value(
self, room, db_file, 'check_inactivity', 0) self, room, db_file, 'check_inactivity', 0)
@ -537,4 +553,4 @@ class XmppChat:
if room in self.settings and self.settings[room]['finished']: if room in self.settings and self.settings[room]['finished']:
response_finished = ('Finished. Total time: {}s' response_finished = ('Finished. Total time: {}s'
.format(command_time_total)) .format(command_time_total))
XmppMessage.send_reply(self, message, response_finished) XmppMessage.send_reply(self, message, response_finished)

View file

@ -3,11 +3,10 @@
import asyncio import asyncio
from datetime import datetime from datetime import datetime
import slixmpp
from kaikout.about import Documentation from kaikout.about import Documentation
from kaikout.database import Toml from kaikout.database import Toml
from kaikout.log import Logger from kaikout.log import Logger
from kaikout.utilities import Config, Log from kaikout.utilities import Config, Log, BlockList
from kaikout.xmpp.bookmark import XmppBookmark from kaikout.xmpp.bookmark import XmppBookmark
from kaikout.xmpp.chat import XmppChat from kaikout.xmpp.chat import XmppChat
from kaikout.xmpp.commands import XmppCommands from kaikout.xmpp.commands import XmppCommands
@ -17,6 +16,7 @@ from kaikout.xmpp.moderation import XmppModeration
from kaikout.xmpp.muc import XmppMuc from kaikout.xmpp.muc import XmppMuc
from kaikout.xmpp.pubsub import XmppPubsub from kaikout.xmpp.pubsub import XmppPubsub
from kaikout.xmpp.status import XmppStatus from kaikout.xmpp.status import XmppStatus
import slixmpp
import time import time
# time_now = datetime.now() # time_now = datetime.now()
@ -53,10 +53,13 @@ class XmppClient(slixmpp.ClientXMPP):
self.reconnect_timeout = Config.get_values('accounts.toml', 'xmpp')['settings']['reconnect_timeout'] self.reconnect_timeout = Config.get_values('accounts.toml', 'xmpp')['settings']['reconnect_timeout']
# A handler for operators. # A handler for operators.
self.operators = Config.get_values('accounts.toml', 'xmpp')['operators'] self.operators = Config.get_values('accounts.toml', 'xmpp')['operators']
# A handler for settings. # A handler for blocklist.
self.settings = {} #self.blocklist = {}
BlockList.load_blocklist(self)
# A handler for sessions. # A handler for sessions.
self.sessions = {} self.sessions = {}
# A handler for settings.
self.settings = {}
# A handler for tasks. # A handler for tasks.
self.tasks = {} self.tasks = {}
# Register plugins. # Register plugins.
@ -267,14 +270,15 @@ class XmppClient(slixmpp.ClientXMPP):
jid = presence['muc']['jid'] jid = presence['muc']['jid']
from hashlib import sha256 from hashlib import sha256
jid_to_sha256 = sha256(jid.bare.encode('utf-8')).hexdigest() jid_to_sha256 = sha256(jid.bare.encode('utf-8')).hexdigest()
rtbl_jid_full = 'xmppbl.org' for jid in self.blocklist['entries']:
rtbl_node_id = 'muc_bans_sha256' if jid not in self.settings[room]['rtbl_ignore']:
rtbl_list = await XmppPubsub.get_items(self, rtbl_jid_full, rtbl_node_id) for node in self.blocklist['entries'][jid]:
for item in rtbl_list['pubsub']['items']: for item_id in self.blocklist['entries'][jid][node]:
if jid_to_sha256 == item['id']: if jid_to_sha256 == item_id:
reason = 'Jabber ID has been marked by RTBL.' reason = 'Jabber ID has been marked by RTBL: Publisher: {}; Node: {}.'.format(
await XmppCommands.devoice(self, room, alias, reason) jid, node)
break await XmppCommands.devoice(self, room, alias, reason)
break
# message_body = 'Greetings {} and welcome to groupchat {}'.format(alias, room) # message_body = 'Greetings {} and welcome to groupchat {}'.format(alias, room)
# XmppMessage.send(self, jid.bare, message_body, 'chat') # XmppMessage.send(self, jid.bare, message_body, 'chat')
# Send MUC-PM in case there is no indication for reception of 1:1 # Send MUC-PM in case there is no indication for reception of 1:1
@ -477,14 +481,32 @@ class XmppClient(slixmpp.ClientXMPP):
""" """
# self.command_list() # self.command_list()
# await self.get_roster() # await self.get_roster()
subscriptions_of_node = await self['xep_0060'].get_node_subscriptions("xmppbl.org", "muc_bans_sha256") rtbl_sources = Config.get_values('rtbl.toml')['sources']
print() for source in rtbl_sources:
print('=== subscriptions_of_node ===') jabber_id = source['jabber_id']
print() node_id = source['node_id']
print(subscriptions_of_node) subscribe = await XmppPubsub.subscribe(self, jabber_id, node_id)
print() if subscribe['pubsub']['subscription']['subscription'] == 'subscribed':
print('=== subscriptions_of_node ===') rtbl_list = await XmppPubsub.get_items(self, jabber_id, node_id)
print() rtbl_items = rtbl_list['pubsub']['items']
for item in rtbl_items:
exist = False
item_id = item['id']
for jid in self.blocklist['entries']:
for node in jid:
for item in node:
if item_id == item:
exist = True
break
if not exist:
# TODO Extract items item_payload.find(namespace + 'title')
# NOTE (Pdb)
# for i in item['payload'].iter(): i.attrib
# {'reason': 'urn:xmpp:reporting:abuse'}
BlockList.add_entry_to_blocklist(self, jabber_id, node_id, item_id)
# subscribe['from'] = xmppbl.org
# subscribe['pubsub']['subscription']['node'] = 'muc_bans_sha256'
subscriptions = await XmppPubsub.get_node_subscriptions(self, jabber_id, node_id)
await self['xep_0115'].update_caps() await self['xep_0115'].update_caps()
bookmarks = await XmppBookmark.get_bookmarks(self) bookmarks = await XmppBookmark.get_bookmarks(self)
print(bookmarks) print(bookmarks)

View file

@ -9,6 +9,7 @@ Functions create_node and create_entry are derived from project atomtopubsub.
import hashlib import hashlib
from kaikout.log import Logger from kaikout.log import Logger
from slixmpp.exceptions import IqTimeout, IqError
logger = Logger(__name__) logger = Logger(__name__)
@ -66,3 +67,16 @@ class XmppPubsub:
async def get_items(self, jid, node): async def get_items(self, jid, node):
items = await self.plugin['xep_0060'].get_items(jid, node) items = await self.plugin['xep_0060'].get_items(jid, node)
return items return items
async def subscribe(self, jid, node):
result = await self['xep_0060'].subscribe(jid, node)
return result
async def get_node_subscriptions(self, jid, node):
try:
subscriptions = await self['xep_0060'].get_node_subscriptions(jid, node)
return subscriptions
except IqError as e:
print(e)