diff --git a/.gitignore b/.gitignore index efaa5e4..dfc11aa 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,6 @@ configuration.yaml configuration_inspircd.json configuration_unreal6.json *.log -test.py \ No newline at end of file +test.py +users.txt +modules.txt \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8ddf336 --- /dev/null +++ b/Makefile @@ -0,0 +1,65 @@ +OS := $(shell uname -s) +CURRENT_USER := $(shell whoami) +PYTHON_VERSION := $(shell python3 -V) +HOME_DIR := $(shell echo $$HOME) +SHELL := /bin/bash + +install: +ifeq ($(wildcard config/configuration.yaml),) + $(error You must provide the Configuration file: config/configuration.yaml) +endif + +ifeq ($(OS), Linux) + $(info Installation for os : $(OS)) + $(info Python version: $(PYTHON_VERSION)) + $(info Home directory: $(HOME_DIR)) + + @python3 core/install.py --check-version + @if [ $$? -eq 0 ]; then \ + echo "Python Version OK! Well done :)"; \ + else \ + echo "Error: Script failed with exit code $$?"; \ + exit 1; \ + fi + + $(info Creating the systemd user folder...) + mkdir -p $(HOME_DIR)/.config/systemd/user + + $(info Creating Python Virtual Environment...) + python3 -m venv .pyenv + @. .pyenv/bin/activate && \ + python -m pip install --upgrade pip && \ + pip cache purge && \ + pip install -r requirements.txt + + @. .pyenv/bin/activate && python core/install.py --install + loginctl enable-linger $(CURRENT_USER) + @sleep 2 + @export echo $DBUS_SESSION_BUS_ADDRESS && \ + systemctl --user daemon-reload && \ + systemctl --user start defender + +endif + +clean: +ifeq ($(OS), Linux) + @export echo $DBUS_SESSION_BUS_ADDRESS && \ + systemctl --user stop defender + $(info Defender has been stopped...) + @if [ -e .pyenv ]; then \ + rm -rf .pyenv; \ + echo "Virtual Env has been removed!"; \ + fi + @if [ -e $(HOME_DIR)/.config/systemd/user/defender.service ]; then \ + rm $(HOME_DIR)/.config/systemd/user/defender.service; \ + echo "Systemd file has been removed!"; \ + fi + @export echo $DBUS_SESSION_BUS_ADDRESS && systemctl --user daemon-reload && echo "Systemd Daemon reloaded!" +endif + +update: +ifeq ($(OS), Linux) + $(info Starting update from the main repository...) + @. .pyenv/bin/activate && python core/install.py --git-update + $(info Update done!) +endif diff --git a/README.md b/README.md index ecae94a..d651878 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,11 @@ Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux u - Python version 3.10 ou supérieure ```bash # Bash - $ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git + $ git clone https://github.com/adator85/DEFENDER.git defender + $ cd defender/ # Renommer le fichier exemple_configuration.json en configuration.json # Configurer le fichier configuration.json - $ python3 main.py + $ make install ``` Si votre configuration est bonne, votre service est censé etre connecté a votre réseau IRC Pour Les prochains lancement de defender vous devez utiliser la commande suivante: @@ -49,11 +50,11 @@ Pour Les prochains lancement de defender vous devez utiliser la commande suivant # Installation manuelle: ```bash # Bash - $ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git - $ cd IRC_DEFENDER_MODULES + $ git clone https://github.com/adator85/DEFENDER.git defender + $ cd defender/ $ python3 -m venv .pyenv $ source .pyenv/bin/activate - (pyenv)$ pip install sqlalchemy, psutil, requests, faker, unrealircd_rpc_py, pyyaml + (pyenv)$ pip install -r requirements.txt # Créer un service nommé "defender.service" # pour votre service et placer le dans "/PATH/TO/USER/.config/systemd/user/" @@ -104,87 +105,91 @@ Pour Les prochains lancement de defender vous devez utiliser la commande suivant GLINE_DURATION: Durée de bannissement temporaire d'un utilisateur en minutes. (default : "30") DEBUG (Debug) - DEBUG_LEVEL: Niveau de verbosité des messages de debug (plus grand est le nombre, plus il y a d'informations). (default : 20) Pour une production + DEBUG_LEVEL: Niveau de verbosité des messages de debug (plus petit est le nombre, plus il y a d'informations). (default : 20) Pour une production + DEBUG_HARD: Généralement utiliser pour les developpeurs. ``` Modification de la configuration - Vous devez modifier le fichier configuration.json en remplaçant les valeurs par défaut avec vos propres informations. Assurez-vous de bien lire la description de chaque paramètre pour une configuration optimale du service. + Vous devez modifier le fichier configuration.yaml en remplaçant les valeurs par défaut avec vos propres informations. Assurez-vous de bien lire la description de chaque paramètre pour une configuration optimale du service. ## Exemple de configuration de base -```json -{ - "SERVEUR_IP": "IP.DE.TON.SERVER", - "SERVEUR_HOSTNAME": "HOST.DE.TON.SERVER", - "SERVEUR_LINK": "LINK.DE.TON.SERVER", - "SERVEUR_PORT": 6901, - "SERVEUR_PASSWORD": "MOT_DE_PASS_DE_TON_LINK", - "SERVEUR_ID": "10Z", - "SERVEUR_SSL": true, +```yaml +configuration: + SERVEUR_IP: "YOUR.SERVER.IP" + SERVEUR_HOSTNAME: "YOUR.SERVER.HOST" + SERVEUR_LINK: "LINK.DE.TON.SERVER" + SERVEUR_PORT: 7002 + SERVEUR_PASSWORD: "YOUR_LINK_PASSWORD" + SERVEUR_ID: "006" + SERVEUR_SSL: true - "SERVICE_NAME": "defender", - "SERVICE_NICKNAME": "PyDefender", - "SERVICE_REALNAME": "Python Defender Security", - "SERVICE_USERNAME": "PyDefender", - "SERVICE_HOST": "HOST.DE.TON.DEFENDER", + SERVICE_NAME: "defender" + 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: "ntsOP" + SERVICE_UMODES: "o" + SERVICE_PREFIX: "!" - "OWNER": "TON_NICK_NAME", - "PASSWORD": "TON_PASSWORD" - -} + OWNER: "TON_NICK_NAME" + PASSWORD: "TON_PASSWORD" ``` ## Exemple complet de configuration -```json -{ - "SERVEUR_IP": "YOUR.SERVER.IP", - "SERVEUR_HOSTNAME": "YOUR.SERVER.HOST", - "SERVEUR_LINK": "LINK.DE.TON.SERVER", - "SERVEUR_PORT": 6901, - "SERVEUR_PASSWORD": "YOUR_LINK_PASSWORD", - "SERVEUR_ID": "10Z", - "SERVEUR_SSL": true, +```yaml +configuration: + SERVEUR_IP: "YOUR.SERVER.IP" + SERVEUR_HOSTNAME: "YOUR.SERVER.HOST" + SERVEUR_LINK: "LINK.DE.TON.SERVER" + SERVEUR_PORT: 7002 + SERVEUR_PASSWORD: "YOUR_LINK_PASSWORD" + SERVEUR_ID: "006" + SERVEUR_SSL: true - "SERVICE_NAME": "defender", - "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": "ntsOP", - "SERVICE_UMODES": "o", - "SERVICE_PREFIX": "!", + SERVICE_NAME: "defender" + 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: "ntsOP" + SERVICE_UMODES: "o" + SERVICE_PREFIX: "!" - "OWNER": "TON_NICK_NAME", - "PASSWORD": "TON_PASSWORD", + OWNER: "TON_NICK_NAME" + PASSWORD: "TON_PASSWORD" - "JSONRPC_URL": "https://your.domaine.com:8600/api", - "JSONRPC_PATH_TO_SOCKET_FILE": "/PATH/TO/YOUR/IRCD/data/rpc.socket", - "JSONRPC_METHOD": "socket", - "JSONRPC_USER": "YOUR_RPC_USER", - "JSONRPC_PASSWORD": "YOUR_RPC_PASSWORD", + JSONRPC_URL: "https://your.domaine.com:8600/api" + JSONRPC_PATH_TO_SOCKET_FILE: "/PATH/TO/YOUR/IRCD/data/rpc.socket" + JSONRPC_METHOD: "unixsocket" + JSONRPC_USER: "YOUR_RPC_USER" + JSONRPC_PASSWORD: "YOUR_RPC_PASSWORD" - "SALON_JAIL": "#jail", - "SALON_JAIL_MODES": "sS", - "SALON_LIBERER": "#welcome", + 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", + CLONE_CHANNEL: "#clones" + CLONE_CMODES: "+nts" + CLONE_LOG_HOST_EXEMPT: ["HOST.TO.SKIP"] + CLONE_CHANNEL_PASSWORD: "YOUR_CHANNEL_PASSWORD" - "API_TIMEOUT": 2, + API_TIMEOUT: 2 - "PORTS_TO_SCAN": [3028, 8080, 1080, 1085, 4145, 9050], - "WHITELISTED_IP": ["127.0.0.1"], - "GLINE_DURATION": "30", + PORTS_TO_SCAN: [3028 8080 1080 1085 4145 9050] + WHITELISTED_IP: ["127.0.0.1"] + GLINE_DURATION: "30" - "DEBUG_LEVEL": 20 - -} + DEBUG_LEVEL: 20 + DEBUG_HARD: true ``` # \\!/ Attention \\!/ @@ -192,7 +197,7 @@ Le mot de passe de l'administrateur et le mot de passe du service doivent être Ne partagez pas vos informations de connexion au serveur IRC avec des tiers. a votre premiere connexion vous devez tapez ``` - /msg [NomDuService] auth [nickname] [password] + /msg [NomDuService] firstauth [nickname] [password] -- Une fois identifié tapez la commande suivante /msg [NomDuService] editaccess [nickname] [Nouveau-Password] 5 ``` diff --git a/config/exemple_configuration.yaml b/config/exemple_configuration.yaml index 6bd50a6..5a26500 100644 --- a/config/exemple_configuration.yaml +++ b/config/exemple_configuration.yaml @@ -6,6 +6,7 @@ configuration: SERVEUR_PASSWORD: "YOUR_LINK_PASSWORD" SERVEUR_ID: "006" SERVEUR_SSL: true + SERVEUR_PROTOCOL: "unreal6" # unreal6 or inspircd SERVICE_NAME: "defender" SERVICE_NICKNAME: "PyDefender" @@ -24,7 +25,7 @@ configuration: JSONRPC_URL: "https://your.domaine.com:8600/api" JSONRPC_PATH_TO_SOCKET_FILE: "/PATH/TO/YOUR/IRCD/data/rpc.socket" - JSONRPC_METHOD: "socket" + JSONRPC_METHOD: "unixsocket" JSONRPC_USER: "YOUR_RPC_USER" JSONRPC_PASSWORD: "YOUR_RPC_PASSWORD" @@ -39,8 +40,8 @@ configuration: API_TIMEOUT: 2 - PORTS_TO_SCAN: [3028 8080 1080 1085 4145 9050] - WHITELISTED_IP: ["127.0.0.1"] + PORTS_TO_SCAN: [3028, 8080, 1080, 1085, 4145, 9050] + WHITELISTED_IP: ["127.0.0.1", "192.168.1.1"] GLINE_DURATION: "30" DEBUG_LEVEL: 20 diff --git a/core/__init__.py b/core/__init__.py index e69de29..d87fb0c 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -0,0 +1 @@ +__version__ = '6.3' \ No newline at end of file diff --git a/core/base.py b/core/base.py index 2755125..801c051 100644 --- a/core/base.py +++ b/core/base.py @@ -1,18 +1,15 @@ -import importlib +import asyncio import os import re import json -import sys import time import socket import threading import ipaddress import ast import requests -from pathlib import Path -from types import ModuleType from dataclasses import fields -from typing import Any, Optional, TYPE_CHECKING +from typing import Any, Callable, Iterable, Optional, TYPE_CHECKING from base64 import b64decode, b64encode from sqlalchemy import create_engine, Engine, Connection, CursorResult from sqlalchemy.sql import text @@ -30,7 +27,8 @@ class Base: self.Utils = loader.Utils self.logs = loader.Logs - self.check_for_new_version(True) # Verifier si une nouvelle version est disponible + # Check if new Defender version is available + self.check_for_new_version(True) # Liste des timers en cours self.running_timers: list[threading.Timer] = self.Settings.RUNNING_TIMERS @@ -38,18 +36,26 @@ class Base: # Liste des threads en cours self.running_threads: list[threading.Thread] = self.Settings.RUNNING_THREADS + # List of all async tasks + self.running_asynctasks: list[asyncio.Task] = self.Settings.RUNNING_ASYNCTASKS + # Les sockets ouvert self.running_sockets: list[socket.socket] = self.Settings.RUNNING_SOCKETS # Liste des fonctions en attentes self.periodic_func: dict[object] = self.Settings.PERIODIC_FUNC - # Création du lock - self.lock = self.Settings.LOCK + # Init install variable + self.install: bool = False + + # Init database connection + self.engine, self.cursor = self.db_init() + + # Create the database + # self.__create_db() - self.install: bool = False # Initialisation de la variable d'installation - self.engine, self.cursor = self.db_init() # Initialisation de la connexion a la base de données - self.__create_db() # Initialisation de la base de données + async def init(self) -> None: + await self.__create_db() def __set_current_defender_version(self) -> None: """This will put the current version of Defender @@ -136,7 +142,7 @@ class Base: except Exception as err: self.logs.error(f'General Error: {err}') - def create_log(self, log_message: str) -> None: + async def create_log(self, log_message: str) -> None: """Enregiste les logs Args: @@ -147,11 +153,11 @@ class Base: """ sql_insert = f"INSERT INTO {self.Config.TABLE_LOG} (datetime, server_msg) VALUES (:datetime, :server_msg)" mes_donnees = {'datetime': str(self.Utils.get_sdatetime()),'server_msg': f'{log_message}'} - self.db_execute_query(sql_insert, mes_donnees) + await self.db_execute_query(sql_insert, mes_donnees) return None - def log_cmd(self, user_cmd: str, cmd: str) -> None: + async def log_cmd(self, user_cmd: str, cmd: str) -> None: """Enregistre les commandes envoyées par les utilisateurs Args: @@ -167,11 +173,11 @@ class Base: insert_cmd_query = f"INSERT INTO {self.Config.TABLE_COMMAND} (datetime, user, commande) VALUES (:datetime, :user, :commande)" mes_donnees = {'datetime': self.Utils.get_sdatetime(), 'user': user_cmd, 'commande': cmd} - self.db_execute_query(insert_cmd_query, mes_donnees) + await self.db_execute_query(insert_cmd_query, mes_donnees) return None - def db_sync_core_config(self, module_name: str, dataclassObj: object) -> bool: + async def db_sync_core_config(self, module_name: str, dataclassObj: object) -> bool: """Sync module local parameters with the database if new module then local param will be stored in the database if old module then db param will be moved to the local dataclassObj @@ -198,7 +204,7 @@ class Base: param_to_search = {'module_name': module_name, 'param_key': param_key} search_query = f'''SELECT id FROM {core_table} WHERE module_name = :module_name AND param_key = :param_key''' - excecute_search_query = self.db_execute_query(search_query, param_to_search) + excecute_search_query = await self.db_execute_query(search_query, param_to_search) result_search_query = excecute_search_query.fetchone() if result_search_query is None: @@ -210,7 +216,7 @@ class Base: insert_query = f'''INSERT INTO {core_table} (datetime, module_name, param_key, param_value) VALUES (:datetime, :module_name, :param_key, :param_value) ''' - execution = self.db_execute_query(insert_query, param_to_insert) + execution = await self.db_execute_query(insert_query, param_to_insert) if execution.rowcount > 0: self.logs.debug(f'New parameter added to the database: {param_key} --> {param_value}') @@ -218,14 +224,14 @@ class Base: # Delete from DB unused parameter query_select = f"SELECT module_name, param_key, param_value FROM {core_table} WHERE module_name = :module_name" parameter = {'module_name': module_name} - execute_query_select = self.db_execute_query(query_select, parameter) + execute_query_select = await self.db_execute_query(query_select, parameter) result_query_select = execute_query_select.fetchall() for result in result_query_select: db_mod_name, db_param_key, db_param_value = result if not hasattr(dataclassObj, db_param_key): mes_donnees = {'param_key': db_param_key, 'module_name': db_mod_name} - execute_delete = self.db_execute_query(f'DELETE FROM {core_table} WHERE module_name = :module_name and param_key = :param_key', mes_donnees) + execute_delete = await self.db_execute_query(f'DELETE FROM {core_table} WHERE module_name = :module_name and param_key = :param_key', mes_donnees) row_affected = execute_delete.rowcount if row_affected > 0: self.logs.debug(f'A parameter has been deleted from the database: {db_param_key} --> {db_param_value} | Mod: {db_mod_name}') @@ -233,7 +239,7 @@ class Base: # Sync local variable with Database query = f"SELECT param_key, param_value FROM {core_table} WHERE module_name = :module_name" parameter = {'module_name': module_name} - response = self.db_execute_query(query, parameter) + response = await self.db_execute_query(query, parameter) result = response.fetchall() for param, value in result: @@ -250,43 +256,43 @@ class Base: self.logs.error(err) return False - def db_update_core_config(self, module_name:str, dataclassObj: object, param_key:str, param_value: str) -> bool: + async def db_update_core_config(self, module_name:str, dataclass_obj: object, param_key:str, param_value: str) -> bool: core_table = self.Config.TABLE_CONFIG # Check if the param exist - if not hasattr(dataclassObj, param_key): + if not hasattr(dataclass_obj, param_key): self.logs.error(f"Le parametre {param_key} n'existe pas dans la variable global") return False mes_donnees = {'module_name': module_name, 'param_key': param_key, 'param_value': param_value} search_param_query = f"SELECT id FROM {core_table} WHERE module_name = :module_name AND param_key = :param_key" - result = self.db_execute_query(search_param_query, mes_donnees) - isParamExist = result.fetchone() + result = await self.db_execute_query(search_param_query, mes_donnees) + is_param_exist = result.fetchone() - if not isParamExist is None: + if not is_param_exist is None: mes_donnees = {'datetime': self.Utils.get_sdatetime(), 'module_name': module_name, 'param_key': param_key, 'param_value': param_value } query = f'''UPDATE {core_table} SET datetime = :datetime, param_value = :param_value WHERE module_name = :module_name AND param_key = :param_key''' - update = self.db_execute_query(query, mes_donnees) + update = await self.db_execute_query(query, mes_donnees) updated_rows = update.rowcount if updated_rows > 0: - setattr(dataclassObj, param_key, self.int_if_possible(param_value)) + setattr(dataclass_obj, param_key, self.int_if_possible(param_value)) self.logs.debug(f'Parameter updated : {param_key} - {param_value} | Module: {module_name}') else: self.logs.error(f'Parameter NOT updated : {param_key} - {param_value} | Module: {module_name}') else: self.logs.error(f'Parameter and Module do not exist: Param ({param_key}) - Value ({param_value}) | Module ({module_name})') - self.logs.debug(dataclassObj) + self.logs.debug(dataclass_obj) return True - def db_create_first_admin(self) -> None: + async def db_create_first_admin(self) -> None: - user = self.db_execute_query(f"SELECT id FROM {self.Config.TABLE_ADMIN}") + user = await self.db_execute_query(f"SELECT id FROM {self.Config.TABLE_ADMIN}") if not user.fetchall(): admin = self.Config.OWNER password = self.Utils.hash_password(self.Config.PASSWORD) @@ -299,7 +305,7 @@ class Base: 'language': 'EN', 'level': 5 } - self.db_execute_query(f""" + await self.db_execute_query(f""" INSERT INTO {self.Config.TABLE_ADMIN} (createdOn, user, password, hostname, vhost, language, level) VALUES @@ -325,7 +331,7 @@ class Base: self.logs.error(f'Assertion Error -> {ae}') return None - def create_thread(self, func:object, func_args: tuple = (), run_once:bool = False, daemon: bool = True) -> None: + def create_thread(self, func: object, func_args: tuple = (), run_once: bool = False, daemon: bool = True) -> None: """Create a new thread and store it into running_threads variable Args: @@ -334,6 +340,9 @@ class Base: run_once (bool, optional): If you want to ensure that this method/function run once. Defaults to False. """ try: + # Clean unused threads first + self.garbage_collector_thread() + func_name = func.__name__ if run_once: @@ -347,8 +356,49 @@ class Base: self.running_threads.append(th) self.logs.debug(f"-- Thread ID : {str(th.ident)} | Thread name : {th.name} | Running Threads : {len(threading.enumerate())}") - except AssertionError as ae: - self.logs.error(f'{ae}') + except Exception as err: + self.logs.error(err, exc_info=True) + + def create_asynctask(self, func: Any, *, async_name: str = None, run_once: bool = False) -> asyncio.Task: + """Create a new asynchrone and store it into running_asynctasks variable + + Args: + func (Callable): The function you want to call in asynchrone way + async_name (str, optional): The task name. Defaults to None. + run_once (bool, optional): If true the task will be run once. Defaults to False. + + Returns: + asyncio.Task: The Task + """ + name = func.__name__ if async_name is None else async_name + + if run_once: + for task in asyncio.all_tasks(): + if task.get_name().lower() == async_name.lower(): + return None + + task = asyncio.create_task(func, name=name) + task.add_done_callback(self.asynctask_done) + self.running_asynctasks.append(task) + + self.logs.debug(f"++ New asynchrone task created as: {task.get_name()}") + return task + + def asynctask_done(self, task: asyncio.Task): + """Log task when done + + Args: + task (asyncio.Task): The Asyncio Task callback + """ + try: + if task.exception(): + self.logs.error(f"[ASYNCIO] Task {task.get_name()} failed with exception: {task.exception()}") + else: + self.logs.debug(f"[ASYNCIO] Task {task.get_name()} completed successfully.") + except asyncio.CancelledError as ce: + self.logs.debug(f"[ASYNCIO] Task {task.get_name()} terminated with cancelled error.") + except asyncio.InvalidStateError as ie: + self.logs.debug(f"[ASYNCIO] Task {task.get_name()} terminated with invalid state error.") def is_thread_alive(self, thread_name: str) -> bool: """Check if the thread is still running! using the is_alive method of Threads. @@ -393,7 +443,7 @@ class Base: Returns: int: Number of threads """ - with self.lock: + with self.Settings.LOCK: count = 0 for thr in self.running_threads: @@ -425,11 +475,11 @@ class Base: if thread.name != 'heartbeat': if not thread.is_alive(): self.running_threads.remove(thread) - self.logs.info(f"-- Thread {str(thread.name)} {str(thread.native_id)} removed") + self.logs.debug(f"-- Thread {str(thread.name)} {str(thread.native_id)} has been removed!") # print(threading.enumerate()) - except AssertionError as ae: - self.logs.error(f'Assertion Error -> {ae}') + except Exception as err: + self.logs.error(err, exc_info=True) def garbage_collector_sockets(self) -> None: @@ -442,16 +492,35 @@ class Base: self.running_sockets.remove(soc) self.logs.debug(f"-- Socket ==> closed {str(soc.fileno())}") - def shutdown(self) -> None: + async def shutdown(self) -> None: """Methode qui va préparer l'arrêt complêt du service """ + # Stop RpcServer if running + await self.Loader.RpcServer.stop_server() + + # unload modules. + self.logs.debug(f"=======> Unloading all modules!") + for module in self.Loader.ModuleUtils.model_get_loaded_modules().copy(): + await self.Loader.ModuleUtils.unload_one_module(module.module_name) + + self.logs.debug(f"=======> Closing all Coroutines!") + try: + await asyncio.wait_for(asyncio.gather(*self.running_asynctasks), timeout=5) + except asyncio.exceptions.TimeoutError as te: + self.logs.debug(f"Asyncio Timeout reached! {te}") + for task in self.running_asynctasks: + task.cancel() + except asyncio.exceptions.CancelledError as cerr: + self.logs.debug(f"Asyncio CancelledError reached! {cerr}") + + # Nettoyage des timers self.logs.debug(f"=======> Checking for Timers to stop") for timer in self.running_timers: while timer.is_alive(): self.logs.debug(f"> waiting for {timer.name} to close") timer.cancel() - time.sleep(0.2) + await asyncio.sleep(0.2) self.running_timers.remove(timer) self.logs.debug(f"> Cancelling {timer.name} {timer.native_id}") @@ -472,6 +541,8 @@ class Base: self.running_sockets.remove(soc) self.logs.debug(f"> Socket ==> closed {str(soc.fileno())}") + self.db_close() + return None def db_init(self) -> tuple[Engine, Connection]: @@ -485,10 +556,10 @@ class Base: engine = create_engine(f'sqlite:///{full_path_db}.db', echo=False) cursor = engine.connect() - self.logs.info("-- database connexion has been initiated") + self.logs.info("-- Database connexion has been initiated") return engine, cursor - def __create_db(self) -> None: + async def __create_db(self) -> None: table_core_log = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_LOG} ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -539,6 +610,7 @@ class Base: vhost TEXT, password TEXT, fingerprint TEXT, + language TEXT, level INTEGER ) ''' @@ -557,27 +629,27 @@ class Base: ) ''' - self.db_execute_query(table_core_log) - self.db_execute_query(table_core_log_command) - self.db_execute_query(table_core_module) - self.db_execute_query(table_core_admin) - self.db_execute_query(table_core_client) - self.db_execute_query(table_core_channel) - self.db_execute_query(table_core_config) + await self.db_execute_query(table_core_log) + await self.db_execute_query(table_core_log_command) + await self.db_execute_query(table_core_module) + await self.db_execute_query(table_core_admin) + await self.db_execute_query(table_core_client) + await self.db_execute_query(table_core_channel) + await self.db_execute_query(table_core_config) # Patch database - self.db_patch(self.Config.TABLE_ADMIN, "language", "TEXT") + await self.db_patch(self.Config.TABLE_ADMIN, "language", "TEXT") if self.install: - self.Loader.ModuleUtils.db_register_module('mod_command', 'sys', True) - self.Loader.ModuleUtils.db_register_module('mod_defender', 'sys', True) + await self.Loader.ModuleUtils.db_register_module('mod_command', 'sys', True) + await self.Loader.ModuleUtils.db_register_module('mod_defender', 'sys', True) self.install = False return None - def db_execute_query(self, query:str, params:dict = {}) -> CursorResult: + async def db_execute_query(self, query:str, params:dict = {}) -> CursorResult: - with self.lock: + async with self.Loader.Settings.AILOCK: insert_query = text(query) if not params: response = self.cursor.execute(insert_query) @@ -588,8 +660,8 @@ class Base: return response - def db_is_column_exist(self, table_name: str, column_name: str) -> bool: - q = self.db_execute_query(f"PRAGMA table_info({table_name})") + async def db_is_column_exist(self, table_name: str, column_name: str) -> bool: + q = await self.db_execute_query(f"PRAGMA table_info({table_name})") existing_columns = [col[1] for col in q.fetchall()] if column_name in existing_columns: @@ -597,12 +669,12 @@ class Base: else: return False - def db_patch(self, table_name: str, column_name: str, column_type: str) -> bool: - if not self.db_is_column_exist(table_name, column_name): - patch = f"ALTER TABLE {self.Config.TABLE_ADMIN} ADD COLUMN {column_name} {column_type}" - update_row = f"UPDATE {self.Config.TABLE_ADMIN} SET language = 'EN' WHERE language is null" - self.db_execute_query(patch) - self.db_execute_query(update_row) + async def db_patch(self, table_name: str, column_name: str, column_type: str) -> bool: + if not await self.db_is_column_exist(table_name, column_name): + patch = f"ALTER TABLE {table_name} ADD COLUMN {column_name} {column_type}" + update_row = f"UPDATE {table_name} SET language = 'EN' WHERE language is null" + await self.db_execute_query(patch) + await self.db_execute_query(update_row) self.logs.debug(f"The patch has been applied") self.logs.debug(f"Table name: {table_name}, Column name: {column_name}, Column type: {column_type}") return True @@ -610,9 +682,9 @@ class Base: return False def db_close(self) -> None: - try: self.cursor.close() + self.logs.debug("Database engine closed!") except AttributeError as ae: self.logs.error(f"Attribute Error : {ae}") diff --git a/core/classes/interfaces/imodule.py b/core/classes/interfaces/imodule.py new file mode 100644 index 0000000..7f8c306 --- /dev/null +++ b/core/classes/interfaces/imodule.py @@ -0,0 +1,84 @@ +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Optional, Union +from dataclasses import dataclass + +if TYPE_CHECKING: + from core.loader import Loader + +class IModule(ABC): + + @abstractmethod + @dataclass + class ModConfModel: + """The Model containing the module parameters + """ + + def __init__(self, uplink: 'Loader') -> None: + + # import the context + self.ctx = uplink + + # Module name (Mandatory) + self.module_name = 'mod_' + str(self.__class__.__name__).lower() + + # Log the module + self.ctx.Logs.debug(f'Loading Module {self.module_name} ...') + + async def sync_db(self) -> None: + # Sync the configuration with core configuration (Mandatory) + await self.ctx.Base.db_sync_core_config(self.module_name, self.mod_config) + return None + + async def update_configuration(self, param_key: str, param_value: Union[str, int]) -> None: + """Update the local and core configuration + + Args: + param_key (str): The parameter key + param_value (str): The parameter value + """ + await self.ctx.Base.db_update_core_config(self.module_name, self.mod_config, param_key, param_value) + + @property + @abstractmethod + def mod_config(self) -> ModConfModel: + """ + The module configuration model + """ + + @abstractmethod + def create_tables(self) -> None: + """Method that will create the database if it does not exist. + A single Session for this class will be created, which will be used within this class/module. + + Returns: + None: No return is expected + """ + + @abstractmethod + async def load(self) -> None: + """This method is executed when the module is loaded or reloaded. + """ + + @abstractmethod + async def unload(self) -> None: + """This method is executed when the module is unloaded or reloaded. + """ + + @abstractmethod + async def cmd(self, data: list) -> None: + """When recieving server messages. + + Args: + data (list): The recieved message + """ + + @abstractmethod + async def hcmds(self, user: str, channel: Optional[str], cmd: list[str], fullcmd: Optional[list[str]] = None) -> None: + """These are the commands recieved from a client + + Args: + user (str): The client + channel (str|None): The channel if available + cmd (list): The user command sent + fullcmd (list, optional): The full server message. Defaults to []. + """ \ No newline at end of file diff --git a/core/classes/protocols/interface.py b/core/classes/interfaces/iprotocol.py similarity index 60% rename from core/classes/protocols/interface.py rename to core/classes/interfaces/iprotocol.py index dc3546d..6ff5bfc 100644 --- a/core/classes/protocols/interface.py +++ b/core/classes/interfaces/iprotocol.py @@ -3,13 +3,27 @@ from typing import Optional, TYPE_CHECKING from core.classes.protocols.command_handler import CommandHandler if TYPE_CHECKING: - from core.definition import MClient, MSasl - + from core.definition import MClient, MSasl, MUser, MChannel + from core.loader import Loader class IProtocol(ABC): Handler: Optional[CommandHandler] = None + def __init__(self, context: 'Loader'): + self.name: Optional[str] = None + self.protocol_version: int = -1 + self.known_protocol: set[str] = set() + self._ctx = context + self.Handler = CommandHandler(context) + self.init_protocol() + self._ctx.Logs.info(f"[PROTOCOL] Protocol [{self.__class__.__name__}] loaded!") + + @abstractmethod + def init_protocol(self): + """Init protocol + """ + @abstractmethod def get_ircd_protocol_poisition(self, cmd: list[str], log: bool = False) -> tuple[int, Optional[str]]: """Get the position of known commands @@ -28,7 +42,7 @@ class IProtocol(ABC): """ @abstractmethod - def send2socket(self, message: str, print_log: bool = True) -> None: + async def send2socket(self, message: str, print_log: bool = True) -> None: """Envoit les commandes à envoyer au serveur. Args: @@ -37,7 +51,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_priv_msg(self, nick_from: str, msg: str, channel: str = None, nick_to: str = None): + async def send_priv_msg(self, nick_from: str, msg: str, channel: str = None, nick_to: str = None): """Sending PRIVMSG to a channel or to a nickname by batches could be either channel or nickname not both together Args: @@ -48,7 +62,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_notice(self, nick_from: str, nick_to: str, msg: str) -> None: + async def send_notice(self, nick_from: str, nick_to: str, msg: str) -> None: """Sending NOTICE by batches Args: @@ -58,13 +72,13 @@ class IProtocol(ABC): """ @abstractmethod - def send_link(self) -> None: + async def send_link(self) -> None: """Créer le link et envoyer les informations nécessaires pour la connexion au serveur. """ @abstractmethod - def send_gline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: + async def send_gline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: """Send a gline command to the server Args: @@ -77,7 +91,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_set_nick(self, newnickname: str) -> None: + async def send_set_nick(self, newnickname: str) -> None: """Change nickname of the server \n This method will also update the User object Args: @@ -85,7 +99,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_set_mode(self, modes: str, *, nickname: Optional[str] = None, channel_name: Optional[str] = None, params: Optional[str] = None) -> None: + async def send_set_mode(self, modes: str, *, nickname: Optional[str] = None, channel_name: Optional[str] = None, params: Optional[str] = None) -> None: """Set a mode to channel or to a nickname or for a user in a channel Args: @@ -96,7 +110,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_squit(self, server_id: str, server_link: str, reason: str) -> None: + async def send_squit(self, server_id: str, server_link: str, reason: str) -> None: """_summary_ Args: @@ -106,7 +120,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_ungline(self, nickname:str, hostname: str) -> None: + async def send_ungline(self, nickname:str, hostname: str) -> None: """_summary_ Args: @@ -115,7 +129,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_kline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: + async def send_kline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: """_summary_ Args: @@ -128,7 +142,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_unkline(self, nickname:str, hostname: str) -> None: + async def send_unkline(self, nickname:str, hostname: str) -> None: """_summary_ Args: @@ -137,7 +151,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_sjoin(self, channel: str) -> None: + async def send_sjoin(self, channel: str) -> None: """Server will join a channel with pre defined umodes Args: @@ -145,7 +159,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_sapart(self, nick_to_sapart: str, channel_name: str) -> None: + async def send_sapart(self, nick_to_sapart: str, channel_name: str) -> None: """_summary_ Args: @@ -154,7 +168,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_sajoin(self, nick_to_sajoin: str, channel_name: str) -> None: + async def send_sajoin(self, nick_to_sajoin: str, channel_name: str) -> None: """_summary_ Args: @@ -163,7 +177,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_svspart(self, nick_to_part: str, channels: list[str], reason: str) -> None: + async def send_svspart(self, nick_to_part: str, channels: list[str], reason: str) -> None: """_summary_ Args: @@ -173,7 +187,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_svsjoin(self, nick_to_part: str, channels: list[str], keys: list[str]) -> None: + async def send_svsjoin(self, nick_to_part: str, channels: list[str], keys: list[str]) -> None: """_summary_ Args: @@ -183,7 +197,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_svsmode(self, nickname: str, user_mode: str) -> None: + async def send_svsmode(self, nickname: str, user_mode: str) -> None: """_summary_ Args: @@ -192,7 +206,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_svs2mode(self, nickname: str, user_mode: str) -> None: + async def send_svs2mode(self, nickname: str, user_mode: str) -> None: """_summary_ Args: @@ -201,7 +215,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_svslogin(self, client_uid: str, user_account: str) -> None: + async def send_svslogin(self, client_uid: str, user_account: str) -> None: """Log a client into his account. Args: @@ -210,7 +224,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_svslogout(self, client_obj: 'MClient') -> None: + async def send_svslogout(self, client_obj: 'MClient') -> None: """Logout a client from his account Args: @@ -218,7 +232,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_quit(self, uid: str, reason: str, print_log: bool = True) -> None: + async def send_quit(self, uid: str, reason: str, print_log: bool = True) -> None: """Send quit message - Delete uid from User object - Delete uid from Reputation object @@ -230,7 +244,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_uid(self, nickname:str, username: str, hostname: str, uid:str, umodes: str, vhost: str, remote_ip: str, realname: str, print_log: bool = True) -> None: + async def send_uid(self, nickname:str, username: str, hostname: str, uid:str, umodes: str, vhost: str, remote_ip: str, realname: str, print_log: bool = True) -> None: """Send UID to the server - Insert User to User Object Args: @@ -246,7 +260,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_join_chan(self, uidornickname: str, channel: str, password: str = None, print_log: bool = True) -> None: + async def send_join_chan(self, uidornickname: str, channel: str, password: str = None, print_log: bool = True) -> None: """Joining a channel Args: @@ -257,7 +271,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_part_chan(self, uidornickname:str, channel: str, print_log: bool = True) -> None: + async def send_part_chan(self, uidornickname:str, channel: str, print_log: bool = True) -> None: """Part from a channel Args: @@ -267,7 +281,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_mode_chan(self, channel_name: str, channel_mode: str) -> None: + async def send_mode_chan(self, channel_name: str, channel_mode: str) -> None: """_summary_ Args: @@ -276,7 +290,7 @@ class IProtocol(ABC): """ @abstractmethod - def send_raw(self, raw_command: str) -> None: + async def send_raw(self, raw_command: str) -> None: """Send raw message to the server Args: @@ -288,56 +302,51 @@ class IProtocol(ABC): # ------------------------------------------------------------------------ @abstractmethod - def parse_uid(self, serverMsg: list[str]) -> dict[str, str]: + def parse_uid(self, server_msg: list[str]) -> Optional['MUser']: """Parse UID and return dictionary. Args: - serverMsg (list[str]): The UID IRCD message + server_msg (list[str]): The UID IRCD message Returns: - dict[str, str]: The response as dictionary. + Optional[MUser]: The MUser object or None """ @abstractmethod - def parse_quit(self, serverMsg: list[str]) -> dict[str, str]: + def parse_quit(self, server_msg: list[str]) -> tuple[Optional['MUser'], str]: """Parse quit and return dictionary. >>> [':97KAAAAAB', 'QUIT', ':Quit:', 'this', 'is', 'my', 'reason', 'to', 'quit'] Args: - serverMsg (list[str]): The server message to parse + server_msg (list[str]): The server message to parse Returns: - dict[str, str]: The response as dictionary. + tuple[MUser, str]: The User Who Quit Object and the reason. """ @abstractmethod - def parse_nick(self, serverMsg: list[str]) -> dict[str, str]: + def parse_nick(self, server_msg: list[str]) -> tuple[Optional['MUser'], str, str]: """Parse nick changes and return dictionary. >>> [':97KAAAAAC', 'NICK', 'testinspir', '1757360740'] Args: - serverMsg (list[str]): The server message to parse + server_msg (list[str]): The server message to parse Returns: - dict[str, str]: The response as dictionary. + tuple(MUser, newnickname(str), timestamp(str)): Tuple of the response. + + >>> MUser, newnickname, timestamp """ @abstractmethod - def parse_privmsg(self, serverMsg: list[str]) -> dict[str, str]: + def parse_privmsg(self, server_msg: list[str]) -> tuple[Optional['MUser'], Optional['MUser'], Optional['MChannel'], str]: """Parse PRIVMSG message. >>> [':97KAAAAAE', 'PRIVMSG', '#welcome', ':This', 'is', 'my', 'public', 'message'] Args: - serverMsg (list[str]): The server message to parse + server_msg (list[str]): The server message to parse Returns: - dict[str, str]: The response as dictionary. - ```python - response = { - "uid": '97KAAAAAE', - "channel": '#welcome', - "message": 'This is my public message' - } - ``` + tuple[MUser(Sender), MUser(Reciever), MChannel, str]: Sender user model, reciever user model, Channel model, messgae. """ # ------------------------------------------------------------------------ @@ -345,181 +354,181 @@ class IProtocol(ABC): # ------------------------------------------------------------------------ @abstractmethod - def on_svs2mode(self, serverMsg: list[str]) -> None: + async def on_svs2mode(self, server_msg: list[str]) -> None: """Handle svs2mode coming from a server >>> [':00BAAAAAG', 'SVS2MODE', '001U01R03', '-r'] Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ @abstractmethod - def on_mode(self, serverMsg: list[str]) -> None: + async def on_mode(self, server_msg: list[str]) -> None: """Handle mode coming from a server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ @abstractmethod - def on_umode2(self, serverMsg: list[str]) -> None: + async def on_umode2(self, server_msg: list[str]) -> None: """Handle umode2 coming from a server >>> [':adator_', 'UMODE2', '-i'] Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ @abstractmethod - def on_quit(self, serverMsg: list[str]) -> None: + async def on_quit(self, server_msg: list[str]) -> None: """Handle quit coming from a server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ @abstractmethod - def on_squit(self, serverMsg: list[str]) -> None: + async def on_squit(self, server_msg: list[str]) -> None: """Handle squit coming from a server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ @abstractmethod - def on_protoctl(self, serverMsg: list[str]) -> None: + async def on_protoctl(self, server_msg: list[str]) -> None: """Handle protoctl coming from a server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ @abstractmethod - def on_nick(self, serverMsg: list[str]) -> None: + async def on_nick(self, server_msg: list[str]) -> None: """Handle nick coming from a server new nickname Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ @abstractmethod - def on_sjoin(self, serverMsg: list[str]) -> None: + async def on_sjoin(self, server_msg: list[str]) -> None: """Handle sjoin coming from a server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ @abstractmethod - def on_part(self, serverMsg: list[str]) -> None: + async def on_part(self, server_msg: list[str]) -> None: """Handle part coming from a server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ @abstractmethod - def on_eos(self, serverMsg: list[str]) -> None: + async def on_eos(self, server_msg: list[str]) -> None: """Handle EOS coming from a server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ @abstractmethod - def on_reputation(self, serverMsg: list[str]) -> None: + async def on_reputation(self, server_msg: list[str]) -> None: """Handle REPUTATION coming from a server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ @abstractmethod - def on_uid(self, serverMsg: list[str]) -> None: + async def on_uid(self, server_msg: list[str]) -> None: """Handle uid message coming from the server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ @abstractmethod - def on_privmsg(self, serverMsg: list[str]) -> None: + async def on_privmsg(self, server_msg: list[str]) -> None: """Handle PRIVMSG message coming from the server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ @abstractmethod - def on_server_ping(self, serverMsg: list[str]) -> None: + async def on_server_ping(self, server_msg: list[str]) -> None: """Send a PONG message to the server Args: - serverMsg (list[str]): List of str coming from the server + server_msg (list[str]): List of str coming from the server """ @abstractmethod - def on_server(self, serverMsg: list[str]) -> None: + async def on_server(self, server_msg: list[str]) -> None: """_summary_ Args: - serverMsg (list[str]): _description_ + server_msg (list[str]): _description_ """ @abstractmethod - def on_version(self, serverMsg: list[str]) -> None: + async def on_version(self, server_msg: list[str]) -> None: """Sending Server Version to the server Args: - serverMsg (list[str]): List of str coming from the server + server_msg (list[str]): List of str coming from the server """ @abstractmethod - def on_time(self, serverMsg: list[str]) -> None: + async def on_time(self, server_msg: list[str]) -> None: """Sending TIME answer to a requestor Args: - serverMsg (list[str]): List of str coming from the server + server_msg (list[str]): List of str coming from the server """ @abstractmethod - def on_ping(self, serverMsg: list[str]) -> None: + async def on_ping(self, server_msg: list[str]) -> None: """Sending a PING answer to requestor Args: - serverMsg (list[str]): List of str coming from the server + server_msg (list[str]): List of str coming from the server """ @abstractmethod - def on_version_msg(self, serverMsg: list[str]) -> None: + async def on_version_msg(self, server_msg: list[str]) -> None: """Handle version coming from the server \n ex. /version Defender Args: - serverMsg (list[str]): Original message from the server + server_msg (list[str]): Original message from the server """ @abstractmethod - def on_smod(self, serverMsg: list[str]) -> None: + async def on_smod(self, server_msg: list[str]) -> None: """Handle SMOD message coming from the server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ @abstractmethod - def on_sasl(self, serverMsg: list[str]) -> Optional['MSasl']: + async def on_sasl(self, server_msg: list[str]) -> Optional['MSasl']: """Handle SASL coming from a server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message Returns: """ @abstractmethod - def on_sasl_authentication_process(self, sasl_model: 'MSasl') -> bool: + async def on_sasl_authentication_process(self, sasl_model: 'MSasl') -> bool: """Finalize sasl authentication Args: @@ -530,18 +539,27 @@ class IProtocol(ABC): """ @abstractmethod - def on_md(self, serverMsg: list[str]) -> None: + async def on_md(self, server_msg: list[str]) -> None: """Handle MD responses [':001', 'MD', 'client', '001MYIZ03', 'certfp', ':d1235648...'] Args: - serverMsg (list[str]): The server reply + server_msg (list[str]): The server reply """ @abstractmethod - def on_kick(self, serverMsg: list[str]) -> None: + async def on_kick(self, server_msg: list[str]) -> None: """When a user is kicked out from a channel Eg. ['@unrealircd.org...', ':001', 'KICK', '#jsonrpc', '001ELW13T', ':Kicked', 'from', 'JSONRPC', 'User'] Args: - serverMsg (list[str]): The server message + server_msg (list[str]): The server message """ + + @abstractmethod + async def on_sethost(self, server_msg: list[str]) -> None: + """On SETHOST command + >>> [':001DN7305', 'SETHOST', ':netadmin.example.org'] + + Args: + server_msg (list[str]): _description_ + """ \ No newline at end of file diff --git a/core/classes/interfaces/irpc_endpoint.py b/core/classes/interfaces/irpc_endpoint.py new file mode 100644 index 0000000..0f5d0b4 --- /dev/null +++ b/core/classes/interfaces/irpc_endpoint.py @@ -0,0 +1,34 @@ +import starlette.status as http_status_code +from typing import TYPE_CHECKING +from core.classes.modules.rpc.rpc_errors import JSONRPCErrorCode + +if TYPE_CHECKING: + from core.loader import Loader + +class IRPC: + + def __init__(self, loader: 'Loader'): + self.ctx = loader + self.http_status_code = http_status_code + self.response_model = { + "jsonrpc": "2.0", + "id": 123 + } + + def reset(self): + self.response_model = { + "jsonrpc": "2.0", + "id": 123 + } + + def create_error_response(self, error_code: JSONRPCErrorCode, details: dict = None) -> dict[str, str]: + """Create a JSON-RPC error!""" + response = { + "code": error_code.value, + "message": error_code.description(), + } + + if details: + response["data"] = details + + return response \ No newline at end of file diff --git a/core/classes/admin.py b/core/classes/modules/admin.py similarity index 86% rename from core/classes/admin.py rename to core/classes/modules/admin.py index 7479e36..a71a87d 100644 --- a/core/classes/admin.py +++ b/core/classes/modules/admin.py @@ -93,18 +93,11 @@ class Admin: Returns: bool: True if the admin has been deleted """ - - for record in self.UID_ADMIN_DB: - if record.uid == uidornickname: - # If the admin exist, delete and do not go further - self.UID_ADMIN_DB.remove(record) - self.Logs.debug(f'UID ({record.uid}) has been deleted') - return True - if record.nickname.lower() == uidornickname.lower(): - # If the admin exist, delete and do not go further - self.UID_ADMIN_DB.remove(record) - self.Logs.debug(f'nickname ({record.nickname}) has been deleted') - return True + admin_obj = self.get_admin(uidornickname) + if admin_obj: + self.UID_ADMIN_DB.remove(admin_obj) + self.Logs.debug(f'UID ({admin_obj.uid}) has been deleted') + return True self.Logs.debug(f'The UID {uidornickname} was not deleted') @@ -180,7 +173,7 @@ class Admin: return admin.language - def db_auth_admin_via_fingerprint(self, fp: str, uidornickname: str) -> bool: + async def db_auth_admin_via_fingerprint(self, fp: str, uidornickname: str) -> bool: """Check the fingerprint Args: @@ -190,9 +183,12 @@ class Admin: Returns: bool: True if found """ + if fp is None: + return False + query = f"SELECT user, level, language FROM {self.Config.TABLE_ADMIN} WHERE fingerprint = :fp" data = {'fp': fp} - exe = self.Base.db_execute_query(query, data) + exe = await self.Base.db_execute_query(query, data) result = exe.fetchone() if result: account = result[0] @@ -200,14 +196,15 @@ class Admin: language = result[2] user_obj = self.User.get_user(uidornickname) if user_obj: - admin_obj = self.Definition.MAdmin(**user_obj.to_dict(),account=account, level=level, language=language) + admin_obj = self.Definition.MAdmin(**user_obj.to_dict(), account=account, level=level, language=language) if self.insert(admin_obj): self.Setting.current_admin = admin_obj + self.Logs.debug(f"[Fingerprint login] {user_obj.nickname} ({admin_obj.account}) has been logged in successfully!") return True - return False + return False - def db_is_admin_exist(self, admin_nickname: str) -> bool: + async def db_is_admin_exist(self, admin_nickname: str) -> bool: """Verify if the admin exist in the database! Args: @@ -219,7 +216,7 @@ class Admin: mes_donnees = {'admin': admin_nickname} query_search_user = f"SELECT id FROM {self.Config.TABLE_ADMIN} WHERE user = :admin" - r = self.Base.db_execute_query(query_search_user, mes_donnees) + r = await self.Base.db_execute_query(query_search_user, mes_donnees) exist_user = r.fetchone() if exist_user: return True diff --git a/core/classes/channel.py b/core/classes/modules/channel.py similarity index 72% rename from core/classes/channel.py rename to core/classes/modules/channel.py index 0bcb0fb..0ea36d4 100644 --- a/core/classes/channel.py +++ b/core/classes/modules/channel.py @@ -17,9 +17,7 @@ class Channel: Args: loader (Loader): The Loader Instance """ - self.Logs = loader.Logs - self.Base = loader.Base - self.Utils = loader.Utils + self._ctx = loader def insert(self, new_channel: 'MChannel') -> bool: """This method will insert a new channel and if the channel exist it will update the user list (uids) @@ -34,14 +32,14 @@ class Channel: exist = False if not self.is_valid_channel(new_channel.name): - self.Logs.error(f"The channel {new_channel.name} is not valid, channel must start with #") + self._ctx.Logs.error(f"The channel {new_channel.name} is not valid, channel must start with #") return False for record in self.UID_CHANNEL_DB: if record.name.lower() == new_channel.name.lower(): # If the channel exist, update the user list and do not go further exist = True - # self.Logs.debug(f'{record.name} already exist') + # self._ctx.Logs.debug(f'{record.name} already exist') for user in new_channel.uids: record.uids.append(user) @@ -49,7 +47,7 @@ class Channel: # Supprimer les doublons del_duplicates = list(set(record.uids)) record.uids = del_duplicates - # self.Logs.debug(f'Updating a new UID to the channel {record}') + # self._ctx.Logs.debug(f'Updating a new UID to the channel {record}') return result if not exist: @@ -57,10 +55,10 @@ class Channel: new_channel.name = new_channel.name.lower() self.UID_CHANNEL_DB.append(new_channel) result = True - # self.Logs.debug(f'New Channel Created: ({new_channel})') + # self._ctx.Logs.debug(f'New Channel Created: ({new_channel})') if not result: - self.Logs.critical(f'The Channel Object was not inserted {new_channel}') + self._ctx.Logs.critical(f'The Channel Object was not inserted {new_channel}') self.clean_channel() @@ -103,7 +101,7 @@ class Channel: return result for userid in chan_obj.uids: - if self.Utils.clean_uid(userid) == self.Utils.clean_uid(uid): + if self._ctx.Utils.clean_uid(userid) == self._ctx.Utils.clean_uid(uid): chan_obj.uids.remove(userid) result = True @@ -111,7 +109,7 @@ class Channel: return result except ValueError as ve: - self.Logs.error(f'{ve}') + self._ctx.Logs.error(f'{ve}') return False def delete_user_from_all_channel(self, uid:str) -> bool: @@ -128,7 +126,7 @@ class Channel: for record in self.UID_CHANNEL_DB: for user_id in record.uids: - if self.Utils.clean_uid(user_id) == self.Utils.clean_uid(uid): + if self._ctx.Utils.clean_uid(user_id) == self._ctx.Utils.clean_uid(uid): record.uids.remove(user_id) result = True @@ -136,7 +134,7 @@ class Channel: return result except ValueError as ve: - self.Logs.error(f'{ve}') + self._ctx.Logs.error(f'{ve}') return False def add_user_to_a_channel(self, channel_name: str, uid: str) -> bool: @@ -154,7 +152,7 @@ class Channel: if chan_obj is None: # Create a new channel if the channel don't exist - self.Logs.debug(f"New channel will be created ({channel_name} - {uid})") + self._ctx.Logs.debug(f"New channel will be created ({channel_name} - {uid})") return self.insert(MChannel(channel_name, uids=[uid])) chan_obj.uids.append(uid) @@ -163,7 +161,7 @@ class Channel: return True except Exception as err: - self.Logs.error(f'{err}') + self._ctx.Logs.error(f'{err}') return False def is_user_present_in_channel(self, channel_name: str, uid: str) -> bool: @@ -180,9 +178,9 @@ class Channel: if chan is None: return False - clean_uid = self.Utils.clean_uid(uid=uid) + clean_uid = self._ctx.Utils.clean_uid(uid=uid) for chan_uid in chan.uids: - if self.Utils.clean_uid(chan_uid) == clean_uid: + if self._ctx.Utils.clean_uid(chan_uid) == clean_uid: return True return False @@ -197,7 +195,7 @@ class Channel: return None except Exception as err: - self.Logs.error(f'{err}') + self._ctx.Logs.error(f'{err}') def get_channel(self, channel_name: str) -> Optional['MChannel']: """Get the channel object @@ -237,13 +235,13 @@ class Channel: else: return True except TypeError as te: - self.Logs.error(f'TypeError: [{channel_to_check}] - {te}') + self._ctx.Logs.error(f'TypeError: [{channel_to_check}] - {te}') return False except Exception as err: - self.Logs.error(f'Error Not defined: {err}') + self._ctx.Logs.error(f'Error Not defined: {err}') return False - def db_query_channel(self, action: Literal['add','del'], module_name: str, channel_name: str) -> bool: + async def db_query_channel(self, action: Literal['add','del'], module_name: str, channel_name: str) -> bool: """You can add a channel or delete a channel. Args: @@ -256,39 +254,49 @@ class Channel: """ try: channel_name = channel_name.lower() if self.is_valid_channel(channel_name) else None - core_table = self.Base.Config.TABLE_CHANNEL + core_table = self._ctx.Base.Config.TABLE_CHANNEL if not channel_name: - self.Logs.warning(f'The channel [{channel_name}] is not correct') + self._ctx.Logs.warning(f'The channel [{channel_name}] is not correct') return False match action: case 'add': mes_donnees = {'module_name': module_name, 'channel_name': channel_name} - response = self.Base.db_execute_query(f"SELECT id FROM {core_table} WHERE module_name = :module_name AND channel_name = :channel_name", mes_donnees) + response = await self._ctx.Base.db_execute_query(f"SELECT id FROM {core_table} WHERE module_name = :module_name AND channel_name = :channel_name", mes_donnees) is_channel_exist = response.fetchone() if is_channel_exist is None: - mes_donnees = {'datetime': self.Utils.get_sdatetime(), 'channel_name': channel_name, 'module_name': module_name} - insert = self.Base.db_execute_query(f"INSERT INTO {core_table} (datetime, channel_name, module_name) VALUES (:datetime, :channel_name, :module_name)", mes_donnees) + mes_donnees = {'datetime': self._ctx.Utils.get_sdatetime(), 'channel_name': channel_name, 'module_name': module_name} + insert = await self._ctx.Base.db_execute_query(f"INSERT INTO {core_table} (datetime, channel_name, module_name) VALUES (:datetime, :channel_name, :module_name)", mes_donnees) if insert.rowcount: - self.Logs.debug(f'Channel added to DB: channel={channel_name} / module_name={module_name}') + self._ctx.Logs.debug(f'Channel added to DB: channel={channel_name} / module_name={module_name}') return True else: return False case 'del': mes_donnes = {'channel_name': channel_name, 'module_name': module_name} - response = self.Base.db_execute_query(f"DELETE FROM {core_table} WHERE channel_name = :channel_name AND module_name = :module_name", mes_donnes) + response = await self._ctx.Base.db_execute_query(f"DELETE FROM {core_table} WHERE channel_name = :channel_name AND module_name = :module_name", mes_donnes) if response.rowcount > 0: - self.Logs.debug(f'Channel deleted from DB: channel={channel_name} / module: {module_name}') + self._ctx.Logs.debug(f'Channel deleted from DB: channel={channel_name} / module: {module_name}') return True else: return False except Exception as err: - self.Logs.error(err) + self._ctx.Logs.error(err) return False + + async def db_join_saved_channels(self) -> None: + """## Joining saved channels""" + exec_query = await self._ctx.Base.db_execute_query(f'SELECT distinct channel_name FROM {self._ctx.Config.TABLE_CHANNEL}') + result_query = exec_query.fetchall() + + if result_query: + for chan_name in result_query: + chan = chan_name[0] + await self._ctx.Irc.Protocol.send_sjoin(channel=chan) \ No newline at end of file diff --git a/core/classes/client.py b/core/classes/modules/client.py similarity index 97% rename from core/classes/client.py rename to core/classes/modules/client.py index 2ea01c3..ec422eb 100644 --- a/core/classes/client.py +++ b/core/classes/modules/client.py @@ -201,7 +201,7 @@ class Client: return True - def db_is_account_exist(self, account: str) -> bool: + async def db_is_account_exist(self, account: str) -> bool: """Check if the account exist in the database Args: @@ -213,7 +213,7 @@ class Client: table_client = self.Base.Config.TABLE_CLIENT account_to_check = {'account': account.lower()} - account_to_check_query = self.Base.db_execute_query(f""" + account_to_check_query = await self.Base.db_execute_query(f""" SELECT id FROM {table_client} WHERE LOWER(account) = :account """, account_to_check) diff --git a/core/classes/commands.py b/core/classes/modules/commands.py similarity index 100% rename from core/classes/commands.py rename to core/classes/modules/commands.py diff --git a/core/classes/config.py b/core/classes/modules/config.py similarity index 100% rename from core/classes/config.py rename to core/classes/modules/config.py diff --git a/core/classes/rehash.py b/core/classes/modules/rehash.py similarity index 60% rename from core/classes/rehash.py rename to core/classes/modules/rehash.py index 75c5947..791fe9b 100644 --- a/core/classes/rehash.py +++ b/core/classes/modules/rehash.py @@ -5,79 +5,84 @@ from typing import TYPE_CHECKING import socket if TYPE_CHECKING: - from core.irc import Irc + from core.loader import Loader # Modules impacted by rehashing! REHASH_MODULES = [ 'core.definition', 'core.utils', - 'core.classes.config', + 'core.classes.modules.config', 'core.base', - 'core.classes.commands', - 'core.classes.protocols.interface', + 'core.classes.modules.commands', + 'core.classes.modules.rpc.rpc_channel', + 'core.classes.modules.rpc.rpc_command', + 'core.classes.modules.rpc.rpc_user', + 'core.classes.modules.rpc.rpc', + 'core.classes.interfaces.iprotocol', + 'core.classes.interfaces.imodule', + 'core.classes.protocols.command_handler', 'core.classes.protocols.factory', 'core.classes.protocols.unreal6', 'core.classes.protocols.inspircd' ] -def restart_service(uplink: 'Irc', reason: str = "Restarting with no reason!") -> None: +async def restart_service(uplink: 'Loader', reason: str = "Restarting with no reason!") -> None: """ Args: uplink (Irc): The Irc instance reason (str): The reason of the restart. """ - # reload modules. + # unload modules. for module in uplink.ModuleUtils.model_get_loaded_modules().copy(): - uplink.ModuleUtils.unload_one_module(uplink, module.module_name) + await uplink.ModuleUtils.unload_one_module(module.module_name) + + uplink.Base.garbage_collector_thread() + + uplink.Logs.debug(f'[{uplink.Config.SERVICE_NICKNAME} RESTART]: Reloading configuration!') + await uplink.Irc.Protocol.send_squit(server_id=uplink.Config.SERVEUR_ID, server_link=uplink.Config.SERVEUR_LINK, reason=reason) + uplink.Logs.debug('Restarting Defender ...') + + for mod in REHASH_MODULES: + importlib.reload(sys.modules[mod]) + + # Reload configuration + uplink.Config = uplink.ConfModule.Configuration(uplink).configuration_model + uplink.Base = uplink.BaseModule.Base(uplink) uplink.ModuleUtils.model_clear() # Clear loaded modules. uplink.User.UID_DB.clear() # Clear User Object uplink.Channel.UID_CHANNEL_DB.clear() # Clear Channel Object uplink.Client.CLIENT_DB.clear() # Clear Client object - uplink.Base.garbage_collector_thread() + uplink.Irc.Protocol.Handler.DB_IRCDCOMMS.clear() - uplink.Logs.debug(f'[{uplink.Config.SERVICE_NICKNAME} RESTART]: Reloading configuration!') - uplink.Protocol.send_squit(server_id=uplink.Config.SERVEUR_ID, server_link=uplink.Config.SERVEUR_LINK, reason=reason) - uplink.Logs.debug('Restarting Defender ...') - uplink.IrcSocket.shutdown(socket.SHUT_RDWR) - uplink.IrcSocket.close() + # Reload Service modules + for module in uplink.ModuleUtils.model_get_loaded_modules().copy(): + await uplink.ModuleUtils.reload_one_module(module.module_name, uplink.Settings.current_admin) - while uplink.IrcSocket.fileno() != -1: - time.sleep(0.5) - uplink.Logs.warning('-- Waiting for socket to close ...') - - # Reload configuration - uplink.Loader.Config = uplink.Loader.ConfModule.Configuration(uplink.Loader).configuration_model - uplink.Loader.Base = uplink.Loader.BaseModule.Base(uplink.Loader) - - for mod in REHASH_MODULES: - importlib.reload(sys.modules[mod]) - - uplink.Protocol = uplink.Loader.PFactory.get() - uplink.Protocol.register_command() - - uplink.init_service_user() - uplink.Utils.create_socket(uplink) - uplink.Protocol.send_link() + uplink.Irc.signal = True + await uplink.Irc.run() uplink.Config.DEFENDER_RESTART = 0 -def rehash_service(uplink: 'Irc', nickname: str) -> None: +async def rehash_service(uplink: 'Loader', nickname: str) -> None: need_a_restart = ["SERVEUR_ID"] uplink.Settings.set_cache('db_commands', uplink.Commands.DB_COMMANDS) + + await uplink.RpcServer.stop_server() + restart_flag = False config_model_bakcup = uplink.Config mods = REHASH_MODULES for mod in mods: importlib.reload(sys.modules[mod]) - uplink.Protocol.send_priv_msg( + await uplink.Irc.Protocol.send_priv_msg( nick_from=uplink.Config.SERVICE_NICKNAME, msg=f'[REHASH] Module [{mod}] reloaded', channel=uplink.Config.SERVICE_CHANLOG ) uplink.Utils = sys.modules['core.utils'] - uplink.Config = uplink.Loader.ConfModule.Configuration(uplink.Loader).configuration_model + uplink.Config = uplink.ConfModule.Configuration(uplink).configuration_model uplink.Config.HSID = config_model_bakcup.HSID uplink.Config.DEFENDER_INIT = config_model_bakcup.DEFENDER_INIT uplink.Config.DEFENDER_RESTART = config_model_bakcup.DEFENDER_RESTART @@ -90,7 +95,7 @@ def rehash_service(uplink: 'Irc', nickname: str) -> None: for key, value in conf_bkp_dict.items(): if config_dict[key] != value and key != 'COLORS': - uplink.Protocol.send_priv_msg( + await uplink.Irc.Protocol.send_priv_msg( nick_from=uplink.Config.SERVICE_NICKNAME, msg=f'[{key}]: {value} ==> {config_dict[key]}', channel=uplink.Config.SERVICE_CHANLOG @@ -99,25 +104,28 @@ def rehash_service(uplink: 'Irc', nickname: str) -> None: restart_flag = True if config_model_bakcup.SERVICE_NICKNAME != uplink.Config.SERVICE_NICKNAME: - uplink.Protocol.send_set_nick(uplink.Config.SERVICE_NICKNAME) + await uplink.Irc.Protocol.send_set_nick(uplink.Config.SERVICE_NICKNAME) if restart_flag: uplink.Config.SERVEUR_ID = config_model_bakcup.SERVEUR_ID - uplink.Protocol.send_priv_msg( + await uplink.Irc.Protocol.send_priv_msg( nick_from=uplink.Config.SERVICE_NICKNAME, channel=uplink.Config.SERVICE_CHANLOG, msg='You need to restart defender !') # Reload Main Commands Module - uplink.Commands = uplink.Loader.CommandModule.Command(uplink.Loader) + uplink.Commands = uplink.CommandModule.Command(uplink) uplink.Commands.DB_COMMANDS = uplink.Settings.get_cache('db_commands') - uplink.Loader.Base = uplink.Loader.BaseModule.Base(uplink.Loader) - uplink.Protocol = uplink.Loader.PFactory.get() - uplink.Protocol.register_command() + uplink.Base = uplink.BaseModule.Base(uplink) + uplink.Irc.Protocol = uplink.PFactory.get() + uplink.Irc.Protocol.register_command() + + uplink.RpcServer = uplink.RpcServerModule.JSonRpcServer(uplink) + uplink.Base.create_asynctask(uplink.RpcServer.start_server()) # Reload Service modules for module in uplink.ModuleUtils.model_get_loaded_modules().copy(): - uplink.ModuleUtils.reload_one_module(uplink, module.module_name, nickname) + await uplink.ModuleUtils.reload_one_module(module.module_name, nickname) return None \ No newline at end of file diff --git a/core/classes/reputation.py b/core/classes/modules/reputation.py similarity index 100% rename from core/classes/reputation.py rename to core/classes/modules/reputation.py diff --git a/core/classes/modules/rpc/__init__.py b/core/classes/modules/rpc/__init__.py new file mode 100644 index 0000000..75977e6 --- /dev/null +++ b/core/classes/modules/rpc/__init__.py @@ -0,0 +1 @@ +__version__ = '1.0.0' \ No newline at end of file diff --git a/core/classes/modules/rpc/rpc.py b/core/classes/modules/rpc/rpc.py new file mode 100644 index 0000000..1b6646d --- /dev/null +++ b/core/classes/modules/rpc/rpc.py @@ -0,0 +1,139 @@ +import base64 +import json +import uvicorn +import core.classes.modules.rpc.rpc_errors as rpcerr +import starlette.status as http_status_code +from starlette.applications import Starlette +from starlette.responses import JSONResponse +from starlette.requests import Request +from starlette.routing import Route +from typing import TYPE_CHECKING, Any, Optional +from core.classes.modules.rpc.rpc_user import RPCUser +from core.classes.modules.rpc.rpc_channel import RPCChannel +from core.classes.modules.rpc.rpc_command import RPCCommand + +if TYPE_CHECKING: + from core.loader import Loader + +class JSonRpcServer: + + def __init__(self, context: 'Loader', *, hostname: str = 'localhost', port: int = 5000): + self._ctx = context + self.live: bool = False + self.host = hostname + self.port = port + self.routes: list[Route] = [] + self.server: Optional[uvicorn.Server] = None + + self.methods: dict = { + 'user.list': RPCUser(context).user_list, + 'user.get': RPCUser(context).user_get, + 'channel.list': RPCChannel(context).channel_list, + 'command.list': RPCCommand(context).command_list, + 'command.get.by.name': RPCCommand(context).command_get_by_name, + 'command.get.by.module': RPCCommand(context).command_get_by_module + } + + async def start_server(self): + + if not self.live: + self.routes = [Route('/api', self.request_handler, methods=['POST'])] + self.app_jsonrpc = Starlette(debug=False, routes=self.routes) + config = uvicorn.Config(self.app_jsonrpc, host=self.host, port=self.port, log_level=self._ctx.Config.DEBUG_LEVEL) + self.server = uvicorn.Server(config) + self.live = True + await self._ctx.Irc.Protocol.send_priv_msg( + self._ctx.Config.SERVICE_NICKNAME, + "[DEFENDER JSONRPC SERVER] RPC Server started!", + self._ctx.Config.SERVICE_CHANLOG + ) + await self.server.serve() + self._ctx.Logs.debug("Server is going to shutdown!") + else: + self._ctx.Logs.debug("Server already running") + + async def stop_server(self): + + if self.server: + self.server.should_exit = True + await self.server.shutdown() + self.live = False + self._ctx.Logs.debug("JSON-RPC Server off!") + await self._ctx.Irc.Protocol.send_priv_msg( + self._ctx.Config.SERVICE_NICKNAME, + "[DEFENDER JSONRPC SERVER] RPC Server Stopped!", + self._ctx.Config.SERVICE_CHANLOG + ) + + async def request_handler(self, request: Request) -> JSONResponse: + + request_data: dict = await request.json() + method = request_data.get("method", None) + params: dict[str, Any] = request_data.get("params", {}) + + auth: JSONResponse = self.authenticate(request.headers, request_data) + if not json.loads(auth.body).get('result', False): + return auth + + response_data = { + "jsonrpc": "2.0", + "id": request_data.get('id', 123) + } + + response_data['method'] = method + rip = request.client.host + rport = request.client.port + http_code = http_status_code.HTTP_200_OK + + if method in self.methods: + r: JSONResponse = self.methods[method](**params) + resp = json.loads(r.body) + resp['id'] = request_data.get('id', 123) + return JSONResponse(resp, r.status_code) + + response_data['error'] = rpcerr.create_error_response(rpcerr.JSONRPCErrorCode.METHOD_NOT_FOUND) + self._ctx.Logs.debug(f'[RPC ERROR] {method} recieved from {rip}:{rport}') + http_code = http_status_code.HTTP_404_NOT_FOUND + return JSONResponse(response_data, http_code) + + def authenticate(self, headers: dict, body: dict) -> JSONResponse: + ok_auth = { + 'jsonrpc': '2.0', + 'id': body.get('id', 123), + 'result': True + } + + logs = self._ctx.Logs + auth: str = headers.get('Authorization', '') + if not auth: + return self.send_auth_error(body) + + # Authorization header format: Basic base64(username:password) + auth_type, auth_string = auth.split(' ', 1) + if auth_type.lower() != 'basic': + return self.send_auth_error(body) + + try: + # Decode the base64-encoded username:password + decoded_credentials = base64.b64decode(auth_string).decode('utf-8') + username, password = decoded_credentials.split(":", 1) + + # Check the username and password. + for rpcuser in self._ctx.Config.RPC_USERS: + if rpcuser.get('USERNAME', None) == username and rpcuser.get('PASSWORD', None) == password: + return JSONResponse(ok_auth) + + return self.send_auth_error(body) + + except Exception as e: + logs.error(e) + return self.send_auth_error(body) + + def send_auth_error(self, request_data: dict) -> JSONResponse: + + response_data = { + 'jsonrpc': '2.0', + 'id': request_data.get('id', 123), + 'error': rpcerr.create_error_response(rpcerr.JSONRPCErrorCode.AUTHENTICATION_ERROR) + } + return JSONResponse(response_data, http_status_code.HTTP_403_FORBIDDEN) diff --git a/core/classes/modules/rpc/rpc_channel.py b/core/classes/modules/rpc/rpc_channel.py new file mode 100644 index 0000000..b1388eb --- /dev/null +++ b/core/classes/modules/rpc/rpc_channel.py @@ -0,0 +1,17 @@ +from typing import TYPE_CHECKING + +from starlette.responses import JSONResponse +from core.classes.interfaces.irpc_endpoint import IRPC +from core.classes.modules.rpc.rpc_errors import JSONRPCErrorCode + +if TYPE_CHECKING: + from core.loader import Loader + +class RPCChannel(IRPC): + def __init__(self, loader: 'Loader'): + super().__init__(loader) + + def channel_list(self, **kwargs) -> JSONResponse: + self.reset() + self.response_model['result'] = [chan.to_dict() for chan in self.ctx.Channel.UID_CHANNEL_DB] + return JSONResponse(self.response_model) \ No newline at end of file diff --git a/core/classes/modules/rpc/rpc_command.py b/core/classes/modules/rpc/rpc_command.py new file mode 100644 index 0000000..ebf0691 --- /dev/null +++ b/core/classes/modules/rpc/rpc_command.py @@ -0,0 +1,44 @@ +from typing import TYPE_CHECKING +from starlette.responses import JSONResponse +from core.classes.interfaces.irpc_endpoint import IRPC +from core.classes.modules.rpc.rpc_errors import JSONRPCErrorCode + +if TYPE_CHECKING: + from core.loader import Loader + +class RPCCommand(IRPC): + def __init__(self, loader: 'Loader'): + super().__init__(loader) + + def command_list(self, **kwargs) -> JSONResponse: + self.reset() + self.response_model['result'] = [command.to_dict() for command in self.ctx.Commands.DB_COMMANDS] + return JSONResponse(self.response_model) + + def command_get_by_module(self, **kwargs) -> JSONResponse: + self.reset() + module_name: str = kwargs.get('module_name', '') + + if not module_name: + self.response_model['error'] = self.create_error_response(JSONRPCErrorCode.INVALID_PARAMS, {'module_name': 'The param to use is module_name'}) + return JSONResponse(self.response_model, self.http_status_code.HTTP_405_METHOD_NOT_ALLOWED) + + self.response_model['result'] = [command.to_dict() for command in self.ctx.Commands.DB_COMMANDS if command.module_name.lower() == module_name.lower()] + return JSONResponse(self.response_model) + + def command_get_by_name(self, **kwargs) -> JSONResponse: + self.reset() + + command_name: str = kwargs.get('command_name', '') + if not command_name: + self.response_model['error'] = self.create_error_response(JSONRPCErrorCode.INVALID_PARAMS, {'command_name': f'The param to use is command_name'}) + return JSONResponse(self.response_model, self.http_status_code.HTTP_405_METHOD_NOT_ALLOWED) + + command_to_return: list[dict] = [] + for command in self.ctx.Commands.DB_COMMANDS: + if command.command_name.lower() == command_name.lower(): + command_to_return.append(command.to_dict()) + + self.response_model['result'] = command_to_return + + return JSONResponse(self.response_model) \ No newline at end of file diff --git a/core/classes/modules/rpc/rpc_errors.py b/core/classes/modules/rpc/rpc_errors.py new file mode 100644 index 0000000..dc9fb90 --- /dev/null +++ b/core/classes/modules/rpc/rpc_errors.py @@ -0,0 +1,43 @@ +from enum import Enum + +class JSONRPCErrorCode(Enum): + PARSE_ERROR = -32700 # Syntax error in the request (malformed JSON) + INVALID_REQUEST = -32600 # Invalid Request (incorrect structure or missing fields) + METHOD_NOT_FOUND = -32601 # Method not found (the requested method does not exist) + INVALID_PARAMS = -32602 # Invalid Params (the parameters provided are incorrect) + INTERNAL_ERROR = -32603 # Internal Error (an internal server error occurred) + + # Custom application-specific errors (beyond standard JSON-RPC codes) + CUSTOM_ERROR = 1001 # Custom application-defined error (e.g., user not found) + AUTHENTICATION_ERROR = 1002 # Authentication failure (e.g., invalid credentials) + PERMISSION_ERROR = 1003 # Permission error (e.g., user does not have access to this method) + RESOURCE_NOT_FOUND = 1004 # Resource not found (e.g., the requested resource does not exist) + DUPLICATE_REQUEST = 1005 # Duplicate request (e.g., a similar request has already been processed) + + def description(self): + """Returns a description associated with each error code""" + descriptions = { + JSONRPCErrorCode.PARSE_ERROR: "The JSON request is malformed.", + JSONRPCErrorCode.INVALID_REQUEST: "The request is invalid (missing or incorrect fields).", + JSONRPCErrorCode.METHOD_NOT_FOUND: "The requested method could not be found.", + JSONRPCErrorCode.INVALID_PARAMS: "The parameters provided are invalid.", + JSONRPCErrorCode.INTERNAL_ERROR: "An internal error occurred on the server.", + JSONRPCErrorCode.CUSTOM_ERROR: "A custom error defined by the application.", + JSONRPCErrorCode.AUTHENTICATION_ERROR: "User authentication failed.", + JSONRPCErrorCode.PERMISSION_ERROR: "User does not have permission to access this method.", + JSONRPCErrorCode.RESOURCE_NOT_FOUND: "The requested resource could not be found.", + JSONRPCErrorCode.DUPLICATE_REQUEST: "The request is a duplicate or is already being processed.", + } + return descriptions.get(self, "Unknown error") + +def create_error_response(error_code: JSONRPCErrorCode, details: dict = None) -> dict[str, str]: + """Create a JSON-RPC error!""" + response = { + "code": error_code.value, + "message": error_code.description(), + } + + if details: + response["data"] = details + + return response \ No newline at end of file diff --git a/core/classes/modules/rpc/rpc_user.py b/core/classes/modules/rpc/rpc_user.py new file mode 100644 index 0000000..667005a --- /dev/null +++ b/core/classes/modules/rpc/rpc_user.py @@ -0,0 +1,45 @@ +from typing import TYPE_CHECKING, Optional + +from starlette.responses import JSONResponse +from core.classes.interfaces.irpc_endpoint import IRPC +from core.classes.modules.rpc.rpc_errors import JSONRPCErrorCode + +if TYPE_CHECKING: + from core.loader import Loader + from core.definition import MUser + +class RPCUser(IRPC): + def __init__(self, loader: 'Loader'): + super().__init__(loader) + + def user_list(self, **kwargs) -> JSONResponse: + self.reset() + users = self.ctx.User.UID_DB.copy() + copy_users: list['MUser'] = [] + + for user in users: + copy_user = user.copy() + copy_user.connexion_datetime = copy_user.connexion_datetime.strftime('%d-%m-%Y') + copy_users.append(copy_user) + + self.response_model['result'] = [user.to_dict() for user in copy_users] + + return JSONResponse(self.response_model) + + def user_get(self, **kwargs) -> JSONResponse: + self.reset() + uidornickname = kwargs.get('uid_or_nickname', '') + + if not uidornickname: + self.response_model['error'] = self.create_error_response(JSONRPCErrorCode.INVALID_PARAMS, {'uid_or_nickname': 'The param to use is uid_or_nickname'}) + return JSONResponse(self.response_model, self.http_status_code.HTTP_405_METHOD_NOT_ALLOWED) + + user = self.ctx.User.get_user(uidornickname) + if user: + user_copy = user.copy() + user_copy.connexion_datetime = user_copy.connexion_datetime.strftime('%d-%m-%Y') + self.response_model['result'] = user_copy.to_dict() + return JSONResponse(self.response_model) + + self.response_model['result'] = 'User not found!' + return JSONResponse(self.response_model, self.http_status_code.HTTP_204_NO_CONTENT) \ No newline at end of file diff --git a/core/classes/sasl.py b/core/classes/modules/sasl.py similarity index 100% rename from core/classes/sasl.py rename to core/classes/modules/sasl.py diff --git a/core/classes/settings.py b/core/classes/modules/settings.py similarity index 95% rename from core/classes/settings.py rename to core/classes/modules/settings.py index 913fbbc..216920e 100644 --- a/core/classes/settings.py +++ b/core/classes/modules/settings.py @@ -1,13 +1,15 @@ """This class should never be reloaded. """ +import asyncio from logging import Logger from threading import Timer, Thread, RLock +from asyncio.locks import Lock from socket import socket from typing import Any, Optional, TYPE_CHECKING from core.definition import MSModule, MAdmin if TYPE_CHECKING: - from core.classes.user import User + from core.classes.modules.user import User class Settings: """This Class will never be reloaded. @@ -17,9 +19,11 @@ class Settings: RUNNING_TIMERS: list[Timer] = [] RUNNING_THREADS: list[Thread] = [] + RUNNING_ASYNCTASKS: list[asyncio.Task] = [] RUNNING_SOCKETS: list[socket] = [] PERIODIC_FUNC: dict[str, Any] = {} LOCK: RLock = RLock() + AILOCK: Lock = Lock() CONSOLE: bool = False diff --git a/core/classes/translation.py b/core/classes/modules/translation.py similarity index 100% rename from core/classes/translation.py rename to core/classes/modules/translation.py diff --git a/core/classes/user.py b/core/classes/modules/user.py similarity index 100% rename from core/classes/user.py rename to core/classes/modules/user.py diff --git a/core/classes/protocols/factory.py b/core/classes/protocols/factory.py index 7ccf267..0ae4425 100644 --- a/core/classes/protocols/factory.py +++ b/core/classes/protocols/factory.py @@ -1,33 +1,32 @@ from typing import TYPE_CHECKING, Optional from .unreal6 import Unrealircd6 from .inspircd import Inspircd -from .interface import IProtocol +from ..interfaces.iprotocol import IProtocol if TYPE_CHECKING: - from core.irc import Irc + from core.loader import Loader class ProtocolFactorty: - def __init__(self, uplink: 'Irc'): + def __init__(self, context: 'Loader'): """ProtocolFactory init. Args: - uplink (Irc): The Irc object + context (Loader): The Context object """ - self.__Config = uplink.Config - self.__uplink = uplink + self.__ctx = context def get(self) -> Optional[IProtocol]: - protocol = self.__Config.SERVEUR_PROTOCOL + protocol = self.__ctx.Config.SERVEUR_PROTOCOL match protocol: case 'unreal6': - self.__uplink.Logs.debug(f"[PROTOCOL] {protocol} has been loaded") - return Unrealircd6(self.__uplink) + self.__ctx.Logs.debug(f"[PROTOCOL] {protocol} has been loaded") + return Unrealircd6(self.__ctx) case 'inspircd': - self.__uplink.Logs.debug(f"[PROTOCOL] {protocol} has been loaded") - return Inspircd(self.__uplink) + self.__ctx.Logs.debug(f"[PROTOCOL] {protocol} has been loaded") + return Inspircd(self.__ctx) case _: - self.__uplink.Logs.critical(f"[PROTOCOL ERROR] This protocol name ({protocol} is not valid!)") + self.__ctx.Logs.critical(f"[PROTOCOL ERROR] This protocol name ({protocol} is not valid!)") raise Exception("Unknown protocol!") diff --git a/core/classes/protocols/inspircd.py b/core/classes/protocols/inspircd.py index a4957b2..f8a6f71 100644 --- a/core/classes/protocols/inspircd.py +++ b/core/classes/protocols/inspircd.py @@ -1,35 +1,21 @@ import sys from base64 import b64decode -from re import match, findall, search +from re import A, match, findall, search from datetime import datetime from typing import TYPE_CHECKING, Any, Optional from ssl import SSLEOFError, SSLError -from core.classes.protocols.command_handler import CommandHandler -from core.classes.protocols.interface import IProtocol +from core.classes.interfaces.iprotocol import IProtocol from core.utils import tr if TYPE_CHECKING: - from core.irc import Irc - from core.definition import MSasl, MClient + from core.definition import MSasl, MClient, MUser, MChannel class Inspircd(IProtocol): - def __init__(self, uplink: 'Irc'): - """ - - Args: - uplink (Irc): The Irc object - """ + async def init_protocol(self): self.name = 'InspIRCd-4' self.protocol_version = 1206 - self.__Irc = uplink - self.__Config = uplink.Config - self.__Base = uplink.Base - self.__Utils = uplink.Loader.Utils - self.__Settings = uplink.Settings - self.__Logs = uplink.Loader.Logs - self.known_protocol: set[str] = {'UID', 'ERROR', 'PRIVMSG', 'SINFO', 'FJOIN', 'PING', 'PONG', 'SASL', 'PART', 'CAPAB', 'ENDBURST', @@ -37,11 +23,7 @@ class Inspircd(IProtocol): 'MODE', 'QUIT', 'SQUIT', 'VERSION'} - self.Handler = CommandHandler(uplink.Loader) - - self.__Logs.info(f"[PROTOCOL] Protocol [{__name__}] loaded!") - - def get_ircd_protocol_poisition(self, cmd: list[str], log: bool = False) -> tuple[int, Optional[str]]: + async def get_ircd_protocol_poisition(self, cmd: list[str], log: bool = False) -> tuple[int, Optional[str]]: """Get the position of known commands Args: @@ -56,12 +38,12 @@ class Inspircd(IProtocol): return index, token.upper() if log: - self.__Logs.debug(f"[IRCD LOGS] You need to handle this response: {cmd}") + self._ctx.Logs.debug(f"[IRCD LOGS] You need to handle this response: {cmd}") return -1, None - def register_command(self): - m = self.__Irc.Loader.Definition.MIrcdCommand + async def register_command(self): + m = self._ctx.Definition.MIrcdCommand self.Handler.register(m('PING', self.on_server_ping)) self.Handler.register(m('NICK', self.on_nick)) self.Handler.register(m('SASL', self.on_sasl)) @@ -76,7 +58,7 @@ class Inspircd(IProtocol): self.Handler.register(m('ENDBURST', self.on_endburst)) self.Handler.register(m('METADATA', self.on_metedata)) - def send2socket(self, message: str, print_log: bool = True) -> None: + async def send2socket(self, message: str, print_log: bool = True) -> None: """Envoit les commandes à envoyer au serveur. Args: @@ -84,31 +66,32 @@ class Inspircd(IProtocol): print_log (bool): if True print the log. """ try: - with self.__Base.lock: - self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[0])) + with self._ctx.Settings.AILOCK: + self._ctx.Irc.writer.write(f"{message}\r\n".encode(self._ctx.Config.SERVEUR_CHARSET[0])) + await self._ctx.Irc.writer.drain() if print_log: - self.__Logs.debug(f'<< {message}') + self._ctx.Logs.debug(f'<< {message}') except UnicodeDecodeError as ude: - self.__Logs.error(f'Decode Error try iso-8859-1 - {ude} - {message}') - self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[1],'replace')) + self._ctx.Logs.error(f'Decode Error try iso-8859-1 - {ude} - {message}') + self._ctx.Irc.writer.write(f"{message}\r\n".encode(self._ctx.Config.SERVEUR_CHARSET[1],'replace')) except UnicodeEncodeError as uee: - self.__Logs.error(f'Encode Error try iso-8859-1 - {uee} - {message}') - self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[1],'replace')) + self._ctx.Logs.error(f'Encode Error try iso-8859-1 - {uee} - {message}') + self._ctx.Irc.writer.write(f"{message}\r\n".encode(self._ctx.Config.SERVEUR_CHARSET[1],'replace')) except AssertionError as ae: - self.__Logs.warning(f'Assertion Error {ae} - message: {message}') + self._ctx.Logs.warning(f'Assertion Error {ae} - message: {message}') except SSLEOFError as soe: - self.__Logs.error(f"SSLEOFError: {soe} - {message}") + self._ctx.Logs.error(f"SSLEOFError: {soe} - {message}") except SSLError as se: - self.__Logs.error(f"SSLError: {se} - {message}") + self._ctx.Logs.error(f"SSLError: {se} - {message}") except OSError as oe: - self.__Logs.error(f"OSError: {oe} - {message}") + self._ctx.Logs.error(f"OSError: {oe} - {message}") if oe.errno == 10053: sys.exit(oe.__str__()) except AttributeError as ae: - self.__Logs.critical(f"Attribute Error: {ae}") + self._ctx.Logs.critical(f"Attribute Error: {ae}") - def send_priv_msg(self, nick_from: str, msg: str, channel: str = None, nick_to: str = None): + async def send_priv_msg(self, nick_from: str, msg: str, channel: str = None, nick_to: str = None): """Sending PRIVMSG to a channel or to a nickname by batches could be either channel or nickname not both together Args: @@ -118,27 +101,27 @@ class Inspircd(IProtocol): nick_to (str, optional): The reciever nickname. Defaults to None. """ try: - batch_size = self.__Config.BATCH_SIZE - user_from = self.__Irc.User.get_user(nick_from) - user_to = self.__Irc.User.get_user(nick_to) if nick_to is not None else None + batch_size = self._ctx.Config.BATCH_SIZE + user_from = self._ctx.User.get_user(nick_from) + user_to = self._ctx.User.get_user(nick_to) if nick_to is not None else None if user_from is None: - self.__Logs.error(f"The sender nickname [{nick_from}] do not exist") + self._ctx.Logs.error(f"The sender nickname [{nick_from}] do not exist") return None 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":{user_from.uid} PRIVMSG {channel} :{batch}") + await self.send2socket(f":{user_from.uid} PRIVMSG {channel} :{batch}") if not nick_to is None: for i in range(0, len(str(msg)), batch_size): batch = str(msg)[i:i+batch_size] - self.send2socket(f":{nick_from} PRIVMSG {user_to.uid} :{batch}") + await self.send2socket(f":{nick_from} PRIVMSG {user_to.uid} :{batch}") except Exception as err: - self.__Logs.error(f"General Error: {err}") + self._ctx.Logs.error(f"General Error: {err}") - def send_notice(self, nick_from: str, nick_to: str, msg: str) -> None: + async def send_notice(self, nick_from: str, nick_to: str, msg: str) -> None: """Sending NOTICE by batches Args: @@ -147,71 +130,71 @@ class Inspircd(IProtocol): nick_to (str): The reciever nickname """ try: - batch_size = self.__Config.BATCH_SIZE - user_from = self.__Irc.User.get_user(nick_from) - user_to = self.__Irc.User.get_user(nick_to) + batch_size = self._ctx.Config.BATCH_SIZE + user_from = self._ctx.User.get_user(nick_from) + user_to = self._ctx.User.get_user(nick_to) if user_from is None or user_to is None: - self.__Logs.error(f"The sender [{nick_from}] or the Reciever [{nick_to}] do not exist") + self._ctx.Logs.error(f"The sender [{nick_from}] or the Reciever [{nick_to}] do not exist") return None for i in range(0, len(str(msg)), batch_size): batch = str(msg)[i:i+batch_size] - self.send2socket(f":{user_from.uid} NOTICE {user_to.uid} :{batch}") + await self.send2socket(f":{user_from.uid} NOTICE {user_to.uid} :{batch}") except Exception as err: - self.__Logs.error(f"General Error: {err}") + self._ctx.Logs.error(f"General Error: {err}") - def send_link(self): + async def send_link(self): """Créer le link et envoyer les informations nécessaires pour la connexion au serveur. """ - service_id = self.__Config.SERVICE_ID - service_nickname = self.__Config.SERVICE_NICKNAME - service_username = self.__Config.SERVICE_USERNAME - service_realname = self.__Config.SERVICE_REALNAME - service_info = self.__Config.SERVICE_INFO - service_smodes = self.__Config.SERVICE_SMODES - service_hostname = self.__Config.SERVICE_HOST - service_name = self.__Config.SERVICE_NAME + service_id = self._ctx.Config.SERVICE_ID + service_nickname = self._ctx.Config.SERVICE_NICKNAME + service_username = self._ctx.Config.SERVICE_USERNAME + service_realname = self._ctx.Config.SERVICE_REALNAME + service_info = self._ctx.Config.SERVICE_INFO + service_smodes = self._ctx.Config.SERVICE_SMODES + service_hostname = self._ctx.Config.SERVICE_HOST + service_name = self._ctx.Config.SERVICE_NAME - server_password = self.__Config.SERVEUR_PASSWORD - server_link = self.__Config.SERVEUR_LINK - server_id = self.__Config.SERVEUR_ID - server_hostname = self.__Settings.MAIN_SERVER_HOSTNAME = self.__Config.SERVEUR_HOSTNAME + server_password = self._ctx.Config.SERVEUR_PASSWORD + server_link = self._ctx.Config.SERVEUR_LINK + server_id = self._ctx.Config.SERVEUR_ID + server_hostname = self._ctx.Settings.MAIN_SERVER_HOSTNAME = self._ctx.Config.SERVEUR_HOSTNAME - version = self.__Config.CURRENT_VERSION - unixtime = self.__Utils.get_unixtime() + version = self._ctx.Config.CURRENT_VERSION + unixtime = self._ctx.Utils.get_unixtime() - self.send2socket(f"CAPAB START {self.protocol_version}") - self.send2socket(f"CAPAB MODULES :services") - self.send2socket(f"CAPAB MODSUPPORT :") - self.send2socket(f"CAPAB CAPABILITIES :NICKMAX=30 CHANMAX=64 MAXMODES=20 IDENTMAX=10 MAXQUIT=255 MAXTOPIC=307 MAXKICK=255 MAXREAL=128 MAXAWAY=200 MAXHOST=64 MAXLINE=512 CASEMAPPING=ascii GLOBOPS=0") - self.send2socket(f"CAPAB END") - self.send2socket(f"SERVER {server_link} {server_password} {server_id} :{service_info}") - self.send2socket(f"BURST {unixtime}") - self.send2socket(f":{server_id} SINFO version :{service_name}-{version.split('.')[0]}. {server_hostname} :") - self.send2socket(f":{server_id} SINFO fullversion :{service_name}-{version}. {service_hostname} :") - self.send2socket(f":{server_id} SINFO rawversion :{service_name}-{version}") - self.send_uid(service_nickname, service_username, service_hostname, service_id, service_smodes, service_hostname, "127.0.0.1", service_realname) - self.send2socket(f":{server_id} ENDBURST") + await self.send2socket(f"CAPAB START {self.protocol_version}") + await self.send2socket(f"CAPAB MODULES :services") + await self.send2socket(f"CAPAB MODSUPPORT :") + await self.send2socket(f"CAPAB CAPABILITIES :NICKMAX=30 CHANMAX=64 MAXMODES=20 IDENTMAX=10 MAXQUIT=255 MAXTOPIC=307 MAXKICK=255 MAXREAL=128 MAXAWAY=200 MAXHOST=64 MAXLINE=512 CASEMAPPING=ascii GLOBOPS=0") + await self.send2socket(f"CAPAB END") + await self.send2socket(f"SERVER {server_link} {server_password} {server_id} :{service_info}") + await self.send2socket(f"BURST {unixtime}") + await self.send2socket(f":{server_id} SINFO version :{service_name}-{version.split('.')[0]}. {server_hostname} :") + await self.send2socket(f":{server_id} SINFO fullversion :{service_name}-{version}. {service_hostname} :") + await self.send2socket(f":{server_id} SINFO rawversion :{service_name}-{version}") + await self.send_uid(service_nickname, service_username, service_hostname, service_id, service_smodes, service_hostname, "127.0.0.1", service_realname) + await self.send2socket(f":{server_id} ENDBURST") # self.send_sjoin(chan) - self.__Logs.debug(f'>> {__name__} Link information sent to the server') + self._ctx.Logs.debug(f'>> {__name__} Link information sent to the server') - def gline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: + async def gline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: # TKL + G user host set_by expire_timestamp set_at_timestamp :reason - self.send2socket(f":{self.__Config.SERVEUR_ID} TKL + G {nickname} {hostname} {set_by} {expire_timestamp} {set_at_timestamp} :{reason}") + await self.send2socket(f":{self._ctx.Config.SERVEUR_ID} TKL + G {nickname} {hostname} {set_by} {expire_timestamp} {set_at_timestamp} :{reason}") return None - def send_set_nick(self, newnickname: str) -> None: + async def send_set_nick(self, newnickname: str) -> None: - self.send2socket(f":{self.__Config.SERVICE_NICKNAME} NICK {newnickname}") + await self.send2socket(f":{self._ctx.Config.SERVICE_NICKNAME} NICK {newnickname}") return None - def send_set_mode(self, modes: str, *, nickname: Optional[str] = None, channel_name: Optional[str] = None, params: Optional[str] = None) -> None: + async def send_set_mode(self, modes: str, *, nickname: Optional[str] = None, channel_name: Optional[str] = None, params: Optional[str] = None) -> None: """Set a mode to channel or to a nickname or for a user in a channel Args: @@ -220,94 +203,94 @@ class Inspircd(IProtocol): channel_name (Optional[str]): The channel name params (Optional[str]): Params to pass to the mode """ - service_id = self.__Config.SERVICE_ID + service_id = self._ctx.Config.SERVICE_ID params = '' if params is None else params if modes[0] not in ['+', '-']: - self.__Logs.error(f"[MODE ERROR] The mode you have provided is missing the sign: {modes}") + self._ctx.Logs.error(f"[MODE ERROR] The mode you have provided is missing the sign: {modes}") return None if nickname and channel_name: - # :98KAAAAAB MODE #services +o defenderdev - if not self.__Irc.Channel.is_valid_channel(channel_name): - self.__Logs.error(f"[MODE ERROR] The channel is not valid: {channel_name}") + # :98KAAAAAB MODE #services +o async defenderdev + if not self._ctx.Channel.is_valid_channel(channel_name): + self._ctx.Logs.error(f"[MODE ERROR] The channel is not valid: {channel_name}") return None - if not all(mode in self.__Settings.PROTOCTL_PREFIX_MODES_SIGNES for mode in list(modes.replace('+','').replace('-',''))): - self.__Logs.debug(f'[USERMODE UNVAILABLE] This mode {modes} is not available!') + if not all(mode in self._ctx.Settings.PROTOCTL_PREFIX_MODES_SIGNES for mode in list(modes.replace('+','').replace('-',''))): + self._ctx.Logs.debug(f'[USERMODE UNVAILABLE] This mode {modes} is not available!') return None - self.send2socket(f":{service_id} MODE {channel_name} {modes} {nickname}") + await self.send2socket(f":{service_id} MODE {channel_name} {modes} {nickname}") return None if nickname and channel_name is None: # :98KAAAAAB MODE nickname +o - if not all(mode in self.__Settings.PROTOCTL_USER_MODES for mode in list(modes.replace('+','').replace('-',''))): - self.__Logs.debug(f'[USERMODE UNVAILABLE] This mode {modes} is not available!') + if not all(mode in self._ctx.Settings.PROTOCTL_USER_MODES for mode in list(modes.replace('+','').replace('-',''))): + self._ctx.Logs.debug(f'[USERMODE UNVAILABLE] This mode {modes} is not available!') return None - self.send2socket(f":{service_id} MODE {nickname} {modes}") + await self.send2socket(f":{service_id} MODE {nickname} {modes}") return None if nickname is None and channel_name: # :98KAAAAAB MODE #channel +o - if not all(mode in self.__Settings.PROTOCTL_CHANNEL_MODES for mode in list(modes.replace('+','').replace('-',''))): - self.__Logs.debug(f'[USERMODE UNVAILABLE] This mode {modes} is not available!') + if not all(mode in self._ctx.Settings.PROTOCTL_CHANNEL_MODES for mode in list(modes.replace('+','').replace('-',''))): + self._ctx.Logs.debug(f'[USERMODE UNVAILABLE] This mode {modes} is not available!') return None - if not self.__Irc.Channel.is_valid_channel(channel_name): - self.__Logs.error(f"[MODE ERROR] The channel is not valid: {channel_name}") + if not self._ctx.Channel.is_valid_channel(channel_name): + self._ctx.Logs.error(f"[MODE ERROR] The channel is not valid: {channel_name}") return None - self.send2socket(f":{service_id} MODE {channel_name} {modes} {params}") + await self.send2socket(f":{service_id} MODE {channel_name} {modes} {params}") return None return None - def send_squit(self, server_id: str, server_link: str, reason: str) -> None: + async def send_squit(self, server_id: str, server_link: str, reason: str) -> None: if not reason: reason = 'Service Shutdown' - self.send2socket(f":{server_id} SQUIT {server_link} :{reason}") + await self.send2socket(f":{server_id} SQUIT {server_link} :{reason}") return None - def send_ungline(self, nickname:str, hostname: str) -> None: + async def send_ungline(self, nickname: str, hostname: str) -> None: - self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - G {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}") + await self.send2socket(f":{self._ctx.Config.SERVEUR_ID} TKL - G {nickname} {hostname} {self._ctx.Config.SERVICE_NICKNAME}") return None - def send_kline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: + async def send_kline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: # TKL + k user host set_by expire_timestamp set_at_timestamp :reason - self.send2socket(f":{self.__Config.SERVEUR_ID} TKL + k {nickname} {hostname} {set_by} {expire_timestamp} {set_at_timestamp} :{reason}") + await self.send2socket(f":{self._ctx.Config.SERVEUR_ID} TKL + k {nickname} {hostname} {set_by} {expire_timestamp} {set_at_timestamp} :{reason}") return None - def send_sjoin(self, channel: str) -> None: + async def send_sjoin(self, channel: str) -> None: """Service join a channel Args: channel (str): The channel name. """ - server_id = self.__Config.SERVEUR_ID - service_nickname = self.__Config.SERVICE_NICKNAME - service_modes = self.__Config.SERVICE_UMODES - service_id = self.__Config.SERVICE_ID + server_id = self._ctx.Config.SERVEUR_ID + service_nickname = self._ctx.Config.SERVICE_NICKNAME + service_modes = self._ctx.Config.SERVICE_UMODES + service_id = self._ctx.Config.SERVICE_ID - if not self.__Irc.Channel.is_valid_channel(channel): - self.__Logs.error(f"The channel [{channel}] is not valid") + if not self._ctx.Channel.is_valid_channel(channel): + self._ctx.Logs.error(f"The channel [{channel}] is not valid") return None - self.send2socket(f":{server_id} FJOIN {channel} {self.__Utils.get_unixtime()} :o, {service_id}") + await self.send2socket(f":{server_id} FJOIN {channel} {self._ctx.Utils.get_unixtime()} :o, {service_id}") self.send_set_mode(service_modes, nickname=service_nickname, channel_name=channel) - # Add defender to the channel uids list - self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[service_id])) + # Add async defender to the channel uids list + self._ctx.Channel.insert(self._ctx.Definition.MChannel(name=channel, uids=[service_id])) return None - def send_quit(self, uid: str, reason: str, print_log: bool = True) -> None: + async def send_quit(self, uid: str, reason: str, print_log: bool = True) -> None: """Send quit message Args: @@ -315,22 +298,24 @@ class Inspircd(IProtocol): reason (str): The reason for the quit print_log (bool): If True then print logs """ - user_obj = self.__Irc.User.get_user(uidornickname=uid) - reputation_obj = self.__Irc.Reputation.get_reputation(uidornickname=uid) + user_obj = self._ctx.User.get_user(uidornickname=uid) + reputation_obj = self._ctx.Reputation.get_reputation(uidornickname=uid) if not user_obj is None: - self.send2socket(f":{user_obj.uid} QUIT :{reason}", print_log=print_log) - self.__Irc.User.delete(user_obj.uid) + await self.send2socket(f":{user_obj.uid} QUIT :{reason}", print_log=print_log) + self._ctx.User.delete(user_obj.uid) if not reputation_obj is None: - self.__Irc.Reputation.delete(reputation_obj.uid) + self._ctx.Reputation.delete(reputation_obj.uid) - if not self.__Irc.Channel.delete_user_from_all_channel(uid): - self.__Logs.error(f"The UID [{uid}] has not been deleted from all channels") + if not self._ctx.Channel.delete_user_from_all_channel(uid): + self._ctx.Logs.error(f"The UID [{uid}] has not been deleted from all channels") return None - def send_uid(self, nickname:str, username: str, hostname: str, uid:str, umodes: str, vhost: str, remote_ip: str, realname: str, print_log: bool = True) -> None: + async def send_uid(self, nickname: str, username: str, hostname: str, + uid:str, umodes: str, vhost: str, remote_ip: str, + realname: str, print_log: bool = True) -> None: """Send UID to the server [:] UID []+ : Args: @@ -345,20 +330,20 @@ class Inspircd(IProtocol): print_log (bool, optional): print logs if true. Defaults to True. """ # {self.Config.SERVEUR_ID} UID - # {clone.nickname} 1 {self.__Utils.get_unixtime()} {clone.username} {clone.hostname} {clone.uid} * {clone.umodes} {clone.vhost} * {self.Base.encode_ip(clone.remote_ip)} :{clone.realname} + # {clone.nickname} 1 {self._ctx.Utils.get_unixtime()} {clone.username} {clone.hostname} {clone.uid} * {clone.umodes} {clone.vhost} * {self.Base.encode_ip(clone.remote_ip)} :{clone.realname} try: - unixtime = self.__Utils.get_unixtime() - # encoded_ip = self.__Base.encode_ip(remote_ip) + unixtime = self._ctx.Utils.get_unixtime() + # encoded_ip = self._ctx.Base.encode_ip(remote_ip) new_umodes = [] for mode in list(umodes.replace('+', '').replace('-', '')): - if mode in self.__Settings.PROTOCTL_USER_MODES: + if mode in self._ctx.Settings.PROTOCTL_USER_MODES: new_umodes.append(mode) final_umodes = '+' + ''.join(new_umodes) # Create the user - self.__Irc.User.insert( - self.__Irc.Loader.Definition.MUser( + self._ctx.User.insert( + self._ctx.Definition.MUser( uid=uid, nickname=nickname, username=username, realname=realname,hostname=hostname, umodes=final_umodes, vhost=vhost, remote_ip=remote_ip @@ -366,17 +351,17 @@ class Inspircd(IProtocol): ) # [:] UID []+ : - # :98K UID 98KAAAAAB 1756932359 defenderdev defenderdev.deb.biz.st defenderdev.deb.biz.st Dev-PyDefender 127.0.0.1 1756932359 + :Dev Python Security + # :98K UID 98KAAAAAB 1756932359 async defenderdev async defenderdev.deb.biz.st async defenderdev.deb.biz.st Dev-PyDefender 127.0.0.1 1756932359 + :Dev Python Security # [':97K', 'UID', '97KAAAAAA', '1756926679', 'adator', '172.18.128.1', 'attila.example.org', '...', '...', '172.18.128.1', '1756926678', '+o', ':...'] - uid_msg = f":{self.__Config.SERVEUR_ID} UID {uid} {unixtime} {nickname} {hostname} {vhost} {username} {username} {remote_ip} {unixtime} {final_umodes} :{realname}" - self.send2socket(uid_msg, print_log=print_log) + uid_msg = f":{self._ctx.Config.SERVEUR_ID} UID {uid} {unixtime} {nickname} {hostname} {vhost} {username} {username} {remote_ip} {unixtime} {final_umodes} :{realname}" + await self.send2socket(uid_msg, print_log=print_log) return None except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def send_join_chan(self, uidornickname: str, channel: str, password: str = None, print_log: bool = True) -> None: + async def send_join_chan(self, uidornickname: str, channel: str, password: str = None, print_log: bool = True) -> None: """Joining a channel Args: @@ -386,23 +371,23 @@ class Inspircd(IProtocol): print_log (bool, optional): Write logs. Defaults to True. """ - user_obj = self.__Irc.User.get_user(uidornickname) + user_obj = self._ctx.User.get_user(uidornickname) password_channel = password if not password is None else '' if user_obj is None: return None - if not self.__Irc.Channel.is_valid_channel(channel): - self.__Logs.error(f"The channel [{channel}] is not valid") + if not self._ctx.Channel.is_valid_channel(channel): + self._ctx.Logs.error(f"The channel [{channel}] is not valid") return None - self.send2socket(f":{user_obj.uid} FJOIN {channel} {self.__Utils.get_unixtime()} :,{user_obj.uid} {password_channel}", print_log=print_log) + await self.send2socket(f":{user_obj.uid} FJOIN {channel} {self._ctx.Utils.get_unixtime()} :,{user_obj.uid} {password_channel}", print_log=print_log) - # Add defender to the channel uids list - self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[user_obj.uid])) + # Add async defender to the channel uids list + self._ctx.Channel.insert(self._ctx.Definition.MChannel(name=channel, uids=[user_obj.uid])) return None - def send_part_chan(self, uidornickname:str, channel: str, print_log: bool = True) -> None: + async def send_part_chan(self, uidornickname: str, channel: str, print_log: bool = True) -> None: """Part from a channel Args: @@ -411,38 +396,38 @@ class Inspircd(IProtocol): print_log (bool, optional): Write logs. Defaults to True. """ - user_obj = self.__Irc.User.get_user(uidornickname) + user_obj = self._ctx.User.get_user(uidornickname) if user_obj is None: - self.__Logs.error(f"The user [{uidornickname}] is not valid") + self._ctx.Logs.error(f"The user [{uidornickname}] is not valid") return None - if not self.__Irc.Channel.is_valid_channel(channel): - self.__Logs.error(f"The channel [{channel}] is not valid") + if not self._ctx.Channel.is_valid_channel(channel): + self._ctx.Logs.error(f"The channel [{channel}] is not valid") return None - self.send2socket(f":{user_obj.uid} PART {channel}", print_log=print_log) + await self.send2socket(f":{user_obj.uid} PART {channel}", print_log=print_log) - # Add defender to the channel uids list - self.__Irc.Channel.delete_user_from_channel(channel, user_obj.uid) + # Add async defender to the channel uids list + self._ctx.Channel.delete_user_from_channel(channel, user_obj.uid) return None - def send_unkline(self, nickname:str, hostname: str) -> None: + async def send_unkline(self, nickname: str, hostname: str) -> None: - self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - K {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}") + await self.send2socket(f":{self._ctx.Config.SERVEUR_ID} TKL - K {nickname} {hostname} {self._ctx.Config.SERVICE_NICKNAME}") return None - def send_raw(self, raw_command: str) -> None: + async def send_raw(self, raw_command: str) -> None: - self.send2socket(f":{self.__Config.SERVEUR_ID} {raw_command}") + await self.send2socket(f":{self._ctx.Config.SERVEUR_ID} {raw_command}") return None # ------------------------------------------------------------------------ # RECIEVED IRC MESSAGES # ------------------------------------------------------------------------ - def on_umode2(self, server_msg: list[str]) -> None: + async def on_umode2(self, server_msg: list[str]) -> None: """Handle umode2 coming from a server Args: @@ -451,25 +436,25 @@ class Inspircd(IProtocol): try: # [':adator_', 'UMODE2', '-iwx'] - user_obj = self.__Irc.User.get_user(str(server_msg[0]).lstrip(':')) + user_obj = self._ctx.User.get_user(str(server_msg[0]).lstrip(':')) user_mode = server_msg[2] if user_obj is None: # If user is not created return None # TODO : User object should be able to update user modes - if self.__Irc.User.update_mode(user_obj.uid, user_mode): + if self._ctx.User.update_mode(user_obj.uid, user_mode): return None - # self.__Logs.debug(f"Updating user mode for [{userObj.nickname}] [{old_umodes}] => [{userObj.umodes}]") + # self._ctx.Logs.debug(f"Updating user mode for [{userObj.nickname}] [{old_umodes}] => [{userObj.umodes}]") return None except IndexError as ie: - self.__Logs.error(f"{__name__} - Index Error: {ie}") + self._ctx.Logs.error(f"{__name__} - Index Error: {ie}") except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_quit(self, server_msg: list[str]) -> None: + async def on_quit(self, server_msg: list[str]) -> None: """Handle quit coming from a server >> [':97KAAAAAZ', 'QUIT', ':Quit:', '....'] Args: @@ -479,40 +464,40 @@ class Inspircd(IProtocol): uid_who_quit = str(server_msg[0]).lstrip(':') - self.__Irc.Channel.delete_user_from_all_channel(uid_who_quit) - self.__Irc.User.delete(uid_who_quit) - self.__Irc.Reputation.delete(uid_who_quit) + self._ctx.Channel.delete_user_from_all_channel(uid_who_quit) + self._ctx.User.delete(uid_who_quit) + self._ctx.Reputation.delete(uid_who_quit) return None except IndexError as ie: - self.__Logs.error(f"{__name__} - Index Error: {ie}") + self._ctx.Logs.error(f"{__name__} - Index Error: {ie}") except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_squit(self, server_msg: list[str]) -> None: + async def on_squit(self, server_msg: list[str]) -> None: """Handle squit coming from a server Args: server_msg (list[str]): Original server message """ - # ['@msgid=QOEolbRxdhpVW5c8qLkbAU;time=2024-09-21T17:33:16.547Z', 'SQUIT', 'defender.deb.biz.st', ':Connection', 'closed'] + # ['@msgid=QOEolbRxdhpVW5c8qLkbAU;time=2024-09-21T17:33:16.547Z', 'SQUIT', 'async defender.deb.biz.st', ':Connection', 'closed'] server_hostname = server_msg[2] uid_to_delete = None - for s_user in self.__Irc.User.UID_DB: + for s_user in self._ctx.User.UID_DB: if s_user.hostname == server_hostname and 'S' in s_user.umodes: uid_to_delete = s_user.uid if uid_to_delete is None: return None - self.__Irc.User.delete(uid_to_delete) - self.__Irc.Channel.delete_user_from_all_channel(uid_to_delete) + self._ctx.User.delete(uid_to_delete) + self._ctx.Channel.delete_user_from_all_channel(uid_to_delete) return None - def on_protoctl(self, server_msg: list[str]) -> None: + async def on_protoctl(self, server_msg: list[str]) -> None: """Handle CAPAB coming from a server Args: @@ -541,9 +526,9 @@ class Inspircd(IProtocol): channel_modes.append(cmode) - self.__Settings.PROTOCTL_PREFIX_SIGNES_MODES = sign_mode - self.__Settings.PROTOCTL_PREFIX_MODES_SIGNES = mode_sign - self.__Settings.PROTOCTL_CHANNEL_MODES = list(set(channel_modes)) + self._ctx.Settings.PROTOCTL_PREFIX_SIGNES_MODES = sign_mode + self._ctx.Settings.PROTOCTL_PREFIX_MODES_SIGNES = mode_sign + self._ctx.Settings.PROTOCTL_CHANNEL_MODES = list(set(channel_modes)) # ['CAPAB', 'USERMODES', ':param-set:snomask=s', 'simple:bot=B', 'simple:invisible=i', 'simple:oper=o', 'simple:servprotect=k', # 'simple:sslqueries=z', 'simple:u_registered=r', 'simple:wallops=w'] @@ -555,11 +540,11 @@ class Inspircd(IProtocol): umode = prefix.split('=')[1] if len(prefix.split('=')) > 1 else None user_modes.append(umode) - self.__Settings.PROTOCTL_USER_MODES = list(set(user_modes)) + self._ctx.Settings.PROTOCTL_USER_MODES = list(set(user_modes)) return None - def on_nick(self, server_msg: list[str]) -> None: + async def on_nick(self, server_msg: list[str]) -> None: """Handle nick coming from a server new nickname @@ -576,18 +561,18 @@ class Inspircd(IProtocol): uid = str(scopy[0]).replace(':','') newnickname = scopy[2] - self.__Irc.User.update_nickname(uid, newnickname) - self.__Irc.Client.update_nickname(uid, newnickname) - self.__Irc.Admin.update_nickname(uid, newnickname) + self._ctx.User.update_nickname(uid, newnickname) + self._ctx.Client.update_nickname(uid, newnickname) + self._ctx.Admin.update_nickname(uid, newnickname) return None except IndexError as ie: - self.__Logs.error(f"{__name__} - Index Error: {ie}") + self._ctx.Logs.error(f"{__name__} - Index Error: {ie}") except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_sjoin(self, server_msg: list[str]) -> None: + async def on_sjoin(self, server_msg: list[str]) -> None: """Handle sjoin coming from a server Args: @@ -607,8 +592,8 @@ class Inspircd(IProtocol): list_users = list(set(list_users)) if list_users: - self.__Irc.Channel.insert( - self.__Irc.Loader.Definition.MChannel( + self._ctx.Channel.insert( + self._ctx.Definition.MChannel( name=channel, uids=list_users ) @@ -616,11 +601,11 @@ class Inspircd(IProtocol): return None except IndexError as ie: - self.__Logs.error(f"{__name__} - Index Error: {ie}") + self._ctx.Logs.error(f"{__name__} - Index Error: {ie}") except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_endburst(self, server_msg: list[str]) -> None: + async def on_endburst(self, server_msg: list[str]) -> None: """Handle EOS coming from a server Args: @@ -630,71 +615,71 @@ class Inspircd(IProtocol): # [':97K', 'ENDBURST'] scopy = server_msg.copy() hsid = str(scopy[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): + if hsid == self._ctx.Config.HSID: + if self._ctx.Config.DEFENDER_INIT == 1: + current_version = self._ctx.Config.CURRENT_VERSION + latest_version = self._ctx.Config.LATEST_VERSION + if self._ctx.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"# SERVEUR : {self._ctx.Config.SERVEUR_IP} ") + print(f"# PORT : {self._ctx.Config.SERVEUR_PORT} ") + print(f"# SSL : {self._ctx.Config.SERVEUR_SSL} ") + print(f"# SSL VER : {self._ctx.Config.SSL_VERSION} ") + print(f"# NICKNAME : {self._ctx.Config.SERVICE_NICKNAME} ") + print(f"# CHANNEL : {self._ctx.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"################################################") + self._ctx.Logs.info(f"################### DEFENDER ###################") + self._ctx.Logs.info(f"# SERVICE CONNECTE ") + self._ctx.Logs.info(f"# SERVEUR : {self._ctx.Config.SERVEUR_IP} ") + self._ctx.Logs.info(f"# PORT : {self._ctx.Config.SERVEUR_PORT} ") + self._ctx.Logs.info(f"# SSL : {self._ctx.Config.SERVEUR_SSL} ") + self._ctx.Logs.info(f"# SSL VER : {self._ctx.Config.SSL_VERSION} ") + self._ctx.Logs.info(f"# NICKNAME : {self._ctx.Config.SERVICE_NICKNAME} ") + self._ctx.Logs.info(f"# CHANNEL : {self._ctx.Config.SERVICE_CHANLOG} ") + self._ctx.Logs.info(f"# VERSION : {version} ") + self._ctx.Logs.info(f"################################################") - self.send_sjoin(self.__Config.SERVICE_CHANLOG) + self.send_sjoin(self._ctx.Config.SERVICE_CHANLOG) - if self.__Base.check_for_new_version(False): + if self._ctx.Base.check_for_new_version(False): self.send_priv_msg( - nick_from=self.__Config.SERVICE_NICKNAME, + nick_from=self._ctx.Config.SERVICE_NICKNAME, msg=f" New Version available {version}", - channel=self.__Config.SERVICE_CHANLOG + channel=self._ctx.Config.SERVICE_CHANLOG ) # Initialisation terminé aprés le premier PING self.send_priv_msg( - nick_from=self.__Config.SERVICE_NICKNAME, - msg=tr("[ %sINFORMATION%s ] >> %s is ready!", self.__Config.COLORS.green, self.__Config.COLORS.nogc, self.__Config.SERVICE_NICKNAME), - channel=self.__Config.SERVICE_CHANLOG + nick_from=self._ctx.Config.SERVICE_NICKNAME, + msg=tr("[ %sINFORMATION%s ] >> %s is ready!", self._ctx.Config.COLORS.green, self._ctx.Config.COLORS.nogc, self._ctx.Config.SERVICE_NICKNAME), + channel=self._ctx.Config.SERVICE_CHANLOG ) - self.__Config.DEFENDER_INIT = 0 + self._ctx.Config.DEFENDER_INIT = 0 # Send EOF to other modules - for module in self.__Irc.ModuleUtils.model_get_loaded_modules().copy(): + for module in self._ctx.ModuleUtils.model_get_loaded_modules().copy(): module.class_instance.cmd(scopy) # Join saved channels & load existing modules - self.__Irc.join_saved_channels() - self.__Irc.ModuleUtils.db_load_all_existing_modules(self.__Irc) + self._ctx.Irc.join_saved_channels() + self._ctx.ModuleUtils.db_load_all_existing_modules(self._ctx.Irc) return None except IndexError as ie: - self.__Logs.error(f"{__name__} - Key Error: {ie}") + self._ctx.Logs.error(f"{__name__} - Key Error: {ie}") except KeyError as ke: - self.__Logs.error(f"{__name__} - Key Error: {ke}") + self._ctx.Logs.error(f"{__name__} - Key Error: {ke}") except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_part(self, server_msg: list[str]) -> None: + async def on_part(self, server_msg: list[str]) -> None: """Handle part coming from a server Args: @@ -706,16 +691,16 @@ class Inspircd(IProtocol): uid = str(server_msg[0]).lstrip(':') channel = str(server_msg[2]).lower() # reason = str(' '.join(server_msg[3:])) - self.__Irc.Channel.delete_user_from_channel(channel, uid) + self._ctx.Channel.delete_user_from_channel(channel, uid) return None except IndexError as ie: - self.__Logs.error(f"{__name__} - Index Error: {ie}") + self._ctx.Logs.error(f"{__name__} - Index Error: {ie}") except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_uid(self, server_msg: list[str]) -> None: + async def on_uid(self, server_msg: list[str]) -> None: """Handle uid message coming from the server [:] UID []+ : [':97K', 'UID', '97KAAAAAB', '1756928055', 'adator_', '172.18.128.1', '172.18.128.1', '...', '...', '172.18.128.1', '1756928055', '+', ':...'] @@ -723,9 +708,9 @@ class Inspircd(IProtocol): server_msg (list[str]): Original server message """ try: - red = self.__Config.COLORS.red - green = self.__Config.COLORS.green - nogc = self.__Config.COLORS.nogc + red = self._ctx.Config.COLORS.red + green = self._ctx.Config.COLORS.green + nogc = self._ctx.Config.COLORS.nogc is_webirc = True if 'webirc' in server_msg[0] else False is_websocket = True if 'websocket' in server_msg[0] else False @@ -737,7 +722,7 @@ class Inspircd(IProtocol): vhost = str(server_msg[6]) if not 'S' in umodes: - # remote_ip = self.__Base.decode_ip(str(serverMsg[9])) + # remote_ip = self._ctx.Base.decode_ip(str(serverMsg[9])) remote_ip = str(server_msg[9]) else: remote_ip = '127.0.0.1' @@ -756,8 +741,8 @@ class Inspircd(IProtocol): score_connexion = 0 - self.__Irc.User.insert( - self.__Irc.Loader.Definition.MUser( + self._ctx.User.insert( + self._ctx.Definition.MUser( uid=uid, nickname=nickname, username=username, @@ -774,39 +759,39 @@ class Inspircd(IProtocol): ) ) - for module in self.__Irc.ModuleUtils.model_get_loaded_modules().copy(): + for module in self._ctx.ModuleUtils.model_get_loaded_modules().copy(): module.class_instance.cmd(server_msg) # SASL authentication - dnickname = self.__Config.SERVICE_NICKNAME - dchanlog = self.__Config.SERVICE_CHANLOG + dnickname = self._ctx.Config.SERVICE_NICKNAME + dchanlog = self._ctx.Config.SERVICE_CHANLOG # uid = serverMsg[8] # nickname = serverMsg[3] - sasl_obj = self.__Irc.Sasl.get_sasl_obj(uid) + sasl_obj = self._ctx.Sasl.get_sasl_obj(uid) if sasl_obj: if sasl_obj.auth_success: - self.__Irc.insert_db_admin(sasl_obj.client_uid, sasl_obj.username, sasl_obj.level, sasl_obj.language) - self.send_priv_msg(nick_from=dnickname, + self._ctx.Irc.insert_db_admin(sasl_obj.client_uid, sasl_obj.username, sasl_obj.level, sasl_obj.language) + await self.send_priv_msg(nick_from=dnickname, msg=tr("[ %sSASL AUTH%s ] - %s (%s) is now connected successfuly to %s", green, nogc, nickname, sasl_obj.username, dnickname), channel=dchanlog) - self.send_notice(nick_from=dnickname, nick_to=nickname, msg=tr("Successfuly connected to %s", dnickname)) + await self.send_notice(nick_from=dnickname, nick_to=nickname, msg=tr("Successfuly connected to %s", dnickname)) else: - self.send_priv_msg(nick_from=dnickname, + await self.send_priv_msg(nick_from=dnickname, msg=tr("[ %sSASL AUTH%s ] - %s provided a wrong password for this username %s", red, nogc, nickname, sasl_obj.username), channel=dchanlog) - self.send_notice(nick_from=dnickname, nick_to=nickname, msg=tr("Wrong password!")) + await self.send_notice(nick_from=dnickname, nick_to=nickname, msg=tr("Wrong password!")) # Delete sasl object! - self.__Irc.Sasl.delete_sasl_client(uid) + self._ctx.Sasl.delete_sasl_client(uid) return None return None except IndexError as ie: - self.__Logs.error(f"{__name__} - Index Error: {ie}") + self._ctx.Logs.error(f"{__name__} - Index Error: {ie}") except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}", exc_info=True) + self._ctx.Logs.error(f"{__name__} - General Error: {err}", exc_info=True) - def on_privmsg(self, server_msg: list[str]) -> None: + async def on_privmsg(self, server_msg: list[str]) -> None: """Handle PRIVMSG message coming from the server Args: @@ -820,36 +805,36 @@ class Inspircd(IProtocol): cmd.pop(0) 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})(.*)$' + user_trigger = self._ctx.User.get_nickname(get_uid_or_nickname) + # dnickname = self._ctx.Config.SERVICE_NICKNAME + pattern = fr'(:\{self._ctx.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 self.__Irc.Commands.is_command_exist(arg[0]): - self.__Logs.debug(f"This command {arg[0]} is not available") + arg.remove(f":{self._ctx.Config.SERVICE_PREFIX}") + if not self._ctx.Commands.is_command_exist(arg[0]): + self._ctx.Logs.debug(f"This command {arg[0]} is not available") self.send_notice( - nick_from=self.__Config.SERVICE_NICKNAME, + nick_from=self._ctx.Config.SERVICE_NICKNAME, nick_to=user_trigger, - msg=f"This command [{self.__Config.COLORS.bold}{arg[0]}{self.__Config.COLORS.bold}] is not available" + msg=f"This command [{self._ctx.Config.COLORS.bold}{arg[0]}{self._ctx.Config.COLORS.bold}] is not available" ) return None cmd_to_send = convert_to_string.replace(':','') - self.__Base.log_cmd(user_trigger, cmd_to_send) + await self._ctx.Base.log_cmd(user_trigger, cmd_to_send) - fromchannel = str(cmd[2]).lower() if self.__Irc.Channel.is_valid_channel(cmd[2]) else None - self.__Irc.hcmds(user_trigger, fromchannel, arg, cmd) + fromchannel = str(cmd[2]).lower() if self._ctx.Channel.is_valid_channel(cmd[2]) else None + self._ctx.Irc.hcmds(user_trigger, fromchannel, arg, cmd) - if cmd[2] == self.__Config.SERVICE_ID: + if cmd[2] == self._ctx.Config.SERVICE_ID: pattern = fr'^:.*?:(.*)$' hcmds = search(pattern, ' '.join(cmd)) - if hcmds: # par /msg defender [commande] + if hcmds: # par /msg async defender [commande] liste_des_commandes = list(hcmds.groups()) convert_to_string = ' '.join(liste_des_commandes) arg = convert_to_string.split() @@ -869,32 +854,32 @@ class Inspircd(IProtocol): self.on_ping(srv_msg) return None - if not self.__Irc.Commands.is_command_exist(arg[0]): - self.__Logs.debug(f"This command {arg[0]} sent by {user_trigger} is not available") + if not self._ctx.Commands.is_command_exist(arg[0]): + self._ctx.Logs.debug(f"This command {arg[0]} sent by {user_trigger} is not available") return None - # if not arg[0].lower() in self.__Irc.module_commands_list: - # self.__Logs.debug(f"This command {arg[0]} sent by {user_trigger} is not available") + # if not arg[0].lower() in self._ctx.Irc.module_commands_list: + # self._ctx.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) + await self._ctx.Base.log_cmd(user_trigger, cmd_to_send) fromchannel = None if len(arg) >= 2: - fromchannel = str(arg[1]).lower() if self.__Irc.Channel.is_valid_channel(arg[1]) else None + fromchannel = str(arg[1]).lower() if self._ctx.Channel.is_valid_channel(arg[1]) else None - self.__Irc.hcmds(user_trigger, fromchannel, arg, cmd) + self._ctx.Irc.hcmds(user_trigger, fromchannel, arg, cmd) return None except KeyError as ke: - self.__Logs.error(f"Key Error: {ke}") + self._ctx.Logs.error(f"Key Error: {ke}") except AttributeError as ae: - self.__Logs.error(f"Attribute Error: {ae}") + self._ctx.Logs.error(f"Attribute Error: {ae}") except Exception as err: - self.__Logs.error(f"General Error: {err}", exc_info=True) + self._ctx.Logs.error(f"General Error: {err}", exc_info=True) - def on_server_ping(self, server_msg: list[str]) -> None: + async def on_server_ping(self, server_msg: list[str]) -> None: """Send a PONG message to the server Args: @@ -906,13 +891,13 @@ class Inspircd(IProtocol): # -> :808 PONG 3IN hsid = str(server_msg[0]).replace(':','') - self.send2socket(f":{self.__Config.SERVEUR_ID} PONG {hsid}", print_log=False) + await self.send2socket(f":{self._ctx.Config.SERVEUR_ID} PONG {hsid}", print_log=False) return None except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_server(self, server_msg: list[str]) -> None: + async def on_server(self, server_msg: list[str]) -> None: """_summary_ >>> [':97K', 'SINFO', 'customversion', ':'] >>> [':97K', 'SINFO', 'rawbranch', ':InspIRCd-4'] @@ -922,16 +907,16 @@ class Inspircd(IProtocol): """ try: param = str(server_msg[2]) - self.__Config.HSID = self.__Settings.MAIN_SERVER_ID = str(server_msg[0]).replace(':', '') + self._ctx.Config.HSID = self._ctx.Settings.MAIN_SERVER_ID = str(server_msg[0]).replace(':', '') if param == 'rawversion': - self.__Logs.debug(f">> Server Version: {server_msg[3].replace(':', '')}") + self._ctx.Logs.debug(f">> Server Version: {server_msg[3].replace(':', '')}") elif param == 'rawbranch': - self.__Logs.debug(f">> Branch Version: {server_msg[3].replace(':', '')}") + self._ctx.Logs.debug(f">> Branch Version: {server_msg[3].replace(':', '')}") except Exception as err: - self.__Logs.error(f'General Error: {err}') + self._ctx.Logs.error(f'General Error: {err}') - def on_version(self, server_msg: list[str]) -> None: + async def on_version(self, server_msg: list[str]) -> None: """Sending Server Version to the server Args: @@ -941,21 +926,21 @@ class Inspircd(IProtocol): # Réponse a un CTCP VERSION try: - nickname = self.__Irc.User.get_nickname(self.__Utils.clean_uid(server_msg[1])) - dnickname = self.__Config.SERVICE_NICKNAME + nickname = self._ctx.User.get_nickname(self._ctx.Utils.clean_uid(server_msg[1])) + dnickname = self._ctx.Config.SERVICE_NICKNAME arg = server_msg[4].replace(':', '') if nickname is None: return None if arg == '\x01VERSION\x01': - self.send2socket(f':{dnickname} NOTICE {nickname} :\x01VERSION Service {self.__Config.SERVICE_NICKNAME} V{self.__Config.CURRENT_VERSION}\x01') + await self.send2socket(f':{dnickname} NOTICE {nickname} :\x01VERSION Service {self._ctx.Config.SERVICE_NICKNAME} V{self._ctx.Config.CURRENT_VERSION}\x01') return None except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_time(self, server_msg: list[str]) -> None: + async def on_time(self, server_msg: list[str]) -> None: """Sending TIME answer to a requestor Args: @@ -965,22 +950,22 @@ class Inspircd(IProtocol): # Réponse a un CTCP VERSION try: - nickname = self.__Irc.User.get_nickname(self.__Utils.clean_uid(server_msg[1])) - dnickname = self.__Config.SERVICE_NICKNAME + nickname = self._ctx.User.get_nickname(self._ctx.Utils.clean_uid(server_msg[1])) + dnickname = self._ctx.Config.SERVICE_NICKNAME arg = server_msg[4].replace(':', '') - current_datetime = self.__Utils.get_sdatetime() + current_datetime = self._ctx.Utils.get_sdatetime() if nickname is None: return None if arg == '\x01TIME\x01': - self.send2socket(f':{dnickname} NOTICE {nickname} :\x01TIME {current_datetime}\x01') + await self.send2socket(f':{dnickname} NOTICE {nickname} :\x01TIME {current_datetime}\x01') return None except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_ping(self, server_msg: list[str]) -> None: + async def on_ping(self, server_msg: list[str]) -> None: """Sending a PING answer to requestor Args: @@ -990,8 +975,8 @@ class Inspircd(IProtocol): # Réponse a un CTCP VERSION try: - nickname = self.__Irc.User.get_nickname(self.__Utils.clean_uid(server_msg[1])) - dnickname = self.__Config.SERVICE_NICKNAME + nickname = self._ctx.User.get_nickname(self._ctx.Utils.clean_uid(server_msg[1])) + dnickname = self._ctx.Config.SERVICE_NICKNAME arg = server_msg[4].replace(':', '') if nickname is None: @@ -999,10 +984,10 @@ class Inspircd(IProtocol): if arg == '\x01PING': recieved_unixtime = int(server_msg[5].replace('\x01','')) - current_unixtime = self.__Utils.get_unixtime() + current_unixtime = self._ctx.Utils.get_unixtime() ping_response = current_unixtime - recieved_unixtime - # self.__Irc.send2socket(f':{dnickname} NOTICE {nickname} :\x01PING {ping_response} secs\x01') + # self._ctx.Irc.send2socket(f':{dnickname} NOTICE {nickname} :\x01PING {ping_response} secs\x01') self.send_notice( nick_from=dnickname, nick_to=nickname, @@ -1011,9 +996,9 @@ class Inspircd(IProtocol): return None except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_version_msg(self, server_msg: list[str]) -> None: + async def on_version_msg(self, server_msg: list[str]) -> None: """Handle version coming from the server Args: @@ -1021,24 +1006,24 @@ class Inspircd(IProtocol): """ try: # ['@label=0073', ':0014E7P06', 'VERSION', 'PyDefender'] - user_obj = self.__Irc.User.get_user(self.__Utils.clean_uid(server_msg[1])) + user_obj = self._ctx.User.get_user(self._ctx.Utils.clean_uid(server_msg[1])) if user_obj is None: return None - response_351 = f"{self.__Config.SERVICE_NAME.capitalize()}-{self.__Config.CURRENT_VERSION} {self.__Config.SERVICE_HOST} {self.name}" - self.send2socket(f':{self.__Config.SERVICE_HOST} 351 {user_obj.nickname} {response_351}') + response_351 = f"{self._ctx.Config.SERVICE_NAME.capitalize()}-{self._ctx.Config.CURRENT_VERSION} {self._ctx.Config.SERVICE_HOST} {self.name}" + await self.send2socket(f':{self._ctx.Config.SERVICE_HOST} 351 {user_obj.nickname} {response_351}') - modules = self.__Irc.ModuleUtils.get_all_available_modules() + modules = self._ctx.ModuleUtils.get_all_available_modules() response_005 = ' | '.join(modules) - self.send2socket(f':{self.__Config.SERVICE_HOST} 005 {user_obj.nickname} {response_005} are supported by this server') + await self.send2socket(f':{self._ctx.Config.SERVICE_HOST} 005 {user_obj.nickname} {response_005} are supported by this server') return None except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_sasl(self, server_msg: list[str]) -> Optional['MSasl']: + async def on_sasl(self, server_msg: list[str]) -> Optional['MSasl']: """Handle SASL coming from a server Args: @@ -1050,12 +1035,12 @@ class Inspircd(IProtocol): # [':97K', 'ENCAP', '98K', 'SASL', '97KAAAAAF', '*', 'H', '172.18.128.1', '172.18.128.1', 'P'] # [':97K', 'ENCAP', '98K', 'SASL', '97KAAAAAF', '*', 'S', 'PLAIN'] # [':97K', 'ENCAP', '98K', 'SASL', '97KAAAAAP', 'irc.inspircd.local', 'C', 'YWRzefezfzefzefzefzefzefzefzezak='] - # [':irc.local.org', 'SASL', 'defender-dev.deb.biz.st', '0014ZZH1F', 'S', 'EXTERNAL', 'zzzzzzzkey'] - # [':irc.local.org', 'SASL', 'defender-dev.deb.biz.st', '00157Z26U', 'C', 'sasakey=='] - # [':irc.local.org', 'SASL', 'defender-dev.deb.biz.st', '00157Z26U', 'D', 'A'] - psasl = self.__Irc.Sasl + # [':irc.local.org', 'SASL', 'async defender-dev.deb.biz.st', '0014ZZH1F', 'S', 'EXTERNAL', 'zzzzzzzkey'] + # [':irc.local.org', 'SASL', 'async defender-dev.deb.biz.st', '00157Z26U', 'C', 'sasakey=='] + # [':irc.local.org', 'SASL', 'async defender-dev.deb.biz.st', '00157Z26U', 'D', 'A'] + psasl = self._ctx.Sasl sasl_enabled = True # Should be False - for smod in self.__Settings.SMOD_MODULES: + for smod in self._ctx.Settings.SMOD_MODULES: if smod.name == 'sasl': sasl_enabled = True break @@ -1067,7 +1052,7 @@ class Inspircd(IProtocol): client_uid = scopy[4] if len(scopy) >= 6 else None # sasl_obj = None sasl_message_type = scopy[6] if len(scopy) >= 6 else None - psasl.insert_sasl_client(self.__Irc.Loader.Definition.MSasl(client_uid=client_uid)) + psasl.insert_sasl_client(self._ctx.Definition.MSasl(client_uid=client_uid)) sasl_obj = psasl.get_sasl_obj(client_uid) if sasl_obj is None: @@ -1085,15 +1070,15 @@ class Inspircd(IProtocol): sasl_obj.mechanisme = str(scopy[7]) if sasl_obj.mechanisme == "PLAIN": - self.send2socket(f":{self.__Config.SERVEUR_ID} SASL {self.__Config.SERVEUR_HOSTNAME} {sasl_obj.client_uid} C +") + await self.send2socket(f":{self._ctx.Config.SERVEUR_ID} SASL {self._ctx.Config.SERVEUR_HOSTNAME} {sasl_obj.client_uid} C +") elif sasl_obj.mechanisme == "EXTERNAL": if str(scopy[7]) == "+": return None sasl_obj.fingerprint = str(scopy[8]) - self.send2socket(f":{self.__Config.SERVEUR_ID} SASL {self.__Config.SERVEUR_HOSTNAME} {sasl_obj.client_uid} C +") + await self.send2socket(f":{self._ctx.Config.SERVEUR_ID} SASL {self._ctx.Config.SERVEUR_HOSTNAME} {sasl_obj.client_uid} C +") - self.on_sasl_authentication_process(sasl_obj) + await self.on_sasl_authentication_process(sasl_obj) return sasl_obj case 'C': @@ -1106,31 +1091,31 @@ class Inspircd(IProtocol): sasl_obj.username = username sasl_obj.password = password - self.on_sasl_authentication_process(sasl_obj) + await self.on_sasl_authentication_process(sasl_obj) return sasl_obj elif sasl_obj.mechanisme == "EXTERNAL": sasl_obj.message_type = sasl_message_type - self.on_sasl_authentication_process(sasl_obj) + await self.on_sasl_authentication_process(sasl_obj) return sasl_obj except Exception as err: - self.__Logs.error(f'General Error: {err}', exc_info=True) + self._ctx.Logs.error(f'General Error: {err}', exc_info=True) - def on_sasl_authentication_process(self, sasl_model: 'MSasl'): + async def on_sasl_authentication_process(self, sasl_model: 'MSasl'): s = sasl_model - server_id = self.__Config.SERVEUR_ID - main_server_hostname = self.__Settings.MAIN_SERVER_HOSTNAME - db_admin_table = self.__Config.TABLE_ADMIN + server_id = self._ctx.Config.SERVEUR_ID + main_server_hostname = self._ctx.Settings.MAIN_SERVER_HOSTNAME + db_admin_table = self._ctx.Config.TABLE_ADMIN if sasl_model: - def db_get_admin_info(*, username: Optional[str] = None, password: Optional[str] = None, fingerprint: Optional[str] = None) -> Optional[dict[str, Any]]: + async def db_get_admin_info(*, username: Optional[str] = None, password: Optional[str] = None, fingerprint: Optional[str] = None) -> Optional[dict[str, Any]]: if fingerprint: mes_donnees = {'fingerprint': fingerprint} query = f"SELECT user, level, language FROM {db_admin_table} WHERE fingerprint = :fingerprint" else: - mes_donnees = {'user': username, 'password': self.__Utils.hash_password(password)} + mes_donnees = {'user': username, 'password': self._ctx.Utils.hash_password(password)} query = f"SELECT user, level, language FROM {db_admin_table} WHERE user = :user AND password = :password" - result = self.__Base.db_execute_query(query, mes_donnees) + result = await self._ctx.Base.db_execute_query(query, mes_donnees) user_from_db = result.fetchone() if user_from_db: return {'user': user_from_db[0], 'level': user_from_db[1], 'language': user_from_db[2]} @@ -1139,37 +1124,37 @@ class Inspircd(IProtocol): if s.message_type == 'C' and s.mechanisme == 'PLAIN': # Connection via PLAIN - admin_info = db_get_admin_info(username=s.username, password=s.password) + admin_info = await db_get_admin_info(username=s.username, password=s.password) if admin_info is not None: s.auth_success = True s.level = admin_info.get('level', 0) s.language = admin_info.get('language', 'EN') - self.send2socket(f":{server_id} SASL {main_server_hostname} {s.client_uid} D S") - self.send2socket(f":{server_id} SASL {s.username} :SASL authentication successful") + await self.send2socket(f":{server_id} SASL {main_server_hostname} {s.client_uid} D S") + await self.send2socket(f":{server_id} SASL {s.username} :SASL authentication successful") else: - self.send2socket(f":{server_id} SASL {main_server_hostname} {s.client_uid} D F") - self.send2socket(f":{server_id} SASL {s.username} :SASL authentication failed") + await self.send2socket(f":{server_id} SASL {main_server_hostname} {s.client_uid} D F") + await self.send2socket(f":{server_id} SASL {s.username} :SASL authentication failed") elif s.message_type == 'S' and s.mechanisme == 'EXTERNAL': # Connection using fingerprints - admin_info = db_get_admin_info(fingerprint=s.fingerprint) + admin_info = await db_get_admin_info(fingerprint=s.fingerprint) if admin_info is not None: s.auth_success = True s.level = admin_info.get('level', 0) s.username = admin_info.get('user', None) s.language = admin_info.get('language', 'EN') - self.send2socket(f":{server_id} SASL {main_server_hostname} {s.client_uid} D S") - self.send2socket(f":{server_id} SASL {s.username} :SASL authentication successful") + await self.send2socket(f":{server_id} SASL {main_server_hostname} {s.client_uid} D S") + await self.send2socket(f":{server_id} SASL {s.username} :SASL authentication successful") else: # "904 :SASL authentication failed" - self.send2socket(f":{server_id} SASL {main_server_hostname} {s.client_uid} D F") - self.send2socket(f":{server_id} SASL {s.username} :SASL authentication failed") + await self.send2socket(f":{server_id} SASL {main_server_hostname} {s.client_uid} D F") + await self.send2socket(f":{server_id} SASL {s.username} :SASL authentication failed") - def on_error(self, server_msg: list[str]) -> None: - self.__Logs.debug(f"{server_msg}") + async def on_error(self, server_msg: list[str]) -> None: + self._ctx.Logs.debug(f"{server_msg}") - def on_metedata(self, server_msg: list[str]) -> None: + async def on_metedata(self, server_msg: list[str]) -> None: """_summary_ Args: @@ -1178,80 +1163,64 @@ class Inspircd(IProtocol): # [':97K', 'METADATA', '97KAAAAAA', 'ssl_cert', ':vTrSe', 'fingerprint90753683519522875', # '/C=FR/OU=Testing/O=Test', 'Sasl/CN=localhost', '/C=FR/OU=Testing/O=Test', 'Sasl/CN=localhost'] scopy = server_msg.copy() - dnickname = self.__Config.SERVICE_NICKNAME - dchanlog = self.__Config.SERVICE_CHANLOG - green = self.__Config.COLORS.green - nogc = self.__Config.COLORS.nogc + dnickname = self._ctx.Config.SERVICE_NICKNAME + dchanlog = self._ctx.Config.SERVICE_CHANLOG + green = self._ctx.Config.COLORS.green + nogc = self._ctx.Config.COLORS.nogc if 'ssl_cert' in scopy: fingerprint = scopy[5] uid = scopy[2] - user_obj = self.__Irc.User.get_user(uid) + user_obj = self._ctx.User.get_user(uid) if user_obj: user_obj.fingerprint = fingerprint - if self.__Irc.Admin.db_auth_admin_via_fingerprint(fingerprint, uid): - admin = self.__Irc.Admin.get_admin(uid) + if await self._ctx.Admin.db_auth_admin_via_fingerprint(fingerprint, uid): + admin = self._ctx.Admin.get_admin(uid) account = admin.account if admin else '' - self.send_priv_msg(nick_from=dnickname, + await self.send_priv_msg(nick_from=dnickname, msg=tr("[ %sSASL AUTO AUTH%s ] - %s (%s) is now connected successfuly to %s", green, nogc, user_obj.nickname, account, dnickname), channel=dchanlog) - self.send_notice(nick_from=dnickname, nick_to=user_obj.nickname, msg=tr("Successfuly connected to %s", dnickname)) + await self.send_notice(nick_from=dnickname, nick_to=user_obj.nickname, msg=tr("Successfuly connected to %s", dnickname)) + + async def on_kick(self, server_msg: list[str]) -> None: + ... # ------------------------------------------------------------------------ # COMMON IRC PARSER # ------------------------------------------------------------------------ - def parse_uid(self, server_msg: list[str]) -> dict[str, str]: + async def parse_uid(self, server_msg: list[str]) -> Optional['MUser']: """Parse UID and return dictionary. - + >>> [':97K', 'UID', '97KAAAAAC', '1762113659', 'adator_', '172.18.128.1', '172.18.128.1', '...', '...', '172.18.128.1', '1762113659', '+', ':...'] Args: server_msg (list[str]): _description_ """ - umodes = str(server_msg[11]) - remote_ip = server_msg[9] if 'S' not in umodes else '127.0.0.1' + scopy = server_msg.copy() + uid = scopy[2] + return self._ctx.User.get_user(uid) - # Extract Geoip information - pattern = r'^.*geoip=cc=(\S{2}).*$' - geoip_match = match(pattern, server_msg[0]) - geoip = geoip_match.group(1) if geoip_match else None - - response = { - 'uid': str(server_msg[2]), - 'nickname': str(server_msg[4]), - 'username': str(server_msg[7]), - 'hostname': str(server_msg[5]), - 'umodes': umodes, - 'vhost': str(server_msg[6]), - 'ip': remote_ip, - 'realname': ' '.join(server_msg[12:]).lstrip(':'), - 'geoip': geoip, - 'reputation_score': 0, - 'iswebirc': True if 'webirc' in server_msg[0] else False, - 'iswebsocket': True if 'websocket' in server_msg[0] else False - } - return response - - def parse_quit(self, server_msg: list[str]) -> dict[str, str]: + async def parse_quit(self, server_msg: list[str]) -> tuple[Optional['MUser'], str]: """Parse quit and return dictionary. >>> [':97KAAAAAB', 'QUIT', ':Quit:', 'this', 'is', 'my', 'reason', 'to', 'quit'] Args: server_msg (list[str]): The server message to parse Returns: - dict[str, str]: The dictionary. + tuple[MUser, str]: The User Who Quit Object and the reason. """ scopy = server_msg.copy() if scopy[0].startswith('@'): scopy.pop(0) + + user_obj = self._ctx.User.get_user(self._ctx.Utils.clean_uid(scopy[0])) + tmp_reason = scopy[3:] + tmp_reason[0] = tmp_reason[0].replace(':', '') + reason = ' '.join(tmp_reason) - response = { - "uid": scopy[0].replace(':', ''), - "reason": " ".join(scopy[3:]) - } - return response + return user_obj, reason - def parse_nick(self, server_msg: list[str]) -> dict[str, str]: + async def parse_nick(self, server_msg: list[str]) -> tuple[Optional['MUser'], str, str]: """Parse nick changes. >>> [':97KAAAAAC', 'NICK', 'testinspir', '1757360740'] @@ -1265,14 +1234,12 @@ class Inspircd(IProtocol): if scopy[0].startswith('@'): scopy.pop(0) - response = { - "uid": scopy[0].replace(':', ''), - "newnickname": scopy[2], - "timestamp": scopy[3] - } - return response + user_obj = self._ctx.User.get_user(self._ctx.User.clean_uid(scopy[0])) + newnickname = scopy[2] + timestamp = scopy[3] + return user_obj, newnickname, timestamp - def parse_privmsg(self, server_msg: list[str]) -> dict[str, str]: + async def parse_privmsg(self, server_msg: list[str]) -> tuple[Optional['MUser'], Optional['MUser'], Optional['MChannel'], str]: """Parse PRIVMSG message. >>> [':97KAAAAAE', 'PRIVMSG', '#welcome', ':This', 'is', 'my', 'public', 'message'] >>> [':97KAAAAAF', 'PRIVMSG', '98KAAAAAB', ':My','Message','...'] @@ -1287,20 +1254,21 @@ class Inspircd(IProtocol): if scopy[0].startswith('@'): scopy.pop(0) - response = { - "uid_sender": scopy[0].replace(':', ''), - "uid_reciever": self.__Irc.User.get_uid(scopy[2]), - "channel": scopy[2] if self.__Irc.Channel.is_valid_channel(scopy[2]) else None, - "message": " ".join(scopy[3:]) - } - return response + sender = self._ctx.User.get_user(self._ctx.Utils.clean_uid(scopy[0])) + reciever = self._ctx.User.get_user(self._ctx.Utils.clean_uid(scopy[2])) + channel = self._ctx.Channel.get_channel(scopy[2]) if self._ctx.Channel.is_valid_channel(scopy[2]) else None + tmp_message = scopy[3:] + tmp_message = tmp_message[0].replace(':', '') + message = ' '.join(tmp_message) + + return sender, reciever, channel, message # ------------------------------------------------------------------------ # IRC SENDER METHODS # ------------------------------------------------------------------------ - def send_mode_chan(self, channel_name: str, channel_mode: str) -> None: + async def send_mode_chan(self, channel_name: str, channel_mode: str) -> None: """_summary_ Args: @@ -1309,7 +1277,7 @@ class Inspircd(IProtocol): """ ... - def send_gline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: + async def send_gline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: """Send a gline command to the server Args: @@ -1322,7 +1290,7 @@ class Inspircd(IProtocol): """ ... - def send_sajoin(self, nick_to_sajoin: str, channel_name: str) -> None: + async def send_sajoin(self, nick_to_sajoin: str, channel_name: str) -> None: """_summary_ Args: @@ -1331,7 +1299,7 @@ class Inspircd(IProtocol): """ ... - def send_sapart(self, nick_to_sapart: str, channel_name: str) -> None: + async def send_sapart(self, nick_to_sapart: str, channel_name: str) -> None: """_summary_ Args: @@ -1340,7 +1308,7 @@ class Inspircd(IProtocol): """ ... - def send_svs2mode(self, nickname: str, user_mode: str) -> None: + async def send_svs2mode(self, nickname: str, user_mode: str) -> None: """_summary_ Args: @@ -1349,7 +1317,7 @@ class Inspircd(IProtocol): """ ... - def send_svsjoin(self, nick_to_part: str, channels: list[str], keys: list[str]) -> None: + async def send_svsjoin(self, nick_to_part: str, channels: list[str], keys: list[str]) -> None: """_summary_ Args: @@ -1359,7 +1327,7 @@ class Inspircd(IProtocol): """ ... - def send_svslogin(self, client_uid: str, user_account: str) -> None: + async def send_svslogin(self, client_uid: str, user_account: str) -> None: """Log a client into his account. Args: @@ -1368,7 +1336,7 @@ class Inspircd(IProtocol): """ ... - def send_svslogout(self, client_obj: 'MClient') -> None: + async def send_svslogout(self, client_obj: 'MClient') -> None: """Logout a client from his account Args: @@ -1376,7 +1344,7 @@ class Inspircd(IProtocol): """ ... - def send_svsmode(self, nickname: str, user_mode: str) -> None: + async def send_svsmode(self, nickname: str, user_mode: str) -> None: """_summary_ Args: @@ -1385,7 +1353,7 @@ class Inspircd(IProtocol): """ ... - def send_svspart(self, nick_to_part: str, channels: list[str], reason: str) -> None: + async def send_svspart(self, nick_to_part: str, channels: list[str], reason: str) -> None: """_summary_ Args: @@ -1395,7 +1363,7 @@ class Inspircd(IProtocol): """ ... - def on_md(self, server_msg: list[str]) -> None: + async def on_md(self, server_msg: list[str]) -> None: """Handle MD responses [':001', 'MD', 'client', '001MYIZ03', 'certfp', ':d1235648...'] Args: @@ -1403,7 +1371,7 @@ class Inspircd(IProtocol): """ ... - def on_mode(self, server_msg: list[str]) -> None: + async def on_mode(self, server_msg: list[str]) -> None: """Handle mode coming from a server Args: @@ -1411,7 +1379,7 @@ class Inspircd(IProtocol): """ ... - def on_reputation(self, server_msg: list[str]) -> None: + async def on_reputation(self, server_msg: list[str]) -> None: """Handle REPUTATION coming from a server Args: @@ -1419,7 +1387,7 @@ class Inspircd(IProtocol): """ ... - def on_smod(self, server_msg: list[str]) -> None: + async def on_smod(self, server_msg: list[str]) -> None: """Handle SMOD message coming from the server Args: @@ -1427,7 +1395,7 @@ class Inspircd(IProtocol): """ ... - def on_svs2mode(self, server_msg: list[str]) -> None: + async def on_svs2mode(self, server_msg: list[str]) -> None: """Handle svs2mode coming from a server >>> [':00BAAAAAG', 'SVS2MODE', '001U01R03', '-r'] @@ -1436,10 +1404,18 @@ class Inspircd(IProtocol): """ ... - def on_eos(self, server_msg: list[str]) -> None: + async def on_eos(self, server_msg: list[str]) -> None: """Handle EOS coming from a server Args: server_msg (list[str]): Original server message """ ... + + async def on_sethost(self, server_msg: list[str]) -> None: + """On SETHOST command + + Args: + server_msg (list[str]): _description_ + """ + ... diff --git a/core/classes/protocols/unreal6.py b/core/classes/protocols/unreal6.py index 66933de..b6dc9ef 100644 --- a/core/classes/protocols/unreal6.py +++ b/core/classes/protocols/unreal6.py @@ -3,39 +3,23 @@ from re import match, findall, search from datetime import datetime from typing import TYPE_CHECKING, Any, Optional from ssl import SSLEOFError, SSLError - -from core.classes.protocols.command_handler import CommandHandler -from core.classes.protocols.interface import IProtocol +from core.classes.interfaces.iprotocol import IProtocol from core.utils import tr if TYPE_CHECKING: - from core.irc import Irc - from core.classes.sasl import Sasl - from core.definition import MClient, MSasl + from core.definition import MClient, MSasl, MUser, MChannel class Unrealircd6(IProtocol): - def __init__(self, ircInstance: 'Irc'): + def init_protocol(self) -> None: self.name = 'UnrealIRCD-6' self.protocol_version = 6100 - - self.__Irc = ircInstance - self.__Config = ircInstance.Config - self.__Base = ircInstance.Base - self.__Settings = ircInstance.Base.Settings - self.__Utils = ircInstance.Loader.Utils - self.__Logs = ircInstance.Loader.Logs - self.known_protocol: set[str] = {'SJOIN', 'UID', 'MD', 'QUIT', 'SQUIT', 'EOS', 'PRIVMSG', 'MODE', 'UMODE2', 'VERSION', 'REPUTATION', 'SVS2MODE', 'SLOG', 'NICK', 'PART', 'PONG', 'SASL', 'PING', 'PROTOCTL', 'SERVER', 'SMOD', 'TKL', 'NETINFO', - '006', '007', '018'} - - self.Handler = CommandHandler(ircInstance.Loader) - - self.__Logs.info(f"[PROTOCOL] Protocol [{__name__}] loaded!") + 'SETHOST', '006', '007', '018'} def get_ircd_protocol_poisition(self, cmd: list[str], log: bool = False) -> tuple[int, Optional[str]]: """Get the position of known commands @@ -48,16 +32,16 @@ class Unrealircd6(IProtocol): tuple[int, Optional[str]]: The position and the command. """ for index, token in enumerate(cmd): - if token.upper() in self.known_protocol: + if token.upper() in self.known_protocol and index < 3: return index, token.upper() if log: - self.__Logs.debug(f"[IRCD LOGS] You need to handle this response: {cmd}") + self._ctx.Logs.debug(f"[IRCD LOGS] You need to handle this response: {cmd}") - return (-1, None) + return -1, None def register_command(self) -> None: - m = self.__Irc.Loader.Definition.MIrcdCommand + m = self._ctx.Definition.MIrcdCommand self.Handler.register(m(command_name="PING", func=self.on_server_ping)) self.Handler.register(m(command_name="UID", func=self.on_uid)) self.Handler.register(m(command_name="QUIT", func=self.on_quit)) @@ -77,6 +61,7 @@ class Unrealircd6(IProtocol): self.Handler.register(m(command_name="MD", func=self.on_md)) self.Handler.register(m(command_name="PRIVMSG", func=self.on_privmsg)) self.Handler.register(m(command_name="KICK", func=self.on_kick)) + self.Handler.register(m(command_name="SETHOST", func=self.on_sethost)) return None @@ -114,36 +99,40 @@ class Unrealircd6(IProtocol): return None - def send2socket(self, message: str, print_log: bool = True) -> None: + async def send2socket(self, message: str, print_log: bool = True) -> None: """Envoit les commandes à envoyer au serveur. Args: - string (Str): contient la commande à envoyer au serveur. + message (str): contient la commande à envoyer au serveur. + print_log (bool): True print log message in the console """ try: - with self.__Base.lock: - self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[0])) + async with self._ctx.Settings.AILOCK: + self._ctx.Irc.writer.write(f"{message}\r\n".encode(self._ctx.Config.SERVEUR_CHARSET[0])) + await self._ctx.Irc.writer.drain() if print_log: - self.__Logs.debug(f'<< {message}') + self._ctx.Logs.debug(f'<< {message}') except UnicodeDecodeError as ude: - self.__Logs.error(f'Decode Error try iso-8859-1 - {ude} - {message}') - self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[1],'replace')) + self._ctx.Logs.error(f'Decode Error try iso-8859-1 - {ude} - {message}') + self._ctx.Irc.writer.write(f"{message}\r\n".encode(self._ctx.Config.SERVEUR_CHARSET[1],'replace')) + await self._ctx.Irc.writer.drain() except UnicodeEncodeError as uee: - self.__Logs.error(f'Encode Error try iso-8859-1 - {uee} - {message}') - self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[1],'replace')) + self._ctx.Logs.error(f'Encode Error try iso-8859-1 - {uee} - {message}') + self._ctx.Irc.writer.write(f"{message}\r\n".encode(self._ctx.Config.SERVEUR_CHARSET[1],'replace')) + await self._ctx.Irc.writer.drain() except AssertionError as ae: - self.__Logs.warning(f'Assertion Error {ae} - message: {message}') + self._ctx.Logs.warning(f'Assertion Error {ae} - message: {message}') except SSLEOFError as soe: - self.__Logs.error(f"SSLEOFError: {soe} - {message}") + self._ctx.Logs.error(f"SSLEOFError: {soe} - {message}") except SSLError as se: - self.__Logs.error(f"SSLError: {se} - {message}") + self._ctx.Logs.error(f"SSLError: {se} - {message}") except OSError as oe: - self.__Logs.error(f"OSError: {oe} - {message}") + self._ctx.Logs.error(f"OSError: {oe} - {message}") except AttributeError as ae: - self.__Logs.critical(f"Attribute Error: {ae}") + self._ctx.Logs.critical(f"Attribute Error: {ae}") - def send_priv_msg(self, nick_from: str, msg: str, channel: str = None, nick_to: str = None): + async def send_priv_msg(self, nick_from: str, msg: str, channel: str = None, nick_to: str = None): """Sending PRIVMSG to a channel or to a nickname by batches could be either channel or nickname not both together Args: @@ -153,29 +142,29 @@ class Unrealircd6(IProtocol): nick_to (str, optional): The reciever nickname. Defaults to None. """ try: - batch_size = self.__Config.BATCH_SIZE - User_from = self.__Irc.User.get_user(nick_from) - User_to = self.__Irc.User.get_user(nick_to) if not nick_to is None else None + batch_size = self._ctx.Config.BATCH_SIZE + user_from = self._ctx.User.get_user(nick_from) + user_to = self._ctx.User.get_user(nick_to) if not nick_to is None else None - if User_from is None: - self.__Logs.error(f"The sender nickname [{nick_from}] do not exist") + if user_from is None: + self._ctx.Logs.error(f"The sender nickname [{nick_from}] do not exist") return None 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":{User_from.uid} PRIVMSG {channel} :{batch}") + await self.send2socket(f":{user_from.uid} PRIVMSG {channel} :{batch}") if not nick_to is None: for i in range(0, len(str(msg)), batch_size): batch = str(msg)[i:i+batch_size] - self.send2socket(f":{nick_from} PRIVMSG {User_to.uid} :{batch}") + await self.send2socket(f":{nick_from} PRIVMSG {user_to.uid} :{batch}") except Exception as err: - self.__Logs.error(f"General Error: {err}") - self.__Logs.error(f"General Error: {nick_from} - {channel} - {nick_to}") + self._ctx.Logs.error(f"General Error: {err}") + self._ctx.Logs.error(f"General Error: {nick_from} - {channel} - {nick_to}") - def send_notice(self, nick_from: str, nick_to: str, msg: str) -> None: + async def send_notice(self, nick_from: str, nick_to: str, msg: str) -> None: """Sending NOTICE by batches Args: @@ -184,61 +173,61 @@ class Unrealircd6(IProtocol): nick_to (str): The reciever nickname """ try: - batch_size = self.__Config.BATCH_SIZE - User_from = self.__Irc.User.get_user(nick_from) - User_to = self.__Irc.User.get_user(nick_to) + batch_size = self._ctx.Config.BATCH_SIZE + user_from = self._ctx.User.get_user(nick_from) + user_to = self._ctx.User.get_user(nick_to) - if User_from is None or User_to is None: - self.__Logs.error(f"The sender [{nick_from}] or the Reciever [{nick_to}] do not exist") + if user_from is None or user_to is None: + self._ctx.Logs.error(f"The sender [{nick_from}] or the Reciever [{nick_to}] do not exist") return None for i in range(0, len(str(msg)), batch_size): batch = str(msg)[i:i+batch_size] - self.send2socket(f":{User_from.uid} NOTICE {User_to.uid} :{batch}") + await self.send2socket(f":{user_from.uid} NOTICE {user_to.uid} :{batch}") except Exception as err: - self.__Logs.error(f"General Error: {err}") + self._ctx.Logs.error(f"General Error: {err}") - def send_link(self): + async def send_link(self): """Créer le link et envoyer les informations nécessaires pour la connexion au serveur. """ - service_id = self.__Config.SERVICE_ID - service_nickname = self.__Config.SERVICE_NICKNAME - service_username = self.__Config.SERVICE_USERNAME - service_realname = self.__Config.SERVICE_REALNAME - service_channel_log = self.__Config.SERVICE_CHANLOG - service_info = self.__Config.SERVICE_INFO - service_smodes = self.__Config.SERVICE_SMODES - service_cmodes = self.__Config.SERVICE_CMODES - service_umodes = self.__Config.SERVICE_UMODES - service_hostname = self.__Config.SERVICE_HOST - service_name = self.__Config.SERVICE_NAME + service_id = self._ctx.Config.SERVICE_ID + service_nickname = self._ctx.Config.SERVICE_NICKNAME + service_username = self._ctx.Config.SERVICE_USERNAME + service_realname = self._ctx.Config.SERVICE_REALNAME + service_channel_log = self._ctx.Config.SERVICE_CHANLOG + service_info = self._ctx.Config.SERVICE_INFO + service_smodes = self._ctx.Config.SERVICE_SMODES + service_cmodes = self._ctx.Config.SERVICE_CMODES + # service_umodes = self._ctx.Config.SERVICE_UMODES + service_hostname = self._ctx.Config.SERVICE_HOST + service_name = self._ctx.Config.SERVICE_NAME protocolversion = self.protocol_version - server_password = self.__Config.SERVEUR_PASSWORD - server_link = self.__Config.SERVEUR_LINK - server_id = self.__Config.SERVEUR_ID + server_password = self._ctx.Config.SERVEUR_PASSWORD + server_link = self._ctx.Config.SERVEUR_LINK + server_id = self._ctx.Config.SERVEUR_ID - version = self.__Config.CURRENT_VERSION - unixtime = self.__Utils.get_unixtime() + version = self._ctx.Config.CURRENT_VERSION + unixtime = self._ctx.Utils.get_unixtime() - self.send2socket(f":{server_id} PASS :{server_password}", print_log=False) - self.send2socket(f":{server_id} PROTOCTL SID NOQUIT NICKv2 SJOIN SJ3 NICKIP TKLEXT2 NEXTBANS CLK EXTSWHOIS MLOCK MTAGS") - self.send2socket(f":{server_id} PROTOCTL EAUTH={server_link},{protocolversion},,{service_name}-v{version}") - self.send2socket(f":{server_id} PROTOCTL SID={server_id}") - self.send2socket(f":{server_id} PROTOCTL BOOTED={unixtime}") - self.send2socket(f":{server_id} SERVER {server_link} 1 :{service_info}") - self.send2socket("EOS") - self.send2socket(f":{server_id} {service_nickname} :Reserved for services") - self.send2socket(f":{server_id} UID {service_nickname} 1 {unixtime} {service_username} {service_hostname} {service_id} * {service_smodes} * * fwAAAQ== :{service_realname}") - self.send_sjoin(service_channel_log) - self.send2socket(f":{server_id} TKL + Q * {service_nickname} {service_hostname} 0 {unixtime} :Reserved for services") - self.send2socket(f":{service_id} MODE {service_channel_log} {service_cmodes}") + await self.send2socket(f":{server_id} PASS :{server_password}", print_log=False) + await self.send2socket(f":{server_id} PROTOCTL SID NOQUIT NICKv2 SJOIN SJ3 NICKIP TKLEXT2 NEXTBANS CLK EXTSWHOIS MLOCK MTAGS") + await self.send2socket(f":{server_id} PROTOCTL EAUTH={server_link},{protocolversion},,{service_name}-v{version}") + await self.send2socket(f":{server_id} PROTOCTL SID={server_id}") + await self.send2socket(f":{server_id} PROTOCTL BOOTED={unixtime}") + await self.send2socket(f":{server_id} SERVER {server_link} 1 :{service_info}") + await self.send2socket(f":{server_id} {service_nickname} :Reserved for services") + await self.send2socket(f":{server_id} UID {service_nickname} 1 {unixtime} {service_username} {service_hostname} {service_id} * {service_smodes} * * fwAAAQ== :{service_realname}") + await self.send2socket("EOS") + await self.send_sjoin(service_channel_log) + await self.send2socket(f":{server_id} TKL + Q * {service_nickname} {service_hostname} 0 {unixtime} :Reserved for services") + await self.send2socket(f":{service_id} MODE {service_channel_log} {service_cmodes}") - self.__Logs.debug(f'>> {__name__} Link information sent to the server') + self._ctx.Logs.debug(f'>> {__name__} Link information sent to the server') - def send_gline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: + async def send_gline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: """Send a gline command to the server Args: @@ -251,23 +240,23 @@ class Unrealircd6(IProtocol): """ # TKL + G user host set_by expire_timestamp set_at_timestamp :reason - self.send2socket(f":{self.__Config.SERVEUR_ID} TKL + G {nickname} {hostname} {set_by} {expire_timestamp} {set_at_timestamp} :{reason}") + await self.send2socket(f":{self._ctx.Config.SERVEUR_ID} TKL + G {nickname} {hostname} {set_by} {expire_timestamp} {set_at_timestamp} :{reason}") return None - def send_set_nick(self, newnickname: str) -> None: + async def send_set_nick(self, newnickname: str) -> None: """Change nickname of the server \n This method will also update the User object Args: newnickname (str): New nickname of the server """ - self.send2socket(f":{self.__Config.SERVICE_NICKNAME} NICK {newnickname}") + await self.send2socket(f":{self._ctx.Config.SERVICE_NICKNAME} NICK {newnickname}") - userObj = self.__Irc.User.get_user(self.__Config.SERVICE_NICKNAME) - self.__Irc.User.update_nickname(userObj.uid, newnickname) + user_obj = self._ctx.User.get_user(self._ctx.Config.SERVICE_NICKNAME) + self._ctx.User.update_nickname(user_obj.uid, newnickname) return None - def send_set_mode(self, modes: str, *, nickname: Optional[str] = None, channel_name: Optional[str] = None, params: Optional[str] = None) -> None: + async def send_set_mode(self, modes: str, *, nickname: Optional[str] = None, channel_name: Optional[str] = None, params: Optional[str] = None) -> None: """Set a mode to channel or to a nickname or for a user in a channel Args: @@ -276,103 +265,102 @@ class Unrealircd6(IProtocol): channel_name (Optional[str]): The channel name params (Optional[str]): Parameters like password. """ - service_id = self.__Config.SERVICE_ID + service_id = self._ctx.Config.SERVICE_ID if modes[0] not in ['+', '-']: - self.__Logs.error(f"[MODE ERROR] The mode you have provided is missing the sign: {modes}") + self._ctx.Logs.error(f"[MODE ERROR] The mode you have provided is missing the sign: {modes}") return None if nickname and channel_name: # :98KAAAAAB MODE #services +o defenderdev - if not self.__Irc.Channel.is_valid_channel(channel_name): - self.__Logs.error(f"[MODE ERROR] The channel is not valid: {channel_name}") + if not self._ctx.Channel.is_valid_channel(channel_name): + self._ctx.Logs.error(f"[MODE ERROR] The channel is not valid: {channel_name}") return None - self.send2socket(f":{service_id} MODE {channel_name} {modes} {nickname}") + await self.send2socket(f":{service_id} MODE {channel_name} {modes} {nickname}") return None - + if nickname and channel_name is None: - self.send2socket(f":{service_id} MODE {nickname} {modes}") + await self.send2socket(f":{service_id} MODE {nickname} {modes}") return None - + if nickname is None and channel_name: - if not self.__Irc.Channel.is_valid_channel(channel_name): - self.__Logs.error(f"[MODE ERROR] The channel is not valid: {channel_name}") + if not self._ctx.Channel.is_valid_channel(channel_name): + self._ctx.Logs.error(f"[MODE ERROR] The channel is not valid: {channel_name}") return None - self.send2socket(f":{service_id} MODE {channel_name} {modes} {params}") + await self.send2socket(f":{service_id} MODE {channel_name} {modes}") if params is None else await self.send2socket(f":{service_id} MODE {channel_name} {modes} {params}") return None - + return None - def send_squit(self, server_id: str, server_link: str, reason: str) -> None: + async def send_squit(self, server_id: str, server_link: str, reason: str) -> None: if not reason: reason = 'Service Shutdown' - self.send2socket(f":{server_id} SQUIT {server_link} :{reason}") + await self.send2socket(f":{server_id} SQUIT {server_link} :{reason}") return None - def send_ungline(self, nickname:str, hostname: str) -> None: + async def send_ungline(self, nickname:str, hostname: str) -> None: - self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - G {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}") + await self.send2socket(f":{self._ctx.Config.SERVEUR_ID} TKL - G {nickname} {hostname} {self._ctx.Config.SERVICE_NICKNAME}") return None - def send_kline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: + async def send_kline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: # TKL + k user host set_by expire_timestamp set_at_timestamp :reason - self.send2socket(f":{self.__Config.SERVEUR_ID} TKL + k {nickname} {hostname} {set_by} {expire_timestamp} {set_at_timestamp} :{reason}") + await self.send2socket(f":{self._ctx.Config.SERVEUR_ID} TKL + k {nickname} {hostname} {set_by} {expire_timestamp} {set_at_timestamp} :{reason}") return None - def send_unkline(self, nickname:str, hostname: str) -> None: + async def send_unkline(self, nickname:str, hostname: str) -> None: - self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - K {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}") + await self.send2socket(f":{self._ctx.Config.SERVEUR_ID} TKL - K {nickname} {hostname} {self._ctx.Config.SERVICE_NICKNAME}") return None - def send_sjoin(self, channel: str) -> None: + async def send_sjoin(self, channel: str) -> None: """Server will join a channel with pre defined umodes Args: channel (str): Channel to join """ - if not self.__Irc.Channel.is_valid_channel(channel): - self.__Logs.error(f"The channel [{channel}] is not valid") + if not self._ctx.Channel.is_valid_channel(channel): + self._ctx.Logs.error(f"The channel [{channel}] is not valid") return None - self.send2socket(f":{self.__Config.SERVEUR_ID} SJOIN {self.__Utils.get_unixtime()} {channel} {self.__Config.SERVICE_UMODES} :{self.__Config.SERVICE_ID}") - self.send2socket(f":{self.__Config.SERVICE_ID} MODE {channel} {self.__Config.SERVICE_UMODES} {self.__Config.SERVICE_ID}") + await self.send2socket(f":{self._ctx.Config.SERVEUR_ID} SJOIN {self._ctx.Utils.get_unixtime()} {channel} {self._ctx.Config.SERVICE_UMODES} :{self._ctx.Config.SERVICE_ID}") + await self.send2socket(f":{self._ctx.Config.SERVICE_ID} MODE {channel} {self._ctx.Config.SERVICE_UMODES} {self._ctx.Config.SERVICE_ID}") # Add defender to the channel uids list - self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[self.__Config.SERVICE_ID])) + self._ctx.Channel.insert(self._ctx.Definition.MChannel(name=channel, uids=[self._ctx.Config.SERVICE_ID])) return None - def send_sapart(self, nick_to_sapart: str, channel_name: str) -> None: + async def send_sapart(self, nick_to_sapart: str, channel_name: str) -> None: """_summary_ Args: - from_nick (str): _description_ - nick_to (str): _description_ + nick_to_sapart (str): _description_ channel_name (str): _description_ """ try: - userObj = self.__Irc.User.get_user(uidornickname=nick_to_sapart) - chanObj = self.__Irc.Channel.get_channel(channel_name) - service_uid = self.__Config.SERVICE_ID + user_obj = self._ctx.User.get_user(uidornickname=nick_to_sapart) + chan_obj = self._ctx.Channel.get_channel(channel_name) + service_uid = self._ctx.Config.SERVICE_ID - if userObj is None or chanObj is None: + if user_obj is None or chan_obj is None: return None - self.send2socket(f":{service_uid} SAPART {userObj.nickname} {chanObj.name}") - self.__Irc.Channel.delete_user_from_channel(chanObj.name, userObj.uid) + await self.send2socket(f":{service_uid} SAPART {user_obj.nickname} {chan_obj.name}") + self._ctx.Channel.delete_user_from_channel(chan_obj.name, user_obj.uid) return None except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def send_sajoin(self, nick_to_sajoin: str, channel_name: str) -> None: + async def send_sajoin(self, nick_to_sajoin: str, channel_name: str) -> None: """_summary_ Args: @@ -381,94 +369,94 @@ class Unrealircd6(IProtocol): """ try: - userObj = self.__Irc.User.get_user(uidornickname=nick_to_sajoin) - chanObj = self.__Irc.Channel.get_channel(channel_name) - service_uid = self.__Config.SERVICE_ID + user_obj = self._ctx.User.get_user(uidornickname=nick_to_sajoin) + chan_obj = self._ctx.Channel.get_channel(channel_name) + service_uid = self._ctx.Config.SERVICE_ID - if userObj is None: + if user_obj is None: # User not exist: leave return None - if chanObj is None: + if chan_obj is None: # Channel not exist - if not self.__Irc.Channel.is_valid_channel(channel_name): + if not self._ctx.Channel.is_valid_channel(channel_name): # Incorrect channel: leave return None # Create the new channel with the uid - newChanObj = self.__Irc.Loader.Definition.MChannel(name=channel_name, uids=[userObj.uid]) - self.__Irc.Channel.insert(newChanObj) - self.send2socket(f":{service_uid} SAJOIN {userObj.nickname} {newChanObj.name}") + new_chan_obj = self._ctx.Definition.MChannel(name=channel_name, uids=[user_obj.uid]) + self._ctx.Channel.insert(new_chan_obj) + await self.send2socket(f":{service_uid} SAJOIN {user_obj.nickname} {new_chan_obj.name}") else: - self.__Irc.Channel.add_user_to_a_channel(channel_name=channel_name, uid=userObj.uid) - self.send2socket(f":{service_uid} SAJOIN {userObj.nickname} {chanObj.name}") + self._ctx.Channel.add_user_to_a_channel(channel_name=channel_name, uid=user_obj.uid) + await self.send2socket(f":{service_uid} SAJOIN {user_obj.nickname} {chan_obj.name}") return None except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def send_svspart(self, nick_to_part: str, channels: list[str], reason: str) -> None: - user_obj = self.__Irc.User.get_user(nick_to_part) + async def send_svspart(self, nick_to_part: str, channels: list[str], reason: str) -> None: + user_obj = self._ctx.User.get_user(nick_to_part) if user_obj is None: - self.__Logs.debug(f"[SVSPART] The nickname {nick_to_part} do not exist!") + self._ctx.Logs.debug(f"[SVSPART] The nickname {nick_to_part} do not exist!") return None - channels_list = ','.join([channel for channel in channels if self.__Irc.Channel.is_valid_channel(channel)]) - service_id = self.__Config.SERVICE_ID - self.send2socket(f':{service_id} SVSPART {user_obj.nickname} {channels_list} {reason}') + channels_list = ','.join([channel for channel in channels if self._ctx.Channel.is_valid_channel(channel)]) + service_id = self._ctx.Config.SERVICE_ID + await self.send2socket(f':{service_id} SVSPART {user_obj.nickname} {channels_list} {reason}') return None - def send_svsjoin(self, nick_to_part: str, channels: list[str], keys: list[str]) -> None: - user_obj = self.__Irc.User.get_user(nick_to_part) + async def send_svsjoin(self, nick_to_part: str, channels: list[str], keys: list[str]) -> None: + user_obj = self._ctx.User.get_user(nick_to_part) if user_obj is None: - self.__Logs.debug(f"[SVSJOIN] The nickname {nick_to_part} do not exist!") + self._ctx.Logs.debug(f"[SVSJOIN] The nickname {nick_to_part} do not exist!") return None - channels_list = ','.join([channel for channel in channels if self.__Irc.Channel.is_valid_channel(channel)]) + channels_list = ','.join([channel for channel in channels if self._ctx.Channel.is_valid_channel(channel)]) keys_list = ','.join([key for key in keys]) - service_id = self.__Config.SERVICE_ID - self.send2socket(f':{service_id} SVSJOIN {user_obj.nickname} {channels_list} {keys_list}') + service_id = self._ctx.Config.SERVICE_ID + await self.send2socket(f':{service_id} SVSJOIN {user_obj.nickname} {channels_list} {keys_list}') return None - def send_svsmode(self, nickname: str, user_mode: str) -> None: + async def send_svsmode(self, nickname: str, user_mode: str) -> None: try: - user_obj = self.__Irc.User.get_user(uidornickname=nickname) - service_uid = self.__Config.SERVICE_ID + user_obj = self._ctx.User.get_user(uidornickname=nickname) + service_uid = self._ctx.Config.SERVICE_ID if user_obj is None: return None - self.send2socket(f':{service_uid} SVSMODE {nickname} {user_mode}') + await self.send2socket(f':{service_uid} SVSMODE {nickname} {user_mode}') # Update new mode - self.__Irc.User.update_mode(user_obj.uid, user_mode) + self._ctx.User.update_mode(user_obj.uid, user_mode) return None except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def send_svs2mode(self, nickname: str, user_mode: str) -> None: + async def send_svs2mode(self, nickname: str, user_mode: str) -> None: try: - user_obj = self.__Irc.User.get_user(uidornickname=nickname) - service_uid = self.__Config.SERVICE_ID + user_obj = self._ctx.User.get_user(uidornickname=nickname) + service_uid = self._ctx.Config.SERVICE_ID if user_obj is None: return None - self.send2socket(f':{service_uid} SVS2MODE {nickname} {user_mode}') + await self.send2socket(f':{service_uid} SVS2MODE {nickname} {user_mode}') # Update new mode - self.__Irc.User.update_mode(user_obj.uid, user_mode) + self._ctx.User.update_mode(user_obj.uid, user_mode) return None except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def send_svslogin(self, client_uid: str, user_account: str) -> None: + async def send_svslogin(self, client_uid: str, user_account: str) -> None: """Log a client into his account. Args: @@ -476,50 +464,52 @@ class Unrealircd6(IProtocol): user_account (str): The account of the user """ try: - self.send2socket(f":{self.__Irc.Config.SERVEUR_LINK} SVSLOGIN {self.__Settings.MAIN_SERVER_HOSTNAME} {client_uid} {user_account}") + await self.send2socket(f":{self._ctx.Config.SERVEUR_LINK} SVSLOGIN {self._ctx.Settings.MAIN_SERVER_HOSTNAME} {client_uid} {user_account}") except Exception as err: - self.__Logs.error(f'General Error: {err}') + self._ctx.Logs.error(f'General Error: {err}') - def send_svslogout(self, client_obj: 'MClient') -> None: + async def send_svslogout(self, client_obj: 'MClient') -> None: """Logout a client from his account Args: - client_uid (str): The Client UID + client_obj (MClient): The Client object """ try: c_uid = client_obj.uid c_nickname = client_obj.nickname - self.send2socket(f":{self.__Irc.Config.SERVEUR_LINK} SVSLOGIN {self.__Settings.MAIN_SERVER_HOSTNAME} {c_uid} 0") + await self.send2socket(f":{self._ctx.Config.SERVEUR_LINK} SVSLOGIN {self._ctx.Settings.MAIN_SERVER_HOSTNAME} {c_uid} 0") self.send_svs2mode(c_nickname, '-r') except Exception as err: - self.__Logs.error(f'General Error: {err}') + self._ctx.Logs.error(f'General Error: {err}') - def send_quit(self, uid: str, reason: str, print_log: True) -> None: + async def send_quit(self, uid: str, reason: str, print_log: bool = True) -> None: """Send quit message - Delete uid from User object - Delete uid from Reputation object Args: - uidornickname (str): The UID or the Nickname + uid (str): The UID or the Nickname reason (str): The reason for the quit + print_log (bool): Print the log """ - user_obj = self.__Irc.User.get_user(uidornickname=uid) - reputationObj = self.__Irc.Reputation.get_reputation(uidornickname=uid) + user_obj = self._ctx.User.get_user(uidornickname=uid) + reputation_obj = self._ctx.Reputation.get_reputation(uidornickname=uid) if not user_obj is None: - self.send2socket(f":{user_obj.uid} QUIT :{reason}", print_log=print_log) - self.__Irc.User.delete(user_obj.uid) + await self.send2socket(f":{user_obj.uid} QUIT :{reason}", print_log=print_log) + self._ctx.User.delete(user_obj.uid) - if not reputationObj is None: - self.__Irc.Reputation.delete(reputationObj.uid) + if not reputation_obj is None: + self._ctx.Reputation.delete(reputation_obj.uid) - if not self.__Irc.Channel.delete_user_from_all_channel(uid): - self.__Logs.error(f"The UID [{uid}] has not been deleted from all channels") + if not self._ctx.Channel.delete_user_from_all_channel(uid): + self._ctx.Logs.error(f"The UID [{uid}] has not been deleted from all channels") return None - def send_uid(self, nickname:str, username: str, hostname: str, uid:str, umodes: str, vhost: str, remote_ip: str, realname: str, print_log: bool = True) -> None: + async def send_uid(self, nickname:str, username: str, hostname: str, uid:str, umodes: str, + vhost: str, remote_ip: str, realname: str, print_log: bool = True) -> None: """Send UID to the server - Insert User to User Object Args: @@ -534,30 +524,30 @@ class Unrealircd6(IProtocol): print_log (bool, optional): print logs if true. Defaults to True. """ # {self.Config.SERVEUR_ID} UID - # {clone.nickname} 1 {self.__Utils.get_unixtime()} {clone.username} {clone.hostname} {clone.uid} * {clone.umodes} {clone.vhost} * {self.Base.encode_ip(clone.remote_ip)} :{clone.realname} + # {clone.nickname} 1 {self._ctx.Utils.get_unixtime()} {clone.username} {clone.hostname} {clone.uid} * {clone.umodes} {clone.vhost} * {self.Base.encode_ip(clone.remote_ip)} :{clone.realname} try: - unixtime = self.__Utils.get_unixtime() - encoded_ip = self.__Base.encode_ip(remote_ip) + unixtime = self._ctx.Utils.get_unixtime() + encoded_ip = self._ctx.Base.encode_ip(remote_ip) # Create the user - self.__Irc.User.insert( - self.__Irc.Loader.Definition.MUser( + self._ctx.User.insert( + self._ctx.Definition.MUser( uid=uid, nickname=nickname, username=username, realname=realname,hostname=hostname, umodes=umodes, vhost=vhost, remote_ip=remote_ip ) ) - uid_msg = f":{self.__Config.SERVEUR_ID} UID {nickname} 1 {unixtime} {username} {hostname} {uid} * {umodes} {vhost} * {encoded_ip} :{realname}" + uid_msg = f":{self._ctx.Config.SERVEUR_ID} UID {nickname} 1 {unixtime} {username} {hostname} {uid} * {umodes} {vhost} * {encoded_ip} :{realname}" - self.send2socket(uid_msg, print_log=print_log) + await self.send2socket(uid_msg, print_log=print_log) return None except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def send_join_chan(self, uidornickname: str, channel: str, password: str = None, print_log: bool = True) -> None: + async def send_join_chan(self, uidornickname: str, channel: str, password: str = None, print_log: bool = True) -> None: """Joining a channel Args: @@ -567,38 +557,38 @@ class Unrealircd6(IProtocol): print_log (bool, optional): Write logs. Defaults to True. """ - userObj = self.__Irc.User.get_user(uidornickname) - passwordChannel = password if not password is None else '' + user_obj = self._ctx.User.get_user(uidornickname) + pwd_channel = password if not password is None else '' - if userObj is None: + if user_obj is None: return None - if not self.__Irc.Channel.is_valid_channel(channel): - self.__Logs.error(f"The channel [{channel}] is not valid") + if not self._ctx.Channel.is_valid_channel(channel): + self._ctx.Logs.error(f"The channel [{channel}] is not valid") return None - self.send2socket(f":{userObj.uid} JOIN {channel} {passwordChannel}", print_log=print_log) + await self.send2socket(f":{user_obj.uid} JOIN {channel} {pwd_channel}", print_log=print_log) - if uidornickname == self.__Config.SERVICE_NICKNAME or uidornickname == self.__Config.SERVICE_ID: - self.send2socket(f":{self.__Config.SERVICE_ID} MODE {channel} {self.__Config.SERVICE_UMODES} {self.__Config.SERVICE_ID}") + if uidornickname == self._ctx.Config.SERVICE_NICKNAME or uidornickname == self._ctx.Config.SERVICE_ID: + await self.send2socket(f":{self._ctx.Config.SERVICE_ID} MODE {channel} {self._ctx.Config.SERVICE_UMODES} {self._ctx.Config.SERVICE_ID}") # Add defender to the channel uids list - self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[userObj.uid])) + self._ctx.Channel.insert(self._ctx.Definition.MChannel(name=channel, uids=[user_obj.uid])) # Set the automode to the user - if 'r' not in userObj.umodes and 'o' not in userObj.umodes: + if 'r' not in user_obj.umodes and 'o' not in user_obj.umodes: return None - db_data: dict[str, str] = {"nickname": userObj.nickname, "channel": channel} - db_query = self.__Base.db_execute_query("SELECT id, mode FROM command_automode WHERE nickname = :nickname AND channel = :channel", db_data) + db_data: dict[str, str] = {"nickname": user_obj.nickname, "channel": channel} + db_query = await self._ctx.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.send2socket(f":{self.__Config.SERVICE_ID} MODE {channel} {mode} {userObj.nickname}") + id_cmd_automode, mode = db_result + await self.send2socket(f":{self._ctx.Config.SERVICE_ID} MODE {channel} {mode} {user_obj.nickname}") return None - def send_part_chan(self, uidornickname:str, channel: str, print_log: bool = True) -> None: + async def send_part_chan(self, uidornickname:str, channel: str, print_log: bool = True) -> None: """Part from a channel Args: @@ -607,35 +597,35 @@ class Unrealircd6(IProtocol): print_log (bool, optional): Write logs. Defaults to True. """ - userObj = self.__Irc.User.get_user(uidornickname) + u = self._ctx.User.get_user(uidornickname) - if userObj is None: - self.__Logs.error(f"The user [{uidornickname}] is not valid") + if u is None: + self._ctx.Logs.error(f"The user [{uidornickname}] is not valid") return None - if not self.__Irc.Channel.is_valid_channel(channel): - self.__Logs.error(f"The channel [{channel}] is not valid") + if not self._ctx.Channel.is_valid_channel(channel): + self._ctx.Logs.error(f"The channel [{channel}] is not valid") return None - self.send2socket(f":{userObj.uid} PART {channel}", print_log=print_log) + await self.send2socket(f":{u.uid} PART {channel}", print_log=print_log) # Add defender to the channel uids list - self.__Irc.Channel.delete_user_from_channel(channel, userObj.uid) + self._ctx.Channel.delete_user_from_channel(channel, u.uid) return None - def send_mode_chan(self, channel_name: str, channel_mode: str) -> None: + async def send_mode_chan(self, channel_name: str, channel_mode: str) -> None: - channel = self.__Irc.Channel.is_valid_channel(channel_name) + channel = self._ctx.Channel.is_valid_channel(channel_name) if not channel: - self.__Logs.error(f'The channel [{channel_name}] is not correct') + self._ctx.Logs.error(f'The channel [{channel_name}] is not correct') return None - self.send2socket(f":{self.__Config.SERVICE_NICKNAME} MODE {channel_name} {channel_mode}") + await self.send2socket(f":{self._ctx.Config.SERVICE_NICKNAME} MODE {channel_name} {channel_mode}") return None - def send_raw(self, raw_command: str) -> None: + async def send_raw(self, raw_command: str) -> None: - self.send2socket(f":{self.__Config.SERVICE_NICKNAME} {raw_command}") + await self.send2socket(f":{self._ctx.Config.SERVICE_NICKNAME} {raw_command}") return None @@ -643,233 +633,213 @@ class Unrealircd6(IProtocol): # COMMON IRC PARSER # ------------------------------------------------------------------------ - def parse_uid(self, serverMsg: list[str]) -> dict[str, str]: + def parse_uid(self, server_msg: list[str]) -> Optional['MUser']: """Parse UID and return dictionary. >>> ['@s2s-md/geoip=cc=GBtag...', ':001', 'UID', 'albatros', '0', '1721564597', 'albatros', 'hostname...', '001HB8G04', '0', '+iwxz', 'hostname-vhost', 'hostname-vhost', 'MyZBwg==', ':...'] Args: - serverMsg (list[str]): The UID ircd response + server_msg (list[str]): The UID ircd response """ - umodes = str(serverMsg[10]) - remote_ip = self.__Base.decode_ip(str(serverMsg[13])) if 'S' not in umodes else '127.0.0.1' - - # Extract Geoip information - pattern = r'^.*geoip=cc=(\S{2}).*$' - geoip_match = match(pattern, serverMsg[0]) - geoip = geoip_match.group(1) if geoip_match else None - - response = { - 'uid': str(serverMsg[8]), - 'nickname': str(serverMsg[3]), - 'username': str(serverMsg[6]), - 'hostname': str(serverMsg[7]), - 'umodes': umodes, - 'vhost': str(serverMsg[11]), - 'ip': remote_ip, - 'realname': ' '.join(serverMsg[12:]).lstrip(':'), - 'geoip': geoip, - 'reputation_score': 0, - 'iswebirc': True if 'webirc' in serverMsg[0] else False, - 'iswebsocket': True if 'websocket' in serverMsg[0] else False - } - return response - - def parse_quit(self, serverMsg: list[str]) -> dict[str, str]: - """Parse quit and return dictionary. - >>> # ['@unrealtag...', ':001JKNY0N', 'QUIT', ':Quit:', '....'] - Args: - serverMsg (list[str]): The server message to parse - - Returns: - dict[str, str]: The dictionary. - """ - scopy = serverMsg.copy() + scopy = server_msg.copy() if scopy[0].startswith('@'): scopy.pop(0) - response = { - "uid": scopy[0].replace(':', ''), - "reason": " ".join(scopy[3:]) - } - return response + uid = scopy[7] + return self._ctx.User.get_user(uid) - def parse_nick(self, serverMsg: list[str]) -> dict[str, str]: + def parse_quit(self, server_msg: list[str]) -> tuple[Optional['MUser'], str]: + """Parse quit and return dictionary. + >>> # ['@unrealtag...', ':001JKNY0N', 'QUIT', ':Quit:', '....'] + Args: + server_msg (list[str]): The server message to parse + + Returns: + tuple[MUser, str]: The User Who Quit Object and the reason. + """ + scopy = server_msg.copy() + if scopy[0].startswith('@'): + scopy.pop(0) + + user_obj = self._ctx.User.get_user(self._ctx.Utils.clean_uid(scopy[0])) + tmp_reason = scopy[3:] + tmp_reason[0] = tmp_reason[0].replace(':', '') + reason = ' '.join(tmp_reason) + + return user_obj, reason + + def parse_nick(self, server_msg: list[str]) -> tuple[Optional['MUser'], str, str]: """Parse nick changes and return dictionary. >>> ['@unrealircd.org/geoip=FR;unrealircd.org/', ':001OOU2H3', 'NICK', 'WebIrc', '1703795844'] Args: - serverMsg (list[str]): The server message to parse + server_msg (list[str]): The server message to parse Returns: - dict: The response as dictionary. + tuple(MUser, newnickname(str), timestamp(str)): Tuple of the response. - >>> {"uid": "", "newnickname": "", "timestamp": ""} + >>> MUser, newnickname, timestamp """ - scopy = serverMsg.copy() + scopy = server_msg.copy() if scopy[0].startswith('@'): scopy.pop(0) - response = { - "uid": scopy[0].replace(':', ''), - "newnickname": scopy[2], - "timestamp": scopy[3] - } - return response + user_obj = self._ctx.User.get_user(self._ctx.User.clean_uid(scopy[0])) + newnickname = scopy[2] + timestamp = scopy[3] + return user_obj, newnickname, timestamp - def parse_privmsg(self, serverMsg: list[str]) -> dict[str, str]: + def parse_privmsg(self, server_msg: list[str]) -> tuple[Optional['MUser'], Optional['MUser'], Optional['MChannel'], str]: """Parse PRIVMSG message. >>> ['@....', ':97KAAAAAE', 'PRIVMSG', '#welcome', ':This', 'is', 'my', 'public', 'message'] >>> [':97KAAAAAF', 'PRIVMSG', '98KAAAAAB', ':sasa'] Args: - serverMsg (list[str]): The server message to parse + server_msg (list[str]): The server message to parse Returns: - dict[str, str]: The response as dictionary. + tuple[MUser(Sender), MUser(Reciever), MChannel, str]: Sender user model, reciever user model, Channel model, messgae . """ - scopy = serverMsg.copy() + scopy = server_msg.copy() if scopy[0].startswith('@'): scopy.pop(0) - response = { - "uid_sender": scopy[0].replace(':', ''), - "uid_reciever": self.__Irc.User.get_uid(scopy[2]), - "channel": scopy[2] if self.__Irc.Channel.is_valid_channel(scopy[2]) else None, - "message": " ".join(scopy[3:]) - } - return response + sender = self._ctx.User.get_user(self._ctx.Utils.clean_uid(scopy[0])) + reciever = self._ctx.User.get_user(self._ctx.Utils.clean_uid(scopy[2])) + channel = self._ctx.Channel.get_channel(scopy[2]) if self._ctx.Channel.is_valid_channel(scopy[2]) else None + + tmp_message = scopy[3:] + tmp_message[0] = tmp_message[0].replace(':', '') + message = ' '.join(tmp_message) + + return sender, reciever, channel, message ##################### # HANDLE EVENTS # ##################### - def on_svs2mode(self, serverMsg: list[str]) -> None: + async def on_svs2mode(self, server_msg: list[str]) -> None: """Handle svs2mode coming from a server >>> [':00BAAAAAG', 'SVS2MODE', '001U01R03', '-r'] Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ try: # >> [':00BAAAAAG', 'SVS2MODE', '001U01R03', '-r'] + scopy = server_msg.copy() + uid_user_to_edit = scopy[2] + umode = scopy[3] - uid_user_to_edit = serverMsg[2] - umode = serverMsg[3] + u = self._ctx.User.get_user(uid_user_to_edit) - userObj = self.__Irc.User.get_user(uid_user_to_edit) - - if userObj is None: + if u is None: return None - if self.__Irc.User.update_mode(userObj.uid, umode): + if self._ctx.User.update_mode(u.uid, umode): return None return None except IndexError as ie: - self.__Logs.error(f"{__name__} - Index Error: {ie}") + self._ctx.Logs.error(f"{__name__} - Index Error: {ie}") except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_mode(self, serverMsg: list[str]) -> None: + async def on_mode(self, server_msg: list[str]) -> None: """Handle mode coming from a server Args: - serverMsg (list[str]): Original server message + server_msg (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: + async def on_umode2(self, server_msg: list[str]) -> None: """Handle umode2 coming from a server >>> [':adator_', 'UMODE2', '-i'] Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ try: # [':adator_', 'UMODE2', '-iwx'] + scopy = server_msg.copy() + u = self._ctx.User.get_user(str(scopy[0]).lstrip(':')) + user_mode = scopy[2] - userObj = self.__Irc.User.get_user(str(serverMsg[0]).lstrip(':')) - userMode = serverMsg[2] - - if userObj is None: # If user is not created + if u is None: # If user is not created return None - # save previous user modes - old_umodes = userObj.umodes - # TODO : User object should be able to update user modes - if self.__Irc.User.update_mode(userObj.uid, userMode): + if self._ctx.User.update_mode(u.uid, user_mode): return None - # self.__Logs.debug(f"Updating user mode for [{userObj.nickname}] [{old_umodes}] => [{userObj.umodes}]") return None except IndexError as ie: - self.__Logs.error(f"{__name__} - Index Error: {ie}") + self._ctx.Logs.error(f"{__name__} - Index Error: {ie}") except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_quit(self, serverMsg: list[str]) -> None: + async def on_quit(self, server_msg: list[str]) -> None: """Handle quit coming from a server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ try: # ['@unrealircd.org/userhost=...@192.168.1.10;unrealircd.org/userip=...@192.168.1.10;msgid=CssUrV08BzekYuq7BfvPHn;time=2024-11-02T15:03:33.182Z', ':001JKNY0N', 'QUIT', ':Quit:', '....'] + scopy = server_msg.copy() + uid_who_quit = str(scopy[1]).lstrip(':') - uid_who_quit = str(serverMsg[1]).lstrip(':') - - self.__Irc.Channel.delete_user_from_all_channel(uid_who_quit) - self.__Irc.User.delete(uid_who_quit) - self.__Irc.Client.delete(uid_who_quit) - self.__Irc.Reputation.delete(uid_who_quit) - self.__Irc.Admin.delete(uid_who_quit) + self._ctx.Channel.delete_user_from_all_channel(uid_who_quit) + self._ctx.User.delete(uid_who_quit) + self._ctx.Client.delete(uid_who_quit) + self._ctx.Reputation.delete(uid_who_quit) + self._ctx.Admin.delete(uid_who_quit) return None except IndexError as ie: - self.__Logs.error(f"{__name__} - Index Error: {ie}") + self._ctx.Logs.error(f"{__name__} - Index Error: {ie}") except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_squit(self, serverMsg: list[str]) -> None: + async def on_squit(self, server_msg: list[str]) -> None: """Handle squit coming from a server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ # ['@msgid=QOEolbRxdhpVW5c8qLkbAU;time=2024-09-21T17:33:16.547Z', 'SQUIT', 'defender.deb.biz.st', ':Connection', 'closed'] - - server_hostname = serverMsg[2] + scopy = server_msg.copy() + server_hostname = scopy[2] uid_to_delete = None - for s_user in self.__Irc.User.UID_DB: + for s_user in self._ctx.User.UID_DB: if s_user.hostname == server_hostname and 'S' in s_user.umodes: uid_to_delete = s_user.uid if uid_to_delete is None: return None - self.__Irc.User.delete(uid_to_delete) - self.__Irc.Channel.delete_user_from_all_channel(uid_to_delete) + self._ctx.User.delete(uid_to_delete) + self._ctx.Channel.delete_user_from_all_channel(uid_to_delete) return None - def on_protoctl(self, serverMsg: list[str]) -> None: + async def on_protoctl(self, server_msg: list[str]) -> None: """Handle protoctl coming from a server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ # ['PROTOCTL', 'CHANMODES=beI,fkL,lFH,cdimnprstzCDGKMNOPQRSTVZ', 'USERMODES=diopqrstwxzBDGHIRSTWZ', 'BOOTED=1728815798', 'PREFIX=(qaohv)~&@%+', 'SID=001', 'MLOCK', 'TS=1730662755', 'EXTSWHOIS'] - user_modes: str = None - prefix: str = None - host_server_id: str = None + user_modes: Optional[str] = None + prefix: Optional[str] = None + host_server_id: Optional[str] = None + + for msg in server_msg: - for msg in serverMsg: - pattern = None if msg.startswith('PREFIX='): pattern = r'^PREFIX=\((.*)\).*$' find_match = match(pattern, msg) @@ -881,48 +851,49 @@ class Unrealircd6(IProtocol): 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) + self._ctx.Config.HSID = host_server_id + self._ctx.Settings.PROTOCTL_USER_MODES = list(user_modes) + self._ctx.Settings.PROTOCTL_PREFIX = list(prefix) return None - def on_nick(self, serverMsg: list[str]) -> None: + async def on_nick(self, server_msg: list[str]) -> None: """Handle nick coming from a server new nickname Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ try: # ['@unrealircd.org/geoip=FR;unrealircd.org/', ':001OOU2H3', 'NICK', 'WebIrc', '1703795844'] # Changement de nickname - uid = str(serverMsg[1]).lstrip(':') - newnickname = serverMsg[3] - self.__Irc.User.update_nickname(uid, newnickname) - self.__Irc.Client.update_nickname(uid, newnickname) - self.__Irc.Admin.update_nickname(uid, newnickname) - self.__Irc.Reputation.update(uid, newnickname) + uid = str(server_msg[1]).lstrip(':') + newnickname = server_msg[3] + self._ctx.User.update_nickname(uid, newnickname) + self._ctx.Client.update_nickname(uid, newnickname) + self._ctx.Admin.update_nickname(uid, newnickname) + self._ctx.Reputation.update(uid, newnickname) return None except IndexError as ie: - self.__Logs.error(f"{__name__} - Index Error: {ie}") + self._ctx.Logs.error(f"{__name__} - Index Error: {ie}") except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_sjoin(self, serverMsg: list[str]) -> None: + async def on_sjoin(self, server_msg: list[str]) -> None: """Handle sjoin coming from a server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ try: # ['@msgid=5sTwGdj349D82L96p749SY;time=2024-08-15T09:50:23.528Z', ':001', 'SJOIN', '1721564574', '#welcome', ':001JD94QH'] @@ -932,34 +903,34 @@ class Unrealircd6(IProtocol): # '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) + scopy = server_msg.copy() + if scopy[0].startswith('@'): + scopy.pop(0) - channel = str(serverMsg_copy[3]).lower() - len_cmd = len(serverMsg_copy) + channel = str(scopy[3]).lower() + len_cmd = len(scopy) list_users:list = [] occurence = 0 start_boucle = 0 # Trouver le premier user for i in range(len_cmd): - s: list = findall(fr':', serverMsg_copy[i]) + s: list = findall(fr':', scopy[i]) if s: occurence += 1 if occurence == 2: start_boucle = i # Boucle qui va ajouter l'ensemble des users (UID) - for i in range(start_boucle, len(serverMsg_copy)): - parsed_UID = str(serverMsg_copy[i]) - clean_uid = self.__Utils.clean_uid(parsed_UID) + for i in range(start_boucle, len(scopy)): + parsed_uid = str(scopy[i]) + clean_uid = self._ctx.Utils.clean_uid(parsed_uid) if not clean_uid is None and len(clean_uid) == 9: list_users.append(clean_uid) if list_users: - self.__Irc.Channel.insert( - self.__Irc.Loader.Definition.MChannel( + self._ctx.Channel.insert( + self._ctx.Definition.MChannel( name=channel, uids=list_users ) @@ -967,185 +938,178 @@ class Unrealircd6(IProtocol): return None except IndexError as ie: - self.__Logs.error(f"{__name__} - Index Error: {ie}") + self._ctx.Logs.error(f"{__name__} - Index Error: {ie}") except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_part(self, serverMsg: list[str]) -> None: + async def on_part(self, server_msg: list[str]) -> None: """Handle part coming from a server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ try: # ['@unrealircd.org', ':001EPFBRD', 'PART', '#welcome', ':WEB', 'IRC', 'Paris'] - uid = str(serverMsg[1]).lstrip(':') - channel = str(serverMsg[3]).lower() - self.__Irc.Channel.delete_user_from_channel(channel, uid) + uid = str(server_msg[1]).lstrip(':') + channel = str(server_msg[3]).lower() + self._ctx.Channel.delete_user_from_channel(channel, uid) return None except IndexError as ie: - self.__Logs.error(f"{__name__} - Index Error: {ie}") + self._ctx.Logs.error(f"{__name__} - Index Error: {ie}") except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_eos(self, serverMsg: list[str]) -> None: + async def on_eos(self, server_msg: list[str]) -> None: """Handle EOS coming from a server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ try: # [':001', 'EOS'] - server_msg_copy = serverMsg.copy() + server_msg_copy = server_msg.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): + if hsid == self._ctx.Config.HSID: + if self._ctx.Config.DEFENDER_INIT == 1: + current_version = self._ctx.Config.CURRENT_VERSION + latest_version = self._ctx.Config.LATEST_VERSION + if self._ctx.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"# SERVEUR : {self._ctx.Config.SERVEUR_IP} ") + print(f"# PORT : {self._ctx.Config.SERVEUR_PORT} ") + print(f"# SSL : {self._ctx.Config.SERVEUR_SSL} ") + print(f"# SSL VER : {self._ctx.Config.SSL_VERSION} ") + print(f"# NICKNAME : {self._ctx.Config.SERVICE_NICKNAME} ") + print(f"# CHANNEL : {self._ctx.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"################################################") + self._ctx.Logs.info(f"################### DEFENDER ###################") + self._ctx.Logs.info(f"# SERVICE CONNECTE ") + self._ctx.Logs.info(f"# SERVEUR : {self._ctx.Config.SERVEUR_IP} ") + self._ctx.Logs.info(f"# PORT : {self._ctx.Config.SERVEUR_PORT} ") + self._ctx.Logs.info(f"# SSL : {self._ctx.Config.SERVEUR_SSL} ") + self._ctx.Logs.info(f"# SSL VER : {self._ctx.Config.SSL_VERSION} ") + self._ctx.Logs.info(f"# NICKNAME : {self._ctx.Config.SERVICE_NICKNAME} ") + self._ctx.Logs.info(f"# CHANNEL : {self._ctx.Config.SERVICE_CHANLOG} ") + self._ctx.Logs.info(f"# VERSION : {version} ") + self._ctx.Logs.info(f"################################################") - self.send_sjoin(self.__Config.SERVICE_CHANLOG) + await self.send_sjoin(self._ctx.Config.SERVICE_CHANLOG) - if self.__Base.check_for_new_version(False): - self.send_priv_msg( - nick_from=self.__Config.SERVICE_NICKNAME, + if self._ctx.Base.check_for_new_version(False): + await self.send_priv_msg( + nick_from=self._ctx.Config.SERVICE_NICKNAME, msg=f" New Version available {version}", - channel=self.__Config.SERVICE_CHANLOG + channel=self._ctx.Config.SERVICE_CHANLOG ) # Initialisation terminé aprés le premier PING - self.send_priv_msg( - nick_from=self.__Config.SERVICE_NICKNAME, - msg=tr("[ %sINFORMATION%s ] >> %s is ready!", self.__Config.COLORS.green, self.__Config.COLORS.nogc, self.__Config.SERVICE_NICKNAME), - channel=self.__Config.SERVICE_CHANLOG + await self.send_priv_msg( + nick_from=self._ctx.Config.SERVICE_NICKNAME, + msg=tr("[ %sINFORMATION%s ] >> %s is ready!", self._ctx.Config.COLORS.green, self._ctx.Config.COLORS.nogc, self._ctx.Config.SERVICE_NICKNAME), + channel=self._ctx.Config.SERVICE_CHANLOG ) - self.__Config.DEFENDER_INIT = 0 + self._ctx.Config.DEFENDER_INIT = 0 # Send EOF to other modules - for module in self.__Irc.ModuleUtils.model_get_loaded_modules().copy(): + for module in self._ctx.ModuleUtils.model_get_loaded_modules().copy(): module.class_instance.cmd(server_msg_copy) # Join saved channels & load existing modules - self.__Irc.join_saved_channels() - self.__Irc.ModuleUtils.db_load_all_existing_modules(self.__Irc) + await self._ctx.Channel.db_join_saved_channels() + await self._ctx.ModuleUtils.db_load_all_existing_modules() + + await self.send2socket(f":{self._ctx.Config.SERVEUR_ID} SMOD :L:Defender:1.0.0 :L:Command:1.0.0") return None except IndexError as ie: - self.__Logs.error(f"{__name__} - Key Error: {ie}") + self._ctx.Logs.error(f"{__name__} - Index Error: {ie}") except KeyError as ke: - self.__Logs.error(f"{__name__} - Key Error: {ke}") + self._ctx.Logs.error(f"{__name__} - Key Error: {ke}") except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}", exc_info=True) - def on_reputation(self, serverMsg: list[str]) -> None: + async def on_reputation(self, server_msg: list[str]) -> None: """Handle REPUTATION coming from a server Args: - serverMsg (list[str]): Original server message + server_msg (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 + server_msg_copy = server_msg.copy() + self._ctx.Irc.first_connexion_ip = server_msg_copy[2] + self._ctx.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.__Irc.first_score + self._ctx.Irc.first_score = int(str(server_msg_copy[3]).replace('*','')) + for user in self._ctx.User.UID_DB: + if user.remote_ip == self._ctx.Irc.first_connexion_ip: + user.score_connexion = self._ctx.Irc.first_score else: - self.__Irc.first_score = int(server_msg_copy[3]) + self._ctx.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}') + self._ctx.Logs.error(f'Index Error {__name__}: {ie}') except ValueError as ve: - self.__Irc.first_score = 0 - self.__Logs.error(f'Value Error {__name__}: {ve}') + self._ctx.Irc.first_score = 0 + self._ctx.Logs.error(f'Value Error {__name__}: {ve}', exc_info=True) except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_uid(self, serverMsg: list[str]) -> None: + async def on_uid(self, server_msg: list[str]) -> None: """Handle uid message coming from the server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ # ['@s2s-md/geoip=cc=GB|cd=United\\sKingdom|asn=16276|asname=OVH\\sSAS;s2s-md/tls_cipher=TLSv1.3-TLS_CHACHA20_POLY1305_SHA256;s2s-md/creationtime=1721564601', # ':001', 'UID', 'albatros', '0', '1721564597', 'albatros', 'vps-91b2f28b.vps.ovh.net', # '001HB8G04', '0', '+iwxz', 'Clk-A62F1D18.vps.ovh.net', 'Clk-A62F1D18.vps.ovh.net', 'MyZBwg==', ':...'] try: + scopy = server_msg.copy() + is_webirc = True if 'webirc' in scopy[0] else False + is_websocket = True if 'websocket' in scopy[0] else False - isWebirc = True if 'webirc' in serverMsg[0] else False - isWebsocket = True if 'websocket' in serverMsg[0] else False - - uid = str(serverMsg[8]) - nickname = str(serverMsg[3]) - username = str(serverMsg[6]) - hostname = str(serverMsg[7]) - umodes = str(serverMsg[10]) - vhost = str(serverMsg[11]) - - if not 'S' in umodes: - remote_ip = self.__Base.decode_ip(str(serverMsg[13])) - else: - remote_ip = '127.0.0.1' - + uid = str(scopy[8]) + nickname = str(scopy[3]) + username = str(scopy[6]) + hostname = str(scopy[7]) + umodes = str(scopy[10]) + vhost = str(scopy[11]) + remote_ip = '127.0.0.1' if 'S' in umodes else self._ctx.Base.decode_ip(str(scopy[13])) + # extract realname - realname = ' '.join(serverMsg[14:]).lstrip(':') + realname = ' '.join(scopy[14:]).lstrip(':') # Extract Geoip information pattern = r'^.*geoip=cc=(\S{2}).*$' - geoip_match = match(pattern, serverMsg[0]) + geoip_match = match(pattern, scopy[0]) + geoip = geoip_match.group(1) if geoip_match else None # Extract Fingerprint information pattern = r'^.*certfp=([^;]+).*$' - fp_match = match(pattern, serverMsg[0]) + fp_match = match(pattern, scopy[0]) fingerprint = fp_match.group(1) if fp_match else None # Extract tls_cipher information pattern = r'^.*tls_cipher=([^;]+).*$' - tlsc_match = match(pattern, serverMsg[0]) + tlsc_match = match(pattern, scopy[0]) tls_cipher = tlsc_match.group(1) if tlsc_match else None + score_connexion = self._ctx.Irc.first_score - if geoip_match: - geoip = geoip_match.group(1) - else: - geoip = None - - score_connexion = self.__Irc.first_score - - self.__Irc.User.insert( - self.__Irc.Loader.Definition.MUser( + self._ctx.User.insert( + self._ctx.Definition.MUser( uid=uid, nickname=nickname, username=username, @@ -1155,8 +1119,8 @@ class Unrealircd6(IProtocol): vhost=vhost, fingerprint=fingerprint, tls_cipher=tls_cipher, - isWebirc=isWebirc, - isWebsocket=isWebsocket, + isWebirc=is_webirc, + isWebsocket=is_websocket, remote_ip=remote_ip, geoip=geoip, score_connexion=score_connexion, @@ -1165,93 +1129,91 @@ class Unrealircd6(IProtocol): ) # Auto Auth admin via fingerprint - dnickname = self.__Config.SERVICE_NICKNAME - dchanlog = self.__Config.SERVICE_CHANLOG - GREEN = self.__Config.COLORS.green - RED = self.__Config.COLORS.red - NOGC = self.__Config.COLORS.nogc + dnickname = self._ctx.Config.SERVICE_NICKNAME + dchanlog = self._ctx.Config.SERVICE_CHANLOG + green = self._ctx.Config.COLORS.green + red = self._ctx.Config.COLORS.red + nogc = self._ctx.Config.COLORS.nogc - for module in self.__Irc.ModuleUtils.model_get_loaded_modules().copy(): - module.class_instance.cmd(serverMsg) + # for module in self._ctx.ModuleUtils.model_get_loaded_modules().copy(): + # module.class_instance.cmd(serverMsg) # SASL authentication # ['@s2s-md/..', ':001', 'UID', 'adator__', '0', '1755987444', '...', 'desktop-h1qck20.mshome.net', '001XLTT0U', '0', '+iwxz', '*', 'Clk-EC2256B2.mshome.net', 'rBKAAQ==', ':...'] - uid = serverMsg[8] - nickname = serverMsg[3] - sasl_obj = self.__Irc.Sasl.get_sasl_obj(uid) + sasl_obj = self._ctx.Sasl.get_sasl_obj(uid) if sasl_obj: if sasl_obj.auth_success: - self.__Irc.insert_db_admin(sasl_obj.client_uid, sasl_obj.username, sasl_obj.level, sasl_obj.language) - self.send_priv_msg(nick_from=dnickname, - msg=tr("[ %sSASL AUTH%s ] - %s (%s) is now connected successfuly to %s", GREEN, NOGC, nickname, sasl_obj.username, dnickname), + self._ctx.Irc.insert_db_admin(sasl_obj.client_uid, sasl_obj.username, sasl_obj.level, sasl_obj.language) + await self.send_priv_msg(nick_from=dnickname, + msg=tr("[ %sSASL AUTH%s ] - %s (%s) is now connected successfuly to %s", green, nogc, nickname, sasl_obj.username, dnickname), channel=dchanlog) - self.send_notice(nick_from=dnickname, nick_to=nickname, msg=tr("Successfuly connected to %s", dnickname)) + await self.send_notice(nick_from=dnickname, nick_to=nickname, msg=tr("Successfuly connected to %s", dnickname)) else: - self.send_priv_msg(nick_from=dnickname, - msg=tr("[ %sSASL AUTH%s ] - %s provided a wrong password for this username %s", RED, NOGC, nickname, sasl_obj.username), + await self.send_priv_msg(nick_from=dnickname, + msg=tr("[ %sSASL AUTH%s ] - %s provided a wrong password for this username %s", red, nogc, nickname, sasl_obj.username), channel=dchanlog) - self.send_notice(nick_from=dnickname, nick_to=nickname, msg=tr("Wrong password!")) + await self.send_notice(nick_from=dnickname, nick_to=nickname, msg=tr("Wrong password!")) # Delete sasl object! - self.__Irc.Sasl.delete_sasl_client(uid) + self._ctx.Sasl.delete_sasl_client(uid) return None # If no sasl authentication then auto connect via fingerprint - if self.__Irc.Admin.db_auth_admin_via_fingerprint(fingerprint, uid): - admin = self.__Irc.Admin.get_admin(uid) + if await self._ctx.Admin.db_auth_admin_via_fingerprint(fingerprint, uid): + admin = self._ctx.Admin.get_admin(uid) account = admin.account if admin else '' - self.send_priv_msg(nick_from=dnickname, - msg=tr("[ %sFINGERPRINT AUTH%s ] - %s (%s) is now connected successfuly to %s", GREEN, NOGC, nickname, account, dnickname), + await self.send_priv_msg(nick_from=dnickname, + msg=tr("[ %sFINGERPRINT AUTH%s ] - %s (%s) is now connected successfuly to %s", green, nogc, nickname, account, dnickname), channel=dchanlog) - self.send_notice(nick_from=dnickname, nick_to=nickname, msg=tr("Successfuly connected to %s", dnickname)) + await self.send_notice(nick_from=dnickname, nick_to=nickname, msg=tr("Successfuly connected to %s", dnickname)) return None except IndexError as ie: - self.__Logs.error(f"{__name__} - Index Error: {ie}") + self._ctx.Logs.error(f"{__name__} - Index Error: {ie}") except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_privmsg(self, serverMsg: list[str]) -> None: + async def on_privmsg(self, server_msg: list[str]) -> None: """Handle PRIVMSG message coming from the server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ + srv_msg = server_msg.copy() + cmd = server_msg.copy() try: - srv_msg = serverMsg.copy() - cmd = serverMsg.copy() + # Supprimer la premiere valeur si MTAGS activé if cmd[0].startswith('@'): cmd.pop(0) 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})(.*)$' + user_trigger = self._ctx.User.get_nickname(get_uid_or_nickname) + pattern = fr'(:\{self._ctx.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 self.__Irc.Commands.is_command_exist(arg[0]): - self.__Logs.debug(f"This command {arg[0]} is not available") - self.send_notice( - nick_from=self.__Config.SERVICE_NICKNAME, + arg.remove(f':{self._ctx.Config.SERVICE_PREFIX}') + if not self._ctx.Commands.is_command_exist(arg[0]): + self._ctx.Logs.debug(f"This command {arg[0]} is not available") + await self.send_notice( + nick_from=self._ctx.Config.SERVICE_NICKNAME, nick_to=user_trigger, - msg=f"This command [{self.__Config.COLORS.bold}{arg[0]}{self.__Config.COLORS.bold}] is not available" + msg=f"This command [{self._ctx.Config.COLORS.bold}{arg[0]}{self._ctx.Config.COLORS.bold}] is not available" ) return None cmd_to_send = convert_to_string.replace(':','') - self.__Base.log_cmd(user_trigger, cmd_to_send) + await self._ctx.Base.log_cmd(user_trigger, cmd_to_send) - fromchannel = str(cmd[2]).lower() if self.__Irc.Channel.is_valid_channel(cmd[2]) else None - self.__Irc.hcmds(user_trigger, fromchannel, arg, cmd) + fromchannel = str(cmd[2]).lower() if self._ctx.Channel.is_valid_channel(cmd[2]) else None + await self._ctx.Irc.hcmds(user_trigger, fromchannel, arg, cmd) - if cmd[2] == self.__Config.SERVICE_ID: + if cmd[2] == self._ctx.Config.SERVICE_ID: pattern = fr'^:.*?:(.*)$' hcmds = search(pattern, ' '.join(cmd)) @@ -1262,212 +1224,207 @@ class Unrealircd6(IProtocol): # Réponse a un CTCP VERSION if arg[0] == '\x01VERSION\x01': - self.on_version(srv_msg) + await self.on_version(srv_msg) return None # Réponse a un TIME if arg[0] == '\x01TIME\x01': - self.on_time(srv_msg) + await self.on_time(srv_msg) return None # Réponse a un PING if arg[0] == '\x01PING': - self.on_ping(srv_msg) + await self.on_ping(srv_msg) return None - if not self.__Irc.Commands.is_command_exist(arg[0]): - self.__Logs.debug(f"This command {arg[0]} sent by {user_trigger} is not available") + if not self._ctx.Commands.is_command_exist(arg[0]): + self._ctx.Logs.debug(f"This command {arg[0]} sent by {user_trigger} is not available") return None - # if not arg[0].lower() in self.__Irc.module_commands_list: - # 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) - + await self._ctx.Base.log_cmd(user_trigger, cmd_to_send) fromchannel = None - if len(arg) >= 2: - fromchannel = str(arg[1]).lower() if self.__Irc.Channel.is_valid_channel(arg[1]) else None - self.__Irc.hcmds(user_trigger, fromchannel, arg, cmd) + if len(arg) >= 2: + fromchannel = str(arg[1]).lower() if self._ctx.Channel.is_valid_channel(arg[1]) else None + + await self._ctx.Irc.hcmds(user_trigger, fromchannel, arg, cmd) return None except KeyError as ke: - self.__Logs.error(f"Key Error: {ke}") + self._ctx.Logs.error(f"Key Error: {ke}") except AttributeError as ae: - self.__Logs.error(f"Attribute Error: {ae}") + self._ctx.Logs.error(f"Attribute Error: {ae}", exc_info=True) except Exception as err: - self.__Logs.error(f"General Error: {err} - {srv_msg}" , exc_info=True) + self._ctx.Logs.error(f"General Error: {err} - {srv_msg}" , exc_info=True) - def on_server_ping(self, serverMsg: list[str]) -> None: + async def on_server_ping(self, server_msg: list[str]) -> None: """Send a PONG message to the server Args: - serverMsg (list[str]): List of str coming from the server + server_msg (list[str]): List of str coming from the server """ try: - pong = str(serverMsg[1]).replace(':','') - self.send2socket(f"PONG :{pong}", print_log=False) + scopy = server_msg.copy() + await self.send2socket(' '.join(scopy).replace('PING', 'PONG'), print_log=False) return None except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_server(self, serverMsg: list[str]) -> None: + async def on_server(self, server_msg: list[str]) -> None: """_summary_ Args: - serverMsg (list[str]): _description_ + server_msg (list[str]): _description_ """ try: # ['SERVER', 'irc.local.org', '1', ':U6100-Fhn6OoE-001', 'Local', 'Server'] - sCopy = serverMsg.copy() - self.__Irc.Settings.MAIN_SERVER_HOSTNAME = sCopy[1] + scopy = server_msg.copy() + self._ctx.Settings.MAIN_SERVER_HOSTNAME = scopy[1] except Exception as err: - self.__Logs.error(f'General Error: {err}') + self._ctx.Logs.error(f'General Error: {err}') - def on_version(self, serverMsg: list[str]) -> None: + async def on_version(self, server_msg: list[str]) -> None: """Sending Server Version to the server Args: - serverMsg (list[str]): List of str coming from the server + server_msg (list[str]): List of str coming from the server """ # ['@unrealircd.org/userhost=StatServ@stats.deb.biz.st;draft/bot;bot;msgid=ehfAq3m2yjMjhgWEfi1UCS;time=2024-10-26T13:49:06.299Z', ':00BAAAAAI', 'PRIVMSG', '12ZAAAAAB', ':\x01VERSION\x01'] # Réponse a un CTCP VERSION try: - - nickname = self.__Irc.User.get_nickname(self.__Utils.clean_uid(serverMsg[1])) - dnickname = self.__Config.SERVICE_NICKNAME - arg = serverMsg[4].replace(':', '') + scopy = server_msg.copy() + nickname = self._ctx.User.get_nickname(self._ctx.Utils.clean_uid(scopy[1])) + dnickname = self._ctx.Config.SERVICE_NICKNAME + arg = scopy[4].replace(':', '') if nickname is None: return None if arg == '\x01VERSION\x01': - self.send2socket(f':{dnickname} NOTICE {nickname} :\x01VERSION Service {self.__Config.SERVICE_NICKNAME} V{self.__Config.CURRENT_VERSION}\x01') + await self.send2socket(f':{dnickname} NOTICE {nickname} :\x01VERSION Service {self._ctx.Config.SERVICE_NICKNAME} V{self._ctx.Config.CURRENT_VERSION}\x01') return None except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_time(self, serverMsg: list[str]) -> None: + async def on_time(self, server_msg: list[str]) -> None: """Sending TIME answer to a requestor Args: - serverMsg (list[str]): List of str coming from the server + server_msg (list[str]): List of str coming from the server """ # ['@unrealircd.org/userhost=StatServ@stats.deb.biz.st;draft/bot;bot;msgid=ehfAq3m2yjMjhgWEfi1UCS;time=2024-10-26T13:49:06.299Z', ':00BAAAAAI', 'PRIVMSG', '12ZAAAAAB', ':\x01TIME\x01'] # Réponse a un CTCP VERSION try: - - nickname = self.__Irc.User.get_nickname(self.__Utils.clean_uid(serverMsg[1])) - dnickname = self.__Config.SERVICE_NICKNAME - arg = serverMsg[4].replace(':', '') - current_datetime = self.__Utils.get_sdatetime() + scopy = server_msg.copy() + nickname = self._ctx.User.get_nickname(self._ctx.Utils.clean_uid(scopy[1])) + dnickname = self._ctx.Config.SERVICE_NICKNAME + arg = scopy[4].replace(':', '') + current_datetime = self._ctx.Utils.get_sdatetime() if nickname is None: return None if arg == '\x01TIME\x01': - self.send2socket(f':{dnickname} NOTICE {nickname} :\x01TIME {current_datetime}\x01') + await self.send2socket(f':{dnickname} NOTICE {nickname} :\x01TIME {current_datetime}\x01') return None except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_ping(self, serverMsg: list[str]) -> None: + async def on_ping(self, server_msg: list[str]) -> None: """Sending a PING answer to requestor Args: - serverMsg (list[str]): List of str coming from the server + server_msg (list[str]): List of str coming from the server """ # ['@unrealircd.org/...', ':001INC60B', 'PRIVMSG', '12ZAAAAAB', ':\x01PING', '762382207\x01'] # Réponse a un CTCP VERSION try: - - nickname = self.__Irc.User.get_nickname(self.__Utils.clean_uid(serverMsg[1])) - dnickname = self.__Config.SERVICE_NICKNAME - arg = serverMsg[4].replace(':', '') + scopy = server_msg.copy() + nickname = self._ctx.User.get_nickname(self._ctx.Utils.clean_uid(scopy[1])) + dnickname = self._ctx.Config.SERVICE_NICKNAME + arg = scopy[4].replace(':', '') if nickname is None: - self.__Logs.debug(serverMsg) + self._ctx.Logs.debug(scopy) return None if arg == '\x01PING': - recieved_unixtime = int(serverMsg[5].replace('\x01','')) - current_unixtime = self.__Utils.get_unixtime() + recieved_unixtime = int(scopy[5].replace('\x01','')) + current_unixtime = self._ctx.Utils.get_unixtime() ping_response = current_unixtime - recieved_unixtime - # self.__Irc.send2socket(f':{dnickname} NOTICE {nickname} :\x01PING {ping_response} secs\x01') - self.send_notice( + # self._ctx.Irc.send2socket(f':{dnickname} NOTICE {nickname} :\x01PING {ping_response} secs\x01') + await self.send_notice( nick_from=dnickname, nick_to=nickname, msg=f"\x01PING {ping_response} secs\x01" ) - self.__Logs.debug(serverMsg) + self._ctx.Logs.debug(scopy) return None except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_version_msg(self, serverMsg: list[str]) -> None: + async def on_version_msg(self, server_msg: list[str]) -> None: """Handle version coming from the server \n ex. /version Defender Args: - serverMsg (list[str]): Original message from the server + server_msg (list[str]): Original message from the server """ try: # ['@label=0073', ':0014E7P06', 'VERSION', 'PyDefender'] - serverMsg_copy = serverMsg.copy() - if '@' in list(serverMsg_copy[0])[0]: - serverMsg_copy.pop(0) + scopy = server_msg.copy() + if '@' in list(scopy[0])[0]: + scopy.pop(0) - getUser = self.__Irc.User.get_user(self.__Utils.clean_uid(serverMsg_copy[0])) + u = self._ctx.User.get_user(self._ctx.Utils.clean_uid(scopy[0])) - if getUser is None: + if u is None: return None - response_351 = f"{self.__Config.SERVICE_NAME.capitalize()}-{self.__Config.CURRENT_VERSION} {self.__Config.SERVICE_HOST} {self.name}" - self.send2socket(f':{self.__Config.SERVICE_HOST} 351 {getUser.nickname} {response_351}') + response_351 = f"{self._ctx.Config.SERVICE_NAME.capitalize()}-{self._ctx.Config.CURRENT_VERSION} {self._ctx.Config.SERVICE_HOST} {self.name}" + await self.send2socket(f':{self._ctx.Config.SERVICE_HOST} 351 {u.nickname} {response_351}') - modules = self.__Irc.ModuleUtils.get_all_available_modules() + modules = self._ctx.ModuleUtils.get_all_available_modules() response_005 = ' | '.join(modules) - self.send2socket(f':{self.__Config.SERVICE_HOST} 005 {getUser.nickname} {response_005} are supported by this server') + await self.send2socket(f':{self._ctx.Config.SERVICE_HOST} 005 {u.nickname} {response_005} are supported by this server') - 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") + response_005 = ''.join(self._ctx.Settings.PROTOCTL_USER_MODES) + await self.send2socket(f":{self._ctx.Config.SERVICE_HOST} 005 {u.nickname} {response_005} are supported by this server") return None except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self._ctx.Logs.error(f"{__name__} - General Error: {err}") - def on_smod(self, serverMsg: list[str]) -> None: + async def on_smod(self, server_msg: list[str]) -> None: """Handle SMOD message coming from the server Args: - serverMsg (list[str]): Original server message + server_msg (list[str]): Original server message """ try: # [':001', 'SMOD', ':L:history_backend_mem:2.0', 'L:channeldb:1.0', 'L:tkldb:1.10', 'L:staff:3.8', 'L:ircops:3.71', ...] - sCopy = serverMsg.copy() - modules = [m.lstrip(':') for m in sCopy[2:]] + scopy = server_msg.copy() + modules = [m.lstrip(':') for m in scopy[2:]] for smod in modules: smod_split = smod.split(':') - sModObj = self.__Irc.Loader.Definition.MSModule(type=smod_split[0], name=smod_split[1], version=smod_split[2]) - self.__Settings.SMOD_MODULES.append(sModObj) + smodobj = self._ctx.Definition.MSModule(type=smod_split[0], name=smod_split[1], version=smod_split[2]) + self._ctx.Settings.SMOD_MODULES.append(smodobj) except Exception as err: - self.__Logs.error(f'General Error: {err}') + self._ctx.Logs.error(f'General Error: {err}') - def on_sasl(self, serverMsg: list[str]) -> Optional['MSasl']: + async def on_sasl(self, server_msg: list[str]) -> Optional['MSasl']: """Handle SASL coming from a server Args: - serverMsg (list[str]): Original server message - psasl (Sasl): The SASL process object + server_msg (list[str]): Original server message """ try: # [':irc.local.org', 'SASL', 'defender-dev.deb.biz.st', '00157Z26U', 'H', '172.18.128.1', '172.18.128.1'] @@ -1475,9 +1432,10 @@ class Unrealircd6(IProtocol): # [':irc.local.org', 'SASL', 'defender-dev.deb.biz.st', '0014ZZH1F', 'S', 'EXTERNAL', 'zzzzzzzkey'] # [':irc.local.org', 'SASL', 'defender-dev.deb.biz.st', '00157Z26U', 'C', 'sasakey=='] # [':irc.local.org', 'SASL', 'defender-dev.deb.biz.st', '00157Z26U', 'D', 'A'] - psasl = self.__Irc.Sasl + scopy = server_msg.copy() + psasl = self._ctx.Sasl sasl_enabled = False - for smod in self.__Settings.SMOD_MODULES: + for smod in self._ctx.Settings.SMOD_MODULES: if smod.name == 'sasl': sasl_enabled = True break @@ -1485,11 +1443,9 @@ class Unrealircd6(IProtocol): if not sasl_enabled: return None - sCopy = serverMsg.copy() - client_uid = sCopy[3] if len(sCopy) >= 6 else None - sasl_obj = None - sasl_message_type = sCopy[4] if len(sCopy) >= 6 else None - psasl.insert_sasl_client(self.__Irc.Loader.Definition.MSasl(client_uid=client_uid)) + client_uid = scopy[3] if len(scopy) >= 6 else None + sasl_message_type = scopy[4] if len(scopy) >= 6 else None + psasl.insert_sasl_client(self._ctx.Definition.MSasl(client_uid=client_uid)) sasl_obj = psasl.get_sasl_obj(client_uid) if sasl_obj is None: @@ -1497,30 +1453,30 @@ class Unrealircd6(IProtocol): match sasl_message_type: case 'H': - sasl_obj.remote_ip = str(sCopy[5]) + sasl_obj.remote_ip = str(scopy[5]) sasl_obj.message_type = sasl_message_type return sasl_obj case 'S': sasl_obj.message_type = sasl_message_type - if str(sCopy[5]) in ['PLAIN', 'EXTERNAL']: - sasl_obj.mechanisme = str(sCopy[5]) + if str(scopy[5]) in ['PLAIN', 'EXTERNAL']: + sasl_obj.mechanisme = str(scopy[5]) if sasl_obj.mechanisme == "PLAIN": - self.send2socket(f":{self.__Config.SERVEUR_LINK} SASL {self.__Settings.MAIN_SERVER_HOSTNAME} {sasl_obj.client_uid} C +") + await self.send2socket(f":{self._ctx.Config.SERVEUR_LINK} SASL {self._ctx.Settings.MAIN_SERVER_HOSTNAME} {sasl_obj.client_uid} C +") elif sasl_obj.mechanisme == "EXTERNAL": - if str(sCopy[5]) == "+": + if str(scopy[5]) == "+": return None - sasl_obj.fingerprint = str(sCopy[6]) - self.send2socket(f":{self.__Config.SERVEUR_LINK} SASL {self.__Settings.MAIN_SERVER_HOSTNAME} {sasl_obj.client_uid} C +") + sasl_obj.fingerprint = str(scopy[6]) + await self.send2socket(f":{self._ctx.Config.SERVEUR_LINK} SASL {self._ctx.Settings.MAIN_SERVER_HOSTNAME} {sasl_obj.client_uid} C +") self.on_sasl_authentication_process(sasl_obj) return sasl_obj case 'C': if sasl_obj.mechanisme == "PLAIN": - credentials = sCopy[5] + credentials = scopy[5] decoded_credentials = b64decode(credentials).decode() user, username, password = decoded_credentials.split('\0') @@ -1528,29 +1484,29 @@ class Unrealircd6(IProtocol): sasl_obj.username = username sasl_obj.password = password - self.on_sasl_authentication_process(sasl_obj) + await self.on_sasl_authentication_process(sasl_obj) return sasl_obj elif sasl_obj.mechanisme == "EXTERNAL": sasl_obj.message_type = sasl_message_type - self.on_sasl_authentication_process(sasl_obj) + await self.on_sasl_authentication_process(sasl_obj) return sasl_obj except Exception as err: - self.__Logs.error(f'General Error: {err}', exc_info=True) + self._ctx.Logs.error(f'General Error: {err}', exc_info=True) - def on_sasl_authentication_process(self, sasl_model: 'MSasl') -> bool: + async def on_sasl_authentication_process(self, sasl_model: 'MSasl') -> None: s = sasl_model if sasl_model: - def db_get_admin_info(*, username: Optional[str] = None, password: Optional[str] = None, fingerprint: Optional[str] = None) -> Optional[dict[str, Any]]: + async def db_get_admin_info(*, username: Optional[str] = None, password: Optional[str] = None, fingerprint: Optional[str] = None) -> Optional[dict[str, Any]]: if fingerprint: mes_donnees = {'fingerprint': fingerprint} - query = f"SELECT user, level, language FROM {self.__Config.TABLE_ADMIN} WHERE fingerprint = :fingerprint" + query = f"SELECT user, level, language FROM {self._ctx.Config.TABLE_ADMIN} WHERE fingerprint = :fingerprint" else: - mes_donnees = {'user': username, 'password': self.__Utils.hash_password(password)} - query = f"SELECT user, level, language FROM {self.__Config.TABLE_ADMIN} WHERE user = :user AND password = :password" + mes_donnees = {'user': username, 'password': self._ctx.Utils.hash_password(password)} + query = f"SELECT user, level, language FROM {self._ctx.Config.TABLE_ADMIN} WHERE user = :user AND password = :password" - result = self.__Base.db_execute_query(query, mes_donnees) + result = await self._ctx.Base.db_execute_query(query, mes_donnees) user_from_db = result.fetchone() if user_from_db: return {'user': user_from_db[0], 'level': user_from_db[1], 'language': user_from_db[2]} @@ -1559,48 +1515,48 @@ class Unrealircd6(IProtocol): if s.message_type == 'C' and s.mechanisme == 'PLAIN': # Connection via PLAIN - admin_info = db_get_admin_info(username=s.username, password=s.password) + admin_info = await db_get_admin_info(username=s.username, password=s.password) if admin_info is not None: s.auth_success = True s.level = admin_info.get('level', 0) s.language = admin_info.get('language', 'EN') - self.send2socket(f":{self.__Config.SERVEUR_LINK} SASL {self.__Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D S") - self.send2socket(f":{self.__Config.SERVEUR_LINK} 903 {s.username} :SASL authentication successful") + await self.send2socket(f":{self._ctx.Config.SERVEUR_LINK} SASL {self._ctx.Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D S") + await self.send2socket(f":{self._ctx.Config.SERVEUR_LINK} 903 {s.username} :SASL authentication successful") else: - self.send2socket(f":{self.__Config.SERVEUR_LINK} SASL {self.__Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D F") - self.send2socket(f":{self.__Config.SERVEUR_LINK} 904 {s.username} :SASL authentication failed") + await self.send2socket(f":{self._ctx.Config.SERVEUR_LINK} SASL {self._ctx.Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D F") + await self.send2socket(f":{self._ctx.Config.SERVEUR_LINK} 904 {s.username} :SASL authentication failed") elif s.message_type == 'S' and s.mechanisme == 'EXTERNAL': # Connection using fingerprints - admin_info = db_get_admin_info(fingerprint=s.fingerprint) + admin_info = await db_get_admin_info(fingerprint=s.fingerprint) if admin_info is not None: s.auth_success = True s.level = admin_info.get('level', 0) s.username = admin_info.get('user', None) s.language = admin_info.get('language', 'EN') - self.send2socket(f":{self.__Config.SERVEUR_LINK} SASL {self.__Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D S") - self.send2socket(f":{self.__Config.SERVEUR_LINK} 903 {s.username} :SASL authentication successful") + await self.send2socket(f":{self._ctx.Config.SERVEUR_LINK} SASL {self._ctx.Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D S") + await self.send2socket(f":{self._ctx.Config.SERVEUR_LINK} 903 {s.username} :SASL authentication successful") else: # "904 :SASL authentication failed" - self.send2socket(f":{self.__Config.SERVEUR_LINK} SASL {self.__Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D F") - self.send2socket(f":{self.__Config.SERVEUR_LINK} 904 {s.username} :SASL authentication failed") + await self.send2socket(f":{self._ctx.Config.SERVEUR_LINK} SASL {self._ctx.Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D F") + await self.send2socket(f":{self._ctx.Config.SERVEUR_LINK} 904 {s.username} :SASL authentication failed") - def on_md(self, serverMsg: list[str]) -> None: + async def on_md(self, server_msg: list[str]) -> None: """Handle MD responses [':001', 'MD', 'client', '001MYIZ03', 'certfp', ':d1235648...'] Args: - serverMsg (list[str]): The server reply + server_msg (list[str]): The server reply """ try: - scopy = serverMsg.copy() - available_vars = ['creationtime', 'certfp', 'tls_cipher'] + scopy = server_msg.copy() + # available_vars = ['creationtime', 'certfp', 'tls_cipher'] uid = str(scopy[3]) var = str(scopy[4]).lower() value = str(scopy[5]).replace(':', '') - user_obj = self.__Irc.User.get_user(uid) + user_obj = self._ctx.User.get_user(uid) if user_obj is None: return None @@ -1612,21 +1568,33 @@ class Unrealircd6(IProtocol): case _: return None - ... except Exception as e: - self.__Logs.error(f"General Error: {e}") + self._ctx.Logs.error(f"General Error: {e}") - def on_kick(self, serverMsg: list[str]) -> None: + async def on_kick(self, server_msg: list[str]) -> None: """When a user is kicked out from a channel ['@unrealircd.org/issued-by=RPC:admin-for-test@...', ':001', 'KICK', '#jsonrpc', '001ELW13T', ':Kicked', 'from', 'JSONRPC', 'User'] Args: - serverMsg (list[str]): The server message + server_msg (list[str]): The server message """ - scopy = serverMsg.copy() + scopy = server_msg.copy() uid = scopy[4] channel = scopy[3] # Delete the user from the channel. - self.__Irc.Channel.delete_user_from_channel(channel, uid) - return None \ No newline at end of file + self._ctx.Channel.delete_user_from_channel(channel, uid) + return None + + async def on_sethost(self, server_msg: list[str]) -> None: + """On SETHOST command + >>> [':001DN7305', 'SETHOST', ':netadmin.example.org'] + + Args: + server_msg (list[str]): _description_ + """ + scopy = server_msg.copy() + uid = self._ctx.User.clean_uid(scopy[0]) + vhost = scopy[2].lstrip(':') + user = self._ctx.User.get_user(uid) + user.vhost = vhost diff --git a/core/definition.py b/core/definition.py index d8a6e05..6d9ef95 100644 --- a/core/definition.py +++ b/core/definition.py @@ -1,8 +1,9 @@ from datetime import datetime from json import dumps -from dataclasses import dataclass, field, asdict, fields +from dataclasses import dataclass, field, asdict, fields, replace from typing import Literal, Any, Optional from os import sep +from core.classes.interfaces.imodule import IModule @dataclass class MainModel: @@ -14,6 +15,10 @@ class MainModel: def to_json(self) -> str: """Return the object of a dataclass a json str.""" return dumps(self.to_dict()) + + def copy(self): + """Return the object of a dataclass a json str.""" + return replace(self) def get_attributes(self) -> list[str]: """Return a list of attributes name""" @@ -205,6 +210,9 @@ class MConfig(MainModel): PASSWORD: str = "password" """The password of the admin of the service""" + RPC_USERS: list[dict] = field(default_factory=list) + """The Defender rpc users""" + JSONRPC_URL: str = None """The RPC url, if local https://127.0.0.1:PORT/api should be fine""" @@ -347,7 +355,15 @@ class MCommand(MainModel): class MModule(MainModel): module_name: str = None class_name: str = None - class_instance: Optional[Any] = None + class_instance: Optional[IModule] = None + +@dataclass +class DefenderModuleHeader(MainModel): + name: str = '' + version: str = '' + description: str = '' + author: str = '' + core_version: str = '' @dataclass class MSModule: diff --git a/core/install.py b/core/install.py new file mode 100644 index 0000000..8ca3b78 --- /dev/null +++ b/core/install.py @@ -0,0 +1,150 @@ +import argparse +import os +import sys +import json +from dataclasses import dataclass +from subprocess import check_call, CalledProcessError, check_output +from pathlib import Path +from platform import python_version_tuple +import traceback + +parser = argparse.ArgumentParser(description="Python Installation Code") +parser.add_argument('--check-version', action='store_true', help='Check if the python version is ok!') +parser.add_argument('--install', action='store_true', help='Run the installation') +parser.add_argument('--git-update', action='store_true', help='Update from git (main repository)') +args = parser.parse_args() + +PYTHON_REQUIRED_VERSION = (3, 10, 0) +PYTHON_SYSTEM_VERSION = tuple(map(int, python_version_tuple())) +ROOT_PATH = os.getcwd() +PYENV = Path(ROOT_PATH).joinpath('.pyenv/bin/python') if os.name != 'nt' else Path(ROOT_PATH).joinpath('.pyenv/Scripts/python.exe') +PIPENV = Path(f'{ROOT_PATH}/.pyenv/bin/pip') if os.name != 'nt' else Path(f'{ROOT_PATH}/.pyenv/Scripts/pip.exe') +USER_HOME_DIRECTORY = Path.home() +SYSTEMD_PATH = Path(USER_HOME_DIRECTORY).joinpath('.config', 'systemd', 'user') +PY_EXEC = 'defender.py' +SERVICE_FILE_NAME = 'defender.service' + +@dataclass +class Package: + name: str = None + version: str = None + +def __load_required_package_versions() -> list[Package]: + """This will create Package model with package names and required version + """ + try: + DB_PACKAGES: list[Package] = [] + version_filename = Path(ROOT_PATH).joinpath('version.json') # f'.{os.sep}version.json' + with open(version_filename, 'r') as version_data: + package_info:dict[str, str] = json.load(version_data) + + for name, version in package_info.items(): + if name == 'version': + continue + DB_PACKAGES.append( + Package(name=name, version=version) + ) + + return DB_PACKAGES + + except FileNotFoundError as fe: + print(f"File not found: {fe}") + except Exception as err: + print(f"General Error: {err}") + +def update_packages() -> None: + try: + newVersion = False + db_packages = __load_required_package_versions() + print(ROOT_PATH) + if sys.prefix not in PYENV.__str__(): + print(f"You are probably running a new installation or you are not using your virtual env {PYENV}") + return newVersion + + print(f"> Checking for dependencies versions ==> WAIT") + for package in db_packages: + newVersion = False + _required_version = package.version + _installed_version: str = None + output = check_output([PIPENV, 'show', package.name]) + for line in output.decode().splitlines(): + if line.startswith('Version:'): + _installed_version = line.split(':')[1].strip() + break + + required_version = tuple(map(int, _required_version.split('.'))) + installed_version = tuple(map(int, _installed_version.split('.'))) + + if required_version > installed_version: + print(f'> New version of {package.name} is available {installed_version} ==> {required_version}') + newVersion = True + + if newVersion: + check_call([PIPENV, 'install', '--upgrade', package.name]) + + print(f"> Dependencies versions ==> OK") + return newVersion + + except CalledProcessError: + print(f"[!] Package {package.name} not installed [!]") + except Exception as err: + print(f"UpdatePackage Error: {err}") + traceback.print_exc() + +def run_git_update() -> None: + check_call(['git', 'pull', 'origin', 'main']) + +def check_python_requirement(): + if PYTHON_SYSTEM_VERSION < PYTHON_REQUIRED_VERSION: + raise RuntimeError(f"Your Python Version is not meeting the requirement, System Version: {PYTHON_SYSTEM_VERSION} < Required Version {PYTHON_REQUIRED_VERSION}") + +def create_service_file(): + + pyenv = PYENV + systemd_path = SYSTEMD_PATH + py_exec = PY_EXEC + service_file_name = SERVICE_FILE_NAME + + if not Path(systemd_path).exists(): + print("[!] Folder not available") + sys.exit(1) + + contain = f'''[Unit] +Description=Defender IRC Service + +[Service] +ExecStart={pyenv} {py_exec} +WorkingDirectory={ROOT_PATH} +SyslogIdentifier=Defender +Restart=on-failure + +[Install] +WantedBy=default.target +''' + with open(Path(systemd_path).joinpath(service_file_name), "w") as file: + file.write(contain) + print('Service file generated with current configuration') + print('Running IRC Service ...') + + print(f"#"*24) + print("Installation complete ...") + print("If the configuration is correct, then you must see your service connected to your irc server") + print(f"If any issue, you can see the log file for debug {ROOT_PATH}{os.sep}logs{os.sep}defender.log") + print(f"#"*24) + +def main(): + if args.check_version: + check_python_requirement() + sys.exit(0) + + if args.install: + create_service_file() + sys.exit(0) + + if args.git_update: + run_git_update() + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/core/installation.py b/core/installation.py deleted file mode 100644 index dcbe1e4..0000000 --- a/core/installation.py +++ /dev/null @@ -1,314 +0,0 @@ -import os -import json -from sys import exit, prefix -from dataclasses import dataclass -from subprocess import check_call, run, CalledProcessError, PIPE, check_output -from platform import python_version, python_version_tuple - -class Install: - - @dataclass - class CoreConfig: - install_log_file: str - unix_systemd_folder: str - service_file_name: str - service_cmd_executable: list - service_cmd_daemon_reload: list - defender_main_executable: str - python_min_version: str - python_current_version_tuple: tuple[int, int, int] - python_current_version: tuple[int, int, int] - defender_install_folder: str - venv_folder: str - venv_cmd_installation: list - venv_cmd_requirements: list[str] - venv_pip_executable: str - venv_python_executable: str - - @dataclass - class Package: - name: str = None - version: str = None - - DB_PACKAGES: list[Package] = [] - - def __init__(self) -> None: - - self.set_configuration() - - if self.skip_install: - self.install_dependencies() - self.check_packages_version() - return None - - self.check_packages_version() - - # Sinon tester les dependances python et les installer avec pip - if self.do_install(): - - self.install_dependencies() - - self.create_service_file() - - self.print_final_message() - - return None - - def set_configuration(self): - - self.skip_install = False - defender_install_folder = os.getcwd() - venv_folder = '.pyenv' - unix_user_home_directory = os.path.expanduser("~") - unix_systemd_folder = os.path.join(unix_user_home_directory, '.config', 'systemd', 'user') - defender_main_executable = os.path.join(defender_install_folder, 'defender.py') - - self.config = self.CoreConfig( - install_log_file='install.log', - unix_systemd_folder=unix_systemd_folder, - service_file_name='defender.service', - service_cmd_executable=['systemctl', '--user', 'start', 'defender'], - service_cmd_daemon_reload=['systemctl', '--user', 'daemon-reload'], - defender_main_executable=defender_main_executable, - python_min_version=(3, 10, 0), - python_current_version_tuple=tuple(map(int, python_version_tuple())), - python_current_version=python_version(), - defender_install_folder=defender_install_folder, - venv_folder=venv_folder, - venv_cmd_installation=['python3', '-m', 'venv', venv_folder], - venv_cmd_requirements=['sqlalchemy','psutil','requests','faker','pyyaml','unrealircd_rpc_py'], - venv_pip_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}pip', - venv_python_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}python' - ) - - if not self.check_python_version(): - # If the Python version is not good then Exit - exit("[!] Python version error [!]") - - if not os.path.exists(os.path.join(self.config.defender_install_folder, 'config', 'configuration.yaml')): - # If configuration file do not exist - exit("[!] Configuration file (core/configuration.yaml) doesn't exist! please create it [!]") - - # Exclude Windows OS from the installation - if os.name == 'nt': - # If windows, modify pip and python virtual environment executable - self.config.venv_pip_executable = f'{os.path.join(defender_install_folder, venv_folder, "Scripts")}{os.sep}pip.exe' - self.config.venv_python_executable = f'{os.path.join(defender_install_folder, venv_folder, "Scripts")}{os.sep}python.exe' - self.skip_install = True - return False - - if self.is_root(): - exit(f'[!] I highly not recommend running Defender as root [!]') - self.skip_install = True - return False - - def is_root(self) -> bool: - - if os.geteuid() != 0: - print('> User without privileges ==> OK') - return False - elif os.geteuid() == 0: - print('[!] Do not use root to install Defender [!]') - exit("Do not use root to install Defender") - return True - - def do_install(self) -> bool: - - full_service_file_path = os.path.join(self.config.unix_systemd_folder, self.config.service_file_name) - - if not os.path.exists(full_service_file_path): - print(f'[!] Service file does not exist [!]') - return True - - # Check if virtual env exist - if not os.path.exists(f'{os.path.join(self.config.defender_install_folder, self.config.venv_folder)}'): - self.run_subprocess(self.config.venv_cmd_installation) - print(f'[!] Virtual env does not exist run the install [!]') - return True - - def run_subprocess(self, command:list) -> None: - - print(f'> {command}') - try: - check_call(command) - print("The command completed successfully.") - except CalledProcessError as e: - print(f"The command failed with the return code: {e.returncode}") - print(f"Try to install dependencies ...") - exit(5) - - def get_packages_version_from_json(self) -> None: - """This will create Package model with package names and required version - """ - try: - - version_filename = f'.{os.sep}version.json' - with open(version_filename, 'r') as version_data: - package_info:dict[str, str] = json.load(version_data) - - for name, version in package_info.items(): - if name == 'version': - continue - - self.DB_PACKAGES.append( - self.Package(name=name, version=version) - ) - - return None - except FileNotFoundError as fe: - print(f"File not found: {fe}") - except Exception as err: - print(f"General Error: {err}") - - def check_packages_version(self) -> bool: - - try: - newVersion = False - self.get_packages_version_from_json() - - if not self.config.venv_folder in prefix: - print(f"You are probably running a new installation or you are not using your virtual env {self.config.venv_folder}") - return newVersion - - print(f"> Checking for dependencies versions ==> WAIT") - for package in self.DB_PACKAGES: - newVersion = False - _required_version = package.version - _installed_version: str = None - - output = check_output([self.config.venv_pip_executable, 'show', package.name]) - for line in output.decode().splitlines(): - if line.startswith('Version:'): - _installed_version = line.split(':')[1].strip() - break - - required_version = tuple(map(int, _required_version.split('.'))) - installed_version = tuple(map(int, _installed_version.split('.'))) - - if required_version > installed_version: - print(f'> New version of {package.name} is available {installed_version} ==> {required_version}') - newVersion = True - - if newVersion: - self.run_subprocess([self.config.venv_pip_executable, 'install', '--upgrade', package.name]) - - print(f"> Dependencies versions ==> OK") - return newVersion - - except CalledProcessError: - print(f"[!] Package {package.name} not installed [!]") - except Exception as err: - print(f"General Error: {err}") - - 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 - """ - if self.config.python_current_version_tuple < self.config.python_min_version: - print(f"## Your python version must be greather than or equal to {self.config.python_min_version} ##") - return False - - print(f"> Version of python : {self.config.python_current_version} ==> OK") - return True - - def check_package(self, package_name) -> bool: - - try: - # Run a command in the virtual environment's Python to check if the package is installed - run([self.config.venv_python_executable, '-c', f'import {package_name}'], check=True, stdout=PIPE, stderr=PIPE) - return True - except CalledProcessError as cpe: - print(cpe) - return False - - def install_dependencies(self) -> None: - """### Verifie les dépendances si elles sont installées - - Test si les modules sont installés - - Met a jour pip - - Install les modules manquants - """ - do_install = False - - # Check if virtual env exist - if not os.path.exists(f'{os.path.join(self.config.defender_install_folder, self.config.venv_folder)}'): - self.run_subprocess(self.config.venv_cmd_installation) - do_install = True - - for module in self.config.venv_cmd_requirements: - module = module.replace('pyyaml', 'yaml') - if not self.check_package(module): - do_install = True - - if not do_install: - return None - - print("===> Clean pip cache") - self.run_subprocess([self.config.venv_pip_executable, 'cache', 'purge']) - - print("===> Check if pip is up to date") - self.run_subprocess([self.config.venv_python_executable, '-m', 'pip', 'install', '--upgrade', 'pip']) - - if not self.check_package('greenlet'): - self.run_subprocess([self.config.venv_pip_executable, 'install', '--only-binary', ':all:', 'greenlet']) - print('====> Greenlet installed') - - for module in self.config.venv_cmd_requirements: - if not self.check_package(module): - print("### Trying to install missing python packages ###") - self.run_subprocess([self.config.venv_pip_executable, 'install', module]) - print(f"====> Module {module} installed!") - else: - print(f"==> {module} already installed") - - def create_service_file(self) -> None: - - full_service_file_path = os.path.join(self.config.unix_systemd_folder, self.config.service_file_name) - - if os.path.exists(full_service_file_path): - print(f'[!] Service file already exist [!]') - self.run_subprocess(self.config.service_cmd_executable) - return None - - contain = f'''[Unit] -Description=Defender IRC Service - -[Service] -ExecStart={self.config.venv_python_executable} {self.config.defender_main_executable} -WorkingDirectory={self.config.defender_install_folder} -SyslogIdentifier=Defender -Restart=on-failure - -[Install] -WantedBy=default.target -''' - # Check if user systemd is available (.config/systemd/user/) - if not os.path.exists(self.config.unix_systemd_folder): - self.run_subprocess(['mkdir', '-p', self.config.unix_systemd_folder]) - - with open(full_service_file_path, 'w+') as servicefile: - servicefile.write(contain) - servicefile.close() - print('Service file generated with current configuration') - print('Running IRC Service ...') - self.run_subprocess(self.config.service_cmd_daemon_reload) - self.run_subprocess(self.config.service_cmd_executable) - - else: - with open(full_service_file_path, 'w+') as servicefile: - servicefile.write(contain) - servicefile.close() - print('Service file generated with current configuration') - print('Running IRC Service ...') - self.run_subprocess(self.config.service_cmd_daemon_reload) - self.run_subprocess(self.config.service_cmd_executable) - - def print_final_message(self) -> None: - - print(f"#"*24) - print("Installation complete ...") - print("If the configuration is correct, then you must see your service connected to your irc server") - print(f"If any issue, you can see the log file for debug {self.config.defender_install_folder}{os.sep}logs{os.sep}defender.log") - print(f"#"*24) - exit(1) diff --git a/core/irc.py b/core/irc.py index 5602238..c691237 100644 --- a/core/irc.py +++ b/core/irc.py @@ -1,13 +1,12 @@ -import sys +import asyncio import socket -import ssl import re import time from ssl import SSLSocket from datetime import datetime, timedelta from typing import TYPE_CHECKING, Any, Optional, Union -from core.classes import rehash -from core.classes.protocols.interface import IProtocol +from core.classes.modules import rehash +from core.classes.interfaces.iprotocol import IProtocol from core.utils import tr if TYPE_CHECKING: @@ -26,61 +25,27 @@ class Irc: def __init__(self, loader: 'Loader'): - # Loader class - self.Loader = loader + self.signal: bool = True - # Load the configuration - self.Config = self.Loader.Config - - # Load Main utils functions - self.Utils = self.Loader.Utils + # Load Context class (Loader) + self.ctx = loader # Date et heure de la premiere connexion de Defender - self.defender_connexion_datetime = self.Config.DEFENDER_CONNEXION_DATETIME + self.defender_connexion_datetime = self.ctx.Config.DEFENDER_CONNEXION_DATETIME # Lancer toutes les 30 secondes des actions de nettoyages - self.beat = self.Config.DEFENDER_HEARTBEAT_FREQUENCY + self.beat = self.ctx.Config.DEFENDER_HEARTBEAT_FREQUENCY # Heartbeat active - self.hb_active = self.Config.DEFENDER_HEARTBEAT + self.hb_active = self.ctx.Config.DEFENDER_HEARTBEAT # ID du serveur qui accueil le service ( Host Serveur Id ) - self.HSID = self.Config.HSID + self.HSID = self.ctx.Config.HSID # Charset utiliser pour décoder/encoder les messages - self.CHARSET = self.Config.SERVEUR_CHARSET + self.CHARSET = self.ctx.Config.SERVEUR_CHARSET """0: utf-8 | 1: iso-8859-1""" - # Use Base Instance - self.Base = self.Loader.Base - - # Logger - self.Logs = self.Loader.Logs - - # Get Settings. - self.Settings = self.Base.Settings - - # Use User Instance - self.User = self.Loader.User - - # Use Admin Instance - self.Admin = self.Loader.Admin - - # Use Client Instance - self.Client = self.Loader.Client - - # Use Channel Instance - self.Channel = self.Loader.Channel - - # Use Reputation Instance - self.Reputation = self.Loader.Reputation - - # Use Module Utils - self.ModuleUtils = self.Loader.ModuleUtils - - # Use Main Sasl module - self.Sasl = self.Loader.Sasl - self.autolimit_started: bool = False """This variable is to make sure the thread is not running""" @@ -91,7 +56,7 @@ class Irc: self.first_connexion_ip: str = None # Load Commands Utils - self.Commands = self.Loader.Commands + # self.Commands = self.Loader.Commands """Command utils""" self.build_command(0, 'core', 'help', 'This provide the help') @@ -99,8 +64,8 @@ class Irc: self.build_command(0, 'core', 'copyright', 'Give some information about the IRC Service') self.build_command(0, 'core', 'uptime', 'Give you since when the service is connected') self.build_command(0, 'core', 'firstauth', 'First authentication of the Service') - self.build_command(0, 'core', 'register', f'Register your nickname /msg {self.Config.SERVICE_NICKNAME} REGISTER ') - self.build_command(0, 'core', 'identify', f'Identify yourself with your password /msg {self.Config.SERVICE_NICKNAME} IDENTIFY ') + self.build_command(0, 'core', 'register', f'Register your nickname /msg {self.ctx.Config.SERVICE_NICKNAME} REGISTER ') + self.build_command(0, 'core', 'identify', f'Identify yourself with your password /msg {self.ctx.Config.SERVICE_NICKNAME} IDENTIFY ') self.build_command(0, 'core', 'logout', 'Reverse the effect of the identify command') self.build_command(1, 'core', 'load', 'Load an existing module') self.build_command(1, 'core', 'unload', 'Unload a module') @@ -110,6 +75,7 @@ class Irc: self.build_command(2, 'core', 'show_modules', 'Display a list of loaded modules') self.build_command(2, 'core', 'show_timers', 'Display active timers') self.build_command(2, 'core', 'show_threads', 'Display active threads in the system') + self.build_command(2, 'core', 'show_asyncio', 'Display active asyncio') self.build_command(2, 'core', 'show_channels', 'Display a list of active channels') self.build_command(2, 'core', 'show_users', 'Display a list of connected users') self.build_command(2, 'core', 'show_clients', 'Display a list of connected clients') @@ -125,136 +91,94 @@ class Irc: self.build_command(3, 'core', 'cert', 'Append your new fingerprint to your account!') self.build_command(4, 'core', 'rehash', 'Reload the configuration file without restarting') self.build_command(4, 'core', 'raw', 'Send a raw command directly to the IRC server') + self.build_command(4, 'core', 'print_vars', 'Print users in a file.') + self.build_command(4, 'core', 'start_rpc', 'Start defender jsonrpc server') + self.build_command(4, 'core', 'stop_rpc', 'Stop defender jsonrpc server') # Define the IrcSocket object self.IrcSocket: Optional[Union[socket.socket, SSLSocket]] = None - self.__create_table() - self.Base.create_thread(func=self.heartbeat, func_args=(self.beat, )) + self.reader: Optional[asyncio.StreamReader] = None + self.writer: Optional[asyncio.StreamWriter] = None + + self.ctx.Base.create_asynctask(self.heartbeat(self.beat)) + + async def connect(self): + + if self.ctx.Config.SERVEUR_SSL: + self.reader, self.writer = await asyncio.open_connection(self.ctx.Config.SERVEUR_IP, self.ctx.Config.SERVEUR_PORT, ssl=self.ctx.Utils.get_ssl_context()) + else: + self.reader, self.writer = await asyncio.open_connection(self.ctx.Config.SERVEUR_IP, self.ctx.Config.SERVEUR_PORT) + + self.init_service_user() + self.Protocol: 'IProtocol' = self.ctx.PFactory.get() + self.Protocol.register_command() + await self.Protocol.send_link() + + async def listen(self): + while self.signal: + data = await self.reader.readuntil(b'\r\n') + await self.send_response(data.splitlines()) + + async def run(self): + try: + await self.connect() + await self.listen() + except asyncio.exceptions.IncompleteReadError as ie: + # When IRCd server is down + # asyncio.exceptions.IncompleteReadError: 0 bytes read on a total of undefined expected bytes + self.ctx.Logs.critical(f"The IRCd server is no more connected! {ie}") + except asyncio.exceptions.CancelledError as cerr: + self.ctx.Logs.debug(f"Asyncio CancelledError reached! {cerr}") ############################################## # CONNEXION IRC # ############################################## - def init_irc(self) -> None: - """Create a socket and connect to irc server - - Args: - ircInstance (Irc): Instance of Irc object. - """ - try: - self.init_service_user() - self.Utils.create_socket(self) - self.__connect_to_irc() - - except AssertionError as ae: - self.Logs.critical(f'Assertion error: {ae}') def init_service_user(self) -> None: - self.User.insert(self.Loader.Definition.MUser( - uid=self.Config.SERVICE_ID, - nickname=self.Config.SERVICE_NICKNAME, - username=self.Config.SERVICE_USERNAME, - realname=self.Config.SERVICE_REALNAME, - hostname=self.Config.SERVICE_HOST, - umodes=self.Config.SERVICE_SMODES + self.ctx.User.insert(self.ctx.Definition.MUser( + uid=self.ctx.Config.SERVICE_ID, + nickname=self.ctx.Config.SERVICE_NICKNAME, + username=self.ctx.Config.SERVICE_USERNAME, + realname=self.ctx.Config.SERVICE_REALNAME, + hostname=self.ctx.Config.SERVICE_HOST, + umodes=self.ctx.Config.SERVICE_SMODES )) return None - def __connect_to_irc(self) -> None: - try: - self.init_service_user() - self.Protocol: 'IProtocol' = self.Loader.PFactory.get() - self.Protocol.register_command() - self.Protocol.send_link() # Etablir le link en fonction du protocol choisi - self.signal = True # Une variable pour initier la boucle infinie - # self.join_saved_channels() # Join existing channels - # self.ModuleUtils.db_load_all_existing_modules(self) - - while self.signal: - try: - if self.Config.DEFENDER_RESTART == 1: - rehash.restart_service(self) - - # 4072 max what the socket can grab - buffer_size = self.IrcSocket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF) - data_in_bytes = self.IrcSocket.recv(buffer_size) - data = data_in_bytes.splitlines(True) - count_bytes = len(data_in_bytes) - - while count_bytes > 4070: - # If the received message is > 4070 then loop and add the value to the variable - new_data = self.IrcSocket.recv(buffer_size) - data_in_bytes += new_data - count_bytes = len(new_data) - - data = data_in_bytes.splitlines(True) - - if not data: - break - - self.send_response(data) - - except ssl.SSLEOFError as soe: - self.Logs.error(f"SSLEOFError __connect_to_irc: {soe} - {data}") - except ssl.SSLError as se: - self.Logs.error(f"SSLError __connect_to_irc: {se} - {data}") - sys.exit(-1) - except OSError as oe: - self.Logs.error(f"SSLError __connect_to_irc: {oe} {oe.errno}") - if oe.errno == 10053: - sys.exit(-1) - except (socket.error, ConnectionResetError): - self.Logs.debug("Connexion reset") - - self.IrcSocket.shutdown(socket.SHUT_RDWR) - self.IrcSocket.close() - self.Logs.info("-- Fermeture de Defender ...") - sys.exit(0) - - except AssertionError as ae: - self.Logs.error(f'AssertionError: {ae}') - except ValueError as ve: - self.Logs.error(f'ValueError: {ve}') - except ssl.SSLEOFError as soe: - self.Logs.error(f"SSLEOFError: {soe}") - except AttributeError as atte: - self.Logs.critical(f"AttributeError: {atte}", exc_info=True) - except Exception as e: - self.Logs.critical(f"General Error: {e}", exc_info=True) - - def join_saved_channels(self) -> None: + async def join_saved_channels(self) -> None: """## Joining saved channels""" - exec_query = self.Base.db_execute_query(f'SELECT distinct channel_name FROM {self.Config.TABLE_CHANNEL}') + exec_query = await self.ctx.Base.db_execute_query(f'SELECT distinct channel_name FROM {self.ctx.Config.TABLE_CHANNEL}') result_query = exec_query.fetchall() if result_query: for chan_name in result_query: chan = chan_name[0] - self.Protocol.send_sjoin(channel=chan) + await self.Protocol.send_sjoin(channel=chan) - def send_response(self, responses:list[bytes]) -> None: + async def send_response(self, responses:list[bytes]) -> None: try: for data in responses: response = data.decode(self.CHARSET[0]).split() - self.cmd(response) + await self.cmd(response) except UnicodeEncodeError as ue: for data in responses: response = data.decode(self.CHARSET[1],'replace').split() - self.cmd(response) - self.Logs.error(f'UnicodeEncodeError: {ue}') - self.Logs.error(response) + await self.cmd(response) + self.ctx.Logs.error(f'UnicodeEncodeError: {ue}') + self.ctx.Logs.error(response) except UnicodeDecodeError as ud: for data in responses: response = data.decode(self.CHARSET[1],'replace').split() - self.cmd(response) - self.Logs.error(f'UnicodeDecodeError: {ud}') - self.Logs.error(response) + await self.cmd(response) + self.ctx.Logs.error(f'UnicodeDecodeError: {ud}') + self.ctx.Logs.error(response) except AssertionError as ae: - self.Logs.error(f"Assertion error : {ae}") + self.ctx.Logs.error(f"Assertion error : {ae}") def unload(self) -> None: # This is only to reference the method @@ -274,31 +198,31 @@ class Irc: command_description (str): The description of the command """ # Build Model. - self.Commands.build(self.Loader.Definition.MCommand(module_name, command_name, command_description, level)) + self.ctx.Commands.build(self.ctx.Definition.MCommand(module_name, command_name, command_description, level)) return None - def generate_help_menu(self, nickname: str, module: Optional[str] = None) -> None: + async def generate_help_menu(self, nickname: str, module: Optional[str] = None) -> None: # Check if the nickname is an admin p = self.Protocol - admin_obj = self.Admin.get_admin(nickname) - dnickname = self.Config.SERVICE_NICKNAME - color_nogc = self.Config.COLORS.nogc - color_black = self.Config.COLORS.black + admin_obj = self.ctx.Admin.get_admin(nickname) + dnickname = self.ctx.Config.SERVICE_NICKNAME + color_nogc = self.ctx.Config.COLORS.nogc + color_black = self.ctx.Config.COLORS.black current_level = 0 if admin_obj is not None: current_level = admin_obj.level - p.send_notice(nick_from=dnickname,nick_to=nickname, msg=f" ***************** LISTE DES COMMANDES *****************") + await p.send_notice(nick_from=dnickname,nick_to=nickname, msg=f" ***************** LISTE DES COMMANDES *****************") header = f" {'Level':<8}| {'Command':<25}| {'Module':<15}| {'Description':<35}" line = "-"*75 - p.send_notice(nick_from=dnickname,nick_to=nickname, msg=header) - p.send_notice(nick_from=dnickname,nick_to=nickname, msg=f" {line}") - for cmd in self.Commands.get_commands_by_level(current_level): + await p.send_notice(nick_from=dnickname,nick_to=nickname, msg=header) + await p.send_notice(nick_from=dnickname,nick_to=nickname, msg=f" {line}") + for cmd in self.ctx.Commands.get_commands_by_level(current_level): if module is None or cmd.module_name.lower() == module.lower(): - p.send_notice( + await p.send_notice( nick_from=dnickname, nick_to=nickname, msg=f" {color_black}{cmd.command_level:<8}{color_nogc}| {cmd.command_name:<25}| {cmd.module_name:<15}| {cmd.description:<35}" @@ -306,18 +230,18 @@ class Irc: return None - def on_sasl_authentication_process(self, sasl_model: 'MSasl') -> bool: + async def on_sasl_authentication_process(self, sasl_model: 'MSasl') -> bool: s = sasl_model if sasl_model: - def db_get_admin_info(*, username: Optional[str] = None, password: Optional[str] = None, fingerprint: Optional[str] = None) -> Optional[dict[str, Any]]: + async def db_get_admin_info(*, username: Optional[str] = None, password: Optional[str] = None, fingerprint: Optional[str] = None) -> Optional[dict[str, Any]]: if fingerprint: mes_donnees = {'fingerprint': fingerprint} - query = f"SELECT user, level, language FROM {self.Config.TABLE_ADMIN} WHERE fingerprint = :fingerprint" + query = f"SELECT user, level, language FROM {self.ctx.Config.TABLE_ADMIN} WHERE fingerprint = :fingerprint" else: - mes_donnees = {'user': username, 'password': self.Utils.hash_password(password)} - query = f"SELECT user, level, language FROM {self.Config.TABLE_ADMIN} WHERE user = :user AND password = :password" + mes_donnees = {'user': username, 'password': self.ctx.Utils.hash_password(password)} + query = f"SELECT user, level, language FROM {self.ctx.Config.TABLE_ADMIN} WHERE user = :user AND password = :password" - result = self.Base.db_execute_query(query, mes_donnees) + result = await self.ctx.Base.db_execute_query(query, mes_donnees) user_from_db = result.fetchone() if user_from_db: return {'user': user_from_db[0], 'level': user_from_db[1], 'language': user_from_db[2]} @@ -326,37 +250,32 @@ class Irc: if s.message_type == 'C' and s.mechanisme == 'PLAIN': # Connection via PLAIN - admin_info = db_get_admin_info(username=s.username, password=s.password) + admin_info = await db_get_admin_info(username=s.username, password=s.password) if admin_info is not None: s.auth_success = True s.level = admin_info.get('level', 0) s.language = admin_info.get('language', 'EN') - self.Protocol.send2socket(f":{self.Config.SERVEUR_LINK} SASL {self.Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D S") - self.Protocol.send2socket(f":{self.Config.SERVEUR_LINK} 903 {s.username} :SASL authentication successful") + await self.Protocol.send2socket(f":{self.ctx.Config.SERVEUR_LINK} SASL {self.ctx.Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D S") + await self.Protocol.send2socket(f":{self.ctx.Config.SERVEUR_LINK} 903 {s.username} :SASL authentication successful") else: - self.Protocol.send2socket(f":{self.Config.SERVEUR_LINK} SASL {self.Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D F") - self.Protocol.send2socket(f":{self.Config.SERVEUR_LINK} 904 {s.username} :SASL authentication failed") + await self.Protocol.send2socket(f":{self.ctx.Config.SERVEUR_LINK} SASL {self.ctx.Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D F") + await self.Protocol.send2socket(f":{self.ctx.Config.SERVEUR_LINK} 904 {s.username} :SASL authentication failed") elif s.message_type == 'S' and s.mechanisme == 'EXTERNAL': # Connection using fingerprints - admin_info = db_get_admin_info(fingerprint=s.fingerprint) + admin_info = await db_get_admin_info(fingerprint=s.fingerprint) if admin_info is not None: s.auth_success = True s.level = admin_info.get('level', 0) s.username = admin_info.get('user', None) s.language = admin_info.get('language', 'EN') - self.Protocol.send2socket(f":{self.Config.SERVEUR_LINK} SASL {self.Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D S") - self.Protocol.send2socket(f":{self.Config.SERVEUR_LINK} 903 {s.username} :SASL authentication successful") + await self.Protocol.send2socket(f":{self.ctx.Config.SERVEUR_LINK} SASL {self.ctx.Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D S") + await self.Protocol.send2socket(f":{self.ctx.Config.SERVEUR_LINK} 903 {s.username} :SASL authentication successful") else: # "904 :SASL authentication failed" - self.Protocol.send2socket(f":{self.Config.SERVEUR_LINK} SASL {self.Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D F") - self.Protocol.send2socket(f":{self.Config.SERVEUR_LINK} 904 {s.username} :SASL authentication failed") - - def __create_table(self) -> None: - """## Create core tables - """ - pass + await self.Protocol.send2socket(f":{self.ctx.Config.SERVEUR_LINK} SASL {self.ctx.Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D F") + await self.Protocol.send2socket(f":{self.ctx.Config.SERVEUR_LINK} 904 {s.username} :SASL authentication failed") def get_defender_uptime(self) -> str: """Savoir depuis quand Defender est connecté @@ -370,7 +289,7 @@ class Irc: return uptime - def heartbeat(self, beat:float) -> None: + async def heartbeat(self, beat: float) -> None: """Execute certaines commandes de nettoyage toutes les x secondes x étant définit a l'initialisation de cette class (self.beat) @@ -378,17 +297,17 @@ class Irc: beat (float): Nombre de secondes entre chaque exécution """ while self.hb_active: - time.sleep(beat) - self.Base.execute_periodic_action() + await asyncio.sleep(beat) + self.ctx.Base.execute_periodic_action() def insert_db_admin(self, uid: str, account: str, level: int, language: str) -> None: - user_obj = self.User.get_user(uid) + user_obj = self.ctx.User.get_user(uid) if user_obj is None: return None - self.Admin.insert( - self.Loader.Definition.MAdmin( + self.ctx.Admin.insert( + self.ctx.Definition.MAdmin( **user_obj.to_dict(), language=language, account=account, @@ -400,15 +319,15 @@ class Irc: def delete_db_admin(self, uid:str) -> None: - if self.Admin.get_admin(uid) is None: + if self.ctx.Admin.get_admin(uid) is None: return None - if not self.Admin.delete(uid): - self.Logs.critical(f'UID: {uid} was not deleted') + if not self.ctx.Admin.delete(uid): + self.ctx.Logs.critical(f'UID: {uid} was not deleted') return None - def create_defender_user(self, sender: str, new_admin: str, new_level: int, new_password: str) -> bool: + async def create_defender_user(self, sender: str, new_admin: str, new_level: int, new_password: str) -> bool: """Create a new admin user for defender Args: @@ -422,57 +341,57 @@ class Irc: """ # > addaccess [nickname] [level] [password] - dnick = self.Config.SERVICE_NICKNAME + dnick = self.ctx.Config.SERVICE_NICKNAME p = self.Protocol - get_user = self.User.get_user(new_admin) - level = self.Base.convert_to_int(new_level) + get_user = self.ctx.User.get_user(new_admin) + level = self.ctx.Base.convert_to_int(new_level) password = new_password if get_user is None: response = tr("The nickname (%s) is not currently connected! please create a new admin when the nickname is connected to the network!", new_admin) - p.send_notice(dnick, sender, response) - self.Logs.debug(f"New admin {new_admin} sent by {sender} is not connected") + await p.send_notice(dnick, sender, response) + self.ctx.Logs.debug(f"New admin {new_admin} sent by {sender} is not connected") return False if level is None or level > 4 or level == 0: - p.send_notice(dnick, sender, tr("The level (%s) must be a number from 1 to 4", level)) - self.Logs.debug(f"Level must a number between 1 to 4 (sent by {sender})") + await p.send_notice(dnick, sender, tr("The level (%s) must be a number from 1 to 4", level)) + self.ctx.Logs.debug(f"Level must a number between 1 to 4 (sent by {sender})") return False nickname = get_user.nickname hostname = get_user.hostname vhost = get_user.vhost - spassword = self.Loader.Utils.hash_password(password) + spassword = self.ctx.Utils.hash_password(password) # Check if the user already exist - if not self.Admin.db_is_admin_exist(nickname): - mes_donnees = {'datetime': self.Utils.get_sdatetime(), 'user': nickname, 'password': spassword, 'hostname': hostname, 'vhost': vhost, 'level': level, 'language': 'EN'} - self.Base.db_execute_query(f'''INSERT INTO {self.Config.TABLE_ADMIN} + if not self.ctx.Admin.db_is_admin_exist(nickname): + mes_donnees = {'datetime': self.ctx.Utils.get_sdatetime(), 'user': nickname, 'password': spassword, 'hostname': hostname, 'vhost': vhost, 'level': level, 'language': self.ctx.Config.LANG} + await self.ctx.Base.db_execute_query(f'''INSERT INTO {self.ctx.Config.TABLE_ADMIN} (createdOn, user, password, hostname, vhost, level, language) VALUES (:datetime, :user, :password, :hostname, :vhost, :level, :language) ''', mes_donnees) - p.send_notice(dnick, sender, tr("New admin (%s) has been added with level %s", nickname, level)) - self.Logs.info(f"A new admin ({nickname}) has been created by {sender}!") + await p.send_notice(dnick, sender, tr("New admin (%s) has been added with level %s", nickname, level)) + self.ctx.Logs.info(f"A new admin ({nickname}) has been created by {sender}!") return True else: - p.send_notice(dnick, sender, tr("The nickname (%s) Already exist!", nickname)) - self.Logs.info(f"The nickname {nickname} already exist! (sent by {sender})") + await p.send_notice(dnick, sender, tr("The nickname (%s) Already exist!", nickname)) + self.ctx.Logs.info(f"The nickname {nickname} already exist! (sent by {sender})") return False - def thread_check_for_new_version(self, fromuser: str) -> None: - dnickname = self.Config.SERVICE_NICKNAME + async def thread_check_for_new_version(self, fromuser: str) -> None: + dnickname = self.ctx.Config.SERVICE_NICKNAME - if self.Base.check_for_new_version(True): - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" New Version available : {self.Config.CURRENT_VERSION} >>> {self.Config.LATEST_VERSION}") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=" Please run (git pull origin main) in the current folder") + if self.ctx.Base.check_for_new_version(True): + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" New Version available : {self.ctx.Config.CURRENT_VERSION} >>> {self.ctx.Config.LATEST_VERSION}") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=" Please run (git pull origin main) in the current folder") else: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=" You have the latest version of defender") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=" You have the latest version of defender") return None - def cmd(self, data: list[str]) -> None: + async def cmd(self, data: list[str]) -> None: """Parse server response Args: @@ -480,34 +399,32 @@ class Irc: """ try: original_response: list[str] = data.copy() - RED = self.Config.COLORS.red - GREEN = self.Config.COLORS.green - NOGC = self.Config.COLORS.nogc - if len(original_response) < 2: - self.Logs.warning(f'Size ({str(len(original_response))}) - {original_response}') + self.ctx.Logs.warning(f'Size ({str(len(original_response))}) - {original_response}') return None - self.Logs.debug(f">> {self.Utils.hide_sensitive_data(original_response)}") - + self.ctx.Logs.debug(f">> {self.ctx.Utils.hide_sensitive_data(original_response)}") pos, parsed_protocol = self.Protocol.get_ircd_protocol_poisition(cmd=original_response, log=True) + modules = self.ctx.ModuleUtils.model_get_loaded_modules().copy() for parsed in self.Protocol.Handler.get_ircd_commands(): if parsed.command_name.upper() == parsed_protocol: - parsed.func(original_response) + await parsed.func(original_response) + for module in modules: + await module.class_instance.cmd(original_response) if self.ctx.Utils.is_coroutinefunction(module.class_instance.cmd) else module.class_instance.cmd(original_response) - if len(original_response) > 2: - if original_response[2] != 'UID': - # Envoyer la commande aux classes dynamiquement chargées - for module in self.ModuleUtils.model_get_loaded_modules().copy(): - module.class_instance.cmd(original_response) + # if len(original_response) > 2: + # if original_response[2] != 'UID': + # # Envoyer la commande aux classes dynamiquement chargées + # for module in self.ctx.ModuleUtils.model_get_loaded_modules().copy(): + # module.class_instance.cmd(original_response) except IndexError as ie: - self.Logs.error(f"IndexError: {ie}") + self.ctx.Logs.error(f"IndexError: {ie}") except Exception as err: - self.Logs.error(f"General Error: {err}", exc_info=True) + self.ctx.Logs.error(f"General Error: {err}", exc_info=True) - def hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None: + async def hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None: """Create Args: @@ -519,180 +436,166 @@ class Irc: Returns: None: Nothing to return """ + u = self.ctx.User.get_user(user) + """The User Object""" + if u is None: + return None - fromuser = self.User.get_nickname(user) # Nickname qui a lancé la commande - uid = self.User.get_uid(user) # Récuperer le uid de l'utilisateur - self.Settings.current_admin = self.Admin.get_admin(user) # set Current admin if any. + c = self.ctx.Client.get_Client(u.uid) + """The Client Object""" - RED = self.Config.COLORS.red - GREEN = self.Config.COLORS.green - NOGC = self.Config.COLORS.nogc + fromuser = u.nickname + uid = u.uid + self.ctx.Settings.current_admin = self.ctx.Admin.get_admin(user) # set Current admin if any. + + RED = self.ctx.Config.COLORS.red + GREEN = self.ctx.Config.COLORS.green + BLACK = self.ctx.Config.COLORS.black + NOGC = self.ctx.Config.COLORS.nogc # Defender information - dnickname = self.Config.SERVICE_NICKNAME # Defender nickname - dchanlog = self.Config.SERVICE_CHANLOG # Defender chan log + dnickname = self.ctx.Config.SERVICE_NICKNAME # Defender nickname + dchanlog = self.ctx.Config.SERVICE_CHANLOG # Defender chan log if len(cmd) > 0: command = str(cmd[0]).lower() else: return False - if not self.Commands.is_client_allowed_to_run_command(fromuser, command): + if not self.ctx.Commands.is_client_allowed_to_run_command(fromuser, command): command = 'notallowed' # Envoyer la commande aux classes dynamiquement chargées if command != 'notallowed': - for module in self.ModuleUtils.DB_MODULES: - module.class_instance.hcmds(user, channel, cmd, fullcmd) + for module in self.ctx.ModuleUtils.DB_MODULES: + await module.class_instance.hcmds(user, channel, cmd, fullcmd) match command: case 'notallowed': try: current_command = str(cmd[0]) - self.Protocol.send_priv_msg( + await self.Protocol.send_priv_msg( msg=tr('[ %s%s%s ] - Access denied to %s', RED, current_command.upper(), NOGC, fromuser), nick_from=dnickname, channel=dchanlog ) - self.Protocol.send_notice( + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg=tr('Access denied!') ) except IndexError as ie: - self.Logs.error(f'{ie}') + self.ctx.Logs.error(f'{ie}') case 'deauth': current_command = str(cmd[0]).upper() - uid_to_deauth = self.User.get_uid(fromuser) + uid_to_deauth = uid self.delete_db_admin(uid_to_deauth) - self.Protocol.send_priv_msg( + await self.Protocol.send_priv_msg( msg=tr("[ %s%s%s ] - %s has been disconnected from %s", RED, current_command, NOGC, fromuser, dnickname), nick_from=dnickname, channel=dchanlog ) - self.Protocol.send_notice(dnickname, fromuser, tr("You have been successfully disconnected from %s", dnickname)) + await self.Protocol.send_notice(dnickname, fromuser, tr("You have been successfully disconnected from %s", dnickname)) return None case 'firstauth': - # firstauth OWNER_NICKNAME OWNER_PASSWORD - current_nickname = self.User.get_nickname(fromuser) - current_uid = self.User.get_uid(fromuser) + # Syntax. /msg defender firstauth OWNER_NICKNAME OWNER_PASSWORD + # Check command + current_nickname = fromuser + current_uid = uid current_command = str(cmd[0]) - query = f"SELECT count(id) as c FROM {self.Config.TABLE_ADMIN}" - result = self.Base.db_execute_query(query) + if current_nickname is None: + self.ctx.Logs.critical(f"This nickname [{fromuser}] don't exist") + return None + + if len(cmd) < 3: + await self.Protocol.send_notice(dnickname,fromuser, tr("Syntax. /msg %s %s [OWNER_NICKNAME] [OWNER_PASSWORD]", self.ctx.Config.SERVICE_NICKNAME, current_command)) + return None + + query = f"SELECT count(id) as c FROM {self.ctx.Config.TABLE_ADMIN}" + result = await self.ctx.Base.db_execute_query(query) result_db = result.fetchone() if result_db[0] > 0: - self.Protocol.send_notice( + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, - msg=tr("You can't use this command anymore ! Please use [%sauth] instead", self.Config.SERVICE_PREFIX) + msg=tr("You can't use this command anymore ! Please use [%sauth] instead", self.ctx.Config.SERVICE_PREFIX) ) - return False - - if current_nickname is None: - self.Logs.critical(f"This nickname [{fromuser}] don't exist") - return False + return None # Credentials sent from the user cmd_owner = str(cmd[1]) cmd_password = str(cmd[2]) # Credentials coming from the Configuration - config_owner = self.Config.OWNER - config_password = self.Config.PASSWORD - - if current_nickname != cmd_owner: - self.Logs.critical(f"The current nickname [{fromuser}] is different than the nickname sent [{cmd_owner}] !") - self.Protocol.send_notice( - nick_from=dnickname, - nick_to=fromuser, - msg=f"The current nickname [{fromuser}] is different than the nickname sent [{cmd_owner}] !" - ) - return False - - if current_nickname != config_owner: - self.Logs.critical(f"The current nickname [{current_nickname}] is different than the configuration owner [{config_owner}] !") - self.Protocol.send_notice( - nick_from=dnickname, - nick_to=fromuser, - msg=f"The current nickname [{current_nickname}] is different than the configuration owner [{config_owner}] !" - ) - return False + config_owner = self.ctx.Config.OWNER + config_password = self.ctx.Config.PASSWORD if cmd_owner != config_owner: - self.Logs.critical(f"The nickname sent [{cmd_owner}] is different than the configuration owner [{config_owner}] !") - self.Protocol.send_notice( + self.ctx.Logs.critical(f"The nickname sent [{cmd_owner}] is different than the configuration owner [{config_owner}] !") + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, - msg=f"The nickname sent [{cmd_owner}] is different than the configuration owner [{config_owner}] !" + msg=tr("The nickname sent [%s] is different than the one set in the configuration file !", cmd_owner) ) - return False + return None if cmd_owner == config_owner and cmd_password == config_password: - self.Base.db_create_first_admin() - self.insert_db_admin(current_uid, cmd_owner, 5, self.Config.LANG) - self.Protocol.send_priv_msg( - msg=f"[ {self.Config.COLORS.green}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.get_nickname(fromuser)} est désormais connecté a {dnickname}", + await self.ctx.Base.db_create_first_admin() + self.insert_db_admin(current_uid, cmd_owner, 5, self.ctx.Config.LANG) + await self.Protocol.send_priv_msg( + msg=tr("[%s %s %s] - %s is now connected to %s", GREEN, current_command.upper(), NOGC, fromuser, dnickname), nick_from=dnickname, channel=dchanlog ) - self.Protocol.send_notice( - nick_from=dnickname, - nick_to=fromuser, - msg=f"Connexion a {dnickname} réussie!" - ) + await self.Protocol.send_notice(dnickname, fromuser, tr("Successfuly connected to %s", dnickname)) else: - self.Protocol.send_priv_msg( - msg=f"[ {self.Config.COLORS.red}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.get_nickname(fromuser)} a tapé un mauvais mot de pass", + await self.Protocol.send_priv_msg( + msg=tr("[ %s %s %s ] - %s provided a wrong password!", RED, current_command.upper(), NOGC, current_nickname), nick_from=dnickname, channel=dchanlog ) - self.Protocol.send_notice( - nick_from=dnickname, - nick_to=fromuser, - msg=f"Mot de passe incorrecte" - ) + await self.Protocol.send_notice(dnickname, fromuser, tr("Wrong password!")) case 'auth': # Syntax. !auth nickname password if len(cmd) < 3: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} [nickname] [password]") + await 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 = cmd[1] password = cmd[2] - current_client = self.User.get_user(fromuser) - admin_obj = self.Admin.get_admin(fromuser) + current_client = u + admin_obj = self.ctx.Admin.get_admin(fromuser) if current_client is None: # This case should never happen - self.Protocol.send_priv_msg(nick_from=dnickname, - msg=f"[ {GREEN}{str(current_command).upper()}{NOGC} ] - Nickname {fromuser} is trying to connect to defender wrongly", + await self.Protocol.send_priv_msg(nick_from=dnickname, + msg=f"[ {RED}{str(command).upper()} FAIL{NOGC} ] - Nickname {fromuser} is trying to connect to defender wrongly", channel=dchanlog) return None if admin_obj: - self.Protocol.send_priv_msg(nick_from=dnickname, - msg=f"[ {GREEN}{str(current_command).upper()}{NOGC} ] - {fromuser} is already connected to {dnickname}", + await self.Protocol.send_priv_msg(nick_from=dnickname, + msg=f"[ {GREEN}{str(command).upper()}{NOGC} ] - {fromuser} is already connected to {dnickname}", channel=dchanlog) - self.Protocol.send_notice(dnickname, fromuser, tr("You are already connected to %s", dnickname)) + await self.Protocol.send_notice(dnickname, fromuser, tr("You are already connected to %s", dnickname)) return None - mes_donnees = {'user': user_to_log, 'password': self.Loader.Utils.hash_password(password)} - query = f"SELECT id, user, level, language FROM {self.Config.TABLE_ADMIN} WHERE user = :user AND password = :password" - result = self.Base.db_execute_query(query, mes_donnees) + mes_donnees = {'user': user_to_log, 'password': self.ctx.Utils.hash_password(password)} + query = f"SELECT id, user, level, language FROM {self.ctx.Config.TABLE_ADMIN} WHERE user = :user AND password = :password" + result = await self.ctx.Base.db_execute_query(query, mes_donnees) user_from_db = result.fetchone() if user_from_db: @@ -700,58 +603,58 @@ class Irc: level = int(user_from_db[2]) language = str(user_from_db[3]) self.insert_db_admin(current_client.uid, account, level, language) - self.Protocol.send_priv_msg(nick_from=dnickname, - msg=f"[ {GREEN}{str(current_command).upper()}{NOGC} ] - {current_client.nickname} ({account}) est désormais connecté a {dnickname}", + await self.Protocol.send_priv_msg(nick_from=dnickname, + msg=f"[ {GREEN}{str(command).upper()} SUCCESS{NOGC} ] - {current_client.nickname} ({account}) est désormais connecté a {dnickname}", channel=dchanlog) - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Connexion a {dnickname} réussie!") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=tr("Successfuly connected to %s", dnickname)) return None else: - self.Protocol.send_priv_msg(nick_from=dnickname, - msg=f"[ {RED}{str(current_command).upper()}{NOGC} ] - {current_client.nickname} a tapé un mauvais mot de pass", + await self.Protocol.send_priv_msg(nick_from=dnickname, + msg=f"[ {RED}{str(command).upper()} FAIL{NOGC} ] - {current_client.nickname} a tapé un mauvais mot de pass", channel=dchanlog) - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Mot de passe incorrecte") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=tr("Wrong password!")) return None case 'addaccess': try: # .addaccess adator 5 password if len(cmd) < 4: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Right command : /msg {dnickname} addaccess [nickname] [level] [password]") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"level: from 1 to 4") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Right command : /msg {dnickname} addaccess [nickname] [level] [password]") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"level: from 1 to 4") return None new_admin = str(cmd[1]) - level = self.Base.int_if_possible(cmd[2]) + level = self.ctx.Base.int_if_possible(cmd[2]) password = str(cmd[3]) self.create_defender_user(fromuser, new_admin, level, password) return None except IndexError as ie: - self.Logs.error(f'_hcmd addaccess: {ie}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} addaccess [nickname] [level] [password]") + self.ctx.Logs.error(f'_hcmd addaccess: {ie}') + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} addaccess [nickname] [level] [password]") except TypeError as te: - self.Logs.error(f'_hcmd addaccess: out of index : {te}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} addaccess [nickname] [level] [password]") + self.ctx.Logs.error(f'_hcmd addaccess: out of index : {te}') + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} addaccess [nickname] [level] [password]") case 'editaccess': - # .editaccess [USER] [PASSWORD] [LEVEL] + # .editaccess [USER] [NEW_PASSWORD] [LEVEL] try: 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") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Right command : /msg {dnickname} editaccess [nickname] [NEWPASSWORD] [NEWLEVEL]") + await 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.Loader.Utils.hash_password(cmd[2]) + user_password = self.ctx.Utils.hash_password(cmd[2]) - get_admin = self.Admin.get_admin(fromuser) + get_admin = self.ctx.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") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"This user {fromuser} has no Admin access") return None - current_user = self.User.get_nickname(fromuser) - current_uid = self.User.get_uid(fromuser) + current_user = fromuser + current_uid = uid current_user_level = get_admin.level user_new_level = int(cmd[3]) if len(cmd) == 4 else get_admin.level @@ -760,40 +663,40 @@ class Irc: user_new_level = get_admin.level if user_new_level > 5: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="Maximum authorized level is 5") + await 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. mes_donnees = {'user': user_to_edit} - query = f"SELECT user, level FROM {self.Config.TABLE_ADMIN} WHERE user = :user" - result = self.Base.db_execute_query(query, mes_donnees) + query = f"SELECT user, level FROM {self.ctx.Config.TABLE_ADMIN} WHERE user = :user" + result = await self.ctx.Base.db_execute_query(query, mes_donnees) isUserExist = result.fetchone() if not isUserExist is None: if current_user_level < int(isUserExist[1]): - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You are not allowed to edit this access") + await 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: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" You can't edit access of a user with same level") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" You can't edit access of a user with same level") return None # Le user existe dans la base de données data_to_update = {'user': user_to_edit, 'password': user_password, 'level': user_new_level} - sql_update = f"UPDATE {self.Config.TABLE_ADMIN} SET level = :level, password = :password WHERE user = :user" - exec_query = self.Base.db_execute_query(sql_update, data_to_update) + sql_update = f"UPDATE {self.ctx.Config.TABLE_ADMIN} SET level = :level, password = :password WHERE user = :user" + exec_query = await self.ctx.Base.db_execute_query(sql_update, data_to_update) if exec_query.rowcount > 0: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" User {user_to_edit} has been modified with level {str(user_new_level)}") - self.Admin.update_level(user_to_edit, user_new_level) + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" User {user_to_edit} has been modified with level {str(user_new_level)}") + self.ctx.Admin.update_level(user_to_edit, user_new_level) else: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Impossible de modifier l'utilisateur {str(user_new_level)}") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Impossible de modifier l'utilisateur {str(user_new_level)}") except TypeError as te: - self.Logs.error(f"Type error : {te}") + self.ctx.Logs.error(f"Type error : {te}") except ValueError as ve: - self.Logs.error(f"Value Error : {ve}") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {self.Config.SERVICE_PREFIX}editaccess [USER] [NEWPASSWORD] [NEWLEVEL]") + self.ctx.Logs.error(f"Value Error : {ve}") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {self.ctx.Config.SERVICE_PREFIX}editaccess [USER] [NEWPASSWORD] [NEWLEVEL]") case 'delaccess': # .delaccess [USER] [CONFIRMUSER] @@ -801,119 +704,139 @@ class Irc: user_confirmation = cmd[2] if user_to_del != user_confirmation: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Les user ne sont pas les mêmes, tu dois confirmer le user que tu veux supprimer") - self.Logs.warning(f':{dnickname} NOTICE {fromuser} : Les user ne sont pas les mêmes, tu dois confirmer le user que tu veux supprimer') + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Les user ne sont pas les mêmes, tu dois confirmer le user que tu veux supprimer") + self.ctx.Logs.warning(f':{dnickname} NOTICE {fromuser} : Les user ne sont pas les mêmes, tu dois confirmer le user que tu veux supprimer') return None if len(cmd) < 3: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{self.Config.SERVICE_PREFIX}delaccess [USER] [CONFIRMUSER]") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{self.ctx.Config.SERVICE_PREFIX}delaccess [USER] [CONFIRMUSER]") return None - get_admin = self.Admin.get_admin(fromuser) + get_admin = self.ctx.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") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"This user {fromuser} has no admin access") return None - current_user = self.User.get_nickname(fromuser) - current_uid = self.User.get_uid(fromuser) + current_user = fromuser + current_uid = uid current_user_level = get_admin.level # Rechercher le user dans la base de données. mes_donnees = {'user': user_to_del} - query = f"SELECT user, level FROM {self.Config.TABLE_ADMIN} WHERE user = :user" - result = self.Base.db_execute_query(query, mes_donnees) + query = f"SELECT user, level FROM {self.ctx.Config.TABLE_ADMIN} WHERE user = :user" + result = await self.ctx.Base.db_execute_query(query, mes_donnees) info_user = result.fetchone() if not info_user is None: level_user_to_del = info_user[1] if current_user_level <= level_user_to_del: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You are not allowed to delete this access") - self.Logs.warning(f':{dnickname} NOTICE {fromuser} : You are not allowed to delete this access') + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You are not allowed to delete this access") + self.ctx.Logs.warning(f':{dnickname} NOTICE {fromuser} : You are not allowed to delete this access') return None data_to_delete = {'user': user_to_del} - sql_delete = f"DELETE FROM {self.Config.TABLE_ADMIN} WHERE user = :user" - exec_query = self.Base.db_execute_query(sql_delete, data_to_delete) + sql_delete = f"DELETE FROM {self.ctx.Config.TABLE_ADMIN} WHERE user = :user" + exec_query = await self.ctx.Base.db_execute_query(sql_delete, data_to_delete) if exec_query.rowcount > 0: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"User {user_to_del} has been deleted !") - self.Admin.delete(user_to_del) + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"User {user_to_del} has been deleted !") + self.ctx.Admin.delete(user_to_del) else: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Impossible de supprimer l'utilisateur.") - self.Logs.warning(f":{dnickname} NOTICE {fromuser} : Impossible de supprimer l'utilisateur.") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Impossible de supprimer l'utilisateur.") + self.ctx.Logs.warning(f":{dnickname} NOTICE {fromuser} : Impossible de supprimer l'utilisateur.") case 'cert': # Syntax !cert try: - admin_obj = self.Admin.get_admin(fromuser) - if admin_obj: - if admin_obj.fingerprint is not None: - query = f'UPDATE {self.Config.TABLE_ADMIN} SET fingerprint = :fingerprint WHERE user = :user' - r = self.Base.db_execute_query(query, {'fingerprint': admin_obj.fingerprint, 'user': admin_obj.account}) - if r.rowcount > 0: - self.Protocol.send_notice(dnickname, fromuser, f'[ {GREEN}CERT{NOGC} ] Your new fingerprint has been attached to your account. {admin_obj.fingerprint}') - else: - self.Protocol.send_notice(dnickname, fromuser, f'[ {RED}CERT{NOGC} ] Impossible to add your fingerprint.{admin_obj.fingerprint}') - else: - self.Protocol.send_notice(dnickname, fromuser, f'[ {RED}CERT{NOGC} ] There is no fingerprint to add.') + if len(cmd) < 2: + await self.Protocol.send_notice(dnickname, fromuser, f"Right command : /msg {dnickname} cert add") + await self.Protocol.send_notice(dnickname, fromuser, f"Right command : /msg {dnickname} cert del") + return None + + admin_obj = self.ctx.Admin.get_admin(fromuser) + param = cmd[1] # add or del + match param: + case 'add': + if admin_obj: + if admin_obj.fingerprint is not None: + query = f'UPDATE {self.ctx.Config.TABLE_ADMIN} SET fingerprint = :fingerprint WHERE user = :user' + r = await self.ctx.Base.db_execute_query(query, {'fingerprint': admin_obj.fingerprint, 'user': admin_obj.account}) + if r.rowcount > 0: + await self.Protocol.send_notice(dnickname, fromuser, f'[ {GREEN}CERT{NOGC} ] Your new fingerprint has been attached to your account. {admin_obj.fingerprint}') + else: + await self.Protocol.send_notice(dnickname, fromuser, f'[ {RED}CERT{NOGC} ] Impossible to add your fingerprint.{admin_obj.fingerprint}') + else: + await self.Protocol.send_notice(dnickname, fromuser, f'[ {RED}CERT{NOGC} ] There is no fingerprint to add.') + case 'del': + if admin_obj: + query = f"UPDATE {self.ctx.Config.TABLE_ADMIN} SET fingerprint = :fingerprint WHERE user =:user" + r = await self.ctx.Base.db_execute_query(query, {'fingerprint': None, 'user': admin_obj.account}) + if r.rowcount > 0: + await self.Protocol.send_notice(dnickname, fromuser, f'[ {GREEN}CERT{NOGC} ] Your fingerprint has been removed from your account. {admin_obj.fingerprint}') + else: + await self.Protocol.send_notice(dnickname, fromuser, f'[ {RED}CERT{NOGC} ] Impossible to remove your fingerprint.{admin_obj.fingerprint}') + case _: + await self.Protocol.send_notice(dnickname, fromuser, f"Right command : /msg {dnickname} cert add") + await self.Protocol.send_notice(dnickname, fromuser, f"Right command : /msg {dnickname} cert del") + return None except Exception as e: - self.Logs.error(e) + self.ctx.Logs.error(e) case 'register': # Syntax. Register PASSWORD EMAIL try: if len(cmd) < 3: - self.Protocol.send_notice( + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, - msg=f'/msg {self.Config.SERVICE_NICKNAME} {command.upper()} ' + msg=f'/msg {self.ctx.Config.SERVICE_NICKNAME} {command.upper()} ' ) return None password = cmd[1] email = cmd[2] - if not self.Base.is_valid_email(email_to_control=email): - self.Protocol.send_notice( + if not self.ctx.Base.is_valid_email(email_to_control=email): + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg='The email is not valid. You must provide a valid email address (first.name@email.extension)' ) return None - user_obj = self.User.get_user(fromuser) + user_obj = u if user_obj is None: - self.Logs.error(f"Nickname ({fromuser}) doesn't exist, it is impossible to register this nickname") + self.ctx.Logs.error(f"Nickname ({fromuser}) doesn't exist, it is impossible to register this nickname") return None # If the account already exist. - if self.Client.db_is_account_exist(fromuser): - self.Protocol.send_notice( + if self.ctx.Client.db_is_account_exist(fromuser): + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, - msg=f"Your account already exist, please try to login instead /msg {self.Config.SERVICE_NICKNAME} IDENTIFY " + msg=f"Your account already exist, please try to login instead /msg {self.ctx.Config.SERVICE_NICKNAME} IDENTIFY " ) return None # If the account doesn't exist then insert into database data_to_record = { - 'createdOn': self.Utils.get_sdatetime(), 'account': fromuser, + 'createdOn': self.ctx.Utils.get_sdatetime(), 'account': fromuser, 'nickname': user_obj.nickname, 'hostname': user_obj.hostname, 'vhost': user_obj.vhost, 'realname': user_obj.realname, 'email': email, - 'password': self.Loader.Utils.hash_password(password=password), 'level': 0 + 'password': self.ctx.Utils.hash_password(password=password), 'level': 0 } - insert_to_db = self.Base.db_execute_query(f""" - INSERT INTO {self.Config.TABLE_CLIENT} + insert_to_db = await self.ctx.Base.db_execute_query(f""" + INSERT INTO {self.ctx.Config.TABLE_CLIENT} (createdOn, account, nickname, hostname, vhost, realname, email, password, level) VALUES (:createdOn, :account, :nickname, :hostname, :vhost, :realname, :email, :password, :level) """, data_to_record) if insert_to_db.rowcount > 0: - self.Protocol.send_notice( + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg=f"You have register your nickname successfully" @@ -922,171 +845,194 @@ class Irc: return None except ValueError as ve: - self.Logs.error(f"Value Error : {ve}") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {self.Config.SERVICE_PREFIX}{command.upper()} ") + self.ctx.Logs.error(f"Value Error : {ve}") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {self.ctx.Config.SERVICE_PREFIX}{command.upper()} ") case 'identify': # Identify ACCOUNT PASSWORD try: if len(cmd) < 3: - self.Protocol.send_notice( + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, - msg=f'/msg {self.Config.SERVICE_NICKNAME} {command.upper()} ' + msg=f'/msg {self.ctx.Config.SERVICE_NICKNAME} {command.upper()} ' ) return None account = str(cmd[1]) # account - encrypted_password = self.Loader.Utils.hash_password(cmd[2]) - user_obj = self.User.get_user(fromuser) - client_obj = self.Client.get_Client(user_obj.uid) + encrypted_password = self.ctx.Utils.hash_password(cmd[2]) + user_obj = u + client_obj = c if client_obj is not None: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You are already logged in") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You are already logged in") return None - db_query = f"SELECT account FROM {self.Config.TABLE_CLIENT} WHERE account = :account AND password = :password" + db_query = f"SELECT account FROM {self.ctx.Config.TABLE_CLIENT} WHERE account = :account AND password = :password" db_param = {'account': account, 'password': encrypted_password} - exec_query = self.Base.db_execute_query(db_query, db_param) + exec_query = await self.ctx.Base.db_execute_query(db_query, db_param) result_query = exec_query.fetchone() if result_query: account = result_query[0] - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You are now logged in") - client = self.Loader.Definition.MClient(**user_obj.to_dict(), account=account) - self.Client.insert(client) - self.Protocol.send_svslogin(user_obj.uid, account) - self.Protocol.send_svs2mode(nickname=fromuser, user_mode='+r') + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You are now logged in") + client = self.ctx.Definition.MClient(**user_obj.to_dict(), account=account) + self.ctx.Client.insert(client) + await self.Protocol.send_svslogin(user_obj.uid, account) + await self.Protocol.send_svs2mode(nickname=fromuser, user_mode='+r') else: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Wrong password or account") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Wrong password or account") return None except ValueError as ve: - self.Logs.error(f"Value Error: {ve}") - self.Protocol.send_notice( + self.ctx.Logs.error(f"Value Error: {ve}") + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, - msg=f'/msg {self.Config.SERVICE_NICKNAME} {command.upper()} ' + msg=f'/msg {self.ctx.Config.SERVICE_NICKNAME} {command.upper()} ' ) except Exception as err: - self.Logs.error(f"General Error: {err}") + self.ctx.Logs.error(f"General Error: {err}") case 'logout': try: # LOGOUT if len(cmd) < 2: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} ") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} ") return None - user_obj = self.User.get_user(fromuser) + user_obj = u if user_obj is None: - self.Logs.error(f"The User [{fromuser}] is not available in the database") + self.ctx.Logs.error(f"The User [{fromuser}] is not available in the database") return None - client_obj = self.Client.get_Client(user_obj.uid) + client_obj = c if client_obj is None: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="Nothing to logout. please login first") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="Nothing to logout. please login first") return None - self.Protocol.send_svslogout(client_obj) - # self.Protocol.send_svsmode(nickname=fromuser, user_mode='-r') - self.Client.delete(user_obj.uid) - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You have been logged out successfully") + await self.Protocol.send_svslogout(client_obj) + self.ctx.Client.delete(user_obj.uid) + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You have been logged out successfully") except ValueError as ve: - self.Logs.error(f"Value Error: {ve}") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} ") + self.ctx.Logs.error(f"Value Error: {ve}") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} ") except Exception as err: - self.Logs.error(f"General Error: {err}") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} ") + self.ctx.Logs.error(f"General Error: {err}") + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} ") case 'help': # Syntax. !help [module_name] module_name = str(cmd[1]) if len(cmd) == 2 else None - self.generate_help_menu(nickname=fromuser, module=module_name) + await self.generate_help_menu(nickname=fromuser, module=module_name) return None case 'load': try: # Load a module ex: .load mod_defender + if len(cmd) < 2: + await self.Protocol.send_notice(dnickname, fromuser, tr("Syntax. /msg %s %s MODULE_NAME", dnickname, command.upper())) + return None + mod_name = str(cmd[1]) - self.ModuleUtils.load_one_module(self, mod_name, fromuser) + await self.ctx.ModuleUtils.load_one_module(mod_name, fromuser) return None except KeyError as ke: - self.Logs.error(f"Key Error: {ke} - list recieved: {cmd}") + self.ctx.Logs.error(f"Key Error: {ke} - list recieved: {cmd}") except Exception as err: - self.Logs.error(f"General Error: {err} - list recieved: {cmd}", exc_info=True) + self.ctx.Logs.error(f"General Error: {err} - list recieved: {cmd}", exc_info=True) case 'unload': # unload mod_defender try: # The module name. exemple: mod_defender + if len(cmd) < 2: + self.Protocol.send_notice(dnickname, fromuser, tr("Syntax. /msg %s %s MODULE_NAME", dnickname, command.upper())) + return None module_name = str(cmd[1]).lower() - self.ModuleUtils.unload_one_module(self, module_name, False) + await self.ctx.ModuleUtils.unload_one_module(module_name, False) return None except Exception as err: - self.Logs.error(f"General Error: {err}") + self.ctx.Logs.error(f"General Error: {err}") case 'reload': # reload mod_defender try: # ==> mod_defender + if len(cmd) < 2: + await self.Protocol.send_notice(dnickname, fromuser, tr("Syntax. /msg %s %s MODULE_NAME", dnickname, command.upper())) + return None + module_name = str(cmd[1]).lower() - self.ModuleUtils.reload_one_module(self, module_name, fromuser) + await self.ctx.ModuleUtils.reload_one_module(module_name, fromuser) return None except Exception as e: - self.Logs.error(f"Something went wrong with a module you want to reload: {e}") - self.Protocol.send_priv_msg( + self.ctx.Logs.error(f"Something went wrong with a module you want to reload: {e}") + await self.Protocol.send_priv_msg( nick_from=dnickname, msg=f"Something went wrong with the module: {e}", channel=dchanlog ) - self.ModuleUtils.db_delete_module(module_name) + await self.ctx.ModuleUtils.db_delete_module(module_name) case 'quit': try: final_reason = ' '.join(cmd[1:]) self.hb_active = False - self.Base.shutdown() - self.Base.execute_periodic_action() + await self.ctx.Base.shutdown() + self.ctx.Base.execute_periodic_action() - for chan_name in self.Channel.UID_CHANNEL_DB: + for chan_name in self.ctx.Channel.UID_CHANNEL_DB: # self.Protocol.send_mode_chan(chan_name.name, '-l') - self.Protocol.send_set_mode('-l', channel_name=chan_name.name) + await self.Protocol.send_set_mode('-l', channel_name=chan_name.name) - for client in self.Client.CLIENT_DB: - self.Protocol.send_svslogout(client) + for client in self.ctx.Client.CLIENT_DB: + await self.Protocol.send_svslogout(client) - self.Protocol.send_notice( + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg=f"Arrêt du service {dnickname}" ) - self.Protocol.send_squit(server_id=self.Config.SERVEUR_ID, server_link=self.Config.SERVEUR_LINK, reason=final_reason) - self.Logs.info(f'Arrêt du server {dnickname}') - self.Config.DEFENDER_RESTART = 0 self.signal = False + await self.Protocol.send_squit(server_id=self.ctx.Config.SERVEUR_ID, server_link=self.ctx.Config.SERVEUR_LINK, reason=final_reason) + self.ctx.Logs.info(f'Arrêt du server {dnickname}') + self.ctx.Config.DEFENDER_RESTART = 0 + + await self.writer.drain() + self.writer.close() + await self.writer.wait_closed() except IndexError as ie: - self.Logs.error(f'{ie}') + self.ctx.Logs.error(f'{ie}') + except ConnectionResetError: + if self.writer.is_closing(): + self.ctx.Logs.debug(f"Defender stopped properly!") case 'restart': final_reason = ' '.join(cmd[1:]) - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{dnickname.capitalize()} is going to restart!") - self.Config.DEFENDER_RESTART = 1 # Set restart status to 1 saying that the service will restart - self.Config.DEFENDER_INIT = 1 # set init to 1 saying that the service will be re initiated + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{dnickname.capitalize()} is going to restart!") + + # Set restart status to 1 saying that the service will restart + self.ctx.Config.DEFENDER_RESTART = 1 + + # set init to 1 saying that the service will be re initiated + self.ctx.Config.DEFENDER_INIT = 1 + + await rehash.restart_service(self.ctx) case 'rehash': - rehash.rehash_service(self, fromuser) + await rehash.rehash_service(self.ctx, fromuser) return None case 'show_modules': - self.Logs.debug('List of modules: ' + ', '.join([module.module_name for module in self.ModuleUtils.model_get_loaded_modules()])) - all_modules = self.ModuleUtils.get_all_available_modules() + self.ctx.Logs.debug('List of modules: ' + ', '.join([module.module_name for module in self.ctx.ModuleUtils.model_get_loaded_modules()])) + all_modules = self.ctx.ModuleUtils.get_all_available_modules() loaded = False - results = self.Base.db_execute_query(f'SELECT datetime, user, module_name FROM {self.Config.TABLE_MODULE}') + results = await self.ctx.Base.db_execute_query(f'SELECT datetime, user, module_name FROM {self.ctx.Config.TABLE_MODULE}') results = results.fetchall() for module in all_modules: @@ -1097,29 +1043,29 @@ class Irc: loaded = True if loaded: - self.Protocol.send_notice( + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg=tr('%s - %sLoaded%s by %s on %s', module, GREEN, NOGC, loaded_user, loaded_datetime) ) loaded = False else: - self.Protocol.send_notice( + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg=tr('%s - %sNot Loaded%s', module, RED, NOGC) ) case 'show_timers': - if self.Base.running_timers: - for the_timer in self.Base.running_timers: - self.Protocol.send_notice( + if self.ctx.Base.running_timers: + for the_timer in self.ctx.Base.running_timers: + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg=f">> {the_timer.name} - {the_timer.is_alive()}" ) else: - self.Protocol.send_notice( + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg="There is no timers that are running!" @@ -1127,23 +1073,33 @@ class Irc: return None case 'show_threads': - for thread in self.Base.running_threads: - self.Protocol.send_notice( + for thread in self.ctx.Base.running_threads: + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg=f">> {thread.name} ({thread.is_alive()})" ) + + return None + + case 'show_asyncio': + for task in asyncio.all_tasks(): + await self.Protocol.send_notice( + nick_from=dnickname, + nick_to=fromuser, + msg=f">> {task.get_name()} (active)" + ) return None case 'show_channels': - for chan in self.Channel.UID_CHANNEL_DB: + for chan in self.ctx.Channel.UID_CHANNEL_DB: list_nicknames: list = [] for uid in chan.uids: pattern = fr'[:|@|%|\+|~|\*]*' parsed_UID = re.sub(pattern, '', uid) - list_nicknames.append(self.User.get_nickname(parsed_UID)) + list_nicknames.append(self.ctx.User.get_nickname(parsed_UID)) - self.Protocol.send_notice( + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg=f"Channel: {chan.name} - Users: {list_nicknames}" @@ -1151,10 +1107,10 @@ class Irc: return None case 'show_users': - count_users = len(self.User.UID_DB) - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Total Connected Users: {count_users}") - for db_user in self.User.UID_DB: - self.Protocol.send_notice( + count_users = len(self.ctx.User.UID_DB) + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Total Connected Users: {count_users}") + for db_user in self.ctx.User.UID_DB: + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg=f"UID : {db_user.uid} - isWebirc: {db_user.isWebirc} - isWebSocket: {db_user.isWebsocket} - Nickname: {db_user.nickname} - Connection: {db_user.connexion_datetime}" @@ -1162,10 +1118,10 @@ class Irc: return None case 'show_clients': - count_users = len(self.Client.CLIENT_DB) - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Total Connected Clients: {count_users}") - for db_client in self.Client.CLIENT_DB: - self.Protocol.send_notice( + count_users = len(self.ctx.Client.CLIENT_DB) + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Total Connected Clients: {count_users}") + for db_client in self.ctx.Client.CLIENT_DB: + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg=f"UID : {db_client.uid} - isWebirc: {db_client.isWebirc} - isWebSocket: {db_client.isWebsocket} - Nickname: {db_client.nickname} - Account: {db_client.account} - Connection: {db_client.connexion_datetime}" @@ -1173,8 +1129,8 @@ class Irc: return None case 'show_admins': - for db_admin in self.Admin.UID_ADMIN_DB: - self.Protocol.send_notice( + for db_admin in self.ctx.Admin.UID_ADMIN_DB: + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg=f"UID : {db_admin.uid} - Nickname: {db_admin.nickname} - Account: {db_admin.account} - Level: {db_admin.level} - Language: {db_admin.language} - Connection: {db_admin.connexion_datetime}" @@ -1182,8 +1138,8 @@ class Irc: return None case 'show_configuration': - for key, value in self.Config.to_dict().items(): - self.Protocol.send_notice( + for key, value in self.ctx.Config.to_dict().items(): + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg=f'{key} = {value}' @@ -1191,9 +1147,9 @@ class Irc: return None case 'show_cache': - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"The cache is currently contains {self.Settings.get_cache_size()} value(s).") - for key, value in self.Settings.show_cache().items(): - self.Protocol.send_notice( + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"The cache is currently contains {self.ctx.Settings.get_cache_size()} value(s).") + for key, value in self.ctx.Settings.show_cache().items(): + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg=f"Key : {key} - Value: {value}" @@ -1201,37 +1157,63 @@ class Irc: return None case 'clear_cache': - cache_size = self.Settings.get_cache_size() + cache_size = self.ctx.Settings.get_cache_size() if cache_size > 0: - self.Settings.clear_cache() - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{cache_size} value(s) has been cleared from the cache.") + self.ctx.Settings.clear_cache() + await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{cache_size} value(s) has been cleared from the cache.") return None case 'uptime': uptime = self.get_defender_uptime() - self.Protocol.send_notice( + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, - msg=f"{uptime}" + msg=uptime ) return None case 'copyright': - self.Protocol.send_notice( + await self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, - msg=f">> Defender V.{self.Config.CURRENT_VERSION} Developped by adator®." + msg=f">> Defender V{self.ctx.Config.CURRENT_VERSION} Developped by adator®." ) return None case 'checkversion': - self.Base.create_thread(self.thread_check_for_new_version, (fromuser, )) + self.ctx.Base.create_asynctask(self.thread_check_for_new_version(fromuser)) return None case 'raw': raw_command = ' '.join(cmd[1:]) - self.Protocol.send_raw(raw_command) + await self.Protocol.send_raw(raw_command) return None + case 'print_vars': + with open('users.txt', 'w') as fw: + i = 1 + for u in self.ctx.User.UID_DB: + w = fw.write(u.to_dict().__str__() + "\n") + self.ctx.Logs.debug(f" {i} - chars written {w}") + i += 1 + await self.Protocol.send_priv_msg(dnickname, "Data written in users.txt file", dchanlog) + + with open('modules.txt', 'w') as fw: + i = 1 + for u in self.ctx.ModuleUtils.DB_MODULE_HEADERS: + w = fw.write(u.to_dict().__str__() + "\n") + self.ctx.Logs.debug(f" {i} - chars written {w}") + i += 1 + await self.Protocol.send_priv_msg(dnickname, "Data written in modules.txt file", dchanlog) + + return None + + case 'start_rpc': + self.ctx.Base.create_asynctask(self.ctx.RpcServer.start_server()) + + case 'stop_rpc': + self.ctx.Base.create_asynctask(self.ctx.RpcServer.stop_server()) + case _: pass + diff --git a/core/language/fr/core-fr.yaml b/core/language/fr/core-fr.yaml index d879cfb..725e866 100644 --- a/core/language/fr/core-fr.yaml +++ b/core/language/fr/core-fr.yaml @@ -2,8 +2,16 @@ traduction: # Message help - orig: "Access denied!" trad: "Accès refusé." + - orig: "Wrong password!" + trad: "Mot de passe incorrect!" - orig: "%s - %sLoaded%s by %s on %s" trad: "%s - %sChargé%s par %s le %s" + - orig: "Module %s loaded!" + trad: "Module %s chargé!" + - orig: "cmd method is not available in the module (%s)" + trad: "La méthode cmd n'est pas disponible dans le module (%s)" + - orig: "[%sMODULE ERROR%s] Module %s is facing issues! %s" + trad: "[%sMODULE ERREUR%s] Le module %s a rencontré une erreur! %s" - orig: "%s - %sNot Loaded%s" trad: "%s - %sNon chargé%s" - orig: "Successfuly connected to %s" diff --git a/core/loader.py b/core/loader.py index 4953940..611fc9f 100644 --- a/core/loader.py +++ b/core/loader.py @@ -1,18 +1,28 @@ from logging import Logger -from core.classes.settings import global_settings -from core.classes import translation, user, admin, client, channel, reputation, settings, sasl +from core.classes.modules.settings import global_settings +from core.classes.modules import translation, user, admin, client, channel, reputation, settings, sasl import core.logs as logs import core.definition as df import core.utils as utils import core.base as base_mod import core.module as module_mod -import core.classes.commands as commands_mod -import core.classes.config as conf_mod +import core.classes.modules.commands as commands_mod +import core.classes.modules.config as conf_mod +import core.classes.modules.rpc.rpc as rpc_mod import core.irc as irc import core.classes.protocols.factory as factory class Loader: + _instance = None + + def __new__(cls, *agrs): + + if cls._instance is None: + cls._instance = super().__new__(cls) + + return cls._instance + def __init__(self): # Load Main Modules @@ -26,6 +36,8 @@ class Loader: self.LoggingModule: logs = logs + self.RpcServerModule: rpc_mod = rpc_mod + self.Utils: utils = utils # Load Classes @@ -67,6 +79,11 @@ class Loader: self.Irc: irc.Irc = irc.Irc(self) - self.PFactory: factory.ProtocolFactorty = factory.ProtocolFactorty(self.Irc) + self.PFactory: factory.ProtocolFactorty = factory.ProtocolFactorty(self) + + self.RpcServer: rpc_mod.JSonRpcServer = rpc_mod.JSonRpcServer(self) self.Logs.debug(self.Utils.tr("Loader %s success", __name__)) + + async def start(self): + await self.Base.init() diff --git a/core/module.py b/core/module.py index e4317e4..4d3b0ee 100644 --- a/core/module.py +++ b/core/module.py @@ -1,27 +1,26 @@ ''' This is the main operational file to handle modules ''' -from pathlib import Path import sys import importlib +from pathlib import Path from types import ModuleType from typing import TYPE_CHECKING, Optional -from core.definition import MModule +from core.definition import DefenderModuleHeader, MModule +from core.utils import tr if TYPE_CHECKING: from core.loader import Loader from core.irc import Irc + from core.classes.interfaces.imodule import IModule class Module: DB_MODULES: list[MModule] = [] + DB_MODULE_HEADERS: list[DefenderModuleHeader] = [] def __init__(self, loader: 'Loader') -> None: - self.__Loader = loader - self.__Base = loader.Base - self.__Logs = loader.Logs - self.__Utils = loader.Utils - self.__Config = loader.Config + self._ctx = loader def get_all_available_modules(self) -> list[str]: """Get list of all main modules @@ -31,71 +30,126 @@ class Module: list[str]: List of all module names. """ base_path = Path('mods') - return [file.name.replace('.py', '') for file in base_path.rglob('mod_*.py')] + modules_available = [file.name.replace('.py', '') for file in base_path.rglob('mod_*.py')] + self._ctx.Logs.debug(f"Modules available: {modules_available}") + return modules_available - def get_module_information(self, module_name: str) -> tuple[str, str, str]: + def get_module_information(self, module_name: str) -> tuple[Optional[str], Optional[str], Optional[str]]: # module_name : mod_defender if not module_name.lower().startswith('mod_'): return None, None, None - module_name = module_name.lower() + module_name = module_name.lower() # --> mod_defender module_folder = module_name.split('_')[1].lower() # --> defender class_name = module_name.split('_')[1].capitalize() # --> Defender + self._ctx.Logs.debug(f"Module information Folder: {module_folder}, Name: {module_name}, Class: {class_name}") return module_folder, module_name, class_name - def load_one_module(self, uplink: 'Irc', module_name: str, nickname: str, is_default: bool = False) -> bool: + def get_module_header(self, module_name: str) -> Optional[DefenderModuleHeader]: + + for mod_h in self.DB_MODULE_HEADERS: + if module_name.lower() == mod_h.name.lower(): + self._ctx.Logs.debug(f"Module Header found: {mod_h}") + return mod_h + + return None + + def create_module_header(self, module_header: dict[str, str]) -> bool: + """Create a new module header into DB_MODULE_HEADERS + + Args: + module_header (dict[str, str]): The module header + + Returns: + bool: True if the module header has been created. + """ + mod_header = DefenderModuleHeader(**module_header) + if self.get_module_header(mod_header.name) is None: + self._ctx.Logs.debug(f"[MOD_HEADER] The module header has been created! ({mod_header.name} v{mod_header.version})") + self.DB_MODULE_HEADERS.append(mod_header) + return True + + return False + + def delete_module_header(self, module_name: str) -> bool: + mod_header = self.get_module_header(module_name) + if mod_header is not None: + self._ctx.Logs.debug(f"[MOD_HEADER] The module header has been deleted ({mod_header.name} v{mod_header.version})") + self.DB_MODULE_HEADERS.remove(mod_header) + return True + + self._ctx.Logs.debug(f"[MOD_HEADER ERROR] Impossible to remove the module header ({module_name})") + return False + + async def load_one_module(self, module_name: str, nickname: str, is_default: bool = False) -> bool: module_folder, module_name, class_name = self.get_module_information(module_name) if module_folder is None or module_name is None or class_name is None: - self.__Logs.error(f"There is an error with the module name! {module_folder}, {module_name}, {class_name}") + self._ctx.Logs.error(f"There is an error with the module name! {module_folder}, {module_name}, {class_name}") return False if self.is_module_exist_in_sys_module(module_name): - self.__Logs.debug(f"Module [{module_folder}.{module_name}] already loaded!") + self._ctx.Logs.debug(f"Module [{module_folder}.{module_name}] already loaded!") if self.model_is_module_exist(module_name): # Si le module existe dans la variable globale retourne False - self.__Logs.debug(f"Module [{module_folder}.{module_name}] exist in the local variable!") - uplink.Protocol.send_priv_msg( - nick_from=self.__Config.SERVICE_NICKNAME, - msg=f"Le module {module_name} est déja chargé ! si vous souhaiter le recharge tapez {self.__Config.SERVICE_PREFIX}reload {module_name}", - channel=self.__Config.SERVICE_CHANLOG + self._ctx.Logs.debug(f"Module [{module_folder}.{module_name}] exist in the local variable!") + await self._ctx.Irc.Protocol.send_priv_msg( + nick_from=self._ctx.Config.SERVICE_NICKNAME, + msg=f"Le module {module_name} est déja chargé ! si vous souhaiter le recharge tapez {self._ctx.Config.SERVICE_PREFIX}reload {module_name}", + channel=self._ctx.Config.SERVICE_CHANLOG ) return False - return self.reload_one_module(uplink, module_name, nickname) + return self.reload_one_module(module_name, nickname) # Charger le module - loaded_module = importlib.import_module(f'mods.{module_folder}.{module_name}') - my_class = getattr(loaded_module, class_name, None) # Récuperer le nom de classe - create_instance_of_the_class = my_class(uplink) # Créer une nouvelle instance de la classe + try: + loaded_module = importlib.import_module(f'mods.{module_folder}.{module_name}') + my_class = getattr(loaded_module, class_name, None) # Récuperer le nom de classe + create_instance_of_the_class: 'IModule' = my_class(self._ctx) # Créer une nouvelle instance de la classe + await create_instance_of_the_class.load() if self._ctx.Utils.is_coroutinefunction(create_instance_of_the_class.load) else create_instance_of_the_class.load() + self.create_module_header(create_instance_of_the_class.MOD_HEADER) + except AttributeError as attr: + red = self._ctx.Config.COLORS.red + nogc = self._ctx.Config.COLORS.red + await self._ctx.Irc.Protocol.send_priv_msg( + nick_from=self._ctx.Config.SERVICE_NICKNAME, + msg=tr("[%sMODULE ERROR%s] Module %s is facing issues! %s", red, nogc, module_name, attr), + channel=self._ctx.Config.SERVICE_CHANLOG + ) + self._ctx.Logs.error(msg=attr, exc_info=True) + return False if not hasattr(create_instance_of_the_class, 'cmd'): - uplink.Protocol.send_priv_msg( - nick_from=self.__Config.SERVICE_NICKNAME, - msg=f"Module {module_name} ne contient pas de méthode cmd", - channel=self.__Config.SERVICE_CHANLOG + await self._ctx.Irc.Protocol.send_priv_msg( + nick_from=self._ctx.Config.SERVICE_NICKNAME, + msg=tr("cmd method is not available in the module (%s)", module_name), + channel=self._ctx.Config.SERVICE_CHANLOG ) - self.__Logs.critical(f"The Module {module_name} has not been loaded because cmd method is not available") - self.db_delete_module(module_name) + self._ctx.Logs.critical(f"The Module {module_name} has not been loaded because cmd method is not available") + await self.db_delete_module(module_name) return False # Charger la nouvelle class dans la variable globale if self.model_insert_module(MModule(module_name, class_name, create_instance_of_the_class)): # Enregistrer le module dans la base de données - self.db_register_module(module_name, nickname, is_default) - uplink.Protocol.send_priv_msg( - nick_from=self.__Config.SERVICE_NICKNAME, - msg=f"Module {module_name} chargé", - channel=self.__Config.SERVICE_CHANLOG + await self.db_register_module(module_name, nickname, is_default) + await self._ctx.Irc.Protocol.send_priv_msg( + nick_from=self._ctx.Config.SERVICE_NICKNAME, + msg=tr("Module %s loaded!", module_name), + channel=self._ctx.Config.SERVICE_CHANLOG ) - self.__Logs.debug(f"Module {class_name} has been loaded") + self._ctx.Logs.debug(f"Module {class_name} has been loaded") + return True + + return False def load_all_modules(self) -> bool: ... - def reload_one_module(self, uplink: 'Irc', module_name: str, nickname: str) -> bool: + async def reload_one_module(self, module_name: str, nickname: str) -> bool: """Reloading one module and insert it into the model as well as the database Args: @@ -107,20 +161,21 @@ class Module: bool: True if the module has been reloaded """ module_folder, module_name, class_name = self.get_module_information(module_name) - red = self.__Config.COLORS.red - nogc = self.__Config.COLORS.nogc + red = self._ctx.Config.COLORS.red + nogc = self._ctx.Config.COLORS.nogc try: if self.is_module_exist_in_sys_module(module_name): module_model = self.model_get_module(module_name) if module_model: - module_model.class_instance.unload() + self.delete_module_header(module_model.class_instance.MOD_HEADER['name']) + await module_model.class_instance.unload() if self._ctx.Utils.is_coroutinefunction(module_model.class_instance.unload) else module_model.class_instance.unload() else: - uplink.Protocol.send_priv_msg( - nick_from=self.__Config.SERVICE_NICKNAME, - msg=f"[ {red}RELOAD MODULE ERROR{nogc} ] Module [{module_folder}.{module_name}] hasn't been reloaded! You must use {self.__Config.SERVICE_PREFIX}load {module_name}", - channel=self.__Config.SERVICE_CHANLOG + await self._ctx.Irc.Protocol.send_priv_msg( + nick_from=self._ctx.Config.SERVICE_NICKNAME, + msg=f"[ {red}RELOAD MODULE ERROR{nogc} ] Module [{module_folder}.{module_name}] hasn't been reloaded! You must use {self._ctx.Config.SERVICE_PREFIX}load {module_name}", + channel=self._ctx.Config.SERVICE_CHANLOG ) - self.__Logs.debug(f"Module [{module_folder}.{module_name}] not found! Please use {self.__Config.SERVICE_PREFIX}load {module_name}") + self._ctx.Logs.debug(f"Module [{module_folder}.{module_name}] not found! Please use {self._ctx.Config.SERVICE_PREFIX}load {module_name}") return False # reload module dependencies @@ -129,36 +184,38 @@ class Module: the_module = sys.modules[f'mods.{module_folder}.{module_name}'] importlib.reload(the_module) my_class = getattr(the_module, class_name, None) - new_instance = my_class(uplink) + new_instance: 'IModule' = my_class(self._ctx) + await new_instance.load() if self._ctx.Utils.is_coroutinefunction(new_instance.load) else new_instance.load() + self.create_module_header(new_instance.MOD_HEADER) module_model.class_instance = new_instance # Créer le module dans la base de données - self.db_register_module(module_name, nickname) - uplink.Protocol.send_priv_msg( - nick_from=self.__Config.SERVICE_NICKNAME, + await self.db_register_module(module_name, nickname) + await self._ctx.Irc.Protocol.send_priv_msg( + nick_from=self._ctx.Config.SERVICE_NICKNAME, msg=f"Module [{module_folder}.{module_name}] has been reloaded!", - channel=self.__Config.SERVICE_CHANLOG + channel=self._ctx.Config.SERVICE_CHANLOG ) - self.__Logs.debug(f"Module [{module_folder}.{module_name}] reloaded!") + self._ctx.Logs.debug(f"Module [{module_folder}.{module_name}] reloaded!") return True else: # Module is not loaded! Nothing to reload - self.__Logs.debug(f"[RELOAD MODULE ERROR] [{module_folder}.{module_name}] is not loaded! You must use {self.__Config.SERVICE_PREFIX}load {module_name}") - uplink.Protocol.send_priv_msg( - nick_from=self.__Config.SERVICE_NICKNAME, - msg=f"[ {red}RELOAD MODULE ERROR{nogc} ] Module [{module_folder}.{module_name}] is not loaded! You must use {self.__Config.SERVICE_PREFIX}load {module_name}", - channel=self.__Config.SERVICE_CHANLOG + self._ctx.Logs.debug(f"[RELOAD MODULE ERROR] [{module_folder}.{module_name}] is not loaded! You must use {self._ctx.Config.SERVICE_PREFIX}load {module_name}") + await self._ctx.Irc.Protocol.send_priv_msg( + nick_from=self._ctx.Config.SERVICE_NICKNAME, + msg=f"[ {red}RELOAD MODULE ERROR{nogc} ] Module [{module_folder}.{module_name}] is not loaded! You must use {self._ctx.Config.SERVICE_PREFIX}load {module_name}", + channel=self._ctx.Config.SERVICE_CHANLOG ) return False except (TypeError, AttributeError, KeyError, Exception) as err: - self.__Logs.error(f"[RELOAD MODULE ERROR]: {err}") - uplink.Protocol.send_priv_msg( - nick_from=self.__Config.SERVICE_NICKNAME, + self._ctx.Logs.error(f"[RELOAD MODULE ERROR]: {err}", exc_info=True) + await self._ctx.Irc.Protocol.send_priv_msg( + nick_from=self._ctx.Config.SERVICE_NICKNAME, msg=f"[RELOAD MODULE ERROR]: {err}", - channel=self.__Config.SERVICE_CHANLOG + channel=self._ctx.Config.SERVICE_CHANLOG ) - self.db_delete_module(module_name) + await self.db_delete_module(module_name) def reload_all_modules(self) -> bool: ... @@ -184,38 +241,41 @@ class Module: try: if 'mod_' not in name and 'schemas' not in name: importlib.reload(module) - self.__Logs.debug(f'[LOAD_MODULE] Module {module} success') + self._ctx.Logs.debug(f'[LOAD_MODULE] Module {module} success') except Exception as err: - self.__Logs.error(f'[LOAD_MODULE] Module {module} failed [!] - {err}') + self._ctx.Logs.error(f'[LOAD_MODULE] Module {module} failed [!] - {err}') - def unload_one_module(self, uplink: 'Irc', module_name: str, keep_in_db: bool = True) -> bool: + async def unload_one_module(self, module_name: str, keep_in_db: bool = True) -> bool: """Unload a module Args: - mod_name (str): Module name ex mod_defender + uplink (Irc): The Irc instance + module_name (str): Module name ex mod_defender + keep_in_db (bool): Keep in database Returns: bool: True if success """ try: # Le nom du module. exemple: mod_defender - red = self.__Config.COLORS.red - nogc = self.__Config.COLORS.nogc + red = self._ctx.Config.COLORS.red + nogc = self._ctx.Config.COLORS.nogc module_folder, module_name, class_name = self.get_module_information(module_name) module = self.model_get_module(module_name) if module is None: - self.__Logs.debug(f"[ UNLOAD MODULE ERROR ] This module {module_name} is not loaded!") - self.db_delete_module(module_name) - uplink.Protocol.send_priv_msg( - nick_from=self.__Config.SERVICE_NICKNAME, + self._ctx.Logs.debug(f"[ UNLOAD MODULE ERROR ] This module {module_name} is not loaded!") + await self.db_delete_module(module_name) + await self._ctx.Irc.Protocol.send_priv_msg( + nick_from=self._ctx.Config.SERVICE_NICKNAME, msg=f"[ {red}UNLOAD MODULE ERROR{nogc} ] This module {module_name} is not loaded!", - channel=self.__Config.SERVICE_CHANLOG + channel=self._ctx.Config.SERVICE_CHANLOG ) return False if module: - module.class_instance.unload() + self.delete_module_header(module.class_instance.MOD_HEADER['name']) + await module.class_instance.unload() if self._ctx.Utils.is_coroutinefunction(module.class_instance.unload) else module.class_instance.unload() self.DB_MODULES.remove(module) # Delete from the sys.modules. @@ -223,25 +283,25 @@ class Module: del sys.modules[f"mods.{module_folder}.{module_name}"] if sys.modules.get(f'mods.{module_folder}.{module_name}'): - self.__Logs.debug(f"Module mods.{module_folder}.{module_name} still in the sys.modules") + self._ctx.Logs.debug(f"Module mods.{module_folder}.{module_name} still in the sys.modules") # Supprimer le module de la base de données if not keep_in_db: - self.db_delete_module(module_name) + await self.db_delete_module(module_name) - uplink.Protocol.send_priv_msg( - nick_from=self.__Config.SERVICE_NICKNAME, + await self._ctx.Irc.Protocol.send_priv_msg( + nick_from=self._ctx.Config.SERVICE_NICKNAME, msg=f"[ UNLOAD MODULE INFO ] Module {module_name} has been unloaded!", - channel=self.__Config.SERVICE_CHANLOG + channel=self._ctx.Config.SERVICE_CHANLOG ) - self.__Logs.debug(f"[ UNLOAD MODULE ] {module_name} has been unloaded!") + self._ctx.Logs.debug(f"[ UNLOAD MODULE ] {module_name} has been unloaded!") return True - self.__Logs.debug(f"[UNLOAD MODULE]: Module {module_name} not found in DB_MODULES variable!") + self._ctx.Logs.debug(f"[UNLOAD MODULE]: Module {module_name} not found in DB_MODULES variable!") return False except Exception as err: - self.__Logs.error(f"General Error: {err}") + self._ctx.Logs.error(f"General Error: {err}", exc_info=True) return False def unload_all_modules(self) -> bool: @@ -258,7 +318,9 @@ class Module: """ module_folder, module_name, class_name = self.get_module_information(module_name) if "mods." + module_folder + "." + module_name in sys.modules: + self._ctx.Logs.debug(f"[SYS MODULE] (mods.{module_folder}.{module_name}) found in sys.modules") return True + self._ctx.Logs.debug(f"[SYS MODULE] (mods.{module_folder}.{module_name}) not found in sys.modules") return False ''' @@ -275,10 +337,10 @@ class Module: """ for module in self.DB_MODULES: if module.module_name.lower() == module_name.lower(): - self.__Logs.debug(f"[MODEL MODULE GET] The module {module_name} has been found in the model DB_MODULES") + self._ctx.Logs.debug(f"[MODEL MODULE GET] The module {module_name} has been found in the model DB_MODULES") return module - self.__Logs.debug(f"[MODEL MODULE GET] The module {module_name} not found in the model DB_MODULES") + self._ctx.Logs.debug(f"[MODEL MODULE GET] The module {module_name} not found in the model DB_MODULES") return None def model_get_loaded_modules(self) -> list[MModule]: @@ -288,7 +350,7 @@ class Module: Returns: list[MModule]: A list of module model object """ - # self.__Logs.debug(f"[MODEL MODULE LOADED MODULES] {len(self.DB_MODULES)} modules found!") + # self._ctx.Logs.debug(f"[MODEL MODULE LOADED MODULES] {len(self.DB_MODULES)} modules found!") return self.DB_MODULES def model_insert_module(self, module_model: MModule) -> bool: @@ -303,17 +365,17 @@ class Module: module = self.model_get_module(module_model.module_name) if module is None: self.DB_MODULES.append(module_model) - self.__Logs.debug(f"[MODEL MODULE INSERT] The module {module_model.module_name} has been inserted in the local variable model DB_MODULES") + self._ctx.Logs.debug(f"[MODEL MODULE INSERT] The module {module_model.module_name} has been inserted in the local variable model DB_MODULES") return True - self.__Logs.debug(f"[MODEL MODULE INSERT] The module {module_model.module_name} already exist in the local variable model DB_MODULES") + self._ctx.Logs.debug(f"[MODEL MODULE INSERT] The module {module_model.module_name} already exist in the local variable model DB_MODULES") return False def model_clear(self) -> None: """Clear DB_MODULES list! """ self.DB_MODULES.clear() - self.__Logs.debug("[MODEL MODULE CLEAR] The local variable model DB_MODULES has been cleared") + self._ctx.Logs.debug("[MODEL MODULE CLEAR] The local variable model DB_MODULES has been cleared") return None def model_is_module_exist(self, module_name: str) -> bool: @@ -326,30 +388,30 @@ class Module: bool: True if the module_name exist """ if self.model_get_module(module_name): - self.__Logs.debug(f"[MODEL MODULE EXIST] The module {module_name} exist in the local model DB_MODULES!") + self._ctx.Logs.debug(f"[MODEL MODULE EXIST] The module {module_name} exist in the local model DB_MODULES!") return True - self.__Logs.debug(f"[MODEL MODULE EXIST] The module {module_name} is not available in the local model DB_MODULES!") + self._ctx.Logs.debug(f"[MODEL MODULE EXIST] The module {module_name} is not available in the local model DB_MODULES!") return False ''' OPERATION DEDICATED TO DATABASE MANAGEMENT ''' - def db_load_all_existing_modules(self, uplink: 'Irc') -> bool: + async def db_load_all_existing_modules(self) -> bool: """Charge les modules qui existe déja dans la base de données Returns: None: Aucun retour requis, elle charge puis c'est tout """ - self.__Logs.debug("[DB LOAD MODULE] Loading modules from the database!") - result = self.__Base.db_execute_query(f"SELECT module_name FROM {self.__Config.TABLE_MODULE}") + self._ctx.Logs.debug("[DB LOAD MODULE] Loading modules from the database!") + result = await self._ctx.Base.db_execute_query(f"SELECT module_name FROM {self._ctx.Config.TABLE_MODULE}") for r in result.fetchall(): - self.load_one_module(uplink, r[0], 'sys', True) + await self.load_one_module(r[0], 'sys', True) return True - def db_is_module_exist(self, module_name: str) -> bool: + async def db_is_module_exist(self, module_name: str) -> bool: """Check if the module exist in the database Args: @@ -358,18 +420,18 @@ class Module: Returns: bool: True if the module exist in the database """ - query = f"SELECT id FROM {self.__Config.TABLE_MODULE} WHERE module_name = :module_name" + query = f"SELECT id FROM {self._ctx.Config.TABLE_MODULE} WHERE module_name = :module_name" mes_donnes = {'module_name': module_name.lower()} - results = self.__Base.db_execute_query(query, mes_donnes) + results = await self._ctx.Base.db_execute_query(query, mes_donnes) if results.fetchall(): - self.__Logs.debug(f"[DB MODULE EXIST] The module {module_name} exist in the database!") + self._ctx.Logs.debug(f"[DB MODULE EXIST] The module {module_name} exist in the database!") return True else: - self.__Logs.debug(f"[DB MODULE EXIST] The module {module_name} is not available in the database!") + self._ctx.Logs.debug(f"[DB MODULE EXIST] The module {module_name} is not available in the database!") return False - def db_register_module(self, module_name: str, nickname: str, is_default: bool = False) -> bool: + async def db_register_module(self, module_name: str, nickname: str, is_default: bool = False) -> bool: """Insert a new module in the database Args: @@ -377,49 +439,49 @@ class Module: nickname (str): The user who loaded the module isdefault (int): Is this a default module. Default 0 """ - if not self.db_is_module_exist(module_name): - insert_cmd_query = f"INSERT INTO {self.__Config.TABLE_MODULE} (datetime, user, module_name, isdefault) VALUES (:datetime, :user, :module_name, :isdefault)" - mes_donnees = {'datetime': self.__Utils.get_sdatetime(), 'user': nickname, 'module_name': module_name.lower(), 'isdefault': is_default} - insert = self.__Base.db_execute_query(insert_cmd_query, mes_donnees) + if not await self.db_is_module_exist(module_name): + insert_cmd_query = f"INSERT INTO {self._ctx.Config.TABLE_MODULE} (datetime, user, module_name, isdefault) VALUES (:datetime, :user, :module_name, :isdefault)" + mes_donnees = {'datetime': self._ctx.Utils.get_sdatetime(), 'user': nickname, 'module_name': module_name.lower(), 'isdefault': is_default} + insert = await self._ctx.Base.db_execute_query(insert_cmd_query, mes_donnees) if insert.rowcount > 0: - self.__Logs.debug(f"[DB REGISTER MODULE] Module {module_name} has been inserted to the database!") + self._ctx.Logs.debug(f"[DB REGISTER MODULE] Module {module_name} has been inserted to the database!") return True else: - self.__Logs.debug(f"[DB REGISTER MODULE] Module {module_name} not inserted to the database!") + self._ctx.Logs.debug(f"[DB REGISTER MODULE] Module {module_name} not inserted to the database!") return False - self.__Logs.debug(f"[DB REGISTER MODULE] Module {module_name} already exist in the database! Nothing to insert!") + self._ctx.Logs.debug(f"[DB REGISTER MODULE] Module {module_name} already exist in the database! Nothing to insert!") return False - def db_update_module(self, module_name: str, nickname: str) -> None: + async def db_update_module(self, module_name: str, nickname: str) -> None: """Update the datetime and the user that updated the module Args: module_name (str): The module name to update nickname (str): The nickname who updated the module """ - update_cmd_query = f"UPDATE {self.__Config.TABLE_MODULE} SET datetime = :datetime, LOWER(user) = :user WHERE LOWER(module_name) = :module_name" - mes_donnees = {'datetime': self.__Utils.get_sdatetime(), 'user': nickname.lower(), 'module_name': module_name.lower()} - result = self.__Base.db_execute_query(update_cmd_query, mes_donnees) + update_cmd_query = f"UPDATE {self._ctx.Config.TABLE_MODULE} SET datetime = :datetime, LOWER(user) = :user WHERE LOWER(module_name) = :module_name" + mes_donnees = {'datetime': self._ctx.Utils.get_sdatetime(), 'user': nickname.lower(), 'module_name': module_name.lower()} + result = await self._ctx.Base.db_execute_query(update_cmd_query, mes_donnees) if result.rowcount > 0: - self.__Logs.debug(f"[DB UPDATE MODULE] Module {module_name} has been updated!") + self._ctx.Logs.debug(f"[DB UPDATE MODULE] Module {module_name} has been updated!") return True else: - self.__Logs.debug(f"[DB UPDATE MODULE] Module {module_name} not found! Nothing to update!") + self._ctx.Logs.debug(f"[DB UPDATE MODULE] Module {module_name} not found! Nothing to update!") return False - def db_delete_module(self, module_name:str) -> None: + async def db_delete_module(self, module_name:str) -> None: """Delete a module from the database Args: module_name (str): The module name you want to delete """ - insert_cmd_query = f"DELETE FROM {self.__Config.TABLE_MODULE} WHERE LOWER(module_name) = :module_name" + insert_cmd_query = f"DELETE FROM {self._ctx.Config.TABLE_MODULE} WHERE LOWER(module_name) = :module_name" mes_donnees = {'module_name': module_name.lower()} - delete = self.__Base.db_execute_query(insert_cmd_query, mes_donnees) + delete = await self._ctx.Base.db_execute_query(insert_cmd_query, mes_donnees) if delete.rowcount > 0: - self.__Logs.debug(f"[DB MODULE DELETE] The module {module_name} has been deleted from the dabatase!") + self._ctx.Logs.debug(f"[DB MODULE DELETE] The module {module_name} has been deleted from the dabatase!") return True - self.__Logs.debug(f"[DB MODULE DELETE] The module {module_name} is not available in the database! Nothing to delete!") + self._ctx.Logs.debug(f"[DB MODULE DELETE] The module {module_name} is not available in the database! Nothing to delete!") return False diff --git a/core/utils.py b/core/utils.py index bbfca6f..a887214 100644 --- a/core/utils.py +++ b/core/utils.py @@ -1,19 +1,19 @@ -''' +""" Main utils library. -''' +""" import gc import ssl import socket import sys from pathlib import Path from re import match, sub -from base64 import b64decode from typing import Literal, Optional, Any, TYPE_CHECKING -from datetime import datetime, timedelta, timezone +from datetime import datetime from time import time from random import choice from hashlib import md5, sha3_512 -from core.classes.settings import global_settings +from core.classes.modules.settings import global_settings +from asyncio import iscoroutinefunction if TYPE_CHECKING: from core.irc import Irc @@ -84,9 +84,9 @@ def get_unixtime() -> int: Returns: int: Current time in seconds since the Epoch (int) """ - cet_offset = timezone(timedelta(hours=2)) - now_cet = datetime.now(cet_offset) - unixtime_cet = int(now_cet.timestamp()) + # cet_offset = timezone(timedelta(hours=2)) + # now_cet = datetime.now(cet_offset) + # unixtime_cet = int(now_cet.timestamp()) return int(time()) def get_sdatetime() -> str: @@ -142,9 +142,9 @@ def create_socket(uplink: 'Irc') -> None: except OSError as oe: uplink.Logs.critical(f"[OS Error]: {oe}") if 'connection refused' in str(oe).lower(): - sys.exit(oe) + sys.exit(oe.__str__()) if oe.errno == 10053: - sys.exit(oe) + sys.exit(oe.__str__()) except AttributeError as ae: uplink.Logs.critical(f"AttributeError: {ae}") @@ -178,7 +178,7 @@ def generate_random_string(lenght: int) -> str: return randomize -def hash_password(password: str, algorithm: Literal["md5, sha3_512"] = 'md5') -> str: +def hash_password(password: str, algorithm: Literal["md5", "sha3_512"] = 'md5') -> str: """Return the crypted password following the selected algorithm Args: @@ -191,16 +191,16 @@ def hash_password(password: str, algorithm: Literal["md5, sha3_512"] = 'md5') -> match algorithm: case 'md5': - password = md5(password.encode()).hexdigest() - return password + hashed_password = md5(password.encode()).hexdigest() + return hashed_password case 'sha3_512': - password = sha3_512(password.encode()).hexdigest() - return password + hashed_password = sha3_512(password.encode()).hexdigest() + return hashed_password case _: - password = md5(password.encode()).hexdigest() - return password + hashed_password = md5(password.encode()).hexdigest() + return hashed_password def get_all_modules() -> list[str]: """Get list of all main modules @@ -225,9 +225,9 @@ def clean_uid(uid: str) -> Optional[str]: return None pattern = fr'[:|@|%|\+|~|\*]*' - parsed_UID = sub(pattern, '', uid) + parsed_uid = sub(pattern, '', uid) - return parsed_UID + return parsed_uid def hide_sensitive_data(srvmsg: list[str]) -> list[str]: try: @@ -244,3 +244,14 @@ def hide_sensitive_data(srvmsg: list[str]) -> list[str]: except ValueError: return srvmsg + +def is_coroutinefunction(func: Any) -> bool: + """Check if the function is a coroutine or not + + Args: + func (Any): an callable object + + Returns: + bool: True if the function is a coroutine + """ + return iscoroutinefunction(func) \ No newline at end of file diff --git a/defender.py b/defender.py index 10577d2..e6aeb98 100644 --- a/defender.py +++ b/defender.py @@ -1,22 +1,22 @@ -from core import installation +import asyncio +from core import install ############################################# -# @Version : 6.3 # +# @Version : 6.4 # # Requierements : # # Python3.10 or higher # # SQLAlchemy, requests, psutil # # unrealircd-rpc-py, pyyaml # +# uvicorn, starlette, faker # # UnrealIRCD 6.2.2 or higher # ############################################# -try: - installation.Install() +async def main(): + install.update_packages() from core.loader import Loader loader = Loader() - loader.Irc.init_irc() + await loader.start() + await loader.Irc.run() -except AssertionError as ae: - print(f'Assertion Error -> {ae}') -except KeyboardInterrupt as k: - # ircInstance.Base.execute_periodic_action() - ... \ No newline at end of file +if __name__ == "__main__": + asyncio.run(main()) diff --git a/mods/clone/clone_manager.py b/mods/clone/clone_manager.py index 486795b..7491830 100644 --- a/mods/clone/clone_manager.py +++ b/mods/clone/clone_manager.py @@ -10,7 +10,7 @@ class CloneManager: def __init__(self, uplink: 'Clone'): - self.Logs = uplink.Logs + self.Logs = uplink.ctx.Logs def insert(self, new_clone_object: MClone) -> bool: """Create new Clone object diff --git a/mods/clone/mod_clone.py b/mods/clone/mod_clone.py index ee73e63..6ae6084 100644 --- a/mods/clone/mod_clone.py +++ b/mods/clone/mod_clone.py @@ -1,90 +1,38 @@ +from dataclasses import dataclass from typing import TYPE_CHECKING, Optional, Any +from core.classes.interfaces.imodule import IModule import mods.clone.utils as utils import mods.clone.threads as thds import mods.clone.schemas as schemas from mods.clone.clone_manager import CloneManager if TYPE_CHECKING: - from core.irc import Irc from faker import Faker + from core.loader import Loader -class Clone: +class Clone(IModule): - def __init__(self, irc_instance: 'Irc') -> None: + @dataclass + class ModConfModel(schemas.ModConfModel): + ... - # Module name (Mandatory) - self.module_name = 'mod_' + str(self.__class__.__name__).lower() + MOD_HEADER: dict[str, str] = { + 'name':'Clone', + 'version':'1.0.0', + 'description':'Connect thousands of clones to your IRCD, by group. You can use them as security moderation.', + 'author':'Defender Team', + 'core_version':'Defender-6' + } - # Add Irc Object to the module (Mandatory) - self.Irc = irc_instance + def __init__(self, context: 'Loader') -> None: + super().__init__(context) + self._mod_config: Optional[schemas.ModConfModel] = None - # Add Irc Protocol Object to the module (Mandatory) - self.Protocol = irc_instance.Protocol + @property + def mod_config(self) -> ModConfModel: + return self._mod_config - # Add Global Configuration to the module (Mandatory) - self.Config = irc_instance.Config - - # Add Base object to the module (Mandatory) - self.Base = irc_instance.Base - - # Add logs object to the module (Mandatory) - self.Logs = irc_instance.Loader.Logs - - # Add User object to the module (Mandatory) - self.User = irc_instance.User - - # Add Channel object to the module (Mandatory) - self.Channel = irc_instance.Channel - - # Add global definitions - self.Definition = irc_instance.Loader.Definition - - # The Global Settings - self.Settings = irc_instance.Loader.Settings - - self.Schemas = schemas - - self.Utils = utils - - self.Threads = thds - - self.Faker: Optional['Faker'] = self.Utils.create_faker_object('en_GB') - - self.Clone = CloneManager(self) - - metadata = self.Settings.get_cache('UID_CLONE_DB') - - if metadata is not None: - self.Clone.UID_CLONE_DB = metadata - self.Logs.debug(f"Cache Size = {self.Settings.get_cache_size()}") - - # Créer les nouvelles commandes du module - self.Irc.build_command(1, self.module_name, 'clone', 'Connect, join, part, kill and say clones') - - # Init the module (Mandatory) - self.__init_module() - - # Log the module - self.Logs.debug(f'Module {self.module_name} loaded ...') - - def __init_module(self) -> None: - - # Créer les tables necessaire a votre module (ce n'es pas obligatoire) - self.__create_tables() - - self.stop = False - - # Load module configuration (Mandatory) - self.__load_module_configuration() - - self.Channel.db_query_channel(action='add', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL) - self.Protocol.send_sjoin(self.Config.CLONE_CHANNEL) - self.Protocol.send_set_mode('+o', nickname=self.Config.SERVICE_NICKNAME, channel_name=self.Config.CLONE_CHANNEL) - self.Protocol.send_set_mode('+nts', channel_name=self.Config.CLONE_CHANNEL) - self.Protocol.send_set_mode('+k', channel_name=self.Config.CLONE_CHANNEL, params=self.Config.CLONE_CHANNEL_PASSWORD) - - - def __create_tables(self) -> None: + async def create_tables(self) -> None: """Methode qui va créer la base de donnée si elle n'existe pas. Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module @@ -100,55 +48,69 @@ class Clone: ) ''' - # self.Base.db_execute_query(table_channel) + # await self.ctx.Base.db_execute_query(table_channel) return None - def __load_module_configuration(self) -> None: - """### Load Module Configuration - """ - try: - # Variable qui va contenir les options de configuration du module Defender - self.ModConfig = self.Schemas.ModConfModel() + async def load(self) -> None: - # Sync the configuration with core configuration (Mandatory) - # self.Base.db_sync_core_config(self.module_name, self.ModConfig) + # Variable qui va contenir les options de configuration du module Defender + self._mod_config: schemas.ModConfModel = self.ModConfModel() - return None + # sync the database with local variable (Mandatory) + await self.sync_db() - except TypeError as te: - self.Logs.critical(te) + self.stop = False + self.Schemas = schemas + self.Utils = utils + self.Threads = thds + self.Faker: Optional['Faker'] = self.Utils.create_faker_object('en_GB') + self.Clone = CloneManager(self) + metadata = self.ctx.Settings.get_cache('UID_CLONE_DB') - def unload(self) -> None: + if metadata is not None: + self.Clone.UID_CLONE_DB = metadata + self.ctx.Logs.debug(f"Cache Size = {self.ctx.Settings.get_cache_size()}") + + # Créer les nouvelles commandes du module + self.ctx.Irc.build_command(1, self.module_name, 'clone', 'Connect, join, part, kill and say clones') + + await self.ctx.Channel.db_query_channel(action='add', module_name=self.module_name, channel_name=self.ctx.Config.CLONE_CHANNEL) + await self.ctx.Irc.Protocol.send_sjoin(self.ctx.Config.CLONE_CHANNEL) + await self.ctx.Irc.Protocol.send_set_mode('+o', nickname=self.ctx.Config.SERVICE_NICKNAME, channel_name=self.ctx.Config.CLONE_CHANNEL) + await self.ctx.Irc.Protocol.send_set_mode('+nts', channel_name=self.ctx.Config.CLONE_CHANNEL) + await self.ctx.Irc.Protocol.send_set_mode('+k', channel_name=self.ctx.Config.CLONE_CHANNEL, params=self.ctx.Config.CLONE_CHANNEL_PASSWORD) + + async def unload(self) -> None: """Cette methode sera executée a chaque désactivation ou rechargement de module """ # Store Clones DB into the global Settings to retrieve it after the reload. - self.Settings.set_cache('UID_CLONE_DB', self.Clone.UID_CLONE_DB) + self.ctx.Settings.set_cache('UID_CLONE_DB', self.Clone.UID_CLONE_DB) - self.Channel.db_query_channel(action='del', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL) - self.Protocol.send_set_mode('-nts', channel_name=self.Config.CLONE_CHANNEL) - self.Protocol.send_set_mode('-k', channel_name=self.Config.CLONE_CHANNEL) - self.Protocol.send_part_chan(self.Config.SERVICE_NICKNAME, self.Config.CLONE_CHANNEL) + await self.ctx.Channel.db_query_channel(action='del', module_name=self.module_name, channel_name=self.ctx.Config.CLONE_CHANNEL) + await self.ctx.Irc.Protocol.send_set_mode('-nts', channel_name=self.ctx.Config.CLONE_CHANNEL) + await self.ctx.Irc.Protocol.send_set_mode('-k', channel_name=self.ctx.Config.CLONE_CHANNEL) + await self.ctx.Irc.Protocol.send_part_chan(self.ctx.Config.SERVICE_NICKNAME, self.ctx.Config.CLONE_CHANNEL) - self.Irc.Commands.drop_command_by_module(self.module_name) + self.ctx.Commands.drop_command_by_module(self.module_name) return None - def cmd(self, data:list) -> None: + async def cmd(self, data:list) -> None: try: if not data or len(data) < 2: return None cmd = data.copy() if isinstance(data, list) else list(data).copy() - index, command = self.Irc.Protocol.get_ircd_protocol_poisition(cmd) + index, command = self.ctx.Irc.Protocol.get_ircd_protocol_poisition(cmd) if index == -1: return None match command: case 'PRIVMSG': - self.Utils.handle_on_privmsg(self, cmd) + await self.Utils.handle_on_privmsg(self, cmd) return None case 'QUIT': @@ -158,10 +120,10 @@ class Clone: return None except Exception as err: - self.Logs.error(f'General Error: {err}', exc_info=True) + self.ctx.Logs.error(f'General Error: {err}', exc_info=True) return None - def hcmds(self, user: str, channel: Any, cmd: list, fullcmd: list = []) -> None: + async def hcmds(self, user: str, channel: Any, cmd: list, fullcmd: list = []) -> None: try: @@ -170,18 +132,18 @@ class Clone: command = str(cmd[0]).lower() fromuser = user - dnickname = self.Config.SERVICE_NICKNAME + dnickname = self.ctx.Config.SERVICE_NICKNAME match command: case 'clone': - if len(cmd) == 1: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone connect NUMBER GROUP_NAME INTERVAL") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill [all | group_name | nickname]") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join [all | group_name | nickname] #channel") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part [all | group_name | nickname] #channel") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone list [group name]") + if len(cmd) < 2: + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone connect NUMBER GROUP_NAME INTERVAL") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill [all | group_name | nickname]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join [all | group_name | nickname] #channel") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part [all | group_name | nickname] #channel") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone list [group name]") return None option = str(cmd[1]).lower() @@ -196,15 +158,13 @@ class Clone: group = str(cmd[3]).lower() connection_interval = int(cmd[4]) if len(cmd) == 5 else 0.2 - self.Base.create_thread( - func=self.Threads.thread_connect_clones, - func_args=(self, number_of_clones, group, False, connection_interval) + self.ctx.Base.create_asynctask( + func=self.Threads.coro_connect_clones(self, number_of_clones, group, False, connection_interval) ) - except Exception as err: - self.Logs.error(f'{err}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone connect [number of clone you want to connect] [Group] [freq]") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Exemple /msg {dnickname} clone connect 6 Ambiance") + except IndexError: + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone connect [number of clone you want to connect] [Group] [freq]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Exemple /msg {dnickname} clone connect 6 Ambiance") case 'kill': try: @@ -213,27 +173,26 @@ class Clone: option = str(cmd[2]) if option.lower() == 'all': - self.Base.create_thread(func=self.Threads.thread_kill_clones, func_args=(self, )) + self.ctx.Base.create_asynctask(func=self.Threads.thread_kill_clones(self)) elif self.Clone.group_exists(option): list_of_clones_in_group = self.Clone.get_clones_from_groupname(option) if len(list_of_clones_in_group) > 0: - self.Logs.debug(f"[Clone Kill Group] - Killing {len(list_of_clones_in_group)} clones in the group {option}") + self.ctx.Logs.debug(f"[Clone Kill Group] - Killing {len(list_of_clones_in_group)} clones in the group {option}") for clone in list_of_clones_in_group: - self.Protocol.send_quit(clone.uid, "Now i am leaving irc but i'll come back soon ...", print_log=False) + await self.ctx.Irc.Protocol.send_quit(clone.uid, "Now i am leaving irc but i'll come back soon ...", print_log=False) self.Clone.delete(clone.uid) else: clone_obj = self.Clone.get_clone(option) if not clone_obj is None: - self.Protocol.send_quit(clone_obj.uid, 'Goood bye', print_log=False) + await self.ctx.Irc.Protocol.send_quit(clone_obj.uid, 'Goood bye', print_log=False) self.Clone.delete(clone_obj.uid) - except Exception as err: - self.Logs.error(f'{err}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill [all | group name | nickname]") + except IndexError: + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill [all | group name | nickname]") case 'join': try: @@ -244,25 +203,24 @@ class Clone: if option.lower() == 'all': for clone in self.Clone.UID_CLONE_DB: - self.Protocol.send_join_chan(uidornickname=clone.uid, channel=clone_channel_to_join, print_log=False) + await self.ctx.Irc.Protocol.send_join_chan(uidornickname=clone.uid, channel=clone_channel_to_join, print_log=False) elif self.Clone.group_exists(option): list_of_clones_in_group = self.Clone.get_clones_from_groupname(option) if len(list_of_clones_in_group) > 0: - self.Logs.debug(f"[Clone Join Group] - Joining {len(list_of_clones_in_group)} clones from group {option} in the channel {clone_channel_to_join}") + self.ctx.Logs.debug(f"[Clone Join Group] - Joining {len(list_of_clones_in_group)} clones from group {option} in the channel {clone_channel_to_join}") for clone in list_of_clones_in_group: - self.Protocol.send_join_chan(uidornickname=clone.nickname, channel=clone_channel_to_join, print_log=False) + await self.ctx.Irc.Protocol.send_join_chan(uidornickname=clone.nickname, channel=clone_channel_to_join, print_log=False) else: if self.Clone.nickname_exists(option): clone_uid = self.Clone.get_clone(option).uid - self.Protocol.send_join_chan(uidornickname=clone_uid, channel=clone_channel_to_join, print_log=False) + await self.ctx.Irc.Protocol.send_join_chan(uidornickname=clone_uid, channel=clone_channel_to_join, print_log=False) - except Exception as err: - self.Logs.error(f'{err}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join [all | group name | nickname] #channel") + except IndexError: + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join [all | group name | nickname] #channel") case 'part': try: @@ -273,67 +231,66 @@ class Clone: if option.lower() == 'all': for clone in self.Clone.UID_CLONE_DB: - self.Protocol.send_part_chan(uidornickname=clone.uid, channel=clone_channel_to_part, print_log=False) + await self.ctx.Irc.Protocol.send_part_chan(uidornickname=clone.uid, channel=clone_channel_to_part, print_log=False) elif self.Clone.group_exists(option): list_of_clones_in_group = self.Clone.get_clones_from_groupname(option) if len(list_of_clones_in_group) > 0: - self.Logs.debug(f"[Clone Part Group] - Part {len(list_of_clones_in_group)} clones from group {option} from the channel {clone_channel_to_part}") + self.ctx.Logs.debug(f"[Clone Part Group] - Part {len(list_of_clones_in_group)} clones from group {option} from the channel {clone_channel_to_part}") for clone in list_of_clones_in_group: - self.Protocol.send_part_chan(uidornickname=clone.uid, channel=clone_channel_to_part, print_log=False) + await self.ctx.Irc.Protocol.send_part_chan(uidornickname=clone.uid, channel=clone_channel_to_part, print_log=False) else: if self.Clone.nickname_exists(option): clone_uid = self.Clone.get_uid(option) if not clone_uid is None: - self.Protocol.send_part_chan(uidornickname=clone_uid, channel=clone_channel_to_part, print_log=False) + await self.ctx.Irc.Protocol.send_part_chan(uidornickname=clone_uid, channel=clone_channel_to_part, print_log=False) - except Exception as err: - self.Logs.error(f'{err}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part [all | group name | nickname] #channel") + except IndexError: + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part [all | group name | nickname] #channel") case 'list': try: # Syntax. /msg defender clone list header = f" {'Nickname':<12}| {'Real name':<25}| {'Group name':<15}| {'Connected':<35}" line = "-"*67 - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=header) - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {line}") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=header) + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {line}") group_name = cmd[2] if len(cmd) > 2 else None if group_name is None: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Number of connected clones: {len(self.Clone.UID_CLONE_DB)}") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {line}") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Number of connected clones: {len(self.Clone.UID_CLONE_DB)}") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {line}") for clone_name in self.Clone.UID_CLONE_DB: - self.Protocol.send_notice( + await self.ctx.Irc.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg=f" {clone_name.nickname:<12}| {clone_name.realname:<25}| {clone_name.group:<15}| {clone_name.connected:<35}") else: if not self.Clone.group_exists(group_name): - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="This Group name doesn't exist!") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="This Group name doesn't exist!") return None clones = self.Clone.get_clones_from_groupname(group_name) - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Number of connected clones: {len(clones)}") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {line}") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Number of connected clones: {len(clones)}") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {line}") for clone in clones: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {clone.nickname:<12}| {clone.realname:<25}| {clone.group:<15}| {clone.connected:<35}") - except Exception as err: - self.Logs.error(f'{err}') + except IndexError: + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone list [group name]") case 'say': try: # clone say clone_nickname #channel message clone_name = str(cmd[2]) - clone_channel = str(cmd[3]) if self.Channel.is_valid_channel(str(cmd[3])) else None + clone_channel = str(cmd[3]) if self.ctx.Channel.is_valid_channel(str(cmd[3])) else None final_message = ' '.join(cmd[4:]) if clone_channel is None or not self.Clone.nickname_exists(clone_name): - self.Protocol.send_notice( + await self.ctx.Irc.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone say [clone_nickname] #channel message" @@ -341,24 +298,21 @@ class Clone: return None if self.Clone.nickname_exists(clone_name): - self.Protocol.send_priv_msg(nick_from=clone_name, msg=final_message, channel=clone_channel) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=clone_name, msg=final_message, channel=clone_channel) - except Exception as err: - self.Logs.error(f'{err}') - self.Protocol.send_notice( + except IndexError: + await self.ctx.Irc.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone say [clone_nickname] #channel message" ) case _: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone connect NUMBER GROUP_NAME INTERVAL") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill [all | group name | nickname]") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join [all | group name | nickname] #channel") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part [all | group name | nickname] #channel") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone list [group name]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone connect NUMBER GROUP_NAME INTERVAL") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill [all | group name | nickname]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join [all | group name | nickname] #channel") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part [all | group name | nickname] #channel") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone list [group name]") - except IndexError as ie: - self.Logs.error(f'Index Error: {ie}') except Exception as err: - self.Logs.error(f'General Error: {err}') + self.ctx.Logs.error(f'General Error: {err}', exc_info=True) diff --git a/mods/clone/threads.py b/mods/clone/threads.py index c8c216a..e078a05 100644 --- a/mods/clone/threads.py +++ b/mods/clone/threads.py @@ -1,10 +1,11 @@ +import asyncio from typing import TYPE_CHECKING from time import sleep if TYPE_CHECKING: from mods.clone.mod_clone import Clone -def thread_connect_clones(uplink: 'Clone', +async def coro_connect_clones(uplink: 'Clone', number_of_clones:int , group: str = 'Default', auto_remote_ip: bool = False, @@ -27,18 +28,18 @@ def thread_connect_clones(uplink: 'Clone', break if not clone.connected: - uplink.Protocol.send_uid(clone.nickname, clone.username, clone.hostname, clone.uid, clone.umodes, clone.vhost, clone.remote_ip, clone.realname, print_log=False) - uplink.Protocol.send_join_chan(uidornickname=clone.uid, channel=uplink.Config.CLONE_CHANNEL, password=uplink.Config.CLONE_CHANNEL_PASSWORD, print_log=False) + await uplink.ctx.Irc.Protocol.send_uid(clone.nickname, clone.username, clone.hostname, clone.uid, clone.umodes, clone.vhost, clone.remote_ip, clone.realname, print_log=False) + await uplink.ctx.Irc.Protocol.send_join_chan(uidornickname=clone.uid, channel=uplink.ctx.Config.CLONE_CHANNEL, password=uplink.ctx.Config.CLONE_CHANNEL_PASSWORD, print_log=False) - sleep(interval) + await asyncio.sleep(interval) clone.connected = True -def thread_kill_clones(uplink: 'Clone'): +async def thread_kill_clones(uplink: 'Clone'): clone_to_kill = uplink.Clone.UID_CLONE_DB.copy() for clone in clone_to_kill: - uplink.Protocol.send_quit(clone.uid, 'Gooood bye', print_log=False) + await uplink.ctx.Irc.Protocol.send_quit(clone.uid, 'Gooood bye', print_log=False) uplink.Clone.delete(clone.uid) del clone_to_kill diff --git a/mods/clone/utils.py b/mods/clone/utils.py index 495252e..4b03bb3 100644 --- a/mods/clone/utils.py +++ b/mods/clone/utils.py @@ -125,8 +125,8 @@ def create_new_clone(uplink: 'Clone', faker_instance: 'Faker', group: str = 'Def """ faker = faker_instance - uid = generate_uid_for_clone(faker, uplink.Config.SERVEUR_ID) - umodes = uplink.Config.CLONE_UMODES + uid = generate_uid_for_clone(faker, uplink.ctx.Config.SERVEUR_ID) + umodes = uplink.ctx.Config.CLONE_UMODES # Generate Username username = generate_username_for_clone(faker) @@ -153,7 +153,7 @@ def create_new_clone(uplink: 'Clone', faker_instance: 'Faker', group: str = 'Def checkNickname = uplink.Clone.nickname_exists(nickname) while checkUid: - uid = generate_uid_for_clone(faker, uplink.Config.SERVEUR_ID) + uid = generate_uid_for_clone(faker, uplink.ctx.Config.SERVEUR_ID) checkUid = uplink.Clone.uid_exists(uid=uid) clone = uplink.Schemas.MClone( @@ -174,27 +174,25 @@ def create_new_clone(uplink: 'Clone', faker_instance: 'Faker', group: str = 'Def return True -def handle_on_privmsg(uplink: 'Clone', srvmsg: list[str]) -> None: +async def handle_on_privmsg(uplink: 'Clone', srvmsg: list[str]) -> None: - parser = uplink.Protocol.parse_privmsg(srvmsg) - uid_sender = uplink.Irc.Utils.clean_uid(parser.get('uid_sender', None)) - senderObj = uplink.User.get_user(uid_sender) + senderObj, recieverObj, channel, message = uplink.ctx.Irc.Protocol.parse_privmsg(srvmsg) if senderObj is not None: - if senderObj.hostname in uplink.Config.CLONE_LOG_HOST_EXEMPT: + if senderObj.hostname in uplink.ctx.Config.CLONE_LOG_HOST_EXEMPT: return - senderMsg = parser.get('message', None) - clone_obj = uplink.Clone.get_clone(parser.get('uid_reciever', None)) + senderMsg = message + clone_obj = recieverObj if clone_obj is None: return - if clone_obj.uid != uplink.Config.SERVICE_ID: + if clone_obj.uid != uplink.ctx.Config.SERVICE_ID: final_message = f"{senderObj.nickname}!{senderObj.username}@{senderObj.hostname} > {senderMsg.lstrip(':')}" - uplink.Protocol.send_priv_msg( + await uplink.ctx.Irc.Protocol.send_priv_msg( nick_from=clone_obj.uid, msg=final_message, - channel=uplink.Config.CLONE_CHANNEL + channel=uplink.ctx.Config.CLONE_CHANNEL ) return None diff --git a/mods/command/mod_command.py b/mods/command/mod_command.py index d125914..f7a7aaa 100644 --- a/mods/command/mod_command.py +++ b/mods/command/mod_command.py @@ -1,13 +1,13 @@ from typing import Optional, TYPE_CHECKING from dataclasses import dataclass +from core.classes.interfaces.imodule import IModule import mods.command.utils as utils if TYPE_CHECKING: - from core.irc import Irc from core.definition import MUser - from sqlalchemy import CursorResult, Row, Sequence + from core.loader import Loader -class Command: +class Command(IModule): @dataclass class ModConfModel: @@ -15,116 +15,23 @@ class Command: """ pass - def __init__(self, ircInstance: 'Irc') -> None: + MOD_HEADER: dict[str, str] = { + 'name':'Command', + 'version':'1.0.0', + 'description':'Module contains all IRC commands', + 'author':'Defender Team', + 'core_version':'Defender-6' + } - # Module name (Mandatory) - self.module_name = 'mod_' + str(self.__class__.__name__).lower() + def __init__(self, uplink: 'Loader'): + super().__init__(uplink) + self._mod_config: Optional[Command.ModConfModel] = self.ModConfModel() + + @property + def mod_config(self) -> ModConfModel: + return self._mod_config - # 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 - - # Add Global Configuration to the module (Mandatory) - self.Config = ircInstance.Config - - # Add Base object to the module (Mandatory) - self.Base = ircInstance.Base - - # Add main Utils to the module - self.MainUtils = ircInstance.Utils - - # Add logs object to the module (Mandatory) - self.Logs = ircInstance.Loader.Logs - - # Add User object to the module (Mandatory) - self.User = ircInstance.User - - # Add Client object to the module (Mandatory) - self.Client = ircInstance.Client - - # Add Channel object to the module (Mandatory) - self.Channel = ircInstance.Channel - - # Module Utils - self.mod_utils = utils - - self.Irc.build_command(2, self.module_name, 'join', 'Join a channel') - self.Irc.build_command(2, self.module_name, 'assign', 'Assign a user to a role or task') - self.Irc.build_command(2, self.module_name, 'part', 'Leave a channel') - self.Irc.build_command(2, self.module_name, 'unassign', 'Remove a user from a role or task') - self.Irc.build_command(2, self.module_name, 'owner', 'Give channel ownership to a user') - self.Irc.build_command(2, self.module_name, 'deowner', 'Remove channel ownership from a user') - self.Irc.build_command(2, self.module_name, 'protect', 'Protect a user from being kicked') - self.Irc.build_command(2, self.module_name, 'deprotect', 'Remove protection from a user') - self.Irc.build_command(2, self.module_name, 'op', 'Grant operator privileges to a user') - self.Irc.build_command(2, self.module_name, 'deop', 'Remove operator privileges from a user') - self.Irc.build_command(1, self.module_name, 'halfop', 'Grant half-operator privileges to a user') - self.Irc.build_command(1, self.module_name, 'dehalfop', 'Remove half-operator privileges from a user') - self.Irc.build_command(1, self.module_name, 'voice', 'Grant voice privileges to a user') - self.Irc.build_command(1, self.module_name, 'devoice', 'Remove voice privileges from a user') - self.Irc.build_command(1, self.module_name, 'topic', 'Change the topic of a channel') - self.Irc.build_command(2, self.module_name, 'opall', 'Grant operator privileges to all users') - self.Irc.build_command(2, self.module_name, 'deopall', 'Remove operator privileges from all users') - self.Irc.build_command(2, self.module_name, 'devoiceall', 'Remove voice privileges from all users') - self.Irc.build_command(2, self.module_name, 'voiceall', 'Grant voice privileges to all users') - self.Irc.build_command(2, self.module_name, 'ban', 'Ban a user from a channel') - self.Irc.build_command(2, self.module_name, 'automode', 'Automatically set user modes upon join') - self.Irc.build_command(2, self.module_name, 'unban', 'Remove a ban from a user') - self.Irc.build_command(2, self.module_name, 'kick', 'Kick a user from a channel') - self.Irc.build_command(2, self.module_name, 'kickban', 'Kick and ban a user from a channel') - self.Irc.build_command(2, self.module_name, 'umode', 'Set user mode') - self.Irc.build_command(2, self.module_name, 'mode', 'Set channel mode') - self.Irc.build_command(2, self.module_name, 'get_mode', 'Retrieve current channel mode') - self.Irc.build_command(2, self.module_name, 'svsjoin', 'Force a user to join a channel') - self.Irc.build_command(2, self.module_name, 'svspart', 'Force a user to leave a channel') - self.Irc.build_command(2, self.module_name, 'svsnick', 'Force a user to change their nickname') - self.Irc.build_command(2, self.module_name, 'wallops', 'Send a message to all operators') - self.Irc.build_command(2, self.module_name, 'globops', 'Send a global operator message') - self.Irc.build_command(2, self.module_name, 'gnotice', 'Send a global notice') - self.Irc.build_command(2, self.module_name, 'whois', 'Get information about a user') - self.Irc.build_command(2, self.module_name, 'names', 'List users in a channel') - self.Irc.build_command(2, self.module_name, 'invite', 'Invite a user to a channel') - self.Irc.build_command(2, self.module_name, 'inviteme', 'Invite yourself to a channel') - self.Irc.build_command(2, self.module_name, 'sajoin', 'Force yourself into a channel') - self.Irc.build_command(2, self.module_name, 'sapart', 'Force yourself to leave a channel') - self.Irc.build_command(2, self.module_name, 'kill', 'Disconnect a user from the server') - self.Irc.build_command(2, self.module_name, 'gline', 'Ban a user from the entire server') - self.Irc.build_command(2, self.module_name, 'ungline', 'Remove a global server ban') - self.Irc.build_command(2, self.module_name, 'kline', 'Ban a user based on their hostname') - self.Irc.build_command(2, self.module_name, 'unkline', 'Remove a K-line ban') - self.Irc.build_command(2, self.module_name, 'shun', 'Prevent a user from sending messages') - self.Irc.build_command(2, self.module_name, 'unshun', 'Remove a shun from a user') - self.Irc.build_command(2, self.module_name, 'glinelist', 'List all global bans') - self.Irc.build_command(2, self.module_name, 'shunlist', 'List all shunned users') - self.Irc.build_command(2, self.module_name, 'klinelist', 'List all K-line bans') - self.Irc.build_command(3, self.module_name, 'map', 'Show the server network map') - - # Init the module - self.__init_module() - - # Log the module - self.Logs.debug(f'-- Module {self.module_name} loaded ...') - - def __init_module(self) -> None: - - # Create you own tables (Mandatory) - self.__create_tables() - - # Load module configuration and sync with core one (Mandatory) - self.__load_module_configuration() - # End of mandatory methods you can start your customization # - - self.user_to_notice: str = '' - self.show_219: bool = True - - return None - - def __create_tables(self) -> None: + def create_tables(self) -> None: """Methode qui va créer la base de donnée si elle n'existe pas. Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module Args: @@ -144,81 +51,121 @@ class Command: ) ''' - self.Base.db_execute_query(table_automode) + self.ctx.Base.db_execute_query(table_automode) return None - def __load_module_configuration(self) -> None: - """### Load Module Configuration - """ - try: - # Build the default configuration model (Mandatory) - self.ModConfig = self.ModConfModel() + def load(self) -> None: + # Module Utils + self.mod_utils = utils + self.user_to_notice: str = '' + self.show_219: bool = True - # Sync the configuration with core configuration (Mandatory) - self.Base.db_sync_core_config(self.module_name, self.ModConfig) + # Register new commands into the protocol + new_cmds = {'403', '401', '006', '018', '219', '223'} + for c in new_cmds: + self.ctx.Irc.Protocol.known_protocol.add(c) - return None - - except TypeError as te: - self.Logs.critical(te) - - def __update_configuration(self, param_key: str, param_value: str): - """Update the local and core configuration - - Args: - param_key (str): The parameter key - param_value (str): The parameter value - """ - self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value) + self.ctx.Irc.build_command(2, self.module_name, 'join', 'Join a channel') + self.ctx.Irc.build_command(2, self.module_name, 'assign', 'Assign a user to a role or task') + self.ctx.Irc.build_command(2, self.module_name, 'part', 'Leave a channel') + self.ctx.Irc.build_command(2, self.module_name, 'unassign', 'Remove a user from a role or task') + self.ctx.Irc.build_command(2, self.module_name, 'owner', 'Give channel ownership to a user') + self.ctx.Irc.build_command(2, self.module_name, 'deowner', 'Remove channel ownership from a user') + self.ctx.Irc.build_command(2, self.module_name, 'protect', 'Protect a user from being kicked') + self.ctx.Irc.build_command(2, self.module_name, 'deprotect', 'Remove protection from a user') + self.ctx.Irc.build_command(2, self.module_name, 'op', 'Grant operator privileges to a user') + self.ctx.Irc.build_command(2, self.module_name, 'deop', 'Remove operator privileges from a user') + self.ctx.Irc.build_command(1, self.module_name, 'halfop', 'Grant half-operator privileges to a user') + self.ctx.Irc.build_command(1, self.module_name, 'dehalfop', 'Remove half-operator privileges from a user') + self.ctx.Irc.build_command(1, self.module_name, 'voice', 'Grant voice privileges to a user') + self.ctx.Irc.build_command(1, self.module_name, 'devoice', 'Remove voice privileges from a user') + self.ctx.Irc.build_command(1, self.module_name, 'topic', 'Change the topic of a channel') + self.ctx.Irc.build_command(2, self.module_name, 'opall', 'Grant operator privileges to all users') + self.ctx.Irc.build_command(2, self.module_name, 'deopall', 'Remove operator privileges from all users') + self.ctx.Irc.build_command(2, self.module_name, 'devoiceall', 'Remove voice privileges from all users') + self.ctx.Irc.build_command(2, self.module_name, 'voiceall', 'Grant voice privileges to all users') + self.ctx.Irc.build_command(2, self.module_name, 'ban', 'Ban a user from a channel') + self.ctx.Irc.build_command(2, self.module_name, 'automode', 'Automatically set user modes upon join') + self.ctx.Irc.build_command(2, self.module_name, 'unban', 'Remove a ban from a user') + self.ctx.Irc.build_command(2, self.module_name, 'kick', 'Kick a user from a channel') + self.ctx.Irc.build_command(2, self.module_name, 'kickban', 'Kick and ban a user from a channel') + self.ctx.Irc.build_command(2, self.module_name, 'umode', 'Set user mode') + self.ctx.Irc.build_command(2, self.module_name, 'mode', 'Set channel mode') + self.ctx.Irc.build_command(2, self.module_name, 'get_mode', 'Retrieve current channel mode') + self.ctx.Irc.build_command(2, self.module_name, 'svsjoin', 'Force a user to join a channel') + self.ctx.Irc.build_command(2, self.module_name, 'svspart', 'Force a user to leave a channel') + self.ctx.Irc.build_command(2, self.module_name, 'svsnick', 'Force a user to change their nickname') + self.ctx.Irc.build_command(2, self.module_name, 'wallops', 'Send a message to all operators') + self.ctx.Irc.build_command(2, self.module_name, 'globops', 'Send a global operator message') + self.ctx.Irc.build_command(2, self.module_name, 'gnotice', 'Send a global notice') + self.ctx.Irc.build_command(2, self.module_name, 'whois', 'Get information about a user') + self.ctx.Irc.build_command(2, self.module_name, 'names', 'List users in a channel') + self.ctx.Irc.build_command(2, self.module_name, 'invite', 'Invite a user to a channel') + self.ctx.Irc.build_command(2, self.module_name, 'inviteme', 'Invite yourself to a channel') + self.ctx.Irc.build_command(2, self.module_name, 'sajoin', 'Force yourself into a channel') + self.ctx.Irc.build_command(2, self.module_name, 'sapart', 'Force yourself to leave a channel') + self.ctx.Irc.build_command(2, self.module_name, 'kill', 'Disconnect a user from the server') + self.ctx.Irc.build_command(2, self.module_name, 'gline', 'Ban a user from the entire server') + self.ctx.Irc.build_command(2, self.module_name, 'ungline', 'Remove a global server ban') + self.ctx.Irc.build_command(2, self.module_name, 'kline', 'Ban a user based on their hostname') + self.ctx.Irc.build_command(2, self.module_name, 'unkline', 'Remove a K-line ban') + self.ctx.Irc.build_command(2, self.module_name, 'shun', 'Prevent a user from sending messages') + self.ctx.Irc.build_command(2, self.module_name, 'unshun', 'Remove a shun from a user') + self.ctx.Irc.build_command(2, self.module_name, 'glinelist', 'List all global bans') + self.ctx.Irc.build_command(2, self.module_name, 'shunlist', 'List all shunned users') + self.ctx.Irc.build_command(2, self.module_name, 'klinelist', 'List all K-line bans') + self.ctx.Irc.build_command(3, self.module_name, 'map', 'Show the server network map') def unload(self) -> None: - self.Irc.Commands.drop_command_by_module(self.module_name) + self.ctx.Commands.drop_command_by_module(self.module_name) return None - def cmd(self, data: list[str]) -> None: + async def cmd(self, data: list[str]) -> None: try: - # service_id = self.Config.SERVICE_ID - dnickname = self.Config.SERVICE_NICKNAME - # dchanlog = self.Config.SERVICE_CHANLOG - red = self.Config.COLORS.red - green = self.Config.COLORS.green - bold = self.Config.COLORS.bold - nogc = self.Config.COLORS.nogc + # service_id = self.ctx.Config.SERVICE_ID + dnickname = self.ctx.Config.SERVICE_NICKNAME + # dchanlog = self.ctx.Config.SERVICE_CHANLOG + red = self.ctx.Config.COLORS.red + green = self.ctx.Config.COLORS.green + bold = self.ctx.Config.COLORS.bold + nogc = self.ctx.Config.COLORS.nogc cmd = list(data).copy() - if len(cmd) < 2: + pos, parsed_cmd = self.ctx.Irc.Protocol.get_ircd_protocol_poisition(cmd=cmd, log=True) + + if pos == -1: return None - match cmd[1]: + match parsed_cmd: # [':irc.deb.biz.st', '403', 'Dev-PyDefender', '#Z', ':No', 'such', 'channel'] case '403' | '401': try: message = ' '.join(cmd[3:]) - self.Protocol.send_notice( + await self.ctx.Irc.Protocol.send_notice( nick_from=dnickname, nick_to=self.user_to_notice, msg=f"[{red}ERROR MSG{nogc}] {message}" ) - self.Logs.error(f"{cmd[1]} - {message}") + self.ctx.Logs.error(f"{cmd[1]} - {message}") except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case '006' | '018': try: # [':irc.deb.biz.st', '006', 'Dev-PyDefender', ':`-services.deb.biz.st', '------', '|', 'Users:', '9', '(47.37%)', '[00B]'] # [':irc.deb.biz.st', '018', 'Dev-PyDefender', ':4', 'servers', 'and', '19', 'users,', 'average', '4.75', 'users', 'per', 'server'] message = ' '.join(cmd[3:]) - self.Protocol.send_notice( + await self.ctx.Irc.Protocol.send_notice( nick_from=dnickname, nick_to=self.user_to_notice, msg=f"[{green}SERVER MSG{nogc}] {message}" ) except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case '219': try: @@ -232,16 +179,16 @@ class Command: match type_of_stats: case 's': - self.Protocol.send_notice(nick_from=dnickname,nick_to=self.user_to_notice, msg="No shun") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname,nick_to=self.user_to_notice, msg="No shun") case 'G': - self.Protocol.send_notice(nick_from=dnickname,nick_to=self.user_to_notice, msg="No gline") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname,nick_to=self.user_to_notice, msg="No gline") case 'k': - self.Protocol.send_notice(nick_from=dnickname,nick_to=self.user_to_notice, msg="No kline") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname,nick_to=self.user_to_notice, msg="No kline") except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case '223': try: @@ -251,35 +198,23 @@ class Command: author = str(cmd[7]) reason = ' '.join(cmd[8:]) - self.Protocol.send_notice(nick_from=dnickname,nick_to=self.user_to_notice, + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname,nick_to=self.user_to_notice, msg=f"{bold}Author{nogc}: {author} - {bold}Host{nogc}: {host} - {bold}Reason{nogc}: {reason}" ) except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') - - case _: - pass - - if len(cmd) < 3: - return None - - match cmd[2]: + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') 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_valid_channel(cmd[4]) else None - client_obj = self.Client.get_Client(user_uid) + user_uid = self.ctx.User.clean_uid(cmd[5]) + userObj: MUser = self.ctx.User.get_user(user_uid) + channel_name = cmd[4] if self.ctx.Channel.is_valid_channel(cmd[4]) else None + client_obj = self.ctx.Client.get_Client(user_uid) nickname = userObj.nickname if userObj is not None else None if client_obj is not None: @@ -288,28 +223,31 @@ class Command: if userObj is None: return None - if 'r' not in userObj.umodes and 'o' not in userObj.umodes and not self.Client.is_exist(userObj.uid): + if 'r' not in userObj.umodes and 'o' not in userObj.umodes and not self.ctx.Client.is_exist(userObj.uid): return None db_data: dict[str, str] = {"nickname": nickname.lower(), "channel": channel_name.lower()} - db_query = self.Base.db_execute_query("SELECT id, mode FROM command_automode WHERE LOWER(nickname) = :nickname AND LOWER(channel) = :channel", db_data) + db_query = await self.ctx.Base.db_execute_query("SELECT id, mode FROM command_automode WHERE LOWER(nickname) = :nickname AND LOWER(channel) = :channel", db_data) db_result = db_query.fetchone() if db_result: id, mode = db_result - self.Protocol.send2socket(f":{self.Config.SERVICE_ID} MODE {channel_name} {mode} {userObj.nickname}") + await self.ctx.Irc.Protocol.send2socket(f":{self.ctx.Config.SERVICE_ID} MODE {channel_name} {mode} {userObj.nickname}") except KeyError as ke: - self.Logs.error(f"Key Error: {err}") + self.ctx.Logs.error(f"Key Error: {err}") + + case _: + pass except Exception as err: - self.Logs.error(f"General Error: {err}") + self.ctx.Logs.error(f"General Error: {err}", exc_info=True) - def hcmds(self, uidornickname: str, channel_name: Optional[str], cmd: list, fullcmd: list = []): + async def hcmds(self, uidornickname: str, channel_name: Optional[str], cmd: list, fullcmd: list = []): command = str(cmd[0]).lower() - dnickname = self.Config.SERVICE_NICKNAME - service_id = self.Config.SERVICE_ID - dchanlog = self.Config.SERVICE_CHANLOG + dnickname = self.ctx.Config.SERVICE_NICKNAME + service_id = self.ctx.Config.SERVICE_ID + dchanlog = self.ctx.Config.SERVICE_CHANLOG self.user_to_notice = uidornickname fromuser = uidornickname fromchannel = channel_name @@ -318,365 +256,365 @@ class Command: case 'automode': try: - self.mod_utils.set_automode(self, cmd, fromuser) + await self.mod_utils.set_automode(self, cmd, fromuser) 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.Loader.Settings.PROTOCTL_PREFIX)}") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} SET [nickname] [+/-mode] [#channel]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} LIST") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"[AUTOMODES AVAILABLE] are {' / '.join(self.ctx.Settings.PROTOCTL_PREFIX)}") except Exception as err: - self.Logs.error(f"General Error: {err}") + self.ctx.Logs.error(f"General Error: {err}") case 'deopall': try: - self.mod_utils.set_deopall(self, fromchannel) + await self.mod_utils.set_deopall(self, fromchannel) except Exception as err: - self.Logs.error(f'Unknown Error: {str(err)}') + self.ctx.Logs.error(f'Unknown Error: {str(err)}') case 'devoiceall': try: - self.mod_utils.set_devoiceall(self, fromchannel) + await self.mod_utils.set_devoiceall(self, fromchannel) except Exception as err: - self.Logs.error(f'Unknown Error: {str(err)}') + self.ctx.Logs.error(f'Unknown Error: {str(err)}') case 'voiceall': try: - self.mod_utils.set_mode_to_all(self, fromchannel, '+', 'v') + await self.mod_utils.set_mode_to_all(self, fromchannel, '+', 'v') except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'opall': try: - self.mod_utils.set_mode_to_all(self, fromchannel, '+', 'o') + await self.mod_utils.set_mode_to_all(self, fromchannel, '+', 'o') except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'op': try: - self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '+o') + await self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '+o') except IndexError as e: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} op [#SALON] [NICKNAME]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} op [#SALON] [NICKNAME]") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'deop': try: - self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '-o') + await self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '-o') except IndexError as e: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} deop [#SALON] [NICKNAME]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} deop [#SALON] [NICKNAME]") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'owner': try: - self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '+q') + await self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '+q') except IndexError as e: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} owner [#SALON] [NICKNAME]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} owner [#SALON] [NICKNAME]") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'deowner': try: - self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '-q') + await self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '-q') except IndexError as e: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'protect': try: - self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '+a') + await self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '+a') except IndexError as e: - self.Logs.warning(f'_hcmd DEOWNER: {str(e)}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]") + self.ctx.Logs.warning(f'_hcmd DEOWNER: {str(e)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'deprotect': try: - self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '-a') + await self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '-a') except IndexError as e: - self.Logs.warning(f'_hcmd DEOWNER: {str(e)}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]") + self.ctx.Logs.warning(f'_hcmd DEOWNER: {str(e)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'halfop': try: - self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '+h') + await self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '+h') except IndexError as e: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command} [#SALON] [NICKNAME]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command} [#SALON] [NICKNAME]") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'dehalfop': try: - self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '-h') + await self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '-h') except IndexError as e: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'voice': try: - self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '+v') + await self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '+v') except IndexError as e: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} voice [#SALON] [NICKNAME]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} voice [#SALON] [NICKNAME]") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'devoice': try: - self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '-v') + await self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '-v') except IndexError as e: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'ban': try: - self.mod_utils.set_ban(self, cmd, '+', fromuser) + await self.mod_utils.set_ban(self, cmd, '+', fromuser) except IndexError as e: - self.Logs.warning(f'_hcmd BAN: {str(e)}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]") + self.ctx.Logs.warning(f'_hcmd BAN: {str(e)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'unban': try: - self.mod_utils.set_ban(self, cmd, '-', fromuser) + await self.mod_utils.set_ban(self, cmd, '-', fromuser) except IndexError as e: - self.Logs.warning(f'_hcmd UNBAN: {str(e)}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]") + self.ctx.Logs.warning(f'_hcmd UNBAN: {str(e)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'kick': try: - self.mod_utils.set_kick(self, cmd, fromuser) + await self.mod_utils.set_kick(self, cmd, fromuser) except IndexError as e: - self.Logs.warning(f'_hcmd KICK: {str(e)}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME] [REASON]") + self.ctx.Logs.warning(f'_hcmd KICK: {str(e)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME] [REASON]") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'kickban': try: - self.mod_utils.set_kickban(self, cmd, fromuser) + await self.mod_utils.set_kickban(self, cmd, fromuser) except IndexError as e: - self.Logs.warning(f'_hcmd KICKBAN: {str(e)}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME] [REASON]") + self.ctx.Logs.warning(f'_hcmd KICKBAN: {str(e)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME] [REASON]") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'join' | 'assign': try: - self.mod_utils.set_assign_channel_to_service(self, cmd, fromuser) + await self.mod_utils.set_assign_channel_to_service(self, cmd, fromuser) except IndexError as ie: - self.Logs.debug(f'{ie}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON]") + self.ctx.Logs.debug(f'{ie}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON]") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'part' | 'unassign': try: # Syntax. !part #channel - self.mod_utils.set_unassign_channel_to_service(self, cmd, fromuser) + await self.mod_utils.set_unassign_channel_to_service(self, cmd, fromuser) except IndexError as ie: - self.Logs.debug(f'{ie}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON]") + self.ctx.Logs.debug(f'{ie}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON]") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'topic': try: if len(cmd) == 1: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} TOPIC #channel THE_TOPIC_MESSAGE") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} TOPIC #channel THE_TOPIC_MESSAGE") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} TOPIC #channel THE_TOPIC_MESSAGE") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} TOPIC #channel THE_TOPIC_MESSAGE") return None chan = str(cmd[1]) - if not self.Channel.is_valid_channel(chan): - 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") + if not self.ctx.Channel.is_valid_channel(chan): + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="The channel must start with #") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} TOPIC #channel THE_TOPIC_MESSAGE") return None topic_msg = ' '.join(cmd[2:]).strip() if topic_msg: - self.Protocol.send2socket(f':{dnickname} TOPIC {chan} :{topic_msg}') + await self.ctx.Irc.Protocol.send2socket(f':{dnickname} TOPIC {chan} :{topic_msg}') else: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You need to specify the topic") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You need to specify the topic") except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'wallops': try: if len(cmd) == 1: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} WALLOPS THE_WALLOPS_MESSAGE") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} WALLOPS THE_WALLOPS_MESSAGE") return None wallops_msg = ' '.join(cmd[1:]).strip() if wallops_msg: - self.Protocol.send2socket(f':{dnickname} WALLOPS {wallops_msg} ({dnickname})') + await self.ctx.Irc.Protocol.send2socket(f':{dnickname} WALLOPS {wallops_msg} ({dnickname})') else: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You need to specify the wallops message") + await self.ctx.Irc.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) + self.ctx.Logs.error(ke) except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'globops': try: if len(cmd) == 1: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} GLOBOPS THE_GLOBOPS_MESSAGE") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} GLOBOPS THE_GLOBOPS_MESSAGE") return None globops_msg = ' '.join(cmd[1:]).strip() if globops_msg: - self.Protocol.send2socket(f':{dnickname} GLOBOPS {globops_msg} ({dnickname})') + await self.ctx.Irc.Protocol.send2socket(f':{dnickname} GLOBOPS {globops_msg} ({dnickname})') else: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You need to specify the globops message") + await self.ctx.Irc.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) + self.ctx.Logs.error(ke) except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'gnotice': try: if len(cmd) == 1: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} THE_GLOBAL_NOTICE_MESSAGE") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} THE_GLOBAL_NOTICE_MESSAGE") return None gnotice_msg = ' '.join(cmd[1:]).strip() 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}") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to='$*.*', msg=f"[{self.ctx.Config.COLORS.red}GLOBAL NOTICE{self.ctx.Config.COLORS.nogc}] {gnotice_msg}") else: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You need to specify the global notice message") + await self.ctx.Irc.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) + self.ctx.Logs.error(ke) except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'whois': try: self.user_to_notice = fromuser if len(cmd) == 1: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} NICKNAME") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} NICKNAME") return None nickname = str(cmd[1]) - if self.User.get_nickname(nickname) is None: - 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") + if self.ctx.User.get_nickname(nickname) is None: + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="Nickname not found !") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} NICKNAME") return None - self.Protocol.send2socket(f':{dnickname} WHOIS {nickname}') + await self.ctx.Irc.Protocol.send2socket(f':{dnickname} WHOIS {nickname}') except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'names': try: if len(cmd) == 1: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} #CHANNEL") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} #CHANNEL") return None chan = str(cmd[1]) - if not self.Channel.is_valid_channel(chan): - 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") + if not self.ctx.Channel.is_valid_channel(chan): + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="The channel must start with #") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} #channel") return None - self.Protocol.send2socket(f':{dnickname} NAMES {chan}') + await self.ctx.Irc.Protocol.send2socket(f':{dnickname} NAMES {chan}') except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'invite': try: if len(cmd) < 3: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} NICKNAME #CHANNEL") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} NICKNAME #CHANNEL") return None nickname = str(cmd[1]) chan = str(cmd[2]) - if not self.Channel.is_valid_channel(chan): - 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") + if not self.ctx.Channel.is_valid_channel(chan): + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="The channel must start with #") + await self.ctx.Irc.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="Nickname not found !") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} NICKNAME #CHANNEL") + if self.ctx.User.get_nickname(nickname) is None: + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="Nickname not found !") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} NICKNAME #CHANNEL") return None - self.Protocol.send2socket(f':{dnickname} INVITE {nickname} {chan}') + await self.ctx.Irc.Protocol.send2socket(f':{dnickname} INVITE {nickname} {chan}') except KeyError as ke: - self.Logs.error(f"KeyError: {ke}") + self.ctx.Logs.error(f"KeyError: {ke}") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'inviteme': try: if len(cmd) == 0: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()}") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()}") return None - self.Protocol.send2socket(f':{dnickname} INVITE {fromuser} {self.Config.SERVICE_CHANLOG}') + await self.ctx.Irc.Protocol.send2socket(f':{dnickname} INVITE {fromuser} {self.ctx.Config.SERVICE_CHANLOG}') except KeyError as ke: - self.Logs.error(f"KeyError: {ke}") + self.ctx.Logs.error(f"KeyError: {ke}") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'map': try: self.user_to_notice = fromuser - self.Protocol.send2socket(f':{dnickname} MAP') + await self.ctx.Irc.Protocol.send2socket(f':{dnickname} MAP') except KeyError as ke: - self.Logs.error(f"KeyError: {ke}") + self.ctx.Logs.error(f"KeyError: {ke}") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'umode': try: # .umode nickname +mode if len(cmd) < 2: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [NICKNAME] [+/-]mode") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [NICKNAME] [+/-]mode") return None nickname = str(cmd[1]) umode = str(cmd[2]) - self.Protocol.send_svsmode(nickname=nickname, user_mode=umode) + await self.ctx.Irc.Protocol.send_svsmode(nickname=nickname, user_mode=umode) except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'mode': # .mode #channel +/-mode @@ -684,100 +622,100 @@ class Command: try: if len(cmd) < 2: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#CHANNEL] [+/-]mode") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#CHANNEL] [+/-]mode") return None if fromchannel is None: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#CHANNEL] [+/-]mode") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#CHANNEL] [+/-]mode") return None if len(cmd) == 2: channel_mode = cmd[1] - if self.Channel.is_valid_channel(fromchannel): - self.Protocol.send2socket(f":{dnickname} MODE {fromchannel} {channel_mode}") + if self.ctx.Channel.is_valid_channel(fromchannel): + await self.ctx.Irc.Protocol.send2socket(f":{dnickname} MODE {fromchannel} {channel_mode}") else: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : Channel [{fromchannel}] is not correct should start with #") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : Channel [{fromchannel}] is not correct should start with #") return None if len(cmd) == 3: provided_channel = cmd[1] channel_mode = cmd[2] - self.Protocol.send2socket(f":{service_id} MODE {provided_channel} {channel_mode}") + await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {provided_channel} {channel_mode}") return None except IndexError as e: - self.Logs.warning(f'_hcmd OP: {str(e)}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#CHANNEL] [+/-]mode") + self.ctx.Logs.warning(f'_hcmd OP: {str(e)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#CHANNEL] [+/-]mode") except Exception as err: - self.Logs.warning(f'Unknown Error: {str(err)}') + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'get_mode': try: - self.Protocol.send2socket(f'MODE {fromchannel}') + await self.ctx.Irc.Protocol.send2socket(f'MODE {fromchannel}') except Exception as err: - self.Logs.error(f"General Error {err}") + self.ctx.Logs.error(f"General Error {err}") case 'svsjoin': try: # SVSJOIN [,..] [key1[,key2[..]]] if len(cmd) < 4: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} SVSJOIN [,..] [key1[,key2[..]]]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} SVSJOIN [,..] [key1[,key2[..]]]") return None nickname = str(cmd[1]) channels = str(cmd[2]).split(',') keys = str(cmd[3]).split(',') - self.Protocol.send_svsjoin(nickname, channels, keys) + await self.ctx.Irc.Protocol.send_svsjoin(nickname, channels, keys) except IndexError as ke: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} SVSJOIN [,..] [key1[,key2[..]]]") - self.Logs.error(ke) + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} SVSJOIN [,..] [key1[,key2[..]]]") + self.ctx.Logs.error(ke) except Exception as err: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} SVSJOIN [,..] [key1[,key2[..]]]") - self.Logs.warning(f'Unknown Error: {str(err)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} SVSJOIN [,..] [key1[,key2[..]]]") + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'svspart': try: # SVSPART [,..] [] if len(cmd) < 4: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} SVSPART [,..] []") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} SVSPART [,..] []") return None nickname = str(cmd[1]) channels = str(cmd[2]).split(',') reason = ' '.join(cmd[3:]) - self.Protocol.send_svspart(nickname, channels, reason) + await self.ctx.Irc.Protocol.send_svspart(nickname, channels, reason) except IndexError as ke: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} SVSPART [,..] []") - self.Logs.error(ke) + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} SVSPART [,..] []") + self.ctx.Logs.error(ke) except Exception as err: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} SVSPART [,..] []") - self.Logs.warning(f'Unknown Error: {str(err)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} SVSPART [,..] []") + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'svsnick': try: # .svsnick nickname newnickname nickname = str(cmd[1]) newnickname = str(cmd[2]) - unixtime = self.MainUtils.get_unixtime() + unixtime = self.ctx.Utils.get_unixtime() - if self.User.get_nickname(nickname) is None: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" This nickname do not exist") + if self.ctx.User.get_nickname(nickname) is None: + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" This nickname do not exist") return None if len(cmd) != 3: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname newnickname") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname newnickname") return None - self.Protocol.send2socket(f':{self.Config.SERVEUR_ID} SVSNICK {nickname} {newnickname} {unixtime}') + await self.ctx.Irc.Protocol.send2socket(f':{self.ctx.Config.SERVEUR_ID} SVSNICK {nickname} {newnickname} {unixtime}') except IndexError as ke: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname newnickname") - self.Logs.error(ke) + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname newnickname") + self.ctx.Logs.error(ke) except Exception as err: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname newnickname") - self.Logs.warning(f'Unknown Error: {str(err)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname newnickname") + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'sajoin': try: @@ -785,78 +723,78 @@ class Command: nickname = str(cmd[1]) channel = str(cmd[2]) if len(cmd) < 3: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname #channel") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname #channel") return None - self.Protocol.send_sajoin(nick_to_sajoin=nickname, channel_name=channel) + await self.ctx.Irc.Protocol.send_sajoin(nick_to_sajoin=nickname, channel_name=channel) except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname #channel") - self.Logs.warning(f'Unknown Error: {str(err)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname #channel") + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'sapart': try: # .sapart nickname #channel if len(cmd) < 3: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname #channel") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname #channel") return None nickname = str(cmd[1]) channel = str(cmd[2]) - self.Protocol.send_sapart(nick_to_sapart=nickname, channel_name=channel) + await self.ctx.Irc.Protocol.send_sapart(nick_to_sapart=nickname, channel_name=channel) except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname #channel") - self.Logs.error(f'Unknown Error: {str(err)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname #channel") + self.ctx.Logs.error(f'Unknown Error: {str(err)}') case 'kill': try: # 'kill', 'gline', 'ungline', 'shun', 'unshun' # .kill nickname reason if len(cmd) < 3: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname reason") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname reason") return None nickname = str(cmd[1]) kill_reason = ' '.join(cmd[2:]) - self.Protocol.send2socket(f":{service_id} KILL {nickname} {kill_reason} ({self.Config.COLORS.red}{dnickname}{self.Config.COLORS.nogc})") + await self.ctx.Irc.Protocol.send2socket(f":{service_id} KILL {nickname} {kill_reason} ({self.ctx.Config.COLORS.red}{dnickname}{self.ctx.Config.COLORS.nogc})") except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} SVSNICK nickname newnickname") - self.Logs.warning(f'Unknown Error: {str(err)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} SVSNICK nickname newnickname") + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'gline': try: # TKL + G user host set_by expire_timestamp set_at_timestamp :reason # .gline [nickname] [host] [reason] if len(cmd) < 4: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") return None nickname = str(cmd[1]) hostname = str(cmd[2]) - set_at_timestamp = self.MainUtils.get_unixtime() + set_at_timestamp = self.ctx.Utils.get_unixtime() expire_time = (60 * 60 * 24) + set_at_timestamp gline_reason = ' '.join(cmd[3:]) if nickname == '*' and hostname == '*': - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" You want to close the server ? i would recommand ./unrealircd stop :)") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" You want to close the server ? i would recommand ./unrealircd stop :)") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") return None - self.Protocol.send_gline(nickname=nickname, hostname=hostname, set_by=dnickname, expire_timestamp=expire_time, set_at_timestamp=set_at_timestamp, reason=gline_reason) + await self.ctx.Irc.Protocol.send_gline(nickname=nickname, hostname=hostname, set_by=dnickname, expire_timestamp=expire_time, set_at_timestamp=set_at_timestamp, reason=gline_reason) except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") - self.Logs.warning(f'Unknown Error: {str(err)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'ungline': try: @@ -864,47 +802,47 @@ class Command: # TKL + G user host set_by expire_timestamp set_at_timestamp :reason # .ungline nickname host if len(cmd) < 2: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname hostname") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname hostname") return None nickname = str(cmd[1]) hostname = str(cmd[2]) - # self.Protocol.send2socket(f":{self.Config.SERVEUR_ID} TKL - G {nickname} {hostname} {dnickname}") - self.Protocol.send_ungline(nickname=nickname, hostname=hostname) + # await self.ctx.Irc.Protocol.send2socket(f":{self.ctx.Config.SERVEUR_ID} TKL - G {nickname} {hostname} {dnickname}") + await self.ctx.Irc.Protocol.send_ungline(nickname=nickname, hostname=hostname) except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname hostname") - self.Logs.warning(f'Unknown Error: {str(err)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname hostname") + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'kline': try: # TKL + k user host set_by expire_timestamp set_at_timestamp :reason # .gline [nickname] [host] [reason] if len(cmd) < 4: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") return None nickname = str(cmd[1]) hostname = str(cmd[2]) - set_at_timestamp = self.MainUtils.get_unixtime() + set_at_timestamp = self.ctx.Utils.get_unixtime() expire_time = (60 * 60 * 24) + set_at_timestamp gline_reason = ' '.join(cmd[3:]) if nickname == '*' and hostname == '*': - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" You want to close the server ? i would recommand ./unrealircd stop :)") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" You want to close the server ? i would recommand ./unrealircd stop :)") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") return None - self.Protocol.send_kline(nickname=nickname, hostname=hostname, set_by=dnickname, expire_timestamp=expire_time, set_at_timestamp=set_at_timestamp, reason=gline_reason) + await self.ctx.Irc.Protocol.send_kline(nickname=nickname, hostname=hostname, set_by=dnickname, expire_timestamp=expire_time, set_at_timestamp=set_at_timestamp, reason=gline_reason) except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") - self.Logs.warning(f'Unknown Error: {str(err)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'unkline': try: @@ -912,19 +850,19 @@ class Command: # TKL + G user host set_by expire_timestamp set_at_timestamp :reason # .ungline nickname host if len(cmd) < 2: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname hostname") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname hostname") return None nickname = str(cmd[1]) hostname = str(cmd[2]) - self.Protocol.send_unkline(nickname=nickname, hostname=hostname) + await self.ctx.Irc.Protocol.send_unkline(nickname=nickname, hostname=hostname) except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname hostname") - self.Logs.warning(f'Unknown Error: {str(err)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname hostname") + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'shun': try: @@ -932,26 +870,26 @@ class Command: # .shun [nickname] [host] [reason] if len(cmd) < 4: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") return None nickname = str(cmd[1]) hostname = str(cmd[2]) - set_at_timestamp = self.MainUtils.get_unixtime() + set_at_timestamp = self.ctx.Utils.get_unixtime() expire_time = (60 * 60 * 24) + set_at_timestamp shun_reason = ' '.join(cmd[3:]) if nickname == '*' and hostname == '*': - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" You want to close the server ? i would recommand ./unrealircd stop :)") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" You want to close the server ? i would recommand ./unrealircd stop :)") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") return None - self.Protocol.send2socket(f":{self.Config.SERVEUR_ID} TKL + s {nickname} {hostname} {dnickname} {expire_time} {set_at_timestamp} :{shun_reason}") + await self.ctx.Irc.Protocol.send2socket(f":{self.ctx.Config.SERVEUR_ID} TKL + s {nickname} {hostname} {dnickname} {expire_time} {set_at_timestamp} :{shun_reason}") except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") - self.Logs.warning(f'Unknown Error: {str(err)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'unshun': try: @@ -959,52 +897,52 @@ class Command: # TKL + G user host set_by expire_timestamp set_at_timestamp :reason # .unshun nickname host if len(cmd) < 2: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname hostname") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname hostname") return None nickname = str(cmd[1]) hostname = str(cmd[2]) - self.Protocol.send2socket(f":{self.Config.SERVEUR_ID} TKL - s {nickname} {hostname} {dnickname}") + await self.ctx.Irc.Protocol.send2socket(f":{self.ctx.Config.SERVEUR_ID} TKL - s {nickname} {hostname} {dnickname}") except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname hostname") - self.Logs.warning(f'Unknown Error: {str(err)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname hostname") + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'glinelist': try: self.user_to_notice = fromuser - self.Protocol.send2socket(f":{self.Config.SERVICE_ID} STATS G") + await self.ctx.Irc.Protocol.send2socket(f":{self.ctx.Config.SERVICE_ID} STATS G") except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()}") - self.Logs.warning(f'Unknown Error: {str(err)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()}") + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'shunlist': try: self.user_to_notice = fromuser - self.Protocol.send2socket(f":{self.Config.SERVICE_ID} STATS s") + await self.ctx.Irc.Protocol.send2socket(f":{self.ctx.Config.SERVICE_ID} STATS s") except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()}") - self.Logs.warning(f'Unknown Error: {str(err)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()}") + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case 'klinelist': try: self.user_to_notice = fromuser - self.Protocol.send2socket(f":{self.Config.SERVICE_ID} STATS k") + await self.ctx.Irc.Protocol.send2socket(f":{self.ctx.Config.SERVICE_ID} STATS k") except KeyError as ke: - self.Logs.error(ke) + self.ctx.Logs.error(ke) except Exception as err: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()}") - self.Logs.warning(f'Unknown Error: {str(err)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()}") + self.ctx.Logs.warning(f'Unknown Error: {str(err)}') case _: pass diff --git a/mods/command/utils.py b/mods/command/utils.py index d7ef806..3a5f521 100644 --- a/mods/command/utils.py +++ b/mods/command/utils.py @@ -4,243 +4,243 @@ if TYPE_CHECKING: from mods.command.mod_command import Command -def set_automode(uplink: 'Command', cmd: list[str], client: str) -> None: +async def set_automode(uplink: 'Command', cmd: list[str], client: str) -> None: command: str = str(cmd[0]).lower() option: str = str(cmd[1]).lower() - allowed_modes: list[str] = uplink.Loader.Settings.PROTOCTL_PREFIX # ['q','a','o','h','v'] - dnickname = uplink.Config.SERVICE_NICKNAME - service_id = uplink.Config.SERVICE_ID + allowed_modes: list[str] = uplink.ctx.Settings.PROTOCTL_PREFIX # ['q','a','o','h','v'] + dnickname = uplink.ctx.Config.SERVICE_NICKNAME + service_id = uplink.ctx.Config.SERVICE_ID fromuser = client match option: case 'set': if len(cmd) < 5: - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} [nickname] [+/-mode] [#channel]") - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"AutoModes available: {' / '.join(allowed_modes)}") + await uplink.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} [nickname] [+/-mode] [#channel]") + await uplink.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"AutoModes available: {' / '.join(allowed_modes)}") return None nickname = str(cmd[2]) mode = str(cmd[3]) - chan: str = str(cmd[4]).lower() if uplink.Channel.is_valid_channel(cmd[4]) else None + chan: str = str(cmd[4]).lower() if uplink.ctx.Channel.is_valid_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: - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You must provide the flag mode + or -") + await uplink.ctx.Irc.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: - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You should use one of those modes {' / '.join(allowed_modes)}") + await uplink.ctx.Irc.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: - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You should use one of those modes {' / '.join(allowed_modes)}") + await uplink.ctx.Irc.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 = uplink.Base.db_execute_query(query="SELECT id FROM command_automode WHERE nickname = :nickname and channel = :channel", params=db_data) + db_query = await uplink.ctx.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": uplink.MainUtils.get_sdatetime(), "nickname": nickname, "channel": chan, "mode": mode} - db_result = uplink.Base.db_execute_query(query="UPDATE command_automode SET mode = :mode, updated_on = :updated_on WHERE nickname = :nickname and channel = :channel", + db_data = {"updated_on": uplink.ctx.Utils.get_sdatetime(), "nickname": nickname, "channel": chan, "mode": mode} + db_result = await uplink.ctx.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: - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Automode {mode} edited for {nickname} in {chan}") + await uplink.ctx.Irc.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 = uplink.Base.db_execute_query(query="DELETE FROM command_automode WHERE nickname = :nickname and channel = :channel and mode = :mode", + db_result = await uplink.ctx.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: - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Automode {mode} deleted for {nickname} in {chan}") + await uplink.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Automode {mode} deleted for {nickname} in {chan}") else: - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"The mode [{mode}] has not been found for {nickname} in channel {chan}") + await uplink.ctx.Irc.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": uplink.MainUtils.get_sdatetime(), "updated_on": uplink.MainUtils.get_sdatetime(), "nickname": nickname, "channel": chan, "mode": mode} - db_query = uplink.Base.db_execute_query( + db_data = {"created_on": uplink.ctx.Utils.get_sdatetime(), "updated_on": uplink.ctx.Utils.get_sdatetime(), "nickname": nickname, "channel": chan, "mode": mode} + db_query = await uplink.ctx.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: - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Automode {mode} applied to {nickname} in {chan}") - if uplink.Channel.is_user_present_in_channel(chan, uplink.User.get_uid(nickname)): - uplink.Protocol.send2socket(f":{service_id} MODE {chan} {mode} {nickname}") + await uplink.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Automode {mode} applied to {nickname} in {chan}") + if uplink.ctx.Channel.is_user_present_in_channel(chan, uplink.ctx.User.get_uid(nickname)): + await uplink.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {chan} {mode} {nickname}") else: - uplink.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") + await uplink.ctx.Irc.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 = uplink.Base.db_execute_query("SELECT nickname, channel, mode FROM command_automode") + db_query = await uplink.ctx.Base.db_execute_query("SELECT nickname, channel, mode FROM command_automode") db_results = db_query.fetchall() if not db_results: - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, + await uplink.ctx.Irc.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 - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, + await uplink.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nickname: {db_nickname} | Channel: {db_channel} | Mode: {db_mode}") case _: - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} SET [nickname] [+/-mode] [#channel]") - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} LIST") - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"[AUTOMODES AVAILABLE] are {' / '.join(allowed_modes)}") + await uplink.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} SET [nickname] [+/-mode] [#channel]") + await uplink.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} LIST") + await uplink.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"[AUTOMODES AVAILABLE] are {' / '.join(allowed_modes)}") -def set_deopall(uplink: 'Command', channel_name: str) -> None: +async def set_deopall(uplink: 'Command', channel_name: str) -> None: - service_id = uplink.Config.SERVICE_ID - uplink.Protocol.send2socket(f":{service_id} SVSMODE {channel_name} -o") + service_id = uplink.ctx.Config.SERVICE_ID + await uplink.ctx.Irc.Protocol.send2socket(f":{service_id} SVSMODE {channel_name} -o") return None -def set_devoiceall(uplink: 'Command', channel_name: str) -> None: +async def set_devoiceall(uplink: 'Command', channel_name: str) -> None: - service_id = uplink.Config.SERVICE_ID - uplink.Protocol.send2socket(f":{service_id} SVSMODE {channel_name} -v") + service_id = uplink.ctx.Config.SERVICE_ID + await uplink.ctx.Irc.Protocol.send2socket(f":{service_id} SVSMODE {channel_name} -v") return None -def set_mode_to_all(uplink: 'Command', channel_name: str, action: Literal['+', '-'], pmode: str) -> None: +async def set_mode_to_all(uplink: 'Command', channel_name: str, action: Literal['+', '-'], pmode: str) -> None: - chan_info = uplink.Channel.get_channel(channel_name) - service_id = uplink.Config.SERVICE_ID - dnickname = uplink.Config.SERVICE_NICKNAME + chan_info = uplink.ctx.Channel.get_channel(channel_name) + service_id = uplink.ctx.Config.SERVICE_ID + dnickname = uplink.ctx.Config.SERVICE_NICKNAME set_mode = pmode mode:str = '' users:str = '' uids_split = [chan_info.uids[i:i + 6] for i in range(0, len(chan_info.uids), 6)] - uplink.Protocol.send2socket(f":{service_id} MODE {channel_name} {action}{set_mode} {dnickname}") + await uplink.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {channel_name} {action}{set_mode} {dnickname}") for uid in uids_split: for i in range(0, len(uid)): mode += set_mode - users += f'{uplink.User.get_nickname(uplink.MainUtils.clean_uid(uid[i]))} ' + users += f'{uplink.ctx.User.get_nickname(uplink.ctx.Utils.clean_uid(uid[i]))} ' if i == len(uid) - 1: - uplink.Protocol.send2socket(f":{service_id} MODE {channel_name} {action}{mode} {users}") + await uplink.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {channel_name} {action}{mode} {users}") mode = '' users = '' -def set_operation(uplink: 'Command', cmd: list[str], channel_name: Optional[str], client: str, mode: str) -> None: +async def set_operation(uplink: 'Command', cmd: list[str], channel_name: Optional[str], client: str, mode: str) -> None: - dnickname = uplink.Config.SERVICE_NICKNAME - service_id = uplink.Config.SERVICE_ID + dnickname = uplink.ctx.Config.SERVICE_NICKNAME + service_id = uplink.ctx.Config.SERVICE_ID if channel_name is None: - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f" Right command : /msg {dnickname} {mode} [#SALON] [NICKNAME]") + await uplink.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f" Right command : /msg {dnickname} {mode} [#SALON] [NICKNAME]") return False if len(cmd) == 1: - # uplink.Protocol.send2socket(f":{service_id} MODE {channel_name} {mode} {client}") - uplink.Protocol.send_set_mode(mode, nickname=client, channel_name=channel_name) + # await uplink.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {channel_name} {mode} {client}") + await uplink.ctx.Irc.Protocol.send_set_mode(mode, nickname=client, channel_name=channel_name) return None # deop nickname if len(cmd) == 2: nickname = cmd[1] - # uplink.Protocol.send2socket(f":{service_id} MODE {channel_name} {mode} {nickname}") - uplink.Protocol.send_set_mode(mode, nickname=nickname, channel_name=channel_name) + # await uplink.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {channel_name} {mode} {nickname}") + await uplink.ctx.Irc.Protocol.send_set_mode(mode, nickname=nickname, channel_name=channel_name) return None nickname = cmd[2] - # uplink.Protocol.send2socket(f":{service_id} MODE {channel_name} {mode} {nickname}") - uplink.Protocol.send_set_mode(mode, nickname=nickname, channel_name=channel_name) + # await uplink.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {channel_name} {mode} {nickname}") + await uplink.ctx.Irc.Protocol.send_set_mode(mode, nickname=nickname, channel_name=channel_name) return None -def set_ban(uplink: 'Command', cmd: list[str], action: Literal['+', '-'], client: str) -> None: +async def set_ban(uplink: 'Command', cmd: list[str], action: Literal['+', '-'], client: str) -> None: command = str(cmd[0]) - dnickname = uplink.Config.SERVICE_NICKNAME - service_id = uplink.Config.SERVICE_ID - sentchannel = str(cmd[1]) if uplink.Channel.is_valid_channel(cmd[1]) else None + dnickname = uplink.ctx.Config.SERVICE_NICKNAME + service_id = uplink.ctx.Config.SERVICE_ID + sentchannel = str(cmd[1]) if uplink.ctx.Channel.is_valid_channel(cmd[1]) else None if sentchannel is None: - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]") + await uplink.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]") return None nickname = cmd[2] - uplink.Protocol.send2socket(f":{service_id} MODE {sentchannel} {action}b {nickname}!*@*") - uplink.Logs.debug(f'{client} has banned {nickname} from {sentchannel}') + await uplink.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {sentchannel} {action}b {nickname}!*@*") + uplink.ctx.Logs.debug(f'{client} has banned {nickname} from {sentchannel}') return None -def set_kick(uplink: 'Command', cmd: list[str], client: str) -> None: +async def set_kick(uplink: 'Command', cmd: list[str], client: str) -> None: command = str(cmd[0]) - dnickname = uplink.Config.SERVICE_NICKNAME - service_id = uplink.Config.SERVICE_ID + dnickname = uplink.ctx.Config.SERVICE_NICKNAME + service_id = uplink.ctx.Config.SERVICE_ID - sentchannel = str(cmd[1]) if uplink.Channel.is_valid_channel(cmd[1]) else None + sentchannel = str(cmd[1]) if uplink.ctx.Channel.is_valid_channel(cmd[1]) else None if sentchannel is None: - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f" Right command : /msg {dnickname} {command} [#SALON] [NICKNAME]") + await uplink.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f" Right command : /msg {dnickname} {command} [#SALON] [NICKNAME]") return False nickname = cmd[2] final_reason = ' '.join(cmd[3:]) - uplink.Protocol.send2socket(f":{service_id} KICK {sentchannel} {nickname} {final_reason}") - uplink.Logs.debug(f'{client} has kicked {nickname} from {sentchannel} : {final_reason}') + await uplink.ctx.Irc.Protocol.send2socket(f":{service_id} KICK {sentchannel} {nickname} {final_reason}") + uplink.ctx.Logs.debug(f'{client} has kicked {nickname} from {sentchannel} : {final_reason}') return None -def set_kickban(uplink: 'Command', cmd: list[str], client: str) -> None: +async def set_kickban(uplink: 'Command', cmd: list[str], client: str) -> None: command = str(cmd[0]) - dnickname = uplink.Config.SERVICE_NICKNAME - service_id = uplink.Config.SERVICE_ID + dnickname = uplink.ctx.Config.SERVICE_NICKNAME + service_id = uplink.ctx.Config.SERVICE_ID - sentchannel = str(cmd[1]) if uplink.Channel.is_valid_channel(cmd[1]) else None + sentchannel = str(cmd[1]) if uplink.ctx.Channel.is_valid_channel(cmd[1]) else None if sentchannel is None: - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f" Right command : /msg {dnickname} {command} [#SALON] [NICKNAME]") + await uplink.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f" Right command : /msg {dnickname} {command} [#SALON] [NICKNAME]") return False nickname = cmd[2] final_reason = ' '.join(cmd[3:]) - uplink.Protocol.send2socket(f":{service_id} KICK {sentchannel} {nickname} {final_reason}") - uplink.Protocol.send2socket(f":{service_id} MODE {sentchannel} +b {nickname}!*@*") - uplink.Logs.debug(f'{client} has kicked and banned {nickname} from {sentchannel} : {final_reason}') + await uplink.ctx.Irc.Protocol.send2socket(f":{service_id} KICK {sentchannel} {nickname} {final_reason}") + await uplink.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {sentchannel} +b {nickname}!*@*") + uplink.ctx.Logs.debug(f'{client} has kicked and banned {nickname} from {sentchannel} : {final_reason}') -def set_assign_channel_to_service(uplink: 'Command', cmd: list[str], client: str) -> None: +async def set_assign_channel_to_service(uplink: 'Command', cmd: list[str], client: str) -> None: if len(cmd) < 2: raise IndexError(f"{cmd[0].upper()} is expecting the channel parameter") command = str(cmd[0]) - dnickname = uplink.Config.SERVICE_NICKNAME - sent_channel = str(cmd[1]) if uplink.Channel.is_valid_channel(cmd[1]) else None + dnickname = uplink.ctx.Config.SERVICE_NICKNAME + sent_channel = str(cmd[1]) if uplink.ctx.Channel.is_valid_channel(cmd[1]) else None if sent_channel is None: - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON]") + await uplink.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON]") return None # self.Protocol.send2socket(f':{service_id} JOIN {sent_channel}') - uplink.Protocol.send_join_chan(uidornickname=dnickname,channel=sent_channel) - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f" Has joined {sent_channel}") - uplink.Channel.db_query_channel('add', uplink.module_name, sent_channel) + await uplink.ctx.Irc.Protocol.send_join_chan(uidornickname=dnickname,channel=sent_channel) + await uplink.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f" Has joined {sent_channel}") + await uplink.ctx.Channel.db_query_channel('add', uplink.module_name, sent_channel) return None -def set_unassign_channel_to_service(uplink: 'Command', cmd: list[str], client: str) -> None: +async def set_unassign_channel_to_service(uplink: 'Command', cmd: list[str], client: str) -> None: if len(cmd) < 2: raise IndexError(f"{cmd[0].upper()} is expecting the channel parameter") command = str(cmd[0]) - dnickname = uplink.Config.SERVICE_NICKNAME - dchanlog = uplink.Config.SERVICE_CHANLOG + dnickname = uplink.ctx.Config.SERVICE_NICKNAME + dchanlog = uplink.ctx.Config.SERVICE_CHANLOG - sent_channel = str(cmd[1]) if uplink.Channel.is_valid_channel(cmd[1]) else None + sent_channel = str(cmd[1]) if uplink.ctx.Channel.is_valid_channel(cmd[1]) else None if sent_channel is None: - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON]") + await uplink.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON]") return None if sent_channel == dchanlog: - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f"[!] CAN'T LEFT {sent_channel} AS IT IS LOG CHANNEL [!]") + await uplink.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f"[!] CAN'T LEFT {sent_channel} AS IT IS LOG CHANNEL [!]") return None - uplink.Protocol.send_part_chan(uidornickname=dnickname, channel=sent_channel) - uplink.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f" Has left {sent_channel}") + await uplink.ctx.Irc.Protocol.send_part_chan(uidornickname=dnickname, channel=sent_channel) + await uplink.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=client, msg=f" Has left {sent_channel}") - uplink.Channel.db_query_channel('del', uplink.module_name, sent_channel) + await uplink.ctx.Channel.db_query_channel('del', uplink.module_name, sent_channel) return None \ No newline at end of file diff --git a/mods/defender/mod_defender.py b/mods/defender/mod_defender.py index d727f9c..1127a3e 100644 --- a/mods/defender/mod_defender.py +++ b/mods/defender/mod_defender.py @@ -1,129 +1,37 @@ -from typing import TYPE_CHECKING +from dataclasses import dataclass +from typing import Any, TYPE_CHECKING, Optional +from core.classes.interfaces.imodule import IModule import mods.defender.schemas as schemas import mods.defender.utils as utils import mods.defender.threads as thds from core.utils import tr if TYPE_CHECKING: - from core.irc import Irc + from core.loader import Loader -class Defender: +class Defender(IModule): - def __init__(self, irc_instance: 'Irc') -> None: + @dataclass + class ModConfModel(schemas.ModConfModel): + ... - # Module name (Mandatory) - self.module_name = 'mod_' + str(self.__class__.__name__).lower() + MOD_HEADER: dict[str, str] = { + 'name':'Defender', + 'version':'1.0.0', + 'description':'Defender main module that uses the reputation security.', + 'author':'Defender Team', + 'core_version':'Defender-6' + } - # Add Irc Object to the module (Mandatory) - self.Irc = irc_instance + def __init__(self, context: 'Loader') -> None: + super().__init__(context) + self._mod_config: Optional[schemas.ModConfModel] = None - # Add Loader Object to the module (Mandatory) - self.Loader = irc_instance.Loader + @property + def mod_config(self) -> ModConfModel: + return self._mod_config - # Add server protocol Object to the module (Mandatory) - self.Protocol = irc_instance.Protocol - - # Add Global Configuration to the module (Mandatory) - self.Config = irc_instance.Config - - # Add Base object to the module (Mandatory) - self.Base = irc_instance.Base - - # Add logs object to the module (Mandatory) - self.Logs = irc_instance.Loader.Logs - - # Add User object to the module (Mandatory) - self.User = irc_instance.User - - # Add Channel object to the module (Mandatory) - self.Channel = irc_instance.Channel - - # Add Settings object to save objects when reloading modules (Mandatory) - self.Settings = irc_instance.Settings - - # Add Reputation object to the module (Optional) - self.Reputation = irc_instance.Reputation - - # Add module schemas - self.Schemas = schemas - - # Add utils functions - self.Utils = utils - - # Create module commands (Mandatory) - self.Irc.build_command(0, self.module_name, 'code', 'Display the code or key for access') - self.Irc.build_command(1, self.module_name, 'info', 'Provide information about the channel or server') - self.Irc.build_command(1, self.module_name, 'autolimit', 'Automatically set channel user limits') - self.Irc.build_command(3, self.module_name, 'reputation', 'Check or manage user reputation') - self.Irc.build_command(3, self.module_name, 'proxy_scan', 'Scan users for proxy connections') - self.Irc.build_command(3, self.module_name, 'flood', 'Handle flood detection and mitigation') - self.Irc.build_command(3, self.module_name, 'status', 'Check the status of the server or bot') - self.Irc.build_command(3, self.module_name, 'timer', 'Set or manage timers') - self.Irc.build_command(3, self.module_name, 'show_reputation', 'Display reputation information') - self.Irc.build_command(3, self.module_name, 'sentinel', 'Monitor and guard the channel or server') - - # Init the module (Mandatory) - self.__init_module() - - # Log the module - self.Logs.debug(f'-- Module {self.module_name} V2 loaded ...') - - def __init_module(self) -> None: - - # Create you own tables if needed (Mandatory) - self.__create_tables() - - # Load module configuration (Mandatory) - self.__load_module_configuration() - # End of mandatory methods you can start your customization # - - self.timeout = self.Config.API_TIMEOUT - - # Listes qui vont contenir les ip a scanner avec les différentes API - self.Schemas.DB_ABUSEIPDB_USERS = [] - self.Schemas.DB_FREEIPAPI_USERS = [] - self.Schemas.DB_CLOUDFILT_USERS = [] - self.Schemas.DB_PSUTIL_USERS = [] - self.Schemas.DB_LOCALSCAN_USERS = [] - - # Variables qui indique que les threads sont en cours d'éxecutions - self.abuseipdb_isRunning:bool = True - self.freeipapi_isRunning:bool = True - self.cloudfilt_isRunning:bool = True - self.psutil_isRunning:bool = True - self.localscan_isRunning:bool = True - self.reputationTimer_isRunning:bool = True - self.autolimit_isRunning: bool = True - - # Variable qui va contenir les users - self.flood_system = {} - - # Contient les premieres informations de connexion - self.reputation_first_connexion = {'ip': '', 'score': -1} - - # Laisser vide si aucune clé - self.abuseipdb_key = '13c34603fee4d2941a2c443cc5c77fd750757ca2a2c1b304bd0f418aff80c24be12651d1a3cfe674' - self.cloudfilt_key = 'r1gEtjtfgRQjtNBDMxsg' - - # Démarrer les threads pour démarrer les api - self.Base.create_thread(func=thds.thread_freeipapi_scan, func_args=(self, )) - self.Base.create_thread(func=thds.thread_cloudfilt_scan, func_args=(self, )) - self.Base.create_thread(func=thds.thread_abuseipdb_scan, func_args=(self, )) - self.Base.create_thread(func=thds.thread_local_scan, func_args=(self, )) - self.Base.create_thread(func=thds.thread_psutil_scan, func_args=(self, )) - - self.Base.create_thread(func=thds.thread_apply_reputation_sanctions, func_args=(self, )) - - if self.ModConfig.autolimit == 1: - self.Base.create_thread(func=thds.thread_autolimit, func_args=(self, )) - - if self.ModConfig.reputation == 1: - self.Protocol.send_sjoin(self.Config.SALON_JAIL) - self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} SAMODE {self.Config.SALON_JAIL} +o {self.Config.SERVICE_NICKNAME}") - - return None - - def __create_tables(self) -> None: + def create_tables(self) -> None: """Methode qui va créer la base de donnée si elle n'existe pas. Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module Args: @@ -141,33 +49,79 @@ class Defender: # ) # ''' - # self.Base.db_execute_query(table_autoop) - # self.Base.db_execute_query(table_config) - # self.Base.db_execute_query(table_trusted) + # self.ctx.Base.db_execute_query(table_autoop) + # self.ctx.Base.db_execute_query(table_config) + # self.ctx.Base.db_execute_query(table_trusted) return None - def __load_module_configuration(self) -> None: - """### Load Module Configuration - """ + async def load(self): + # Variable qui va contenir les options de configuration du module Defender - self.ModConfig = self.Schemas.ModConfModel() + self._mod_config: schemas.ModConfModel = self.ModConfModel() - # Sync the configuration with core configuration (Mandatory) - self.Base.db_sync_core_config(self.module_name, self.ModConfig) + # sync the database with local variable (Mandatory) + await self.sync_db() - return None + # Add module schemas + self.Schemas = schemas - def __update_configuration(self, param_key: str, param_value: str): + # Add module utils functions + self.mod_utils = utils + + # Create module commands (Mandatory) + self.ctx.Irc.build_command(0, self.module_name, 'code', 'Display the code or key for access') + self.ctx.Irc.build_command(1, self.module_name, 'info', 'Provide information about the channel or server') + self.ctx.Irc.build_command(1, self.module_name, 'autolimit', 'Automatically set channel user limits') + self.ctx.Irc.build_command(3, self.module_name, 'reputation', 'Check or manage user reputation') + self.ctx.Irc.build_command(3, self.module_name, 'proxy_scan', 'Scan users for proxy connections') + self.ctx.Irc.build_command(3, self.module_name, 'flood', 'Handle flood detection and mitigation') + self.ctx.Irc.build_command(3, self.module_name, 'status', 'Check the status of the server or bot') + self.ctx.Irc.build_command(3, self.module_name, 'show_reputation', 'Display reputation information') + self.ctx.Irc.build_command(3, self.module_name, 'sentinel', 'Monitor and guard the channel or server') - self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value) + self.timeout = self.ctx.Config.API_TIMEOUT + + # Listes qui vont contenir les ip a scanner avec les différentes API + self.Schemas.DB_ABUSEIPDB_USERS = self.Schemas.DB_FREEIPAPI_USERS = self.Schemas.DB_CLOUDFILT_USERS = [] + self.Schemas.DB_PSUTIL_USERS = self.Schemas.DB_LOCALSCAN_USERS = [] + + # Variables qui indique que les threads sont en cours d'éxecutions + self.abuseipdb_isRunning = self.freeipapi_isRunning = self.cloudfilt_isRunning = True + self.psutil_isRunning = self.localscan_isRunning = self.reputationTimer_isRunning = True + self.autolimit_isRunning = True + + # Variable qui va contenir les users + self.flood_system = {} + + # Contient les premieres informations de connexion + self.reputation_first_connexion = {'ip': '', 'score': -1} + + # Laisser vide si aucune clé + self.abuseipdb_key = '13c34603fee4d2941a2c443cc5c77fd750757ca2a2c1b304bd0f418aff80c24be12651d1a3cfe674' + self.cloudfilt_key = 'r1gEtjtfgRQjtNBDMxsg' + + # Démarrer les threads pour démarrer les api + self.ctx.Base.create_asynctask(thds.coro_freeipapi_scan(self)) + self.ctx.Base.create_asynctask(thds.coro_cloudfilt_scan(self)) + self.ctx.Base.create_asynctask(thds.coro_abuseipdb_scan(self)) + self.ctx.Base.create_asynctask(thds.coro_local_scan(self)) + self.ctx.Base.create_asynctask(thds.coro_psutil_scan(self)) + self.ctx.Base.create_asynctask(thds.coro_apply_reputation_sanctions(self)) + + if self.mod_config.autolimit == 1: + self.ctx.Base.create_asynctask(thds.coro_autolimit(self)) + + if self.mod_config.reputation == 1: + await self.ctx.Irc.Protocol.send_sjoin(self.ctx.Config.SALON_JAIL) + await self.ctx.Irc.Protocol.send2socket(f":{self.ctx.Config.SERVICE_NICKNAME} SAMODE {self.ctx.Config.SALON_JAIL} +o {self.ctx.Config.SERVICE_NICKNAME}") def __onload(self): - abuseipdb = self.Settings.get_cache('ABUSEIPDB') - freeipapi = self.Settings.get_cache('FREEIPAPI') - cloudfilt = self.Settings.get_cache('CLOUDFILT') - psutils = self.Settings.get_cache('PSUTIL') - localscan = self.Settings.get_cache('LOCALSCAN') + abuseipdb = self.ctx.Settings.get_cache('ABUSEIPDB') + freeipapi = self.ctx.Settings.get_cache('FREEIPAPI') + cloudfilt = self.ctx.Settings.get_cache('CLOUDFILT') + psutils = self.ctx.Settings.get_cache('PSUTIL') + localscan = self.ctx.Settings.get_cache('LOCALSCAN') if abuseipdb: self.Schemas.DB_ABUSEIPDB_USERS = abuseipdb @@ -202,269 +156,240 @@ class Defender: self.reputationTimer_isRunning:bool = False self.autolimit_isRunning: bool = False - self.Irc.Commands.drop_command_by_module(self.module_name) + self.ctx.Commands.drop_command_by_module(self.module_name) return None - def insert_db_trusted(self, uid: str, nickname:str) -> None: + async def insert_db_trusted(self, uid: str, nickname:str) -> None: + u = self.ctx.User.get_user(uid) + if u is None: + return None - uid = self.User.get_uid(uid) - nickname = self.User.get_nickname(nickname) + uid = u.uid + nickname = u.nickname query = "SELECT id FROM def_trusted WHERE user = ?" - exec_query = self.Base.db_execute_query(query, {"user": nickname}) + exec_query = await self.ctx.Base.db_execute_query(query, {"user": nickname}) response = exec_query.fetchone() if response is not None: q_insert = "INSERT INTO def_trusted (datetime, user, host, vhost) VALUES (?, ?, ?, ?)" - mes_donnees = {'datetime': self.Loader.Utils.get_datetime(), 'user': nickname, 'host': '*', 'vhost': '*'} - exec_query = self.Base.db_execute_query(q_insert, mes_donnees) + mes_donnees = {'datetime': self.ctx.mod_utils.get_datetime(), 'user': nickname, 'host': '*', 'vhost': '*'} + exec_query = self.ctx.Base.db_execute_query(q_insert, mes_donnees) pass - def join_saved_channels(self) -> None: + async def join_saved_channels(self) -> None: """_summary_ """ try: - result = self.Base.db_execute_query(f"SELECT distinct channel_name FROM {self.Config.TABLE_CHANNEL}") + result = await self.ctx.Base.db_execute_query(f"SELECT distinct channel_name FROM {self.ctx.Config.TABLE_CHANNEL}") channels = result.fetchall() - jail_chan = self.Config.SALON_JAIL - jail_chan_mode = self.Config.SALON_JAIL_MODES - service_id = self.Config.SERVICE_ID - dumodes = self.Config.SERVICE_UMODES - dnickname = self.Config.SERVICE_NICKNAME + jail_chan = self.ctx.Config.SALON_JAIL + jail_chan_mode = self.ctx.Config.SALON_JAIL_MODES + service_id = self.ctx.Config.SERVICE_ID + dumodes = self.ctx.Config.SERVICE_UMODES + dnickname = self.ctx.Config.SERVICE_NICKNAME for channel in channels: chan = channel[0] - self.Protocol.send_sjoin(chan) + await self.ctx.Irc.Protocol.send_sjoin(chan) if chan == jail_chan: - self.Protocol.send2socket(f":{service_id} SAMODE {jail_chan} +{dumodes} {dnickname}") - self.Protocol.send2socket(f":{service_id} MODE {jail_chan} +{jail_chan_mode}") + await self.ctx.Irc.Protocol.send2socket(f":{service_id} SAMODE {jail_chan} +{dumodes} {dnickname}") + await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {jail_chan} +{jail_chan_mode}") return None except Exception as err: - self.Logs.error(f"General Error: {err}") + self.ctx.Logs.error(f"General Error: {err}") - def run_db_action_timer(self, wait_for: float = 0) -> None: - - query = f"SELECT param_key FROM {self.Config.TABLE_CONFIG}" - res = self.Base.db_execute_query(query) - service_id = self.Config.SERVICE_ID - dchanlog = self.Config.SERVICE_CHANLOG - - for param in res.fetchall(): - if param[0] == 'reputation': - self.Protocol.send_priv_msg( - nick_from=service_id, - msg=f" ===> {param[0]}", - channel=dchanlog - ) - else: - self.Protocol.send_priv_msg( - nick_from=service_id, - msg=f"{param[0]}", - channel=dchanlog - ) - - return None - - def cmd(self, data: list[str]) -> None: + async def cmd(self, data: list[str]) -> None: if not data or len(data) < 2: return None cmd = data.copy() if isinstance(data, list) else list(data).copy() try: - index, command = self.Irc.Protocol.get_ircd_protocol_poisition(cmd) + index, command = self.ctx.Irc.Protocol.get_ircd_protocol_poisition(cmd) if index == -1: return None match command: case 'REPUTATION': - self.Utils.handle_on_reputation(self, cmd) + self.mod_utils.handle_on_reputation(self, cmd) return None case 'MODE': - self.Utils.handle_on_mode(self, cmd) + await self.mod_utils.handle_on_mode(self, cmd) return None case 'PRIVMSG': - self.Utils.handle_on_privmsg(self, cmd) + await self.mod_utils.handle_on_privmsg(self, cmd) return None case 'UID': - self.Utils.handle_on_uid(self, cmd) + await self.mod_utils.handle_on_uid(self, cmd) return None case 'SJOIN': - - self.Utils.handle_on_sjoin(self, cmd) + await self.mod_utils.handle_on_sjoin(self, cmd) return None case 'SLOG': - self.Utils.handle_on_slog(self, cmd) + self.mod_utils.handle_on_slog(self, cmd) return None case 'NICK': - self.Utils.handle_on_nick(self, cmd) + await self.mod_utils.handle_on_nick(self, cmd) return None case 'QUIT': - self.Utils.handle_on_quit(self, cmd) + await self.mod_utils.handle_on_quit(self, cmd) return None case _: return None except KeyError as ke: - self.Logs.error(f"{ke} / {cmd} / length {str(len(cmd))}") + self.ctx.Logs.error(f"{ke} / {cmd} / length {str(len(cmd))}") except IndexError as ie: - self.Logs.error(f"{ie} / {cmd} / length {str(len(cmd))}") + self.ctx.Logs.error(f"{ie} / {cmd} / length {str(len(cmd))}") except Exception as err: - self.Logs.error(f"General Error: {err}", exc_info=True) + self.ctx.Logs.error(f"General Error: {err}", exc_info=True) - def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None: + async def hcmds(self, user: str, channel: Any, cmd: list, fullcmd: list = []) -> None: + u = self.ctx.User.get_user(user) + if u is None: + return None command = str(cmd[0]).lower() - fromuser = user - channel = fromchannel = channel if self.Channel.is_valid_channel(channel) else None + fromuser = u.nickname + channel = fromchannel = channel if self.ctx.Channel.is_valid_channel(channel) else None - dnickname = self.Config.SERVICE_NICKNAME # Defender nickname - dchanlog = self.Config.SERVICE_CHANLOG # Defender chan log - dumodes = self.Config.SERVICE_UMODES # Les modes de Defender - service_id = self.Config.SERVICE_ID # Defender serveur id - jail_chan = self.Config.SALON_JAIL # Salon pot de miel - jail_chan_mode = self.Config.SALON_JAIL_MODES # Mode du salon "pot de miel" + dnickname = self.ctx.Config.SERVICE_NICKNAME # Defender nickname + dchanlog = self.ctx.Config.SERVICE_CHANLOG # Defender chan log + dumodes = self.ctx.Config.SERVICE_UMODES # Les modes de Defender + service_id = self.ctx.Config.SERVICE_ID # Defender serveur id + jail_chan = self.ctx.Config.SALON_JAIL # Salon pot de miel + jail_chan_mode = self.ctx.Config.SALON_JAIL_MODES # Mode du salon "pot de miel" match command: - case 'timer': - try: - timer_sent = self.Base.int_if_possible(cmd[1]) - timer_sent = int(timer_sent) - self.Base.create_timer(timer_sent, self.run_db_action_timer) - - except TypeError as te: - self.Logs.error(f"Type Error -> {te}") - except ValueError as ve: - self.Logs.error(f"Value Error -> {ve}") - case 'show_reputation': - if not self.Reputation.UID_REPUTATION_DB: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="No one is suspected") + if not self.ctx.Reputation.UID_REPUTATION_DB: + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="No one is suspected") - for suspect in self.Reputation.UID_REPUTATION_DB: - self.Protocol.send_notice(nick_from=dnickname, + for suspect in self.ctx.Reputation.UID_REPUTATION_DB: + await self.ctx.Irc.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.connexion_datetime}") case 'code': try: release_code = cmd[1] - jailed_nickname = self.User.get_nickname(fromuser) - jailed_UID = self.User.get_uid(fromuser) - get_reputation = self.Reputation.get_reputation(jailed_UID) + jailed_nickname = u.nickname + jailed_UID = u.uid + get_reputation = self.ctx.Reputation.get_reputation(jailed_UID) if get_reputation is None: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=" No code is requested ...") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=" No code is requested ...") return False jailed_IP = get_reputation.remote_ip - jailed_salon = self.Config.SALON_JAIL - reputation_seuil = self.ModConfig.reputation_seuil - welcome_salon = self.Config.SALON_LIBERER + jailed_salon = self.ctx.Config.SALON_JAIL + reputation_seuil = self.mod_config.reputation_seuil + welcome_salon = self.ctx.Config.SALON_LIBERER - self.Logs.debug(f"IP de {jailed_nickname} : {jailed_IP}") - link = self.Config.SERVEUR_LINK - color_green = self.Config.COLORS.green - color_black = self.Config.COLORS.black + self.ctx.Logs.debug(f"IP de {jailed_nickname} : {jailed_IP}") + link = self.ctx.Config.SERVEUR_LINK + color_green = self.ctx.Config.COLORS.green + color_black = self.ctx.Config.COLORS.black if release_code == get_reputation.secret_code: - self.Protocol.send_priv_msg(nick_from=dnickname, msg="Bon mot de passe. Allez du vent !", channel=jailed_salon) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg="Bon mot de passe. Allez du vent !", channel=jailed_salon) - if self.ModConfig.reputation_ban_all_chan == 1: - for chan in self.Channel.UID_CHANNEL_DB: + if self.mod_config.reputation_ban_all_chan == 1: + for chan in self.ctx.Channel.UID_CHANNEL_DB: if chan.name != jailed_salon: - self.Protocol.send2socket(f":{service_id} MODE {chan.name} -b {jailed_nickname}!*@*") + await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {chan.name} -b {jailed_nickname}!*@*") - self.Reputation.delete(jailed_UID) - self.Logs.debug(f'{jailed_UID} - {jailed_nickname} removed from REPUTATION_DB') - self.Protocol.send_sapart(nick_to_sapart=jailed_nickname, channel_name=jailed_salon) - self.Protocol.send_sajoin(nick_to_sajoin=jailed_nickname, channel_name=welcome_salon) - self.Protocol.send2socket(f":{link} REPUTATION {jailed_IP} {self.ModConfig.reputation_score_after_release}") - self.User.get_user(jailed_UID).score_connexion = reputation_seuil + 1 - self.Protocol.send_priv_msg(nick_from=dnickname, + self.ctx.Reputation.delete(jailed_UID) + self.ctx.Logs.debug(f'{jailed_UID} - {jailed_nickname} removed from REPUTATION_DB') + await self.ctx.Irc.Protocol.send_sapart(nick_to_sapart=jailed_nickname, channel_name=jailed_salon) + await self.ctx.Irc.Protocol.send_sajoin(nick_to_sajoin=jailed_nickname, channel_name=welcome_salon) + await self.ctx.Irc.Protocol.send2socket(f":{link} REPUTATION {jailed_IP} {self.mod_config.reputation_score_after_release}") + u.score_connexion = reputation_seuil + 1 + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[{color_green} MOT DE PASS CORRECT {color_black}] : You have now the right to enjoy the network !", nick_to=jailed_nickname) else: - self.Protocol.send_priv_msg( + await self.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, msg="Mauvais password", channel=jailed_salon ) - self.Protocol.send_priv_msg( + await self.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, - msg=f"[{color_green} MAUVAIS PASSWORD {color_black}] You have typed a wrong code. for recall your password is: {self.Config.SERVICE_PREFIX}code {get_reputation.secret_code}", + msg=f"[{color_green} MAUVAIS PASSWORD {color_black}] You have typed a wrong code. for recall your password is: {self.ctx.Config.SERVICE_PREFIX}code {get_reputation.secret_code}", nick_to=jailed_nickname ) except IndexError as ie: - self.Logs.error(f'Index Error: {ie}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} code [code]") + self.ctx.Logs.error(f'Index Error: {ie}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} code [code]") except KeyError as ke: - self.Logs.error(f'_hcmd code: KeyError {ke}') + self.ctx.Logs.error(f'_hcmd code: KeyError {ke}') case 'autolimit': 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]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.ctx.Config.SERVICE_NICKNAME} {command.upper()} ON") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.ctx.Config.SERVICE_NICKNAME} {command.upper()} SET [AMOUNT] [INTERVAL]") return None arg = str(cmd[1]).lower() match arg: case 'on': - if self.ModConfig.autolimit == 0: - self.__update_configuration('autolimit', 1) + if self.mod_config.autolimit == 0: + await self.update_configuration('autolimit', 1) self.autolimit_isRunning = True - self.Base.create_thread(func=thds.thread_autolimit, func_args=(self, )) - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[{self.Config.COLORS.green}AUTOLIMIT{self.Config.COLORS.nogc}] Activated", channel=self.Config.SERVICE_CHANLOG) + self.ctx.Base.create_asynctask(thds.coro_autolimit(self), async_name='coro_autolimit') + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[{self.ctx.Config.COLORS.green}AUTOLIMIT{self.ctx.Config.COLORS.nogc}] Activated", channel=self.ctx.Config.SERVICE_CHANLOG) else: - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[{self.Config.COLORS.red}AUTOLIMIT{self.Config.COLORS.nogc}] Already activated", channel=self.Config.SERVICE_CHANLOG) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[{self.ctx.Config.COLORS.red}AUTOLIMIT{self.ctx.Config.COLORS.nogc}] Already activated", channel=self.ctx.Config.SERVICE_CHANLOG) case 'off': - if self.ModConfig.autolimit == 1: - self.__update_configuration('autolimit', 0) - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[{self.Config.COLORS.green}AUTOLIMIT{self.Config.COLORS.nogc}] Deactivated", channel=self.Config.SERVICE_CHANLOG) + if self.mod_config.autolimit == 1: + await self.update_configuration('autolimit', 0) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[{self.ctx.Config.COLORS.green}AUTOLIMIT{self.ctx.Config.COLORS.nogc}] Deactivated", channel=self.ctx.Config.SERVICE_CHANLOG) else: - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[{self.Config.COLORS.red}AUTOLIMIT{self.Config.COLORS.nogc}] Already Deactivated", channel=self.Config.SERVICE_CHANLOG) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[{self.ctx.Config.COLORS.red}AUTOLIMIT{self.ctx.Config.COLORS.nogc}] Already Deactivated", channel=self.ctx.Config.SERVICE_CHANLOG) case 'set': amount = int(cmd[2]) interval = int(cmd[3]) - self.__update_configuration('autolimit_amount', amount) - self.__update_configuration('autolimit_interval', interval) - self.Protocol.send_priv_msg( + await self.update_configuration('autolimit_amount', amount) + await self.update_configuration('autolimit_interval', interval) + await self.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, - msg=f"[{self.Config.COLORS.green}AUTOLIMIT{self.Config.COLORS.nogc}] Amount set to ({amount}) | Interval set to ({interval})", - channel=self.Config.SERVICE_CHANLOG + msg=f"[{self.ctx.Config.COLORS.green}AUTOLIMIT{self.ctx.Config.COLORS.nogc}] Amount set to ({amount}) | Interval set to ({interval})", + channel=self.ctx.Config.SERVICE_CHANLOG ) case _: - 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]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.ctx.Config.SERVICE_NICKNAME} {command.upper()} ON") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.ctx.Config.SERVICE_NICKNAME} {command.upper()} SET [AMOUNT] [INTERVAL]") except Exception as err: - 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}") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.ctx.Config.SERVICE_NICKNAME} {command.upper()} ON") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.ctx.Config.SERVICE_NICKNAME} {command.upper()} SET [AMOUNT] [INTERVAL]") + self.ctx.Logs.error(f"Value Error -> {err}") case 'reputation': # .reputation [on/off] --> activate or deactivate reputation system @@ -484,54 +409,54 @@ class Defender: key = 'reputation' if activation == 'on': - if self.ModConfig.reputation == 1: - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.Config.COLORS.green}REPUTATION{self.Config.COLORS.black} ] : Already activated", channel=dchanlog) + if self.mod_config.reputation == 1: + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.ctx.Config.COLORS.green}REPUTATION{self.ctx.Config.COLORS.black} ] : Already activated", channel=dchanlog) return False # self.update_db_configuration('reputation', 1) - self.__update_configuration(key, 1) + await self.update_configuration(key, 1) - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.Config.COLORS.green}REPUTATION{self.Config.COLORS.black} ] : Activated by {fromuser}", channel=dchanlog) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.ctx.Config.COLORS.green}REPUTATION{self.ctx.Config.COLORS.black} ] : Activated by {fromuser}", channel=dchanlog) - self.Protocol.send_join_chan(uidornickname=dnickname, channel=jail_chan) - self.Protocol.send2socket(f":{service_id} SAMODE {jail_chan} +{dumodes} {dnickname}") - self.Protocol.send2socket(f":{service_id} MODE {jail_chan} +{jail_chan_mode}") + await self.ctx.Irc.Protocol.send_join_chan(uidornickname=dnickname, channel=jail_chan) + await self.ctx.Irc.Protocol.send2socket(f":{service_id} SAMODE {jail_chan} +{dumodes} {dnickname}") + await self.ctx.Irc.Protocol.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 self.mod_config.reputation_sg == 1: + for chan in self.ctx.Channel.UID_CHANNEL_DB: if chan.name != jail_chan: - self.Protocol.send2socket(f":{service_id} MODE {chan.name} +b ~security-group:unknown-users") - self.Protocol.send2socket(f":{service_id} MODE {chan.name} +eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users") + await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {chan.name} +b ~security-group:unknown-users") + await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {chan.name} +eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users") - self.Channel.db_query_channel('add', self.module_name, jail_chan) + await self.ctx.Channel.db_query_channel('add', self.module_name, jail_chan) if activation == 'off': - if self.ModConfig.reputation == 0: - self.Protocol.send_priv_msg( + if self.mod_config.reputation == 0: + await self.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, - msg=f"[ {self.Config.COLORS.green}REPUTATION{self.Config.COLORS.black} ] : Already deactivated", + msg=f"[ {self.ctx.Config.COLORS.green}REPUTATION{self.ctx.Config.COLORS.black} ] : Already deactivated", channel=dchanlog ) return False - self.__update_configuration(key, 0) + await self.update_configuration(key, 0) - self.Protocol.send_priv_msg( + await self.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, - msg=f"[ {self.Config.COLORS.red}REPUTATION{self.Config.COLORS.black} ] : Deactivated by {fromuser}", + msg=f"[ {self.ctx.Config.COLORS.red}REPUTATION{self.ctx.Config.COLORS.black} ] : Deactivated by {fromuser}", channel=dchanlog ) - self.Protocol.send2socket(f":{service_id} SAMODE {jail_chan} -{dumodes} {dnickname}") - self.Protocol.send2socket(f":{service_id} MODE {jail_chan} -sS") - self.Protocol.send2socket(f":{service_id} PART {jail_chan}") + await self.ctx.Irc.Protocol.send2socket(f":{service_id} SAMODE {jail_chan} -{dumodes} {dnickname}") + await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {jail_chan} -sS") + await self.ctx.Irc.Protocol.send2socket(f":{service_id} PART {jail_chan}") - for chan in self.Channel.UID_CHANNEL_DB: + for chan in self.ctx.Channel.UID_CHANNEL_DB: if chan.name != jail_chan: - self.Protocol.send2socket(f":{service_id} MODE {chan.name} -b ~security-group:unknown-users") - self.Protocol.send2socket(f":{service_id} MODE {chan.name} -eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users") + await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {chan.name} -b ~security-group:unknown-users") + await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {chan.name} -eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users") - self.Channel.db_query_channel('del', self.module_name, jail_chan) + await self.ctx.Channel.db_query_channel('del', self.module_name, jail_chan) if len_cmd == 3: get_options = str(cmd[1]).lower() @@ -539,44 +464,44 @@ class Defender: match get_options: case 'release': # .reputation release [nick] - p = self.Protocol - link = self.Config.SERVEUR_LINK - jailed_salon = self.Config.SALON_JAIL - welcome_salon = self.Config.SALON_LIBERER - client_obj = self.User.get_user(str(cmd[2])) + p = await self.ctx.Irc.Protocol + link = self.ctx.Config.SERVEUR_LINK + jailed_salon = self.ctx.Config.SALON_JAIL + welcome_salon = self.ctx.Config.SALON_LIBERER + client_obj = self.ctx.User.get_user(str(cmd[2])) if client_obj is None: - p.send_notice(nick_from=dnickname, + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"This nickname ({str(cmd[2])}) is not connected to the network!") return None - client_to_release = self.Reputation.get_reputation(client_obj.uid) + client_to_release = self.ctx.Reputation.get_reputation(client_obj.uid) if client_to_release is None: - p.send_notice(nick_from=dnickname, + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"This nickname ({str(cmd[2])}) doesn't exist in the reputation databalse!") return None - if self.Reputation.delete(client_to_release.uid): - p.send_priv_msg( + if self.ctx.Reputation.delete(client_to_release.uid): + await self.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, - msg=f"[ {self.Config.COLORS.green}REPUTATION RELEASE{self.Config.COLORS.black} ] : {client_to_release.nickname} has been released", + msg=f"[ {self.ctx.Config.COLORS.green}REPUTATION RELEASE{self.ctx.Config.COLORS.black} ] : {client_to_release.nickname} has been released", channel=dchanlog) - p.send_notice(nick_from=dnickname, + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"This nickname has been released from reputation system") - p.send_notice(nick_from=dnickname, + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=client_to_release.nickname, msg=f"You have been released from the reputation system by ({fromuser})") - p.send_sapart(nick_to_sapart=client_to_release.nickname, channel_name=jailed_salon) - p.send_sajoin(nick_to_sajoin=client_to_release.nickname, channel_name=welcome_salon) - p.send2socket(f":{link} REPUTATION {client_to_release.remote_ip} {self.ModConfig.reputation_score_after_release}") + await self.ctx.Irc.Protocol.send_sapart(nick_to_sapart=client_to_release.nickname, channel_name=jailed_salon) + await self.ctx.Irc.Protocol.send_sajoin(nick_to_sajoin=client_to_release.nickname, channel_name=welcome_salon) + await self.ctx.Irc.Protocol.send2socket(f":{link} REPUTATION {client_to_release.remote_ip} {self.mod_config.reputation_score_after_release}") return None else: - p.send_priv_msg( + await self.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, - msg=f"[ {self.Config.COLORS.red}REPUTATION RELEASE ERROR{self.Config.COLORS.black} ] : " + msg=f"[ {self.ctx.Config.COLORS.red}REPUTATION RELEASE ERROR{self.ctx.Config.COLORS.black} ] : " f"{client_to_release.nickname} has not been released! as he is not in the reputation database", channel=dchanlog ) @@ -595,38 +520,38 @@ class Defender: get_value = str(cmd[3]).lower() if get_value == 'on': - if self.ModConfig.reputation_ban_all_chan == 1: - self.Protocol.send_priv_msg( + if self.mod_config.reputation_ban_all_chan == 1: + await self.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, - msg=f"[ {self.Config.COLORS.red}BAN ON ALL CHANS{self.Config.COLORS.black} ] : Already activated", + msg=f"[ {self.ctx.Config.COLORS.red}BAN ON ALL CHANS{self.ctx.Config.COLORS.black} ] : Already activated", channel=dchanlog ) return False # self.update_db_configuration(key, 1) - self.__update_configuration(key, 1) + await self.update_configuration(key, 1) - self.Protocol.send_priv_msg( + await self.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, - msg=f"[ {self.Config.COLORS.green}BAN ON ALL CHANS{self.Config.COLORS.black} ] : Activated by {fromuser}", + msg=f"[ {self.ctx.Config.COLORS.green}BAN ON ALL CHANS{self.ctx.Config.COLORS.black} ] : Activated by {fromuser}", channel=dchanlog ) elif get_value == 'off': - if self.ModConfig.reputation_ban_all_chan == 0: - self.Protocol.send_priv_msg( + if self.mod_config.reputation_ban_all_chan == 0: + await self.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, - msg=f"[ {self.Config.COLORS.red}BAN ON ALL CHANS{self.Config.COLORS.black} ] : Already deactivated", + msg=f"[ {self.ctx.Config.COLORS.red}BAN ON ALL CHANS{self.ctx.Config.COLORS.black} ] : Already deactivated", channel=dchanlog ) return False # self.update_db_configuration(key, 0) - self.__update_configuration(key, 0) + await self.update_configuration(key, 0) - self.Protocol.send_priv_msg( + await self.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, - msg=f"[ {self.Config.COLORS.green}BAN ON ALL CHANS{self.Config.COLORS.black} ] : Deactivated by {fromuser}", + msg=f"[ {self.ctx.Config.COLORS.green}BAN ON ALL CHANS{self.ctx.Config.COLORS.black} ] : Deactivated by {fromuser}", channel=dchanlog ) @@ -635,73 +560,73 @@ class Defender: key = 'reputation_seuil' # self.update_db_configuration(key, reputation_seuil) - self.__update_configuration(key, reputation_seuil) + await self.update_configuration(key, reputation_seuil) - self.Protocol.send_priv_msg( + await self.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, - msg=f"[ {self.Config.COLORS.green}REPUTATION SEUIL{self.Config.COLORS.black} ] : Limit set to {str(reputation_seuil)} by {fromuser}", + msg=f"[ {self.ctx.Config.COLORS.green}REPUTATION SEUIL{self.ctx.Config.COLORS.black} ] : Limit set to {str(reputation_seuil)} by {fromuser}", channel=dchanlog ) - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Reputation set to {reputation_seuil}") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Reputation set to {reputation_seuil}") case 'timer': reputation_timer = int(cmd[3]) key = 'reputation_timer' - self.__update_configuration(key, reputation_timer) + await self.update_configuration(key, reputation_timer) - self.Protocol.send_priv_msg( + await self.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, - msg=f"[ {self.Config.COLORS.green}REPUTATION TIMER{self.Config.COLORS.black} ] : Timer set to {str(reputation_timer)} minute(s) by {fromuser}", + msg=f"[ {self.ctx.Config.COLORS.green}REPUTATION TIMER{self.ctx.Config.COLORS.black} ] : Timer set to {str(reputation_timer)} minute(s) by {fromuser}", channel=dchanlog ) - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Reputation set to {reputation_timer}") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Reputation set to {reputation_timer}") case 'score_after_release': reputation_score_after_release = int(cmd[3]) key = 'reputation_score_after_release' - self.__update_configuration(key, reputation_score_after_release) + await self.update_configuration(key, reputation_score_after_release) - self.Protocol.send_priv_msg( + await self.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, - msg=f"[ {self.Config.COLORS.green}REPUTATION SCORE AFTER RELEASE{self.Config.COLORS.black} ] : Reputation score after release set to {str(reputation_score_after_release)} by {fromuser}", + msg=f"[ {self.ctx.Config.COLORS.green}REPUTATION SCORE AFTER RELEASE{self.ctx.Config.COLORS.black} ] : Reputation score after release set to {str(reputation_score_after_release)} by {fromuser}", channel=dchanlog ) - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Reputation score after release set to {reputation_score_after_release}") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Reputation score after release set to {reputation_score_after_release}") case 'security_group': reputation_sg = int(cmd[3]) key = 'reputation_sg' - self.__update_configuration(key, reputation_sg) + await self.update_configuration(key, reputation_sg) - self.Protocol.send_priv_msg( + await self.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, - msg=f"[ {self.Config.COLORS.green}REPUTATION SECURITY-GROUP{self.Config.COLORS.black} ] : Reputation Security-group set to {str(reputation_sg)} by {fromuser}", + msg=f"[ {self.ctx.Config.COLORS.green}REPUTATION SECURITY-GROUP{self.ctx.Config.COLORS.black} ] : Reputation Security-group set to {str(reputation_sg)} by {fromuser}", channel=dchanlog ) - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Reputation score after release set to {reputation_sg}") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Reputation score after release set to {reputation_sg}") case _: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation [ON/OFF]") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation release [nickname]") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set banallchan [ON/OFF]") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set limit [1234]") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set score_after_release [1234]") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set timer [1234]") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set action [kill|None]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation [ON/OFF]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation release [nickname]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set banallchan [ON/OFF]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set limit [1234]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set score_after_release [1234]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set timer [1234]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set action [kill|None]") except IndexError as ie: - self.Logs.warning(f'{ie}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation [ON/OFF]") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation release [nickname]") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set banallchan [ON/OFF]") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set limit [1234]") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set score_after_release [1234]") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set timer [1234]") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set action [kill|None]") + self.ctx.Logs.warning(f'{ie}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation [ON/OFF]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation release [nickname]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set banallchan [ON/OFF]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set limit [1234]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set score_after_release [1234]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set timer [1234]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} reputation set action [kill|None]") except ValueError as ve: - self.Logs.warning(f'{ve}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=" La valeur devrait etre un entier >= 0") + self.ctx.Logs.warning(f'{ve}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=" La valeur devrait etre un entier >= 0") case 'proxy_scan': @@ -709,19 +634,19 @@ class Defender: # .proxy_scan set psutil_scan on/off --> Active les informations de connexion a la machine locale # .proxy_scan set abuseipdb_scan on/off --> Active le scan via l'api abuseipdb len_cmd = len(cmd) - color_green = self.Config.COLORS.green - color_red = self.Config.COLORS.red - color_black = self.Config.COLORS.black + color_green = self.ctx.Config.COLORS.green + color_red = self.ctx.Config.COLORS.red + color_black = self.ctx.Config.COLORS.black if len_cmd == 4: set_key = str(cmd[1]).lower() if set_key != 'set': - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set local_scan [ON/OFF]') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set psutil_scan [ON/OFF]') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set abuseipdb_scan [ON/OFF]') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set freeipapi_scan [ON/OFF]') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set cloudfilt_scan [ON/OFF]') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set local_scan [ON/OFF]') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set psutil_scan [ON/OFF]') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set abuseipdb_scan [ON/OFF]') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set freeipapi_scan [ON/OFF]') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set cloudfilt_scan [ON/OFF]') option = str(cmd[2]).lower() # => local_scan, psutil_scan, abuseipdb_scan action = str(cmd[3]).lower() # => on / off @@ -729,106 +654,106 @@ class Defender: match option: case 'local_scan': if action == 'on': - if self.ModConfig.local_scan == 1: - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog) + if self.mod_config.local_scan == 1: + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog) return None - self.__update_configuration(option, 1) + await self.update_configuration(option, 1) - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog) elif action == 'off': - if self.ModConfig.local_scan == 0: - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated", channel=dchanlog) + if self.mod_config.local_scan == 0: + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated", channel=dchanlog) return None - self.__update_configuration(option, 0) + await self.update_configuration(option, 0) - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog) case 'psutil_scan': if action == 'on': - if self.ModConfig.psutil_scan == 1: - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog) + if self.mod_config.psutil_scan == 1: + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog) return None - self.__update_configuration(option, 1) + await self.update_configuration(option, 1) - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog) elif action == 'off': - if self.ModConfig.psutil_scan == 0: - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated", channel=dchanlog) + if self.mod_config.psutil_scan == 0: + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated", channel=dchanlog) return None - self.__update_configuration(option, 0) + await self.update_configuration(option, 0) - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog) case 'abuseipdb_scan': if action == 'on': - if self.ModConfig.abuseipdb_scan == 1: - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog) + if self.mod_config.abuseipdb_scan == 1: + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog) return None - self.__update_configuration(option, 1) + await self.update_configuration(option, 1) - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog) elif action == 'off': - if self.ModConfig.abuseipdb_scan == 0: - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated", channel=dchanlog) + if self.mod_config.abuseipdb_scan == 0: + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated", channel=dchanlog) return None - self.__update_configuration(option, 0) + await self.update_configuration(option, 0) - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog) case 'freeipapi_scan': if action == 'on': - if self.ModConfig.freeipapi_scan == 1: - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog) + if self.mod_config.freeipapi_scan == 1: + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog) return None - self.__update_configuration(option, 1) + await self.update_configuration(option, 1) - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog) elif action == 'off': - if self.ModConfig.freeipapi_scan == 0: - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated", channel=dchanlog) + if self.mod_config.freeipapi_scan == 0: + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated", channel=dchanlog) return None - self.__update_configuration(option, 0) + await self.update_configuration(option, 0) - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog) case 'cloudfilt_scan': if action == 'on': - if self.ModConfig.cloudfilt_scan == 1: - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog) + if self.mod_config.cloudfilt_scan == 1: + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog) return None - self.__update_configuration(option, 1) + await self.update_configuration(option, 1) - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog) elif action == 'off': - if self.ModConfig.cloudfilt_scan == 0: - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated", channel=dchanlog) + if self.mod_config.cloudfilt_scan == 0: + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated", channel=dchanlog) return None - self.__update_configuration(option, 0) + await self.update_configuration(option, 0) - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog) case _: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set local_scan [ON/OFF]') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set psutil_scan [ON/OFF]') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set abuseipdb_scan [ON/OFF]') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set freeipapi_scan [ON/OFF]') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set cloudfilt_scan [ON/OFF]') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set local_scan [ON/OFF]') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set psutil_scan [ON/OFF]') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set abuseipdb_scan [ON/OFF]') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set freeipapi_scan [ON/OFF]') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set cloudfilt_scan [ON/OFF]') else: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set local_scan [ON/OFF]') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set psutil_scan [ON/OFF]') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set abuseipdb_scan [ON/OFF]') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set freeipapi_scan [ON/OFF]') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set cloudfilt_scan [ON/OFF]') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set local_scan [ON/OFF]') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set psutil_scan [ON/OFF]') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set abuseipdb_scan [ON/OFF]') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set freeipapi_scan [ON/OFF]') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Right command : /msg {dnickname} proxy_scan set cloudfilt_scan [ON/OFF]') case 'flood': # .flood on/off @@ -842,22 +767,22 @@ class Defender: activation = str(cmd[1]).lower() key = 'flood' if activation == 'on': - if self.ModConfig.flood == 1: - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Already activated", channel=dchanlog) + if self.mod_config.flood == 1: + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.ctx.Config.COLORS.green}FLOOD{self.ctx.Config.COLORS.black} ] : Already activated", channel=dchanlog) return False - self.__update_configuration(key, 1) + await self.update_configuration(key, 1) - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Activated by {fromuser}", channel=dchanlog) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.ctx.Config.COLORS.green}FLOOD{self.ctx.Config.COLORS.black} ] : Activated by {fromuser}", channel=dchanlog) if activation == 'off': - if self.ModConfig.flood == 0: - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.Config.COLORS.red}FLOOD{self.Config.COLORS.black} ] : Already Deactivated", channel=dchanlog) + if self.mod_config.flood == 0: + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.ctx.Config.COLORS.red}FLOOD{self.ctx.Config.COLORS.black} ] : Already Deactivated", channel=dchanlog) return False - self.__update_configuration(key, 0) + await self.update_configuration(key, 0) - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Deactivated by {fromuser}", channel=dchanlog) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.ctx.Config.COLORS.green}FLOOD{self.ctx.Config.COLORS.black} ] : Deactivated by {fromuser}", channel=dchanlog) if len_cmd == 4: set_key = str(cmd[2]).lower() @@ -867,110 +792,126 @@ class Defender: case 'flood_message': key = 'flood_message' set_value = int(cmd[3]) - self.__update_configuration(key, set_value) + await self.update_configuration(key, set_value) - self.Protocol.send_priv_msg(nick_from=dnickname, - msg=f"[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Flood message set to {set_value} by {fromuser}", + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, + msg=f"[ {self.ctx.Config.COLORS.green}FLOOD{self.ctx.Config.COLORS.black} ] : Flood message set to {set_value} by {fromuser}", channel=dchanlog) case 'flood_time': key = 'flood_time' set_value = int(cmd[3]) - self.__update_configuration(key, set_value) + await self.update_configuration(key, set_value) - self.Protocol.send_priv_msg(nick_from=dnickname, - msg=f"[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Flood time set to {set_value} by {fromuser}", + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, + msg=f"[ {self.ctx.Config.COLORS.green}FLOOD{self.ctx.Config.COLORS.black} ] : Flood time set to {set_value} by {fromuser}", channel=dchanlog) case 'flood_timer': key = 'flood_timer' set_value = int(cmd[3]) - self.__update_configuration(key, set_value) + await self.update_configuration(key, set_value) - self.Protocol.send_priv_msg(nick_from=dnickname, - msg=f"[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Flood timer set to {set_value} by {fromuser}", + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, + msg=f"[ {self.ctx.Config.COLORS.green}FLOOD{self.ctx.Config.COLORS.black} ] : Flood timer set to {set_value} by {fromuser}", channel=dchanlog) case _: pass except ValueError as ve: - self.Logs.error(f"{self.__class__.__name__} Value Error : {ve}") + self.ctx.Logs.error(f"{self.__class__.__name__} Value Error : {ve}") case 'status': - color_green = self.Config.COLORS.green - color_red = self.Config.COLORS.red - color_black = self.Config.COLORS.black - nogc = self.Config.COLORS.nogc + color_green = self.ctx.Config.COLORS.green + color_red = self.ctx.Config.COLORS.red + color_black = self.ctx.Config.COLORS.black + nogc = self.ctx.Config.COLORS.nogc try: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' [{color_green if self.ModConfig.reputation == 1 else color_red}Reputation{nogc}] ==> {self.ModConfig.reputation}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' reputation_seuil ==> {self.ModConfig.reputation_seuil}') - 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=' [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.autolimit == 1 else color_red}Autolimit{nogc}] ==> {self.ModConfig.autolimit}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.autolimit == 1 else color_red}Autolimit Amount{nogc} ==> {self.ModConfig.autolimit_amount}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.autolimit == 1 else color_red}Autolimit Interval{nogc} ==> {self.ModConfig.autolimit_interval}') - 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=' 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}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' [{color_green if self.mod_config.reputation == 1 else color_red}Reputation{nogc}] ==> {self.mod_config.reputation}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' reputation_seuil ==> {self.mod_config.reputation_seuil}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' reputation_after_release ==> {self.mod_config.reputation_score_after_release}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' reputation_ban_all_chan ==> {self.mod_config.reputation_ban_all_chan}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' reputation_timer ==> {self.mod_config.reputation_timer}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=' [Proxy_scan]') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.mod_config.local_scan == 1 else color_red}local_scan{nogc} ==> {self.mod_config.local_scan}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.mod_config.psutil_scan == 1 else color_red}psutil_scan{nogc} ==> {self.mod_config.psutil_scan}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.mod_config.abuseipdb_scan == 1 else color_red}abuseipdb_scan{nogc} ==> {self.mod_config.abuseipdb_scan}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.mod_config.freeipapi_scan == 1 else color_red}freeipapi_scan{nogc} ==> {self.mod_config.freeipapi_scan}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.mod_config.cloudfilt_scan == 1 else color_red}cloudfilt_scan{nogc} ==> {self.mod_config.cloudfilt_scan}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' [{color_green if self.mod_config.autolimit == 1 else color_red}Autolimit{nogc}] ==> {self.mod_config.autolimit}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.mod_config.autolimit == 1 else color_red}Autolimit Amount{nogc} ==> {self.mod_config.autolimit_amount}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.mod_config.autolimit == 1 else color_red}Autolimit Interval{nogc} ==> {self.mod_config.autolimit_interval}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' [{color_green if self.mod_config.flood == 1 else color_red}Flood{nogc}] ==> {self.mod_config.flood}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=' flood_action ==> Coming soon') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_message ==> {self.mod_config.flood_message}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_time ==> {self.mod_config.flood_time}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_timer ==> {self.mod_config.flood_timer}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' [{color_green if self.mod_config.flood == 1 else color_red}Sentinel{nogc}] ==> {self.mod_config.sentinel}') except KeyError as ke: - self.Logs.error(f"Key Error : {ke}") + self.ctx.Logs.error(f"Key Error : {ke}") case 'info': try: if len(cmd) < 2: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Syntax. /msg {dnickname} INFO [nickname]") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Syntax. /msg {dnickname} INFO [nickname]") return None nickoruid = cmd[1] - UserObject = self.User.get_user(nickoruid) + UserObject = self.ctx.User.get_user(nickoruid) if UserObject is not None: - channels: list = [chan.name for chan in self.Channel.UID_CHANNEL_DB for uid_in_chan in chan.uids if self.Loader.Utils.clean_uid(uid_in_chan) == UserObject.uid] + channels: list = [chan.name for chan in self.ctx.Channel.UID_CHANNEL_DB for uid_in_chan in chan.uids if self.ctx.mod_utils.clean_uid(uid_in_chan) == UserObject.uid] - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' UID : {UserObject.uid}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' NICKNAME : {UserObject.nickname}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' USERNAME : {UserObject.username}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' REALNAME : {UserObject.realname}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' HOSTNAME : {UserObject.hostname}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' VHOST : {UserObject.vhost}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' IP : {UserObject.remote_ip}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Country : {UserObject.geoip}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' WebIrc : {UserObject.isWebirc}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' WebWebsocket : {UserObject.isWebsocket}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' REPUTATION : {UserObject.score_connexion}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' MODES : {UserObject.umodes}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' CHANNELS : {", ".join(channels)}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' CONNECTION TIME : {UserObject.connexion_datetime}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' UID : {UserObject.uid}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' NICKNAME : {UserObject.nickname}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' USERNAME : {UserObject.username}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' REALNAME : {UserObject.realname}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' HOSTNAME : {UserObject.hostname}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' VHOST : {UserObject.vhost}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' IP : {UserObject.remote_ip}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' Country : {UserObject.geoip}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' WebIrc : {UserObject.isWebirc}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' WebWebsocket : {UserObject.isWebsocket}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' REPUTATION : {UserObject.score_connexion}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' MODES : {UserObject.umodes}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' CHANNELS : {", ".join(channels)}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' CONNECTION TIME : {UserObject.connexion_datetime}') else: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"This user {nickoruid} doesn't exist") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"This user {nickoruid} doesn't exist") except KeyError as ke: - self.Logs.warning(f"Key error info user : {ke}") + self.ctx.Logs.warning(f"Key error info user : {ke}") case 'sentinel': # .sentinel on activation = str(cmd[1]).lower() - channel_to_dont_quit = [self.Config.SALON_JAIL, self.Config.SERVICE_CHANLOG] + channel_to_dont_quit = [self.ctx.Config.SALON_JAIL, self.ctx.Config.SERVICE_CHANLOG] if activation == 'on': - for chan in self.Channel.UID_CHANNEL_DB: + result = await self.ctx.Base.db_execute_query(f"SELECT distinct channel_name FROM {self.ctx.Config.TABLE_CHANNEL}") + channels = result.fetchall() + channel_in_db = [channel[0] for channel in channels] + channel_to_dont_quit.extend(channel_in_db) + + await self.update_configuration('sentinel', 1) + for chan in self.ctx.Channel.UID_CHANNEL_DB: if chan.name not in channel_to_dont_quit: - self.Protocol.send_join_chan(uidornickname=dnickname, channel=chan.name) + await self.ctx.Irc.Protocol.send_join_chan(uidornickname=dnickname, channel=chan.name) + await self.ctx.Irc.Protocol.send_priv_msg(dnickname, f"Sentinel mode activated on {channel}", channel=chan.name) return None if activation == 'off': - for chan in self.Channel.UID_CHANNEL_DB: + result = await self.ctx.Base.db_execute_query(f"SELECT distinct channel_name FROM {self.ctx.Config.TABLE_CHANNEL}") + channels = result.fetchall() + channel_in_db = [channel[0] for channel in channels] + channel_to_dont_quit.extend(channel_in_db) + await self.update_configuration('sentinel', 0) + for chan in self.ctx.Channel.UID_CHANNEL_DB: if chan.name not in channel_to_dont_quit: - self.Protocol.send_part_chan(uidornickname=dnickname, channel=chan.name) - self.join_saved_channels() + await self.ctx.Irc.Protocol.send_part_chan(uidornickname=dnickname, channel=chan.name) + await self.ctx.Irc.Protocol.send_priv_msg(dnickname, f"Sentinel mode deactivated on {channel}", channel=chan.name) + + await self.join_saved_channels() + return None diff --git a/mods/defender/schemas.py b/mods/defender/schemas.py index b404685..a7d7a78 100644 --- a/mods/defender/schemas.py +++ b/mods/defender/schemas.py @@ -20,6 +20,7 @@ class ModConfModel(MainModel): autolimit: int = 0 autolimit_amount: int = 3 autolimit_interval: int = 3 + sentinel: int = 0 @dataclass class FloodUser(MainModel): diff --git a/mods/defender/threads.py b/mods/defender/threads.py index 9f00077..e785238 100644 --- a/mods/defender/threads.py +++ b/mods/defender/threads.py @@ -1,115 +1,116 @@ +import asyncio from typing import TYPE_CHECKING from time import sleep if TYPE_CHECKING: from mods.defender.mod_defender import Defender -def thread_apply_reputation_sanctions(uplink: 'Defender'): +async def coro_apply_reputation_sanctions(uplink: 'Defender'): while uplink.reputationTimer_isRunning: - uplink.Utils.action_apply_reputation_santions(uplink) - sleep(5) + await uplink.mod_utils.action_apply_reputation_santions(uplink) + await asyncio.sleep(5) -def thread_cloudfilt_scan(uplink: 'Defender'): +async def coro_cloudfilt_scan(uplink: 'Defender'): while uplink.cloudfilt_isRunning: list_to_remove:list = [] for user in uplink.Schemas.DB_CLOUDFILT_USERS: - uplink.Utils.action_scan_client_with_cloudfilt(uplink, user) + uplink.mod_utils.action_scan_client_with_cloudfilt(uplink, user) list_to_remove.append(user) - sleep(1) + await asyncio.sleep(1) for user_model in list_to_remove: uplink.Schemas.DB_CLOUDFILT_USERS.remove(user_model) - sleep(1) + await asyncio.sleep(1) -def thread_freeipapi_scan(uplink: 'Defender'): +async def coro_freeipapi_scan(uplink: 'Defender'): while uplink.freeipapi_isRunning: list_to_remove: list = [] for user in uplink.Schemas.DB_FREEIPAPI_USERS: - uplink.Utils.action_scan_client_with_freeipapi(uplink, user) + uplink.mod_utils.action_scan_client_with_freeipapi(uplink, user) list_to_remove.append(user) - sleep(1) + await asyncio.sleep(1) for user_model in list_to_remove: uplink.Schemas.DB_FREEIPAPI_USERS.remove(user_model) - sleep(1) + await asyncio.sleep(1) -def thread_abuseipdb_scan(uplink: 'Defender'): +async def coro_abuseipdb_scan(uplink: 'Defender'): while uplink.abuseipdb_isRunning: list_to_remove: list = [] for user in uplink.Schemas.DB_ABUSEIPDB_USERS: - uplink.Utils.action_scan_client_with_abuseipdb(uplink, user) + uplink.mod_utils.action_scan_client_with_abuseipdb(uplink, user) list_to_remove.append(user) - sleep(1) + await asyncio.sleep(1) for user_model in list_to_remove: uplink.Schemas.DB_ABUSEIPDB_USERS.remove(user_model) - sleep(1) + await asyncio.sleep(1) -def thread_local_scan(uplink: 'Defender'): +async def coro_local_scan(uplink: 'Defender'): while uplink.localscan_isRunning: list_to_remove:list = [] for user in uplink.Schemas.DB_LOCALSCAN_USERS: - uplink.Utils.action_scan_client_with_local_socket(uplink, user) + uplink.mod_utils.action_scan_client_with_local_socket(uplink, user) list_to_remove.append(user) - sleep(1) + await asyncio.sleep(1) for user_model in list_to_remove: uplink.Schemas.DB_LOCALSCAN_USERS.remove(user_model) - sleep(1) + await asyncio.sleep(1) -def thread_psutil_scan(uplink: 'Defender'): +async def coro_psutil_scan(uplink: 'Defender'): while uplink.psutil_isRunning: list_to_remove:list = [] for user in uplink.Schemas.DB_PSUTIL_USERS: - uplink.Utils.action_scan_client_with_psutil(uplink, user) + uplink.mod_utils.action_scan_client_with_psutil(uplink, user) list_to_remove.append(user) - sleep(1) + await asyncio.sleep(1) for user_model in list_to_remove: uplink.Schemas.DB_PSUTIL_USERS.remove(user_model) - sleep(1) + await asyncio.sleep(1) -def thread_autolimit(uplink: 'Defender'): +async def coro_autolimit(uplink: 'Defender'): - if uplink.ModConfig.autolimit == 0: - uplink.Logs.debug("autolimit deactivated ... canceling the thread") + if uplink.mod_config.autolimit == 0: + uplink.ctx.Logs.debug("autolimit deactivated ... canceling the thread") return None - while uplink.Irc.autolimit_started: - sleep(0.2) + while uplink.ctx.Irc.autolimit_started: + await asyncio.sleep(0.2) - uplink.Irc.autolimit_started = True - init_amount = uplink.ModConfig.autolimit_amount - p = uplink.Protocol + uplink.ctx.Irc.autolimit_started = True + init_amount = uplink.mod_config.autolimit_amount + p = uplink.ctx.Irc.Protocol INIT = 1 # Copy Channels to a list of dict - chanObj_copy: list[dict[str, int]] = [{"name": c.name, "uids_count": len(c.uids)} for c in uplink.Channel.UID_CHANNEL_DB] - chan_list: list[str] = [c.name for c in uplink.Channel.UID_CHANNEL_DB] + chanObj_copy: list[dict[str, int]] = [{"name": c.name, "uids_count": len(c.uids)} for c in uplink.ctx.Channel.UID_CHANNEL_DB] + chan_list: list[str] = [c.name for c in uplink.ctx.Channel.UID_CHANNEL_DB] while uplink.autolimit_isRunning: - if uplink.ModConfig.autolimit == 0: - uplink.Logs.debug("autolimit deactivated ... stopping the current thread") + if uplink.mod_config.autolimit == 0: + uplink.ctx.Logs.debug("autolimit deactivated ... stopping the current thread") break - for chan in uplink.Channel.UID_CHANNEL_DB: + for chan in uplink.ctx.Channel.UID_CHANNEL_DB: for chan_copy in chanObj_copy: if chan_copy["name"] == chan.name and len(chan.uids) != chan_copy["uids_count"]: - p.send2socket(f":{uplink.Config.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + uplink.ModConfig.autolimit_amount}") + await p.send2socket(f":{uplink.ctx.Config.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + uplink.mod_config.autolimit_amount}") chan_copy["uids_count"] = len(chan.uids) if chan.name not in chan_list: @@ -117,51 +118,55 @@ def thread_autolimit(uplink: 'Defender'): 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 uplink.Channel.UID_CHANNEL_DB] + current_chan_in_list = [d.name for d in uplink.ctx.Channel.UID_CHANNEL_DB] for c in chan_list: if c not in current_chan_in_list: chan_list.remove(c) # Si c'est la premiere execution if INIT == 1: - for chan in uplink.Channel.UID_CHANNEL_DB: - p.send2socket(f":{uplink.Config.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + uplink.ModConfig.autolimit_amount}") + for chan in uplink.ctx.Channel.UID_CHANNEL_DB: + await p.send2socket(f":{uplink.ctx.Config.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + uplink.mod_config.autolimit_amount}") # Si le nouveau amount est différent de l'initial - if init_amount != uplink.ModConfig.autolimit_amount: - init_amount = uplink.ModConfig.autolimit_amount - for chan in uplink.Channel.UID_CHANNEL_DB: - p.send2socket(f":{uplink.Config.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + uplink.ModConfig.autolimit_amount}") + if init_amount != uplink.mod_config.autolimit_amount: + init_amount = uplink.mod_config.autolimit_amount + for chan in uplink.ctx.Channel.UID_CHANNEL_DB: + await p.send2socket(f":{uplink.ctx.Config.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + uplink.mod_config.autolimit_amount}") INIT = 0 if uplink.autolimit_isRunning: - sleep(uplink.ModConfig.autolimit_interval) + await asyncio.sleep(uplink.mod_config.autolimit_interval) - for chan in uplink.Channel.UID_CHANNEL_DB: - p.send2socket(f":{uplink.Config.SERVICE_ID} MODE {chan.name} -l") + for chan in uplink.ctx.Channel.UID_CHANNEL_DB: + # await p.send2socket(f":{uplink.ctx.Config.SERVICE_ID} MODE {chan.name} -l") + await p.send_set_mode('-l', channel_name=chan.name) - uplink.Irc.autolimit_started = False + uplink.ctx.Irc.autolimit_started = False return None -def timer_release_mode_mute(uplink: 'Defender', action: str, channel: str): - """DO NOT EXECUTE THIS FUNCTION WITHOUT THREADING +async def coro_release_mode_mute(uplink: 'Defender', action: str, channel: str): + """DO NOT EXECUTE THIS FUNCTION DIRECTLY + IT WILL BLOCK THE PROCESS Args: - action (str): _description_ + action (str): mode-m channel (str): The related channel """ - service_id = uplink.Config.SERVICE_ID + service_id = uplink.ctx.Config.SERVICE_ID + timeout = uplink.mod_config.flood_timer + await asyncio.sleep(timeout) - if not uplink.Channel.is_valid_channel(channel): - uplink.Logs.debug(f"Channel is not valid {channel}") + if not uplink.ctx.Channel.is_valid_channel(channel): + uplink.ctx.Logs.debug(f"Channel is not valid {channel}") return match action: case 'mode-m': # Action -m sur le salon - uplink.Protocol.send2socket(f":{service_id} MODE {channel} -m") + await uplink.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {channel} -m") case _: pass diff --git a/mods/defender/utils.py b/mods/defender/utils.py index 32fb911..a880a42 100644 --- a/mods/defender/utils.py +++ b/mods/defender/utils.py @@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Optional from mods.defender.schemas import FloodUser if TYPE_CHECKING: + from core.loader import Loader from core.definition import MUser from mods.defender.mod_defender import Defender @@ -28,10 +29,10 @@ def handle_on_reputation(uplink: 'Defender', srvmsg: list[str]): return # Possibilité de déclancher les bans a ce niveau. - if not uplink.Base.is_valid_ip(ip): + if not uplink.ctx.Base.is_valid_ip(ip): return -def handle_on_mode(uplink: 'Defender', srvmsg: list[str]): +async def handle_on_mode(uplink: 'Defender', srvmsg: list[str]): """_summary_ >>> srvmsg = ['@unrealircd.org/...', ':001C0MF01', 'MODE', '#services', '+l', '1'] >>> srvmsg = ['...', ':001XSCU0Q', 'MODE', '#jail', '+b', '~security-group:unknown-users'] @@ -40,10 +41,10 @@ def handle_on_mode(uplink: 'Defender', srvmsg: list[str]): srvmsg (list[str]): The Server MSG confmodel (ModConfModel): The Module Configuration """ - irc = uplink.Irc - gconfig = uplink.Config - p = uplink.Protocol - confmodel = uplink.ModConfig + irc = uplink.ctx.Irc + gconfig = uplink.ctx.Config + p = irc.Protocol + confmodel = uplink.mod_config channel = str(srvmsg[3]) mode = str(srvmsg[4]) @@ -52,20 +53,25 @@ def handle_on_mode(uplink: 'Defender', srvmsg: list[str]): if confmodel.autolimit == 1: if mode == '+l' or mode == '-l': - chan = irc.Channel.get_channel(channel) - p.send2socket(f":{gconfig.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + confmodel.autolimit_amount}") + chan = uplink.ctx.Channel.get_channel(channel) + await p.send2socket(f":{gconfig.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + confmodel.autolimit_amount}") if gconfig.SALON_JAIL == channel: if mode == '+b' and group_to_unban in group_to_check: - p.send2socket(f":{gconfig.SERVICE_ID} MODE {gconfig.SALON_JAIL} -b ~security-group:unknown-users") - p.send2socket(f":{gconfig.SERVICE_ID} MODE {gconfig.SALON_JAIL} -eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users") + await p.send2socket(f":{gconfig.SERVICE_ID} MODE {gconfig.SALON_JAIL} -b ~security-group:unknown-users") + await p.send2socket(f":{gconfig.SERVICE_ID} MODE {gconfig.SALON_JAIL} -eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users") -def handle_on_privmsg(uplink: 'Defender', srvmsg: list[str]): +async def handle_on_privmsg(uplink: 'Defender', srvmsg: list[str]): # ['@mtag....',':python', 'PRIVMSG', '#defender', ':zefzefzregreg', 'regg', 'aerg'] - action_on_flood(uplink, srvmsg) + + sender, reciever, channel, message = uplink.ctx.Irc.Protocol.parse_privmsg(srvmsg) + if uplink.mod_config.sentinel == 1 and channel.name != uplink.ctx.Config.SERVICE_CHANLOG: + await uplink.ctx.Irc.Protocol.send_priv_msg(uplink.ctx.Config.SERVICE_NICKNAME, f"{sender.nickname} say on {channel.name}: {' '.join(message)}", uplink.ctx.Config.SERVICE_CHANLOG) + + await action_on_flood(uplink, srvmsg) return None -def handle_on_sjoin(uplink: 'Defender', srvmsg: list[str]): +async def handle_on_sjoin(uplink: 'Defender', srvmsg: list[str]): """If Joining a new channel, it applies group bans. >>> srvmsg = ['@msgid..', ':001', 'SJOIN', '1702138958', '#welcome', ':0015L1AHL'] @@ -75,37 +81,37 @@ def handle_on_sjoin(uplink: 'Defender', srvmsg: list[str]): srvmsg (list[str]): The Server MSG confmodel (ModConfModel): The Module Configuration """ - irc = uplink.Irc + irc = uplink.ctx.Irc p = irc.Protocol - gconfig = uplink.Config - confmodel = uplink.ModConfig + gconfig = uplink.ctx.Config + confmodel = uplink.mod_config - parsed_chan = srvmsg[4] if irc.Channel.is_valid_channel(srvmsg[4]) else None - parsed_UID = uplink.Loader.Utils.clean_uid(srvmsg[5]) + parsed_chan = srvmsg[4] if uplink.ctx.Channel.is_valid_channel(srvmsg[4]) else None + parsed_UID = uplink.ctx.Utils.clean_uid(srvmsg[5]) if parsed_chan is None or parsed_UID is None: return if confmodel.reputation == 1: - get_reputation = irc.Reputation.get_reputation(parsed_UID) + get_reputation = uplink.ctx.Reputation.get_reputation(parsed_UID) if parsed_chan != gconfig.SALON_JAIL: - p.send2socket(f":{gconfig.SERVICE_ID} MODE {parsed_chan} +b ~security-group:unknown-users") - p.send2socket(f":{gconfig.SERVICE_ID} MODE {parsed_chan} +eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users") + await p.send2socket(f":{gconfig.SERVICE_ID} MODE {parsed_chan} +b ~security-group:unknown-users") + await p.send2socket(f":{gconfig.SERVICE_ID} MODE {parsed_chan} +eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users") if get_reputation is not None: isWebirc = get_reputation.isWebirc if not isWebirc: if parsed_chan != gconfig.SALON_JAIL: - p.send_sapart(nick_to_sapart=get_reputation.nickname, channel_name=parsed_chan) + await p.send_sapart(nick_to_sapart=get_reputation.nickname, channel_name=parsed_chan) if confmodel.reputation_ban_all_chan == 1 and not isWebirc: if parsed_chan != gconfig.SALON_JAIL: - p.send2socket(f":{gconfig.SERVICE_ID} MODE {parsed_chan} +b {get_reputation.nickname}!*@*") - p.send2socket(f":{gconfig.SERVICE_ID} KICK {parsed_chan} {get_reputation.nickname}") + await p.send2socket(f":{gconfig.SERVICE_ID} MODE {parsed_chan} +b {get_reputation.nickname}!*@*") + await p.send2socket(f":{gconfig.SERVICE_ID} KICK {parsed_chan} {get_reputation.nickname}") - irc.Logs.debug(f'SJOIN parsed_uid : {parsed_UID}') + uplink.ctx.Logs.debug(f'SJOIN parsed_uid : {parsed_UID}') def handle_on_slog(uplink: 'Defender', srvmsg: list[str]): """Handling SLOG messages @@ -117,27 +123,27 @@ def handle_on_slog(uplink: 'Defender', srvmsg: list[str]): """ ['@unrealircd...', ':001', 'SLOG', 'info', 'blacklist', 'BLACKLIST_HIT', ':[Blacklist]', 'IP', '162.x.x.x', 'matches', 'blacklist', 'dronebl', '(dnsbl.dronebl.org/reply=6)'] - if not uplink.Base.is_valid_ip(srvmsg[8]): + if not uplink.ctx.Base.is_valid_ip(srvmsg[8]): return None - # if self.ModConfig.local_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP: + # if self.mod_config.local_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP: # self.localscan_remote_ip.append(cmd[7]) - # if self.ModConfig.psutil_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP: + # if self.mod_config.psutil_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP: # self.psutil_remote_ip.append(cmd[7]) - # if self.ModConfig.abuseipdb_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP: + # if self.mod_config.abuseipdb_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP: # self.abuseipdb_remote_ip.append(cmd[7]) - # if self.ModConfig.freeipapi_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP: + # if self.mod_config.freeipapi_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP: # self.freeipapi_remote_ip.append(cmd[7]) - # if self.ModConfig.cloudfilt_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP: + # if self.mod_config.cloudfilt_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP: # self.cloudfilt_remote_ip.append(cmd[7]) return None -def handle_on_nick(uplink: 'Defender', srvmsg: list[str]): +async def handle_on_nick(uplink: 'Defender', srvmsg: list[str]): """Handle nickname changes. >>> srvmsg = ['@unrealircd.org...', ':001MZQ0RB', 'NICK', 'newnickname', '1754663712'] >>> [':97KAAAAAC', 'NICK', 'testinspir', '1757360740'] @@ -146,59 +152,67 @@ def handle_on_nick(uplink: 'Defender', srvmsg: list[str]): srvmsg (list[str]): The Server MSG confmodel (ModConfModel): The Module Configuration """ - p = uplink.Protocol - parser = p.parse_nick(srvmsg) - uid = uplink.Loader.Utils.clean_uid(parser.get('uid', None)) - confmodel = uplink.ModConfig + p = uplink.ctx.Irc.Protocol + u, new_nickname, timestamp = p.parse_nick(srvmsg) - get_reputation = uplink.Reputation.get_reputation(uid) - jail_salon = uplink.Config.SALON_JAIL - service_id = uplink.Config.SERVICE_ID + if u is None: + uplink.ctx.Logs.error(f"[USER OBJ ERROR {timestamp}] - {srvmsg}") + return None + + uid = u.uid + confmodel = uplink.mod_config + + get_reputation = uplink.ctx.Reputation.get_reputation(uid) + jail_salon = uplink.ctx.Config.SALON_JAIL + service_id = uplink.ctx.Config.SERVICE_ID if get_reputation is None: - uplink.Logs.debug(f'This UID: {uid} is not listed in the reputation dataclass') + uplink.ctx.Logs.debug(f'This UID: {uid} is not listed in the reputation dataclass') return None # Update the new nickname oldnick = get_reputation.nickname - newnickname = parser.get('newnickname', None) + newnickname = new_nickname get_reputation.nickname = newnickname # If ban in all channel is ON then unban old nickname an ban the new nickname if confmodel.reputation_ban_all_chan == 1: - for chan in uplink.Channel.UID_CHANNEL_DB: + for chan in uplink.ctx.Channel.UID_CHANNEL_DB: if chan.name != jail_salon: - p.send2socket(f":{service_id} MODE {chan.name} -b {oldnick}!*@*") - p.send2socket(f":{service_id} MODE {chan.name} +b {newnickname}!*@*") + await p.send2socket(f":{service_id} MODE {chan.name} -b {oldnick}!*@*") + await p.send2socket(f":{service_id} MODE {chan.name} +b {newnickname}!*@*") -def handle_on_quit(uplink: 'Defender', srvmsg: list[str]): +async def handle_on_quit(uplink: 'Defender', srvmsg: list[str]): """Handle on quit message >>> srvmsg = ['@unrealircd.org...', ':001MZQ0RB', 'QUIT', ':Quit:', 'quit message'] Args: uplink (Irc): The Defender Module instance srvmsg (list[str]): The Server MSG """ - p = uplink.Protocol - parser = p.parse_quit(srvmsg) - confmodel = uplink.ModConfig + p = uplink.ctx.Irc.Protocol + userobj, reason = p.parse_quit(srvmsg) + confmodel = uplink.mod_config + + if userobj is None: + uplink.ctx.Logs.debug(f"This UID do not exist anymore: {srvmsg}") + return None - ban_all_chan = uplink.Base.int_if_possible(confmodel.reputation_ban_all_chan) - final_UID = uplink.Loader.Utils.clean_uid(str(parser.get('uid', None))) - jail_salon = uplink.Config.SALON_JAIL - service_id = uplink.Config.SERVICE_ID - get_user_reputation = uplink.Reputation.get_reputation(final_UID) + ban_all_chan = uplink.ctx.Base.int_if_possible(confmodel.reputation_ban_all_chan) + jail_salon = uplink.ctx.Config.SALON_JAIL + service_id = uplink.ctx.Config.SERVICE_ID + get_user_reputation = uplink.ctx.Reputation.get_reputation(userobj.uid) if get_user_reputation is not None: final_nickname = get_user_reputation.nickname - for chan in uplink.Channel.UID_CHANNEL_DB: + for chan in uplink.ctx.Channel.UID_CHANNEL_DB: if chan.name != jail_salon and ban_all_chan == 1: - p.send2socket(f":{service_id} MODE {chan.name} -b {final_nickname}!*@*") - uplink.Logs.debug(f"Mode -b {final_nickname} on channel {chan.name}") + await p.send2socket(f":{service_id} MODE {chan.name} -b {final_nickname}!*@*") + uplink.ctx.Logs.debug(f"Mode -b {final_nickname} on channel {chan.name}") - uplink.Reputation.delete(final_UID) - uplink.Logs.debug(f"Client {get_user_reputation.nickname} has been removed from Reputation local DB") + uplink.ctx.Reputation.delete(userobj.uid) + uplink.ctx.Logs.debug(f"Client {get_user_reputation.nickname} has been removed from Reputation local DB") -def handle_on_uid(uplink: 'Defender', srvmsg: list[str]): +async def handle_on_uid(uplink: 'Defender', srvmsg: list[str]): """_summary_ >>> ['@s2s-md...', ':001', 'UID', 'nickname', '0', '1754675249', '...', '125-168-141-239.hostname.net', '001BAPN8M', '0', '+iwx', '*', '32001BBE.25ACEFE7.429FE90D.IP', 'ZA2ic7w==', ':realname'] @@ -207,20 +221,18 @@ def handle_on_uid(uplink: 'Defender', srvmsg: list[str]): uplink (Defender): The Defender instance srvmsg (list[str]): The Server MSG """ - parser_uid = uplink.Protocol.parse_uid(srvmsg) - gconfig = uplink.Config - irc = uplink.Irc - confmodel = uplink.ModConfig + irc = uplink.ctx.Irc + _User = irc.Protocol.parse_uid(srvmsg) + gconfig = uplink.ctx.Config + confmodel = uplink.mod_config # If Init then do nothing if gconfig.DEFENDER_INIT == 1: return None # Get User information - _User = irc.User.get_user(parser_uid.get('uid', None)) - if _User is None: - irc.Logs.warning(f'This UID: [{parser_uid.get("uid", None)}] is not available please check why') + uplink.ctx.Logs.warning(f'Error when parsing UID', exc_info=True) return # If user is not service or IrcOp then scan them @@ -239,38 +251,38 @@ def handle_on_uid(uplink: 'Defender', srvmsg: list[str]): if not match(r'^.*[S|o?].*$', _User.umodes): if reputation_flag == 1 and _User.score_connexion <= reputation_seuil: # currentDateTime = self.Base.get_datetime() - irc.Reputation.insert( - irc.Loader.Definition.MReputation( + uplink.ctx.Reputation.insert( + uplink.ctx.Definition.MReputation( **_User.to_dict(), - secret_code=irc.Utils.generate_random_string(8) + secret_code=uplink.ctx.Utils.generate_random_string(8) ) ) - if irc.Reputation.is_exist(_User.uid): + if uplink.ctx.Reputation.is_exist(_User.uid): if reputation_flag == 1 and _User.score_connexion <= reputation_seuil: - action_add_reputation_sanctions(uplink, _User.uid) - irc.Logs.info(f'[REPUTATION] Reputation system ON (Nickname: {_User.nickname}, uid: {_User.uid})') + await action_add_reputation_sanctions(uplink, _User.uid) + uplink.ctx.Logs.info(f'[REPUTATION] Reputation system ON (Nickname: {_User.nickname}, uid: {_User.uid})') #################### # ACTION FUNCTIONS # #################### # [:] UID []+ : # [:] UID nickname hopcount timestamp username hostname uid servicestamp umodes virthost cloakedhost ip :gecos -def action_on_flood(uplink: 'Defender', srvmsg: list[str]): +async def action_on_flood(uplink: 'Defender', srvmsg: list[str]): - confmodel = uplink.ModConfig + confmodel = uplink.mod_config if confmodel.flood == 0: return None - irc = uplink.Irc - gconfig = uplink.Config - p = uplink.Protocol + irc = uplink.ctx.Irc + gconfig = uplink.ctx.Config + p = irc.Protocol flood_users = uplink.Schemas.DB_FLOOD_USERS user_trigger = str(srvmsg[1]).replace(':','') channel = srvmsg[3] - User = irc.User.get_user(user_trigger) + User = uplink.ctx.User.get_user(user_trigger) - if User is None or not irc.Channel.is_valid_channel(channel_to_check=channel): + if User is None or not uplink.ctx.Channel.is_valid_channel(channel_to_check=channel): return flood_time = confmodel.flood_time @@ -283,7 +295,7 @@ def action_on_flood(uplink: 'Defender', srvmsg: list[str]): get_detected_uid = User.uid get_detected_nickname = User.nickname - unixtime = irc.Utils.get_unixtime() + unixtime = uplink.ctx.Utils.get_unixtime() get_diff_secondes = 0 def get_flood_user(uid: str) -> Optional[FloodUser]: @@ -304,29 +316,29 @@ def action_on_flood(uplink: 'Defender', srvmsg: list[str]): fu.nbr_msg = 0 get_diff_secondes = unixtime - fu.first_msg_time elif fu.nbr_msg > flood_message: - irc.Logs.info('system de flood detecté') - p.send_priv_msg( + uplink.ctx.Logs.info('system de flood detecté') + await p.send_priv_msg( nick_from=dnickname, msg=f"{color_red} {color_bold} Flood detected. Apply the +m mode (Ô_o)", channel=channel ) - p.send2socket(f":{service_id} MODE {channel} +m") - irc.Logs.info(f'FLOOD Détecté sur {get_detected_nickname} mode +m appliqué sur le salon {channel}') + await p.send2socket(f":{service_id} MODE {channel} +m") + uplink.ctx.Logs.info(f'FLOOD Détecté sur {get_detected_nickname} mode +m appliqué sur le salon {channel}') fu.nbr_msg = 0 fu.first_msg_time = unixtime - irc.Base.create_timer(flood_timer, dthreads.timer_release_mode_mute, (uplink, 'mode-m', channel)) + uplink.ctx.Base.create_asynctask(dthreads.coro_release_mode_mute(uplink, 'mode-m', channel)) -def action_add_reputation_sanctions(uplink: 'Defender', jailed_uid: str ): +async def action_add_reputation_sanctions(uplink: 'Defender', jailed_uid: str ): - irc = uplink.Irc - gconfig = uplink.Config - p = uplink.Protocol - confmodel = uplink.ModConfig + irc = uplink.ctx.Irc + gconfig = uplink.ctx.Config + p = irc.Protocol + confmodel = uplink.mod_config - get_reputation = irc.Reputation.get_reputation(jailed_uid) + get_reputation = uplink.ctx.Reputation.get_reputation(jailed_uid) if get_reputation is None: - irc.Logs.warning(f'UID {jailed_uid} has not been found') + uplink.ctx.Logs.warning(f'UID {jailed_uid} has not been found') return salon_logs = gconfig.SERVICE_CHANLOG @@ -346,33 +358,33 @@ def action_add_reputation_sanctions(uplink: 'Defender', jailed_uid: str ): if not get_reputation.isWebirc: # Si le user ne vient pas de webIrc - p.send_sajoin(nick_to_sajoin=jailed_nickname, channel_name=salon_jail) - p.send_priv_msg(nick_from=gconfig.SERVICE_NICKNAME, + await p.send_sajoin(nick_to_sajoin=jailed_nickname, channel_name=salon_jail) + await p.send_priv_msg(nick_from=gconfig.SERVICE_NICKNAME, msg=f" [{color_red} REPUTATION {nogc}] : Connexion de {jailed_nickname} ({jailed_score}) ==> {salon_jail}", channel=salon_logs ) - p.send_notice( + await p.send_notice( nick_from=gconfig.SERVICE_NICKNAME, nick_to=jailed_nickname, msg=f"[{color_red} {jailed_nickname} {color_black}] : Merci de tapez la commande suivante {color_bold}{service_prefix}code {code}{color_bold}" ) if reputation_ban_all_chan == 1: - for chan in irc.Channel.UID_CHANNEL_DB: + for chan in uplink.ctx.Channel.UID_CHANNEL_DB: if chan.name != salon_jail: - p.send2socket(f":{service_id} MODE {chan.name} +b {jailed_nickname}!*@*") - p.send2socket(f":{service_id} KICK {chan.name} {jailed_nickname}") + await p.send2socket(f":{service_id} MODE {chan.name} +b {jailed_nickname}!*@*") + await p.send2socket(f":{service_id} KICK {chan.name} {jailed_nickname}") - irc.Logs.info(f"[REPUTATION] {jailed_nickname} jailed (UID: {jailed_uid}, score: {jailed_score})") + uplink.ctx.Logs.info(f"[REPUTATION] {jailed_nickname} jailed (UID: {jailed_uid}, score: {jailed_score})") else: - irc.Logs.info(f"[REPUTATION] {jailed_nickname} skipped (trusted or WebIRC)") - irc.Reputation.delete(jailed_uid) + uplink.ctx.Logs.info(f"[REPUTATION] {jailed_nickname} skipped (trusted or WebIRC)") + uplink.ctx.Reputation.delete(jailed_uid) -def action_apply_reputation_santions(uplink: 'Defender') -> None: +async def action_apply_reputation_santions(uplink: 'Defender') -> None: - irc = uplink.Irc - gconfig = uplink.Config - p = uplink.Protocol - confmodel = uplink.ModConfig + irc = uplink.ctx.Irc + gconfig = uplink.ctx.Config + p = irc.Protocol + confmodel = uplink.mod_config reputation_flag = confmodel.reputation reputation_timer = confmodel.reputation_timer @@ -385,36 +397,36 @@ def action_apply_reputation_santions(uplink: 'Defender') -> None: salon_jail = gconfig.SALON_JAIL uid_to_clean = [] - if reputation_flag == 0 or reputation_timer == 0: + if reputation_flag == 0 or reputation_timer == 0 or not uplink.ctx.Reputation.UID_REPUTATION_DB: return None - for user in irc.Reputation.UID_REPUTATION_DB: + for user in uplink.ctx.Reputation.UID_REPUTATION_DB: if not user.isWebirc: # Si il ne vient pas de WebIRC - if irc.User.get_user_uptime_in_minutes(user.uid) >= reputation_timer and int(user.score_connexion) <= int(reputation_seuil): - p.send_priv_msg( + if uplink.ctx.User.get_user_uptime_in_minutes(user.uid) >= reputation_timer and int(user.score_connexion) <= int(reputation_seuil): + await p.send_priv_msg( nick_from=service_id, msg=f"[{color_red} REPUTATION {nogc}] : Action sur {user.nickname} aprés {str(reputation_timer)} minutes d'inactivité", channel=dchanlog ) - p.send2socket(f":{service_id} KILL {user.nickname} After {str(reputation_timer)} minutes of inactivity you should reconnect and type the password code") - p.send2socket(f":{gconfig.SERVEUR_LINK} REPUTATION {user.remote_ip} 0") + await p.send2socket(f":{service_id} KILL {user.nickname} After {str(reputation_timer)} minutes of inactivity you should reconnect and type the password code") + await p.send2socket(f":{gconfig.SERVEUR_LINK} REPUTATION {user.remote_ip} 0") - irc.Logs.info(f"Nickname: {user.nickname} KILLED after {str(reputation_timer)} minutes of inactivity") + uplink.ctx.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: # Suppression des éléments dans {UID_DB} et {REPUTATION_DB} - for chan in irc.Channel.UID_CHANNEL_DB: + for chan in uplink.ctx.Channel.UID_CHANNEL_DB: if chan.name != salon_jail and ban_all_chan == 1: - get_user_reputation = irc.Reputation.get_reputation(uid) - p.send2socket(f":{service_id} MODE {chan.name} -b {get_user_reputation.nickname}!*@*") + get_user_reputation = uplink.ctx.Reputation.get_reputation(uid) + await p.send2socket(f":{service_id} MODE {chan.name} -b {get_user_reputation.nickname}!*@*") # Lorsqu'un utilisateur quitte, il doit être supprimé de {UID_DB}. - irc.Channel.delete_user_from_all_channel(uid) - irc.Reputation.delete(uid) - irc.User.delete(uid) + uplink.ctx.Channel.delete_user_from_all_channel(uid) + uplink.ctx.Reputation.delete(uid) + uplink.ctx.User.delete(uid) -def action_scan_client_with_cloudfilt(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]: +async def action_scan_client_with_cloudfilt(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]: """Analyse l'ip avec cloudfilt Cette methode devra etre lancer toujours via un thread ou un timer. Args: @@ -429,19 +441,19 @@ def action_scan_client_with_cloudfilt(uplink: 'Defender', user_model: 'MUser') - username = user_model.username hostname = user_model.hostname nickname = user_model.nickname - p = uplink.Protocol + p = uplink.ctx.Irc.Protocol - if remote_ip in uplink.Config.WHITELISTED_IP: + if remote_ip in uplink.ctx.Config.WHITELISTED_IP: return None - if uplink.ModConfig.cloudfilt_scan == 0: + if uplink.mod_config.cloudfilt_scan == 0: return None if uplink.cloudfilt_key == '': return None - service_id = uplink.Config.SERVICE_ID - service_chanlog = uplink.Config.SERVICE_CHANLOG - color_red = uplink.Config.COLORS.red - nogc = uplink.Config.COLORS.nogc + service_id = uplink.ctx.Config.SERVICE_ID + service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG + color_red = uplink.ctx.Config.COLORS.red + nogc = uplink.ctx.Config.COLORS.nogc url = "https://developers18334.cloudfilt.com/" @@ -455,7 +467,7 @@ def action_scan_client_with_cloudfilt(uplink: 'Defender', user_model: 'MUser') - decoded_response: dict = loads(response.text) status_code = response.status_code if status_code != 200: - uplink.Logs.warning(f'Error connecting to cloudfilt API | Code: {str(status_code)}') + uplink.ctx.Logs.warning(f'Error connecting to cloudfilt API | Code: {str(status_code)}') return result = { @@ -468,22 +480,22 @@ def action_scan_client_with_cloudfilt(uplink: 'Defender', user_model: 'MUser') - # pseudo!ident@host fullname = f'{nickname}!{username}@{hostname}' - p.send_priv_msg( + await p.send_priv_msg( nick_from=service_id, msg=f"[ {color_red}CLOUDFILT_SCAN{nogc} ] : Connexion de {fullname} ({remote_ip}) ==> Host: {str(result['host'])} | country: {str(result['countryiso'])} | listed: {str(result['listed'])} | listed by : {str(result['listed_by'])}", channel=service_chanlog) - uplink.Logs.debug(f"[CLOUDFILT SCAN] ({fullname}) connected from ({result['countryiso']}), Listed: {result['listed']}, by: {result['listed_by']}") + uplink.ctx.Logs.debug(f"[CLOUDFILT SCAN] ({fullname}) connected from ({result['countryiso']}), Listed: {result['listed']}, by: {result['listed_by']}") if result['listed']: - p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.Config.GLINE_DURATION} Your connexion is listed as dangerous {str(result['listed'])} {str(result['listed_by'])} - detected by cloudfilt") - uplink.Logs.debug(f"[CLOUDFILT SCAN GLINE] Dangerous connection ({fullname}) from ({result['countryiso']}) Listed: {result['listed']}, by: {result['listed_by']}") + await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} Your connexion is listed as dangerous {str(result['listed'])} {str(result['listed_by'])} - detected by cloudfilt") + uplink.ctx.Logs.debug(f"[CLOUDFILT SCAN GLINE] Dangerous connection ({fullname}) from ({result['countryiso']}) Listed: {result['listed']}, by: {result['listed_by']}") response.close() return result -def action_scan_client_with_freeipapi(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]: +async def action_scan_client_with_freeipapi(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]: """Analyse l'ip avec Freeipapi Cette methode devra etre lancer toujours via un thread ou un timer. Args: @@ -493,21 +505,21 @@ def action_scan_client_with_freeipapi(uplink: 'Defender', user_model: 'MUser') - dict[str, any] | None: les informations du provider keys : 'countryCode', 'isProxy' """ - p = uplink.Protocol + p = uplink.ctx.Irc.Protocol remote_ip = user_model.remote_ip username = user_model.username hostname = user_model.hostname nickname = user_model.nickname - if remote_ip in uplink.Config.WHITELISTED_IP: + if remote_ip in uplink.ctx.Config.WHITELISTED_IP: return None - if uplink.ModConfig.freeipapi_scan == 0: + if uplink.mod_config.freeipapi_scan == 0: return None - service_id = uplink.Config.SERVICE_ID - service_chanlog = uplink.Config.SERVICE_CHANLOG - color_red = uplink.Config.COLORS.red - nogc = uplink.Config.COLORS.nogc + service_id = uplink.ctx.Config.SERVICE_ID + service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG + color_red = uplink.ctx.Config.COLORS.red + nogc = uplink.ctx.Config.COLORS.nogc url = f'https://freeipapi.com/api/json/{remote_ip}' @@ -522,10 +534,10 @@ def action_scan_client_with_freeipapi(uplink: 'Defender', user_model: 'MUser') - status_code = response.status_code if status_code == 429: - uplink.Logs.warning('Too Many Requests - The rate limit for the API has been exceeded.') + uplink.ctx.Logs.warning('Too Many Requests - The rate limit for the API has been exceeded.') return None elif status_code != 200: - uplink.Logs.warning(f'status code = {str(status_code)}') + uplink.ctx.Logs.warning(f'status code = {str(status_code)}') return None result = { @@ -536,21 +548,21 @@ def action_scan_client_with_freeipapi(uplink: 'Defender', user_model: 'MUser') - # pseudo!ident@host fullname = f'{nickname}!{username}@{hostname}' - p.send_priv_msg( + await p.send_priv_msg( nick_from=service_id, msg=f"[ {color_red}FREEIPAPI_SCAN{nogc} ] : Connexion de {fullname} ({remote_ip}) ==> Proxy: {str(result['isProxy'])} | Country : {str(result['countryCode'])}", channel=service_chanlog) - uplink.Logs.debug(f"[FREEIPAPI SCAN] ({fullname}) connected from ({result['countryCode']}), Proxy: {result['isProxy']}") + uplink.ctx.Logs.debug(f"[FREEIPAPI SCAN] ({fullname}) connected from ({result['countryCode']}), Proxy: {result['isProxy']}") if result['isProxy']: - p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.Config.GLINE_DURATION} This server do not allow proxy connexions {str(result['isProxy'])} - detected by freeipapi") - uplink.Logs.debug(f"[FREEIPAPI SCAN GLINE] Server do not allow proxy connexions {result['isProxy']}") + await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} This server do not allow proxy connexions {str(result['isProxy'])} - detected by freeipapi") + uplink.ctx.Logs.debug(f"[FREEIPAPI SCAN GLINE] Server do not allow proxy connexions {result['isProxy']}") response.close() return result -def action_scan_client_with_abuseipdb(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]: +async def action_scan_client_with_abuseipdb(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]: """Analyse l'ip avec AbuseIpDB Cette methode devra etre lancer toujours via un thread ou un timer. Args: @@ -560,15 +572,15 @@ def action_scan_client_with_abuseipdb(uplink: 'Defender', user_model: 'MUser') - Returns: dict[str, str] | None: les informations du provider """ - p = uplink.Protocol + p = uplink.ctx.Irc.Protocol remote_ip = user_model.remote_ip username = user_model.username hostname = user_model.hostname nickname = user_model.nickname - if remote_ip in uplink.Config.WHITELISTED_IP: + if remote_ip in uplink.ctx.Config.WHITELISTED_IP: return None - if uplink.ModConfig.abuseipdb_scan == 0: + if uplink.mod_config.abuseipdb_scan == 0: return None if uplink.abuseipdb_key == '': @@ -600,81 +612,81 @@ def action_scan_client_with_abuseipdb(uplink: 'Defender', user_model: 'MUser') - 'totalReports': decoded_response.get('data', {}).get('totalReports', 0) } - service_id = uplink.Config.SERVICE_ID - service_chanlog = uplink.Config.SERVICE_CHANLOG - color_red = uplink.Config.COLORS.red - nogc = uplink.Config.COLORS.nogc + service_id = uplink.ctx.Config.SERVICE_ID + service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG + color_red = uplink.ctx.Config.COLORS.red + nogc = uplink.ctx.Config.COLORS.nogc # pseudo!ident@host fullname = f'{nickname}!{username}@{hostname}' - p.send_priv_msg( + await p.send_priv_msg( nick_from=service_id, msg=f"[ {color_red}ABUSEIPDB_SCAN{nogc} ] : Connexion de {fullname} ({remote_ip}) ==> Score: {str(result['score'])} | Country : {result['country']} | Tor : {str(result['isTor'])} | Total Reports : {str(result['totalReports'])}", channel=service_chanlog ) - uplink.Logs.debug(f"[ABUSEIPDB SCAN] ({fullname}) connected from ({result['country']}), Score: {result['score']}, Tor: {result['isTor']}") + uplink.ctx.Logs.debug(f"[ABUSEIPDB SCAN] ({fullname}) connected from ({result['country']}), Score: {result['score']}, Tor: {result['isTor']}") if result['isTor']: - p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.Config.GLINE_DURATION} This server do not allow Tor connexions {str(result['isTor'])} - Detected by Abuseipdb") - uplink.Logs.debug(f"[ABUSEIPDB SCAN GLINE] Server do not allow Tor connections Tor: {result['isTor']}, Score: {result['score']}") + await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} This server do not allow Tor connexions {str(result['isTor'])} - Detected by Abuseipdb") + uplink.ctx.Logs.debug(f"[ABUSEIPDB SCAN GLINE] Server do not allow Tor connections Tor: {result['isTor']}, Score: {result['score']}") elif result['score'] >= 95: - p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.Config.GLINE_DURATION} You were banned from this server because your abuse score is = {str(result['score'])} - Detected by Abuseipdb") - uplink.Logs.debug(f"[ABUSEIPDB SCAN GLINE] Server do not high risk connections Country: {result['country']}, Score: {result['score']}") + await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} You were banned from this server because your abuse score is = {str(result['score'])} - Detected by Abuseipdb") + uplink.ctx.Logs.debug(f"[ABUSEIPDB SCAN GLINE] Server do not high risk connections Country: {result['country']}, Score: {result['score']}") response.close() return result -def action_scan_client_with_local_socket(uplink: 'Defender', user_model: 'MUser'): +async def action_scan_client_with_local_socket(uplink: 'Defender', user_model: 'MUser'): """local_scan Args: uplink (Defender): Defender instance object user_model (MUser): l'objet User qui contient l'ip """ - p = uplink.Protocol + p = uplink.ctx.Irc.Protocol remote_ip = user_model.remote_ip username = user_model.username hostname = user_model.hostname nickname = user_model.nickname fullname = f'{nickname}!{username}@{hostname}' - if remote_ip in uplink.Config.WHITELISTED_IP: + if remote_ip in uplink.ctx.Config.WHITELISTED_IP: return None - for port in uplink.Config.PORTS_TO_SCAN: + for port in uplink.ctx.Config.PORTS_TO_SCAN: try: newSocket = '' newSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK) newSocket.settimeout(0.5) - connection = (remote_ip, uplink.Base.int_if_possible(port)) + connection = (remote_ip, uplink.ctx.Base.int_if_possible(port)) newSocket.connect(connection) - p.send_priv_msg( - nick_from=uplink.Config.SERVICE_NICKNAME, - msg=f"[ {uplink.Config.COLORS.red}PROXY_SCAN{uplink.Config.COLORS.nogc} ] {fullname} ({remote_ip}) : Port [{str(port)}] ouvert sur l'adresse ip [{remote_ip}]", - channel=uplink.Config.SERVICE_CHANLOG + await p.send_priv_msg( + nick_from=uplink.ctx.Config.SERVICE_NICKNAME, + msg=f"[ {uplink.ctx.Config.COLORS.red}PROXY_SCAN{uplink.ctx.Config.COLORS.nogc} ] {fullname} ({remote_ip}) : Port [{str(port)}] ouvert sur l'adresse ip [{remote_ip}]", + channel=uplink.ctx.Config.SERVICE_CHANLOG ) # print(f"=======> Le port {str(port)} est ouvert !!") - uplink.Base.running_sockets.append(newSocket) + uplink.ctx.Base.running_sockets.append(newSocket) # print(newSocket) newSocket.shutdown(socket.SHUT_RDWR) newSocket.close() except (socket.timeout, ConnectionRefusedError): - uplink.Logs.info(f"Le port {remote_ip}:{str(port)} est fermé") + uplink.ctx.Logs.info(f"Le port {remote_ip}:{str(port)} est fermé") except AttributeError as ae: - uplink.Logs.warning(f"AttributeError ({remote_ip}): {ae}") + uplink.ctx.Logs.warning(f"AttributeError ({remote_ip}): {ae}") except socket.gaierror as err: - uplink.Logs.warning(f"Address Info Error ({remote_ip}): {err}") + uplink.ctx.Logs.warning(f"Address Info Error ({remote_ip}): {err}") finally: # newSocket.shutdown(socket.SHUT_RDWR) newSocket.close() - uplink.Logs.info('=======> Fermeture de la socket') + uplink.ctx.Logs.info('=======> Fermeture de la socket') -def action_scan_client_with_psutil(uplink: 'Defender', user_model: 'MUser') -> list[int]: +async def action_scan_client_with_psutil(uplink: 'Defender', user_model: 'MUser') -> list[int]: """psutil_scan for Linux (should be run on the same location as the unrealircd server) Args: @@ -683,13 +695,13 @@ def action_scan_client_with_psutil(uplink: 'Defender', user_model: 'MUser') -> l Returns: list[int]: list of ports """ - p = uplink.Protocol + p = uplink.ctx.Irc.Protocol remote_ip = user_model.remote_ip username = user_model.username hostname = user_model.hostname nickname = user_model.nickname - if remote_ip in uplink.Config.WHITELISTED_IP: + if remote_ip in uplink.ctx.Config.WHITELISTED_IP: return None try: @@ -697,16 +709,16 @@ def action_scan_client_with_psutil(uplink: 'Defender', user_model: 'MUser') -> l fullname = f'{nickname}!{username}@{hostname}' matching_ports = [conn.raddr.port for conn in connections if conn.raddr and conn.raddr.ip == remote_ip] - uplink.Logs.info(f"Connexion of {fullname} ({remote_ip}) using ports : {str(matching_ports)}") + uplink.ctx.Logs.info(f"Connexion of {fullname} ({remote_ip}) using ports : {str(matching_ports)}") if matching_ports: - p.send_priv_msg( - nick_from=uplink.Config.SERVICE_NICKNAME, - msg=f"[ {uplink.Config.COLORS.red}PSUTIL_SCAN{uplink.Config.COLORS.black} ] {fullname} ({remote_ip}) : is using ports {matching_ports}", - channel=uplink.Config.SERVICE_CHANLOG + await p.send_priv_msg( + nick_from=uplink.ctx.Config.SERVICE_NICKNAME, + msg=f"[ {uplink.ctx.Config.COLORS.red}PSUTIL_SCAN{uplink.ctx.Config.COLORS.black} ] {fullname} ({remote_ip}) : is using ports {matching_ports}", + channel=uplink.ctx.Config.SERVICE_CHANLOG ) return matching_ports except psutil.AccessDenied as ad: - uplink.Logs.critical(f'psutil_scan: Permission error: {ad}') \ No newline at end of file + uplink.ctx.Logs.critical(f'psutil_scan: Permission error: {ad}') \ No newline at end of file diff --git a/mods/jsonrpc/mod_jsonrpc.py b/mods/jsonrpc/mod_jsonrpc.py index 1b7bcea..61787d3 100644 --- a/mods/jsonrpc/mod_jsonrpc.py +++ b/mods/jsonrpc/mod_jsonrpc.py @@ -1,149 +1,61 @@ import logging -import asyncio +from typing import TYPE_CHECKING, Any, Optional from unrealircd_rpc_py.objects.Definition import LiveRPCResult +from core.classes.interfaces.imodule import IModule +import mods.jsonrpc.schemas as schemas import mods.jsonrpc.utils as utils import mods.jsonrpc.threads as thds -from time import sleep -from typing import TYPE_CHECKING from dataclasses import dataclass from unrealircd_rpc_py.ConnectionFactory import ConnectionFactory from unrealircd_rpc_py.LiveConnectionFactory import LiveConnectionFactory if TYPE_CHECKING: - from core.irc import Irc + from core.loader import Loader -class Jsonrpc(): +class Jsonrpc(IModule): @dataclass - class ModConfModel: + class ModConfModel(schemas.ModConfModel): """The Model containing the module parameters """ - jsonrpc: int = 0 + ... - def __init__(self, ircInstance: 'Irc') -> None: + MOD_HEADER: dict[str, str] = { + 'name':'JsonRPC', + 'version':'1.0.0', + 'description':'Module using the unrealircd-rpc-py library', + 'author':'Defender Team', + 'core_version':'Defender-6' + } - # Module name (Mandatory) - self.module_name = 'mod_' + str(self.__class__.__name__).lower() + def __init__(self, context: 'Loader') -> None: + super().__init__(context) + self._mod_config: Optional[schemas.ModConfModel] = None - # Add Irc Object to the module (Mandatory) - self.Irc = ircInstance + @property + def mod_config(self) -> ModConfModel: + return self._mod_config - # Add Protocol to the module (Mandatory) - self.Protocol = ircInstance.Protocol + async def callback_sent_to_irc(self, response: LiveRPCResult) -> None: - # Add Global Configuration to the module (Mandatory) - self.Config = ircInstance.Config - - # Add Base object to the module (Mandatory) - self.Base = ircInstance.Base - - # Add Main Utils (Mandatory) - self.MainUtils = ircInstance.Utils - - # Add logs object to the module (Mandatory) - self.Logs = ircInstance.Loader.Logs - - # Add User object to the module (Mandatory) - self.User = ircInstance.User - - # Add Channel object to the module (Mandatory) - self.Channel = ircInstance.Channel - - # Is RPC Active? - self.is_streaming = False - - # Module Utils - self.Utils = utils - - # Module threads - self.Threads = thds - - # Run Garbage collector. - self.Base.create_timer(10, self.MainUtils.run_python_garbage_collector) - - # Create module commands (Mandatory) - self.Irc.build_command(1, self.module_name, 'jsonrpc', 'Activate the JSON RPC Live connection [ON|OFF]') - self.Irc.build_command(1, self.module_name, 'jruser', 'Get Information about a user using JSON RPC') - self.Irc.build_command(1, self.module_name, 'jrinstances', 'Get number of instances') - - # Init the module - self.__init_module() - - # Log the module - self.Logs.debug(f'Module {self.module_name} loaded ...') - - def __init_module(self) -> None: - - logging.getLogger('websockets').setLevel(logging.WARNING) - logging.getLogger('unrealircd-rpc-py').setLevel(logging.CRITICAL) - logging.getLogger('unrealircd-liverpc-py').setLevel(logging.CRITICAL) - - # Create you own tables (Mandatory) - # self.__create_tables() - - # Load module configuration and sync with core one (Mandatory) - self.__load_module_configuration() - # End of mandatory methods you can start your customization # - - try: - self.Rpc = ConnectionFactory(self.Config.DEBUG_LEVEL).get(self.Config.JSONRPC_METHOD) - self.LiveRpc = LiveConnectionFactory(self.Config.DEBUG_LEVEL).get(self.Config.JSONRPC_METHOD) - self.Rpc.setup({'url': self.Config.JSONRPC_URL, 'username': self.Config.JSONRPC_USER, 'password': self.Config.JSONRPC_PASSWORD}) - self.LiveRpc.setup({'url': self.Config.JSONRPC_URL, 'username': self.Config.JSONRPC_USER, 'password': self.Config.JSONRPC_PASSWORD, - 'callback_object_instance' : self, 'callback_method_or_function_name': 'callback_sent_to_irc'}) - - if self.ModConfig.jsonrpc == 1: - self.Base.create_thread(func=self.Threads.thread_subscribe, func_args=(self, ), run_once=True) - - return None - except Exception as err: - self.Protocol.send_priv_msg( - nick_from=self.Config.SERVICE_NICKNAME, - msg=f"[{self.Config.COLORS.red}JSONRPC ERROR{self.Config.COLORS.nogc}] {err.__str__()}", - channel=self.Config.SERVICE_CHANLOG - ) - self.Logs.error(f"JSONRPC ERROR: {err.__str__()}") - - def __create_tables(self) -> None: - """Methode qui va créer la base de donnée si elle n'existe pas. - Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module - Args: - database_name (str): Nom de la base de données ( pas d'espace dans le nom ) - - Returns: - None: Aucun retour n'es attendu - """ - - table_logs = '''CREATE TABLE IF NOT EXISTS test_logs ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - datetime TEXT, - server_msg TEXT - ) - ''' - - self.Base.db_execute_query(table_logs) - return None - - def callback_sent_to_irc(self, response: LiveRPCResult) -> None: - - dnickname = self.Config.SERVICE_NICKNAME - dchanlog = self.Config.SERVICE_CHANLOG - green = self.Config.COLORS.green - nogc = self.Config.COLORS.nogc - bold = self.Config.COLORS.bold - red = self.Config.COLORS.red + dnickname = self.ctx.Config.SERVICE_NICKNAME + dchanlog = self.ctx.Config.SERVICE_CHANLOG + green = self.ctx.Config.COLORS.green + nogc = self.ctx.Config.COLORS.nogc + bold = self.ctx.Config.COLORS.bold + red = self.ctx.Config.COLORS.red if response.error.code != 0: - self.Protocol.send_priv_msg(nick_from=dnickname, + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[{bold}{red}JSONRPC ERROR{nogc}{bold}] {response.error.message} ({response.error.code})", channel=dchanlog) return None if isinstance(response.result, bool): if response.result: - self.Protocol.send_priv_msg( - nick_from=self.Config.SERVICE_NICKNAME, - msg=f"[{bold}{green}JSONRPC{nogc}{bold}] JSONRPC Event activated on {self.Config.JSONRPC_URL}", + await self.ctx.Irc.Protocol.send_priv_msg( + nick_from=self.ctx.Config.SERVICE_NICKNAME, + msg=f"[{bold}{green}JSONRPC{nogc}{bold}] JSONRPC Event activated on {self.ctx.Config.JSONRPC_URL}", channel=dchanlog) return None @@ -154,56 +66,92 @@ class Jsonrpc(): msg = response.result.msg if hasattr(response.result, 'msg') else '' build_msg = f"{green}{log_source}{nogc}: [{bold}{level}{bold}] {subsystem}.{event_id} - {msg}" - self.Protocol.send_priv_msg(nick_from=dnickname, msg=build_msg, channel=dchanlog) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=build_msg, channel=dchanlog) return None - def __load_module_configuration(self) -> None: - """### Load Module Configuration - """ - try: - # Build the default configuration model (Mandatory) - self.ModConfig = self.ModConfModel(jsonrpc=0) + def create_tables(self) -> None: + return None - # Sync the configuration with core configuration (Mandatory) - self.Base.db_sync_core_config(self.module_name, self.ModConfig) + async def load(self) -> None: + logging.getLogger('websockets').setLevel(logging.WARNING) + logging.getLogger('unrealircd-rpc-py').setLevel(logging.CRITICAL) + logging.getLogger('unrealircd-liverpc-py').setLevel(logging.CRITICAL) + + self._mod_config = self.ModConfModel(jsonrpc=0) + + await self.sync_db() + + if self.ctx.Config.SERVEUR_PROTOCOL.lower() != 'unreal6': + self.ctx.ModuleUtils.unload_one_module(self.module_name, False) return None - except TypeError as te: - self.Logs.critical(te) + # Is RPC Active? + self.is_streaming = False - def update_configuration(self, param_key: str, param_value: str) -> None: - """Update the local and core configuration + # Create module commands (Mandatory) + self.ctx.Irc.build_command(1, self.module_name, 'jsonrpc', 'Activate the JSON RPC Live connection [ON|OFF]') + self.ctx.Irc.build_command(1, self.module_name, 'jruser', 'Get Information about a user using JSON RPC') + self.ctx.Irc.build_command(1, self.module_name, 'jrinstances', 'Get number of instances') - Args: - param_key (str): The parameter key - param_value (str): The parameter value - """ - self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value) + try: + self.Rpc = ConnectionFactory(self.ctx.Config.DEBUG_LEVEL).get(self.ctx.Config.JSONRPC_METHOD) + self.LiveRpc = LiveConnectionFactory(self.ctx.Config.DEBUG_LEVEL).get(self.ctx.Config.JSONRPC_METHOD) + + sync_unixsocket = {'path_to_socket_file': self.ctx.Config.JSONRPC_PATH_TO_SOCKET_FILE} + sync_http = {'url': self.ctx.Config.JSONRPC_URL, 'username': self.ctx.Config.JSONRPC_USER, 'password': self.ctx.Config.JSONRPC_PASSWORD} + + live_unixsocket = {'path_to_socket_file': self.ctx.Config.JSONRPC_PATH_TO_SOCKET_FILE, + 'callback_object_instance' : self, 'callback_method_or_function_name': 'callback_sent_to_irc'} + live_http = {'url': self.ctx.Config.JSONRPC_URL, 'username': self.ctx.Config.JSONRPC_USER, 'password': self.ctx.Config.JSONRPC_PASSWORD, + 'callback_object_instance' : self, 'callback_method_or_function_name': 'callback_sent_to_irc'} - def unload(self) -> None: - if self.is_streaming: - self.Protocol.send_priv_msg( - nick_from=self.Config.SERVICE_NICKNAME, - msg=f"[{self.Config.COLORS.green}JSONRPC INFO{self.Config.COLORS.nogc}] Shutting down RPC system!", - channel=self.Config.SERVICE_CHANLOG + sync_param = sync_unixsocket if self.ctx.Config.JSONRPC_METHOD == 'unixsocket' else sync_http + live_param = live_unixsocket if self.ctx.Config.JSONRPC_METHOD == 'unixsocket' else live_http + + self.Rpc.setup(sync_param) + self.LiveRpc.setup(live_param) + + if self.mod_config.jsonrpc == 1: + self.ctx.Base.create_asynctask(thds.thread_subscribe(self)) + + return None + except Exception as err: + await self.ctx.Irc.Protocol.send_priv_msg( + nick_from=self.ctx.Config.SERVICE_NICKNAME, + msg=f"[{self.ctx.Config.COLORS.red}JSONRPC ERROR{self.ctx.Config.COLORS.nogc}] {err.__str__()}", + channel=self.ctx.Config.SERVICE_CHANLOG ) - self.Base.create_thread(func=self.Threads.thread_unsubscribe, func_args=(self, ), run_once=True) - self.update_configuration('jsonrpc', 0) - self.Irc.Commands.drop_command_by_module(self.module_name) - self.Logs.debug(f"Unloading {self.module_name}") + self.ctx.Logs.error(f"JSONRPC ERROR: {err.__str__()}") + + async def unload(self) -> None: + + if self.ctx.Config.SERVEUR_PROTOCOL != 'unreal6': + await self.ctx.ModuleUtils.unload_one_module(self.ctx.Irc, self.module_name, False) + return None + + if self.is_streaming: + await self.ctx.Irc.Protocol.send_priv_msg( + nick_from=self.ctx.Config.SERVICE_NICKNAME, + msg=f"[{self.ctx.Config.COLORS.green}JSONRPC INFO{self.ctx.Config.COLORS.nogc}] Shutting down RPC system!", + channel=self.ctx.Config.SERVICE_CHANLOG + ) + self.ctx.Base.create_asynctask(thds.thread_unsubscribe(self)) + # await self.update_configuration('jsonrpc', 0) + self.ctx.Commands.drop_command_by_module(self.module_name) + self.ctx.Logs.debug(f"Unloading {self.module_name}") return None - def cmd(self, data: list) -> None: + def cmd(self, data: list[str]) -> None: return None - def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None: + async def hcmds(self, user: str, channel: Any, cmd: list[str], fullcmd: list[str] = []) -> None: command = str(cmd[0]).lower() - dnickname = self.Config.SERVICE_NICKNAME - dchannel = self.Config.SERVICE_CHANLOG + dnickname = self.ctx.Config.SERVICE_NICKNAME + dchannel = self.ctx.Config.SERVICE_CHANLOG fromuser = user fromchannel = str(channel) if not channel is None else None @@ -212,39 +160,30 @@ class Jsonrpc(): case 'jsonrpc': try: if len(cmd) < 2: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jsonrpc on') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jsonrpc off') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jsonrpc on') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jsonrpc off') return None option = str(cmd[1]).lower() match option: case 'on': - thread_name = 'thread_subscribe' - if self.Base.is_thread_alive(thread_name): - self.Protocol.send_priv_msg(nick_from=dnickname, channel=dchannel, msg=f"The Subscription is running") - return None - elif self.Base.is_thread_exist(thread_name): - self.Protocol.send_priv_msg( - nick_from=dnickname, channel=dchannel, - msg=f"The subscription is not running, wait untill the process will be cleaned up" - ) - return None - - self.Base.create_thread(func=self.Threads.thread_subscribe, func_args=(self, ), run_once=True) - self.update_configuration('jsonrpc', 1) + self.ctx.Base.create_asynctask(thds.thread_subscribe(self)) + await self.update_configuration('jsonrpc', 1) case 'off': - self.Base.create_thread(func=self.Threads.thread_unsubscribe, func_args=(self, ), run_once=True) - self.update_configuration('jsonrpc', 0) + self.ctx.Base.create_asynctask(thds.thread_unsubscribe(self)) + await self.update_configuration('jsonrpc', 0) except IndexError as ie: - self.Logs.error(ie) + self.ctx.Logs.error(ie) case 'jruser': try: if len(cmd) < 2: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jruser get nickname') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jruser get nickname') + return None + option = str(cmd[1]).lower() match option: case 'get': @@ -253,42 +192,42 @@ class Jsonrpc(): UserInfo = rpc.User.get(nickname) if UserInfo.error.code != 0: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'{UserInfo.error.message}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'{UserInfo.error.message}') return None - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'UID : {UserInfo.id}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'NICKNAME : {UserInfo.name}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'USERNAME : {UserInfo.user.username}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'REALNAME : {UserInfo.user.realname}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'MODES : {UserInfo.user.modes}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CHANNELS : {[chan.name for chan in UserInfo.user.channels]}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'SECURITY GROUP : {UserInfo.user.security_groups}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'REPUTATION : {UserInfo.user.reputation}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'UID : {UserInfo.id}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'NICKNAME : {UserInfo.name}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'USERNAME : {UserInfo.user.username}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'REALNAME : {UserInfo.user.realname}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'MODES : {UserInfo.user.modes}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CHANNELS : {[chan.name for chan in UserInfo.user.channels]}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'SECURITY GROUP : {UserInfo.user.security_groups}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'REPUTATION : {UserInfo.user.reputation}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'IP : {UserInfo.ip}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'COUNTRY CODE : {UserInfo.geoip.country_code}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'ASN : {UserInfo.geoip.asn}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'ASNAME : {UserInfo.geoip.asname}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CLOAKED HOST : {UserInfo.user.cloakedhost}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'HOSTNAME : {UserInfo.hostname}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'VHOST : {UserInfo.user.vhost}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CLIENT PORT : {UserInfo.client_port}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'SERVER PORT : {UserInfo.server_port}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'IP : {UserInfo.ip}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'COUNTRY CODE : {UserInfo.geoip.country_code}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'ASN : {UserInfo.geoip.asn}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'ASNAME : {UserInfo.geoip.asname}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CLOAKED HOST : {UserInfo.user.cloakedhost}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'HOSTNAME : {UserInfo.hostname}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'VHOST : {UserInfo.user.vhost}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CLIENT PORT : {UserInfo.client_port}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'SERVER PORT : {UserInfo.server_port}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CERTFP : {UserInfo.tls.certfp}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CIPHER : {UserInfo.tls.cipher}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CERTFP : {UserInfo.tls.certfp}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CIPHER : {UserInfo.tls.cipher}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'IDLE SINCE : {UserInfo.idle_since}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CONNECTED SINCE : {UserInfo.connected_since}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'IDLE SINCE : {UserInfo.idle_since}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CONNECTED SINCE : {UserInfo.connected_since}') except IndexError as ie: - self.Logs.error(ie) + self.ctx.Logs.error(ie) case 'jrinstances': try: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"GC Collect: {self.MainUtils.run_python_garbage_collector()}") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nombre d'instance LiveWebsock: {self.MainUtils.get_number_gc_objects(LiveConnectionFactory)}") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nombre d'instance ConnectionFactory: {self.MainUtils.get_number_gc_objects(ConnectionFactory)}") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nombre de toute les instances: {self.MainUtils.get_number_gc_objects()}") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"GC Collect: {self.ctx.Utils.run_python_garbage_collector()}") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nombre d'instance LiveWebsock: {self.ctx.Utils.get_number_gc_objects(LiveConnectionFactory)}") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nombre d'instance ConnectionFactory: {self.ctx.Utils.get_number_gc_objects(ConnectionFactory)}") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nombre de toute les instances: {self.ctx.Utils.get_number_gc_objects()}") except Exception as err: - self.Logs.error(f"Unknown Error: {err}") \ No newline at end of file + self.ctx.Logs.error(f"Unknown Error: {err}") \ No newline at end of file diff --git a/mods/jsonrpc/schemas.py b/mods/jsonrpc/schemas.py new file mode 100644 index 0000000..ce969f3 --- /dev/null +++ b/mods/jsonrpc/schemas.py @@ -0,0 +1,5 @@ +from core.definition import MainModel, dataclass + +@dataclass +class ModConfModel(MainModel): + jsonrpc: int = 0 diff --git a/mods/jsonrpc/threads.py b/mods/jsonrpc/threads.py index 1585acd..c28442e 100644 --- a/mods/jsonrpc/threads.py +++ b/mods/jsonrpc/threads.py @@ -4,16 +4,23 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from mods.jsonrpc.mod_jsonrpc import Jsonrpc -def thread_subscribe(uplink: 'Jsonrpc') -> None: +async def thread_subscribe(uplink: 'Jsonrpc') -> None: + + snickname = uplink.ctx.Config.SERVICE_NICKNAME + schannel = uplink.ctx.Config.SERVICE_CHANLOG + if uplink.is_streaming: + await uplink.ctx.Irc.Protocol.send_priv_msg(nick_from=snickname, + msg=f"[{uplink.ctx.Config.COLORS.green}JSONRPC INFO{uplink.ctx.Config.COLORS.nogc}] IRCd Json-rpc already connected!", + channel=schannel + ) + return None - snickname = uplink.Config.SERVICE_NICKNAME - schannel = uplink.Config.SERVICE_CHANLOG uplink.is_streaming = True - response = asyncio.run(uplink.LiveRpc.subscribe(["all"])) + response = await uplink.LiveRpc.subscribe(["all"]) if response.error.code != 0: - uplink.Protocol.send_priv_msg(nick_from=snickname, - msg=f"[{uplink.Config.COLORS.red}JSONRPC ERROR{uplink.Config.COLORS.nogc}] {response.error.message}", + await uplink.ctx.Irc.Protocol.send_priv_msg(nick_from=snickname, + msg=f"[{uplink.ctx.Config.COLORS.red}JSONRPC ERROR{uplink.ctx.Config.COLORS.nogc}] {response.error.message}", channel=schannel ) @@ -21,33 +28,41 @@ def thread_subscribe(uplink: 'Jsonrpc') -> None: message = response.error.message if code == 0: - uplink.Protocol.send_priv_msg( + await uplink.ctx.Irc.Protocol.send_priv_msg( nick_from=snickname, - msg=f"[{uplink.Config.COLORS.green}JSONRPC{uplink.Config.COLORS.nogc}] Stream is OFF", + msg=f"[{uplink.ctx.Config.COLORS.green}JSONRPC{uplink.ctx.Config.COLORS.nogc}] Stream is OFF", channel=schannel ) + uplink.is_streaming = False else: - uplink.Protocol.send_priv_msg( + await uplink.ctx.Irc.Protocol.send_priv_msg( nick_from=snickname, - msg=f"[{uplink.Config.COLORS.red}JSONRPC{uplink.Config.COLORS.nogc}] Stream has crashed! {code} - {message}", + msg=f"[{uplink.ctx.Config.COLORS.red}JSONRPC{uplink.ctx.Config.COLORS.nogc}] Stream has crashed! {code} - {message}", channel=schannel ) + uplink.is_streaming = False -def thread_unsubscribe(uplink: 'Jsonrpc') -> None: +async def thread_unsubscribe(uplink: 'Jsonrpc') -> None: - response = asyncio.run(uplink.LiveRpc.unsubscribe()) - uplink.Logs.debug("[JSONRPC UNLOAD] Unsubscribe from the stream!") + snickname = uplink.ctx.Config.SERVICE_NICKNAME + schannel = uplink.ctx.Config.SERVICE_CHANLOG + + if not uplink.is_streaming: + await uplink.ctx.Irc.Protocol.send_priv_msg(nick_from=snickname, + msg=f"[{uplink.ctx.Config.COLORS.green}JSONRPC INFO{uplink.ctx.Config.COLORS.nogc}] IRCd Json-rpc is already off!", + channel=schannel + ) + return None + + response = await uplink.LiveRpc.unsubscribe() + uplink.ctx.Logs.debug("[JSONRPC UNLOAD] Unsubscribe from the stream!") uplink.is_streaming = False - uplink.update_configuration('jsonrpc', 0) - snickname = uplink.Config.SERVICE_NICKNAME - schannel = uplink.Config.SERVICE_CHANLOG - code = response.error.code message = response.error.message if code != 0: - uplink.Protocol.send_priv_msg( + await uplink.ctx.Irc.Protocol.send_priv_msg( nick_from=snickname, - msg=f"[{uplink.Config.COLORS.red}JSONRPC ERROR{uplink.Config.COLORS.nogc}] {message} ({code})", + msg=f"[{uplink.ctx.Config.COLORS.red}JSONRPC ERROR{uplink.ctx.Config.COLORS.nogc}] {message} ({code})", channel=schannel ) diff --git a/mods/test/mod_test.py b/mods/test/mod_test.py index 4fa746f..f4dd35f 100644 --- a/mods/test/mod_test.py +++ b/mods/test/mod_test.py @@ -1,79 +1,38 @@ -from typing import TYPE_CHECKING -from dataclasses import dataclass, fields +import asyncio +from typing import Any, TYPE_CHECKING, Optional +from core.classes.interfaces.imodule import IModule +from dataclasses import dataclass if TYPE_CHECKING: - from core.irc import Irc + from core.loader import Loader -class Test(): +class Test(IModule): @dataclass class ModConfModel: - """The Model containing the module parameters + """The Model containing the module parameters (Mandatory) + you can leave it without params. + just use pass | if you leave it empty, in the load() method just init empty object ==> self.ModConfig = ModConfModel() """ param_exemple1: str param_exemple2: int - def __init__(self, ircInstance: 'Irc') -> None: + MOD_HEADER: dict[str, str] = { + 'name':'Test', + 'version':'1.0.0', + 'description':'The test module', + 'author':'Defender Team', + 'core_version':'Defender-6' + } + """Module Header (Mandatory)""" - # Module name (Mandatory) - self.module_name = 'mod_' + str(self.__class__.__name__).lower() + def __init__(self, uplink: 'Loader'): + super().__init__(uplink) + self._mod_config: Optional[Test.ModConfModel] = None - # Add Irc Object to the module (Mandatory) - self.Irc = ircInstance - - # Add Loader Object to the module (Mandatory) - self.Loader = ircInstance.Loader - - # Add server protocol Object to the module (Mandatory) - self.Protocol = ircInstance.Protocol - - # Add Global Configuration to the module (Mandatory) - self.Config = ircInstance.Config - - # Add Base object to the module (Mandatory) - self.Base = ircInstance.Base - - # Add logs object to the module (Mandatory) - self.Logs = ircInstance.Loader.Logs - - # Add User object to the module (Mandatory) - self.User = ircInstance.User - - # Add Channel object to the module (Mandatory) - self.Channel = ircInstance.Channel - - # Add Reputation object to the module (Optional) - self.Reputation = ircInstance.Reputation - - # Create module commands (Mandatory) - self.Irc.build_command(0, self.module_name, 'test-command', 'Execute a test command') - self.Irc.build_command(1, self.module_name, 'test_level_1', 'Execute a level 1 test command') - self.Irc.build_command(2, self.module_name, 'test_level_2', 'Execute a level 2 test command') - self.Irc.build_command(3, self.module_name, 'test_level_3', 'Execute a level 3 test command') - - - # Init the module - self.__init_module() - - # Log the module - self.Logs.debug(f'Module {self.module_name} loaded ...') - - def __init_module(self) -> None: - - # Create you own tables (Mandatory) - self.__create_tables() - - # Load module configuration and sync with core one (Mandatory) - self.__load_module_configuration() - # End of mandatory methods you can start your customization # - - return None - - def __create_tables(self) -> None: + def create_tables(self) -> None: """Methode qui va créer la base de donnée si elle n'existe pas. Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module - Args: - database_name (str): Nom de la base de données ( pas d'espace dans le nom ) Returns: None: Aucun retour n'es attendu @@ -86,72 +45,97 @@ class Test(): ) ''' - self.Base.db_execute_query(table_logs) + # self.ctx.Base.db_execute_query(table_logs) return None - def __load_module_configuration(self) -> None: - """### Load Module Configuration + async def load(self) -> None: + """### Load Module Configuration (Mandatory) """ - try: - # Build the default configuration model (Mandatory) - self.ModConfig = self.ModConfModel(param_exemple1='param value 1', param_exemple2=1) - # Sync the configuration with core configuration (Mandatory) - self.Base.db_sync_core_config(self.module_name, self.ModConfig) + # Create module commands (Mandatory) + self.ctx.Irc.build_command(0, self.module_name, 'test-command', 'Execute a test command') + self.ctx.Irc.build_command(0, self.module_name, 'asyncio', 'Create a new asynchron task!') + self.ctx.Irc.build_command(1, self.module_name, 'test_level_1', 'Execute a level 1 test command') + self.ctx.Irc.build_command(2, self.module_name, 'test_level_2', 'Execute a level 2 test command') + self.ctx.Irc.build_command(3, self.module_name, 'test_level_3', 'Execute a level 3 test command') - return None + # Build the default configuration model (Mandatory) + self._mod_config = self.ModConfModel(param_exemple1='str', param_exemple2=1) - except TypeError as te: - self.Logs.critical(te) + # sync the database with local variable (Mandatory) + await self.sync_db() - def __update_configuration(self, param_key: str, param_value: str): - """Update the local and core configuration + if self.mod_config.param_exemple2 == 1: + await self.ctx.Irc.Protocol.send_priv_msg(self.ctx.Config.SERVICE_NICKNAME, "Param activated", self.ctx.Config.SERVICE_CHANLOG) - Args: - param_key (str): The parameter key - param_value (str): The parameter value - """ - self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value) + @property + def mod_config(self) -> ModConfModel: + return self._mod_config def unload(self) -> None: - self.Irc.Commands.drop_command_by_module(self.module_name) + """### This method is called when you unload, or you reload the module (Mandatory)""" + self.ctx.Commands.drop_command_by_module(self.module_name) return None - def cmd(self, data:list) -> None: + def cmd(self, data: list[str]) -> None: + """All messages coming from the IRCD server will be handled using this method (Mandatory) + + Args: + data (list): Messages coming from the IRCD server. + """ + cmd = list(data).copy() try: - cmd = list(data).copy() - return None - except KeyError as ke: - self.Logs.error(f"Key Error: {ke}") - except IndexError as ie: - self.Logs.error(f"{ie} / {cmd} / length {str(len(cmd))}") except Exception as err: - self.Logs.error(f"General Error: {err}") + self.ctx.Logs.error(f"General Error: {err}") - def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None: + async def asyncio_func(self) -> None: + self.ctx.Logs.debug(f"Starting async method in a task: {self.__class__.__name__}") + await asyncio.sleep(2) + self.ctx.Logs.debug(f"End of the task: {self.__class__.__name__}") + + async def hcmds(self, user: str, channel: Any, cmd: list, fullcmd: Optional[list] = None) -> None: + """All messages coming from the user commands (Mandatory) + + Args: + user (str): The user who send the request. + channel (Any): The channel from where is coming the message (could be None). + cmd (list): The messages coming from the IRCD server. + fullcmd (list, optional): The full messages coming from the IRCD server. Defaults to []. + """ + u = self.ctx.User.get_user(user) + c = self.ctx.Channel.get_channel(channel) if self.ctx.Channel.is_valid_channel(channel) else None + if u is None: + return None command = str(cmd[0]).lower() - dnickname = self.Config.SERVICE_NICKNAME - fromuser = user - fromchannel = str(channel) if not channel is None else None + dnickname = self.ctx.Config.SERVICE_NICKNAME match command: + + case 'asyncio': + self.ctx.Base.create_asynctask(self.asyncio_func()) + return None case 'test-command': try: + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=u.nickname, msg="This is a notice to the sender ...") + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"This is private message to the sender ...", nick_to=u.nickname) - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="This is a notice to the sender ...") - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"This is private message to the sender ...", nick_to=fromuser) - - if not fromchannel is None: - self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"This is private message to the sender ...", channel=fromchannel) + if c is not None: + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"This is private message to the sender ...", channel=c.name) # How to update your module configuration - self.__update_configuration('param_exemple2', 7) + self.update_configuration('param_exemple2', 7) + self.update_configuration('param_exemple1', 'my_value') # Log if you want the result - self.Logs.debug(f"Test logs ready") + self.ctx.Logs.debug(f"Test logs ready") + return None except Exception as err: - self.Logs.error(f"Unknown Error: {err}") \ No newline at end of file + self.ctx.Logs.error(f"Unknown Error: {err}") + return None + + case _: + return None \ No newline at end of file diff --git a/mods/votekick/mod_votekick.py b/mods/votekick/mod_votekick.py index 3da9f5b..86aa308 100644 --- a/mods/votekick/mod_votekick.py +++ b/mods/votekick/mod_votekick.py @@ -1,95 +1,49 @@ """ File : mod_votekick.py - Version : 1.0.0 + Version : 1.0.2 Description : Manages votekick sessions for multiple channels. Handles activation, ongoing vote checks, and cleanup. Author : adator Created : 2025-08-16 - Last Updated: 2025-08-16 + Last Updated: 2025-11-01 ----------------------------------------- """ +from dataclasses import dataclass import re +from core.classes.interfaces.imodule import IModule import mods.votekick.schemas as schemas import mods.votekick.utils as utils from mods.votekick.votekick_manager import VotekickManager import mods.votekick.threads as thds from typing import TYPE_CHECKING, Any, Optional + if TYPE_CHECKING: - from core.irc import Irc + from core.loader import Loader +class Votekick(IModule): -class Votekick: + @dataclass + class ModConfModel(schemas.VoteChannelModel): + ... - def __init__(self, uplink: 'Irc') -> None: + MOD_HEADER: dict[str, str] = { + 'name':'votekick', + 'version':'1.0.2', + 'description':'Channel Democraty', + 'author':'Defender Team', + 'core_version':'Defender-6' + } - # Module name (Mandatory) - self.module_name = 'mod_' + str(self.__class__.__name__).lower() + def __init__(self, context: 'Loader') -> None: + super().__init__(context) + self._mod_config: Optional[schemas.VoteChannelModel] = None - # Add Irc Object to the module - self.Irc = uplink + @property + def mod_config(self) -> ModConfModel: + return self._mod_config - # Add Loader Object to the module (Mandatory) - self.Loader = uplink.Loader - - # Add server protocol Object to the module (Mandatory) - self.Protocol = uplink.Protocol - - # Add Global Configuration to the module - self.Config = uplink.Config - - # Add Base object to the module - self.Base = uplink.Base - - # Add logs object to the module - self.Logs = uplink.Logs - - # Add User object to the module - self.User = uplink.User - - # Add Channel object to the module - self.Channel = uplink.Channel - - # Add Utils. - self.Utils = uplink.Utils - - # Add Utils module - self.ModUtils = utils - - # Add Schemas module - self.Schemas = schemas - - # Add Threads module - self.Threads = thds - - # Add VoteKick Manager - self.VoteKickManager = VotekickManager(self) - - metadata = uplink.Loader.Settings.get_cache('VOTEKICK') - - if metadata is not None: - self.VoteKickManager.VOTE_CHANNEL_DB = metadata - # self.VOTE_CHANNEL_DB = metadata - - # Créer les nouvelles commandes du module - self.Irc.build_command(1, self.module_name, 'vote', 'The kick vote module') - - # Init the module - self.__init_module() - - # Log the module - self.Logs.debug(f'-- Module {self.module_name} loaded ...') - - def __init_module(self) -> None: - - # Add admin object to retrieve admin users - self.Admin = self.Irc.Admin - self.__create_tables() - self.ModUtils.join_saved_channels(self) - - return None - - def __create_tables(self) -> None: + def create_tables(self) -> None: """Methode qui va créer la base de donnée si elle n'existe pas. Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module @@ -111,30 +65,52 @@ class Votekick: ) ''' - self.Base.db_execute_query(table_logs) - self.Base.db_execute_query(table_vote) + self.ctx.Base.db_execute_query(table_logs) + self.ctx.Base.db_execute_query(table_vote) return None - def unload(self) -> None: + async def load(self) -> None: + + self._mod_config = self.ModConfModel() + await self.sync_db() + + # Add VoteKick Manager + self.VoteKickManager = VotekickManager(self) + + # Add Threads module + self.Threads = thds + + await utils.join_saved_channels(self) + + metadata = self.ctx.Settings.get_cache('VOTEKICK') + + if metadata is not None: + self.VoteKickManager.VOTE_CHANNEL_DB = metadata + + # Créer les nouvelles commandes du module + self.ctx.Irc.build_command(1, self.module_name, 'vote', 'The kick vote module') + + async def unload(self) -> None: try: # Cache the local DB with current votes. - self.Loader.Settings.set_cache('VOTEKICK', self.VoteKickManager.VOTE_CHANNEL_DB) + if self.VoteKickManager.VOTE_CHANNEL_DB: + self.ctx.Settings.set_cache('VOTEKICK', self.VoteKickManager.VOTE_CHANNEL_DB) for chan in self.VoteKickManager.VOTE_CHANNEL_DB: - self.Protocol.send_part_chan(uidornickname=self.Config.SERVICE_ID, channel=chan.channel_name) + await self.ctx.Irc.Protocol.send_part_chan(uidornickname=self.ctx.Config.SERVICE_ID, channel=chan.channel_name) self.VoteKickManager.VOTE_CHANNEL_DB = [] - self.Logs.debug(f'Delete memory DB VOTE_CHANNEL_DB: {self.VoteKickManager.VOTE_CHANNEL_DB}') + self.ctx.Logs.debug(f'Delete memory DB VOTE_CHANNEL_DB: {self.VoteKickManager.VOTE_CHANNEL_DB}') - self.Irc.Commands.drop_command_by_module(self.module_name) + self.ctx.Commands.drop_command_by_module(self.module_name) return None except UnboundLocalError as ne: - self.Logs.error(f'{ne}') + self.ctx.Logs.error(f'{ne}') except NameError as ue: - self.Logs.error(f'{ue}') + self.ctx.Logs.error(f'{ue}') except Exception as err: - self.Logs.error(f'General Error: {err}') + self.ctx.Logs.error(f'General Error: {err}') def cmd(self, data: list) -> None: @@ -142,7 +118,7 @@ class Votekick: return None cmd = data.copy() if isinstance(data, list) else list(data).copy() - index, command = self.Irc.Protocol.get_ircd_protocol_poisition(cmd) + index, command = self.ctx.Irc.Protocol.get_ircd_protocol_poisition(cmd) if index == -1: return None @@ -160,19 +136,19 @@ class Votekick: return None except KeyError as ke: - self.Logs.error(f"Key Error: {ke}") + self.ctx.Logs.error(f"Key Error: {ke}") except IndexError as ie: - self.Logs.error(f"{ie} / {cmd} / length {str(len(cmd))}") + self.ctx.Logs.error(f"{ie} / {cmd} / length {str(len(cmd))}") except Exception as err: - self.Logs.error(f"General Error: {err}") + self.ctx.Logs.error(f"General Error: {err}") - def hcmds(self, user:str, channel: Any, cmd: list, fullcmd: Optional[list] = None) -> None: + async def hcmds(self, user:str, channel: Any, cmd: list, fullcmd: Optional[list] = None) -> None: # cmd is the command starting from the user command # full cmd is sending the entire server response command = str(cmd[0]).lower() fullcmd = fullcmd - dnickname = self.Config.SERVICE_NICKNAME + dnickname = self.ctx.Config.SERVICE_NICKNAME fromuser = user fromchannel = channel @@ -181,14 +157,14 @@ class Votekick: case 'vote': if len(cmd) == 1: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote activate #channel') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote deactivate #channel') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote +') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote -') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote cancel') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote status') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote submit nickname') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote verdict') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote activate #channel') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote deactivate #channel') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote +') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote -') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote cancel') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote status') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote submit nickname') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote verdict') return None option = str(cmd[1]).lower() @@ -198,19 +174,19 @@ class Votekick: case 'activate': try: # vote activate #channel - if self.Admin.get_admin(fromuser) is None: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' :Your are not allowed to execute this command') + if self.ctx.Admin.get_admin(fromuser) is None: + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' :Your are not allowed to execute this command') return None - sentchannel = str(cmd[2]).lower() if self.Channel.is_valid_channel(str(cmd[2]).lower()) else None + sentchannel = str(cmd[2]).lower() if self.ctx.Channel.is_valid_channel(str(cmd[2]).lower()) else None if sentchannel is None: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" The correct command is {self.Config.SERVICE_PREFIX}{command} {option} #CHANNEL") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" The correct command is {self.ctx.Config.SERVICE_PREFIX}{command} {option} #CHANNEL") if self.VoteKickManager.activate_new_channel(sentchannel): - self.Channel.db_query_channel('add', self.module_name, sentchannel) - self.Protocol.send_join_chan(uidornickname=dnickname, channel=sentchannel) - self.Protocol.send2socket(f":{dnickname} SAMODE {sentchannel} +o {dnickname}") - self.Protocol.send_priv_msg(nick_from=dnickname, + await self.ctx.Channel.db_query_channel('add', self.module_name, sentchannel) + await self.ctx.Irc.Protocol.send_join_chan(uidornickname=dnickname, channel=sentchannel) + await self.ctx.Irc.Protocol.send2socket(f":{dnickname} SAMODE {sentchannel} +o {dnickname}") + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg="You can now use !submit to decide if he will stay or not on this channel ", channel=sentchannel ) @@ -218,117 +194,117 @@ class Votekick: return None except Exception as err: - self.Logs.error(f'{err}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option} #channel') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option} #welcome') + self.ctx.Logs.error(f'{err}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option} #channel') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option} #welcome') case 'deactivate': try: # vote deactivate #channel - if self.Admin.get_admin(fromuser) is None: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" Your are not allowed to execute this command") + if self.ctx.Admin.get_admin(fromuser) is None: + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" Your are not allowed to execute this command") return None - sentchannel = str(cmd[2]).lower() if self.Channel.is_valid_channel(str(cmd[2]).lower()) else None + sentchannel = str(cmd[2]).lower() if self.ctx.Channel.is_valid_channel(str(cmd[2]).lower()) else None if sentchannel is None: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" The correct command is {self.Config.SERVICE_PREFIX}{command} {option} #CHANNEL") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" The correct command is {self.ctx.Config.SERVICE_PREFIX}{command} {option} #CHANNEL") - self.Protocol.send2socket(f":{dnickname} SAMODE {sentchannel} -o {dnickname}") - self.Protocol.send_part_chan(uidornickname=dnickname, channel=sentchannel) + await self.ctx.Irc.Protocol.send2socket(f":{dnickname} SAMODE {sentchannel} -o {dnickname}") + await self.ctx.Irc.Protocol.send_part_chan(uidornickname=dnickname, channel=sentchannel) if self.VoteKickManager.drop_vote_channel_model(sentchannel): - self.Channel.db_query_channel('del', self.module_name, sentchannel) + await self.ctx.Channel.db_query_channel('del', self.module_name, sentchannel) return None except Exception as err: - self.Logs.error(f'{err}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" /msg {dnickname} {command} {option} #channel") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" Exemple /msg {dnickname} {command} {option} #welcome") + self.ctx.Logs.error(f'{err}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" /msg {dnickname} {command} {option} #channel") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" Exemple /msg {dnickname} {command} {option} #welcome") case '+': try: # vote + channel = fromchannel if self.VoteKickManager.action_vote(channel, fromuser, '+'): - self.Protocol.send_priv_msg(nick_from=dnickname, msg="Vote recorded, thank you",channel=channel) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg="Vote recorded, thank you",channel=channel) else: - self.Protocol.send_priv_msg(nick_from=dnickname, msg="You already submitted a vote", channel=channel) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg="You already submitted a vote", channel=channel) except Exception as err: - self.Logs.error(f'{err}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}') + self.ctx.Logs.error(f'{err}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}') case '-': try: # vote - channel = fromchannel if self.VoteKickManager.action_vote(channel, fromuser, '-'): - self.Protocol.send_priv_msg(nick_from=dnickname, msg="Vote recorded, thank you",channel=channel) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg="Vote recorded, thank you",channel=channel) else: - self.Protocol.send_priv_msg(nick_from=dnickname, msg="You already submitted a vote", channel=channel) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg="You already submitted a vote", channel=channel) except Exception as err: - self.Logs.error(f'{err}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}') + self.ctx.Logs.error(f'{err}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}') case 'cancel': try: # vote cancel - if self.Admin.get_admin(fromuser) is None: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Your are not allowed to execute this command') + if self.ctx.Admin.get_admin(fromuser) is None: + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Your are not allowed to execute this command') return None if channel is None: - self.Logs.error(f"The channel is not known, defender can't cancel the vote") - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' You need to specify the channel => /msg {dnickname} vote_cancel #channel') + self.ctx.Logs.error(f"The channel is not known, defender can't cancel the vote") + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' You need to specify the channel => /msg {dnickname} vote_cancel #channel') for vote in self.VoteKickManager.VOTE_CHANNEL_DB: if vote.channel_name == channel: if self.VoteKickManager.init_vote_system(channel): - self.Protocol.send_priv_msg(nick_from=dnickname, + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg="Vote system re-initiated", channel=channel ) except Exception as err: - self.Logs.error(f'{err}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}') + self.ctx.Logs.error(f'{err}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}') case 'status': try: # vote status for chan in self.VoteKickManager.VOTE_CHANNEL_DB: if chan.channel_name == channel: - self.Protocol.send_priv_msg(nick_from=dnickname, - msg=f"Channel: {chan.channel_name} | Target: {self.User.get_nickname(chan.target_user)} | For: {chan.vote_for} | Against: {chan.vote_against} | Number of voters: {str(len(chan.voter_users))}", + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, + msg=f"Channel: {chan.channel_name} | Target: {self.ctx.User.get_nickname(chan.target_user)} | For: {chan.vote_for} | Against: {chan.vote_against} | Number of voters: {str(len(chan.voter_users))}", channel=channel ) except Exception as err: - self.Logs.error(f'{err}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}') + self.ctx.Logs.error(f'{err}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}') case 'submit': try: # vote submit nickname - if self.Admin.get_admin(fromuser) is None: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Your are not allowed to execute this command') + if self.ctx.Admin.get_admin(fromuser) is None: + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Your are not allowed to execute this command') return None nickname_submitted = cmd[2] - uid_submitted = self.User.get_uid(nickname_submitted) - user_submitted = self.User.get_user(nickname_submitted) + uid_submitted = self.ctx.User.get_uid(nickname_submitted) + user_submitted = self.ctx.User.get_user(nickname_submitted) ongoing_user = None # check if there is an ongoing vote if self.VoteKickManager.is_vote_ongoing(channel): votec = self.VoteKickManager.get_vote_channel_model(channel) if votec: - ongoing_user = self.User.get_nickname(votec.target_user) - self.Protocol.send_priv_msg(nick_from=dnickname, + ongoing_user = self.ctx.User.get_nickname(votec.target_user) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"There is an ongoing vote on {ongoing_user}", channel=channel ) @@ -336,24 +312,24 @@ class Votekick: # check if the user exist if user_submitted is None: - self.Protocol.send_priv_msg(nick_from=dnickname, + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"This nickname <{nickname_submitted}> do not exist", channel=channel ) return None - uid_cleaned = self.Loader.Utils.clean_uid(uid_submitted) - channel_obj = self.Channel.get_channel(channel) + uid_cleaned = self.ctx.Utils.clean_uid(uid_submitted) + channel_obj = self.ctx.Channel.get_channel(channel) if channel_obj is None: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' This channel [{channel}] do not exist in the Channel Object') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' This channel [{channel}] do not exist in the Channel Object') return None clean_uids_in_channel: list = [] for uid in channel_obj.uids: - clean_uids_in_channel.append(self.Loader.Utils.clean_uid(uid)) + clean_uids_in_channel.append(self.ctx.Utils.clean_uid(uid)) if not uid_cleaned in clean_uids_in_channel: - self.Protocol.send_priv_msg(nick_from=dnickname, + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"This nickname <{nickname_submitted}> is not available in this channel", channel=channel ) @@ -363,7 +339,7 @@ class Votekick: pattern = fr'[o|B|S]' operator_user = re.findall(pattern, user_submitted.umodes) if operator_user: - self.Protocol.send_priv_msg(nick_from=dnickname, + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg="You cant vote for this user ! he/she is protected", channel=channel ) @@ -371,49 +347,49 @@ class Votekick: for chan in self.VoteKickManager.VOTE_CHANNEL_DB: if chan.channel_name == channel: - chan.target_user = self.User.get_uid(nickname_submitted) + chan.target_user = self.ctx.User.get_uid(nickname_submitted) - self.Protocol.send_priv_msg(nick_from=dnickname, + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"{nickname_submitted} has been targeted for a vote", channel=channel ) - self.Base.create_timer(60, self.Threads.timer_vote_verdict, (self, channel)) - self.Protocol.send_priv_msg(nick_from=dnickname, + self.ctx.Base.create_asynctask(thds.timer_vote_verdict(self, channel)) + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg="This vote will end after 60 secondes", channel=channel ) except Exception as err: - self.Logs.error(f'{err}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option} nickname') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option} adator') + self.ctx.Logs.error(f'{err}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option} nickname') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option} adator') case 'verdict': try: # vote verdict - if self.Admin.get_admin(fromuser) is None: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f'Your are not allowed to execute this command') + if self.ctx.Admin.get_admin(fromuser) is None: + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f'Your are not allowed to execute this command') return None votec = self.VoteKickManager.get_vote_channel_model(channel) if votec: - target_user = self.User.get_nickname(votec.target_user) + target_user = self.ctx.User.get_nickname(votec.target_user) if votec.vote_for >= votec.vote_against: - self.Protocol.send_priv_msg(nick_from=dnickname, - msg=f"User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {votec.vote_against} votes against and {votec.vote_for} votes for. For this reason, it\'ll be kicked from the channel", + await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, + msg=f"User {self.ctx.Config.COLORS.bold}{target_user}{self.ctx.Config.COLORS.nogc} has {votec.vote_against} votes against and {votec.vote_for} votes for. For this reason, it\'ll be kicked from the channel", channel=channel ) - self.Protocol.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}") + await self.ctx.Irc.Protocol.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}") else: - self.Protocol.send_priv_msg( + await self.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, - msg=f"User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {votec.vote_against} votes against and {votec.vote_for} votes for. For this reason, it\'ll remain in the channel", + msg=f"User {self.ctx.Config.COLORS.bold}{target_user}{self.ctx.Config.COLORS.nogc} has {votec.vote_against} votes against and {votec.vote_for} votes for. For this reason, it\'ll remain in the channel", channel=channel ) if self.VoteKickManager.init_vote_system(channel): - self.Protocol.send_priv_msg( + await self.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, msg="System vote re initiated", channel=channel @@ -421,19 +397,19 @@ class Votekick: return None except Exception as err: - self.Logs.error(f'{err}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}') + self.ctx.Logs.error(f'{err}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}') case _: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote activate #channel') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote deactivate #channel') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote +') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote -') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote cancel') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote status') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote submit nickname') - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote verdict') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote activate #channel') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote deactivate #channel') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote +') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote -') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote cancel') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote status') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote submit nickname') + await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote verdict') return None case _: diff --git a/mods/votekick/threads.py b/mods/votekick/threads.py index 66d4edc..14eb209 100644 --- a/mods/votekick/threads.py +++ b/mods/votekick/threads.py @@ -1,35 +1,38 @@ +import asyncio from typing import TYPE_CHECKING if TYPE_CHECKING: from mods.votekick.mod_votekick import Votekick -def timer_vote_verdict(uplink: 'Votekick', channel: str) -> None: +async def timer_vote_verdict(uplink: 'Votekick', channel: str) -> None: - dnickname = uplink.Config.SERVICE_NICKNAME + dnickname = uplink.ctx.Config.SERVICE_NICKNAME if not uplink.VoteKickManager.is_vote_ongoing(channel): return None - + + asyncio.sleep(60) + votec = uplink.VoteKickManager.get_vote_channel_model(channel) if votec: - target_user = uplink.User.get_nickname(votec.target_user) + target_user = uplink.ctx.User.get_nickname(votec.target_user) if votec.vote_for >= votec.vote_against and votec.vote_for != 0: - uplink.Protocol.send_priv_msg(nick_from=dnickname, - msg=f"User {uplink.Config.COLORS.bold}{target_user}{uplink.Config.COLORS.nogc} has {votec.vote_against} votes against and {votec.vote_for} votes for. For this reason, it\'ll be kicked from the channel", + await uplink.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, + msg=f"User {uplink.ctx.Config.COLORS.bold}{target_user}{uplink.ctx.Config.COLORS.nogc} has {votec.vote_against} votes against and {votec.vote_for} votes for. For this reason, it\'ll be kicked from the channel", channel=channel ) - uplink.Protocol.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}") + await uplink.ctx.Irc.Protocol.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}") else: - uplink.Protocol.send_priv_msg( + await uplink.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, - msg=f"User {uplink.Config.COLORS.bold}{target_user}{uplink.Config.COLORS.nogc} has {votec.vote_against} votes against and {votec.vote_for} votes for. For this reason, it\'ll remain in the channel", + msg=f"User {uplink.ctx.Config.COLORS.bold}{target_user}{uplink.ctx.Config.COLORS.nogc} has {votec.vote_against} votes against and {votec.vote_for} votes for. For this reason, it\'ll remain in the channel", channel=channel ) if uplink.VoteKickManager.init_vote_system(channel): - uplink.Protocol.send_priv_msg( + await uplink.ctx.Irc.Protocol.send_priv_msg( nick_from=dnickname, msg="System vote re initiated", channel=channel diff --git a/mods/votekick/utils.py b/mods/votekick/utils.py index 7f28bc9..0361ea2 100644 --- a/mods/votekick/utils.py +++ b/mods/votekick/utils.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from mods.votekick.mod_votekick import Votekick -def add_vote_channel_to_database(uplink: 'Votekick', channel: str) -> bool: +async def add_vote_channel_to_database(uplink: 'Votekick', channel: str) -> bool: """Adds a new channel to the votekick database if it doesn't already exist. This function checks if the specified channel is already registered in the @@ -18,16 +18,16 @@ def add_vote_channel_to_database(uplink: 'Votekick', channel: str) -> bool: bool: True if the channel was successfully inserted into the database. False if the channel already exists or the insertion failed. """ - current_datetime = uplink.Utils.get_sdatetime() + current_datetime = uplink.ctx.Utils.get_sdatetime() mes_donnees = {'channel': channel} - response = uplink.Base.db_execute_query("SELECT id FROM votekick_channel WHERE channel = :channel", mes_donnees) + response = await uplink.ctx.Base.db_execute_query("SELECT id FROM votekick_channel WHERE channel = :channel", mes_donnees) is_channel_exist = response.fetchone() if is_channel_exist is None: mes_donnees = {'datetime': current_datetime, 'channel': channel} - insert = uplink.Base.db_execute_query(f"INSERT INTO votekick_channel (datetime, channel) VALUES (:datetime, :channel)", mes_donnees) + insert = await uplink.ctx.Base.db_execute_query(f"INSERT INTO votekick_channel (datetime, channel) VALUES (:datetime, :channel)", mes_donnees) if insert.rowcount > 0: return True else: @@ -35,7 +35,7 @@ def add_vote_channel_to_database(uplink: 'Votekick', channel: str) -> bool: else: return False -def delete_vote_channel_from_database(uplink: 'Votekick', channel: str) -> bool: +async def delete_vote_channel_from_database(uplink: 'Votekick', channel: str) -> bool: """Deletes a channel entry from the votekick database. This function removes the specified channel from the `votekick_channel` table @@ -49,7 +49,7 @@ def delete_vote_channel_from_database(uplink: 'Votekick', channel: str) -> bool: bool: True if the channel was successfully deleted, False if no rows were affected. """ mes_donnes = {'channel': channel} - response = uplink.Base.db_execute_query("DELETE FROM votekick_channel WHERE channel = :channel", mes_donnes) + response = await uplink.ctx.Base.db_execute_query("DELETE FROM votekick_channel WHERE channel = :channel", mes_donnes) affected_row = response.rowcount @@ -58,17 +58,17 @@ def delete_vote_channel_from_database(uplink: 'Votekick', channel: str) -> bool: else: return False -def join_saved_channels(uplink: 'Votekick') -> None: +async def join_saved_channels(uplink: 'Votekick') -> None: param = {'module_name': uplink.module_name} - result = uplink.Base.db_execute_query(f"SELECT id, channel_name FROM {uplink.Config.TABLE_CHANNEL} WHERE module_name = :module_name", param) + result = await uplink.ctx.Base.db_execute_query(f"SELECT id, channel_name FROM {uplink.ctx.Config.TABLE_CHANNEL} WHERE module_name = :module_name", param) channels = result.fetchall() for channel in channels: id_, chan = channel uplink.VoteKickManager.activate_new_channel(chan) - uplink.Protocol.send_sjoin(channel=chan) - uplink.Protocol.send2socket(f":{uplink.Config.SERVICE_NICKNAME} SAMODE {chan} +o {uplink.Config.SERVICE_NICKNAME}") + await uplink.ctx.Irc.Protocol.send_sjoin(channel=chan) + await uplink.ctx.Irc.Protocol.send2socket(f":{uplink.ctx.Config.SERVICE_NICKNAME} SAMODE {chan} +o {uplink.ctx.Config.SERVICE_NICKNAME}") return None \ No newline at end of file diff --git a/mods/votekick/votekick_manager.py b/mods/votekick/votekick_manager.py index d61fed0..b5dea33 100644 --- a/mods/votekick/votekick_manager.py +++ b/mods/votekick/votekick_manager.py @@ -10,8 +10,8 @@ class VotekickManager: def __init__(self, uplink: 'Votekick'): self.uplink = uplink - self.Logs = uplink.Logs - self.Utils = uplink.Utils + self.Logs = uplink.ctx.Logs + self.Utils = uplink.ctx.Utils def activate_new_channel(self, channel_name: str) -> bool: """Activate a new channel in the votekick systeme diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c0408c3 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +Faker==37.12.0 +psutil==7.1.2 +PyYAML==6.0.3 +requests==2.32.5 +SQLAlchemy==2.0.44 +unrealircd_rpc_py==3.0.2 +starlette==0.50.0 +uvicorn==0.38.0 \ No newline at end of file diff --git a/version.json b/version.json index b0b4b2f..7583b06 100644 --- a/version.json +++ b/version.json @@ -1,10 +1,12 @@ { - "version": "6.3.2", + "version": "6.4.0", - "requests": "2.32.3", - "psutil": "6.0.0", - "unrealircd_rpc_py": "3.0.1", - "sqlalchemy": "2.0.35", - "faker": "30.1.0", - "pyyaml": "6.0.2" + "requests": "2.32.5", + "psutil": "7.1.2", + "unrealircd_rpc_py": "3.0.3", + "sqlalchemy": "2.0.44", + "faker": "37.12.0", + "pyyaml": "6.0.3", + "starlette":"0.50.0", + "uvicorn":"0.38.0" }