diff --git a/README.md b/README.md index 5dee40c..a1793c5 100644 --- a/README.md +++ b/README.md @@ -23,27 +23,27 @@ Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux u # Installation automatique sur une machine Debian/Ubuntu -Prérequis: - - Système d'exploitation Linux (Windows non supporté) - - Droits d'administrateur (root) pour l'exécution du script - - Python version 3.10 ou supérieure + Prérequis: + - Système d'exploitation Linux (Windows non supporté) + - Droits d'administrateur (root) pour l'exécution du script + - Python version 3.10 ou supérieure -Bash - $ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git - - Renommer le fichier exemple_configuration.json en configuration.json - - Configurer le fichier configuration.json - $ sudo python3 install.py + Bash: + $ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git + - Renommer le fichier exemple_configuration.json en configuration.json + - Configurer le fichier configuration.json + $ sudo python3 install.py Si votre configuration est bonne, votre service est censé etre connecté a votre réseau IRC # Installation manuelle: -Bash - $ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git - $ cd IRC_DEFENDER_MODULES - $ python3 -m venv .pyenv - $ source .pyenv/bin/activate - - Créer un service nommé "Defender.service" pour votre service et placer le dans "/etc/systemd/system/" - $ sudo systemctl start Defender + Bash: + $ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git + $ cd IRC_DEFENDER_MODULES + $ python3 -m venv .pyenv + $ source .pyenv/bin/activate + - Créer un service nommé "Defender.service" pour votre service et placer le dans "/etc/systemd/system/" + $ sudo systemctl start Defender # Configuration diff --git a/core/Model.py b/core/Model.py index 6769a52..00a2cef 100644 --- a/core/Model.py +++ b/core/Model.py @@ -275,9 +275,19 @@ class Channel: @dataclass class ChannelModel: name: str + """### Channel name + It include the #""" uids: list + """### List of UID available in the channel + including their modes ~ @ % + * + + Returns: + list: The list of UID's including theirs modes + """ UID_CHANNEL_DB: list[ChannelModel] = [] + """List that contains all the Channels objects (ChannelModel) + """ def __init__(self, Base: Base) -> None: self.log = Base.logs @@ -362,6 +372,26 @@ class Channel: except ValueError as ve: self.log.error(f'{ve}') + def delete_user_from_all_channel(self, uid:str) -> bool: + try: + result = False + + for record in self.UID_CHANNEL_DB: + for user_id in record.uids: + if self.Base.clean_uid(user_id) == self.Base.clean_uid(uid): + record.uids.remove(user_id) + self.log.debug(f'The UID {uid} has been removed, here is the new object: {record}') + result = True + + for record in self.UID_CHANNEL_DB: + if not record.uids: + self.UID_CHANNEL_DB.remove(record) + self.log.debug(f'The Channel {record.name} has been removed, here is the new object: {record}') + + return result + except ValueError as ve: + self.log.error(f'{ve}') + def get_Channel(self, name: str) -> Union[ChannelModel, None]: Channel = None diff --git a/core/installation.py b/core/installation.py index e50b008..24e5076 100644 --- a/core/installation.py +++ b/core/installation.py @@ -1,6 +1,6 @@ from importlib.util import find_spec from subprocess import check_call, run, CalledProcessError -from platform import python_version +from platform import python_version, python_version_tuple from sys import exit import os @@ -28,14 +28,19 @@ class Install: Returns: bool: True si la version de python est autorisé sinon False """ - python_required_version = self.PYTHON_MIN_VERSION.split('.') - python_current_version = python_version().split('.') + # Current system version + sys_major, sys_minor, sys_patch = python_version_tuple() - if int(python_current_version[0]) < int(python_required_version[0]): + # min python version required + python_required_version = self.PYTHON_MIN_VERSION.split('.') + min_major, min_minor = tuple((python_required_version[0], python_required_version[1])) + + if int(sys_major) < int(min_major): print(f"## Your python version must be greather than or equal to {self.PYTHON_MIN_VERSION} ##") return False - elif int(python_current_version[1]) < int(python_required_version[1]): - print(f"### Your python version must be greather than or equal to {self.PYTHON_MIN_VERSION} ###") + + elif (int(sys_major) <= int(min_major)) and (int(sys_minor) < int(min_minor)): + print(f"## Your python version must be greather than or equal to {self.PYTHON_MIN_VERSION} ##") return False print(f"===> Version of python : {python_version()} ==> OK") diff --git a/core/irc.py b/core/irc.py index 22dc206..ae43358 100644 --- a/core/irc.py +++ b/core/irc.py @@ -28,9 +28,9 @@ class Irc: # Liste des commandes internes du bot self.commands_level = { - 0: ['help', 'auth', 'copyright'], - 1: ['load','reload','unload', 'deauth', 'uptime', 'checkversion'], - 2: ['show_modules', 'show_timers', 'show_threads', 'show_channels'], + 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'] } @@ -418,6 +418,7 @@ class Irc: 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}") 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}") def insert_db_admin(self, uid:str, level:int) -> None: @@ -619,8 +620,18 @@ class Irc: # if self.Config.ABUSEIPDB == 1: # self.Base.create_thread(self.abuseipdb_scan, (cmd[2], )) self.first_connexion_ip = cmd[2] - self.first_score = int(cmd[3]) - pass + + self.first_score = 0 + if str(cmd[3]).find('*') != -1: + # If * available, it means that an ircop changed the repurtation score + # means also that the user exist will try to update all users with same IP + self.first_score = int(str(cmd[3]).replace('*','')) + for user in self.User.UID_DB: + if user.remote_ip == self.first_connexion_ip: + user.score_connexion = self.first_score + else: + self.first_score = int(cmd[3]) + # Possibilité de déclancher les bans a ce niveau. except IndexError as ie: self.Base.logs.error(f'{ie}') @@ -695,6 +706,7 @@ class Irc: cmd.pop(0) uid_who_quit = str(cmd[0]).replace(':', '') self.User.delete(uid_who_quit) + self.Channel.delete_user_from_all_channel(uid_who_quit) case 'PONG': # ['@msgid=aTNJhp17kcPboF5diQqkUL;time=2023-12-28T20:35:58.411Z', ':irc.deb.biz.st', 'PONG', 'irc.deb.biz.st', ':Dev-PyDefender'] @@ -718,27 +730,34 @@ class Irc: case 'SJOIN': # ['@msgid=5sTwGdj349D82L96p749SY;time=2024-08-15T09:50:23.528Z', ':001', 'SJOIN', '1721564574', '#welcome', ':001JD94QH'] # ['@msgid=bvceb6HthbLJapgGLXn1b0;time=2024-08-15T09:50:11.464Z', ':001', 'SJOIN', '1721564574', '#welcome', '+lnrt', '13', ':001CIVLQF', '+11ZAAAAAB', '001QGR10C', '*@0014UE10B', '001NL1O07', '001SWZR05', '001HB8G04', '@00BAAAAAJ', '0019M7101'] + # ['@msgid=SKUeuVzOrTShRDduq8VerX;time=2024-08-23T19:37:04.266Z', ':001', 'SJOIN', '1723993047', '#welcome', '+lnrt', '13', + # ':001T6VU3F', '001JGWB2K', '@11ZAAAAAB', + # '001F16WGR', '001X9YMGQ', '*+001DYPFGP', '@00BAAAAAJ', '001AAGOG9', '001FMFVG8', '001DAEEG7', + # '&~G:unknown-users', '"~G:websocket-users', '"~G:known-users', '"~G:webirc-users'] cmd.pop(0) channel = str(cmd[3]).lower() - mode = cmd[4] len_cmd = len(cmd) list_users:list = [] - + occurence = 0 start_boucle = 0 - + # Trouver le premier user for i in range(len_cmd): s: list = re.findall(fr':', cmd[i]) if s: - start_boucle = i + occurence += 1 + if occurence == 2: + start_boucle = i # Boucle qui va ajouter l'ensemble des users (UID) for i in range(start_boucle, len(cmd)): parsed_UID = str(cmd[i]) - pattern = fr'[:|@|%|\+|~|\*]*' + # pattern = fr'[:|@|%|\+|~|\*]*' pattern = fr':' parsed_UID = re.sub(pattern, '', parsed_UID) - list_users.append(parsed_UID) + clean_uid = self.Base.clean_uid(parsed_UID) + if len(clean_uid) == 9: + list_users.append(parsed_UID) self.Channel.insert( self.Channel.ChannelModel( @@ -887,8 +906,8 @@ class Irc: def _hcmds(self, user: str, cmd:list, fullcmd: list = []) -> None: - fromuser = self.User.get_nickname(user) # Nickname qui a lancé la commande - uid = self.User.get_uid(fromuser) # Récuperer le uid de l'utilisateur + fromuser = self.User.get_nickname(user) # Nickname qui a lancé la commande + uid = self.User.get_uid(fromuser) # Récuperer le uid de l'utilisateur # Defender information dnickname = self.Config.SERVICE_NICKNAME # Defender nickname @@ -931,6 +950,11 @@ class Irc: user_to_log = self.User.get_nickname(cmd[1]) password = cmd[2] + if fromuser != user_to_log: + # If the current nickname is different from the nickname you want to log in with + self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Your current nickname is different from the nickname you want to log in with") + return False + if not user_to_log is None: mes_donnees = {'user': user_to_log, 'password': self.Base.crypt_password(password)} query = f"SELECT id, level FROM {self.Config.table_admin} WHERE user = :user AND password = :password" @@ -1134,11 +1158,12 @@ class Irc: class_name = module_name.split('_')[1].capitalize() # ==> Defender if 'mods.' + module_name in sys.modules: + self.Base.logs.info('Unload the module ...') self.loaded_classes[class_name].unload() self.Base.logs.info('Module Already Loaded ... reloading the module ...') the_module = sys.modules['mods.' + module_name] importlib.reload(the_module) - + # Supprimer la class déja instancier if class_name in self.loaded_classes: # Supprimer les commandes déclarer dans la classe @@ -1158,8 +1183,23 @@ class Irc: return False else: self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} n'est pas chargé !") - except: - self.Base.logs.error(f"Something went wrong with a module you want to reload") + + except TypeError as te: + self.Base.logs.error(f"A TypeError raised: {te}") + self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :A TypeError raised: {te}") + self.Base.db_delete_module(module_name) + except AttributeError as ae: + self.Base.logs.error(f"Missing Attribute: {ae}") + self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Missing Attribute: {ae}") + self.Base.db_delete_module(module_name) + except KeyError as ke: + self.Base.logs.error(f"Key Error: {ke}") + self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Key Error: {ke}") + 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 reload: {e}") + self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Something went wrong with the module: {e}") + self.Base.db_delete_module(module_name) case 'quit': try: @@ -1260,6 +1300,14 @@ class Irc: self.send2socket(f":{dnickname} NOTICE {fromuser} : Channel: {chan.name} - Users: {list_nicknames}") + case 'show_users': + for db_user in self.User.UID_DB: + self.send2socket(f":{dnickname} NOTICE {fromuser} :UID : {db_user.uid} - isWebirc: {db_user.isWebirc} - Nickname: {db_user.nickname} - Connection: {db_user.connexion_datetime}") + + case 'show_admins': + for db_admin in self.Admin.UID_ADMIN_DB: + self.send2socket(f":{dnickname} NOTICE {fromuser} :UID : {db_admin.uid} - Nickname: {db_admin.nickname} - Level: {db_admin.level} - Connection: {db_admin.connexion_datetime}") + case 'uptime': uptime = self.get_defender_uptime() self.send2socket(f':{dnickname} NOTICE {fromuser} : {uptime}') diff --git a/install.py b/install.py index a279768..6d18c22 100644 --- a/install.py +++ b/install.py @@ -1,8 +1,12 @@ from subprocess import check_call, run, CalledProcessError, PIPE -from platform import python_version +from platform import python_version, python_version_tuple, system from sys import exit -import os, logging, shutil, pwd +import os, logging, shutil +try: + import pwd +except ModuleNotFoundError as err: + print(err) class Install: @@ -16,6 +20,7 @@ class Install: self.venv_dependencies: list[str] = ['sqlalchemy','psutil','requests'] self.install_folder = os.getcwd() self.osname = os.name + self.system_name = system() self.cmd_linux_requirements: list[str] = ['apt', 'install', '-y', 'python3', 'python3-pip', 'python3-venv'] self.venv_pip_full_path = os.path.join(self.venv_name, f'bin{os.sep}pip') self.venv_python_full_path = os.path.join(self.venv_name, f'bin{os.sep}python') @@ -28,6 +33,7 @@ class Install: if self.osname == 'nt': print('/!\\ Windows OS is not supported by this automatic installation /!\\') self.Logs.critical('/!\\ Windows OS is not supported by this automatic install /!\\') + print(self.system_name) exit(5) if not self.is_root(): @@ -47,7 +53,7 @@ class Install: self.install_linux_dependencies() # Check python version - self.checkPythonVersion() + self.check_python_version() # Create systemd service file self.create_service_file() @@ -142,23 +148,29 @@ class Install: self.Logs.critical(f"Command failed :{e.returncode}") exit(5) - def checkPythonVersion(self) -> bool: + def check_python_version(self) -> bool: """Test si la version de python est autorisée ou non Returns: bool: True si la version de python est autorisé sinon False """ - python_required_version = self.python_min_version.split('.') - python_current_version = python_version().split('.') self.Logs.debug(f'The current python version is: {python_version()}') - if int(python_current_version[0]) < int(python_required_version[0]): - print(f"## Your python version must be greather than or equal to {self.python_min_version} ##") + # Current system version + sys_major, sys_minor, sys_patch = python_version_tuple() + + # min python version required + python_required_version = self.PYTHON_MIN_VERSION.split('.') + min_major, min_minor = tuple((python_required_version[0], python_required_version[1])) + + if int(sys_major) < int(min_major): + print(f"## Your python version must be greather than or equal to {self.PYTHON_MIN_VERSION} ##") self.Logs.critical(f'Your python version must be greather than or equal to {self.python_min_version}') return False - elif int(python_current_version[1]) < int(python_required_version[1]): - print(f"### Your python version must be greather than or equal to {self.python_min_version} ###") + + elif (int(sys_major) <= int(min_major)) and (int(sys_minor) < int(min_minor)): + print(f"## Your python version must be greather than or equal to {self.PYTHON_MIN_VERSION} ##") self.Logs.critical(f'Your python version must be greather than or equal to {self.python_min_version}') return False diff --git a/mods/mod_defender.py b/mods/mod_defender.py index ca7e90c..747c602 100644 --- a/mods/mod_defender.py +++ b/mods/mod_defender.py @@ -23,7 +23,9 @@ class Defender(): reputation: int reputation_timer: int reputation_seuil: int + reputation_score_after_release: int reputation_ban_all_chan: int + reputation_sg: int local_scan: int psutil_scan: int abuseipdb_scan: int @@ -81,7 +83,7 @@ class Defender(): 0: ['code'], 1: ['join','part', 'info'], 2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice', 'devoice', 'ban', 'unban','kick', 'kickban'], - 3: ['reputation','proxy_scan', 'flood', 'status', 'timer','show_reputation', 'show_users', 'sentinel'] + 3: ['reputation','proxy_scan', 'flood', 'status', 'timer','show_reputation', 'sentinel'] } self.__set_commands(self.commands_level) # Enrigstrer les nouvelles commandes dans le code @@ -173,12 +175,16 @@ class Defender(): # Rejoindre les salons self.join_saved_channels() - # Variable qui va contenir les options de configuration du module Defender - self.ModConfig = self.ModConfModel( - reputation=0, reputation_timer=0, reputation_seuil=10, reputation_ban_all_chan=0, - local_scan=0, psutil_scan=0, abuseipdb_scan=0, freeipapi_scan=0, cloudfilt_scan=0, - flood=0, flood_message=5, flood_time=1, flood_timer=20 - ) + try: + # Variable qui va contenir les options de configuration du module Defender + self.ModConfig = self.ModConfModel( + reputation=0, reputation_timer=0, reputation_seuil=26, reputation_score_after_release=27, + reputation_ban_all_chan=0,reputation_sg=1, + local_scan=0, psutil_scan=0, abuseipdb_scan=0, freeipapi_scan=0, cloudfilt_scan=0, + flood=0, flood_message=5, flood_time=1, flood_timer=20 + ) + except TypeError as te: + self.Logs.critical(te) # Logger en debug la variable de configuration self.Logs.debug(self.ModConfig) @@ -470,7 +476,7 @@ class Defender(): # - Defender devra libérer l'utilisateur et l'envoyer vers un salon défini dans la configuration {welcome_chan} # - Defender devra intégrer une liste d'IDs (pseudo/host) exemptés de 'Reputation security' malgré un score de rép. faible et un pseudo non enregistré. try: - + get_reputation = self.reputation_get_Reputation(uid) if get_reputation is None: @@ -479,7 +485,7 @@ class Defender(): salon_logs = self.Config.SERVICE_CHANLOG salon_jail = self.Config.SALON_JAIL - + code = get_reputation.secret_code jailed_nickname = get_reputation.nickname jailed_score = get_reputation.score @@ -489,7 +495,7 @@ class Defender(): color_bold = self.Config.CONFIG_COLOR['gras'] service_id = self.Config.SERVICE_ID service_prefix = self.Config.SERVICE_PREFIX - reputation_ban_all_chan = self.Base.int_if_possible(self.ModConfig.reputation_ban_all_chan) + reputation_ban_all_chan = self.ModConfig.reputation_ban_all_chan if not get_reputation.isWebirc: # Si le user ne vient pas de webIrc @@ -518,7 +524,7 @@ class Defender(): reputation_flag = self.ModConfig.reputation reputation_timer = self.ModConfig.reputation_timer reputation_seuil = self.ModConfig.reputation_seuil - ban_all_chan = self.Base.int_if_possible(self.ModConfig.reputation_ban_all_chan) + ban_all_chan = self.ModConfig.reputation_ban_all_chan service_id = self.Config.SERVICE_ID dchanlog = self.Config.SERVICE_CHANLOG color_red = self.Config.CONFIG_COLOR['rouge'] @@ -534,15 +540,11 @@ class Defender(): for user in self.UID_REPUTATION_DB: if not user.isWebirc: # Si il ne vient pas de WebIRC - # self.Irc.debug(f"Nickname: {self.db_reputation[uid]['nickname']} | uptime: {self.get_user_uptime_in_minutes(uid)} | reputation time: {reputation_timer}") if self.get_user_uptime_in_minutes(user.uid) >= reputation_timer and int(user.score) <= int(reputation_seuil): self.Irc.send2socket(f":{service_id} PRIVMSG {dchanlog} :[{color_red} REPUTATION {color_black}] : Action sur {user.nickname} aprés {str(reputation_timer)} minutes d'inactivité") - # if not system_reputation_timer_action(cglobal['reputation_timer_action'], uid, self.db_reputation[uid]['nickname']): - # return False self.Irc.send2socket(f":{service_id} KILL {user.nickname} After {str(reputation_timer)} minutes of inactivity you should reconnect and type the password code ") self.Logs.info(f"Nickname: {user.nickname} KILLED after {str(reputation_timer)} minutes of inactivity") - uid_to_clean.append(user.uid) for uid in uid_to_clean: @@ -553,8 +555,9 @@ class Defender(): self.Irc.send2socket(f":{service_id} MODE {chan.name} -b {get_user_reputation.nickname}!*@*") # Lorsqu'un utilisateur quitte, il doit être supprimé de {UID_DB}. - self.User.delete(uid) + self.Channel.delete_user_from_all_channel(uid) self.reputation_delete(uid) + self.User.delete(uid) except AssertionError as ae: self.Logs.error(f'Assertion Error -> {ae}') @@ -1004,6 +1007,9 @@ class Defender(): try: self.reputation_first_connexion['ip'] = cmd[2] self.reputation_first_connexion['score'] = cmd[3] + if str(cmd[3]).find('*') != -1: + # If the reputation changed, we do not need to scan the IP + return None if not self.Base.is_valid_ip(cmd[2]): return None @@ -1038,71 +1044,39 @@ class Defender(): self.flood(find_nickname, channel) case 'UID': - + # If Init then do nothing if self.Irc.INIT == 1: return None - if 'webirc' in cmd[0]: - isWebirc = True - else: - isWebirc = False - # Supprimer la premiere valeur et finir le code normalement cmd.pop(0) - uid = str(cmd[7]) - nickname = str(cmd[2]) - username = str(cmd[5]) - hostname = str(cmd[6]) - umodes = str(cmd[9]) - vhost = str(cmd[10]) + # Get User information + _User = self.User.get_User(str(cmd[7])) + if _User is None: + self.Logs.critical(f'This UID: [{cmd[7]}] is not available please check why') + return None - reputation_flag = self.Base.int_if_possible(self.ModConfig.reputation) - reputation_seuil = self.Base.int_if_possible(self.ModConfig.reputation_seuil) + reputation_flag = self.ModConfig.reputation + reputation_seuil = self.ModConfig.reputation_seuil if self.Irc.INIT == 0: - # A chaque nouvelle connexion chargé les données dans reputation - client_ip = '' - client_score = 0 - if 'ip' in self.reputation_first_connexion: - client_ip = self.reputation_first_connexion['ip'] - if 'score' in self.reputation_first_connexion: - client_score = self.reputation_first_connexion['score'] - - # Si réputation activé lancer un whois sur le nickname connecté - # Si le user n'es pas un service ni un IrcOP alors whois - if not re.match(fr'^.*[S|o?].*$', umodes): - if reputation_flag == 1 and int(client_score) <= int(reputation_seuil): - # if not db_isTrusted_user(user_id): - - # get user information - get_user = self.User.get_User(uid) - if get_user is None: - self.Logs.error(f'This UID {uid} does not exisit') - - # self.insert_db_reputation(uid, client_ip, nickname, username, hostname, umodes, vhost, client_score, isWebirc) - + # Si le user n'es pas un service ni un IrcOP + if not re.match(fr'^.*[S|o?].*$', _User.umodes): + if reputation_flag == 1 and _User.score_connexion <= reputation_seuil: currentDateTime = self.Base.get_datetime() self.reputation_insert( self.ReputationModel( - uid=uid, - nickname=nickname, - username=username, - hostname=hostname, - umodes=umodes, - vhost=vhost, - ip=client_ip, - score=client_score, - secret_code=self.Base.get_random(8), - isWebirc=isWebirc, - connected_datetime=currentDateTime, + uid=_User.uid, nickname=_User.nickname, username=_User.username, hostname=_User.hostname, + umodes=_User.umodes, vhost=_User.vhost, ip=_User.remote_ip, score=_User.score_connexion, + secret_code=self.Base.get_random(8), isWebirc=_User.isWebirc, connected_datetime=currentDateTime, updated_datetime=currentDateTime ) ) # self.Irc.send2socket(f":{service_id} WHOIS {nickname}") - if self.reputation_check(uid): - if reputation_flag == 1 and int(client_score) <= int(reputation_seuil): - self.system_reputation(uid) + if self.reputation_check(_User.uid): + if reputation_flag == 1 and _User.score_connexion <= reputation_seuil: + self.system_reputation(_User.uid) self.Logs.info('Démarrer le systeme de reputation') case 'SJOIN': @@ -1110,19 +1084,6 @@ class Defender(): try: cmd.pop(0) parsed_chan = cmd[3] - - ''' - mode = '' - if len(cmd) > 4: - mode = cmd[4] - - self.Channel.insert( - self.Channel.ChannelModel( - name=parsed_chan, - mode=mode - ) - ) - ''' if self.ModConfig.reputation == 1: parsed_UID = cmd[4] @@ -1134,6 +1095,10 @@ class Defender(): if not get_reputation is None: isWebirc = get_reputation.isWebirc + if not isWebirc: + if parsed_chan != self.Config.SALON_JAIL: + self.Irc.send2socket(f":{service_id} SAPART {get_reputation.nickname} {parsed_chan}") + if self.ModConfig.reputation_ban_all_chan == 1 and not isWebirc: if parsed_chan != self.Config.SALON_JAIL: self.Irc.send2socket(f":{service_id} MODE {parsed_chan} +b {get_reputation.nickname}!*@*") @@ -1172,24 +1137,25 @@ class Defender(): cmd.pop(0) uid = str(cmd[0]).replace(':','') get_Reputation = self.reputation_get_Reputation(uid) + jail_salon = self.Config.SALON_JAIL + service_id = self.Config.SERVICE_ID if get_Reputation is None: self.Logs.debug(f'This UID: {uid} is not listed in the reputation dataclass') return None + # Update the new nickname oldnick = get_Reputation.nickname newnickname = cmd[2] - - jail_salon = self.Config.SALON_JAIL - service_id = self.Config.SERVICE_ID - - # self.update_db_reputation(uid, newnickname) get_Reputation.nickname = newnickname - for chan in self.Channel.UID_CHANNEL_DB: - if chan.name != jail_salon: - self.Irc.send2socket(f":{service_id} MODE {chan.name} -b {oldnick}!*@*") - self.Irc.send2socket(f":{service_id} MODE {chan.name} +b {newnickname}!*@*") + # If ban in all channel is ON then unban old nickname an ban the new nickname + if self.ModConfig.reputation_ban_all_chan == 1: + for chan in self.Channel.UID_CHANNEL_DB: + if chan.name != jail_salon: + self.Irc.send2socket(f":{service_id} MODE {chan.name} -b {oldnick}!*@*") + self.Irc.send2socket(f":{service_id} MODE {chan.name} +b {newnickname}!*@*") + except KeyError as ke: self.Logs.error(f'cmd - NICK - KeyError: {ke}') @@ -1247,9 +1213,7 @@ class Defender(): try: timer_sent = self.Base.int_if_possible(cmd[1]) timer_sent = int(timer_sent) - # self.Irc.create_ping_timer(timer_sent, 'Defender', 'run_db_action_timer') self.Base.create_timer(timer_sent, self.run_db_action_timer) - # self.Base.create_timer(timer_sent, self.Base.garbage_collector_sockets) except TypeError as te: self.Logs.error(f"Type Error -> {te}") @@ -1262,7 +1226,7 @@ class Defender(): self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} : No one is suspected') for suspect in self.UID_REPUTATION_DB: - self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} : Uid: {suspect.uid} | Nickname: {suspect.nickname} | Connected on: {suspect.connected_datetime} | Updated on: {suspect.updated_datetime}') + self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} : Uid: {suspect.uid} | Nickname: {suspect.nickname} | Reputation: {suspect.score} | Secret code: {suspect.secret_code} | Connected on: {suspect.connected_datetime}') case 'code': try: @@ -1297,21 +1261,20 @@ class Defender(): self.Logs.debug(f'{jailed_UID} - {jailed_nickname} removed from REPUTATION_DB') self.Irc.send2socket(f":{service_id} SAPART {jailed_nickname} {jailed_salon}") self.Irc.send2socket(f":{service_id} SAJOIN {jailed_nickname} {welcome_salon}") - self.Irc.send2socket(f":{link} REPUTATION {jailed_IP} {int(reputation_seuil) + 1}") + self.Irc.send2socket(f":{link} REPUTATION {jailed_IP} {self.ModConfig.reputation_score_after_release}") self.User.get_User(jailed_UID).score_connexion = reputation_seuil + 1 self.Irc.send2socket(f":{service_id} PRIVMSG {jailed_nickname} :[{color_green} MOT DE PASS CORRECT {color_black}] : You have now the right to enjoy the network !") else: self.Irc.send2socket(f':{dnickname} PRIVMSG {jailed_salon} : Mauvais password') self.Irc.send2socket(f":{service_id} PRIVMSG {jailed_nickname} :[{color_green} MAUVAIS PASSWORD {color_black}]") - + except IndexError: self.Logs.error('_hcmd code: out of index') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} code [code]') except KeyError as ke: self.Logs.error(f'_hcmd code: KeyError {ke}') # self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} code [code]') - pass case 'reputation': # .reputation [on/off] --> activate or deactivate reputation system @@ -1336,6 +1299,15 @@ class Defender(): self.Irc.send2socket(f":{service_id} JOIN {jail_chan}") self.Irc.send2socket(f":{service_id} SAMODE {jail_chan} +{dumodes} {dnickname}") self.Irc.send2socket(f":{service_id} MODE {jail_chan} +{jail_chan_mode}") + + if self.ModConfig.reputation_sg == 1: + for chan in self.Channel.UID_CHANNEL_DB: + if chan.name != jail_chan: + self.Irc.send2socket(f":{service_id} MODE {chan.name} +b ~security-group:unknown-users") + self.Irc.send2socket(f":{service_id} MODE {chan.name} +e ~security-group:webirc-users") + self.Irc.send2socket(f":{service_id} MODE {chan.name} +e ~security-group:known-users") + self.Irc.send2socket(f":{service_id} MODE {chan.name} +e ~security-group:websocket-users") + self.add_defender_channel(jail_chan) if activation == 'off': @@ -1349,17 +1321,26 @@ class Defender(): self.Irc.send2socket(f":{service_id} SAMODE {jail_chan} -{dumodes} {dnickname}") self.Irc.send2socket(f":{service_id} MODE {jail_chan} -sS") self.Irc.send2socket(f":{service_id} PART {jail_chan}") + + for chan in self.Channel.UID_CHANNEL_DB: + if chan.name != jail_chan: + self.Irc.send2socket(f":{service_id} MODE {chan.name} -b ~security-group:unknown-users") + self.Irc.send2socket(f":{service_id} MODE {chan.name} -e ~security-group:webirc-users") + self.Irc.send2socket(f":{service_id} MODE {chan.name} -e ~security-group:known-users") + self.Irc.send2socket(f":{service_id} MODE {chan.name} -e ~security-group:websocket-users") + self.delete_defender_channel(jail_chan) if len_cmd == 4: get_set = str(cmd[1]).lower() - + if get_set != 'set': - return False + raise IndexError('Showing help') get_options = str(cmd[2]).lower() match get_options: + case 'banallchan': key = 'reputation_ban_all_chan' get_value = str(cmd[3]).lower() @@ -1379,6 +1360,7 @@ class Defender(): self.update_db_configuration(key, 0) self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}BAN ON ALL CHANS{self.Config.CONFIG_COLOR["noire"]} ] : Deactivated by {fromuser}') + case 'limit': reputation_seuil = int(cmd[3]) key = 'reputation_seuil' @@ -1394,14 +1376,34 @@ class Defender(): self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}REPUTATION TIMER{self.Config.CONFIG_COLOR["noire"]} ] : Timer set to {str(reputation_timer)} minute(s) by {fromuser}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Reputation set to {reputation_timer}') + case 'score_after_release': + reputation_score_after_release = int(cmd[3]) + key = 'reputation_score_after_release' + self.update_db_configuration(key, reputation_score_after_release) + self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}REPUTATION SCORE AFTER RELEASE{self.Config.CONFIG_COLOR["noire"]} ] : Reputation score after release set to {str(reputation_score_after_release)} by {fromuser}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Reputation score after release set to {reputation_score_after_release}') + + case 'security_group': + reputation_sg = int(cmd[3]) + key = 'reputation_sg' + self.update_db_configuration(key, reputation_sg) + self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}REPUTATION SECURITY-GROUP{self.Config.CONFIG_COLOR["noire"]} ] : Reputation Security-group set to {str(reputation_sg)} by {fromuser}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Reputation score after release set to {reputation_sg}') + case _: - pass + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Right command : /msg {dnickname} reputation [ON/OFF]') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Right command : /msg {dnickname} reputation set banallchan [ON/OFF]') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Right command : /msg {dnickname} reputation set limit [1234]') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Right command : /msg {dnickname} reputation set score_after_release [1234]') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Right command : /msg {dnickname} reputation set timer [1234]') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Right command : /msg {dnickname} reputation set action [kill|None]') except IndexError as ie: self.Logs.warning(f'{ie}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation [ON/OFF]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set banallchan [ON/OFF]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set limit [1234]') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set score_after_release [1234]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set timer [1234]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set action [kill|None]') @@ -1583,6 +1585,7 @@ class Defender(): try: self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : [{color_green if self.ModConfig.reputation == 1 else color_red}Reputation{color_black}] ==> {self.ModConfig.reputation}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : reputation_seuil ==> {self.ModConfig.reputation_seuil}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : reputation_after_release ==> {self.ModConfig.reputation_score_after_release}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : reputation_ban_all_chan ==> {self.ModConfig.reputation_ban_all_chan}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : reputation_timer ==> {self.ModConfig.reputation_timer}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : [Proxy_scan]') @@ -1923,8 +1926,3 @@ class Defender(): if not chan.name in channel_to_dont_quit: self.Irc.send2socket(f":{service_id} PART {chan.name}") self.join_saved_channels() - - case 'show_users': - - for db_user in self.User.UID_DB: - self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :UID : {db_user.uid} - isWebirc: {db_user.isWebirc} - Nickname: {db_user.nickname} - Connection: {db_user.connexion_datetime}") diff --git a/version.json b/version.json index b11fda4..0efa03c 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "5.0.6" + "version": "5.0.7" } \ No newline at end of file