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.
"""
[rtbl]
allow = """
ignore [+|-] <keyword>
Jabber IDs to ignore
comma-separated keywords
'+' appends to, '-' removes from.
"""
[statistics]
score = """
scores <jid>

View file

@ -1,6 +1,7 @@
# This file lists default RTBL sources per database.
# See file /usr/share/kaikout/rtbl.toml
[[node]]
[[sources]]
description = "A general block list focussing on spam and service abuse."
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.
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.
rtbl_ignore = [] # A list of RTBL lists to exclude.
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.
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)
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:

View file

@ -260,7 +260,7 @@ class XmppChat:
else:
response = str(self.settings[room]['action'])
case _ if command_lowercase.startswith('allow +'):
value = command[7:]
value = command[7:].strip()
if value:
response = XmppCommands.set_filter(
self, room, db_file, value, 'allow', True)
@ -268,7 +268,7 @@ class XmppChat:
response = ('No action has been taken. '
'Missing keywords.')
case _ if command_lowercase.startswith('allow -'):
value = command[7:]
value = command[7:].strip()
if value:
response = XmppCommands.set_filter(
self, room, db_file, value, 'allow', False)
@ -386,6 +386,22 @@ class XmppChat:
await XmppCommands.muc_leave(self, room)
else:
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':
XmppCommands.update_setting_value(
self, room, db_file, 'check_inactivity', 0)

View file

@ -3,11 +3,10 @@
import asyncio
from datetime import datetime
import slixmpp
from kaikout.about import Documentation
from kaikout.database import Toml
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.chat import XmppChat
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.pubsub import XmppPubsub
from kaikout.xmpp.status import XmppStatus
import slixmpp
import time
# time_now = datetime.now()
@ -53,10 +53,13 @@ class XmppClient(slixmpp.ClientXMPP):
self.reconnect_timeout = Config.get_values('accounts.toml', 'xmpp')['settings']['reconnect_timeout']
# A handler for operators.
self.operators = Config.get_values('accounts.toml', 'xmpp')['operators']
# A handler for settings.
self.settings = {}
# A handler for blocklist.
#self.blocklist = {}
BlockList.load_blocklist(self)
# A handler for sessions.
self.sessions = {}
# A handler for settings.
self.settings = {}
# A handler for tasks.
self.tasks = {}
# Register plugins.
@ -267,12 +270,13 @@ class XmppClient(slixmpp.ClientXMPP):
jid = presence['muc']['jid']
from hashlib import sha256
jid_to_sha256 = sha256(jid.bare.encode('utf-8')).hexdigest()
rtbl_jid_full = 'xmppbl.org'
rtbl_node_id = 'muc_bans_sha256'
rtbl_list = await XmppPubsub.get_items(self, rtbl_jid_full, rtbl_node_id)
for item in rtbl_list['pubsub']['items']:
if jid_to_sha256 == item['id']:
reason = 'Jabber ID has been marked by RTBL.'
for jid in self.blocklist['entries']:
if jid not in self.settings[room]['rtbl_ignore']:
for node in self.blocklist['entries'][jid]:
for item_id in self.blocklist['entries'][jid][node]:
if jid_to_sha256 == item_id:
reason = 'Jabber ID has been marked by RTBL: Publisher: {}; Node: {}.'.format(
jid, node)
await XmppCommands.devoice(self, room, alias, reason)
break
# message_body = 'Greetings {} and welcome to groupchat {}'.format(alias, room)
@ -477,14 +481,32 @@ class XmppClient(slixmpp.ClientXMPP):
"""
# self.command_list()
# await self.get_roster()
subscriptions_of_node = await self['xep_0060'].get_node_subscriptions("xmppbl.org", "muc_bans_sha256")
print()
print('=== subscriptions_of_node ===')
print()
print(subscriptions_of_node)
print()
print('=== subscriptions_of_node ===')
print()
rtbl_sources = Config.get_values('rtbl.toml')['sources']
for source in rtbl_sources:
jabber_id = source['jabber_id']
node_id = source['node_id']
subscribe = await XmppPubsub.subscribe(self, jabber_id, node_id)
if subscribe['pubsub']['subscription']['subscription'] == 'subscribed':
rtbl_list = await XmppPubsub.get_items(self, jabber_id, node_id)
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()
bookmarks = await XmppBookmark.get_bookmarks(self)
print(bookmarks)

View file

@ -9,6 +9,7 @@ Functions create_node and create_entry are derived from project atomtopubsub.
import hashlib
from kaikout.log import Logger
from slixmpp.exceptions import IqTimeout, IqError
logger = Logger(__name__)
@ -66,3 +67,16 @@ class XmppPubsub:
async def get_items(self, jid, node):
items = await self.plugin['xep_0060'].get_items(jid, node)
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)