4 Commits

Author SHA1 Message Date
adator
f7664c9874 V6.0.4 2024-11-18 23:49:45 +01:00
adator
d37c152160 Update to version 6.0.3 2024-11-17 21:09:14 +01:00
adator
b81f502b95 V6.0.2 2024-11-15 22:14:11 +01:00
adator
44da01945c V6.0.1 2024-11-11 23:38:05 +01:00
14 changed files with 766 additions and 474 deletions

View File

@@ -194,14 +194,19 @@ class Base:
file_hanlder = logging.FileHandler(f'logs{self.Config.OS_SEP}defender.log',encoding='UTF-8')
file_hanlder.setLevel(self.Config.DEBUG_LEVEL)
stdout_handler = logging.StreamHandler()
stdout_handler.setLevel(50)
# Define log format
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s')
# Apply log format
file_hanlder.setFormatter(formatter)
stdout_handler.setFormatter(formatter)
# Add handler to logs
self.logs.addHandler(file_hanlder)
self.logs.addHandler(stdout_handler)
# Apply the filter
self.logs.addFilter(self.replace_filter)

View File

@@ -2,6 +2,8 @@ from re import findall
from typing import Union, Literal, TYPE_CHECKING
from dataclasses import asdict
from core.classes import user
if TYPE_CHECKING:
from core.definition import MChannel
from core.base import Base
@@ -31,6 +33,10 @@ class Channel:
result = False
exist = False
if not self.Is_Channel(newChan.name):
self.Logs.error(f"The channel {newChan.name} is not valid, channel must start with #")
return False
for record in self.UID_CHANNEL_DB:
if record.name.lower() == newChan.name.lower():
# If the channel exist, update the user list and do not go further
@@ -129,6 +135,29 @@ class Channel:
except Exception as err:
self.Logs.error(f'{err}')
def is_user_present_in_channel(self, channel_name: str, uid: str) -> bool:
"""Check if a user is present in the channel
Args:
channel_name (str): The channel to check
uid (str): The UID
Returns:
bool: True if the user is present in the channel
"""
user_found = False
chan = self.get_Channel(channel_name=channel_name)
if chan is None:
return user_found
clean_uid = self.Base.clean_uid(uid=uid)
for chan_uid in chan.uids:
if self.Base.clean_uid(chan_uid) == clean_uid:
user_found = True
break
return user_found
def clean_channel(self) -> None:
"""Remove Channels if empty
"""

View File

@@ -1,9 +1,11 @@
from re import match, findall
from re import match, findall, search
from datetime import datetime
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Union
from ssl import SSLEOFError, SSLError
from dataclasses import dataclass
from websockets import serve
if TYPE_CHECKING:
from core.irc import Irc
@@ -17,6 +19,12 @@ class Unrealircd6:
self.__Base = ircInstance.Base
self.__Settings = ircInstance.Base.Settings
self.known_protocol = ['SJOIN', 'UID', 'MD', 'QUIT', 'SQUIT',
'EOS', 'PRIVMSG', 'MODE', 'UMODE2',
'VERSION', 'REPUTATION', 'SVS2MODE',
'SLOG', 'NICK', 'PART', 'PONG'
]
self.__Base.logs.info(f"** Loading protocol [{__name__}]")
def send2socket(self, message: str, print_log: bool = True) -> None:
@@ -104,6 +112,40 @@ class Unrealircd6:
except Exception as err:
self.__Base.logs.error(f"General Error: {err}")
def parse_server_msg(self, server_msg: list[str]) -> Union[str, None]:
"""Parse the server message and return the command
Args:
server_msg (list[str]): The Original server message >>
Returns:
Union[str, None]: Return the command protocol name
"""
protocol_exception = ['PING', 'SERVER', 'PROTOCTL']
increment = 0
server_msg_copy = server_msg.copy()
first_index = 0
second_index = 0
for index, element in enumerate(server_msg_copy):
# Handle the protocol exceptions ex. ping, server ....
if element in protocol_exception and index == 0:
return element
if element.startswith(':'):
increment += 1
first_index = index + 1 if increment == 1 else first_index
second_index = index if increment == 2 else second_index
second_index = len(server_msg_copy) if second_index == 0 else second_index
parsed_msg = server_msg_copy[first_index:second_index]
for cmd in parsed_msg:
if cmd in self.known_protocol:
return cmd
return None
def link(self):
"""Créer le link et envoyer les informations nécessaires pour la
connexion au serveur.
@@ -408,6 +450,43 @@ class Unrealircd6:
self.__Irc.Channel.delete_user_from_channel(channel, userObj.uid)
return None
def on_svs2mode(self, serverMsg: list[str]) -> None:
"""Handle svs2mode coming from a server
Args:
serverMsg (list[str]): Original server message
"""
try:
# >> [':00BAAAAAG', 'SVS2MODE', '001U01R03', '-r']
uid_user_to_edit = serverMsg[2]
umode = serverMsg[3]
userObj = self.__Irc.User.get_User(uid_user_to_edit)
if userObj is None:
return None
if self.__Irc.User.update_mode(userObj.uid, umode):
return None
return None
except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_mode(self, serverMsg: list[str]) -> None:
"""Handle mode coming from a server
Args:
serverMsg (list[str]): Original server message
"""
#['@msgid=d0ySx56Yd0nc35oHts2SkC-/J9mVUA1hfM6...', ':001', 'MODE', '#a', '+nt', '1723207536']
#['@unrealircd.org/userhost=adator@localhost;...', ':001LQ0L0C', 'MODE', '#services', '-l']
return None
def on_umode2(self, serverMsg: list[str]) -> None:
"""Handle umode2 coming from a server
@@ -490,12 +569,32 @@ class Unrealircd6:
serverMsg (list[str]): Original server message
"""
# ['PROTOCTL', 'CHANMODES=beI,fkL,lFH,cdimnprstzCDGKMNOPQRSTVZ', 'USERMODES=diopqrstwxzBDGHIRSTWZ', 'BOOTED=1728815798', 'PREFIX=(qaohv)~&@%+', 'SID=001', 'MLOCK', 'TS=1730662755', 'EXTSWHOIS']
if len(serverMsg) > 5:
if '=' in serverMsg[5]:
serveur_hosting_id = str(serverMsg[5]).split('=')
self.__Config.HSID = serveur_hosting_id[1]
if 'USERMODES=' in serverMsg[2]:
self.__Settings.USER_MODES = list(serverMsg[2].split('=')[1])
user_modes: str = None
prefix: str = None
host_server_id: str = None
for msg in serverMsg:
pattern = None
if msg.startswith('PREFIX='):
pattern = r'^PREFIX=\((.*)\).*$'
find_match = match(pattern, msg)
prefix = find_match.group(1) if find_match else None
if find_match:
prefix = find_match.group(1)
elif msg.startswith('USERMODES='):
pattern = r'^USERMODES=(.*)$'
find_match = match(pattern, msg)
user_modes = find_match.group(1) if find_match else None
elif msg.startswith('SID='):
host_server_id = msg.split('=')[1]
if user_modes is None or prefix is None or host_server_id is None:
return None
self.__Config.HSID = host_server_id
self.__Settings.PROTOCTL_USER_MODES = list(user_modes)
self.__Settings.PROTOCTL_PREFIX = list(prefix)
return None
@@ -534,8 +633,11 @@ class Unrealircd6:
# ':001T6VU3F', '001JGWB2K', '@11ZAAAAAB',
# '001F16WGR', '001X9YMGQ', '*+001DYPFGP', '@00BAAAAAJ', '001AAGOG9', '001FMFVG8', '001DAEEG7',
# '&~G:unknown-users', '"~G:websocket-users', '"~G:known-users', '"~G:webirc-users']
# [':00B', 'SJOIN', '1731872579', '#services', '+', ':00BAAAAAB']
serverMsg_copy = serverMsg.copy()
if serverMsg_copy[0].startswith('@'):
serverMsg_copy.pop(0)
channel = str(serverMsg_copy[3]).lower()
len_cmd = len(serverMsg_copy)
list_users:list = []
@@ -592,6 +694,105 @@ class Unrealircd6:
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_eos(self, serverMsg: list[str]) -> None:
"""Handle EOS coming from a server
Args:
serverMsg (list[str]): Original server message
"""
try:
# [':001', 'EOS']
server_msg_copy = serverMsg.copy()
hsid = str(server_msg_copy[0]).replace(':','')
if hsid == self.__Config.HSID:
if self.__Config.DEFENDER_INIT == 1:
current_version = self.__Config.CURRENT_VERSION
latest_version = self.__Config.LATEST_VERSION
if self.__Base.check_for_new_version(False):
version = f'{current_version} >>> {latest_version}'
else:
version = f'{current_version}'
print(f"################### DEFENDER ###################")
print(f"# SERVICE CONNECTE ")
print(f"# SERVEUR : {self.__Config.SERVEUR_IP} ")
print(f"# PORT : {self.__Config.SERVEUR_PORT} ")
print(f"# SSL : {self.__Config.SERVEUR_SSL} ")
print(f"# SSL VER : {self.__Config.SSL_VERSION} ")
print(f"# NICKNAME : {self.__Config.SERVICE_NICKNAME} ")
print(f"# CHANNEL : {self.__Config.SERVICE_CHANLOG} ")
print(f"# VERSION : {version} ")
print(f"################################################")
self.__Base.logs.info(f"################### DEFENDER ###################")
self.__Base.logs.info(f"# SERVICE CONNECTE ")
self.__Base.logs.info(f"# SERVEUR : {self.__Config.SERVEUR_IP} ")
self.__Base.logs.info(f"# PORT : {self.__Config.SERVEUR_PORT} ")
self.__Base.logs.info(f"# SSL : {self.__Config.SERVEUR_SSL} ")
self.__Base.logs.info(f"# SSL VER : {self.__Config.SSL_VERSION} ")
self.__Base.logs.info(f"# NICKNAME : {self.__Config.SERVICE_NICKNAME} ")
self.__Base.logs.info(f"# CHANNEL : {self.__Config.SERVICE_CHANLOG} ")
self.__Base.logs.info(f"# VERSION : {version} ")
self.__Base.logs.info(f"################################################")
if self.__Base.check_for_new_version(False):
self.send_priv_msg(
nick_from=self.__Config.SERVICE_NICKNAME,
msg=f" New Version available {version}",
channel=self.__Config.SERVICE_CHANLOG
)
# Initialisation terminé aprés le premier PING
self.send_priv_msg(
nick_from=self.__Config.SERVICE_NICKNAME,
msg=f"[{self.__Config.COLORS.green}INFORMATION{self.__Config.COLORS.nogc}] >> Defender is ready",
channel=self.__Config.SERVICE_CHANLOG
)
self.__Config.DEFENDER_INIT = 0
# Send EOF to other modules
for classe_name, classe_object in self.__Irc.loaded_classes.items():
classe_object.cmd(server_msg_copy)
return None
except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Key Error: {ie}")
except KeyError as ke:
self.__Base.logs.error(f"{__name__} - Key Error: {ke}")
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_reputation(self, serverMsg: list[str]) -> None:
"""Handle REPUTATION coming from a server
Args:
serverMsg (list[str]): Original server message
"""
try:
# :001 REPUTATION 127.0.0.1 118
server_msg_copy = serverMsg.copy()
self.__Irc.first_connexion_ip = server_msg_copy[2]
self.__Irc.first_score = 0
if str(server_msg_copy[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.__Irc.first_score = int(str(server_msg_copy[3]).replace('*',''))
for user in self.__Irc.User.UID_DB:
if user.remote_ip == self.__Irc.first_connexion_ip:
user.score_connexion = self.first_score
else:
self.__Irc.first_score = int(server_msg_copy[3])
# Possibilité de déclancher les bans a ce niveau.
except IndexError as ie:
self.Logs.error(f'Index Error {__name__}: {ie}')
except ValueError as ve:
self.__Irc.first_score = 0
self.Logs.error(f'Value Error {__name__}: {ve}')
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_uid(self, serverMsg: list[str]) -> None:
"""Handle uid message coming from the server
@@ -630,7 +831,7 @@ class Unrealircd6:
else:
geoip = None
score_connexion = 0
score_connexion = self.__Irc.first_score
self.__Irc.User.insert(
self.__Irc.Loader.Definition.MUser(
@@ -655,6 +856,102 @@ class Unrealircd6:
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_privmsg(self, serverMsg: list[str]) -> None:
"""Handle PRIVMSG message coming from the server
Args:
serverMsg (list[str]): Original server message
"""
try:
srv_msg = serverMsg.copy()
# Supprimer la premiere valeur
if srv_msg[0].startswith('@'):
srv_msg.pop(0)
cmd = srv_msg
# Hide auth logs
if len(cmd) == 7:
if cmd[2] == 'PRIVMSG' and cmd[4] == ':auth':
data_copy = cmd.copy()
data_copy[6] = '**********'
self.__Base.logs.debug(f">> {data_copy}")
else:
self.__Base.logs.debug(f">> {cmd}")
else:
self.__Base.logs.debug(f">> {cmd}")
get_uid_or_nickname = str(cmd[0].replace(':',''))
user_trigger = self.__Irc.User.get_nickname(get_uid_or_nickname)
dnickname = self.__Config.SERVICE_NICKNAME
pattern = fr'(:\{self.__Config.SERVICE_PREFIX})(.*)$'
hcmds = search(pattern, ' '.join(cmd)) # va matcher avec tout les caractéres aprés le .
if hcmds: # Commande qui commencent par le point
liste_des_commandes = list(hcmds.groups())
convert_to_string = ' '.join(liste_des_commandes)
arg = convert_to_string.split()
arg.remove(f':{self.__Config.SERVICE_PREFIX}')
if not arg[0].lower() in self.__Irc.commands:
self.__Base.logs.debug(f"This command {arg[0]} is not available")
self.send_notice(
nick_from=self.__Config.SERVICE_NICKNAME,
nick_to=user_trigger,
msg=f"This command [{self.__Config.COLORS.bold}{arg[0]}{self.__Config.COLORS.bold}] is not available"
)
return None
cmd_to_send = convert_to_string.replace(':','')
self.__Base.log_cmd(user_trigger, cmd_to_send)
fromchannel = str(cmd[2]).lower() if self.__Irc.Channel.Is_Channel(cmd[2]) else None
self.__Irc.hcmds(user_trigger, fromchannel, arg, cmd)
if cmd[2] == self.__Config.SERVICE_ID:
pattern = fr'^:.*?:(.*)$'
hcmds = search(pattern, ' '.join(cmd))
if hcmds: # par /msg defender [commande]
liste_des_commandes = list(hcmds.groups())
convert_to_string = ' '.join(liste_des_commandes)
arg = convert_to_string.split()
# Réponse a un CTCP VERSION
if arg[0] == '\x01VERSION\x01':
self.on_version(srv_msg)
return False
# Réponse a un TIME
if arg[0] == '\x01TIME\x01':
self.on_time(srv_msg)
return False
# Réponse a un PING
if arg[0] == '\x01PING':
self.on_ping(srv_msg)
return False
if not arg[0].lower() in self.__Irc.commands:
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(user_trigger, cmd_to_send)
fromchannel = None
if len(arg) >= 2:
fromchannel = str(arg[1]).lower() if self.__Irc.Channel.Is_Channel(arg[1]) else None
self.__Irc.hcmds(user_trigger, fromchannel, arg, cmd)
return None
except KeyError as ke:
self.__Base.logs.error(f"Key Error: {ke}")
except Exception as err:
self.__Base.logs.error(f"General Error: {err}")
def on_server_ping(self, serverMsg: list[str]) -> None:
"""Send a PONG message to the server
@@ -662,7 +959,7 @@ class Unrealircd6:
serverMsg (list[str]): List of str coming from the server
"""
try:
#
pong = str(serverMsg[1]).replace(':','')
self.send2socket(f"PONG :{pong}", print_log=False)
@@ -754,7 +1051,7 @@ class Unrealircd6:
def on_version_msg(self, serverMsg: list[str]) -> None:
"""Handle version coming from the server
\n ex. /version Defender
Args:
serverMsg (list[str]): Original message from the server
"""
@@ -776,7 +1073,7 @@ class Unrealircd6:
response_005 = ' | '.join(modules)
self.send2socket(f':{self.__Config.SERVICE_HOST} 005 {getUser.nickname} {response_005} are supported by this server')
response_005 = ''.join(self.__Settings.USER_MODES)
response_005 = ''.join(self.__Settings.PROTOCTL_USER_MODES)
self.send2socket(f":{self.__Config.SERVICE_HOST} 005 {getUser.nickname} {response_005} are supported by this server")
return None

View File

@@ -11,4 +11,5 @@ class Settings:
CONSOLE: bool = False
USER_MODES: list[str] = []
PROTOCTL_USER_MODES: list[str] = []
PROTOCTL_PREFIX: list[str] = []

View File

@@ -92,7 +92,7 @@ class User:
return False
liste_umodes = list(umodes)
final_umodes_liste = [x for x in self.Base.Settings.USER_MODES if x in liste_umodes]
final_umodes_liste = [x for x in self.Base.Settings.PROTOCTL_USER_MODES if x in liste_umodes]
final_umodes = ''.join(final_umodes_liste)
userObj.umodes = f"+{final_umodes}"

View File

@@ -1,3 +1,5 @@
from ast import parse
from http import server
import sys
import socket
import threading
@@ -9,6 +11,8 @@ import traceback
from ssl import SSLSocket
from datetime import datetime, timedelta
from typing import Union
from websockets import serve
from core.loader import Loader
from core.classes.protocol import Protocol
@@ -73,10 +77,13 @@ class Irc:
self.autolimit_started: bool = False
"""This variable is to make sure the thread is not running"""
self.first_score: int = 100
# define first reputation score to 0
self.first_score: int = 0
# Define the dict that will contain all loaded modules
self.loaded_classes:dict[str, 'Irc'] = {} # Definir la variable qui contiendra la liste modules chargés
# Define the IrcSocket object
self.IrcSocket:Union[socket.socket, SSLSocket] = None
# Liste des commandes internes du bot
@@ -731,10 +738,8 @@ class Irc:
"""
try:
original_response: list[str] = data.copy()
interm_response: list[str] = data.copy()
"""This the original without first value"""
interm_response.pop(0)
if len(original_response) == 0 or len(original_response) == 1:
@@ -751,179 +756,22 @@ class Irc:
else:
self.Logs.debug(f">> {original_response}")
match original_response[0]:
parsed_protocol = self.Protocol.parse_server_msg(original_response.copy())
match parsed_protocol:
case 'PING':
# Sending PONG response to the serveur
self.Protocol.on_server_ping(original_response)
self.Protocol.on_server_ping(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
return None
case 'PROTOCTL':
#['PROTOCTL', 'CHANMODES=beI,fkL,lFH,cdimnprstzCDGKMNOPQRSTVZ', 'USERMODES=diopqrstwxzBDGHIRSTWZ', 'BOOTED=1702138935',
# 'PREFIX=(qaohv)~&@%+', 'SID=001', 'MLOCK', 'TS=1703793941', 'EXTSWHOIS']
# GET SERVER ID HOST
self.Protocol.on_protoctl(serverMsg=original_response)
return None
case _:
pass
if len(original_response) < 2:
return False
match original_response[1]:
case 'PING':
# Sending PONG response to the serveur
self.Protocol.on_server_ping(original_response)
return None
case 'SLOG':
# self.Base.scan_ports(cmd[7])
# if self.Config.ABUSEIPDB == 1:
# self.Base.create_thread(self.abuseipdb_scan, (cmd[7], ))
pass
case 'VERSION':
self.Protocol.on_version_msg(original_response)
case 'UMODE2':
# [':adator_', 'UMODE2', '-i']
self.Protocol.on_umode2(serverMsg=original_response)
case 'SQUIT':
self.Protocol.on_squit(serverMsg=original_response)
case 'REPUTATION':
# :001 REPUTATION 127.0.0.1 118
try:
self.first_connexion_ip = original_response[2]
self.first_score = 0
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(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(original_response[3])
# Possibilité de déclancher les bans a ce niveau.
except IndexError as ie:
self.Logs.error(f'{ie}')
except ValueError as ve:
self.first_score = 0
self.Logs.error(f'Impossible to convert first_score: {ve}')
case '320':
#:irc.deb.biz.st 320 PyDefender IRCParis07 :is in security-groups: known-users,webirc-users,tls-and-known-users,tls-users
pass
case '318':
#:irc.deb.biz.st 318 PyDefender IRCParis93 :End of /WHOIS list.
pass
case 'MD':
# [':001', 'MD', 'client', '001CG0TG7', 'webirc', ':2']
pass
case 'EOS':
hsid = str(original_response[0]).replace(':','')
if hsid == self.Config.HSID:
if self.Config.DEFENDER_INIT == 1:
current_version = self.Config.CURRENT_VERSION
latest_version = self.Config.LATEST_VERSION
if self.Base.check_for_new_version(False):
version = f'{current_version} >>> {latest_version}'
else:
version = f'{current_version}'
print(f"################### DEFENDER ###################")
print(f"# SERVICE CONNECTE ")
print(f"# SERVEUR : {self.Config.SERVEUR_IP} ")
print(f"# PORT : {self.Config.SERVEUR_PORT} ")
print(f"# SSL : {self.Config.SERVEUR_SSL} ")
print(f"# SSL VER : {self.Config.SSL_VERSION} ")
print(f"# NICKNAME : {self.Config.SERVICE_NICKNAME} ")
print(f"# CHANNEL : {self.Config.SERVICE_CHANLOG} ")
print(f"# VERSION : {version} ")
print(f"################################################")
self.Logs.info(f"################### DEFENDER ###################")
self.Logs.info(f"# SERVICE CONNECTE ")
self.Logs.info(f"# SERVEUR : {self.Config.SERVEUR_IP} ")
self.Logs.info(f"# PORT : {self.Config.SERVEUR_PORT} ")
self.Logs.info(f"# SSL : {self.Config.SERVEUR_SSL} ")
self.Logs.info(f"# SSL VER : {self.Config.SSL_VERSION} ")
self.Logs.info(f"# NICKNAME : {self.Config.SERVICE_NICKNAME} ")
self.Logs.info(f"# CHANNEL : {self.Config.SERVICE_CHANLOG} ")
self.Logs.info(f"# VERSION : {version} ")
self.Logs.info(f"################################################")
if self.Base.check_for_new_version(False):
self.Protocol.send_priv_msg(
nick_from=self.Config.SERVICE_NICKNAME,
msg=f" New Version available {version}",
channel=self.Config.SERVICE_CHANLOG
)
# Initialisation terminé aprés le premier PING
self.Protocol.send_priv_msg(
nick_from=self.Config.SERVICE_NICKNAME,
msg=f"[{self.Config.COLORS.green}INFORMATION{self.Config.COLORS.nogc}] >> Defender is ready",
channel=self.Config.SERVICE_CHANLOG
)
self.Config.DEFENDER_INIT = 0
# Send EOF to other modules
for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(original_response)
# Stop here When EOS
return None
case _:
pass
if len(original_response) < 3:
return False
match original_response[2]:
case 'VERSION':
self.Protocol.on_version_msg(original_response)
case 'QUIT':
self.Protocol.on_quit(serverMsg=original_response)
case 'PONG':
# ['@msgid=aTNJhp17kcPboF5diQqkUL;time=2023-12-28T20:35:58.411Z', ':irc.deb.biz.st', 'PONG', 'irc.deb.biz.st', ':Dev-PyDefender']
self.Base.execute_periodic_action()
case 'NICK':
self.Protocol.on_nick(original_response)
case 'MODE':
#['@msgid=d0ySx56Yd0nc35oHts2SkC-/J9mVUA1hfM6+Z4494xWUg;time=2024-08-09T12:45:36.651Z',
# ':001', 'MODE', '#a', '+nt', '1723207536']
# [':adator_', 'UMODE2', '-i']
pass
case 'SJOIN':
self.Protocol.on_sjoin(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'PART':
self.Protocol.on_part(serverMsg=original_response)
case 'EOS':
self.Protocol.on_eos(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'UID':
try:
@@ -932,95 +780,79 @@ class Irc:
for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
except Exception as err:
self.Logs.error(f'General Error: {err}')
case 'QUIT':
self.Protocol.on_quit(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'PROTOCTL':
self.Protocol.on_protoctl(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'SVS2MODE':
# >> [':00BAAAAAG', 'SVS2MODE', '001U01R03', '-r']
self.Protocol.on_svs2mode(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'SQUIT':
self.Protocol.on_squit(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'PART':
self.Protocol.on_part(serverMsg=parsed_protocol)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'VERSION':
self.Protocol.on_version_msg(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'UMODE2':
# [':adator_', 'UMODE2', '-i']
self.Protocol.on_umode2(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'NICK':
self.Protocol.on_nick(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'REPUTATION':
self.Protocol.on_reputation(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'SLOG': # TODO
self.Logs.debug(f"** handle {parsed_protocol}")
case 'MD': # TODO
self.Logs.debug(f"** handle {parsed_protocol}")
case 'PRIVMSG':
try:
# Supprimer la premiere valeur
cmd = interm_response.copy()
self.Protocol.on_privmsg(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
get_uid_or_nickname = str(cmd[0].replace(':',''))
user_trigger = self.User.get_nickname(get_uid_or_nickname)
dnickname = self.Config.SERVICE_NICKNAME
case 'PONG': # TODO
self.Logs.debug(f"** handle {parsed_protocol}")
if len(cmd) == 6:
if cmd[1] == 'PRIVMSG' and str(cmd[3]).replace(self.Config.SERVICE_PREFIX,'') == ':auth':
cmd_copy = cmd.copy()
cmd_copy[5] = '**********'
self.Logs.info(f'>> {cmd_copy}')
else:
self.Logs.info(f'>> {cmd}')
else:
self.Logs.info(f'>> {cmd}')
case 'MODE': # TODO
#['@msgid=d0ySx56Yd0nc35oHts2SkC-/J9mVUA1hfM6...', ':001', 'MODE', '#a', '+nt', '1723207536']
#['@unrealircd.org/userhost=adator@localhost;...', ':001LQ0L0C', 'MODE', '#services', '-l']
self.Logs.debug(f"** handle {parsed_protocol}")
pattern = fr'(:\{self.Config.SERVICE_PREFIX})(.*)$'
hcmds = re.search(pattern, ' '.join(cmd)) # va matcher avec tout les caractéres aprés le .
case '320': # TODO
#:irc.deb.biz.st 320 PyDefender IRCParis07 :is in security-groups: known-users,webirc-users,tls-and-known-users,tls-users
self.Logs.debug(f"** handle {parsed_protocol}")
if hcmds: # Commande qui commencent par le point
liste_des_commandes = list(hcmds.groups())
convert_to_string = ' '.join(liste_des_commandes)
arg = convert_to_string.split()
arg.remove(f':{self.Config.SERVICE_PREFIX}')
if not arg[0].lower() in self.commands:
self.Logs.debug(f"This command {arg[0]} is not available")
self.Protocol.send_notice(
nick_from=self.Config.SERVICE_NICKNAME,
nick_to=user_trigger,
msg=f"This command [{self.Config.COLORS.bold}{arg[0]}{self.Config.COLORS.bold}] is not available"
)
return None
case '318': # TODO
#:irc.deb.biz.st 318 PyDefender IRCParis93 :End of /WHOIS list.
self.Logs.debug(f"** handle {parsed_protocol}")
cmd_to_send = convert_to_string.replace(':','')
self.Base.log_cmd(user_trigger, cmd_to_send)
fromchannel = str(cmd[2]).lower() if self.Channel.Is_Channel(cmd[2]) else None
self._hcmds(user_trigger, fromchannel, arg, cmd)
if cmd[2] == self.Config.SERVICE_ID:
pattern = fr'^:.*?:(.*)$'
hcmds = re.search(pattern, ' '.join(cmd))
if hcmds: # par /msg defender [commande]
liste_des_commandes = list(hcmds.groups())
convert_to_string = ' '.join(liste_des_commandes)
arg = convert_to_string.split()
# Réponse a un CTCP VERSION
if arg[0] == '\x01VERSION\x01':
self.Protocol.on_version(original_response)
return False
# Réponse a un TIME
if arg[0] == '\x01TIME\x01':
self.Protocol.on_time(original_response)
return False
# Réponse a un PING
if arg[0] == '\x01PING':
self.Protocol.on_ping(original_response)
return False
if not arg[0].lower() in self.commands:
self.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(user_trigger, cmd_to_send)
fromchannel = None
if len(arg) >= 2:
fromchannel = str(arg[1]).lower() if self.Channel.Is_Channel(arg[1]) else None
self._hcmds(user_trigger, fromchannel, arg, cmd)
except IndexError as io:
self.Logs.error(f'{io}')
case _:
pass
case None:
self.Logs.debug(f"** TO BE HANDLE {original_response}")
if len(original_response) > 2:
if original_response[2] != 'UID':
# Envoyer la commande aux classes dynamiquement chargées
for classe_name, classe_object in self.loaded_classes.items():
@@ -1032,8 +864,8 @@ class Irc:
self.Logs.error(f"General Error: {err}")
self.Logs.error(f"General Error: {traceback.format_exc()}")
def _hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None:
"""_summary_
def hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None:
"""Create
Args:
user (str): The user who sent the query
@@ -1064,7 +896,7 @@ class Irc:
# Envoyer la commande aux classes dynamiquement chargées
if command != 'notallowed':
for classe_name, classe_object in self.loaded_classes.items():
classe_object._hcmds(user, channel, cmd, fullcmd)
classe_object.hcmds(user, channel, cmd, fullcmd)
match command:
@@ -1184,6 +1016,10 @@ class Irc:
case 'auth':
# ['auth', 'adator', 'password']
if len(cmd) != 3:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} [nickname] [password]")
return None
current_command = cmd[0]
user_to_log = self.User.get_nickname(cmd[1])
password = cmd[2]
@@ -1241,14 +1077,14 @@ class Irc:
case 'editaccess':
# .editaccess [USER] [PASSWORD] [LEVEL]
try:
user_to_edit = cmd[1]
user_new_level = int(cmd[3])
user_password = self.Base.crypt_password(cmd[2])
if len(cmd) < 4 or len(cmd) > 4:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{self.Config.SERVICE_PREFIX}editaccess [USER] [NEWPASSWORD] [NEWLEVEL]")
if len(cmd) < 3:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Right command : /msg {dnickname} editaccess [nickname] [NEWPASSWORD] [NEWLEVEL]")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"level: from 1 to 4")
return None
user_to_edit = cmd[1]
user_password = self.Base.crypt_password(cmd[2])
get_admin = self.Admin.get_Admin(fromuser)
if get_admin is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"This user {fromuser} has no Admin access")
@@ -1258,8 +1094,13 @@ class Irc:
current_uid = self.User.get_uid(fromuser)
current_user_level = get_admin.level
user_new_level = int(cmd[3]) if len(cmd) == 4 else get_admin.level
if current_user == fromuser:
user_new_level = get_admin.level
if user_new_level > 5:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Maximum authorized level is 5")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="Maximum authorized level is 5")
return None
# Rechercher le user dans la base de données.
@@ -1271,7 +1112,7 @@ class Irc:
if not isUserExist is None:
if current_user_level < int(isUserExist[1]):
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" You are not allowed to edit this access")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You are not allowed to edit this access")
return None
if current_user_level == int(isUserExist[1]) and current_user != user_to_edit:
@@ -1615,7 +1456,7 @@ class Irc:
self.Protocol.send_notice(
nick_from=dnickname,
nick_to=fromuser,
msg=f'{key} > {value}'
msg=f'{key} = {value}'
)
case 'uptime':

View File

@@ -1,5 +1,4 @@
from core.classes import user, admin, channel, clone, reputation, settings
import utils
import core.definition as df
import core.base as baseModule
import core.classes.config as confModule
@@ -15,8 +14,6 @@ class Loader:
self.BaseModule: baseModule = baseModule
self.Utils: utils
# Load Classes
self.Settings: settings = settings.Settings()

View File

@@ -306,7 +306,7 @@ class Clone():
except Exception as err:
self.Logs.error(f'General Error: {err}')
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
try:
command = str(cmd[0]).lower()

View File

@@ -1,10 +1,12 @@
from typing import TYPE_CHECKING
from dataclasses import dataclass, fields
from typing import Union, TYPE_CHECKING
from dataclasses import dataclass
if TYPE_CHECKING:
from core.irc import Irc
from core.definition import MUser
from sqlalchemy import CursorResult, Row, Sequence
class Command():
class Command:
@dataclass
class ModConfModel:
@@ -20,6 +22,9 @@ class Command():
# Add Irc Object to the module (Mandatory)
self.Irc = ircInstance
# Add Loader Object to the module (Mandatory)
self.Loader = ircInstance.Loader
# Add Protocol object to the module (Mandatory)
self.Protocol = ircInstance.Protocol
@@ -42,12 +47,12 @@ class Command():
self.commands_level = {
1: ['join', 'part','owner', 'deowner', 'protect', 'deprotect', 'op',
'deop', 'halfop', 'dehalfop', 'voice','devoice', 'topic'],
2: ['opall', 'deopall', 'devoiceall', 'voiceall', 'ban',
'unban','kick', 'kickban', 'umode', 'mode', 'get_mode', 'svsjoin', 'svspart', 'svsnick',
2: ['opall', 'deopall', 'devoiceall', 'voiceall', 'ban', 'automode',
'unban', 'kick', 'kickban', 'umode',
'mode', 'get_mode', 'svsjoin', 'svspart', 'svsnick',
'wallops', 'globops','gnotice','whois', 'names', 'invite', 'inviteme',
'sajoin', 'sapart',
'kill', 'gline', 'ungline', 'kline', 'unkline', 'shun', 'unshun',
'glinelist', 'shunlist', 'klinelist'],
'sajoin', 'sapart', 'kill', 'gline', 'ungline', 'kline',
'unkline', 'shun', 'unshun', 'glinelist', 'shunlist', 'klinelist'],
3: ['map']
}
@@ -82,7 +87,7 @@ class Command():
"""
for level, com in commands.items():
for c in commands[level]:
if not c in self.Irc.commands:
if c not in self.Irc.commands:
self.Irc.commands_level[level].append(c)
self.Irc.commands.append(c)
@@ -98,14 +103,17 @@ class Command():
None: Aucun retour n'es attendu
"""
table_logs = '''CREATE TABLE IF NOT EXISTS test_logs (
table_automode = '''CREATE TABLE IF NOT EXISTS command_automode (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
server_msg TEXT
created_on TEXT,
updated_on TEXT,
nickname TEXT,
channel TEXT,
mode TEXT
)
'''
# self.Base.db_execute_query(table_logs)
self.Base.db_execute_query(table_automode)
return None
def __load_module_configuration(self) -> None:
@@ -136,11 +144,11 @@ class Command():
return None
def cmd(self, data: list) -> None:
service_id = self.Config.SERVICE_ID
def cmd(self, data: list[str]) -> None:
try:
# service_id = self.Config.SERVICE_ID
dnickname = self.Config.SERVICE_NICKNAME
dchanlog = self.Config.SERVICE_CHANLOG
# dchanlog = self.Config.SERVICE_CHANLOG
red = self.Config.COLORS.red
green = self.Config.COLORS.green
bold = self.Config.COLORS.bold
@@ -224,19 +232,146 @@ class Command():
case _:
pass
if len(cmd) < 3:
return None
def _hcmds(self, user: str, channel: any, cmd: list, fullcmd: list = []) -> None:
match cmd[2]:
case 'SJOIN':
# ['@msgid=yldTlbwAGbzCGUcCIHi3ku;time=2024-11-11T17:56:24.297Z', ':001', 'SJOIN', '1728815963', '#znc', ':001LQ0L0C']
# Check if the user has an automode
try:
if len(cmd) < 6:
return None
user_uid = self.User.clean_uid(cmd[5])
userObj: MUser = self.User.get_User(user_uid)
channel_name = cmd[4] if self.Channel.Is_Channel(cmd[4]) else None
if userObj is None:
return None
if 'r' not in userObj.umodes and 'o' not in userObj.umodes:
return None
db_data: dict[str, str] = {"nickname": userObj.nickname, "channel": channel_name}
db_query = self.Base.db_execute_query("SELECT id, mode FROM command_automode WHERE nickname = :nickname AND channel = :channel", db_data)
db_result = db_query.fetchone()
if db_result is not None:
id, mode = db_result
self.Protocol.send2socket(f":{self.Config.SERVICE_ID} MODE {channel_name} {mode} {userObj.nickname}")
except KeyError as ke:
self.Logs.error(f"Key Error: {err}")
except Exception as err:
self.Logs.error(f"General Error: {err}")
def hcmds(self, uidornickname: str, channel_name: Union[str, None], cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower()
dnickname = self.Config.SERVICE_NICKNAME
service_id = self.Config.SERVICE_ID
dchanlog = self.Config.SERVICE_CHANLOG
self.user_to_notice = user
fromuser = user
fromchannel = channel
self.user_to_notice = uidornickname
fromuser = uidornickname
fromchannel = channel_name
match command:
case "automode":
# automode set nickname [+/-mode] #channel
# automode set adator +o #channel
try:
option: str = str(cmd[1]).lower()
match option:
case 'set':
allowed_modes: list[str] = self.Base.Settings.PROTOCTL_PREFIX # ['q','a','o','h','v']
if len(cmd) < 5:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} [nickname] [+/-mode] [#channel]")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"AutoModes available: {' / '.join(allowed_modes)}")
return None
# userObj: MUser = self.User.get_User(str(cmd[2]))
nickname = str(cmd[2])
mode = str(cmd[3])
chan: str = str(cmd[4]).lower() if self.Channel.Is_Channel(cmd[4]) else None
sign = mode[0] if mode.startswith( ('+', '-')) else None
clean_mode = mode[1:] if len(mode) > 0 else None
if sign is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You must provide the flag mode + or -")
return None
if clean_mode not in allowed_modes:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You should use one of those modes {' / '.join(allowed_modes)}")
return None
if chan is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You should use one of those modes {' / '.join(allowed_modes)}")
return None
db_data: dict[str, str] = {"nickname": nickname, "channel": chan}
db_query = self.Base.db_execute_query(query="SELECT id FROM command_automode WHERE nickname = :nickname and channel = :channel", params=db_data)
db_result = db_query.fetchone()
if db_result is not None:
if sign == '+':
db_data = {"updated_on": self.Base.get_datetime(), "nickname": nickname, "channel": chan, "mode": mode}
db_result = self.Base.db_execute_query(query="UPDATE command_automode SET mode = :mode, updated_on = :updated_on WHERE nickname = :nickname and channel = :channel",
params=db_data)
if db_result.rowcount > 0:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Automode {mode} edited for {nickname} in {chan}")
elif sign == '-':
db_data = {"nickname": nickname, "channel": chan, "mode": f"+{clean_mode}"}
db_result = self.Base.db_execute_query(query="DELETE FROM command_automode WHERE nickname = :nickname and channel = :channel and mode = :mode",
params=db_data)
if db_result.rowcount > 0:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Automode {mode} deleted for {nickname} in {chan}")
else:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"The mode [{mode}] has not been found for {nickname} in channel {chan}")
return None
# Instert a new automode
if sign == '+':
db_data = {"created_on": self.Base.get_datetime(), "updated_on": self.Base.get_datetime(), "nickname": nickname, "channel": chan, "mode": mode}
db_query = self.Base.db_execute_query(
query="INSERT INTO command_automode (created_on, updated_on, nickname, channel, mode) VALUES (:created_on, :updated_on, :nickname, :channel, :mode)",
params=db_data
)
if db_query.rowcount > 0:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Automode {mode} applied to {nickname} in {chan}")
if self.Channel.is_user_present_in_channel(chan, self.User.get_uid(nickname)):
self.Protocol.send2socket(f":{service_id} MODE {chan} {mode} {nickname}")
else:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"AUTOMODE {mode} cannot be added to {nickname} in {chan} because it doesn't exist")
case 'list':
db_query: CursorResult = self.Base.db_execute_query("SELECT nickname, channel, mode FROM command_automode")
db_results: Sequence[Row] = db_query.fetchall()
if not db_results:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,
msg="There is no automode to display.")
for db_result in db_results:
db_nickname, db_channel, db_mode = db_result
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,
msg=f"Nickname: {db_nickname} | Channel: {db_channel} | Mode: {db_mode}")
case _:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} SET [nickname] [+/-mode] [#channel]")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} LIST")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"[AUTOMODES AVAILABLE] are {' / '.join(allowed_modes)}")
except IndexError:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} SET [nickname] [+/-mode] [#channel]")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} LIST")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"[AUTOMODES AVAILABLE] are {' / '.join(self.Base.Settings.PROTOCTL_PREFIX)}")
except Exception as err:
self.Logs.error(f"General Error: {err}")
case 'deopall':
try:
@@ -696,7 +831,7 @@ class Command():
chan = str(cmd[1])
if not self.Channel.Is_Channel(chan):
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"The channel must start with #")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="The channel must start with #")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} TOPIC #channel THE_TOPIC_MESSAGE")
return None
@@ -705,7 +840,7 @@ class Command():
if topic_msg:
self.Protocol.send2socket(f':{dnickname} TOPIC {chan} :{topic_msg}')
else:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You need to specify the topic")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You need to specify the topic")
except KeyError as ke:
self.Logs.error(ke)
@@ -723,7 +858,7 @@ class Command():
if wallops_msg:
self.Protocol.send2socket(f':{dnickname} WALLOPS {wallops_msg} ({dnickname})')
else:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You need to specify the wallops message")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You need to specify the wallops message")
except KeyError as ke:
self.Logs.error(ke)
@@ -741,7 +876,7 @@ class Command():
if globops_msg:
self.Protocol.send2socket(f':{dnickname} GLOBOPS {globops_msg} ({dnickname})')
else:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You need to specify the globops message")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You need to specify the globops message")
except KeyError as ke:
self.Logs.error(ke)
@@ -759,7 +894,7 @@ class Command():
if gnotice_msg:
self.Protocol.send_notice(nick_from=dnickname, nick_to='$*.*', msg=f"[{self.Config.COLORS.red}GLOBAL NOTICE{self.Config.COLORS.nogc}] {gnotice_msg}")
else:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You need to specify the global notice message")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You need to specify the global notice message")
except KeyError as ke:
self.Logs.error(ke)
@@ -776,7 +911,7 @@ class Command():
nickname = str(cmd[1])
if self.User.get_nickname(nickname) is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nickname not found !")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="Nickname not found !")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} NICKNAME")
return None
@@ -796,7 +931,7 @@ class Command():
chan = str(cmd[1])
if not self.Channel.Is_Channel(chan):
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"The channel must start with #")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="The channel must start with #")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} #channel")
return None
@@ -817,12 +952,12 @@ class Command():
chan = str(cmd[2])
if not self.Channel.Is_Channel(chan):
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"The channel must start with #")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="The channel must start with #")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} NICKNAME #CHANNEL")
return None
if self.User.get_nickname(nickname) is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nickname not found !")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="Nickname not found !")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} NICKNAME #CHANNEL")
return None
@@ -906,8 +1041,10 @@ class Command():
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'get_mode':
self.Protocol.send2socket(f'MODE {channel}')
try:
self.Protocol.send2socket(f'MODE {fromchannel}')
except Exception as err:
self.Logs.error(f"General Error {err}")
case 'svsjoin':
try:
@@ -927,7 +1064,7 @@ class Command():
case 'svspart':
try:
# .svspart nickname #channel
# svspart nickname #channel
nickname = str(cmd[1])
channel = str(cmd[2])
if len(cmd) != 3:

View File

@@ -4,10 +4,9 @@ import time
import re
import psutil
import requests
from dataclasses import dataclass, fields, field
from dataclasses import dataclass
from datetime import datetime
from typing import Union, TYPE_CHECKING
from sys import exit
import core.definition as df
# Le module crée devra réspecter quelques conditions
@@ -19,7 +18,7 @@ import core.definition as df
# 4 . Créer vos tables, en utilisant toujours le nom des votre classe en minuscule ==> defender_votre-table
# 3. Methode suivantes:
# cmd(self, data:list)
# _hcmds(self, user:str, cmd: list)
# hcmds(self, user:str, cmd: list)
# unload(self)
if TYPE_CHECKING:
@@ -141,6 +140,8 @@ class Defender():
self.Base.create_thread(func=self.thread_local_scan)
self.Base.create_thread(func=self.thread_psutil_scan)
self.Base.create_thread(func=self.thread_reputation_timer)
if self.ModConfig.autolimit == 1:
self.Base.create_thread(func=self.thread_autolimit)
if self.ModConfig.reputation == 1:
@@ -157,7 +158,7 @@ class Defender():
"""
for level, com in commands.items():
for c in commands[level]:
if not c in self.Irc.commands:
if c not in self.Irc.commands:
self.Irc.commands_level[level].append(c)
self.Irc.commands.append(c)
@@ -173,31 +174,15 @@ class Defender():
None: Aucun retour n'es attendu
"""
table_channel = '''CREATE TABLE IF NOT EXISTS def_channels (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
channel TEXT
)
'''
# table_autoop = '''CREATE TABLE IF NOT EXISTS defender_autoop (
# id INTEGER PRIMARY KEY AUTOINCREMENT,
# datetime TEXT,
# nickname TEXT,
# channel TEXT
# )
# '''
table_config = '''CREATE TABLE IF NOT EXISTS def_config (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
parameter TEXT,
value TEXT
)
'''
table_trusted = '''CREATE TABLE IF NOT EXISTS def_trusted (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
user TEXT,
host TEXT,
vhost TEXT
)
'''
# self.Base.db_execute_query(table_channel)
# self.Base.db_execute_query(table_autoop)
# self.Base.db_execute_query(table_config)
# self.Base.db_execute_query(table_trusted)
return None
@@ -255,7 +240,7 @@ class Defender():
exec_query = self.Base.db_execute_query(query, {"user": nickname})
response = exec_query.fetchone()
if not response is None:
if response is not None:
q_insert = "INSERT INTO def_trusted (datetime, user, host, vhost) VALUES (?, ?, ?, ?)"
mes_donnees = {'datetime': self.Base.get_datetime(), 'user': nickname, 'host': '*', 'vhost': '*'}
exec_query = self.Base.db_execute_query(q_insert, mes_donnees)
@@ -301,7 +286,8 @@ class Defender():
# Convertir la date enregistrée dans UID_DB en un objet {datetime}
connected_time_string = get_user.connexion_datetime
if type(connected_time_string) == datetime:
if isinstance(connected_time_string, datetime):
connected_time = connected_time_string
else:
connected_time = datetime.strptime(connected_time_string, "%Y-%m-%d %H:%M:%S.%f")
@@ -386,7 +372,6 @@ class Defender():
service_id = self.Config.SERVICE_ID
dchanlog = self.Config.SERVICE_CHANLOG
color_red = self.Config.COLORS.red
color_black = self.Config.COLORS.black
nogc = self.Config.COLORS.nogc
salon_jail = self.Config.SALON_JAIL
@@ -480,7 +465,7 @@ class Defender():
unixtime = self.Base.get_unixtime()
get_diff_secondes = 0
if not get_detected_uid in self.flood_system:
if get_detected_uid not in self.flood_system:
self.flood_system[get_detected_uid] = {
'nbr_msg': 0,
'first_msg_time': unixtime
@@ -694,7 +679,7 @@ class Defender():
# Formatted output
decodedResponse = json.loads(response.text)
if not 'data' in decodedResponse:
if 'data' not in decodedResponse:
return None
result = {
@@ -707,7 +692,6 @@ class Defender():
service_id = self.Config.SERVICE_ID
service_chanlog = self.Config.SERVICE_CHANLOG
color_red = self.Config.COLORS.red
color_black = self.Config.COLORS.black
nogc = self.Config.COLORS.nogc
# pseudo!ident@host
@@ -780,7 +764,6 @@ class Defender():
service_id = self.Config.SERVICE_ID
service_chanlog = self.Config.SERVICE_CHANLOG
color_red = self.Config.COLORS.red
color_black = self.Config.COLORS.black
nogc = self.Config.COLORS.nogc
url = f'https://freeipapi.com/api/json/{remote_ip}'
@@ -797,7 +780,7 @@ class Defender():
status_code = response.status_code
if status_code == 429:
self.Logs.warning(f'Too Many Requests - The rate limit for the API has been exceeded.')
self.Logs.warning('Too Many Requests - The rate limit for the API has been exceeded.')
return None
elif status_code != 200:
self.Logs.warning(f'status code = {str(status_code)}')
@@ -873,10 +856,9 @@ class Defender():
service_id = self.Config.SERVICE_ID
service_chanlog = self.Config.SERVICE_CHANLOG
color_red = self.Config.COLORS.red
color_black = self.Config.COLORS.black
nogc = self.Config.COLORS.nogc
url = f"https://developers18334.cloudfilt.com/"
url = "https://developers18334.cloudfilt.com/"
data = {
'ip': remote_ip,
@@ -941,7 +923,7 @@ class Defender():
def thread_autolimit(self) -> None:
if self.ModConfig.autolimit == 0:
self.Logs.debug(f"autolimit deactivated ... canceling the thread")
self.Logs.debug("autolimit deactivated ... canceling the thread")
return None
while self.Irc.autolimit_started:
@@ -958,7 +940,7 @@ class Defender():
while self.autolimit_isRunning:
if self.ModConfig.autolimit == 0:
self.Logs.debug(f"autolimit deactivated ... stopping the current thread")
self.Logs.debug("autolimit deactivated ... stopping the current thread")
break
for chan in self.Channel.UID_CHANNEL_DB:
@@ -967,14 +949,14 @@ class Defender():
self.Protocol.send2socket(f":{self.Config.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + self.ModConfig.autolimit_amount}")
chan_copy["uids_count"] = len(chan.uids)
if not chan.name in chan_list:
if chan.name not in chan_list:
chan_list.append(chan.name)
chanObj_copy.append({"name": chan.name, "uids_count": 0})
# Verifier si un salon a été vidé
current_chan_in_list = [d.name for d in self.Channel.UID_CHANNEL_DB]
for c in chan_list:
if not c in current_chan_in_list:
if c not in current_chan_in_list:
chan_list.remove(c)
# Si c'est la premiere execution
@@ -1008,7 +990,7 @@ class Defender():
match cmd[1]:
case 'REPUTATION':
# :001 REPUTATION 91.168.141.239 118
# :001 REPUTATION 8.8.8.8 118
try:
self.reputation_first_connexion['ip'] = cmd[2]
self.reputation_first_connexion['score'] = cmd[3]
@@ -1059,12 +1041,12 @@ class Defender():
_User = self.User.get_User(str(cmd[7]))
# If user is not service or IrcOp then scan them
if not re.match(fr'^.*[S|o?].*$', _User.umodes):
self.abuseipdb_UserModel.append(_User) if self.ModConfig.abuseipdb_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.freeipapi_UserModel.append(_User) if self.ModConfig.freeipapi_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.cloudfilt_UserModel.append(_User) if self.ModConfig.cloudfilt_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.psutil_UserModel.append(_User) if self.ModConfig.psutil_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.localscan_UserModel.append(_User) if self.ModConfig.local_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
if not re.match(r'^.*[S|o?].*$', _User.umodes):
self.abuseipdb_UserModel.append(_User) if self.ModConfig.abuseipdb_scan == 1 and _User.remote_ip not in self.Config.WHITELISTED_IP else None
self.freeipapi_UserModel.append(_User) if self.ModConfig.freeipapi_scan == 1 and _User.remote_ip not in self.Config.WHITELISTED_IP else None
self.cloudfilt_UserModel.append(_User) if self.ModConfig.cloudfilt_scan == 1 and _User.remote_ip not in self.Config.WHITELISTED_IP else None
self.psutil_UserModel.append(_User) if self.ModConfig.psutil_scan == 1 and _User.remote_ip not in self.Config.WHITELISTED_IP else None
self.localscan_UserModel.append(_User) if self.ModConfig.local_scan == 1 and _User.remote_ip not in self.Config.WHITELISTED_IP else None
if _User is None:
self.Logs.critical(f'This UID: [{cmd[7]}] is not available please check why')
@@ -1075,9 +1057,9 @@ class Defender():
if self.Config.DEFENDER_INIT == 0:
# Si le user n'es pas un service ni un IrcOP
if not re.match(fr'^.*[S|o?].*$', _User.umodes):
if not re.match(r'^.*[S|o?].*$', _User.umodes):
if reputation_flag == 1 and _User.score_connexion <= reputation_seuil:
currentDateTime = self.Base.get_datetime()
# currentDateTime = self.Base.get_datetime()
self.Reputation.insert(
self.Loader.Definition.MReputation(
**_User.__dict__,
@@ -1107,7 +1089,7 @@ class Defender():
self.Protocol.send2socket(f":{service_id} MODE {parsed_chan} +b ~security-group:unknown-users")
self.Protocol.send2socket(f":{service_id} MODE {parsed_chan} +eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users")
if not get_reputation is None:
if get_reputation is not None:
isWebirc = get_reputation.isWebirc
if not isWebirc:
@@ -1187,7 +1169,7 @@ class Defender():
get_user_reputation = self.Reputation.get_Reputation(final_UID)
if not get_user_reputation is None:
if get_user_reputation is not None:
final_nickname = get_user_reputation.nickname
for chan in self.Channel.UID_CHANNEL_DB:
if chan.name != jail_salon and ban_all_chan == 1:
@@ -1201,7 +1183,7 @@ class Defender():
except Exception as err:
self.Logs.error(f"General Error: {err}")
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower()
fromuser = user
@@ -1236,7 +1218,7 @@ class Defender():
for suspect in self.Reputation.UID_REPUTATION_DB:
self.Protocol.send_notice(nick_from=dnickname,
nick_to=fromuser,
msg=f" Uid: {suspect.uid} | Nickname: {suspect.nickname} | Reputation: {suspect.score_connexion} | Secret code: {suspect.secret_code} | Connected on: {suspect.connected_datetime}")
msg=f" Uid: {suspect.uid} | Nickname: {suspect.nickname} | Reputation: {suspect.score_connexion} | Secret code: {suspect.secret_code} | Connected on: {suspect.connexion_datetime}")
case 'code':
try:
@@ -1299,6 +1281,10 @@ class Defender():
try:
# autolimit on
# autolimit set [amount] [interval]
if len(cmd) < 2:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} ON")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} SET [AMOUNT] [INTERVAL]")
return None
arg = str(cmd[1]).lower()
@@ -1332,12 +1318,12 @@ class Defender():
)
case _:
self.Protocol.send_notice(nick_from=dnickname, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} ON", nickname=fromuser)
self.Protocol.send_notice(nick_from=dnickname, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} SET [AMOUNT] [INTERVAL]", nickname=fromuser)
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} ON")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} SET [AMOUNT] [INTERVAL]")
except Exception as err:
self.Protocol.send_notice(nick_from=dnickname, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} ON", nickname=fromuser)
self.Protocol.send_notice(nick_from=dnickname, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} SET [AMOUNT] [INTERVAL]", nickname=fromuser)
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} ON")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} SET [AMOUNT] [INTERVAL]")
self.Logs.error(f"Value Error -> {err}")
case 'reputation':
@@ -1522,7 +1508,7 @@ class Defender():
except ValueError as ve:
self.Logs.warning(f'{ve}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" La valeur devrait etre un entier >= 0")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=" La valeur devrait etre un entier >= 0")
case 'proxy_scan':
@@ -1729,14 +1715,14 @@ class Defender():
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' reputation_after_release ==> {self.ModConfig.reputation_score_after_release}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' reputation_ban_all_chan ==> {self.ModConfig.reputation_ban_all_chan}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' reputation_timer ==> {self.ModConfig.reputation_timer}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' [Proxy_scan]')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=' [Proxy_scan]')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.local_scan == 1 else color_red}local_scan{nogc} ==> {self.ModConfig.local_scan}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.psutil_scan == 1 else color_red}psutil_scan{nogc} ==> {self.ModConfig.psutil_scan}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.abuseipdb_scan == 1 else color_red}abuseipdb_scan{nogc} ==> {self.ModConfig.abuseipdb_scan}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.freeipapi_scan == 1 else color_red}freeipapi_scan{nogc} ==> {self.ModConfig.freeipapi_scan}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.cloudfilt_scan == 1 else color_red}cloudfilt_scan{nogc} ==> {self.ModConfig.cloudfilt_scan}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' [{color_green if self.ModConfig.flood == 1 else color_red}Flood{nogc}] ==> {self.ModConfig.flood}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_action ==> Coming soon')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=' flood_action ==> Coming soon')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_message ==> {self.ModConfig.flood_message}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_time ==> {self.ModConfig.flood_time}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_timer ==> {self.ModConfig.flood_timer}')
@@ -1748,7 +1734,7 @@ class Defender():
nickoruid = cmd[1]
UserObject = self.User.get_User(nickoruid)
if not UserObject is None:
if UserObject is not None:
channels: list = []
for chan in self.Channel.UID_CHANNEL_DB:
for uid_in_chan in chan.uids:
@@ -1784,10 +1770,10 @@ class Defender():
if activation == 'on':
for chan in self.Channel.UID_CHANNEL_DB:
if not chan.name in channel_to_dont_quit:
if chan.name not in channel_to_dont_quit:
self.Protocol.send_join_chan(uidornickname=dnickname, channel=chan.name)
if activation == 'off':
for chan in self.Channel.UID_CHANNEL_DB:
if not chan.name in channel_to_dont_quit:
if chan.name not in channel_to_dont_quit:
self.Protocol.part(uidornickname=dnickname, channel=chan.name)
self.join_saved_channels()

View File

@@ -57,6 +57,7 @@ class Jsonrpc():
# Insert module commands into the core one (Mandatory)
self.__set_commands(self.commands_level)
logging.getLogger('websockets').setLevel(logging.WARNING)
logging.getLogger('unrealircd-rpc-py').setLevel(logging.CRITICAL)
# Create you own tables (Mandatory)
# self.__create_tables()
@@ -65,13 +66,6 @@ class Jsonrpc():
self.__load_module_configuration()
# End of mandatory methods you can start your customization #
# self.UnrealIrcdRpcLive: Live = Live(
# req_method='unixsocket',
# path_to_socket_file=self.Config.JSONRPC_PATH_TO_SOCKET_FILE,
# callback_object_instance=self,
# callback_method_name='callback_sent_to_irc'
# )
self.UnrealIrcdRpcLive: Live = Live(
req_method='websocket',
url=self.Config.JSONRPC_URL,
@@ -214,7 +208,7 @@ class Jsonrpc():
return None
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower()
dnickname = self.Config.SERVICE_NICKNAME
@@ -235,6 +229,11 @@ class Jsonrpc():
match option:
case 'on':
# for logger_name, logger in logging.root.manager.loggerDict.items():
# if isinstance(logger, logging.Logger):
# self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{logger_name} - {logger.level}")
for thread in self.Base.running_threads:
if thread.getName() == 'thread_start_jsonrpc':
if thread.is_alive():

View File

@@ -147,7 +147,7 @@ class Test():
except Exception as err:
self.Logs.error(f"General Error: {err}")
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower()
dnickname = self.Config.SERVICE_NICKNAME

View File

@@ -274,7 +274,7 @@ class Votekick():
except Exception as err:
self.Logs.error(f"General Error: {err}")
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
# cmd is the command starting from the user command
# full cmd is sending the entire server response

View File

@@ -1,9 +1,9 @@
{
"version": "6.0.0",
"version": "6.0.4",
"requests": "2.32.3",
"psutil": "6.0.0",
"unrealircd_rpc_py": "1.0.6",
"unrealircd_rpc_py": "1.0.7",
"sqlalchemy": "2.0.35",
"faker": "30.1.0"
}