This commit is contained in:
adator
2024-09-22 16:20:02 +02:00
parent d7503768b6
commit ff603ab2a4
9 changed files with 204 additions and 151 deletions

View File

@@ -1,4 +1,6 @@
import socket, ssl
import socket
import ssl
import traceback
from ssl import SSLSocket
from core.loadConf import Config
from core.Model import Clones
@@ -15,7 +17,8 @@ class Connection:
self.nickname = nickname
self.username = username
self.realname = realname
self.clone_chanlog = self.Config.SALON_CLONES
self.clone_chanlog = self.Config.CLONE_CHANNEL
self.clone_log_exempt = self.Config.CLONE_LOG_HOST_EXEMPT
self.channels:list[str] = channels
self.CHARSET = ['utf-8', 'iso-8859-1']
self.Clones = CloneObject
@@ -157,6 +160,7 @@ class Connection:
self.Base.logs.error(f"OS Error __connect_to_irc: {soe}")
except AttributeError as atte:
self.Base.logs.critical(f"{atte}")
self.Base.logs.critical(f"{traceback.format_exc()}")
except Exception as e:
self.Base.logs.error(f"Exception: {e}")
@@ -190,9 +194,10 @@ class Connection:
for channel in self.channels:
self.send2socket(f"JOIN {channel}")
self.send2socket(f"JOIN {self.clone_chanlog}")
self.send2socket(f"JOIN {self.clone_chanlog} {self.Config.CLONE_CHANNEL_PASSWORD}")
return None
case '422':
# Missing MOTD
self.currentCloneObject.connected = True
@@ -200,7 +205,15 @@ class Connection:
for channel in self.channels:
self.send2socket(f"JOIN {channel}")
self.send2socket(f"JOIN {self.clone_chanlog}")
self.send2socket(f"JOIN {self.clone_chanlog} {self.Config.CLONE_CHANNEL_PASSWORD}")
return None
case '433':
# Nickname already in use
self.currentCloneObject.connected = False
self.currentCloneObject.init = False
self.send2socket(f'QUIT :Thanks and goodbye')
self.Base.logs.warning(f"Nickname {self.currentCloneObject.nickname} already in use >> Clone should be disconnected")
return None
case 'PRIVMSG':
@@ -214,13 +227,20 @@ class Connection:
for i in range(3, len(response)):
message.append(response[i])
final_message = ' '.join(message)
self.send2socket(f"PRIVMSG {self.clone_chanlog} :{fullname} => {final_message[1:]}")
exampt = False
for log_exception in self.clone_log_exempt:
if log_exception in fullname:
exampt = True
if not exampt:
self.send2socket(f"PRIVMSG {self.clone_chanlog} :{fullname} => {final_message[1:]}")
if nickname == self.Config.SERVICE_NICKNAME:
command = str(response[3]).replace(':','')
if command == 'KILL':
self.send2socket(f'QUIT :Thanks and goodbye', disconnect=True)
self.send2socket(f'QUIT :Thanks and goodbye')
if command == 'JOIN':
channel_to_join = str(response[4])

View File

@@ -1,48 +1,42 @@
{
"SERVEUR_IP": "0.0.0.0",
"SERVEUR_HOSTNAME": "your.host.name",
"SERVEUR_LINK": "your.link.to.server",
"SERVEUR_IP": "YOUR.SERVER.IP",
"SERVEUR_HOSTNAME": "YOUR.SERVER.HOST",
"SERVEUR_LINK": "LINK.DE.TON.SERVER",
"SERVEUR_PORT": 7002,
"SERVEUR_PASSWORD": "link_password",
"SERVEUR_PASSWORD": "YOUR_LINK_PASSWORD",
"SERVEUR_ID": "006",
"SERVEUR_SSL": true,
"SERVICE_NAME": "defender",
"SERVICE_NICKNAME": "BotNickname",
"SERVICE_REALNAME": "BotRealname",
"SERVICE_USERNAME": "BotUsername",
"SERVICE_HOST": "your.service.hostname",
"SERVICE_NICKNAME": "PyDefender",
"SERVICE_REALNAME": "Python Defender Security",
"SERVICE_USERNAME": "PyDefender",
"SERVICE_HOST": "HOST.DE.TON.DEFENDER",
"SERVICE_INFO": "Network IRC Service",
"SERVICE_CHANLOG": "#services",
"SERVICE_SMODES": "+ioqBS",
"SERVICE_CMODES": "ntsO",
"SERVICE_CMODES": "ntsOP",
"SERVICE_UMODES": "o",
"SERVICE_PREFIX": "!",
"OWNER": "admin",
"PASSWORD": "password",
"OWNER": "TON_NICK_NAME",
"PASSWORD": "TON_PASSWORD",
"SALON_JAIL": "#jail",
"SALON_JAIL_MODES": "sS",
"SALON_LIBERER": "#welcome",
"CLONE_CHANNEL": "#clones",
"CLONE_CMODES": "+nts",
"CLONE_LOG_HOST_EXEMPT": ["HOST.TO.SKIP"],
"CLONE_CHANNEL_PASSWORD": "YOUR_CHANNEL_PASSWORD",
"API_TIMEOUT": 2,
"PORTS_TO_SCAN": [3028, 8080, 1080, 1085, 4145, 9050],
"WHITELISTED_IP": ["127.0.0.1"],
"GLINE_DURATION": "30",
"DEBUG_LEVEL": 20,
"CONFIG_COLOR": {
"blanche": "\\u0003\\u0030",
"noire": "\\u0003\\u0031",
"bleue": "\\u0003\\u0020",
"verte": "\\u0003\\u0033",
"rouge": "\\u0003\\u0034",
"jaune": "\\u0003\\u0036",
"gras": "\\u0002",
"nogc": "\\u0002\\u0003"
}
"DEBUG_LEVEL": 20
}

View File

@@ -175,8 +175,8 @@ class Irc:
except AttributeError as atte:
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())
self.Base.logs.critical(f"General Error: {e}")
self.Base.logs.critical(traceback.format_exc())
def __link(self, writer:Union[socket.socket, SSLSocket]) -> None:
"""Créer le link et envoyer les informations nécessaires pour la
@@ -222,7 +222,7 @@ class Irc:
writer.send(f":{service_id} MODE {chan} +{cmodes}\r\n".encode(charset))
writer.send(f":{service_id} MODE {chan} +{umodes} {service_id}\r\n".encode(charset))
self.Base.logs.debug('Link information sent to the server')
self.Base.logs.debug('>> Link information sent to the server')
return None
except AttributeError as ae:
@@ -249,7 +249,6 @@ class Irc:
"""
try:
with self.Base.lock:
# print(f">{str(send_message)}")
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0]))
self.Base.logs.debug(f'{send_message}')
@@ -268,6 +267,41 @@ class Irc:
except OSError as oe:
self.Base.logs.error(f"OSError: {oe} - {send_message}")
def sendNotice(self, msg:str, nickname: str) -> None:
"""Sending NOTICE by batches
Args:
msg (str): The message to send to the server
nickname (str): The reciever Nickname
"""
batch_size = self.Config.BATCH_SIZE
service_nickname = self.Config.SERVICE_NICKNAME
for i in range(0, len(str(msg)), batch_size):
batch = str(msg)[i:i+batch_size]
self.send2socket(f":{service_nickname} NOTICE {nickname} :{batch}")
def sendPrivMsg(self, msg: str, channel: str = None, nickname: str = None):
"""Sending PRIVMSG to a channel or to a nickname by batches
could be either channel or nickname not both together
Args:
msg (str): The message to send
channel (str, optional): The receiver channel. Defaults to None.
nickname (str, optional): The reciever nickname. Defaults to None.
"""
batch_size = self.Config.BATCH_SIZE
service_nickname = self.Config.SERVICE_NICKNAME
if not channel is None:
for i in range(0, len(str(msg)), batch_size):
batch = str(msg)[i:i+batch_size]
self.send2socket(f":{service_nickname} PRIVMSG {channel} :{batch}")
if not nickname is None:
for i in range(0, len(str(msg)), batch_size):
batch = str(msg)[i:i+batch_size]
self.send2socket(f":{service_nickname} PRIVMSG {nickname} :{batch}")
def send_response(self, responses:list[bytes]) -> None:
try:
# print(data)
@@ -452,11 +486,11 @@ class Irc:
except ModuleNotFoundError as moduleNotFound:
self.Base.logs.error(f"MODULE_NOT_FOUND: {moduleNotFound}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}MODULE_NOT_FOUND{self.Config.CONFIG_COLOR['noire']} ]: {moduleNotFound}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.COLORS.red}MODULE_NOT_FOUND{self.Config.COLORS.black} ]: {moduleNotFound}")
self.Base.db_delete_module(module_name)
except Exception as e:
self.Base.logs.error(f"Something went wrong with a module you want to load : {e}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}ERROR{self.Config.CONFIG_COLOR['noire']} ]: {e}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.COLORS.red}ERROR{self.Config.COLORS.black} ]: {e}")
def insert_db_admin(self, uid:str, level:int) -> None:
@@ -752,6 +786,7 @@ class Irc:
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} : New Version available {version}")
# Initialisation terminé aprés le premier PING
self.sendPrivMsg(msg=f'[{self.Config.COLORS.green}INFORMATION{self.Config.COLORS.nogc}] >> Defender is ready', channel='#devservices')
self.INIT = 0
case _:
@@ -991,6 +1026,9 @@ class Irc:
except IndexError as ie:
self.Base.logs.error(f"{ie} / {original_response} / length {str(len(original_response))}")
except Exception as err:
self.Base.logs.error(f"General Error: {err}")
self.Base.logs.error(f"General Error: {traceback.format_exc()}")
def _hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None:
"""_summary_
@@ -1031,7 +1069,7 @@ class Irc:
case 'notallowed':
try:
current_command = cmd[0]
self.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["rouge"]}{current_command}{self.Config.CONFIG_COLOR["noire"]} ] - Accès Refusé à {self.User.get_nickname(fromuser)}')
self.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.red}{current_command}{self.Config.COLORS.black} ] - Accès Refusé à {self.User.get_nickname(fromuser)}')
self.send2socket(f':{dnickname} NOTICE {fromuser} : Accès Refusé')
except IndexError as ie:
self.Base.logs.error(f'{ie}')
@@ -1041,7 +1079,7 @@ class Irc:
current_command = cmd[0]
uid_to_deauth = self.User.get_uid(fromuser)
self.delete_db_admin(uid_to_deauth)
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}{str(current_command).upper()} ]{self.Config.CONFIG_COLOR['noire']} - {self.User.get_nickname(fromuser)} est désormais déconnecter de {dnickname}")
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.red}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.get_nickname(fromuser)} est désormais déconnecter de {dnickname}")
case 'firstauth':
# firstauth OWNER_NICKNAME OWNER_PASSWORD
@@ -1087,10 +1125,10 @@ class Irc:
if cmd_owner == config_owner and cmd_password == config_password:
self.Base.db_create_first_admin()
self.insert_db_admin(current_uid, 5)
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}{str(current_command).upper()} ]{self.Config.CONFIG_COLOR['noire']} - {self.User.get_nickname(fromuser)} est désormais connecté a {dnickname}")
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.get_nickname(fromuser)} est désormais connecté a {dnickname}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Connexion a {dnickname} réussie!")
else:
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}{str(current_command).upper()} ]{self.Config.CONFIG_COLOR['noire']} - {self.User.get_nickname(fromuser)} a tapé un mauvais mot de pass")
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.red}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.get_nickname(fromuser)} a tapé un mauvais mot de pass")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Mot de passe incorrecte")
case 'auth':
@@ -1113,10 +1151,10 @@ class Irc:
if not user_from_db is None:
uid_user = self.User.get_uid(user_to_log)
self.insert_db_admin(uid_user, user_from_db[1])
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}{str(current_command).upper()} ]{self.Config.CONFIG_COLOR['noire']} - {self.User.get_nickname(fromuser)} est désormais connecté a {dnickname}")
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.get_nickname(fromuser)} est désormais connecté a {dnickname}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Connexion a {dnickname} réussie!")
else:
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}{str(current_command).upper()} ]{self.Config.CONFIG_COLOR['noire']} - {self.User.get_nickname(fromuser)} a tapé un mauvais mot de pass")
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.red}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.get_nickname(fromuser)} a tapé un mauvais mot de pass")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Mot de passe incorrecte")
else:
@@ -1255,13 +1293,13 @@ class Irc:
else:
user_level = 0
self.send2socket(f':{dnickname} NOTICE {fromuser} : **************** LIST DES COMMANDES *****************')
self.send2socket(f':{dnickname} NOTICE {fromuser} : ***************** LISTE DES COMMANDES *****************')
self.send2socket(f':{dnickname} NOTICE {fromuser} : ')
for levDef in self.commands_level:
if int(user_level) >= int(count_level_definition):
self.send2socket(f':{dnickname} NOTICE {fromuser} : **************** {self.Config.CONFIG_COLOR["noire"]}[ {self.Config.CONFIG_COLOR["verte"]}LEVEL {str(levDef)} {self.Config.CONFIG_COLOR["noire"]}] ****************')
self.send2socket(f':{dnickname} NOTICE {fromuser} : ***************** {self.Config.COLORS.nogc}[ {self.Config.COLORS.green}LEVEL {str(levDef)} {self.Config.COLORS.nogc}] *****************')
count_commands = 0
help = ''
for comm in self.commands_level[count_level_definition]:
@@ -1275,7 +1313,7 @@ class Irc:
count_level_definition += 1
self.send2socket(f':{dnickname} NOTICE {fromuser} : **************** FIN DES COMMANDES *****************')
self.send2socket(f':{dnickname} NOTICE {fromuser} : ***************** FIN DES COMMANDES *****************')
case 'load':
@@ -1411,9 +1449,9 @@ class Irc:
found = True
if found:
self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.CONFIG_COLOR['verte']}Loaded{self.Config.CONFIG_COLOR['nogc']}")
self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.COLORS.green}Loaded{self.Config.COLORS.nogc}")
else:
self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.CONFIG_COLOR['rouge']}Not Loaded{self.Config.CONFIG_COLOR['nogc']}")
self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.COLORS.red}Not Loaded{self.Config.COLORS.nogc}")
found = False

View File

@@ -7,6 +7,17 @@ from dataclasses import dataclass, field
# CONFIGURATION FILE #
##########################################
@dataclass
class ColorModel:
white: str = "\x0300"
black: str = "\x0301"
blue: str = "\x0302"
green: str = "\x0303"
red: str = "\x0304"
yellow: str = "\x0306"
bold: str = "\x02"
nogc: str = "\x03"
@dataclass
class ConfigDataModel:
@@ -82,8 +93,17 @@ class ConfigDataModel:
SALON_LIBERER: str
"""Channel where the nickname will be released"""
SALON_CLONES: str
"""Channel to host clones"""
CLONE_CHANNEL: str
"""Channel where clones are hosted and will log PRIVMSG"""
CLONE_CMODES: str
"""Clone channel modes"""
CLONE_LOG_HOST_EXEMPT: list[str]
"""Hosts that clones will not log"""
CLONE_CHANNEL_PASSWORD: str
"""Clone password channel"""
API_TIMEOUT: int
"""Default api timeout in second"""
@@ -100,8 +120,6 @@ class ConfigDataModel:
DEBUG_LEVEL:Literal[10, 20, 30, 40, 50]
"""Logs level: DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50"""
CONFIG_COLOR: dict[str, str]
table_admin: str
"""Admin table"""
@@ -132,6 +150,12 @@ class ConfigDataModel:
db_path: str
"""The database path"""
COLORS: ColorModel = ColorModel()
"""Available colors in Defender"""
BATCH_SIZE: int = 400
"""The batch size used for privmsg and notice"""
def __post_init__(self):
# Initialiser SERVICE_ID après la création de l'objet
self.SERVICE_ID:str = f"{self.SERVEUR_ID}AAAAAB"
@@ -150,34 +174,6 @@ class Config:
with open(conf_filename, 'r') as configuration_data:
configuration:dict[str, Union[str, int, list, dict]] = json.load(configuration_data)
config_dict = {"CONFIG_COLOR" : {
"blanche": "\x0300",
"noire": "\x0301",
"bleue": "\x0302",
"verte": "\x0303",
"rouge": "\x0304",
"jaune": "\x0306",
"gras": "\x02",
"nogc": "\x02\x03"
}
}
missing_color = False
if not "CONFIG_COLOR" in configuration:
missing_color = True
configuration_color = config_dict
else:
configuration_color = configuration["CONFIG_COLOR"]
if missing_color:
for key, value in configuration_color.items():
configuration_color['CONFIG_COLOR'][key] = str(value).encode('utf-8').decode('unicode_escape')
configuration['CONFIG_COLOR'] = configuration_color['CONFIG_COLOR']
else:
for key, value in configuration['CONFIG_COLOR'].items():
configuration['CONFIG_COLOR'][key] = str(value).encode('utf-8').decode('unicode_escape')
return configuration
except FileNotFoundError as fe:
@@ -215,13 +211,15 @@ class Config:
SALON_JAIL=import_config["SALON_JAIL"] if "SALON_JAIL" in import_config else '#jail',
SALON_JAIL_MODES=import_config["SALON_JAIL_MODES"] if "SALON_JAIL_MODES" in import_config else 'sS',
SALON_LIBERER=import_config["SALON_LIBERER"] if "SALON_LIBERER" in import_config else '#welcome',
SALON_CLONES=import_config["SALON_CLONES"] if "SALON_CLONES" in import_config else '#clones',
CLONE_CHANNEL=import_config["CLONE_CHANNEL"] if "CLONE_CHANNEL" in import_config else '#clones',
CLONE_CMODES=import_config["CLONE_CMODES"] if "CLONE_CMODES" in import_config else '+nts',
CLONE_LOG_HOST_EXEMPT=import_config["CLONE_LOG_HOST_EXEMPT"] if "CLONE_LOG_HOST_EXEMPT" in import_config else [],
CLONE_CHANNEL_PASSWORD=import_config["CLONE_CHANNEL_PASSWORD"] if "CLONE_CHANNEL_PASSWORD" in import_config else "clone_Password_1234",
API_TIMEOUT=import_config["API_TIMEOUT"] if "API_TIMEOUT" in import_config else 2,
PORTS_TO_SCAN=import_config["PORTS_TO_SCAN"] if "PORTS_TO_SCAN" in import_config else [],
WHITELISTED_IP=import_config["WHITELISTED_IP"] if "WHITELISTED_IP" in import_config else ['127.0.0.1'],
GLINE_DURATION=import_config["GLINE_DURATION"] if "GLINE_DURATION" in import_config else '30',
DEBUG_LEVEL=import_config["DEBUG_LEVEL"] if "DEBUG_LEVEL" in import_config else 20,
CONFIG_COLOR=import_config["CONFIG_COLOR"],
table_admin='core_admin',
table_commande='core_command',
table_log='core_log',