From 01dcc90d6303286fa96393714726020a167304b2 Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Thu, 29 Aug 2024 01:13:55 +0200 Subject: [PATCH] V5.1.0 --- core/Model.py | 96 +++++++++++++++++++++++++++++++++++++++++++- core/connection.py | 17 +++++--- core/installation.py | 10 +++++ core/irc.py | 15 ++----- mods/mod_clone.py | 72 +++++++++++++++++++++------------ mods/mod_command.py | 12 +++++- mods/mod_defender.py | 4 +- 7 files changed, 179 insertions(+), 47 deletions(-) diff --git a/core/Model.py b/core/Model.py index 00a2cef..946435c 100644 --- a/core/Model.py +++ b/core/Model.py @@ -315,7 +315,7 @@ class Channel: for user in newChan.uids: record.uids.append(user) - # Supprimer les doublons + # Supprimer les doublons del_duplicates = list(set(record.uids)) record.uids = del_duplicates self.log.debug(f'Updating a new UID to the channel {record}') @@ -402,3 +402,97 @@ class Channel: self.log.debug(f'Search {name} -- result = {Channel}') return Channel + +class Clones: + + @dataclass + class CloneModel: + alive: bool + nickname: str + username: str + + UID_CLONE_DB: list[CloneModel] = [] + + def __init__(self, Base: Base) -> None: + self.log = Base.logs + + def insert(self, newCloneObject: CloneModel) -> bool: + """Create new Clone object + + Args: + newCloneObject (CloneModel): New CloneModel object + + Returns: + bool: True if inserted + """ + result = False + exist = False + + for record in self.UID_CLONE_DB: + if record.nickname == newCloneObject.nickname: + # If the user exist then return False and do not go further + exist = True + self.log.debug(f'{record.nickname} already exist') + return result + + if not exist: + self.UID_CLONE_DB.append(newCloneObject) + result = True + self.log.debug(f'New Clone Object Created: ({newCloneObject})') + + if not result: + self.log.critical(f'The Clone Object was not inserted {newCloneObject}') + + return result + + def delete(self, nickname: str) -> bool: + """Delete the Clone Object starting from the nickname + + Args: + nickname (str): nickname of the clone + + Returns: + bool: True if deleted + """ + result = False + + for record in self.UID_CLONE_DB: + if record.nickname == nickname: + # If the user exist then remove and return True and do not go further + self.UID_CLONE_DB.remove(record) + result = True + self.log.debug(f'The clone ({record.nickname}) has been deleted') + return result + + if not result: + self.log.critical(f'The UID {nickname} was not deleted') + + return result + + def exists(self, nickname: str) -> bool: + """Check if the nickname exist + + Args: + nickname (str): Nickname of the clone + + Returns: + bool: True if the nickname exist + """ + response = False + + for cloneObject in self.UID_CLONE_DB: + if cloneObject.nickname == nickname: + response = True + + return response + + def kill(self, nickname:str) -> bool: + + response = False + + for cloneObject in self.UID_CLONE_DB: + if cloneObject.nickname == nickname: + cloneObject.alive = False # Kill the clone + response = True + + return response diff --git a/core/connection.py b/core/connection.py index 9a54451..d865db1 100644 --- a/core/connection.py +++ b/core/connection.py @@ -1,20 +1,26 @@ import socket, ssl, time from ssl import SSLSocket from core.loadConf import Config +from core.Model import Clones from core.base import Base from typing import Union class Connection: - def __init__(self, server_port: int, nickname: str, username: str, channels:list[str], ssl:bool = False) -> None: + def __init__(self, server_port: int, nickname: str, username: str, channels:list[str], CloneObject: Clones, ssl:bool = False) -> None: + self.Config = Config().ConfigObject self.Base = Base(self.Config) self.IrcSocket: Union[socket.socket, SSLSocket] = None - self.signal: bool = True self.nickname = nickname self.username = username self.channels:list[str] = channels self.CHARSET = ['utf-8', 'iso-8859-1'] + self.Clones = CloneObject + self.signal: bool = True + for clone in self.Clones.UID_CLONE_DB: + if clone.nickname == nickname: + self.currentCloneObject = clone self.create_socket(self.Config.SERVEUR_IP, self.Config.SERVEUR_HOSTNAME, server_port, ssl) self.send_connection_information_to_server(self.IrcSocket) @@ -126,7 +132,6 @@ class Connection: break self.parser(data) - except ssl.SSLEOFError as soe: self.Base.logs.error(f"SSLEOFError __connect_to_irc: {soe} - {data}") self.signal = False @@ -156,6 +161,7 @@ class Connection: try: for data in cmd: response = data.decode(self.CHARSET[0]).split() + self.signal = self.currentCloneObject.alive # print(response) match response[0]: @@ -174,13 +180,15 @@ class Connection: self.send2socket(f"JOIN {channel}") return None case 'PRIVMSG': + self.Base.logs.debug(response) + self.Base.logs.debug(f'{self.currentCloneObject.nickname} - {self.currentCloneObject.alive}') fullname = str(response[0]).replace(':', '') 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 = False + self.signal = self.currentCloneObject.alive if command == 'JOIN': channel_to_join = str(response[4]) self.send2socket(f"JOIN {channel_to_join}") @@ -193,7 +201,6 @@ class Connection: response = data.decode(self.CHARSET[1],'replace').split() except AssertionError as ae: self.Base.logs.error(f"Assertion error : {ae}") - pass def __ssl_context(self) -> ssl.SSLContext: ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) diff --git a/core/installation.py b/core/installation.py index 5708269..90e48c6 100644 --- a/core/installation.py +++ b/core/installation.py @@ -27,6 +27,9 @@ class Install: def __init__(self) -> None: self.set_configuration() + + if self.skip_install: + return None if not self.check_python_version(): # Tester si c'est la bonne version de python @@ -45,6 +48,13 @@ class Install: def set_configuration(self): + self.skip_install = False + # Exclude Windows OS + if os.name == 'nt': + #print('/!\\ Skip installation /!\\') + self.skip_install = True + return False + defender_install_folder = os.getcwd() venv_folder = '.pyenv' unix_user_home_directory = os.path.expanduser("~") diff --git a/core/irc.py b/core/irc.py index 3fb5e50..794e3ca 100644 --- a/core/irc.py +++ b/core/irc.py @@ -3,7 +3,7 @@ from ssl import SSLSocket from datetime import datetime, timedelta from typing import Union from core.loadConf import Config -from core.Model import User, Admin, Channel +from core.Model import User, Admin, Channel, Clones from core.base import Base class Irc: @@ -30,7 +30,7 @@ class Irc: 0: ['help', 'auth', 'copyright', 'uptime'], 1: ['load','reload','unload', 'deauth', 'checkversion'], 2: ['show_modules', 'show_timers', 'show_threads', 'show_channels', 'show_users', 'show_admins'], - 3: ['quit', 'restart','addaccess','editaccess', 'delaccess','umode'] + 3: ['quit', 'restart','addaccess','editaccess', 'delaccess'] } # l'ensemble des commandes. @@ -43,6 +43,7 @@ class Irc: self.User = User(self.Base) self.Admin = Admin(self.Base) self.Channel = Channel(self.Base) + self.Clones = Clones(self.Base) self.__create_table() self.Base.create_thread(func=self.heartbeat, func_args=(self.beat, )) @@ -1341,15 +1342,5 @@ class Irc: (fromuser, ) ) - case 'umode': - try: - # .umode nickname +mode - nickname = str(cmd[1]) - umode = str(cmd[2]) - - self.send2socket(f':{dnickname} SVSMODE {nickname} {umode}') - except KeyError as ke: - self.Base.logs.error(ke) - case _: pass diff --git a/mods/mod_clone.py b/mods/mod_clone.py index 81c16fa..0446285 100644 --- a/mods/mod_clone.py +++ b/mods/mod_clone.py @@ -9,7 +9,6 @@ class Clone(): @dataclass class ModConfModel: - clone_count: int clone_nicknames: list[str] def __init__(self, ircInstance:Irc) -> None: @@ -35,6 +34,8 @@ class Clone(): # Add Channel object to the module (Mandatory) self.Channel = ircInstance.Channel + self.Clone = ircInstance.Clones + # Créer les nouvelles commandes du module self.commands_level = { 1: ['clone_connect', 'clone_join', 'clone_kill', 'clone_list'] @@ -97,15 +98,13 @@ class Clone(): """### Load Module Configuration """ try: - # Variable qui va contenir les options de configuration du module Defender # Variable qui va contenir les options de configuration du module Defender self.ModConfig = self.ModConfModel( - clone_count=0, clone_nicknames=[] ) # Sync the configuration with core configuration (Mandatory) - self.Base.db_sync_core_config(self.module_name, self.ModConfig) + # self.Base.db_sync_core_config(self.module_name, self.ModConfig) return None @@ -123,22 +122,23 @@ class Clone(): return None - def thread_create_clones(self, nickname: str, username: str, channels:list, server_port:int, ssl:bool) -> None: + def thread_create_clones(self, nickname: str, username: str, channels: list, server_port: int, ssl: bool) -> None: - Connection(server_port=server_port, nickname=nickname, username=username, channels=channels, ssl=ssl) + Connection(server_port=server_port, nickname=nickname, username=username, channels=channels, CloneObject=self.Clone, ssl=ssl) return None def thread_join_channels(self, channel_name: str, wait: float, clone_name:str = None): if clone_name is None: - for clone in self.ModConfig.clone_nicknames: - self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone} :JOIN {channel_name}') + for clone in self.Clone.UID_CLONE_DB: + self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}') time.sleep(wait) else: - for clone in self.ModConfig.clone_nicknames: - if clone_name == clone: - self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone} :JOIN {channel_name}') + for clone in self.Clone.UID_CLONE_DB: + if clone_name == clone.nickname: + self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}') + time.sleep(wait) def generate_names(self) -> tuple[str, str]: try: @@ -146,13 +146,25 @@ class Clone(): nickname = fake.first_name() username = fake.last_name() - if not nickname in self.ModConfig.clone_nicknames: - self.ModConfig.clone_nicknames.append(nickname) - else: + if self.Clone.exists(nickname=nickname): caracteres = '0123456789' randomize = ''.join(random.choice(caracteres) for _ in range(2)) nickname = nickname + str(randomize) - self.ModConfig.clone_nicknames.append(nickname) + self.Clone.insert( + self.Clone.CloneModel(alive=True, nickname=nickname, username=username) + ) + else: + self.Clone.insert( + self.Clone.CloneModel(alive=True, nickname=nickname, username=username) + ) + + # if not nickname in self.ModConfig.clone_nicknames: + # self.ModConfig.clone_nicknames.append(nickname) + # else: + # caracteres = '0123456789' + # randomize = ''.join(random.choice(caracteres) for _ in range(2)) + # nickname = nickname + str(randomize) + # self.ModConfig.clone_nicknames.append(nickname) return (nickname, username) @@ -204,16 +216,24 @@ class Clone(): case 'clone_kill': try: clone_name = str(cmd[1]) + clone_to_kill: list[str] = [] if clone_name.lower() == 'all': - for clone in self.ModConfig.clone_nicknames: - self.Irc.send2socket(f':{dnickname} PRIVMSG {clone} :KILL') - self.ModConfig.clone_nicknames.remove(clone) + for clone in self.Clone.UID_CLONE_DB: + self.Irc.send2socket(f':{dnickname} PRIVMSG {clone.nickname} :KILL') + clone_to_kill.append(clone.nickname) + clone.alive = False + + for clone_nickname in clone_to_kill: + self.Clone.delete(clone_nickname) + + del clone_to_kill + else: - for clone in self.ModConfig.clone_nicknames: - if clone_name == clone: - self.Irc.send2socket(f':{dnickname} PRIVMSG {clone} :KILL') - self.ModConfig.clone_nicknames.remove(clone) + if self.Clone.exists(clone_name): + self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :KILL') + self.Clone.kill(clone_name) + self.Clone.delete(clone_name) except Exception as err: self.Logs.error(f'{err}') @@ -227,9 +247,9 @@ class Clone(): clone_channel_to_join = cmd[2] if clone_name.lower() == 'all': - self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 4)) + self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2)) else: - self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 4, clone_name)) + self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2, clone_name)) except Exception as err: self.Logs.error(f'{err}') @@ -238,5 +258,5 @@ class Clone(): case 'clone_list': - for clone_name in self.ModConfig.clone_nicknames: - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> {clone_name}') + for clone_name in self.Clone.UID_CLONE_DB: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> {clone_name.nickname} | {clone_name.username}') diff --git a/mods/mod_command.py b/mods/mod_command.py index 8b643ab..cba38ef 100644 --- a/mods/mod_command.py +++ b/mods/mod_command.py @@ -35,7 +35,7 @@ class Command(): # Create module commands (Mandatory) self.commands_level = { 1: ['join', 'part'], - 2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice', 'devoice', 'ban', 'unban','kick', 'kickban'] + 2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice', 'devoice', 'ban', 'unban','kick', 'kickban', 'umode'] } # Init the module @@ -481,3 +481,13 @@ class Command(): except IndexError as ie: self.Logs.error(f'{ie}') + + case 'umode': + try: + # .umode nickname +mode + nickname = str(cmd[1]) + umode = str(cmd[2]) + + self.send2socket(f':{dnickname} SVSMODE {nickname} {umode}') + except KeyError as ke: + self.Base.logs.error(ke) \ No newline at end of file diff --git a/mods/mod_defender.py b/mods/mod_defender.py index a1da1c2..a442ac4 100644 --- a/mods/mod_defender.py +++ b/mods/mod_defender.py @@ -272,14 +272,14 @@ class Defender(): return False def reputation_insert(self, reputationModel: ReputationModel) -> bool: - + response = False # Check if the user already exist for reputation in self.UID_REPUTATION_DB: if reputation.uid == reputationModel.uid: return response - + self.UID_REPUTATION_DB.append(reputationModel) self.Logs.debug(f'Reputation inserted: {reputationModel}') response = True