forked from sch/KaikOut
Add RTBL configuration file;
Add functionality to exclude specified RTBL lists.
This commit is contained in:
parent
9e051fa0a6
commit
8df43fd327
7 changed files with 148 additions and 25 deletions
|
@ -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>
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,12 +270,13 @@ 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(
|
||||||
|
jid, node)
|
||||||
await XmppCommands.devoice(self, room, alias, reason)
|
await XmppCommands.devoice(self, room, alias, reason)
|
||||||
break
|
break
|
||||||
# message_body = 'Greetings {} and welcome to groupchat {}'.format(alias, room)
|
# message_body = 'Greetings {} and welcome to groupchat {}'.format(alias, room)
|
||||||
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue