3 Commits

Author SHA1 Message Date
adator
37684eaede With unrealircd json rpc 2024-09-13 23:51:13 +02:00
adator
e6156fa301 V5.1.8 2024-09-08 00:42:18 +02:00
adator
58e3ebd287 V5.1.7 2024-09-03 00:19:13 +02:00
11 changed files with 369 additions and 92 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@ __pycache__/
configuration.json
install.log
test.py
# mods/mod_ia.py

View File

@@ -1,4 +1,9 @@
# IRC-DEFENDER
![Static Badge](https://img.shields.io/badge/UnrealIRCd-6.2.2%20or%20later-green)
![Static Badge](https://img.shields.io/badge/Python3-3.10%20or%20later-green)
![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fadator85%2FIRC_DEFENDER_MODULES%2Fmain%2Fversion.json&query=version&label=Current%20Version)
![Static Badge](https://img.shields.io/badge/Maintained-Yes-green)
Defender est un service IRC basé sur la sécurité des réseaux IRC ( UnrealIRCD )
Il permet d'ajouter une sécurité supplémentaire pour vérifier les users connectés au réseau
en demandant aux user un code de validation.
@@ -9,9 +14,9 @@ Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux u
Kick: Expulser un utilisateur du canal.
Ban: Interdire définitivement l'accès au canal.
Unban: Lever une interdiction.
Op/Deop: Attribuer ou retirer les droits d'opérateur.
Op/Deop/Opall/Deopall: Attribuer ou retirer les droits d'opérateur.
Halfop/Dehalfop: Attribuer ou retirer les droits
Voice/Devoice: Attribuer ou retirer les droits de voix.
Voice/Devoice/VoiceAll/DevoiceAll: Attribuer ou retirer les droits de voix.
Système de quarantaine:
Mise en quarantaine: Isoler temporairement un utilisateur dans un canal privé.
@@ -25,6 +30,7 @@ Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux u
Prérequis:
- Système d'exploitation Linux (Windows non supporté)
- Un server UnrealIRCD corréctement configuré
- Python version 3.10 ou supérieure
Bash:

View File

@@ -1,7 +1,7 @@
import time, threading, os, random, socket, hashlib, ipaddress, logging, requests, json, re, ast
from dataclasses import fields
from typing import Union, Literal
from base64 import b64decode
from base64 import b64decode, b64encode
from datetime import datetime
from sqlalchemy import create_engine, Engine, Connection, CursorResult
from sqlalchemy.sql import text
@@ -307,7 +307,7 @@ class Base:
def db_update_core_config(self, module_name:str, dataclassObj: object, param_key:str, param_value: str) -> bool:
core_table = 'core_config'
core_table = self.Config.table_config
# Check if the param exist
if not hasattr(dataclassObj, param_key):
self.logs.error(f"Le parametre {param_key} n'existe pas dans la variable global")
@@ -330,6 +330,10 @@ class Base:
if updated_rows > 0:
setattr(dataclassObj, param_key, self.int_if_possible(param_value))
self.logs.debug(f'Parameter updated : {param_key} - {param_value} | Module: {module_name}')
else:
self.logs.error(f'Parameter NOT updated : {param_key} - {param_value} | Module: {module_name}')
else:
self.logs.error(f'Parameter and Module do not exist: Param ({param_key}) - Value ({param_value}) | Module ({module_name})')
self.logs.debug(dataclassObj)
@@ -678,6 +682,17 @@ class Base:
self.logs.critical(f'This remote ip is not valid : {ve}')
return None
# def encode_ip(self, remote_ip_address: str) -> Union[str, None]:
# binary_ip = b64encode()
# try:
# decoded_ip = ipaddress.ip_address(binary_ip)
# return decoded_ip.exploded
# except ValueError as ve:
# self.logs.critical(f'This remote ip is not valid : {ve}')
# return None
def get_random(self, lenght:int) -> str:
"""
Retourn une chaîne aléatoire en fonction de la longueur spécifiée.

View File

@@ -1,4 +1,4 @@
import socket, ssl, time
import socket, ssl
from ssl import SSLSocket
from core.loadConf import Config
from core.Model import Clones
@@ -186,15 +186,17 @@ class Connection:
nickname = fullname.split('!')[0].replace(':','')
if nickname == self.Config.SERVICE_NICKNAME:
command = str(response[3]).replace(':','')
if command == 'KILL':
self.send2socket(f'QUIT :Thanks and goodbye')
self.signal = self.currentCloneObject.alive
if command == 'JOIN':
channel_to_join = str(response[4])
self.send2socket(f"JOIN {channel_to_join}")
if command == 'SAY':
clone_channel = str(response[4])
message = []
for i in range(5, len(response)):
message.append(response[i])
@@ -202,7 +204,6 @@ class Connection:
self.send2socket(f"PRIVMSG {clone_channel} :{final_message}")
except UnicodeEncodeError:
for data in cmd:
response = data.decode(self.CHARSET[1],'replace').split()

View File

@@ -1,4 +1,4 @@
import ssl, re, importlib, sys, time, threading, socket
import ssl, re, importlib, sys, time, threading, socket, traceback
from ssl import SSLSocket
from datetime import datetime, timedelta
from typing import Union, Literal
@@ -110,8 +110,8 @@ class Irc:
self.ircObject = ircInstance # créer une copie de l'instance Irc
self.__link(self.IrcSocket) # établir la connexion au serveur IRC
self.signal = True # Une variable pour initier la boucle infinie
self.load_existing_modules() # Charger les modules existant dans la base de données
self.__join_saved_channels() # Join existing channels
self.load_existing_modules() # Charger les modules existant dans la base de données
while self.signal:
try:
@@ -131,6 +131,7 @@ class Irc:
self.__create_socket()
self.__link(self.IrcSocket)
self.__join_saved_channels()
self.load_existing_modules()
self.RESTART = 0
@@ -175,6 +176,7 @@ class Irc:
self.Base.logs.critical(f"AttributeError: {atte}")
except Exception as e:
self.Base.logs.critical(f"Exception: {e}")
self.Base.logs.critical(traceback.print_exc())
def __link(self, writer:Union[socket.socket, SSLSocket]) -> None:
"""Créer le link et envoyer les informations nécessaires pour la
@@ -212,7 +214,8 @@ class Irc:
writer.send(f":{sid} PROTOCTL SID={sid}\r\n".encode(charset))
writer.send(f":{sid} SERVER {link} 1 :{info}\r\n".encode(charset))
writer.send(f":{sid} {nickname} :Reserved for services\r\n".encode(charset))
writer.send(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * * :{realname}\r\n".encode(charset))
#writer.send(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * * :{realname}\r\n".encode(charset))
writer.send(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * fwAAAQ== :{realname}\r\n".encode(charset))
writer.send(f":{sid} SJOIN {unixtime} {chan} + :{service_id}\r\n".encode(charset))
writer.send(f":{sid} TKL + Q * {nickname} {host} 0 {unixtime} :Reserved for services\r\n".encode(charset))
@@ -272,14 +275,20 @@ class Irc:
response = data.decode(self.CHARSET[0]).split()
self.cmd(response)
except UnicodeEncodeError:
except UnicodeEncodeError as ue:
for data in responses:
response = data.decode(self.CHARSET[1],'replace').split()
self.cmd(response)
except UnicodeDecodeError:
self.Base.logs.error(f'UnicodeEncodeError: {ue}')
self.Base.logs.error(response)
except UnicodeDecodeError as ud:
for data in responses:
response = data.decode(self.CHARSET[1],'replace').split()
self.cmd(response)
self.Base.logs.error(f'UnicodeDecodeError: {ud}')
self.Base.logs.error(response)
except AssertionError as ae:
self.Base.logs.error(f"Assertion error : {ae}")
@@ -574,7 +583,6 @@ class Irc:
return None
def thread_check_for_new_version(self, fromuser: str) -> None:
dnickname = self.Config.SERVICE_NICKNAME
if self.Base.check_for_new_version(True):
@@ -585,35 +593,39 @@ class Irc:
return None
def cmd(self, data:list) -> None:
def cmd(self, data: list[str]) -> None:
"""Parse server response
Args:
data (list[str]): Server response splitted in a list
"""
try:
original_response: list[str] = data.copy()
cmd_to_send:list[str] = data.copy()
cmd = data.copy()
interm_response: list[str] = data.copy()
"""This the original without first value"""
cmd_to_debug = data.copy()
cmd_to_debug.pop(0)
interm_response.pop(0)
if len(cmd) == 0 or len(cmd) == 1:
self.Base.logs.warning(f'Size ({str(len(cmd))}) - {cmd}')
if len(original_response) == 0 or len(original_response) == 1:
self.Base.logs.warning(f'Size ({str(len(original_response))}) - {original_response}')
return False
# self.debug(cmd_to_debug)
if len(data) == 7:
if data[2] == 'PRIVMSG' and data[4] == ':auth':
data_copy = data.copy()
if len(original_response) == 7:
if original_response[2] == 'PRIVMSG' and original_response[4] == ':auth':
data_copy = original_response.copy()
data_copy[6] = '**********'
self.Base.logs.debug(data_copy)
else:
self.Base.logs.debug(data)
self.Base.logs.debug(original_response)
else:
self.Base.logs.debug(data)
self.Base.logs.debug(original_response)
match cmd[0]:
match original_response[0]:
case 'PING':
# Sending PONG response to the serveur
pong = str(cmd[1]).replace(':','')
pong = str(original_response[1]).replace(':','')
self.send2socket(f"PONG :{pong}")
return None
@@ -622,19 +634,19 @@ class Irc:
# 'PREFIX=(qaohv)~&@%+', 'SID=001', 'MLOCK', 'TS=1703793941', 'EXTSWHOIS']
# GET SERVER ID HOST
if len(cmd) > 5:
if '=' in cmd[5]:
serveur_hosting_id = str(cmd[5]).split('=')
if len(original_response) > 5:
if '=' in original_response[5]:
serveur_hosting_id = str(original_response[5]).split('=')
self.HSID = serveur_hosting_id[1]
return False
case _:
pass
if len(cmd) < 2:
if len(original_response) < 2:
return False
match cmd[1]:
match original_response[1]:
case 'SLOG':
# self.Base.scan_ports(cmd[7])
@@ -645,20 +657,18 @@ class Irc:
case 'REPUTATION':
# :001 REPUTATION 91.168.141.239 118
try:
# if self.Config.ABUSEIPDB == 1:
# self.Base.create_thread(self.abuseipdb_scan, (cmd[2], ))
self.first_connexion_ip = cmd[2]
self.first_connexion_ip = original_response[2]
self.first_score = 0
if str(cmd[3]).find('*') != -1:
if str(original_response[3]).find('*') != -1:
# If * available, it means that an ircop changed the repurtation score
# means also that the user exist will try to update all users with same IP
self.first_score = int(str(cmd[3]).replace('*',''))
self.first_score = int(str(original_response[3]).replace('*',''))
for user in self.User.UID_DB:
if user.remote_ip == self.first_connexion_ip:
user.score_connexion = self.first_score
else:
self.first_score = int(cmd[3])
self.first_score = int(original_response[3])
# Possibilité de déclancher les bans a ce niveau.
except IndexError as ie:
@@ -681,7 +691,7 @@ class Irc:
case 'EOS':
hsid = str(cmd[0]).replace(':','')
hsid = str(original_response[0]).replace(':','')
if hsid == self.HSID:
if self.INIT == 1:
current_version = self.Config.current_version
@@ -691,10 +701,6 @@ class Irc:
else:
version = f'{current_version}'
# self.send2socket(f":{self.Config.SERVICE_NICKNAME} SVSJOIN {self.Config.SERVICE_NICKNAME} {self.Config.SERVICE_CHANLOG}")
# self.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.SERVICE_CHANLOG} +o {self.Config.SERVICE_NICKNAME}")
# self.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.SERVICE_CHANLOG} +{self.Config.SERVICE_CMODES}")
print(f"################### DEFENDER ###################")
print(f"# SERVICE CONNECTE ")
print(f"# SERVEUR : {self.Config.SERVEUR_IP} ")
@@ -726,15 +732,15 @@ class Irc:
case _:
pass
if len(cmd) < 3:
if len(original_response) < 3:
return False
match cmd[2]:
match original_response[2]:
case 'QUIT':
# :001N1WD7L QUIT :Quit: free_znc_1
cmd.pop(0)
uid_who_quit = str(cmd[0]).replace(':', '')
uid_who_quit = str(interm_response[0]).replace(':', '')
self.User.delete(uid_who_quit)
self.Channel.delete_user_from_all_channel(uid_who_quit)
@@ -746,10 +752,8 @@ class Irc:
# ['@unrealircd.org/geoip=FR;unrealircd.org/', ':001OOU2H3', 'NICK', 'WebIrc', '1703795844']
# Changement de nickname
# Supprimer la premiere valeur de la liste
cmd.pop(0)
uid = str(cmd[0]).replace(':','')
newnickname = cmd[2]
uid = str(interm_response[0]).replace(':','')
newnickname = interm_response[2]
self.User.update(uid, newnickname)
case 'MODE':
@@ -764,24 +768,24 @@ class Irc:
# ':001T6VU3F', '001JGWB2K', '@11ZAAAAAB',
# '001F16WGR', '001X9YMGQ', '*+001DYPFGP', '@00BAAAAAJ', '001AAGOG9', '001FMFVG8', '001DAEEG7',
# '&~G:unknown-users', '"~G:websocket-users', '"~G:known-users', '"~G:webirc-users']
cmd.pop(0)
channel = str(cmd[3]).lower()
len_cmd = len(cmd)
channel = str(interm_response[3]).lower()
len_cmd = len(interm_response)
list_users:list = []
occurence = 0
start_boucle = 0
# Trouver le premier user
for i in range(len_cmd):
s: list = re.findall(fr':', cmd[i])
s: list = re.findall(fr':', interm_response[i])
if s:
occurence += 1
if occurence == 2:
start_boucle = i
# Boucle qui va ajouter l'ensemble des users (UID)
for i in range(start_boucle, len(cmd)):
parsed_UID = str(cmd[i])
for i in range(start_boucle, len(interm_response)):
parsed_UID = str(interm_response[i])
# pattern = fr'[:|@|%|\+|~|\*]*'
# pattern = fr':'
# parsed_UID = re.sub(pattern, '', parsed_UID)
@@ -799,29 +803,31 @@ class Irc:
case 'PART':
# ['@unrealircd.org/geoip=FR;unrealircd.org/userhost=50d6492c@80.214.73.44;unrealircd.org/userip=50d6492c@80.214.73.44;msgid=YSIPB9q4PcRu0EVfC9ci7y-/mZT0+Gj5FLiDSZshH5NCw;time=2024-08-15T15:35:53.772Z',
# ':001EPFBRD', 'PART', '#welcome', ':WEB', 'IRC', 'Paris']
uid = str(cmd[1]).replace(':','')
channel = str(cmd[3]).lower()
try:
uid = str(interm_response[0]).replace(':','')
channel = str(interm_response[2]).lower()
self.Channel.delete_user_from_channel(channel, uid)
pass
except IndexError as ie:
self.Base.logs.error(f'Index Error: {ie}')
case 'UID':
# ['@s2s-md/geoip=cc=GB|cd=United\\sKingdom|asn=16276|asname=OVH\\sSAS;s2s-md/tls_cipher=TLSv1.3-TLS_CHACHA20_POLY1305_SHA256;s2s-md/creationtime=1721564601',
# ':001', 'UID', 'albatros', '0', '1721564597', 'albatros', 'vps-91b2f28b.vps.ovh.net',
# '001HB8G04', '0', '+iwxz', 'Clk-A62F1D18.vps.ovh.net', 'Clk-A62F1D18.vps.ovh.net', 'MyZBwg==', ':...']
if 'webirc' in cmd[0]:
if 'webirc' in original_response[0]:
isWebirc = True
else:
isWebirc = False
uid = str(cmd[8])
nickname = str(cmd[3])
username = str(cmd[6])
hostname = str(cmd[7])
umodes = str(cmd[10])
vhost = str(cmd[11])
uid = str(original_response[8])
nickname = str(original_response[3])
username = str(original_response[6])
hostname = str(original_response[7])
umodes = str(original_response[10])
vhost = str(original_response[11])
if not 'S' in umodes:
remote_ip = self.Base.decode_ip(str(cmd[13]))
remote_ip = self.Base.decode_ip(str(original_response[13]))
else:
remote_ip = '127.0.0.1'
@@ -843,18 +849,19 @@ class Irc:
)
for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(cmd_to_send)
classe_object.cmd(original_response)
case 'PRIVMSG':
try:
# Supprimer la premiere valeur
cmd.pop(0)
cmd = interm_response.copy()
get_uid_or_nickname = str(cmd[0].replace(':',''))
user_trigger = self.User.get_nickname(get_uid_or_nickname)
dnickname = self.Config.SERVICE_NICKNAME
if len(cmd) == 6:
if cmd[1] == 'PRIVMSG' and str(cmd[3]).replace('.','') == ':auth':
if cmd[1] == 'PRIVMSG' and str(cmd[3]).replace(self.Config.SERVICE_PREFIX,'') == ':auth':
cmd_copy = cmd.copy()
cmd_copy[5] = '**********'
self.Base.logs.info(cmd_copy)
@@ -911,11 +918,11 @@ class Irc:
return False
if not arg[0].lower() in self.commands:
self.debug(f"This command {arg[0]} is not available")
self.Base.logs.debug(f"This command {arg[0]} sent by {user_trigger} is not available")
return False
cmd_to_send = convert_to_string.replace(':','')
self.Base.log_cmd(self.User.get_nickname(user_trigger), cmd_to_send)
self.Base.log_cmd(user_trigger, cmd_to_send)
fromchannel = None
if len(arg) >= 2:
@@ -929,15 +936,26 @@ class Irc:
case _:
pass
if cmd[2] != 'UID':
if original_response[2] != 'UID':
# Envoyer la commande aux classes dynamiquement chargées
for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(cmd_to_send)
classe_object.cmd(original_response)
except IndexError as ie:
self.Base.logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
self.Base.logs.error(f"{ie} / {original_response} / length {str(len(original_response))}")
def _hcmds(self, user: str, channel: Union[str, None], cmd:list, fullcmd: list = []) -> None:
def _hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None:
"""_summary_
Args:
user (str): The user who sent the query
channel (Union[str, None]): If the command contain the channel
cmd (list): The defender cmd
fullcmd (list, optional): The full list of the cmd coming from PRIVMS. Defaults to [].
Returns:
None: Nothing to return
"""
fromuser = self.User.get_nickname(user) # Nickname qui a lancé la commande
uid = self.User.get_uid(fromuser) # Récuperer le uid de l'utilisateur
@@ -1283,10 +1301,6 @@ class Irc:
results = self.Base.db_execute_query(f'SELECT module_name FROM {self.Config.table_module}')
results = results.fetchall()
# if len(results) == 0:
# self.send2socket(f":{dnickname} NOTICE {fromuser} :There is no module loaded")
# return False
found = False
for module in all_modules:
@@ -1301,9 +1315,6 @@ class Irc:
found = False
# for r in results:
# self.send2socket(f":{dnickname} NOTICE {fromuser} :{r[0]} - {self.Config.CONFIG_COLOR['verte']}Loaded{self.Config.CONFIG_COLOR['nogc']}")
case 'show_timers':
if self.Base.running_timers:

View File

@@ -35,7 +35,9 @@ class Command():
# Create module commands (Mandatory)
self.commands_level = {
1: ['join', 'part'],
2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice', 'devoice', 'opall', 'deopall', 'devoiceall', 'voiceall', 'ban', 'unban','kick', 'kickban', 'umode']
2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice',
'devoice', 'opall', 'deopall', 'devoiceall', 'voiceall', 'ban',
'unban','kick', 'kickban', 'umode', 'svsjoin', 'svspart', 'svsnick']
}
# Init the module
@@ -581,8 +583,62 @@ class Command():
nickname = str(cmd[1])
umode = str(cmd[2])
self.send2socket(f':{dnickname} SVSMODE {nickname} {umode}')
self.Irc.send2socket(f':{dnickname} SVSMODE {nickname} {umode}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'svsjoin':
try:
# .svsjoin nickname #channel
nickname = str(cmd[1])
channel = str(cmd[2])
if len(cmd) != 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSJOIN nickname #channel')
return None
self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SVSJOIN {nickname} {channel}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSJOIN nickname #channel')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'svspart':
try:
# .svspart nickname #channel
nickname = str(cmd[1])
channel = str(cmd[2])
if len(cmd) != 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSPART nickname #channel')
return None
self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SVSPART {nickname} {channel}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSPART nickname #channel')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'svsnick':
try:
# .svsnick nickname newnickname
nickname = str(cmd[1])
newnickname = str(cmd[2])
unixtime = self.Base.get_unixtime()
if self.User.get_nickname(nickname) is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : This nickname do not exist')
return None
if len(cmd) != 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSNICK nickname newnickname')
return None
self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SVSNICK {nickname} {newnickname} {unixtime}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSNICK nickname newnickname')
self.Logs.warning(f'Unknown Error: {str(err)}')

View File

@@ -140,6 +140,9 @@ class Defender():
self.Base.create_thread(func=self.thread_psutil_scan)
self.Base.create_thread(func=self.thread_reputation_timer)
if self.ModConfig.reputation == 1:
self.Irc.send2socket(f":{self.Config.SERVICE_ID} SAMODE {self.Config.SALON_JAIL} +{self.Config.SERVICE_UMODES} {self.Config.SERVICE_NICKNAME}")
return None
def __set_commands(self, commands:dict[int, list[str]]) -> None:
@@ -487,6 +490,7 @@ class Defender():
if self.get_user_uptime_in_minutes(user.uid) >= reputation_timer and int(user.score) <= int(reputation_seuil):
self.Irc.send2socket(f":{service_id} PRIVMSG {dchanlog} :[{color_red} REPUTATION {color_black}] : Action sur {user.nickname} aprés {str(reputation_timer)} minutes d'inactivité")
self.Irc.send2socket(f":{service_id} KILL {user.nickname} After {str(reputation_timer)} minutes of inactivity you should reconnect and type the password code ")
self.Irc.send2socket(f":{self.Config.SERVEUR_LINK} REPUTATION {user.ip} 0")
self.Logs.info(f"Nickname: {user.nickname} KILLED after {str(reputation_timer)} minutes of inactivity")
uid_to_clean.append(user.uid)
@@ -936,7 +940,7 @@ class Defender():
except ValueError as ve:
self.Logs.error(f"Thread_cloudfilt_scan Error : {ve}")
def cmd(self, data:list) -> None:
def cmd(self, data: list) -> None:
service_id = self.Config.SERVICE_ID # Defender serveur id
cmd = list(data).copy()
@@ -946,6 +950,10 @@ class Defender():
match cmd[1]:
case 'EOS':
if self.Irc.INIT == 0:
self.Irc.send2socket(f":{service_id} SAMODE {self.Config.SALON_JAIL} +{self.Config.SERVICE_UMODES} {self.Config.SERVICE_NICKNAME}")
case 'REPUTATION':
# :001 REPUTATION 91.168.141.239 118
try:

179
mods/mod_jsonrpc.py Normal file
View File

@@ -0,0 +1,179 @@
from dataclasses import dataclass
from core.irc import Irc
from unrealircd_rpc_py.Live import Live
class Jsonrpc():
@dataclass
class ModConfModel:
"""The Model containing the module parameters
"""
param_exemple1: str
param_exemple2: int
def __init__(self, ircInstance:Irc) -> None:
# Module name (Mandatory)
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
# Add Irc Object to the module (Mandatory)
self.Irc = ircInstance
# Add Global Configuration to the module (Mandatory)
self.Config = ircInstance.Config
# Add Base object to the module (Mandatory)
self.Base = ircInstance.Base
# Add logs object to the module (Mandatory)
self.Logs = ircInstance.Base.logs
# Add User object to the module (Mandatory)
self.User = ircInstance.User
# Add Channel object to the module (Mandatory)
self.Channel = ircInstance.Channel
# Create module commands (Mandatory)
self.commands_level = {
1: ['jsonrpc']
}
# Init the module
self.__init_module()
# Log the module
self.Logs.debug(f'Module {self.module_name} loaded ...')
def __init_module(self) -> None:
# Insert module commands into the core one (Mandatory)
self.__set_commands(self.commands_level)
# Create you own tables (Mandatory)
# self.__create_tables()
# Load module configuration and sync with core one (Mandatory)
self.__load_module_configuration()
# End of mandatory methods you can start your customization #
return None
def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal
Args:
commands (list): Liste des commandes du module
"""
for level, com in commands.items():
for c in commands[level]:
if not c in self.Irc.commands:
self.Irc.commands_level[level].append(c)
self.Irc.commands.append(c)
return None
def __create_tables(self) -> None:
"""Methode qui va créer la base de donnée si elle n'existe pas.
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
Args:
database_name (str): Nom de la base de données ( pas d'espace dans le nom )
Returns:
None: Aucun retour n'es attendu
"""
table_logs = '''CREATE TABLE IF NOT EXISTS test_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
server_msg TEXT
)
'''
self.Base.db_execute_query(table_logs)
return None
def callback_sent_to_irc(self, json_response: str):
dnickname = self.Config.SERVICE_NICKNAME
dchanlog = self.Config.SERVICE_CHANLOG
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :{json_response}")
pass
def thread_start_jsonrpc(self):
liveRpc = Live(path_to_socket_file='/home/adator/IRC/unrealircd6.1.2.1/data/rpc.socket',
callback_object_instance=self,
callback_method_name='callback_sent_to_irc'
)
liveRpc.execute_async_method()
pass
def __load_module_configuration(self) -> None:
"""### Load Module Configuration
"""
try:
# Build the default configuration model (Mandatory)
self.ModConfig = self.ModConfModel(param_exemple1='param value 1', param_exemple2=1)
# Sync the configuration with core configuration (Mandatory)
#self.Base.db_sync_core_config(self.module_name, self.ModConfig)
return None
except TypeError as te:
self.Logs.critical(te)
def __update_configuration(self, param_key: str, param_value: str):
"""Update the local and core configuration
Args:
param_key (str): The parameter key
param_value (str): The parameter value
"""
self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
def unload(self) -> None:
return None
def cmd(self, data:list) -> None:
return None
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower()
dnickname = self.Config.SERVICE_NICKNAME
fromuser = user
fromchannel = str(channel) if not channel is None else None
match command:
case 'jsonrpc':
self.Base.create_thread(self.thread_start_jsonrpc, run_once=True)
pass
case 'ia':
try:
self.Base.create_thread(self.thread_ask_ia, ('',))
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} : This is a notice to the sender ...")
self.Irc.send2socket(f":{dnickname} PRIVMSG {fromuser} : This is private message to the sender ...")
if not fromchannel is None:
self.Irc.send2socket(f":{dnickname} PRIVMSG {fromchannel} : This is channel message to the sender ...")
# How to update your module configuration
self.__update_configuration('param_exemple2', 7)
# Log if you want the result
self.Logs.debug(f"Test logs ready")
except Exception as err:
self.Logs.error(f"Unknown Error: {err}")

Binary file not shown.

Binary file not shown.

View File

@@ -1,3 +1,3 @@
{
"version": "5.1.6"
"version": "5.1.9"
}