Merge pull request #82 from adator85/v6.2.0

V6.2.0
This commit is contained in:
adator
2025-08-21 01:00:12 +02:00
committed by GitHub
42 changed files with 3630 additions and 2882 deletions

2
.gitignore vendored
View File

@@ -1,4 +1,6 @@
.pyenv/ .pyenv/
.venv/
.idea/
db/ db/
logs/ logs/
__pycache__/ __pycache__/

View File

@@ -1,6 +1,8 @@
import importlib
import os import os
import re import re
import json import json
import sys
import time import time
import random import random
import socket import socket
@@ -8,41 +10,44 @@ import hashlib
import logging import logging
import threading import threading
import ipaddress import ipaddress
import ast import ast
import requests import requests
from pathlib import Path
from types import ModuleType
from dataclasses import fields from dataclasses import fields
from typing import Union, Literal, TYPE_CHECKING from typing import Any, Optional, TYPE_CHECKING
from base64 import b64decode, b64encode from base64 import b64decode, b64encode
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from sqlalchemy import create_engine, Engine, Connection, CursorResult from sqlalchemy import create_engine, Engine, Connection, CursorResult
from sqlalchemy.sql import text from sqlalchemy.sql import text
from core.definition import MConfig
if TYPE_CHECKING: if TYPE_CHECKING:
from core.classes.settings import Settings from core.loader import Loader
class Base: class Base:
def __init__(self, Config: MConfig, settings: 'Settings') -> None: def __init__(self, loader: 'Loader') -> None:
self.Config = Config # Assigner l'objet de configuration self.Loader = loader
self.Settings: Settings = settings self.Config = loader.Config
self.init_log_system() # Demarrer le systeme de log self.Settings = loader.Settings
self.Utils = loader.Utils
self.logs = loader.Logs
# self.init_log_system() # Demarrer le systeme de log
self.check_for_new_version(True) # Verifier si une nouvelle version est disponible self.check_for_new_version(True) # Verifier si une nouvelle version est disponible
# Liste des timers en cours # Liste des timers en cours
self.running_timers:list[threading.Timer] = self.Settings.RUNNING_TIMERS self.running_timers: list[threading.Timer] = self.Settings.RUNNING_TIMERS
# Liste des threads en cours # Liste des threads en cours
self.running_threads:list[threading.Thread] = self.Settings.RUNNING_THREADS self.running_threads: list[threading.Thread] = self.Settings.RUNNING_THREADS
# Les sockets ouvert # Les sockets ouvert
self.running_sockets: list[socket.socket] = self.Settings.RUNNING_SOCKETS self.running_sockets: list[socket.socket] = self.Settings.RUNNING_SOCKETS
# Liste des fonctions en attentes # Liste des fonctions en attentes
self.periodic_func:dict[object] = self.Settings.PERIODIC_FUNC self.periodic_func: dict[object] = self.Settings.PERIODIC_FUNC
# Création du lock # Création du lock
self.lock = self.Settings.LOCK self.lock = self.Settings.LOCK
@@ -136,120 +141,62 @@ class Base:
except Exception as err: except Exception as err:
self.logs.error(f'General Error: {err}') self.logs.error(f'General Error: {err}')
def get_unixtime(self) -> int: def get_all_modules(self) -> list[str]:
"""Get list of all main modules
using this pattern mod_*.py
Returns:
list[str]: List of module names.
""" """
Cette fonction retourne un UNIXTIME de type 12365456 base_path = Path('mods')
Return: Current time in seconds since the Epoch (int) return [file.name.replace('.py', '') for file in base_path.rglob('mod_*.py')]
def reload_modules_with_dependencies(self, prefix: str = 'mods'):
""" """
cet_offset = timezone(timedelta(hours=2)) Reload all modules in sys.modules that start with the given prefix.
now_cet = datetime.now(cet_offset) Useful for reloading a full package during development.
unixtime_cet = int(now_cet.timestamp())
unixtime = int( time.time() )
return unixtime
def get_datetime(self) -> str:
""" """
Retourne une date au format string (24-12-2023 20:50:59) modules_to_reload = []
"""
currentdate = datetime.now().strftime('%d-%m-%Y %H:%M:%S')
return currentdate
def get_all_modules(self) -> list: # Collect target modules
for name, module in sys.modules.items():
if (
isinstance(module, ModuleType)
and module is not None
and name.startswith(prefix)
):
modules_to_reload.append((name, module))
all_files = os.listdir('mods/') # Sort to reload submodules before parent modules
all_modules: list = [] for name, module in sorted(modules_to_reload, key=lambda x: x[0], reverse=True):
for module in all_files: try:
if module.endswith('.py') and not module == '__init__.py': if 'mod_' not in name and 'schemas' not in name:
all_modules.append(module.replace('.py', '').lower()) importlib.reload(module)
self.logs.debug(f'[LOAD_MODULE] Module {module} success')
return all_modules except Exception as err:
self.logs.error(f'[LOAD_MODULE] Module {module} failed [!] - {err}')
def create_log(self, log_message: str) -> None: def create_log(self, log_message: str) -> None:
"""Enregiste les logs """Enregiste les logs
Args: Args:
string (str): Le message a enregistrer log_message (str): Le message a enregistrer
Returns: Returns:
None: Aucun retour None: Aucun retour
""" """
sql_insert = f"INSERT INTO {self.Config.TABLE_LOG} (datetime, server_msg) VALUES (:datetime, :server_msg)" sql_insert = f"INSERT INTO {self.Config.TABLE_LOG} (datetime, server_msg) VALUES (:datetime, :server_msg)"
mes_donnees = {'datetime': str(self.get_datetime()),'server_msg': f'{log_message}'} mes_donnees = {'datetime': str(self.Utils.get_sdatetime()),'server_msg': f'{log_message}'}
self.db_execute_query(sql_insert, mes_donnees) self.db_execute_query(sql_insert, mes_donnees)
return None return None
def init_log_system(self) -> None: def log_cmd(self, user_cmd: str, cmd: str) -> None:
# Create folder if not available
logs_directory = f'logs{self.Config.OS_SEP}'
if not os.path.exists(f'{logs_directory}'):
os.makedirs(logs_directory)
# Init logs object
self.logs = logging.getLogger(self.Config.LOGGING_NAME)
self.logs.setLevel(self.Config.DEBUG_LEVEL)
# Add Handlers
file_hanlder = logging.FileHandler(f'logs{self.Config.OS_SEP}defender.log',encoding='UTF-8')
file_hanlder.setLevel(self.Config.DEBUG_LEVEL)
stdout_handler = logging.StreamHandler()
stdout_handler.setLevel(50)
# Define log format
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s')
# Apply log format
file_hanlder.setFormatter(formatter)
stdout_handler.setFormatter(formatter)
# Add handler to logs
self.logs.addHandler(file_hanlder)
self.logs.addHandler(stdout_handler)
# Apply the filter
self.logs.addFilter(self.replace_filter)
# self.logs.Logger('defender').addFilter(self.replace_filter)
self.logs.info('#################### STARTING DEFENDER ####################')
return None
def replace_filter(self, record: logging.LogRecord) -> bool:
response = True
filter: list[str] = ['PING', f":{self.Config.SERVICE_PREFIX}auth"]
# record.msg = record.getMessage().replace("PING", "[REDACTED]")
if self.Settings.CONSOLE:
print(record.getMessage())
for f in filter:
if f in record.getMessage():
response = False
return response # Retourne True pour permettre l'affichage du message
def delete_logger(self, logger_name: str) -> None:
# Récupérer le logger
logger = logging.getLogger(logger_name)
# Retirer tous les gestionnaires du logger et les fermer
for handler in logger.handlers[:]: # Utiliser une copie de la liste
logger.removeHandler(handler)
handler.close()
# Supprimer le logger du dictionnaire global
logging.Logger.manager.loggerDict.pop(logger_name, None)
return None
def log_cmd(self, user_cmd:str, cmd:str) -> None:
"""Enregistre les commandes envoyées par les utilisateurs """Enregistre les commandes envoyées par les utilisateurs
Args: Args:
user_cmd (str): The user who performed the command
cmd (str): la commande a enregistrer cmd (str): la commande a enregistrer
""" """
cmd_list = cmd.split() cmd_list = cmd.split()
@@ -260,10 +207,10 @@ class Base:
cmd = ' '.join(cmd_list) cmd = ' '.join(cmd_list)
insert_cmd_query = f"INSERT INTO {self.Config.TABLE_COMMAND} (datetime, user, commande) VALUES (:datetime, :user, :commande)" insert_cmd_query = f"INSERT INTO {self.Config.TABLE_COMMAND} (datetime, user, commande) VALUES (:datetime, :user, :commande)"
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'commande': cmd} mes_donnees = {'datetime': self.Utils.get_sdatetime(), 'user': user_cmd, 'commande': cmd}
self.db_execute_query(insert_cmd_query, mes_donnees) self.db_execute_query(insert_cmd_query, mes_donnees)
return False return None
def db_isModuleExist(self, module_name:str) -> bool: def db_isModuleExist(self, module_name:str) -> bool:
"""Teste si un module existe déja dans la base de données """Teste si un module existe déja dans la base de données
@@ -283,22 +230,24 @@ class Base:
else: else:
return False return False
def db_record_module(self, user_cmd:str, module_name:str, isdefault:int = 0) -> None: def db_record_module(self, user_cmd: str, module_name: str, isdefault: int = 0) -> None:
"""Enregistre les modules dans la base de données """Enregistre les modules dans la base de données
Args: Args:
cmd (str): le module a enregistrer user_cmd (str): The user who performed the command
module_name (str): The module name
isdefault (int): Is this a default module. Default 0
""" """
if not self.db_isModuleExist(module_name): if not self.db_isModuleExist(module_name):
self.logs.debug(f"Le module {module_name} n'existe pas alors ont le créer") self.logs.debug(f"Le module {module_name} n'existe pas alors ont le créer")
insert_cmd_query = f"INSERT INTO {self.Config.TABLE_MODULE} (datetime, user, module_name, isdefault) VALUES (:datetime, :user, :module_name, :isdefault)" 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.get_datetime(), 'user': user_cmd, 'module_name': module_name, 'isdefault': isdefault} mes_donnees = {'datetime': self.Utils.get_sdatetime(), 'user': user_cmd, 'module_name': module_name, 'isdefault': isdefault}
self.db_execute_query(insert_cmd_query, mes_donnees) self.db_execute_query(insert_cmd_query, mes_donnees)
else: else:
self.logs.debug(f"Le module {module_name} existe déja dans la base de données") self.logs.debug(f"Le module {module_name} existe déja dans la base de données")
return False return None
def db_update_module(self, user_cmd: str, module_name: str) -> None: def db_update_module(self, user_cmd: str, module_name: str) -> None:
"""Modifie la date et le user qui a rechargé le module """Modifie la date et le user qui a rechargé le module
@@ -308,22 +257,22 @@ class Base:
module_name (str): le module a rechargé module_name (str): le module a rechargé
""" """
update_cmd_query = f"UPDATE {self.Config.TABLE_MODULE} SET datetime = :datetime, user = :user WHERE module_name = :module_name" update_cmd_query = f"UPDATE {self.Config.TABLE_MODULE} SET datetime = :datetime, user = :user WHERE module_name = :module_name"
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'module_name': module_name} mes_donnees = {'datetime': self.Utils.get_sdatetime(), 'user': user_cmd, 'module_name': module_name}
self.db_execute_query(update_cmd_query, mes_donnees) self.db_execute_query(update_cmd_query, mes_donnees)
return False return None
def db_delete_module(self, module_name:str) -> None: def db_delete_module(self, module_name:str) -> None:
"""Supprime les modules de la base de données """Supprime les modules de la base de données
Args: Args:
cmd (str): le module a supprimer module_name (str): The module name you want to delete
""" """
insert_cmd_query = f"DELETE FROM {self.Config.TABLE_MODULE} WHERE module_name = :module_name" insert_cmd_query = f"DELETE FROM {self.Config.TABLE_MODULE} WHERE module_name = :module_name"
mes_donnees = {'module_name': module_name} mes_donnees = {'module_name': module_name}
self.db_execute_query(insert_cmd_query, mes_donnees) self.db_execute_query(insert_cmd_query, mes_donnees)
return False return None
def db_sync_core_config(self, module_name: str, dataclassObj: object) -> bool: def db_sync_core_config(self, module_name: str, dataclassObj: object) -> bool:
"""Sync module local parameters with the database """Sync module local parameters with the database
@@ -341,7 +290,7 @@ class Base:
""" """
try: try:
response = True response = True
current_date = self.get_datetime() current_date = self.Utils.get_sdatetime()
core_table = self.Config.TABLE_CONFIG core_table = self.Config.TABLE_CONFIG
# Add local parameters to DB # Add local parameters to DB
@@ -391,7 +340,7 @@ class Base:
result = response.fetchall() result = response.fetchall()
for param, value in result: for param, value in result:
if type(getattr(dataclassObj, param)) == list: if isinstance(getattr(dataclassObj, param), list):
value = ast.literal_eval(value) value = ast.literal_eval(value)
setattr(dataclassObj, param, self.int_if_possible(value)) setattr(dataclassObj, param, self.int_if_possible(value))
@@ -418,7 +367,7 @@ class Base:
isParamExist = result.fetchone() isParamExist = result.fetchone()
if not isParamExist is None: if not isParamExist is None:
mes_donnees = {'datetime': self.get_datetime(), mes_donnees = {'datetime': self.Utils.get_sdatetime(),
'module_name': module_name, 'module_name': module_name,
'param_key': param_key, 'param_key': param_key,
'param_value': param_value 'param_value': param_value
@@ -443,9 +392,9 @@ class Base:
user = self.db_execute_query(f"SELECT id FROM {self.Config.TABLE_ADMIN}") user = self.db_execute_query(f"SELECT id FROM {self.Config.TABLE_ADMIN}")
if not user.fetchall(): if not user.fetchall():
admin = self.Config.OWNER admin = self.Config.OWNER
password = self.crypt_password(self.Config.PASSWORD) password = self.Utils.hash_password(self.Config.PASSWORD)
mes_donnees = {'createdOn': self.get_datetime(), mes_donnees = {'createdOn': self.Utils.get_sdatetime(),
'user': admin, 'user': admin,
'password': password, 'password': password,
'hostname': '*', 'hostname': '*',
@@ -472,8 +421,11 @@ class Base:
self.logs.debug(f"-- Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}") self.logs.debug(f"-- Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}")
return None
except AssertionError as ae: except AssertionError as ae:
self.logs.error(f'Assertion Error -> {ae}') 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 """Create a new thread and store it into running_threads variable
@@ -500,6 +452,39 @@ class Base:
except AssertionError as ae: except AssertionError as ae:
self.logs.error(f'{ae}') self.logs.error(f'{ae}')
def is_thread_alive(self, thread_name: str) -> bool:
"""Check if the thread is still running! using the is_alive method of Threads.
Args:
thread_name (str): The thread name
Returns:
bool: True if is alive
"""
for thread in self.running_threads:
if thread.name.lower() == thread_name.lower():
if thread.is_alive():
return True
else:
return False
return False
def is_thread_exist(self, thread_name: str) -> bool:
"""Check if the thread exist in the local var (running_threads)
Args:
thread_name (str): The thread name
Returns:
bool: True if the thread exist
"""
for thread in self.running_threads:
if thread.name.lower() == thread_name.lower():
return True
return False
def thread_count(self, thread_name: str) -> int: def thread_count(self, thread_name: str) -> int:
"""This method return the number of existing threads """This method return the number of existing threads
currently running or not running currently running or not running
@@ -709,19 +694,6 @@ class Base:
except AttributeError as ae: except AttributeError as ae:
self.logs.error(f"Attribute Error : {ae}") self.logs.error(f"Attribute Error : {ae}")
def crypt_password(self, password:str) -> str:
"""Retourne un mot de passe chiffré en MD5
Args:
password (str): Le password en clair
Returns:
str: Le password en MD5
"""
md5_password = hashlib.md5(password.encode()).hexdigest()
return md5_password
def int_if_possible(self, value): def int_if_possible(self, value):
"""Convertit la valeur reçue en entier, si possible. """Convertit la valeur reçue en entier, si possible.
Sinon elle retourne la valeur initiale. Sinon elle retourne la valeur initiale.
@@ -740,14 +712,14 @@ class Base:
except TypeError: except TypeError:
return value return value
def convert_to_int(self, value: any) -> Union[int, None]: def convert_to_int(self, value: Any) -> Optional[int]:
"""Convert a value to int """Convert a value to int
Args: Args:
value (any): Value to convert to int if possible value (any): Value to convert to int if possible
Returns: Returns:
Union[int, None]: Return the int value or None if not possible int: Return the int value or None if not possible
""" """
try: try:
response = int(value) response = int(value)
@@ -788,7 +760,7 @@ class Base:
self.logs.error(f'General Error: {err}') self.logs.error(f'General Error: {err}')
return False return False
def decode_ip(self, ip_b64encoded: str) -> Union[str, None]: def decode_ip(self, ip_b64encoded: str) -> Optional[str]:
binary_ip = b64decode(ip_b64encoded) binary_ip = b64decode(ip_b64encoded)
try: try:
@@ -799,7 +771,7 @@ class Base:
self.logs.critical(f'This remote ip is not valid : {ve}') self.logs.critical(f'This remote ip is not valid : {ve}')
return None return None
def encode_ip(self, remote_ip_address: str) -> Union[str, None]: def encode_ip(self, remote_ip_address: str) -> Optional[str]:
binary_ip = socket.inet_aton(remote_ip_address) binary_ip = socket.inet_aton(remote_ip_address)
try: try:
@@ -813,15 +785,6 @@ class Base:
self.logs.critical(f'General Error: {err}') self.logs.critical(f'General Error: {err}')
return None return None
def get_random(self, lenght:int) -> str:
"""
Retourn une chaîne aléatoire en fonction de la longueur spécifiée.
"""
caracteres = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
randomize = ''.join(random.choice(caracteres) for _ in range(lenght))
return randomize
def execute_periodic_action(self) -> None: def execute_periodic_action(self) -> None:
if not self.periodic_func: if not self.periodic_func:
@@ -861,23 +824,3 @@ class Base:
self.logs.debug(f'Method to execute : {str(self.periodic_func)}') self.logs.debug(f'Method to execute : {str(self.periodic_func)}')
return None return None
def clean_uid(self, uid:str) -> Union[str, None]:
"""Clean UID by removing @ / % / + / ~ / * / :
Args:
uid (str): The UID to clean
Returns:
str: Clean UID without any sign
"""
try:
if uid is None:
return None
pattern = fr'[:|@|%|\+|~|\*]*'
parsed_UID = re.sub(pattern, '', uid)
return parsed_UID
except TypeError as te:
self.logs.error(f'Type Error: {te}')

View File

@@ -1,127 +1,157 @@
from typing import Union from typing import TYPE_CHECKING, Optional
import core.definition as df
from core.base import Base from core.base import Base
from core.definition import MAdmin
if TYPE_CHECKING:
from core.loader import Loader
class Admin: class Admin:
UID_ADMIN_DB: list[df.MAdmin] = [] UID_ADMIN_DB: list[MAdmin] = []
def __init__(self, baseObj: Base) -> None: def __init__(self, loader: 'Loader') -> None:
self.Logs = baseObj.logs self.Logs = loader.Logs
pass
def insert(self, newAdmin: df.MAdmin) -> bool: def insert(self, new_admin: MAdmin) -> bool:
"""Insert a new admin object model
result = False Args:
exist = False new_admin (MAdmin): The new admin object model to insert
Returns:
bool: True if it was inserted
"""
for record in self.UID_ADMIN_DB: for record in self.UID_ADMIN_DB:
if record.uid == newAdmin.uid: if record.uid == new_admin.uid:
# If the admin exist then return False and do not go further # If the admin exist then return False and do not go further
exist = True
self.Logs.debug(f'{record.uid} already exist') self.Logs.debug(f'{record.uid} already exist')
return result return False
if not exist: self.UID_ADMIN_DB.append(new_admin)
self.UID_ADMIN_DB.append(newAdmin) self.Logs.debug(f'A new admin ({new_admin.nickname}) has been created')
result = True return True
self.Logs.debug(f'UID ({newAdmin.uid}) has been created')
if not result: def update_nickname(self, uid: str, new_admin_nickname: str) -> bool:
self.Logs.critical(f'The User Object was not inserted {newAdmin}') """Update nickname of an admin
return result Args:
uid (str): The Admin UID
new_admin_nickname (str): The new nickname of the admin
def update_nickname(self, uid: str, newNickname: str) -> bool: Returns:
bool: True if the nickname has been updated.
result = False """
for record in self.UID_ADMIN_DB: for record in self.UID_ADMIN_DB:
if record.uid == uid: if record.uid == uid:
# If the admin exist, update and do not go further # If the admin exist, update and do not go further
record.nickname = newNickname record.nickname = new_admin_nickname
result = True self.Logs.debug(f'UID ({record.uid}) has been updated with new nickname {new_admin_nickname}')
self.Logs.debug(f'UID ({record.uid}) has been updated with new nickname {newNickname}') return True
return result
if not result:
self.Logs.debug(f'The new nickname {newNickname} was not updated, uid = {uid} - The Client is not an admin')
return result self.Logs.debug(f'The new nickname {new_admin_nickname} was not updated, uid = {uid} - The Client is not an admin')
return False
def update_level(self, nickname: str, newLevel: int) -> bool: def update_level(self, nickname: str, new_admin_level: int) -> bool:
"""Update the admin level
result = False Args:
nickname (str): The admin nickname
new_admin_level (int): The new level of the admin
Returns:
bool: True if the admin level has been updated
"""
for record in self.UID_ADMIN_DB: for record in self.UID_ADMIN_DB:
if record.nickname == nickname: if record.nickname == nickname:
# If the admin exist, update and do not go further # If the admin exist, update and do not go further
record.level = newLevel record.level = new_admin_level
result = True self.Logs.debug(f'Admin ({record.nickname}) has been updated with new level {new_admin_level}')
self.Logs.debug(f'Admin ({record.nickname}) has been updated with new level {newLevel}') return True
return result
if not result: self.Logs.debug(f'The new level {new_admin_level} was not updated, nickname = {nickname} - The Client is not an admin')
self.Logs.debug(f'The new level {newLevel} was not updated, nickname = {nickname} - The Client is not an admin')
return result return False
def delete(self, uidornickname: str) -> bool: def delete(self, uidornickname: str) -> bool:
"""Delete admin
result = False Args:
uidornickname (str): The UID or nickname of the admin
Returns:
bool: True if the admin has been deleted
"""
for record in self.UID_ADMIN_DB: for record in self.UID_ADMIN_DB:
if record.uid == uidornickname: if record.uid == uidornickname:
# If the admin exist, delete and do not go further # If the admin exist, delete and do not go further
self.UID_ADMIN_DB.remove(record) self.UID_ADMIN_DB.remove(record)
result = True
self.Logs.debug(f'UID ({record.uid}) has been deleted') self.Logs.debug(f'UID ({record.uid}) has been deleted')
return result return True
if record.nickname == uidornickname: if record.nickname.lower() == uidornickname.lower():
# If the admin exist, delete and do not go further # If the admin exist, delete and do not go further
self.UID_ADMIN_DB.remove(record) self.UID_ADMIN_DB.remove(record)
result = True
self.Logs.debug(f'nickname ({record.nickname}) has been deleted') self.Logs.debug(f'nickname ({record.nickname}) has been deleted')
return result return True
if not result: self.Logs.debug(f'The UID {uidornickname} was not deleted')
self.Logs.critical(f'The UID {uidornickname} was not deleted')
return result return False
def get_Admin(self, uidornickname: str) -> Union[df.MAdmin, None]: def get_admin(self, uidornickname: str) -> Optional[MAdmin]:
"""Get the admin object model
Args:
uidornickname (str): UID or Nickname of the admin
Returns:
Optional[MAdmin]: The MAdmin object model if exist
"""
Admin = None
for record in self.UID_ADMIN_DB: for record in self.UID_ADMIN_DB:
if record.uid == uidornickname: if record.uid == uidornickname:
Admin = record return record
elif record.nickname == uidornickname: elif record.nickname.lower() == uidornickname.lower():
Admin = record return record
#self.Logs.debug(f'Search {uidornickname} -- result = {Admin}') return None
return Admin def get_uid(self, uidornickname:str) -> Optional[str]:
"""Get the UID of the admin
def get_uid(self, uidornickname:str) -> Union[str, None]: Args:
uidornickname (str): The UID or nickname of the admin
Returns:
Optional[str]: The UID of the admin
"""
uid = None
for record in self.UID_ADMIN_DB: for record in self.UID_ADMIN_DB:
if record.uid == uidornickname: if record.uid == uidornickname:
uid = record.uid return record.uid
if record.nickname == uidornickname: if record.nickname.lower() == uidornickname.lower():
uid = record.uid return record.uid
self.Logs.debug(f'The UID that you are looking for {uidornickname} has been found {uid}') return None
return uid
def get_nickname(self, uidornickname:str) -> Union[str, None]: def get_nickname(self, uidornickname:str) -> Optional[str]:
"""Get the nickname of the admin
Args:
uidornickname (str): The UID or the nickname of the admin
Returns:
Optional[str]: The nickname of the admin
"""
nickname = None
for record in self.UID_ADMIN_DB: for record in self.UID_ADMIN_DB:
if record.nickname == uidornickname: if record.nickname.lower() == uidornickname.lower():
nickname = record.nickname return record.nickname
if record.uid == uidornickname: if record.uid == uidornickname:
nickname = record.nickname return record.nickname
self.Logs.debug(f'The value {uidornickname} -- {nickname}')
return nickname return None

View File

@@ -1,12 +1,9 @@
from re import findall from re import findall
from typing import Union, Literal, TYPE_CHECKING from typing import Any, Optional, Literal, TYPE_CHECKING
from dataclasses import asdict
from core.classes import user
if TYPE_CHECKING: if TYPE_CHECKING:
from core.definition import MChannel from core.definition import MChannel
from core.base import Base from core.loader import Loader
class Channel: class Channel:
@@ -14,18 +11,19 @@ class Channel:
"""List that contains all the Channels objects (ChannelModel) """List that contains all the Channels objects (ChannelModel)
""" """
def __init__(self, baseObj: 'Base') -> None: def __init__(self, loader: 'Loader') -> None:
self.Logs = baseObj.logs self.Logs = loader.Logs
self.Base = baseObj self.Base = loader.Base
self.Utils = loader.Utils
return None return None
def insert(self, newChan: 'MChannel') -> bool: 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) """This method will insert a new channel and if the channel exist it will update the user list (uids)
Args: Args:
newChan (ChannelModel): The channel model object new_channel (MChannel): The channel model object
Returns: Returns:
bool: True if new channel, False if channel exist (However UID could be updated) bool: True if new channel, False if channel exist (However UID could be updated)
@@ -33,17 +31,17 @@ class Channel:
result = False result = False
exist = False exist = False
if not self.Is_Channel(newChan.name): if not self.is_valid_channel(new_channel.name):
self.Logs.error(f"The channel {newChan.name} is not valid, channel must start with #") self.Logs.error(f"The channel {new_channel.name} is not valid, channel must start with #")
return False return False
for record in self.UID_CHANNEL_DB: for record in self.UID_CHANNEL_DB:
if record.name.lower() == newChan.name.lower(): if record.name.lower() == new_channel.name.lower():
# If the channel exist, update the user list and do not go further # If the channel exist, update the user list and do not go further
exist = True exist = True
# self.Logs.debug(f'{record.name} already exist') # self.Logs.debug(f'{record.name} already exist')
for user in newChan.uids: for user in new_channel.uids:
record.uids.append(user) record.uids.append(user)
# Supprimer les doublons # Supprimer les doublons
@@ -54,41 +52,58 @@ class Channel:
if not exist: if not exist:
# If the channel don't exist, then create it # If the channel don't exist, then create it
newChan.name = newChan.name.lower() new_channel.name = new_channel.name.lower()
self.UID_CHANNEL_DB.append(newChan) self.UID_CHANNEL_DB.append(new_channel)
result = True result = True
# self.Logs.debug(f'New Channel Created: ({newChan})') # self.Logs.debug(f'New Channel Created: ({new_channel})')
if not result: if not result:
self.Logs.critical(f'The Channel Object was not inserted {newChan}') self.Logs.critical(f'The Channel Object was not inserted {new_channel}')
self.clean_channel() self.clean_channel()
return result return result
def delete(self, channel_name: str) -> bool: def delete(self, channel_name: str) -> bool:
"""Delete channel from the UID_CHANNEL_DB
chanObj = self.get_Channel(channel_name) Args:
channel_name (str): The Channel name
if chanObj is None: Returns:
bool: True if it was deleted
"""
chan_obj = self.get_channel(channel_name)
if chan_obj is None:
return False return False
self.UID_CHANNEL_DB.remove(chanObj) self.UID_CHANNEL_DB.remove(chan_obj)
return True return True
def delete_user_from_channel(self, channel_name: str, uid:str) -> bool: def delete_user_from_channel(self, channel_name: str, uid:str) -> bool:
"""Delete a user from a channel
Args:
channel_name (str): The channel name
uid (str): The Client UID
Returns:
bool: True if the client has been deleted from the channel
"""
try: try:
result = False result = False
chanObj = self.get_Channel(channel_name.lower()) chan_obj = self.get_channel(channel_name.lower())
if chanObj is None: if chan_obj is None:
return result return result
for userid in chanObj.uids: for userid in chan_obj.uids:
if self.Base.clean_uid(userid) == self.Base.clean_uid(uid): if self.Utils.clean_uid(userid) == self.Utils.clean_uid(uid):
chanObj.uids.remove(userid) chan_obj.uids.remove(userid)
result = True result = True
self.clean_channel() self.clean_channel()
@@ -98,14 +113,21 @@ class Channel:
self.Logs.error(f'{ve}') self.Logs.error(f'{ve}')
def delete_user_from_all_channel(self, uid:str) -> bool: def delete_user_from_all_channel(self, uid:str) -> bool:
"""Delete a client from all channels
Args:
uid (str): The client UID
Returns:
bool: True if the client has been deleted from all channels
"""
try: try:
result = False result = False
for record in self.UID_CHANNEL_DB: for record in self.UID_CHANNEL_DB:
for user_id in record.uids: for user_id in record.uids:
if self.Base.clean_uid(user_id) == self.Base.clean_uid(uid): if self.Utils.clean_uid(user_id) == self.Utils.clean_uid(uid):
record.uids.remove(user_id) record.uids.remove(user_id)
# self.Logs.debug(f'The UID {uid} has been removed, here is the new object: {record}')
result = True result = True
self.clean_channel() self.clean_channel()
@@ -115,104 +137,113 @@ class Channel:
self.Logs.error(f'{ve}') self.Logs.error(f'{ve}')
def add_user_to_a_channel(self, channel_name: str, uid: str) -> bool: def add_user_to_a_channel(self, channel_name: str, uid: str) -> bool:
"""Add a client to a channel
Args:
channel_name (str): The channel name
uid (str): The client UID
Returns:
bool: True is the clien has been added
"""
try: try:
result = False chan_obj = self.get_channel(channel_name)
chanObj = self.get_Channel(channel_name)
self.Logs.debug(f"** {__name__}")
if chanObj is None: if chan_obj is None:
result = self.insert(MChannel(channel_name, uids=[uid])) # Create a new channel if the channel don't exist
# self.Logs.debug(f"** {__name__} - result: {result}") self.Logs.debug(f"New channel will be created ({channel_name} - {uid})")
# self.Logs.debug(f'New Channel Created: ({chanObj})') return self.insert(MChannel(channel_name, uids=[uid]))
return result
chanObj.uids.append(uid) chan_obj.uids.append(uid)
del_duplicates = list(set(chanObj.uids)) del_duplicates = list(set(chan_obj.uids))
chanObj.uids = del_duplicates chan_obj.uids = del_duplicates
# self.Logs.debug(f'New Channel Created: ({chanObj})')
return True return True
except Exception as err: except Exception as err:
self.Logs.error(f'{err}') self.Logs.error(f'{err}')
return False
def is_user_present_in_channel(self, channel_name: str, uid: str) -> bool: def is_user_present_in_channel(self, channel_name: str, uid: str) -> bool:
"""Check if a user is present in the channel """Check if a user is present in the channel
Args: Args:
channel_name (str): The channel to check channel_name (str): The channel name to check
uid (str): The UID uid (str): The client UID
Returns: Returns:
bool: True if the user is present in the channel bool: True if the user is present in the channel
""" """
user_found = False chan = self.get_channel(channel_name=channel_name)
chan = self.get_Channel(channel_name=channel_name)
if chan is None: if chan is None:
return user_found return False
clean_uid = self.Base.clean_uid(uid=uid) clean_uid = self.Utils.clean_uid(uid=uid)
for chan_uid in chan.uids: for chan_uid in chan.uids:
if self.Base.clean_uid(chan_uid) == clean_uid: if self.Utils.clean_uid(chan_uid) == clean_uid:
user_found = True return True
break
return user_found return False
def clean_channel(self) -> None: def clean_channel(self) -> None:
"""Remove Channels if empty """If channel doesn't contain any client this method will remove the channel
""" """
try: try:
for record in self.UID_CHANNEL_DB: for record in self.UID_CHANNEL_DB:
if not record.uids: if not record.uids:
self.UID_CHANNEL_DB.remove(record) self.UID_CHANNEL_DB.remove(record)
# self.Logs.debug(f'The Channel {record.name} has been removed, here is the new object: {record}')
return None return None
except Exception as err: except Exception as err:
self.Logs.error(f'{err}') self.Logs.error(f'{err}')
def get_Channel(self, channel_name: str) -> Union['MChannel', None]: def get_channel(self, channel_name: str) -> Optional['MChannel']:
"""Get the channel object
Channel = None
for record in self.UID_CHANNEL_DB:
if record.name == channel_name:
Channel = record
return Channel
def get_Channel_AsDict(self, chan_name: str) -> Union[dict[str, any], None]:
chanObj = self.get_Channel(chan_name=chan_name)
if not chanObj is None:
chan_as_dict = asdict(chanObj)
return chan_as_dict
else:
return None
def Is_Channel(self, channelToCheck: str) -> bool:
"""Check if the string has the # caractere and return True if this is a channel
Args: Args:
channelToCheck (str): The string to test if it is a channel or not channel_name (str): The Channel name
Returns:
MChannel: The channel object model if exist else None
"""
for record in self.UID_CHANNEL_DB:
if record.name.lower() == channel_name.lower():
return record
return None
def get_channel_asdict(self, channel_name: str) -> Optional[dict[str, Any]]:
channel_obj: Optional['MChannel'] = self.get_channel(channel_name)
if channel_obj is None:
return None
return channel_obj.to_dict()
def is_valid_channel(self, channel_to_check: str) -> bool:
"""Check if the string has the # caractere and return True if this is a valid channel
Args:
channel_to_check (str): The string to test if it is a channel or not
Returns: Returns:
bool: True if the string is a channel / False if this is not a channel bool: True if the string is a channel / False if this is not a channel
""" """
try: try:
if channelToCheck is None: if channel_to_check is None:
return False return False
pattern = fr'^#' pattern = fr'^#'
isChannel = findall(pattern, channelToCheck) isChannel = findall(pattern, channel_to_check)
if not isChannel: if not isChannel:
return False return False
else: else:
return True return True
except TypeError as te: except TypeError as te:
self.Logs.error(f'TypeError: [{channelToCheck}] - {te}') self.Logs.error(f'TypeError: [{channel_to_check}] - {te}')
except Exception as err: except Exception as err:
self.Logs.error(f'Error Not defined: {err}') self.Logs.error(f'Error Not defined: {err}')
@@ -228,7 +259,7 @@ class Channel:
bool: True if action done bool: True if action done
""" """
try: try:
channel_name = channel_name.lower() if self.Is_Channel(channel_name) else None channel_name = channel_name.lower() if self.is_valid_channel(channel_name) else None
core_table = self.Base.Config.TABLE_CHANNEL core_table = self.Base.Config.TABLE_CHANNEL
if not channel_name: if not channel_name:
@@ -240,10 +271,10 @@ class Channel:
case 'add': case 'add':
mes_donnees = {'module_name': module_name, 'channel_name': channel_name} 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 = self.Base.db_execute_query(f"SELECT id FROM {core_table} WHERE module_name = :module_name AND channel_name = :channel_name", mes_donnees)
isChannelExist = response.fetchone() is_channel_exist = response.fetchone()
if isChannelExist is None: if is_channel_exist is None:
mes_donnees = {'datetime': self.Base.get_datetime(), 'channel_name': channel_name, 'module_name': module_name} 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) insert = self.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: if insert.rowcount:
self.Logs.debug(f'New channel added: channel={channel_name} / module_name={module_name}') self.Logs.debug(f'New channel added: channel={channel_name} / module_name={module_name}')
@@ -266,4 +297,3 @@ class Channel:
except Exception as err: except Exception as err:
self.Logs.error(err) self.Logs.error(err)

View File

@@ -1,39 +1,36 @@
from re import sub from re import sub
from typing import Union, TYPE_CHECKING from typing import Any, Optional, Union, TYPE_CHECKING
from dataclasses import asdict
if TYPE_CHECKING: if TYPE_CHECKING:
from core.base import Base from core.loader import Loader
from core.definition import MClient from core.definition import MClient
class Client: class Client:
CLIENT_DB: list['MClient'] = [] CLIENT_DB: list['MClient'] = []
def __init__(self, baseObj: 'Base') -> None: def __init__(self, loader: 'Loader'):
self.Logs = baseObj.logs self.Logs = loader.Logs
self.Base = baseObj self.Base = loader.Base
return None def insert(self, new_client: 'MClient') -> bool:
def insert(self, newUser: 'MClient') -> bool:
"""Insert a new User object """Insert a new User object
Args: Args:
newUser (UserModel): New userModel object new_client (MClient): New Client object
Returns: Returns:
bool: True if inserted bool: True if inserted
""" """
userObj = self.get_Client(newUser.uid) client_obj = self.get_Client(new_client.uid)
if not userObj is None: if not client_obj is None:
# User already created return False # User already created return False
return False return False
self.CLIENT_DB.append(newUser) self.CLIENT_DB.append(new_client)
return True return True
@@ -47,12 +44,12 @@ class Client:
Returns: Returns:
bool: True if updated bool: True if updated
""" """
userObj = self.get_Client(uidornickname=uid) user_obj = self.get_Client(uidornickname=uid)
if userObj is None: if user_obj is None:
return False return False
userObj.nickname = newNickname user_obj.nickname = newNickname
return True return True
@@ -67,16 +64,16 @@ class Client:
bool: True if user mode has been updaed bool: True if user mode has been updaed
""" """
response = True response = True
userObj = self.get_Client(uidornickname=uidornickname) user_obj = self.get_Client(uidornickname=uidornickname)
if userObj is None: if user_obj is None:
return False return False
action = modes[0] action = modes[0]
new_modes = modes[1:] new_modes = modes[1:]
existing_umodes = userObj.umodes existing_umodes = user_obj.umodes
umodes = userObj.umodes umodes = user_obj.umodes
if action == '+': if action == '+':
@@ -95,7 +92,7 @@ class Client:
final_umodes_liste = [x for x in self.Base.Settings.PROTOCTL_USER_MODES if x in liste_umodes] final_umodes_liste = [x for x in self.Base.Settings.PROTOCTL_USER_MODES if x in liste_umodes]
final_umodes = ''.join(final_umodes_liste) final_umodes = ''.join(final_umodes_liste)
userObj.umodes = f"+{final_umodes}" user_obj.umodes = f"+{final_umodes}"
return response return response
@@ -109,16 +106,16 @@ class Client:
bool: True if deleted bool: True if deleted
""" """
userObj = self.get_Client(uidornickname=uid) user_obj = self.get_Client(uidornickname=uid)
if userObj is None: if user_obj is None:
return False return False
self.CLIENT_DB.remove(userObj) self.CLIENT_DB.remove(user_obj)
return True return True
def get_Client(self, uidornickname: str) -> Union['MClient', None]: def get_Client(self, uidornickname: str) -> Optional['MClient']:
"""Get The Client Object model """Get The Client Object model
Args: Args:
@@ -127,16 +124,15 @@ class Client:
Returns: Returns:
UserModel|None: The UserModel Object | None UserModel|None: The UserModel Object | None
""" """
User = None
for record in self.CLIENT_DB: for record in self.CLIENT_DB:
if record.uid == uidornickname: if record.uid == uidornickname:
User = record return record
elif record.nickname == uidornickname: elif record.nickname == uidornickname:
User = record return record
return User return None
def get_uid(self, uidornickname:str) -> Union[str, None]: def get_uid(self, uidornickname:str) -> Optional[str]:
"""Get the UID of the user starting from the UID or the Nickname """Get the UID of the user starting from the UID or the Nickname
Args: Args:
@@ -146,12 +142,12 @@ class Client:
str|None: Return the UID str|None: Return the UID
""" """
userObj = self.get_Client(uidornickname=uidornickname) client_obj = self.get_Client(uidornickname=uidornickname)
if userObj is None: if client_obj is None:
return None return None
return userObj.uid return client_obj.uid
def get_nickname(self, uidornickname:str) -> Union[str, None]: def get_nickname(self, uidornickname:str) -> Union[str, None]:
"""Get the Nickname starting from UID or the nickname """Get the Nickname starting from UID or the nickname
@@ -162,14 +158,14 @@ class Client:
Returns: Returns:
str|None: the nickname str|None: the nickname
""" """
userObj = self.get_Client(uidornickname=uidornickname) client_obj = self.get_Client(uidornickname=uidornickname)
if userObj is None: if client_obj is None:
return None return None
return userObj.nickname return client_obj.nickname
def get_Client_AsDict(self, uidornickname: str) -> Union[dict[str, any], None]: def get_client_asdict(self, uidornickname: str) -> Optional[dict[str, Any]]:
"""Transform User Object to a dictionary """Transform User Object to a dictionary
Args: Args:
@@ -178,12 +174,12 @@ class Client:
Returns: Returns:
Union[dict[str, any], None]: User Object as a dictionary or None Union[dict[str, any], None]: User Object as a dictionary or None
""" """
userObj = self.get_Client(uidornickname=uidornickname) client_obj = self.get_Client(uidornickname=uidornickname)
if userObj is None: if client_obj is None:
return None return None
return asdict(userObj) return client_obj.to_dict()
def is_exist(self, uidornikname: str) -> bool: def is_exist(self, uidornikname: str) -> bool:
"""Check if the UID or the nickname exist in the USER DB """Check if the UID or the nickname exist in the USER DB
@@ -194,9 +190,9 @@ class Client:
Returns: Returns:
bool: True if exist bool: True if exist
""" """
userObj = self.get_Client(uidornickname=uidornikname) user_obj = self.get_Client(uidornickname=uidornikname)
if userObj is None: if user_obj is None:
return False return False
return True return True

View File

@@ -1,161 +0,0 @@
from dataclasses import asdict
from core.definition import MClone
from typing import Union
from core.base import Base
class Clone:
UID_CLONE_DB: list[MClone] = []
def __init__(self, baseObj: Base) -> None:
self.Logs = baseObj.logs
return None
def insert(self, newCloneObject: MClone) -> bool:
"""Create new Clone object
Args:
newCloneObject (CloneModel): New CloneModel object
Returns:
bool: True if inserted
"""
result = False
exist = False
for record in self.UID_CLONE_DB:
if record.nickname == newCloneObject.nickname:
# If the user exist then return False and do not go further
exist = True
self.Logs.warning(f'Nickname {record.nickname} already exist')
return result
if record.uid == newCloneObject.uid:
exist = True
self.Logs.warning(f'UID: {record.uid} already exist')
return result
if not exist:
self.UID_CLONE_DB.append(newCloneObject)
result = True
# self.Logs.debug(f'New Clone Object Created: ({newCloneObject})')
if not result:
self.Logs.critical(f'The Clone Object was not inserted {newCloneObject}')
return result
def delete(self, uidornickname: str) -> bool:
"""Delete the Clone Object starting from the nickname or the UID
Args:
uidornickname (str): UID or nickname of the clone
Returns:
bool: True if deleted
"""
cloneObj = self.get_Clone(uidornickname=uidornickname)
if cloneObj is None:
return False
self.UID_CLONE_DB.remove(cloneObj)
return True
def exists(self, nickname: str) -> bool:
"""Check if the nickname exist
Args:
nickname (str): Nickname of the clone
Returns:
bool: True if the nickname exist
"""
response = False
for cloneObject in self.UID_CLONE_DB:
if cloneObject.nickname == nickname:
response = True
return response
def uid_exists(self, uid: str) -> bool:
"""Check if the nickname exist
Args:
uid (str): uid of the clone
Returns:
bool: True if the nickname exist
"""
response = False
for cloneObject in self.UID_CLONE_DB:
if cloneObject.uid == uid:
response = True
return response
def get_Clone(self, uidornickname: str) -> Union[MClone, None]:
"""Get MClone object or None
Args:
uidornickname (str): The UID or the Nickname
Returns:
Union[MClone, None]: Return MClone object or None
"""
cloneObj = None
for clone in self.UID_CLONE_DB:
if clone.uid == uidornickname:
cloneObj = clone
if clone.nickname == uidornickname:
cloneObj = clone
return cloneObj
def get_uid(self, uidornickname: str) -> Union[str, None]:
"""Get the UID of the clone starting from the UID or the Nickname
Args:
uidornickname (str): UID or Nickname
Returns:
str|None: Return the UID
"""
uid = None
for record in self.UID_CLONE_DB:
if record.uid == uidornickname:
uid = record.uid
if record.nickname == uidornickname:
uid = record.uid
# if not uid is None:
# self.Logs.debug(f'The UID that you are looking for {uidornickname} has been found {uid}')
return uid
def get_Clone_AsDict(self, uidornickname: str) -> Union[dict[str, any], None]:
cloneObj = self.get_Clone(uidornickname=uidornickname)
if not cloneObj is None:
cloneObj_as_dict = asdict(cloneObj)
return cloneObj_as_dict
else:
return None
def kill(self, nickname:str) -> bool:
response = False
for cloneObject in self.UID_CLONE_DB:
if cloneObject.nickname == nickname:
cloneObject.alive = False # Kill the clone
response = True
return response

59
core/classes/commands.py Normal file
View File

@@ -0,0 +1,59 @@
from typing import TYPE_CHECKING, Optional
from core.definition import MCommand
if TYPE_CHECKING:
from core.loader import Loader
class Command:
DB_COMMANDS: list['MCommand'] = []
def __init__(self, loader: 'Loader'):
self.Base = loader.Base
def build(self, new_command_obj: MCommand) -> bool:
command = self.get_command(new_command_obj.command_name, new_command_obj.module_name)
if command is None:
self.DB_COMMANDS.append(new_command_obj)
return True
# Update command if it exist
# Removing the object
if self.drop_command(command.command_name, command.module_name):
# Add the new object
self.DB_COMMANDS.append(new_command_obj)
return True
return False
def get_command(self, command_name: str, module_name: str) -> Optional[MCommand]:
for command in self.DB_COMMANDS:
if command.command_name.lower() == command_name and command.module_name == module_name:
return command
return None
def drop_command(self, command_name: str, module_name: str) -> bool:
cmd = self.get_command(command_name, module_name)
if cmd is not None:
self.DB_COMMANDS.remove(cmd)
return True
return False
def get_ordered_commands(self) -> list[MCommand]:
return sorted(self.DB_COMMANDS, key=lambda c: (c.command_level, c.module_name))
def get_commands_by_level(self, level: int = 0) -> Optional[list[MCommand]]:
cmd_list = self.get_ordered_commands()
new_list: list[MCommand] = []
for cmd in cmd_list:
if cmd.command_level <= level:
new_list.append(cmd)
return new_list

View File

@@ -3,13 +3,13 @@ from sys import exit
from os import sep from os import sep
from typing import Union from typing import Union
from core.definition import MConfig from core.definition import MConfig
from logging import Logger
class Configuration: class Configuration:
def __init__(self) -> None: def __init__(self, logs: Logger) -> None:
self.Logs = logs
self.ConfigObject: MConfig = self.__load_service_configuration() self.ConfigObject: MConfig = self.__load_service_configuration()
return None return None
@@ -22,18 +22,18 @@ class Configuration:
return configuration return configuration
except FileNotFoundError as fe: except FileNotFoundError as fe:
print(f'FileNotFound: {fe}') self.Logs.error(f'FileNotFound: {fe}')
print('Configuration file not found please create config/configuration.json') self.Logs.error('Configuration file not found please create config/configuration.json')
exit(0) exit(0)
except KeyError as ke: except KeyError as ke:
print(f'Key Error: {ke}') self.Logs.error(f'Key Error: {ke}')
print('The key must be defined in core/configuration.json') self.Logs.error('The key must be defined in core/configuration.json')
def __load_service_configuration(self) -> MConfig: def __load_service_configuration(self) -> MConfig:
try: try:
import_config = self.__load_json_service_configuration() import_config = self.__load_json_service_configuration()
Model_keys = MConfig().__dict__ Model_keys = MConfig().to_dict()
model_key_list: list = [] model_key_list: list = []
json_config_key_list: list = [] json_config_key_list: list = []
@@ -46,12 +46,13 @@ class Configuration:
for json_conf in json_config_key_list: for json_conf in json_config_key_list:
if not json_conf in model_key_list: if not json_conf in model_key_list:
import_config.pop(json_conf, None) import_config.pop(json_conf, None)
print(f"\!/ The key {json_conf} is not expected, it has been removed from the system ! please remove it from configuration.json file \!/") self.Logs.warning(f"[!] The key {json_conf} is not expected, it has been removed from the system ! please remove it from configuration.json file [!]")
ConfigObject: MConfig = MConfig( ConfigObject: MConfig = MConfig(
**import_config **import_config
) )
return ConfigObject return ConfigObject
except TypeError as te: except TypeError as te:
print(te) self.Logs.error(te)

View File

@@ -14,8 +14,10 @@ class Inspircd:
self.__Irc = ircInstance self.__Irc = ircInstance
self.__Config = ircInstance.Config self.__Config = ircInstance.Config
self.__Base = ircInstance.Base self.__Base = ircInstance.Base
self.__Utils = ircInstance.Loader.Utils
self.__Logs = ircInstance.Loader.Logs
self.__Base.logs.info(f"** Loading protocol [{__name__}]") self.__Logs.info(f"** Loading protocol [{__name__}]")
def send2socket(self, message: str, print_log: bool = True) -> None: def send2socket(self, message: str, print_log: bool = True) -> None:
"""Envoit les commandes à envoyer au serveur. """Envoit les commandes à envoyer au serveur.
@@ -27,24 +29,24 @@ class Inspircd:
with self.__Base.lock: with self.__Base.lock:
self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[0])) self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[0]))
if print_log: if print_log:
self.__Base.logs.debug(f'<< {message}') self.__Logs.debug(f'<< {message}')
except UnicodeDecodeError as ude: except UnicodeDecodeError as ude:
self.__Base.logs.error(f'Decode Error try iso-8859-1 - {ude} - {message}') 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.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[1],'replace'))
except UnicodeEncodeError as uee: except UnicodeEncodeError as uee:
self.__Base.logs.error(f'Encode Error try iso-8859-1 - {uee} - {message}') 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.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[1],'replace'))
except AssertionError as ae: except AssertionError as ae:
self.__Base.logs.warning(f'Assertion Error {ae} - message: {message}') self.__Logs.warning(f'Assertion Error {ae} - message: {message}')
except SSLEOFError as soe: except SSLEOFError as soe:
self.__Base.logs.error(f"SSLEOFError: {soe} - {message}") self.__Logs.error(f"SSLEOFError: {soe} - {message}")
except SSLError as se: except SSLError as se:
self.__Base.logs.error(f"SSLError: {se} - {message}") self.__Logs.error(f"SSLError: {se} - {message}")
except OSError as oe: except OSError as oe:
self.__Base.logs.error(f"OSError: {oe} - {message}") self.__Logs.error(f"OSError: {oe} - {message}")
except AttributeError as ae: except AttributeError as ae:
self.__Base.logs.critical(f"Attribute Error: {ae}") self.__Logs.critical(f"Attribute Error: {ae}")
def send_priv_msg(self, nick_from: str, msg: str, channel: str = None, nick_to: str = None): 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 """Sending PRIVMSG to a channel or to a nickname by batches
@@ -61,7 +63,7 @@ class Inspircd:
User_to = self.__Irc.User.get_User(nick_to) if nick_to is None else None User_to = self.__Irc.User.get_User(nick_to) if nick_to is None else None
if User_from is None: if User_from is None:
self.__Base.logs.error(f"The sender nickname [{nick_from}] do not exist") self.__Logs.error(f"The sender nickname [{nick_from}] do not exist")
return None return None
if not channel is None: if not channel is None:
@@ -74,7 +76,7 @@ class Inspircd:
batch = str(msg)[i:i+batch_size] batch = str(msg)[i:i+batch_size]
self.send2socket(f":{nick_from} PRIVMSG {User_to.uid} :{batch}") self.send2socket(f":{nick_from} PRIVMSG {User_to.uid} :{batch}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"General Error: {err}") self.__Logs.error(f"General Error: {err}")
def send_notice(self, nick_from: str, nick_to: str, msg: str) -> None: def send_notice(self, nick_from: str, nick_to: str, msg: str) -> None:
"""Sending NOTICE by batches """Sending NOTICE by batches
@@ -90,7 +92,7 @@ class Inspircd:
User_to = self.__Irc.User.get_User(nick_to) User_to = self.__Irc.User.get_User(nick_to)
if User_from is None or User_to is None: if User_from is None or User_to is None:
self.__Base.logs.error(f"The sender [{nick_from}] or the Reciever [{nick_to}] do not exist") self.__Logs.error(f"The sender [{nick_from}] or the Reciever [{nick_to}] do not exist")
return None return None
for i in range(0, len(str(msg)), batch_size): for i in range(0, len(str(msg)), batch_size):
@@ -98,9 +100,9 @@ class Inspircd:
self.send2socket(f":{User_from.uid} NOTICE {User_to.uid} :{batch}") self.send2socket(f":{User_from.uid} NOTICE {User_to.uid} :{batch}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"General Error: {err}") self.__Logs.error(f"General Error: {err}")
def link(self): def send_link(self):
"""Créer le link et envoyer les informations nécessaires pour la """Créer le link et envoyer les informations nécessaires pour la
connexion au serveur. connexion au serveur.
""" """
@@ -122,7 +124,7 @@ class Inspircd:
service_id = self.__Config.SERVICE_ID service_id = self.__Config.SERVICE_ID
version = self.__Config.CURRENT_VERSION version = self.__Config.CURRENT_VERSION
unixtime = self.__Base.get_unixtime() unixtime = self.__Utils.get_unixtime()
self.send2socket(f"CAPAB START 1206") self.send2socket(f"CAPAB START 1206")
@@ -132,7 +134,7 @@ class Inspircd:
self.send2socket(f"BURST {unixtime}") self.send2socket(f"BURST {unixtime}")
self.send2socket(f":{server_id} ENDBURST") self.send2socket(f":{server_id} ENDBURST")
self.__Base.logs.debug(f'>> {__name__} Link information sent to the server') self.__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: 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 # TKL + G user host set_by expire_timestamp set_at_timestamp :reason
@@ -141,12 +143,12 @@ class Inspircd:
return None return None
def set_nick(self, newnickname: str) -> None: def send_set_nick(self, newnickname: str) -> None:
self.send2socket(f":{self.__Config.SERVICE_NICKNAME} NICK {newnickname}") self.send2socket(f":{self.__Config.SERVICE_NICKNAME} NICK {newnickname}")
return None return None
def squit(self, server_id: str, server_link: str, reason: str) -> None: def send_squit(self, server_id: str, server_link: str, reason: str) -> None:
if not reason: if not reason:
reason = 'Service Shutdown' reason = 'Service Shutdown'
@@ -154,26 +156,26 @@ class Inspircd:
self.send2socket(f":{server_id} SQUIT {server_link} :{reason}") self.send2socket(f":{server_id} SQUIT {server_link} :{reason}")
return None return None
def ungline(self, nickname:str, hostname: str) -> None: def send_ungline(self, nickname:str, hostname: str) -> None:
self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - G {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}") self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - G {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}")
return None return None
def kline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: 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 # 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}") self.send2socket(f":{self.__Config.SERVEUR_ID} TKL + k {nickname} {hostname} {set_by} {expire_timestamp} {set_at_timestamp} :{reason}")
return None return None
def sjoin(self, channel: str) -> None: def send_sjoin(self, channel: str) -> None:
if not self.__Irc.Channel.Is_Channel(channel): if not self.__Irc.Channel.is_valid_channel(channel):
self.__Base.logs.error(f"The channel [{channel}] is not valid") self.__Logs.error(f"The channel [{channel}] is not valid")
return None return None
self.send2socket(f":{self.__Config.SERVEUR_ID} SJOIN {self.__Base.get_unixtime()} {channel} + :{self.__Config.SERVICE_ID}") self.send2socket(f":{self.__Config.SERVEUR_ID} SJOIN {self.__Utils.get_unixtime()} {channel} + :{self.__Config.SERVICE_ID}")
# Add defender to the channel uids list # Add defender to the channel uids list
self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[self.__Config.SERVICE_ID])) self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[self.__Config.SERVICE_ID]))
@@ -186,22 +188,22 @@ class Inspircd:
uidornickname (str): The UID or the Nickname uidornickname (str): The UID or the Nickname
reason (str): The reason for the quit reason (str): The reason for the quit
""" """
userObj = self.__Irc.User.get_User(uidornickname=uid) user_obj = self.__Irc.User.get_User(uidornickname=uid)
cloneObj = self.__Irc.Clone.get_Clone(uidornickname=uid) clone_obj = self.__Irc.Clone.get_clone(uidornickname=uid)
reputationObj = self.__Irc.Reputation.get_Reputation(uidornickname=uid) reputationObj = self.__Irc.Reputation.get_Reputation(uidornickname=uid)
if not userObj is None: if not user_obj is None:
self.send2socket(f":{userObj.uid} QUIT :{reason}", print_log=print_log) self.send2socket(f":{user_obj.uid} QUIT :{reason}", print_log=print_log)
self.__Irc.User.delete(userObj.uid) self.__Irc.User.delete(user_obj.uid)
if not cloneObj is None: if not clone_obj is None:
self.__Irc.Clone.delete(cloneObj.uid) self.__Irc.Clone.delete(clone_obj.uid)
if not reputationObj is None: if not reputationObj is None:
self.__Irc.Reputation.delete(reputationObj.uid) self.__Irc.Reputation.delete(reputationObj.uid)
if not self.__Irc.Channel.delete_user_from_all_channel(uid): if not self.__Irc.Channel.delete_user_from_all_channel(uid):
self.__Base.logs.error(f"The UID [{uid}] has not been deleted from all channels") self.__Logs.error(f"The UID [{uid}] has not been deleted from all channels")
return None return None
@@ -220,9 +222,9 @@ class Inspircd:
print_log (bool, optional): print logs if true. Defaults to True. print_log (bool, optional): print logs if true. Defaults to True.
""" """
# {self.Config.SERVEUR_ID} UID # {self.Config.SERVEUR_ID} UID
# {clone.nickname} 1 {self.Base.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.__Utils.get_unixtime()} {clone.username} {clone.hostname} {clone.uid} * {clone.umodes} {clone.vhost} * {self.Base.encode_ip(clone.remote_ip)} :{clone.realname}
try: try:
unixtime = self.__Base.get_unixtime() unixtime = self.__Utils.get_unixtime()
encoded_ip = self.__Base.encode_ip(remote_ip) encoded_ip = self.__Base.encode_ip(remote_ip)
# Create the user # Create the user
@@ -241,7 +243,7 @@ class Inspircd:
return None return None
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def send_join_chan(self, uidornickname: str, channel: str, password: str = None, print_log: bool = True) -> None: def send_join_chan(self, uidornickname: str, channel: str, password: str = None, print_log: bool = True) -> None:
"""Joining a channel """Joining a channel
@@ -259,8 +261,8 @@ class Inspircd:
if userObj is None: if userObj is None:
return None return None
if not self.__Irc.Channel.Is_Channel(channel): if not self.__Irc.Channel.is_valid_channel(channel):
self.__Base.logs.error(f"The channel [{channel}] is not valid") self.__Logs.error(f"The channel [{channel}] is not valid")
return None return None
self.send2socket(f":{userObj.uid} JOIN {channel} {passwordChannel}", print_log=print_log) self.send2socket(f":{userObj.uid} JOIN {channel} {passwordChannel}", print_log=print_log)
@@ -281,11 +283,11 @@ class Inspircd:
userObj = self.__Irc.User.get_User(uidornickname) userObj = self.__Irc.User.get_User(uidornickname)
if userObj is None: if userObj is None:
self.__Base.logs.error(f"The user [{uidornickname}] is not valid") self.__Logs.error(f"The user [{uidornickname}] is not valid")
return None return None
if not self.__Irc.Channel.Is_Channel(channel): if not self.__Irc.Channel.is_valid_channel(channel):
self.__Base.logs.error(f"The channel [{channel}] is not valid") self.__Logs.error(f"The channel [{channel}] is not valid")
return None return None
self.send2socket(f":{userObj.uid} PART {channel}", print_log=print_log) self.send2socket(f":{userObj.uid} PART {channel}", print_log=print_log)
@@ -294,7 +296,7 @@ class Inspircd:
self.__Irc.Channel.delete_user_from_channel(channel, userObj.uid) self.__Irc.Channel.delete_user_from_channel(channel, userObj.uid)
return None return None
def unkline(self, nickname:str, hostname: str) -> None: def send_unkline(self, nickname:str, hostname: str) -> None:
self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - K {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}") self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - K {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}")
@@ -321,14 +323,14 @@ class Inspircd:
# TODO : User object should be able to update user modes # TODO : User object should be able to update user modes
if self.__Irc.User.update_mode(userObj.uid, userMode): if self.__Irc.User.update_mode(userObj.uid, userMode):
return None return None
# self.__Base.logs.debug(f"Updating user mode for [{userObj.nickname}] [{old_umodes}] => [{userObj.umodes}]") # self.__Logs.debug(f"Updating user mode for [{userObj.nickname}] [{old_umodes}] => [{userObj.umodes}]")
return None return None
except IndexError as ie: except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}") self.__Logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_quit(self, serverMsg: list[str]) -> None: def on_quit(self, serverMsg: list[str]) -> None:
"""Handle quit coming from a server """Handle quit coming from a server
@@ -349,9 +351,9 @@ class Inspircd:
return None return None
except IndexError as ie: except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}") self.__Logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_squit(self, serverMsg: list[str]) -> None: def on_squit(self, serverMsg: list[str]) -> None:
"""Handle squit coming from a server """Handle squit coming from a server
@@ -408,9 +410,9 @@ class Inspircd:
return None return None
except IndexError as ie: except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}") self.__Logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_sjoin(self, serverMsg: list[str]) -> None: def on_sjoin(self, serverMsg: list[str]) -> None:
"""Handle sjoin coming from a server """Handle sjoin coming from a server
@@ -443,7 +445,7 @@ class Inspircd:
# Boucle qui va ajouter l'ensemble des users (UID) # Boucle qui va ajouter l'ensemble des users (UID)
for i in range(start_boucle, len(serverMsg)): for i in range(start_boucle, len(serverMsg)):
parsed_UID = str(serverMsg[i]) parsed_UID = str(serverMsg[i])
clean_uid = self.__Irc.User.clean_uid(parsed_UID) clean_uid = self.__Utils.clean_uid(parsed_UID)
if not clean_uid is None and len(clean_uid) == 9: if not clean_uid is None and len(clean_uid) == 9:
list_users.append(parsed_UID) list_users.append(parsed_UID)
@@ -457,9 +459,9 @@ class Inspircd:
return None return None
except IndexError as ie: except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}") self.__Logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_part(self, serverMsg: list[str]) -> None: def on_part(self, serverMsg: list[str]) -> None:
"""Handle part coming from a server """Handle part coming from a server
@@ -478,9 +480,9 @@ class Inspircd:
return None return None
except IndexError as ie: except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}") self.__Logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_uid(self, serverMsg: list[str]) -> None: def on_uid(self, serverMsg: list[str]) -> None:
"""Handle uid message coming from the server """Handle uid message coming from the server
@@ -541,9 +543,9 @@ class Inspircd:
) )
return None return None
except IndexError as ie: except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}") self.__Logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_server_ping(self, serverMsg: list[str]) -> None: def on_server_ping(self, serverMsg: list[str]) -> None:
"""Send a PONG message to the server """Send a PONG message to the server
@@ -561,7 +563,7 @@ class Inspircd:
return None return None
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_version(self, serverMsg: list[str]) -> None: def on_version(self, serverMsg: list[str]) -> None:
"""Sending Server Version to the server """Sending Server Version to the server
@@ -573,7 +575,7 @@ class Inspircd:
# Réponse a un CTCP VERSION # Réponse a un CTCP VERSION
try: try:
nickname = self.__Irc.User.get_nickname(self.__Base.clean_uid(serverMsg[1])) nickname = self.__Irc.User.get_nickname(self.__Utils.clean_uid(serverMsg[1]))
dnickname = self.__Config.SERVICE_NICKNAME dnickname = self.__Config.SERVICE_NICKNAME
arg = serverMsg[4].replace(':', '') arg = serverMsg[4].replace(':', '')
@@ -585,7 +587,7 @@ class Inspircd:
return None return None
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_time(self, serverMsg: list[str]) -> None: def on_time(self, serverMsg: list[str]) -> None:
"""Sending TIME answer to a requestor """Sending TIME answer to a requestor
@@ -597,10 +599,10 @@ class Inspircd:
# Réponse a un CTCP VERSION # Réponse a un CTCP VERSION
try: try:
nickname = self.__Irc.User.get_nickname(self.__Base.clean_uid(serverMsg[1])) nickname = self.__Irc.User.get_nickname(self.__Utils.clean_uid(serverMsg[1]))
dnickname = self.__Config.SERVICE_NICKNAME dnickname = self.__Config.SERVICE_NICKNAME
arg = serverMsg[4].replace(':', '') arg = serverMsg[4].replace(':', '')
current_datetime = self.__Base.get_datetime() current_datetime = self.__Utils.get_sdatetime()
if nickname is None: if nickname is None:
return None return None
@@ -610,7 +612,7 @@ class Inspircd:
return None return None
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_ping(self, serverMsg: list[str]) -> None: def on_ping(self, serverMsg: list[str]) -> None:
"""Sending a PING answer to requestor """Sending a PING answer to requestor
@@ -622,7 +624,7 @@ class Inspircd:
# Réponse a un CTCP VERSION # Réponse a un CTCP VERSION
try: try:
nickname = self.__Irc.User.get_nickname(self.__Base.clean_uid(serverMsg[1])) nickname = self.__Irc.User.get_nickname(self.__Utils.clean_uid(serverMsg[1]))
dnickname = self.__Config.SERVICE_NICKNAME dnickname = self.__Config.SERVICE_NICKNAME
arg = serverMsg[4].replace(':', '') arg = serverMsg[4].replace(':', '')
@@ -631,7 +633,7 @@ class Inspircd:
if arg == '\x01PING': if arg == '\x01PING':
recieved_unixtime = int(serverMsg[5].replace('\x01','')) recieved_unixtime = int(serverMsg[5].replace('\x01',''))
current_unixtime = self.__Base.get_unixtime() current_unixtime = self.__Utils.get_unixtime()
ping_response = current_unixtime - recieved_unixtime ping_response = current_unixtime - recieved_unixtime
# self.__Irc.send2socket(f':{dnickname} NOTICE {nickname} :\x01PING {ping_response} secs\x01') # self.__Irc.send2socket(f':{dnickname} NOTICE {nickname} :\x01PING {ping_response} secs\x01')
@@ -643,7 +645,7 @@ class Inspircd:
return None return None
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_version_msg(self, serverMsg: list[str]) -> None: def on_version_msg(self, serverMsg: list[str]) -> None:
"""Handle version coming from the server """Handle version coming from the server
@@ -653,7 +655,7 @@ class Inspircd:
""" """
try: try:
# ['@label=0073', ':0014E7P06', 'VERSION', 'PyDefender'] # ['@label=0073', ':0014E7P06', 'VERSION', 'PyDefender']
getUser = self.__Irc.User.get_User(self.__Irc.User.clean_uid(serverMsg[1])) getUser = self.__Irc.User.get_User(self.__Utils.clean_uid(serverMsg[1]))
if getUser is None: if getUser is None:
return None return None
@@ -668,4 +670,4 @@ class Inspircd:
return None return None
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")

View File

@@ -1,6 +1,6 @@
from re import match, findall, search from re import match, findall, search
from datetime import datetime from datetime import datetime
from typing import TYPE_CHECKING, Union from typing import TYPE_CHECKING, Optional
from ssl import SSLEOFError, SSLError from ssl import SSLEOFError, SSLError
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -16,14 +16,33 @@ class Unrealircd6:
self.__Config = ircInstance.Config self.__Config = ircInstance.Config
self.__Base = ircInstance.Base self.__Base = ircInstance.Base
self.__Settings = ircInstance.Base.Settings self.__Settings = ircInstance.Base.Settings
self.__Utils = ircInstance.Loader.Utils
self.__Logs = ircInstance.Loader.Logs
self.known_protocol = ['SJOIN', 'UID', 'MD', 'QUIT', 'SQUIT', self.known_protocol: set[str] = {'SJOIN', 'UID', 'MD', 'QUIT', 'SQUIT',
'EOS', 'PRIVMSG', 'MODE', 'UMODE2', 'EOS', 'PRIVMSG', 'MODE', 'UMODE2',
'VERSION', 'REPUTATION', 'SVS2MODE', 'VERSION', 'REPUTATION', 'SVS2MODE',
'SLOG', 'NICK', 'PART', 'PONG' 'SLOG', 'NICK', 'PART', 'PONG',
] 'PROTOCTL', 'SERVER', 'SMOD', 'TKL', 'NETINFO'}
self.__Base.logs.info(f"** Loading protocol [{__name__}]") self.__Logs.info(f"** Loading protocol [{__name__}]")
def get_ircd_protocol_poisition(self, cmd: list[str]) -> tuple[int, Optional[str]]:
"""Get the position of known commands
Args:
cmd (list[str]): The server response
Returns:
tuple[int, Optional[str]]: The position and the command.
"""
for index, token in enumerate(cmd):
if token.upper() in self.known_protocol:
return index, token.upper()
self.__Logs.debug(f"[IRCD LOGS] You need to handle this response: {cmd}")
return (-1, None)
def send2socket(self, message: str, print_log: bool = True) -> None: def send2socket(self, message: str, print_log: bool = True) -> None:
"""Envoit les commandes à envoyer au serveur. """Envoit les commandes à envoyer au serveur.
@@ -35,24 +54,24 @@ class Unrealircd6:
with self.__Base.lock: with self.__Base.lock:
self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[0])) self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[0]))
if print_log: if print_log:
self.__Base.logs.debug(f'<< {message}') self.__Logs.debug(f'<< {message}')
except UnicodeDecodeError as ude: except UnicodeDecodeError as ude:
self.__Base.logs.error(f'Decode Error try iso-8859-1 - {ude} - {message}') 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.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[1],'replace'))
except UnicodeEncodeError as uee: except UnicodeEncodeError as uee:
self.__Base.logs.error(f'Encode Error try iso-8859-1 - {uee} - {message}') 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.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[1],'replace'))
except AssertionError as ae: except AssertionError as ae:
self.__Base.logs.warning(f'Assertion Error {ae} - message: {message}') self.__Logs.warning(f'Assertion Error {ae} - message: {message}')
except SSLEOFError as soe: except SSLEOFError as soe:
self.__Base.logs.error(f"SSLEOFError: {soe} - {message}") self.__Logs.error(f"SSLEOFError: {soe} - {message}")
except SSLError as se: except SSLError as se:
self.__Base.logs.error(f"SSLError: {se} - {message}") self.__Logs.error(f"SSLError: {se} - {message}")
except OSError as oe: except OSError as oe:
self.__Base.logs.error(f"OSError: {oe} - {message}") self.__Logs.error(f"OSError: {oe} - {message}")
except AttributeError as ae: except AttributeError as ae:
self.__Base.logs.critical(f"Attribute Error: {ae}") self.__Logs.critical(f"Attribute Error: {ae}")
def send_priv_msg(self, nick_from: str, msg: str, channel: str = None, nick_to: str = None): 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 """Sending PRIVMSG to a channel or to a nickname by batches
@@ -69,7 +88,7 @@ class Unrealircd6:
User_to = self.__Irc.User.get_User(nick_to) if not nick_to is None else None User_to = self.__Irc.User.get_User(nick_to) if not nick_to is None else None
if User_from is None: if User_from is None:
self.__Base.logs.error(f"The sender nickname [{nick_from}] do not exist") self.__Logs.error(f"The sender nickname [{nick_from}] do not exist")
return None return None
if not channel is None: if not channel is None:
@@ -83,8 +102,8 @@ class Unrealircd6:
self.send2socket(f":{nick_from} PRIVMSG {User_to.uid} :{batch}") self.send2socket(f":{nick_from} PRIVMSG {User_to.uid} :{batch}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"General Error: {err}") self.__Logs.error(f"General Error: {err}")
self.__Base.logs.error(f"General Error: {nick_from} - {channel} - {nick_to}") self.__Logs.error(f"General Error: {nick_from} - {channel} - {nick_to}")
def send_notice(self, nick_from: str, nick_to: str, msg: str) -> None: def send_notice(self, nick_from: str, nick_to: str, msg: str) -> None:
"""Sending NOTICE by batches """Sending NOTICE by batches
@@ -100,7 +119,7 @@ class Unrealircd6:
User_to = self.__Irc.User.get_User(nick_to) User_to = self.__Irc.User.get_User(nick_to)
if User_from is None or User_to is None: if User_from is None or User_to is None:
self.__Base.logs.error(f"The sender [{nick_from}] or the Reciever [{nick_to}] do not exist") self.__Logs.error(f"The sender [{nick_from}] or the Reciever [{nick_to}] do not exist")
return None return None
for i in range(0, len(str(msg)), batch_size): for i in range(0, len(str(msg)), batch_size):
@@ -108,9 +127,9 @@ class Unrealircd6:
self.send2socket(f":{User_from.uid} NOTICE {User_to.uid} :{batch}") self.send2socket(f":{User_from.uid} NOTICE {User_to.uid} :{batch}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"General Error: {err}") self.__Logs.error(f"General Error: {err}")
def parse_server_msg(self, server_msg: list[str]) -> Union[str, None]: def parse_server_msg(self, server_msg: list[str]) -> Optional[str]:
"""Parse the server message and return the command """Parse the server message and return the command
Args: Args:
@@ -144,7 +163,7 @@ class Unrealircd6:
return None return None
def link(self): def send_link(self):
"""Créer le link et envoyer les informations nécessaires pour la """Créer le link et envoyer les informations nécessaires pour la
connexion au serveur. connexion au serveur.
""" """
@@ -167,7 +186,7 @@ class Unrealircd6:
service_id = self.__Config.SERVICE_ID service_id = self.__Config.SERVICE_ID
version = self.__Config.CURRENT_VERSION version = self.__Config.CURRENT_VERSION
unixtime = self.__Base.get_unixtime() unixtime = self.__Utils.get_unixtime()
self.send2socket(f":{server_id} PASS :{password}", print_log=False) self.send2socket(f":{server_id} PASS :{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 SID NOQUIT NICKv2 SJOIN SJ3 NICKIP TKLEXT2 NEXTBANS CLK EXTSWHOIS MLOCK MTAGS")
@@ -177,20 +196,20 @@ class Unrealircd6:
self.send2socket(f":{server_id} SERVER {link} 1 :{info}") self.send2socket(f":{server_id} SERVER {link} 1 :{info}")
self.send2socket(f":{server_id} {nickname} :Reserved for services") self.send2socket(f":{server_id} {nickname} :Reserved for services")
self.send2socket(f":{server_id} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * fwAAAQ== :{realname}") self.send2socket(f":{server_id} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * fwAAAQ== :{realname}")
self.sjoin(chan) self.send_sjoin(chan)
self.send2socket(f":{server_id} TKL + Q * {nickname} {host} 0 {unixtime} :Reserved for services") self.send2socket(f":{server_id} TKL + Q * {nickname} {host} 0 {unixtime} :Reserved for services")
self.send2socket(f":{service_id} MODE {chan} {cmodes}") self.send2socket(f":{service_id} MODE {chan} {cmodes}")
self.__Base.logs.debug(f'>> {__name__} Link information sent to the server') self.__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: def send_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 # 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}") self.send2socket(f":{self.__Config.SERVEUR_ID} TKL + G {nickname} {hostname} {set_by} {expire_timestamp} {set_at_timestamp} :{reason}")
return None return None
def set_nick(self, newnickname: str) -> None: def send_set_nick(self, newnickname: str) -> None:
"""Change nickname of the server """Change nickname of the server
\n This method will also update the User object \n This method will also update the User object
Args: Args:
@@ -202,7 +221,7 @@ class Unrealircd6:
self.__Irc.User.update_nickname(userObj.uid, newnickname) self.__Irc.User.update_nickname(userObj.uid, newnickname)
return None return None
def squit(self, server_id: str, server_link: str, reason: str) -> None: def send_squit(self, server_id: str, server_link: str, reason: str) -> None:
if not reason: if not reason:
reason = 'Service Shutdown' reason = 'Service Shutdown'
@@ -210,36 +229,36 @@ class Unrealircd6:
self.send2socket(f":{server_id} SQUIT {server_link} :{reason}") self.send2socket(f":{server_id} SQUIT {server_link} :{reason}")
return None return None
def ungline(self, nickname:str, hostname: str) -> None: def send_ungline(self, nickname:str, hostname: str) -> None:
self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - G {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}") self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - G {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}")
return None return None
def kline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: 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 # 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}") self.send2socket(f":{self.__Config.SERVEUR_ID} TKL + k {nickname} {hostname} {set_by} {expire_timestamp} {set_at_timestamp} :{reason}")
return None return None
def unkline(self, nickname:str, hostname: str) -> None: def send_unkline(self, nickname:str, hostname: str) -> None:
self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - K {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}") self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - K {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}")
return None return None
def sjoin(self, channel: str) -> None: def send_sjoin(self, channel: str) -> None:
"""Server will join a channel with pre defined umodes """Server will join a channel with pre defined umodes
Args: Args:
channel (str): Channel to join channel (str): Channel to join
""" """
if not self.__Irc.Channel.Is_Channel(channel): if not self.__Irc.Channel.is_valid_channel(channel):
self.__Base.logs.error(f"The channel [{channel}] is not valid") self.__Logs.error(f"The channel [{channel}] is not valid")
return None return None
self.send2socket(f":{self.__Config.SERVEUR_ID} SJOIN {self.__Base.get_unixtime()} {channel} {self.__Config.SERVICE_UMODES} :{self.__Config.SERVICE_ID}") 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}") self.send2socket(f":{self.__Config.SERVICE_ID} MODE {channel} {self.__Config.SERVICE_UMODES} {self.__Config.SERVICE_ID}")
# Add defender to the channel uids list # Add defender to the channel uids list
@@ -257,7 +276,7 @@ class Unrealircd6:
try: try:
userObj = self.__Irc.User.get_User(uidornickname=nick_to_sapart) userObj = self.__Irc.User.get_User(uidornickname=nick_to_sapart)
chanObj = self.__Irc.Channel.get_Channel(channel_name) chanObj = self.__Irc.Channel.get_channel(channel_name)
service_uid = self.__Config.SERVICE_ID service_uid = self.__Config.SERVICE_ID
if userObj is None or chanObj is None: if userObj is None or chanObj is None:
@@ -269,7 +288,7 @@ class Unrealircd6:
return None return None
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def send_sajoin(self, nick_to_sajoin: str, channel_name: str) -> None: def send_sajoin(self, nick_to_sajoin: str, channel_name: str) -> None:
"""_summary_ """_summary_
@@ -281,7 +300,7 @@ class Unrealircd6:
try: try:
userObj = self.__Irc.User.get_User(uidornickname=nick_to_sajoin) userObj = self.__Irc.User.get_User(uidornickname=nick_to_sajoin)
chanObj = self.__Irc.Channel.get_Channel(channel_name) chanObj = self.__Irc.Channel.get_channel(channel_name)
service_uid = self.__Config.SERVICE_ID service_uid = self.__Config.SERVICE_ID
if userObj is None: if userObj is None:
@@ -290,7 +309,7 @@ class Unrealircd6:
if chanObj is None: if chanObj is None:
# Channel not exist # Channel not exist
if not self.__Irc.Channel.Is_Channel(channel_name): if not self.__Irc.Channel.is_valid_channel(channel_name):
# Incorrect channel: leave # Incorrect channel: leave
return None return None
@@ -306,7 +325,7 @@ class Unrealircd6:
return None return None
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def send_svs_mode(self, nickname: str, user_mode: str) -> None: def send_svs_mode(self, nickname: str, user_mode: str) -> None:
try: try:
@@ -325,34 +344,29 @@ class Unrealircd6:
return None return None
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def send_quit(self, uid: str, reason: str, print_log: True) -> None: def send_quit(self, uid: str, reason: str, print_log: True) -> None:
"""Send quit message """Send quit message
- Delete uid from User object - Delete uid from User object
- Delete uid from Clone object
- Delete uid from Reputation object - Delete uid from Reputation object
Args: Args:
uidornickname (str): The UID or the Nickname uidornickname (str): The UID or the Nickname
reason (str): The reason for the quit reason (str): The reason for the quit
""" """
userObj = self.__Irc.User.get_User(uidornickname=uid) user_obj = self.__Irc.User.get_User(uidornickname=uid)
cloneObj = self.__Irc.Clone.get_Clone(uidornickname=uid)
reputationObj = self.__Irc.Reputation.get_Reputation(uidornickname=uid) reputationObj = self.__Irc.Reputation.get_Reputation(uidornickname=uid)
if not userObj is None: if not user_obj is None:
self.send2socket(f":{userObj.uid} QUIT :{reason}", print_log=print_log) self.send2socket(f":{user_obj.uid} QUIT :{reason}", print_log=print_log)
self.__Irc.User.delete(userObj.uid) self.__Irc.User.delete(user_obj.uid)
if not cloneObj is None:
self.__Irc.Clone.delete(cloneObj.uid)
if not reputationObj is None: if not reputationObj is None:
self.__Irc.Reputation.delete(reputationObj.uid) self.__Irc.Reputation.delete(reputationObj.uid)
if not self.__Irc.Channel.delete_user_from_all_channel(uid): if not self.__Irc.Channel.delete_user_from_all_channel(uid):
self.__Base.logs.error(f"The UID [{uid}] has not been deleted from all channels") self.__Logs.error(f"The UID [{uid}] has not been deleted from all channels")
return None return None
@@ -371,9 +385,9 @@ class Unrealircd6:
print_log (bool, optional): print logs if true. Defaults to True. print_log (bool, optional): print logs if true. Defaults to True.
""" """
# {self.Config.SERVEUR_ID} UID # {self.Config.SERVEUR_ID} UID
# {clone.nickname} 1 {self.Base.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.__Utils.get_unixtime()} {clone.username} {clone.hostname} {clone.uid} * {clone.umodes} {clone.vhost} * {self.Base.encode_ip(clone.remote_ip)} :{clone.realname}
try: try:
unixtime = self.__Base.get_unixtime() unixtime = self.__Utils.get_unixtime()
encoded_ip = self.__Base.encode_ip(remote_ip) encoded_ip = self.__Base.encode_ip(remote_ip)
# Create the user # Create the user
@@ -392,7 +406,7 @@ class Unrealircd6:
return None return None
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def send_join_chan(self, uidornickname: str, channel: str, password: str = None, print_log: bool = True) -> None: def send_join_chan(self, uidornickname: str, channel: str, password: str = None, print_log: bool = True) -> None:
"""Joining a channel """Joining a channel
@@ -410,8 +424,8 @@ class Unrealircd6:
if userObj is None: if userObj is None:
return None return None
if not self.__Irc.Channel.Is_Channel(channel): if not self.__Irc.Channel.is_valid_channel(channel):
self.__Base.logs.error(f"The channel [{channel}] is not valid") self.__Logs.error(f"The channel [{channel}] is not valid")
return None return None
self.send2socket(f":{userObj.uid} JOIN {channel} {passwordChannel}", print_log=print_log) self.send2socket(f":{userObj.uid} JOIN {channel} {passwordChannel}", print_log=print_log)
@@ -447,11 +461,11 @@ class Unrealircd6:
userObj = self.__Irc.User.get_User(uidornickname) userObj = self.__Irc.User.get_User(uidornickname)
if userObj is None: if userObj is None:
self.__Base.logs.error(f"The user [{uidornickname}] is not valid") self.__Logs.error(f"The user [{uidornickname}] is not valid")
return None return None
if not self.__Irc.Channel.Is_Channel(channel): if not self.__Irc.Channel.is_valid_channel(channel):
self.__Base.logs.error(f"The channel [{channel}] is not valid") self.__Logs.error(f"The channel [{channel}] is not valid")
return None return None
self.send2socket(f":{userObj.uid} PART {channel}", print_log=print_log) self.send2socket(f":{userObj.uid} PART {channel}", print_log=print_log)
@@ -462,9 +476,9 @@ class Unrealircd6:
def send_mode_chan(self, channel_name: str, channel_mode: str) -> None: def send_mode_chan(self, channel_name: str, channel_mode: str) -> None:
channel = self.__Irc.Channel.Is_Channel(channelToCheck=channel_name) channel = self.__Irc.Channel.is_valid_channel(channel_name)
if not channel: if not channel:
self.__Base.logs.error(f'The channel [{channel_name}] is not correct') self.__Logs.error(f'The channel [{channel_name}] is not correct')
return None return None
self.send2socket(f":{self.__Config.SERVICE_NICKNAME} MODE {channel_name} {channel_mode}") self.send2socket(f":{self.__Config.SERVICE_NICKNAME} MODE {channel_name} {channel_mode}")
@@ -502,9 +516,9 @@ class Unrealircd6:
return None return None
except IndexError as ie: except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}") self.__Logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_mode(self, serverMsg: list[str]) -> None: def on_mode(self, serverMsg: list[str]) -> None:
"""Handle mode coming from a server """Handle mode coming from a server
@@ -538,14 +552,14 @@ class Unrealircd6:
# TODO : User object should be able to update user modes # TODO : User object should be able to update user modes
if self.__Irc.User.update_mode(userObj.uid, userMode): if self.__Irc.User.update_mode(userObj.uid, userMode):
return None return None
# self.__Base.logs.debug(f"Updating user mode for [{userObj.nickname}] [{old_umodes}] => [{userObj.umodes}]") # self.__Logs.debug(f"Updating user mode for [{userObj.nickname}] [{old_umodes}] => [{userObj.umodes}]")
return None return None
except IndexError as ie: except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}") self.__Logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_quit(self, serverMsg: list[str]) -> None: def on_quit(self, serverMsg: list[str]) -> None:
"""Handle quit coming from a server """Handle quit coming from a server
@@ -562,14 +576,13 @@ class Unrealircd6:
self.__Irc.User.delete(uid_who_quit) self.__Irc.User.delete(uid_who_quit)
self.__Irc.Client.delete(uid_who_quit) self.__Irc.Client.delete(uid_who_quit)
self.__Irc.Reputation.delete(uid_who_quit) self.__Irc.Reputation.delete(uid_who_quit)
self.__Irc.Clone.delete(uid_who_quit)
return None return None
except IndexError as ie: except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}") self.__Logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_squit(self, serverMsg: list[str]) -> None: def on_squit(self, serverMsg: list[str]) -> None:
"""Handle squit coming from a server """Handle squit coming from a server
@@ -649,9 +662,9 @@ class Unrealircd6:
return None return None
except IndexError as ie: except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}") self.__Logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_sjoin(self, serverMsg: list[str]) -> None: def on_sjoin(self, serverMsg: list[str]) -> None:
"""Handle sjoin coming from a server """Handle sjoin coming from a server
@@ -688,7 +701,7 @@ class Unrealircd6:
# Boucle qui va ajouter l'ensemble des users (UID) # Boucle qui va ajouter l'ensemble des users (UID)
for i in range(start_boucle, len(serverMsg_copy)): for i in range(start_boucle, len(serverMsg_copy)):
parsed_UID = str(serverMsg_copy[i]) parsed_UID = str(serverMsg_copy[i])
clean_uid = self.__Irc.User.clean_uid(parsed_UID) clean_uid = self.__Utils.clean_uid(parsed_UID)
if not clean_uid is None and len(clean_uid) == 9: if not clean_uid is None and len(clean_uid) == 9:
list_users.append(clean_uid) list_users.append(clean_uid)
@@ -702,9 +715,9 @@ class Unrealircd6:
return None return None
except IndexError as ie: except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}") self.__Logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_part(self, serverMsg: list[str]) -> None: def on_part(self, serverMsg: list[str]) -> None:
"""Handle part coming from a server """Handle part coming from a server
@@ -723,9 +736,9 @@ class Unrealircd6:
return None return None
except IndexError as ie: except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}") self.__Logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_eos(self, serverMsg: list[str]) -> None: def on_eos(self, serverMsg: list[str]) -> None:
"""Handle EOS coming from a server """Handle EOS coming from a server
@@ -757,16 +770,16 @@ class Unrealircd6:
print(f"# VERSION : {version} ") print(f"# VERSION : {version} ")
print(f"################################################") print(f"################################################")
self.__Base.logs.info(f"################### DEFENDER ###################") self.__Logs.info(f"################### DEFENDER ###################")
self.__Base.logs.info(f"# SERVICE CONNECTE ") self.__Logs.info(f"# SERVICE CONNECTE ")
self.__Base.logs.info(f"# SERVEUR : {self.__Config.SERVEUR_IP} ") self.__Logs.info(f"# SERVEUR : {self.__Config.SERVEUR_IP} ")
self.__Base.logs.info(f"# PORT : {self.__Config.SERVEUR_PORT} ") self.__Logs.info(f"# PORT : {self.__Config.SERVEUR_PORT} ")
self.__Base.logs.info(f"# SSL : {self.__Config.SERVEUR_SSL} ") self.__Logs.info(f"# SSL : {self.__Config.SERVEUR_SSL} ")
self.__Base.logs.info(f"# SSL VER : {self.__Config.SSL_VERSION} ") self.__Logs.info(f"# SSL VER : {self.__Config.SSL_VERSION} ")
self.__Base.logs.info(f"# NICKNAME : {self.__Config.SERVICE_NICKNAME} ") self.__Logs.info(f"# NICKNAME : {self.__Config.SERVICE_NICKNAME} ")
self.__Base.logs.info(f"# CHANNEL : {self.__Config.SERVICE_CHANLOG} ") self.__Logs.info(f"# CHANNEL : {self.__Config.SERVICE_CHANLOG} ")
self.__Base.logs.info(f"# VERSION : {version} ") self.__Logs.info(f"# VERSION : {version} ")
self.__Base.logs.info(f"################################################") self.__Logs.info(f"################################################")
if self.__Base.check_for_new_version(False): if self.__Base.check_for_new_version(False):
self.send_priv_msg( self.send_priv_msg(
@@ -789,11 +802,11 @@ class Unrealircd6:
return None return None
except IndexError as ie: except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Key Error: {ie}") self.__Logs.error(f"{__name__} - Key Error: {ie}")
except KeyError as ke: except KeyError as ke:
self.__Base.logs.error(f"{__name__} - Key Error: {ke}") self.__Logs.error(f"{__name__} - Key Error: {ke}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_reputation(self, serverMsg: list[str]) -> None: def on_reputation(self, serverMsg: list[str]) -> None:
"""Handle REPUTATION coming from a server """Handle REPUTATION coming from a server
@@ -824,7 +837,7 @@ class Unrealircd6:
self.__Irc.first_score = 0 self.__Irc.first_score = 0
self.Logs.error(f'Value Error {__name__}: {ve}') self.Logs.error(f'Value Error {__name__}: {ve}')
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_uid(self, serverMsg: list[str]) -> None: def on_uid(self, serverMsg: list[str]) -> None:
"""Handle uid message coming from the server """Handle uid message coming from the server
@@ -885,9 +898,9 @@ class Unrealircd6:
) )
return None return None
except IndexError as ie: except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}") self.__Logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_privmsg(self, serverMsg: list[str]) -> None: def on_privmsg(self, serverMsg: list[str]) -> None:
"""Handle PRIVMSG message coming from the server """Handle PRIVMSG message coming from the server
@@ -908,11 +921,11 @@ class Unrealircd6:
if cmd[2] == 'PRIVMSG' and cmd[4] == ':auth': if cmd[2] == 'PRIVMSG' and cmd[4] == ':auth':
data_copy = cmd.copy() data_copy = cmd.copy()
data_copy[6] = '**********' data_copy[6] = '**********'
self.__Base.logs.debug(f">> {data_copy}") self.__Logs.debug(f">> {data_copy}")
else: else:
self.__Base.logs.debug(f">> {cmd}") self.__Logs.debug(f">> {cmd}")
else: else:
self.__Base.logs.debug(f">> {cmd}") self.__Logs.debug(f">> {cmd}")
get_uid_or_nickname = str(cmd[0].replace(':','')) get_uid_or_nickname = str(cmd[0].replace(':',''))
user_trigger = self.__Irc.User.get_nickname(get_uid_or_nickname) user_trigger = self.__Irc.User.get_nickname(get_uid_or_nickname)
@@ -927,7 +940,7 @@ class Unrealircd6:
arg = convert_to_string.split() arg = convert_to_string.split()
arg.remove(f':{self.__Config.SERVICE_PREFIX}') arg.remove(f':{self.__Config.SERVICE_PREFIX}')
if not arg[0].lower() in self.__Irc.module_commands_list: if not arg[0].lower() in self.__Irc.module_commands_list:
self.__Base.logs.debug(f"This command {arg[0]} is not available") self.__Logs.debug(f"This command {arg[0]} is not available")
self.send_notice( self.send_notice(
nick_from=self.__Config.SERVICE_NICKNAME, nick_from=self.__Config.SERVICE_NICKNAME,
nick_to=user_trigger, nick_to=user_trigger,
@@ -938,7 +951,7 @@ class Unrealircd6:
cmd_to_send = convert_to_string.replace(':','') cmd_to_send = convert_to_string.replace(':','')
self.__Base.log_cmd(user_trigger, cmd_to_send) self.__Base.log_cmd(user_trigger, cmd_to_send)
fromchannel = str(cmd[2]).lower() if self.__Irc.Channel.Is_Channel(cmd[2]) else None fromchannel = str(cmd[2]).lower() if self.__Irc.Channel.is_valid_channel(cmd[2]) else None
self.__Irc.hcmds(user_trigger, fromchannel, arg, cmd) self.__Irc.hcmds(user_trigger, fromchannel, arg, cmd)
if cmd[2] == self.__Config.SERVICE_ID: if cmd[2] == self.__Config.SERVICE_ID:
@@ -966,7 +979,7 @@ class Unrealircd6:
return False return False
if not arg[0].lower() in self.__Irc.module_commands_list: if not arg[0].lower() in self.__Irc.module_commands_list:
self.__Base.logs.debug(f"This command {arg[0]} sent by {user_trigger} is not available") self.__Logs.debug(f"This command {arg[0]} sent by {user_trigger} is not available")
return False return False
cmd_to_send = convert_to_string.replace(':','') cmd_to_send = convert_to_string.replace(':','')
@@ -974,17 +987,17 @@ class Unrealircd6:
fromchannel = None fromchannel = None
if len(arg) >= 2: if len(arg) >= 2:
fromchannel = str(arg[1]).lower() if self.__Irc.Channel.Is_Channel(arg[1]) else None fromchannel = str(arg[1]).lower() if self.__Irc.Channel.is_valid_channel(arg[1]) else None
self.__Irc.hcmds(user_trigger, fromchannel, arg, cmd) self.__Irc.hcmds(user_trigger, fromchannel, arg, cmd)
return None return None
except KeyError as ke: except KeyError as ke:
self.__Base.logs.error(f"Key Error: {ke}") self.__Logs.error(f"Key Error: {ke}")
except AttributeError as ae: except AttributeError as ae:
self.__Base.logs.error(f"Attribute Error: {ae}") self.__Logs.error(f"Attribute Error: {ae}")
except Exception as err: except Exception as err:
self.__Base.logs.error(f"General Error: {err} - {srv_msg}") self.__Logs.error(f"General Error: {err} - {srv_msg}")
def on_server_ping(self, serverMsg: list[str]) -> None: def on_server_ping(self, serverMsg: list[str]) -> None:
"""Send a PONG message to the server """Send a PONG message to the server
@@ -999,7 +1012,7 @@ class Unrealircd6:
return None return None
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_version(self, serverMsg: list[str]) -> None: def on_version(self, serverMsg: list[str]) -> None:
"""Sending Server Version to the server """Sending Server Version to the server
@@ -1011,7 +1024,7 @@ class Unrealircd6:
# Réponse a un CTCP VERSION # Réponse a un CTCP VERSION
try: try:
nickname = self.__Irc.User.get_nickname(self.__Base.clean_uid(serverMsg[1])) nickname = self.__Irc.User.get_nickname(self.__Utils.clean_uid(serverMsg[1]))
dnickname = self.__Config.SERVICE_NICKNAME dnickname = self.__Config.SERVICE_NICKNAME
arg = serverMsg[4].replace(':', '') arg = serverMsg[4].replace(':', '')
@@ -1023,7 +1036,7 @@ class Unrealircd6:
return None return None
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_time(self, serverMsg: list[str]) -> None: def on_time(self, serverMsg: list[str]) -> None:
"""Sending TIME answer to a requestor """Sending TIME answer to a requestor
@@ -1035,10 +1048,10 @@ class Unrealircd6:
# Réponse a un CTCP VERSION # Réponse a un CTCP VERSION
try: try:
nickname = self.__Irc.User.get_nickname(self.__Base.clean_uid(serverMsg[1])) nickname = self.__Irc.User.get_nickname(self.__Utils.clean_uid(serverMsg[1]))
dnickname = self.__Config.SERVICE_NICKNAME dnickname = self.__Config.SERVICE_NICKNAME
arg = serverMsg[4].replace(':', '') arg = serverMsg[4].replace(':', '')
current_datetime = self.__Base.get_datetime() current_datetime = self.__Utils.get_sdatetime()
if nickname is None: if nickname is None:
return None return None
@@ -1048,7 +1061,7 @@ class Unrealircd6:
return None return None
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_ping(self, serverMsg: list[str]) -> None: def on_ping(self, serverMsg: list[str]) -> None:
"""Sending a PING answer to requestor """Sending a PING answer to requestor
@@ -1060,7 +1073,7 @@ class Unrealircd6:
# Réponse a un CTCP VERSION # Réponse a un CTCP VERSION
try: try:
nickname = self.__Irc.User.get_nickname(self.__Base.clean_uid(serverMsg[1])) nickname = self.__Irc.User.get_nickname(self.__Utils.clean_uid(serverMsg[1]))
dnickname = self.__Config.SERVICE_NICKNAME dnickname = self.__Config.SERVICE_NICKNAME
arg = serverMsg[4].replace(':', '') arg = serverMsg[4].replace(':', '')
@@ -1069,7 +1082,7 @@ class Unrealircd6:
if arg == '\x01PING': if arg == '\x01PING':
recieved_unixtime = int(serverMsg[5].replace('\x01','')) recieved_unixtime = int(serverMsg[5].replace('\x01',''))
current_unixtime = self.__Base.get_unixtime() current_unixtime = self.__Utils.get_unixtime()
ping_response = current_unixtime - recieved_unixtime ping_response = current_unixtime - recieved_unixtime
# self.__Irc.send2socket(f':{dnickname} NOTICE {nickname} :\x01PING {ping_response} secs\x01') # self.__Irc.send2socket(f':{dnickname} NOTICE {nickname} :\x01PING {ping_response} secs\x01')
@@ -1081,7 +1094,7 @@ class Unrealircd6:
return None return None
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")
def on_version_msg(self, serverMsg: list[str]) -> None: def on_version_msg(self, serverMsg: list[str]) -> None:
"""Handle version coming from the server """Handle version coming from the server
@@ -1095,7 +1108,7 @@ class Unrealircd6:
if '@' in list(serverMsg_copy[0])[0]: if '@' in list(serverMsg_copy[0])[0]:
serverMsg_copy.pop(0) serverMsg_copy.pop(0)
getUser = self.__Irc.User.get_User(self.__Irc.User.clean_uid(serverMsg_copy[0])) getUser = self.__Irc.User.get_User(self.__Utils.clean_uid(serverMsg_copy[0]))
if getUser is None: if getUser is None:
return None return None
@@ -1113,4 +1126,4 @@ class Unrealircd6:
return None return None
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Logs.error(f"{__name__} - General Error: {err}")

View File

@@ -1,23 +1,23 @@
from typing import Union from typing import TYPE_CHECKING, Optional
from core.definition import MReputation from core.definition import MReputation
from core.base import Base
if TYPE_CHECKING:
from core.loader import Loader
class Reputation: class Reputation:
UID_REPUTATION_DB: list[MReputation] = [] UID_REPUTATION_DB: list[MReputation] = []
def __init__(self, baseObj: Base) -> None: def __init__(self, loader: 'Loader'):
self.Logs = baseObj.logs self.Logs = loader.Logs
self.MReputation: MReputation = MReputation self.MReputation: MReputation = MReputation
return None def insert(self, new_reputation_user: MReputation) -> bool:
def insert(self, newReputationUser: MReputation) -> bool:
"""Insert a new Reputation User object """Insert a new Reputation User object
Args: Args:
newReputationUser (MReputation): New Reputation Model object new_reputation_user (MReputation): New Reputation Model object
Returns: Returns:
bool: True if inserted bool: True if inserted
@@ -26,23 +26,23 @@ class Reputation:
exist = False exist = False
for record in self.UID_REPUTATION_DB: for record in self.UID_REPUTATION_DB:
if record.uid == newReputationUser.uid: if record.uid == new_reputation_user.uid:
# If the user exist then return False and do not go further # If the user exist then return False and do not go further
exist = True exist = True
self.Logs.debug(f'{record.uid} already exist') self.Logs.debug(f'{record.uid} already exist')
return result return result
if not exist: if not exist:
self.UID_REPUTATION_DB.append(newReputationUser) self.UID_REPUTATION_DB.append(new_reputation_user)
result = True result = True
self.Logs.debug(f'New Reputation User Captured: ({newReputationUser})') self.Logs.debug(f'New Reputation User Captured: ({new_reputation_user})')
if not result: if not result:
self.Logs.critical(f'The Reputation User Object was not inserted {newReputationUser}') self.Logs.critical(f'The Reputation User Object was not inserted {new_reputation_user}')
return result return result
def update(self, uid: str, newNickname: str) -> bool: def update(self, uid: str, new_nickname: str) -> bool:
"""Update the nickname starting from the UID """Update the nickname starting from the UID
Args: Args:
@@ -53,12 +53,12 @@ class Reputation:
bool: True if updated bool: True if updated
""" """
reputationObj = self.get_Reputation(uid) reputation_obj = self.get_Reputation(uid)
if reputationObj is None: if reputation_obj is None:
return False return False
reputationObj.nickname = newNickname reputation_obj.nickname = new_nickname
return True return True
@@ -89,7 +89,7 @@ class Reputation:
return result return result
def get_Reputation(self, uidornickname: str) -> Union[MReputation, None]: def get_Reputation(self, uidornickname: str) -> Optional[MReputation]:
"""Get The User Object model """Get The User Object model
Args: Args:
@@ -98,16 +98,15 @@ class Reputation:
Returns: Returns:
UserModel|None: The UserModel Object | None UserModel|None: The UserModel Object | None
""" """
User = None
for record in self.UID_REPUTATION_DB: for record in self.UID_REPUTATION_DB:
if record.uid == uidornickname: if record.uid == uidornickname:
User = record return record
elif record.nickname == uidornickname: elif record.nickname == uidornickname:
User = record return record
return User return None
def get_uid(self, uidornickname:str) -> Union[str, None]: def get_uid(self, uidornickname: str) -> Optional[str]:
"""Get the UID of the user starting from the UID or the Nickname """Get the UID of the user starting from the UID or the Nickname
Args: Args:
@@ -117,14 +116,14 @@ class Reputation:
str|None: Return the UID str|None: Return the UID
""" """
reputationObj = self.get_Reputation(uidornickname) reputation_obj = self.get_Reputation(uidornickname)
if reputationObj is None: if reputation_obj is None:
return None return None
return reputationObj.uid return reputation_obj.uid
def get_nickname(self, uidornickname:str) -> Union[str, None]: def get_nickname(self, uidornickname: str) -> Optional[str]:
"""Get the Nickname starting from UID or the nickname """Get the Nickname starting from UID or the nickname
Args: Args:
@@ -133,12 +132,12 @@ class Reputation:
Returns: Returns:
str|None: the nickname str|None: the nickname
""" """
reputationObj = self.get_Reputation(uidornickname) reputation_obj = self.get_Reputation(uidornickname)
if reputationObj is None: if reputation_obj is None:
return None return None
return reputationObj.nickname return reputation_obj.nickname
def is_exist(self, uidornickname: str) -> bool: def is_exist(self, uidornickname: str) -> bool:
"""Check if the UID or the nickname exist in the reputation DB """Check if the UID or the nickname exist in the reputation DB
@@ -150,9 +149,9 @@ class Reputation:
bool: True if exist bool: True if exist
""" """
reputationObj = self.get_Reputation(uidornickname) reputation_obj = self.get_Reputation(uidornickname)
if reputationObj is None: if isinstance(reputation_obj, MReputation):
return False
else:
return True return True
return False

View File

@@ -1,7 +1,14 @@
'''This class should never be reloaded.
'''
from threading import Timer, Thread, RLock from threading import Timer, Thread, RLock
from socket import socket from socket import socket
from typing import Any, Optional
class Settings: class Settings:
"""This Class will never be reloaded.
Means that the variables are available during
the whole life of the app
"""
RUNNING_TIMERS: list[Timer] = [] RUNNING_TIMERS: list[Timer] = []
RUNNING_THREADS: list[Thread] = [] RUNNING_THREADS: list[Thread] = []
@@ -13,3 +20,30 @@ class Settings:
PROTOCTL_USER_MODES: list[str] = [] PROTOCTL_USER_MODES: list[str] = []
PROTOCTL_PREFIX: list[str] = [] PROTOCTL_PREFIX: list[str] = []
__CACHE: dict[str, Any] = {}
"""Use set_cache or get_cache instead"""
def set_cache(self, key: str, value_to_cache: Any):
"""When you want to store a variable
Ex.
```python
set_cache('MY_KEY', {'key1': 'value1', 'key2', 'value2'})
```
Args:
key (str): The key you want to add.
value_to_cache (Any): The Value you want to store.
"""
self.__CACHE[key] = value_to_cache
def get_cache(self, key) -> Optional[Any]:
"""It returns the value associated to the key and finally it removes the entry"""
if self.__CACHE.get(key):
return self.__CACHE.pop(key)
return None
def get_cache_size(self) -> int:
return len(self.__CACHE)

View File

@@ -1,23 +1,21 @@
from re import sub from re import sub
from typing import Union, TYPE_CHECKING from typing import Any, Optional, TYPE_CHECKING
from dataclasses import asdict from datetime import datetime
if TYPE_CHECKING: if TYPE_CHECKING:
from core.base import Base from core.loader import Loader
from core.definition import MUser from core.definition import MUser
class User: class User:
UID_DB: list['MUser'] = [] UID_DB: list['MUser'] = []
def __init__(self, baseObj: 'Base') -> None: def __init__(self, loader: 'Loader'):
self.Logs = baseObj.logs self.Logs = loader.Logs
self.Base = baseObj self.Base = loader.Base
return None def insert(self, new_user: 'MUser') -> bool:
def insert(self, newUser: 'MUser') -> bool:
"""Insert a new User object """Insert a new User object
Args: Args:
@@ -27,32 +25,32 @@ class User:
bool: True if inserted bool: True if inserted
""" """
userObj = self.get_User(newUser.uid) user_obj = self.get_User(new_user.uid)
if not userObj is None: if not user_obj is None:
# User already created return False # User already created return False
return False return False
self.UID_DB.append(newUser) self.UID_DB.append(new_user)
return True return True
def update_nickname(self, uid: str, newNickname: str) -> bool: def update_nickname(self, uid: str, new_nickname: str) -> bool:
"""Update the nickname starting from the UID """Update the nickname starting from the UID
Args: Args:
uid (str): UID of the user uid (str): UID of the user
newNickname (str): New nickname new_nickname (str): New nickname
Returns: Returns:
bool: True if updated bool: True if updated
""" """
userObj = self.get_User(uidornickname=uid) user_obj = self.get_User(uidornickname=uid)
if userObj is None: if user_obj is None:
return False return False
userObj.nickname = newNickname user_obj.nickname = new_nickname
return True return True
@@ -67,16 +65,16 @@ class User:
bool: True if user mode has been updaed bool: True if user mode has been updaed
""" """
response = True response = True
userObj = self.get_User(uidornickname=uidornickname) user_obj = self.get_User(uidornickname=uidornickname)
if userObj is None: if user_obj is None:
return False return False
action = modes[0] action = modes[0]
new_modes = modes[1:] new_modes = modes[1:]
existing_umodes = userObj.umodes existing_umodes = user_obj.umodes
umodes = userObj.umodes umodes = user_obj.umodes
if action == '+': if action == '+':
@@ -95,7 +93,7 @@ class User:
final_umodes_liste = [x for x in self.Base.Settings.PROTOCTL_USER_MODES if x in liste_umodes] final_umodes_liste = [x for x in self.Base.Settings.PROTOCTL_USER_MODES if x in liste_umodes]
final_umodes = ''.join(final_umodes_liste) final_umodes = ''.join(final_umodes_liste)
userObj.umodes = f"+{final_umodes}" user_obj.umodes = f"+{final_umodes}"
return response return response
@@ -109,16 +107,16 @@ class User:
bool: True if deleted bool: True if deleted
""" """
userObj = self.get_User(uidornickname=uid) user_obj = self.get_User(uidornickname=uid)
if userObj is None: if user_obj is None:
return False return False
self.UID_DB.remove(userObj) self.UID_DB.remove(user_obj)
return True return True
def get_User(self, uidornickname: str) -> Union['MUser', None]: def get_User(self, uidornickname: str) -> Optional['MUser']:
"""Get The User Object model """Get The User Object model
Args: Args:
@@ -127,16 +125,15 @@ class User:
Returns: Returns:
UserModel|None: The UserModel Object | None UserModel|None: The UserModel Object | None
""" """
User = None
for record in self.UID_DB: for record in self.UID_DB:
if record.uid == uidornickname: if record.uid == uidornickname:
User = record return record
elif record.nickname == uidornickname: elif record.nickname == uidornickname:
User = record return record
return User return None
def get_uid(self, uidornickname:str) -> Union[str, None]: def get_uid(self, uidornickname:str) -> Optional[str]:
"""Get the UID of the user starting from the UID or the Nickname """Get the UID of the user starting from the UID or the Nickname
Args: Args:
@@ -146,14 +143,14 @@ class User:
str|None: Return the UID str|None: Return the UID
""" """
userObj = self.get_User(uidornickname=uidornickname) user_obj = self.get_User(uidornickname=uidornickname)
if userObj is None: if user_obj is None:
return None return None
return userObj.uid return user_obj.uid
def get_nickname(self, uidornickname:str) -> Union[str, None]: def get_nickname(self, uidornickname:str) -> Optional[str]:
"""Get the Nickname starting from UID or the nickname """Get the Nickname starting from UID or the nickname
Args: Args:
@@ -162,14 +159,14 @@ class User:
Returns: Returns:
str|None: the nickname str|None: the nickname
""" """
userObj = self.get_User(uidornickname=uidornickname) user_obj = self.get_User(uidornickname=uidornickname)
if userObj is None: if user_obj is None:
return None return None
return userObj.nickname return user_obj.nickname
def get_User_AsDict(self, uidornickname: str) -> Union[dict[str, any], None]: def get_user_asdict(self, uidornickname: str) -> Optional[dict[str, Any]]:
"""Transform User Object to a dictionary """Transform User Object to a dictionary
Args: Args:
@@ -178,12 +175,12 @@ class User:
Returns: Returns:
Union[dict[str, any], None]: User Object as a dictionary or None Union[dict[str, any], None]: User Object as a dictionary or None
""" """
userObj = self.get_User(uidornickname=uidornickname) user_obj = self.get_User(uidornickname=uidornickname)
if userObj is None: if user_obj is None:
return None return None
return asdict(userObj) return user_obj.to_dict()
def is_exist(self, uidornikname: str) -> bool: def is_exist(self, uidornikname: str) -> bool:
"""Check if the UID or the nickname exist in the USER DB """Check if the UID or the nickname exist in the USER DB
@@ -194,14 +191,14 @@ class User:
Returns: Returns:
bool: True if exist bool: True if exist
""" """
userObj = self.get_User(uidornickname=uidornikname) user_obj = self.get_User(uidornickname=uidornikname)
if userObj is None: if user_obj is None:
return False return False
return True return True
def clean_uid(self, uid: str) -> Union[str, None]: def clean_uid(self, uid: str) -> Optional[str]:
"""Clean UID by removing @ / % / + / ~ / * / : """Clean UID by removing @ / % / + / ~ / * / :
Args: Args:
@@ -217,4 +214,35 @@ class User:
if not parsed_UID: if not parsed_UID:
return None return None
return parsed_UID return parsed_UID
def get_user_uptime_in_minutes(self, uidornickname: str) -> float:
"""Retourne depuis quand l'utilisateur est connecté (in minutes).
Args:
uid (str): The uid or le nickname
Returns:
int: How long in minutes has the user been connected?
"""
get_user = self.get_User(uidornickname)
if get_user is None:
return 0
# Convertir la date enregistrée dans UID_DB en un objet {datetime}
connected_time_string = get_user.connexion_datetime
if isinstance(connected_time_string, datetime):
connected_time = connected_time_string
else:
connected_time = datetime.strptime(connected_time_string, "%Y-%m-%d %H:%M:%S.%f")
# What time is it ?
current_datetime = datetime.now()
uptime = current_datetime - connected_time
convert_to_minutes = uptime.seconds / 60
uptime_minutes = round(number=convert_to_minutes, ndigits=2)
return uptime_minutes

View File

@@ -1,10 +1,26 @@
from datetime import datetime from datetime import datetime
from dataclasses import dataclass, field from json import dumps
from typing import Literal from dataclasses import dataclass, field, asdict, fields
from typing import Literal, Any
from os import sep from os import sep
@dataclass @dataclass
class MClient: class MainModel:
"""Parent Model contains important methods"""
def to_dict(self) -> dict[str, Any]:
"""Return the fields of a dataclass instance as a new dictionary mapping field names to field values."""
return asdict(self)
def to_json(self) -> str:
"""Return the object of a dataclass a json str."""
return dumps(self.to_dict())
def get_attributes(self) -> list[str]:
"""Return a list of attributes name"""
return [f.name for f in fields(self)]
@dataclass
class MClient(MainModel):
"""Model Client for registred nickname""" """Model Client for registred nickname"""
uid: str = None uid: str = None
account: str = None account: str = None
@@ -22,7 +38,7 @@ class MClient:
connexion_datetime: datetime = field(default=datetime.now()) connexion_datetime: datetime = field(default=datetime.now())
@dataclass @dataclass
class MUser: class MUser(MainModel):
"""Model User""" """Model User"""
uid: str = None uid: str = None
@@ -40,7 +56,7 @@ class MUser:
connexion_datetime: datetime = field(default=datetime.now()) connexion_datetime: datetime = field(default=datetime.now())
@dataclass @dataclass
class MAdmin: class MAdmin(MainModel):
"""Model Admin""" """Model Admin"""
uid: str = None uid: str = None
@@ -59,7 +75,7 @@ class MAdmin:
level: int = 0 level: int = 0
@dataclass @dataclass
class MReputation: class MReputation(MainModel):
"""Model Reputation""" """Model Reputation"""
uid: str = None uid: str = None
nickname: str = None nickname: str = None
@@ -77,7 +93,7 @@ class MReputation:
secret_code: str = None secret_code: str = None
@dataclass @dataclass
class MChannel: class MChannel(MainModel):
"""Model Channel""" """Model Channel"""
name: str = None name: str = None
@@ -92,7 +108,7 @@ class MChannel:
""" """
@dataclass @dataclass
class ColorModel: class ColorModel(MainModel):
white: str = "\x0300" white: str = "\x0300"
black: str = "\x0301" black: str = "\x0301"
blue: str = "\x0302" blue: str = "\x0302"
@@ -104,7 +120,7 @@ class ColorModel:
underline: str = "\x1F" underline: str = "\x1F"
@dataclass @dataclass
class MConfig: class MConfig(MainModel):
"""Model Configuration""" """Model Configuration"""
SERVEUR_IP: str = "127.0.0.1" SERVEUR_IP: str = "127.0.0.1"
@@ -305,16 +321,8 @@ class MConfig:
"""0: utf-8 | 1: iso-8859-1""" """0: utf-8 | 1: iso-8859-1"""
@dataclass @dataclass
class MClone: class MCommand(MainModel):
"""Model Clone""" module_name: str = None
connected: bool = False command_name: str = None
uid: str = None description: str = None
nickname: str = None command_level: int = 0
username: str = None
realname: str = None
channels: list = field(default_factory=list)
vhost: str = None
hostname: str = 'localhost'
umodes: str = None
remote_ip: str = '127.0.0.1'
group: str = 'Default'

View File

@@ -261,21 +261,21 @@ class Install:
if not do_install: if not do_install:
return None return None
print("===> Vider le cache de pip") print("===> Clean pip cache")
self.run_subprocess([self.config.venv_pip_executable, 'cache', 'purge']) self.run_subprocess([self.config.venv_pip_executable, 'cache', 'purge'])
print("===> Verifier si pip est a jour") print("===> Check if pip is up to date")
self.run_subprocess([self.config.venv_python_executable, '-m', 'pip', 'install', '--upgrade', 'pip']) self.run_subprocess([self.config.venv_python_executable, '-m', 'pip', 'install', '--upgrade', 'pip'])
if not self.check_package('greenlet'): if not self.check_package('greenlet'):
self.run_subprocess([self.config.venv_pip_executable, 'install', '--only-binary', ':all:', 'greenlet']) self.run_subprocess([self.config.venv_pip_executable, 'install', '--only-binary', ':all:', 'greenlet'])
print('====> Module Greenlet installé') print('====> Greenlet installed')
for module in self.config.venv_cmd_requirements: for module in self.config.venv_cmd_requirements:
if not self.check_package(module): if not self.check_package(module):
print("### Trying to install missing python packages ###") print("### Trying to install missing python packages ###")
self.run_subprocess([self.config.venv_pip_executable, 'install', module]) self.run_subprocess([self.config.venv_pip_executable, 'install', module])
print(f"====> Module {module} installé") print(f"====> Module {module} installed!")
else: else:
print(f"==> {module} already installed") print(f"==> {module} already installed")
@@ -307,8 +307,8 @@ WantedBy=default.target
with open(full_service_file_path, 'w+') as servicefile: with open(full_service_file_path, 'w+') as servicefile:
servicefile.write(contain) servicefile.write(contain)
servicefile.close() servicefile.close()
print(f'Service file generated with current configuration') print('Service file generated with current configuration')
print(f'Running Defender IRC Service ...') print('Running IRC Service ...')
self.run_subprocess(self.config.service_cmd_daemon_reload) self.run_subprocess(self.config.service_cmd_daemon_reload)
self.run_subprocess(self.config.service_cmd_executable) self.run_subprocess(self.config.service_cmd_executable)
@@ -316,8 +316,8 @@ WantedBy=default.target
with open(full_service_file_path, 'w+') as servicefile: with open(full_service_file_path, 'w+') as servicefile:
servicefile.write(contain) servicefile.write(contain)
servicefile.close() servicefile.close()
print(f'Service file generated with current configuration') print('Service file generated with current configuration')
print(f'Running Defender IRC Service ...') print('Running IRC Service ...')
self.run_subprocess(self.config.service_cmd_daemon_reload) self.run_subprocess(self.config.service_cmd_daemon_reload)
self.run_subprocess(self.config.service_cmd_executable) self.run_subprocess(self.config.service_cmd_executable)

View File

@@ -8,9 +8,10 @@ import time
import traceback import traceback
from ssl import SSLSocket from ssl import SSLSocket
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Union from typing import Optional, Union
from core.loader import Loader from core.loader import Loader
from core.classes.protocol import Protocol from core.classes.protocol import Protocol
from core.classes.commands import Command
class Irc: class Irc:
_instance = None _instance = None
@@ -30,6 +31,9 @@ class Irc:
# Load the configuration # Load the configuration
self.Config = self.Loader.Config self.Config = self.Loader.Config
# Load Main utils functions
self.Utils = self.Loader.Utils
# Date et heure de la premiere connexion de Defender # Date et heure de la premiere connexion de Defender
self.defender_connexion_datetime = self.Config.DEFENDER_CONNEXION_DATETIME self.defender_connexion_datetime = self.Config.DEFENDER_CONNEXION_DATETIME
@@ -50,7 +54,7 @@ class Irc:
self.Base = self.Loader.Base self.Base = self.Loader.Base
# Logger # Logger
self.Logs = self.Loader.Base.logs self.Logs = self.Loader.Logs
# Get Settings. # Get Settings.
self.Settings = self.Base.Settings self.Settings = self.Base.Settings
@@ -67,9 +71,6 @@ class Irc:
# Use Channel Instance # Use Channel Instance
self.Channel = self.Loader.Channel self.Channel = self.Loader.Channel
# Use Clones Instance
self.Clone = self.Loader.Clone
# Use Reputation Instance # Use Reputation Instance
self.Reputation = self.Loader.Reputation self.Reputation = self.Loader.Reputation
@@ -83,7 +84,11 @@ class Irc:
self.first_connexion_ip: str = None self.first_connexion_ip: str = None
# Define the dict that will contain all loaded modules # Define the dict that will contain all loaded modules
self.loaded_classes:dict[str, 'Irc'] = {} # Definir la variable qui contiendra la liste modules chargés self.loaded_classes:dict[str, 'Irc'] = {}
# Load Commands Utils
self.Commands = self.Loader.Commands
"""Command utils"""
# Global full module commands that contains level, module name, commands and description # Global full module commands that contains level, module name, commands and description
self.module_commands: dict[int, dict[str, dict[str, str]]] = {} self.module_commands: dict[int, dict[str, dict[str, str]]] = {}
@@ -122,7 +127,7 @@ class Irc:
# Define the IrcSocket object # Define the IrcSocket object
self.IrcSocket:Union[socket.socket, SSLSocket] = None self.IrcSocket: Union[socket.socket, SSLSocket] = None
self.__create_table() self.__create_table()
self.Base.create_thread(func=self.heartbeat, func_args=(self.beat, )) self.Base.create_thread(func=self.heartbeat, func_args=(self.beat, ))
@@ -140,6 +145,7 @@ class Irc:
self.init_service_user() self.init_service_user()
self.__create_socket() self.__create_socket()
self.__connect_to_irc(ircInstance) self.__connect_to_irc(ircInstance)
except AssertionError as ae: except AssertionError as ae:
self.Logs.critical(f'Assertion error: {ae}') self.Logs.critical(f'Assertion error: {ae}')
@@ -192,6 +198,7 @@ class Irc:
self.Logs.critical(f"AttributeError: {ae} - {soc.fileno()}") self.Logs.critical(f"AttributeError: {ae} - {soc.fileno()}")
def __ssl_context(self) -> ssl.SSLContext: def __ssl_context(self) -> ssl.SSLContext:
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.check_hostname = False ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE ctx.verify_mode = ssl.CERT_NONE
@@ -209,7 +216,7 @@ class Irc:
protocol=self.Config.SERVEUR_PROTOCOL, protocol=self.Config.SERVEUR_PROTOCOL,
ircInstance=self.ircObject ircInstance=self.ircObject
).Protocol ).Protocol
self.Protocol.link() # Etablir le link en fonction du protocol choisi self.Protocol.send_link() # Etablir le link en fonction du protocol choisi
self.signal = True # Une variable pour initier la boucle infinie self.signal = True # Une variable pour initier la boucle infinie
self.__join_saved_channels() # Join existing channels self.__join_saved_channels() # Join existing channels
self.load_existing_modules() # Charger les modules existant dans la base de données self.load_existing_modules() # Charger les modules existant dans la base de données
@@ -226,14 +233,15 @@ class Irc:
self.Logs.warning('--* Waiting for socket to close ...') self.Logs.warning('--* Waiting for socket to close ...')
# Reload configuration # Reload configuration
self.Loader.Logs = self.Loader.LoggingModule.ServiceLogging().get_logger()
self.Logs.debug('Reloading configuration') self.Logs.debug('Reloading configuration')
self.Config = self.Loader.ConfModule.Configuration().ConfigObject self.Config = self.Loader.ConfModule.Configuration(self.Logs).ConfigObject
self.Base = self.Loader.BaseModule.Base(self.Config, self.Settings) self.Base = self.Loader.BaseModule.Base(self.Loader)
self.Protocol = Protocol(self.Config.SERVEUR_PROTOCOL, ircInstance).Protocol self.Protocol = Protocol(self.Config.SERVEUR_PROTOCOL, ircInstance).Protocol
self.init_service_user() self.init_service_user()
self.__create_socket() self.__create_socket()
self.Protocol.link() self.Protocol.send_link()
self.__join_saved_channels() self.__join_saved_channels()
self.load_existing_modules() self.load_existing_modules()
self.Config.DEFENDER_RESTART = 0 self.Config.DEFENDER_RESTART = 0
@@ -295,7 +303,7 @@ class Irc:
if result_query: if result_query:
for chan_name in result_query: for chan_name in result_query:
chan = chan_name[0] chan = chan_name[0]
self.Protocol.sjoin(channel=chan) self.Protocol.send_sjoin(channel=chan)
def send_response(self, responses:list[bytes]) -> None: def send_response(self, responses:list[bytes]) -> None:
try: try:
@@ -341,12 +349,43 @@ class Irc:
self.module_commands.setdefault(level, {}).setdefault(module_name, {}).update({command_name: command_description}) self.module_commands.setdefault(level, {}).setdefault(module_name, {}).update({command_name: command_description})
self.module_commands_list.append(command_name) self.module_commands_list.append(command_name)
# Build Model.
self.Commands.build(self.Loader.Definition.MCommand(module_name, command_name, command_description, level))
return None return None
def generate_help_menu(self, nickname: str) -> None: def generate_help_menu(self, nickname: str, module: Optional[str] = None) -> None:
# Check if the nickname is an admin # Check if the nickname is an admin
admin_obj = self.Admin.get_Admin(nickname) 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
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 *****************")
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):
if module is None or cmd.module_name.lower() == module.lower():
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}"
)
return None
def generate_help_menu_bakcup(self, nickname: str) -> None:
# Check if the nickname is an admin
admin_obj = self.Admin.get_admin(nickname)
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME
color_bold = self.Config.COLORS.bold color_bold = self.Config.COLORS.bold
color_nogc = self.Config.COLORS.nogc color_nogc = self.Config.COLORS.nogc
@@ -380,7 +419,7 @@ class Irc:
def is_cmd_allowed(self, nickname: str, command_name: str) -> bool: def is_cmd_allowed(self, nickname: str, command_name: str) -> bool:
admin_obj = self.Admin.get_Admin(nickname) admin_obj = self.Admin.get_admin(nickname)
current_level = 0 current_level = 0
if admin_obj is not None: if admin_obj is not None:
@@ -494,16 +533,15 @@ class Irc:
try: try:
# module_name : mod_voice # module_name : mod_voice
module_name = module_name.lower() module_name = module_name.lower()
class_name = module_name.split('_')[1].capitalize() # ==> Voice module_folder = module_name.split('_')[1].lower() # ==> voice
class_name = module_name.split('_')[1].capitalize() # ==> Voice
# print(self.loaded_classes) # Check if the module is already loaded.
if 'mods.' + module_folder + '.' + module_name in sys.modules:
# Si le module est déja chargé self.Logs.debug(f"Module [{module_folder}.{module_name}] already loaded!")
if 'mods.' + module_name in sys.modules:
self.Logs.info("Module déja chargé ...")
self.Logs.info('module name = ' + module_name)
if class_name in self.loaded_classes: if class_name in self.loaded_classes:
# Si le module existe dans la variable globale retourne False # Si le module existe dans la variable globale retourne False
self.Logs.debug(f"Module [{module_folder}.{module_name}] exist in the local variable!")
self.Protocol.send_priv_msg( self.Protocol.send_priv_msg(
nick_from=self.Config.SERVICE_NICKNAME, 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}", msg=f"Le module {module_name} est déja chargé ! si vous souhaiter le recharge tapez {self.Config.SERVICE_PREFIX}reload {module_name}",
@@ -511,7 +549,7 @@ class Irc:
) )
return False return False
the_module = sys.modules['mods.' + module_name] the_module = sys.modules[f'mods.{module_folder}.{module_name}']
importlib.reload(the_module) importlib.reload(the_module)
my_class = getattr(the_module, class_name, None) my_class = getattr(the_module, class_name, None)
new_instance = my_class(self.ircObject) new_instance = my_class(self.ircObject)
@@ -526,11 +564,11 @@ class Irc:
msg=f"Module {module_name} chargé", msg=f"Module {module_name} chargé",
channel=self.Config.SERVICE_CHANLOG channel=self.Config.SERVICE_CHANLOG
) )
return False self.Logs.debug(f"Module [{module_folder}.{module_name}] reloaded!")
return True
# Charger le module # Charger le module
loaded_module = importlib.import_module(f"mods.{module_name}") 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 my_class = getattr(loaded_module, class_name, None) # Récuperer le nom de classe
create_instance_of_the_class = my_class(self.ircObject) # Créer une nouvelle instance de la classe create_instance_of_the_class = my_class(self.ircObject) # Créer une nouvelle instance de la classe
@@ -557,7 +595,7 @@ class Irc:
channel=self.Config.SERVICE_CHANLOG channel=self.Config.SERVICE_CHANLOG
) )
self.Logs.info(f"Module {class_name} has been loaded") self.Logs.debug(f"Module {class_name} has been loaded")
return True return True
@@ -565,18 +603,21 @@ class Irc:
self.Logs.error(f"MODULE_NOT_FOUND: {moduleNotFound}") self.Logs.error(f"MODULE_NOT_FOUND: {moduleNotFound}")
self.Protocol.send_priv_msg( self.Protocol.send_priv_msg(
nick_from=self.Config.SERVICE_NICKNAME, nick_from=self.Config.SERVICE_NICKNAME,
msg=f"[ {self.Config.COLORS.red}MODULE_NOT_FOUND{self.Config.COLORS.black} ]: {moduleNotFound}", msg=f"[ {self.Config.COLORS.red}MODULE ERROR{self.Config.COLORS.black} ]: {moduleNotFound}",
channel=self.Config.SERVICE_CHANLOG channel=self.Config.SERVICE_CHANLOG
) )
self.Base.db_delete_module(module_name) self.Base.db_delete_module(module_name)
return False
except Exception as err: except Exception as err:
self.Logs.error(f"Something went wrong with a module you want to load : {err}") self.Logs.error(f"[LOAD MODULE ERROR]: {err}", exc_info=True)
self.Protocol.send_priv_msg( self.Protocol.send_priv_msg(
nick_from=self.Config.SERVICE_NICKNAME, nick_from=self.Config.SERVICE_NICKNAME,
msg=f"[ {self.Config.COLORS.red}ERROR{self.Config.COLORS.black} ]: {err}", msg=f"[ {self.Config.COLORS.red}MODULE ERROR{self.Config.COLORS.black} ]: {err}",
channel=self.Config.SERVICE_CHANLOG channel=self.Config.SERVICE_CHANLOG
) )
self.Base.db_delete_module(module_name) self.Base.db_delete_module(module_name)
return False
def unload_module(self, mod_name: str) -> bool: def unload_module(self, mod_name: str) -> bool:
"""Unload a module """Unload a module
@@ -588,21 +629,28 @@ class Irc:
bool: True if success bool: True if success
""" """
try: try:
module_name = mod_name.lower() # Le nom du module. exemple: mod_defender # Le nom du module. exemple: mod_defender
class_name = module_name.split('_')[1].capitalize() # Nom de la class. exemple: Defender module_name = mod_name.lower()
module_folder = module_name.split('_')[1].lower() # ==> defender
class_name = module_name.split('_')[1].capitalize() # Nom de la class. exemple: Defender
if class_name in self.loaded_classes: if class_name in self.loaded_classes:
self.loaded_classes[class_name].unload() self.loaded_classes[class_name].unload()
del self.loaded_classes[class_name] del self.loaded_classes[class_name]
# Delete from the sys.
if sys.modules.get(f'{module_folder}.{module_name}'):
del sys.modules[f"{module_folder}.{module_name}"]
# Supprimer le module de la base de données # Supprimer le module de la base de données
self.Base.db_delete_module(module_name) self.Base.db_delete_module(module_name)
self.Protocol.send_priv_msg( self.Protocol.send_priv_msg(
nick_from=self.Config.SERVICE_NICKNAME, nick_from=self.Config.SERVICE_NICKNAME,
msg=f"Module {module_name} supprimé", msg=f"[ MODULE INFO ] Module {module_name} has been deleted!",
channel=self.Config.SERVICE_CHANLOG channel=self.Config.SERVICE_CHANLOG
) )
self.Logs.debug(f"[ MODULE ] {module_name} has been deleted!")
return True return True
except Exception as err: except Exception as err:
@@ -612,13 +660,17 @@ class Irc:
def reload_module(self, from_user: str, mod_name: str) -> bool: def reload_module(self, from_user: str, mod_name: str) -> bool:
try: try:
module_name = mod_name.lower() # ==> mod_defender module_name = mod_name.lower() # ==> mod_defender
module_folder = module_name.split('_')[1].lower() # ==> defender
class_name = module_name.split('_')[1].capitalize() # ==> Defender class_name = module_name.split('_')[1].capitalize() # ==> Defender
if 'mods.' + module_name in sys.modules: if f'mods.{module_folder}.{module_name}' in sys.modules:
self.Logs.info('Unload the module ...') self.Logs.info('Unload the module ...')
self.loaded_classes[class_name].unload() self.loaded_classes[class_name].unload()
self.Logs.info('Module Already Loaded ... reloading the module ...') self.Logs.info('Module Already Loaded ... reloading the module ...')
the_module = sys.modules['mods.' + module_name]
# Load dependencies
self.Base.reload_modules_with_dependencies(f'mods.{module_folder}')
the_module = sys.modules[f'mods.{module_folder}.{module_name}']
importlib.reload(the_module) importlib.reload(the_module)
# Supprimer la class déja instancier # Supprimer la class déja instancier
@@ -681,7 +733,7 @@ class Irc:
if self.User.get_User(uid) is None: if self.User.get_User(uid) is None:
return None return None
getUser = self.User.get_User_AsDict(uid) getUser = self.User.get_user_asdict(uid)
level = int(level) level = int(level)
@@ -696,7 +748,7 @@ class Irc:
def delete_db_admin(self, uid:str) -> None: def delete_db_admin(self, uid:str) -> None:
if self.Admin.get_Admin(uid) is None: if self.Admin.get_admin(uid) is None:
return None return None
if not self.Admin.delete(uid): if not self.Admin.delete(uid):
@@ -732,7 +784,7 @@ class Irc:
hostname = get_user.hostname hostname = get_user.hostname
vhost = get_user.vhost vhost = get_user.vhost
spassword = self.Base.crypt_password(password) spassword = self.Loader.Utils.hash_password(password)
mes_donnees = {'admin': nickname} mes_donnees = {'admin': nickname}
query_search_user = f"SELECT id FROM {self.Config.TABLE_ADMIN} WHERE user=:admin" query_search_user = f"SELECT id FROM {self.Config.TABLE_ADMIN} WHERE user=:admin"
@@ -741,7 +793,7 @@ class Irc:
# On verifie si le user exist dans la base # On verifie si le user exist dans la base
if not exist_user: if not exist_user:
mes_donnees = {'datetime': self.Base.get_datetime(), 'user': nickname, 'password': spassword, 'hostname': hostname, 'vhost': vhost, 'level': level} mes_donnees = {'datetime': self.Utils.get_sdatetime(), 'user': nickname, 'password': spassword, 'hostname': hostname, 'vhost': vhost, 'level': level}
self.Base.db_execute_query(f'''INSERT INTO {self.Config.TABLE_ADMIN} self.Base.db_execute_query(f'''INSERT INTO {self.Config.TABLE_ADMIN}
(createdOn, user, password, hostname, vhost, level) VALUES (createdOn, user, password, hostname, vhost, level) VALUES
(:datetime, :user, :password, :hostname, :vhost, :level) (:datetime, :user, :password, :hostname, :vhost, :level)
@@ -763,7 +815,7 @@ class Irc:
log_msg (str): the message to log log_msg (str): the message to log
""" """
try: try:
mes_donnees = {'datetime': self.Base.get_datetime(), 'server_msg': log_msg} mes_donnees = {'datetime': self.Utils.get_sdatetime(), 'server_msg': log_msg}
self.Base.db_execute_query(f'INSERT INTO {self.Config.TABLE_LOG} (datetime, server_msg) VALUES (:datetime, :server_msg)', mes_donnees) self.Base.db_execute_query(f'INSERT INTO {self.Config.TABLE_LOG} (datetime, server_msg) VALUES (:datetime, :server_msg)', mes_donnees)
return None return None
@@ -813,16 +865,13 @@ class Irc:
case 'PING': case 'PING':
self.Protocol.on_server_ping(serverMsg=original_response) self.Protocol.on_server_ping(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
return None return None
case 'SJOIN': case 'SJOIN':
self.Protocol.on_sjoin(serverMsg=original_response) self.Protocol.on_sjoin(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'EOS': case 'EOS':
self.Protocol.on_eos(serverMsg=original_response) self.Protocol.on_eos(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'UID': case 'UID':
try: try:
@@ -831,48 +880,37 @@ class Irc:
for classe_name, classe_object in self.loaded_classes.items(): for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(original_response) classe_object.cmd(original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
except Exception as err: except Exception as err:
self.Logs.error(f'General Error: {err}') self.Logs.error(f'General Error: {err}')
case 'QUIT': case 'QUIT':
self.Protocol.on_quit(serverMsg=original_response) self.Protocol.on_quit(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'PROTOCTL': case 'PROTOCTL':
self.Protocol.on_protoctl(serverMsg=original_response) self.Protocol.on_protoctl(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'SVS2MODE': case 'SVS2MODE':
# >> [':00BAAAAAG', 'SVS2MODE', '001U01R03', '-r'] # >> [':00BAAAAAG', 'SVS2MODE', '001U01R03', '-r']
self.Protocol.on_svs2mode(serverMsg=original_response) self.Protocol.on_svs2mode(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'SQUIT': case 'SQUIT':
self.Protocol.on_squit(serverMsg=original_response) self.Protocol.on_squit(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'PART': case 'PART':
self.Protocol.on_part(serverMsg=parsed_protocol) self.Protocol.on_part(serverMsg=parsed_protocol)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'VERSION': case 'VERSION':
self.Protocol.on_version_msg(serverMsg=original_response) self.Protocol.on_version_msg(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'UMODE2': case 'UMODE2':
# [':adator_', 'UMODE2', '-i'] # [':adator_', 'UMODE2', '-i']
self.Protocol.on_umode2(serverMsg=original_response) self.Protocol.on_umode2(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'NICK': case 'NICK':
self.Protocol.on_nick(serverMsg=original_response) self.Protocol.on_nick(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'REPUTATION': case 'REPUTATION':
self.Protocol.on_reputation(serverMsg=original_response) self.Protocol.on_reputation(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'SLOG': # TODO case 'SLOG': # TODO
self.Logs.debug(f"** handle {parsed_protocol}") self.Logs.debug(f"** handle {parsed_protocol}")
@@ -882,7 +920,6 @@ class Irc:
case 'PRIVMSG': case 'PRIVMSG':
self.Protocol.on_privmsg(serverMsg=original_response) self.Protocol.on_privmsg(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'PONG': # TODO case 'PONG': # TODO
self.Logs.debug(f"** handle {parsed_protocol}") self.Logs.debug(f"** handle {parsed_protocol}")
@@ -910,10 +947,9 @@ class Irc:
classe_object.cmd(original_response) classe_object.cmd(original_response)
except IndexError as ie: except IndexError as ie:
self.Logs.error(f"{ie} / {original_response} / length {str(len(original_response))}") self.Logs.error(f"IndexError: {ie}")
except Exception as err: except Exception as err:
self.Logs.error(f"General Error: {err}") self.Logs.error(f"General Error: {err}", exc_info=True)
self.Logs.error(f"General Error: {traceback.format_exc()}")
def hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None: def hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None:
"""Create """Create
@@ -1081,7 +1117,7 @@ class Irc:
return False return False
if not user_to_log is None: if not user_to_log is None:
mes_donnees = {'user': user_to_log, 'password': self.Base.crypt_password(password)} mes_donnees = {'user': user_to_log, 'password': self.Loader.Utils.hash_password(password)}
query = f"SELECT id, level FROM {self.Config.TABLE_ADMIN} WHERE user = :user AND password = :password" query = f"SELECT id, level FROM {self.Config.TABLE_ADMIN} WHERE user = :user AND password = :password"
result = self.Base.db_execute_query(query, mes_donnees) result = self.Base.db_execute_query(query, mes_donnees)
user_from_db = result.fetchone() user_from_db = result.fetchone()
@@ -1134,9 +1170,9 @@ class Irc:
return None return None
user_to_edit = cmd[1] user_to_edit = cmd[1]
user_password = self.Base.crypt_password(cmd[2]) user_password = self.Loader.Utils.hash_password(cmd[2])
get_admin = self.Admin.get_Admin(fromuser) get_admin = self.Admin.get_admin(fromuser)
if get_admin is None: if get_admin is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"This user {fromuser} has no Admin access") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"This user {fromuser} has no Admin access")
return None return None
@@ -1200,7 +1236,7 @@ class Irc:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{self.Config.SERVICE_PREFIX}delaccess [USER] [CONFIRMUSER]") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{self.Config.SERVICE_PREFIX}delaccess [USER] [CONFIRMUSER]")
return None return None
get_admin = self.Admin.get_Admin(fromuser) get_admin = self.Admin.get_admin(fromuser)
if get_admin is None: if get_admin is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"This user {fromuser} has no admin access") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"This user {fromuser} has no admin access")
@@ -1273,9 +1309,9 @@ class Irc:
# If the account doesn't exist then insert into database # If the account doesn't exist then insert into database
data_to_record = { data_to_record = {
'createdOn': self.Base.get_datetime(), 'account': fromuser, 'createdOn': self.Utils.get_sdatetime(), 'account': fromuser,
'nickname': user_obj.nickname, 'hostname': user_obj.hostname, 'vhost': user_obj.vhost, 'realname': user_obj.realname, 'email': email, 'nickname': user_obj.nickname, 'hostname': user_obj.hostname, 'vhost': user_obj.vhost, 'realname': user_obj.realname, 'email': email,
'password': self.Base.crypt_password(password=password), 'level': 0 'password': self.Loader.Utils.hash_password(password=password), 'level': 0
} }
insert_to_db = self.Base.db_execute_query(f""" insert_to_db = self.Base.db_execute_query(f"""
@@ -1310,7 +1346,7 @@ class Irc:
return None return None
account = str(cmd[1]) # account account = str(cmd[1]) # account
encrypted_password = self.Base.crypt_password(cmd[2]) encrypted_password = self.Loader.Utils.hash_password(cmd[2])
user_obj = self.User.get_User(fromuser) user_obj = self.User.get_User(fromuser)
client_obj = self.Client.get_Client(user_obj.uid) client_obj = self.Client.get_Client(user_obj.uid)
@@ -1382,9 +1418,10 @@ class Irc:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} <account>") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} <account>")
case 'help': case 'help':
# Syntax. !help [module_name]
self.generate_help_menu(nickname=fromuser) module_name = str(cmd[1]) if len(cmd) == 2 else None
self.generate_help_menu(nickname=fromuser, module=module_name)
case 'load': case 'load':
try: try:
# Load a module ex: .load mod_defender # Load a module ex: .load mod_defender
@@ -1434,7 +1471,7 @@ class Irc:
nick_to=fromuser, nick_to=fromuser,
msg=f"Arrêt du service {dnickname}" msg=f"Arrêt du service {dnickname}"
) )
self.Protocol.squit(server_id=self.Config.SERVEUR_ID, server_link=self.Config.SERVEUR_LINK, reason=final_reason) 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.Logs.info(f'Arrêt du server {dnickname}')
self.Config.DEFENDER_RESTART = 0 self.Config.DEFENDER_RESTART = 0
self.signal = False self.signal = False
@@ -1459,18 +1496,23 @@ class Irc:
self.User.UID_DB.clear() # Clear User Object self.User.UID_DB.clear() # Clear User Object
self.Channel.UID_CHANNEL_DB.clear() # Clear Channel Object self.Channel.UID_CHANNEL_DB.clear() # Clear Channel Object
self.Base.delete_logger(self.Config.LOGGING_NAME) self.Base.garbage_collector_thread()
self.Protocol.squit(server_id=self.Config.SERVEUR_ID, server_link=self.Config.SERVEUR_LINK, reason=final_reason) self.Protocol.send_squit(server_id=self.Config.SERVEUR_ID, server_link=self.Config.SERVEUR_LINK, reason=final_reason)
self.Logs.info(f'Redémarrage du server {dnickname}') self.Logs.info(f'Redémarrage du server {dnickname}')
self.loaded_classes.clear() self.loaded_classes.clear()
self.Config.DEFENDER_RESTART = 1 # Set restart status to 1 saying that the service will 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 self.Config.DEFENDER_INIT = 1 # set init to 1 saying that the service will be re initiated
self.Loader.ServiceLogging.remove_logger()
case 'rehash': case 'rehash':
self.Loader.ServiceLogging.remove_logger()
self.Loader.ServiceLogging = self.Loader.LoggingModule.ServiceLogging()
self.Loader.Logs = self.Loader.ServiceLogging.get_logger()
need_a_restart = ["SERVEUR_ID"] need_a_restart = ["SERVEUR_ID"]
restart_flag = False restart_flag = False
Config_bakcup = self.Config.__dict__.copy() Config_bakcup = self.Config.to_dict().copy()
serveur_id = self.Config.SERVEUR_ID serveur_id = self.Config.SERVEUR_ID
service_nickname = self.Config.SERVICE_NICKNAME service_nickname = self.Config.SERVICE_NICKNAME
hsid = self.Config.HSID hsid = self.Config.HSID
@@ -1490,7 +1532,7 @@ class Irc:
importlib.reload(mod_definition) importlib.reload(mod_definition)
importlib.reload(mod_config) importlib.reload(mod_config)
self.Config = self.Loader.ConfModule.Configuration().ConfigObject self.Config = self.Loader.ConfModule.Configuration(self.Loader.Logs).ConfigObject
self.Config.HSID = hsid self.Config.HSID = hsid
self.Config.DEFENDER_INIT = defender_init self.Config.DEFENDER_INIT = defender_init
self.Config.DEFENDER_RESTART = defender_restart self.Config.DEFENDER_RESTART = defender_restart
@@ -1500,7 +1542,7 @@ class Irc:
importlib.reload(mod_base) importlib.reload(mod_base)
conf_bkp_dict: dict = Config_bakcup conf_bkp_dict: dict = Config_bakcup
config_dict: dict = self.Config.__dict__ config_dict: dict = self.Config.to_dict()
for key, value in conf_bkp_dict.items(): for key, value in conf_bkp_dict.items():
if config_dict[key] != value and key != 'COLORS': if config_dict[key] != value and key != 'COLORS':
@@ -1513,14 +1555,13 @@ class Irc:
restart_flag = True restart_flag = True
if service_nickname != self.Config.SERVICE_NICKNAME: if service_nickname != self.Config.SERVICE_NICKNAME:
self.Protocol.set_nick(self.Config.SERVICE_NICKNAME) self.Protocol.send_set_nick(self.Config.SERVICE_NICKNAME)
if restart_flag: if restart_flag:
self.Config.SERVEUR_ID = serveur_id self.Config.SERVEUR_ID = serveur_id
self.Protocol.send_priv_msg(nick_from=self.Config.SERVICE_NICKNAME, msg='You need to restart defender !', channel=self.Config.SERVICE_CHANLOG) self.Protocol.send_priv_msg(nick_from=self.Config.SERVICE_NICKNAME, msg='You need to restart defender !', channel=self.Config.SERVICE_CHANLOG)
self.Base.delete_logger(self.Config.LOGGING_NAME) self.Base = self.Loader.BaseModule.Base(self.Loader)
self.Base = self.Loader.BaseModule.Base(self.Config, self.Settings)
importlib.reload(mod_unreal6) importlib.reload(mod_unreal6)
importlib.reload(mod_protocol) importlib.reload(mod_protocol)

View File

@@ -1,34 +1,45 @@
from core.classes import user, admin, client, channel, clone, reputation, settings from logging import Logger
from core.classes import user, admin, client, channel, reputation, settings, commands
import core.logs as logs
import core.definition as df import core.definition as df
import core.base as baseModule import core.utils as utils
import core.classes.config as confModule import core.base as base_module
import core.classes.config as conf_module
class Loader: class Loader:
def __init__(self): def __init__(self):
# Load Modules # Load Main Modules
self.Definition: df = df self.Definition: df = df
self.ConfModule: confModule = confModule self.ConfModule: conf_module = conf_module
self.BaseModule: baseModule = baseModule self.BaseModule: base_module = base_module
self.Utils: utils = utils
self.LoggingModule: logs = logs
# Load Classes # Load Classes
self.Settings: settings = settings.Settings() self.ServiceLogging: logs.ServiceLogging = logs.ServiceLogging()
self.Config: df.MConfig = self.ConfModule.Configuration().ConfigObject self.Logs: Logger = self.ServiceLogging.get_logger()
self.Base: baseModule.Base = self.BaseModule.Base(self.Config, self.Settings) self.Settings: settings.Settings = settings.Settings()
self.User: user.User = user.User(self.Base) self.Config: df.MConfig = self.ConfModule.Configuration(self.Logs).ConfigObject
self.Client: client.Client = client.Client(self.Base) self.Base: base_module.Base = self.BaseModule.Base(self)
self.Admin: admin.Admin = admin.Admin(self.Base) self.User: user.User = user.User(self)
self.Channel: channel.Channel = channel.Channel(self.Base) self.Client: client.Client = client.Client(self)
self.Clone: clone.Clone = clone.Clone(self.Base) self.Admin: admin.Admin = admin.Admin(self)
self.Reputation: reputation.Reputation = reputation.Reputation(self.Base) self.Channel: channel.Channel = channel.Channel(self)
self.Reputation: reputation.Reputation = reputation.Reputation(self)
self.Commands: commands.Command = commands.Command(self)

112
core/logs.py Normal file
View File

@@ -0,0 +1,112 @@
import logging
from os import path, makedirs, sep
from typing import Optional
class ServiceLogging:
def __init__(self, loggin_name: str = "defender"):
"""Create the Logging object
"""
self.OS_SEP = sep
self.LOGGING_NAME = loggin_name
self.DEBUG_LEVEL, self.DEBUG_FILE_LEVEL, self.DEBUG_STDOUT_LEVEL = (10, 10, 10)
self.SERVER_PREFIX = None
self.LOGGING_CONSOLE = True
self.LOG_FILTERS: list[str] = ['PING', f":{self.SERVER_PREFIX}auth", "['PASS'"]
self.file_handler = None
self.stdout_handler = None
self.logs: logging.Logger = self.start_log_system()
def get_logger(self) -> logging.Logger:
logs_obj: logging.Logger = self.logs
return logs_obj
def remove_logger(self, logger_name: Optional[str] = None) -> None:
if logger_name is None:
logger_name = self.LOGGING_NAME
# Récupérer le logger
logger = logging.getLogger(logger_name)
# Retirer tous les gestionnaires du logger et les fermer
for handler in logger.handlers[:]: # Utiliser une copie de la liste
# print(handler)
logger.removeHandler(handler)
handler.close()
# Supprimer le logger du dictionnaire global
logging.Logger.manager.loggerDict.pop(logger_name, None)
return None
def start_log_system(self) -> logging.Logger:
os_sep = self.OS_SEP
logging_name = self.LOGGING_NAME
debug_level = self.DEBUG_LEVEL
debug_file_level = self.DEBUG_FILE_LEVEL
debug_stdout_level = self.DEBUG_STDOUT_LEVEL
# Create folder if not available
logs_directory = f'logs{os_sep}'
if not path.exists(f'{logs_directory}'):
makedirs(logs_directory)
# Init logs object
logs = logging.getLogger(logging_name)
logs.setLevel(debug_level)
# Add Handlers
self.file_handler = logging.FileHandler(f'logs{os_sep}{logging_name}.log',encoding='UTF-8')
self.file_handler.setLevel(debug_file_level)
self.stdout_handler = logging.StreamHandler()
self.stdout_handler.setLevel(debug_stdout_level)
# Define log format
formatter = logging.Formatter(
fmt='%(asctime)s - %(levelname)s - %(message)s (%(filename)s:%(funcName)s:%(lineno)d)',
datefmt='%Y-%m-%d %H:%M:%S'
)
# Apply log format
self.file_handler.setFormatter(formatter)
self.stdout_handler.setFormatter(formatter)
# Add handler to logs
logs.addHandler(self.file_handler)
logs.addHandler(self.stdout_handler)
# Apply the filter
logs.addFilter(self.replace_filter)
logs.info(f'#################### STARTING {self.LOGGING_NAME} ####################')
return logs
def set_stdout_handler_level(self, level: int) -> None:
self.stdout_handler.setLevel(level)
def set_file_handler_level(self, level: int) -> None:
self.file_handler.setLevel(level)
def replace_filter(self, record: logging.LogRecord) -> bool:
response = True
filter: list[str] = ['PING', f":{self.SERVER_PREFIX}auth", "['PASS'"]
# record.msg = record.getMessage().replace("PING", "[REDACTED]")
# if self.LOGGING_CONSOLE:
# print(record.getMessage())
for f in filter:
if f in record.getMessage():
response = False
return response # Retourne True pour permettre l'affichage du message

View File

@@ -1,25 +1,29 @@
from typing import Literal, Union '''
from datetime import datetime Main utils library.
'''
import gc
from pathlib import Path
from re import sub
from typing import Literal, Optional, Any
from datetime import datetime, timedelta, timezone
from time import time from time import time
from random import choice from random import choice
from hashlib import md5, sha3_512 from hashlib import md5, sha3_512
def convert_to_int(value: any) -> Union[int, None]: def convert_to_int(value: Any) -> Optional[int]:
"""Convert a value to int """Convert a value to int
Args: Args:
value (any): Value to convert to int if possible value (Any): Value to convert to int if possible
Returns: Returns:
Union[int, None]: Return the int value or None if not possible int: Return the int value or None if not possible
""" """
try: try:
value_to_int = int(value) value_to_int = int(value)
return value_to_int return value_to_int
except ValueError: except ValueError:
return None return None
except Exception:
return None
def get_unixtime() -> int: def get_unixtime() -> int:
"""Cette fonction retourne un UNIXTIME de type 12365456 """Cette fonction retourne un UNIXTIME de type 12365456
@@ -27,9 +31,12 @@ def get_unixtime() -> int:
Returns: Returns:
int: Current time in seconds since the Epoch (int) 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())
return int(time()) return int(time())
def get_datetime() -> str: def get_sdatetime() -> str:
"""Retourne une date au format string (24-12-2023 20:50:59) """Retourne une date au format string (24-12-2023 20:50:59)
Returns: Returns:
@@ -38,6 +45,31 @@ def get_datetime() -> str:
currentdate = datetime.now().strftime('%d-%m-%Y %H:%M:%S') currentdate = datetime.now().strftime('%d-%m-%Y %H:%M:%S')
return currentdate return currentdate
def get_datetime() -> datetime:
"""
Return the current datetime in a datetime object
"""
return datetime.now()
def run_python_garbage_collector() -> int:
"""Run Python garbage collector
Returns:
int: The number of unreachable objects is returned.
"""
return gc.collect()
def get_number_gc_objects(your_object_to_count: Optional[Any] = None) -> int:
"""Get The number of objects tracked by the collector (excluding the list returned).
Returns:
int: Number of tracked objects by the collector
"""
if your_object_to_count is None:
return len(gc.get_objects())
return sum(1 for obj in gc.get_objects() if isinstance(obj, your_object_to_count))
def generate_random_string(lenght: int) -> str: def generate_random_string(lenght: int) -> str:
"""Retourn une chaîne aléatoire en fonction de la longueur spécifiée. """Retourn une chaîne aléatoire en fonction de la longueur spécifiée.
@@ -49,15 +81,15 @@ def generate_random_string(lenght: int) -> str:
return randomize return randomize
def hash(password: str, algorithm: Literal["md5, sha3_512"] = 'md5') -> str: def hash_password(password: str, algorithm: Literal["md5, sha3_512"] = 'md5') -> str:
"""Retourne un mot de passe chiffré en fonction de l'algorithme utilisé """Return the crypted password following the selected algorithm
Args: Args:
password (str): Le password en clair password (str): The plain text password
algorithm (str): L'algorithm a utilisé algorithm (str): The algorithm to use
Returns: Returns:
str: Le password haché str: The crypted password, default md5
""" """
match algorithm: match algorithm:
@@ -72,3 +104,30 @@ def hash(password: str, algorithm: Literal["md5, sha3_512"] = 'md5') -> str:
case _: case _:
password = md5(password.encode()).hexdigest() password = md5(password.encode()).hexdigest()
return password return password
def get_all_modules() -> list[str]:
"""Get list of all main modules
using this pattern mod_*.py
Returns:
list[str]: List of module names.
"""
base_path = Path('mods')
return [file.name.replace('.py', '') for file in base_path.rglob('mod_*.py')]
def clean_uid(uid: str) -> Optional[str]:
"""Clean UID by removing @ / % / + / ~ / * / :
Args:
uid (str): The UID to clean
Returns:
str: Clean UID without any sign
"""
if uid is None:
return None
pattern = fr'[:|@|%|\+|~|\*]*'
parsed_UID = sub(pattern, '', uid)
return parsed_UID

View File

@@ -1,10 +1,11 @@
from core import installation from core import installation
############################################# #############################################
# @Version : 6 # # @Version : 6.2 #
# Requierements : # # Requierements : #
# Python3.10 or higher # # Python3.10 or higher #
# SQLAlchemy, requests, psutil # # SQLAlchemy, requests, psutil #
# unrealircd-rpc-py #
# UnrealIRCD 6.2.2 or higher # # UnrealIRCD 6.2.2 or higher #
############################################# #############################################
@@ -14,7 +15,6 @@ try:
from core.loader import Loader from core.loader import Loader
from core.irc import Irc from core.irc import Irc
# loader = Loader()
ircInstance = Irc(Loader()) ircInstance = Irc(Loader())
ircInstance.init_irc(ircInstance) ircInstance.init_irc(ircInstance)

163
mods/clone/clone_manager.py Normal file
View File

@@ -0,0 +1,163 @@
from typing import Optional, TYPE_CHECKING
from mods.clone.schemas import MClone
if TYPE_CHECKING:
from mods.clone.mod_clone import Clone
class CloneManager:
UID_CLONE_DB: list[MClone] = []
def __init__(self, uplink: 'Clone'):
self.Logs = uplink.Logs
def insert(self, new_clone_object: MClone) -> bool:
"""Create new Clone object
Args:
new_clone_object (MClone): New Clone object
Returns:
bool: True if inserted
"""
if new_clone_object is None:
self.Logs.debug('New Clone object must not be None')
return False
for record in self.UID_CLONE_DB:
if record.nickname == new_clone_object.nickname or record.uid == new_clone_object.uid:
# If the user exist then return False and do not go further
self.Logs.debug(f'Nickname/UID {record.nickname}/{record.uid} already exist')
return False
self.UID_CLONE_DB.append(new_clone_object)
self.Logs.debug(f'New Clone object created: {new_clone_object}')
return True
def delete(self, uidornickname: str) -> bool:
"""Delete the Clone Object starting from the nickname or the UID
Args:
uidornickname (str): UID or nickname of the clone
Returns:
bool: True if deleted
"""
clone_obj = self.get_clone(uidornickname=uidornickname)
if clone_obj is None:
return False
self.UID_CLONE_DB.remove(clone_obj)
return True
def nickname_exists(self, nickname: str) -> bool:
"""Check if the nickname exist
Args:
nickname (str): Nickname of the clone
Returns:
bool: True if the nickname exist
"""
clone = self.get_clone(nickname)
if isinstance(clone, MClone):
return True
return False
def uid_exists(self, uid: str) -> bool:
"""Check if the nickname exist
Args:
uid (str): uid of the clone
Returns:
bool: True if the nickname exist
"""
clone = self.get_clone(uid)
if isinstance(clone, MClone):
return True
return False
def group_exists(self, groupname: str) -> bool:
"""Verify if a group exist
Args:
groupname (str): The group name
Returns:
bool: _description_
"""
for clone in self.UID_CLONE_DB:
if clone.group.strip().lower() == groupname.strip().lower():
return True
return False
def get_clone(self, uidornickname: str) -> Optional[MClone]:
"""Get MClone object or None
Args:
uidornickname (str): The UID or the Nickname
Returns:
Union[MClone, None]: Return MClone object or None
"""
for clone in self.UID_CLONE_DB:
if clone.uid == uidornickname:
return clone
if clone.nickname == uidornickname:
return clone
return None
def get_clones_from_groupname(self, groupname: str) -> list[MClone]:
"""Get list of clone objects by group name
Args:
groupname (str): The group name
Returns:
list[MClone]: List of clones in the group
"""
group_of_clone: list[MClone] = []
if self.group_exists(groupname):
for clone in self.UID_CLONE_DB:
if clone.group.strip().lower() == groupname.strip().lower():
group_of_clone.append(clone)
return group_of_clone
def get_uid(self, uidornickname: str) -> Optional[str]:
"""Get the UID of the clone starting from the UID or the Nickname
Args:
uidornickname (str): UID or Nickname
Returns:
str|None: Return the UID
"""
for record in self.UID_CLONE_DB:
if record.uid == uidornickname:
return record.uid
if record.nickname == uidornickname:
return record.uid
return None
def kill(self, nickname:str) -> bool:
response = False
for clone in self.UID_CLONE_DB:
if clone.nickname == nickname:
clone.alive = False # Kill the clone
response = True
return response

View File

@@ -1,46 +1,62 @@
from dataclasses import dataclass from typing import TYPE_CHECKING, Optional, Any
import random, faker, time, logging import mods.clone.utils as utils
from typing import TYPE_CHECKING import mods.clone.threads as thds
import mods.clone.schemas as schemas
from mods.clone.clone_manager import CloneManager
if TYPE_CHECKING: if TYPE_CHECKING:
from core.irc import Irc from core.irc import Irc
from faker import Faker
class Clone(): class Clone:
@dataclass def __init__(self, irc_instance: 'Irc') -> None:
class ModConfModel:
clone_nicknames: list[str]
def __init__(self, ircInstance: 'Irc') -> None:
# Module name (Mandatory) # Module name (Mandatory)
self.module_name = 'mod_' + str(self.__class__.__name__).lower() self.module_name = 'mod_' + str(self.__class__.__name__).lower()
# Add Irc Object to the module (Mandatory) # Add Irc Object to the module (Mandatory)
self.Irc = ircInstance self.Irc = irc_instance
# Add Irc Protocol Object to the module (Mandatory) # Add Irc Protocol Object to the module (Mandatory)
self.Protocol = ircInstance.Protocol self.Protocol = irc_instance.Protocol
# Add Global Configuration to the module (Mandatory) # Add Global Configuration to the module (Mandatory)
self.Config = ircInstance.Config self.Config = irc_instance.Config
# Add Base object to the module (Mandatory) # Add Base object to the module (Mandatory)
self.Base = ircInstance.Base self.Base = irc_instance.Base
# Add logs object to the module (Mandatory) # Add logs object to the module (Mandatory)
self.Logs = ircInstance.Base.logs self.Logs = irc_instance.Loader.Logs
# Add User object to the module (Mandatory) # Add User object to the module (Mandatory)
self.User = ircInstance.User self.User = irc_instance.User
# Add Channel object to the module (Mandatory) # Add Channel object to the module (Mandatory)
self.Channel = ircInstance.Channel self.Channel = irc_instance.Channel
# Add global definitions
self.Definition = irc_instance.Loader.Definition
# Add clone object to the module (Optionnal) # The Global Settings
self.Clone = ircInstance.Clone self.Settings = irc_instance.Loader.Settings
self.Definition = ircInstance.Loader.Definition 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 # Créer les nouvelles commandes du module
self.Irc.build_command(1, self.module_name, 'clone', 'Connect, join, part, kill and say clones') self.Irc.build_command(1, self.module_name, 'clone', 'Connect, join, part, kill and say clones')
@@ -57,10 +73,6 @@ class Clone():
self.__create_tables() self.__create_tables()
self.stop = False self.stop = False
logging.getLogger('faker').setLevel(logging.CRITICAL)
self.fakeEN = faker.Faker('en_GB')
self.fakeFR = faker.Faker('fr_FR')
# Load module configuration (Mandatory) # Load module configuration (Mandatory)
self.__load_module_configuration() self.__load_module_configuration()
@@ -75,8 +87,6 @@ class Clone():
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. """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 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: Returns:
None: Aucun retour n'es attendu None: Aucun retour n'es attendu
@@ -99,9 +109,7 @@ class Clone():
""" """
try: try:
# Variable qui va contenir les options de configuration du module Defender # Variable qui va contenir les options de configuration du module Defender
self.ModConfig = self.ModConfModel( self.ModConfig = self.Schemas.ModConfModel()
clone_nicknames=[]
)
# Sync the configuration with core configuration (Mandatory) # Sync the configuration with core configuration (Mandatory)
# self.Base.db_sync_core_config(self.module_name, self.ModConfig) # self.Base.db_sync_core_config(self.module_name, self.ModConfig)
@@ -115,6 +123,8 @@ class Clone():
"""Cette methode sera executée a chaque désactivation ou """Cette methode sera executée a chaque désactivation ou
rechargement de module 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.Channel.db_query_channel(action='del', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL) self.Channel.db_query_channel(action='del', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} -nts") self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} -nts")
@@ -123,175 +133,41 @@ class Clone():
return None return None
def generate_vhost(self) -> str:
fake = self.fakeEN
rand_1 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
rand_2 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
rand_3 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
vhost = ''.join(rand_1) + '.' + ''.join(rand_2) + '.' + ''.join(rand_3) + '.IP'
return vhost
def generate_clones(self, group: str = 'Default', auto_remote_ip: bool = False) -> None:
try:
fakeEN = self.fakeEN
fakeFR = self.fakeFR
unixtime = self.Base.get_unixtime()
chaine = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
generate_uid = fakeEN.random_sample(chaine, 6)
uid = self.Config.SERVEUR_ID + ''.join(generate_uid)
umodes = self.Config.CLONE_UMODES
# Generate Username
chaine = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
new_username = fakeEN.random_sample(chaine, 9)
username = ''.join(new_username)
# Create realname XX F|M Department
gender = fakeEN.random_choices(['F','M'], 1)
gender = ''.join(gender)
if gender == 'F':
nickname = fakeEN.first_name_female()
elif gender == 'M':
nickname = fakeEN.first_name_male()
else:
nickname = fakeEN.first_name()
age = random.randint(20, 60)
department = fakeFR.department_name()
realname = f'{age} {gender} {department}'
decoded_ip = fakeEN.ipv4_private() if auto_remote_ip else '127.0.0.1'
hostname = fakeEN.hostname()
vhost = self.generate_vhost()
checkNickname = self.Clone.exists(nickname=nickname)
checkUid = self.Clone.uid_exists(uid=uid)
while checkNickname:
caracteres = '0123456789'
randomize = ''.join(random.choice(caracteres) for _ in range(2))
nickname = nickname + str(randomize)
checkNickname = self.Clone.exists(nickname=nickname)
while checkUid:
chaine = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
generate_uid = fakeEN.random_sample(chaine, 6)
uid = self.Config.SERVEUR_ID + ''.join(generate_uid)
checkUid = self.Clone.uid_exists(uid=uid)
clone = self.Definition.MClone(
connected=False,
nickname=nickname,
username=username,
realname=realname,
hostname=hostname,
umodes=umodes,
uid=uid,
remote_ip=decoded_ip,
vhost=vhost,
group=group,
channels=[]
)
self.Clone.insert(clone)
return None
except AttributeError as ae:
self.Logs.error(f'Attribute Error : {ae}')
except Exception as err:
self.Logs.error(f"General Error: {err}")
def thread_connect_clones(self, number_of_clones:int , group: str = 'Default', auto_remote_ip: bool = False, interval: float = 0.2) -> None:
for i in range(0, number_of_clones):
self.generate_clones(group=group, auto_remote_ip=auto_remote_ip)
for clone in self.Clone.UID_CLONE_DB:
if self.stop:
print(f"Stop creating clones ...")
self.stop = False
break
if not clone.connected:
self.Protocol.send_uid(clone.nickname, clone.username, clone.hostname, clone.uid, clone.umodes, clone.vhost, clone.remote_ip, clone.realname, print_log=False)
self.Protocol.send_join_chan(uidornickname=clone.uid, channel=self.Config.CLONE_CHANNEL, password=self.Config.CLONE_CHANNEL_PASSWORD, print_log=False)
time.sleep(interval)
clone.connected = True
def thread_kill_clones(self, fromuser: str) -> None:
clone_to_kill: list[str] = []
for clone in self.Clone.UID_CLONE_DB:
clone_to_kill.append(clone.uid)
for clone_uid in clone_to_kill:
self.Protocol.send_quit(clone_uid, 'Gooood bye', print_log=False)
del clone_to_kill
return None
def cmd(self, data:list) -> None: def cmd(self, data:list) -> None:
try: try:
service_id = self.Config.SERVICE_ID # Defender serveur id if not data or len(data) < 2:
cmd = list(data).copy()
if len(cmd) < 2:
return None return None
match cmd[1]: cmd = data.copy() if isinstance(data, list) else list(data).copy()
index, command = self.Irc.Protocol.get_ircd_protocol_poisition(cmd)
case 'REPUTATION': if index == -1:
pass
if len(cmd) < 3:
return None return None
match cmd[2]: match command:
case 'PRIVMSG': case 'PRIVMSG':
# print(cmd) return self.Utils.handle_on_privmsg(self, cmd)
uid_sender = self.User.clean_uid(cmd[1])
senderObj = self.User.get_User(uid_sender)
if senderObj.hostname in self.Config.CLONE_LOG_HOST_EXEMPT: case 'QUIT':
return None return None
if not senderObj is None: case _:
senderMsg = ' '.join(cmd[4:]) return None
getClone = self.Clone.get_Clone(cmd[3])
if getClone is None:
return None
if getClone.uid != self.Config.SERVICE_ID:
final_message = f"{senderObj.nickname}!{senderObj.username}@{senderObj.hostname} > {senderMsg.lstrip(':')}"
self.Protocol.send_priv_msg(
nick_from=getClone.uid,
msg=final_message,
channel=self.Config.CLONE_CHANNEL
)
except Exception as err: except Exception as err:
self.Logs.error(f'General Error: {err}') self.Logs.error(f'General Error: {err}', exc_info=True)
return None
def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None: def hcmds(self, user: str, channel: Any, cmd: list, fullcmd: list = []) -> None:
try: try:
if len(cmd) < 1:
return
command = str(cmd[0]).lower() command = str(cmd[0]).lower()
fromuser = user fromuser = user
dnickname = self.Config.SERVICE_NICKNAME
dnickname = self.Config.SERVICE_NICKNAME # Defender nickname
match command: match command:
@@ -299,10 +175,11 @@ class Clone():
if len(cmd) == 1: 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 connect NUMBER GROUP_NAME INTERVAL")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill [all | nickname]") 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 | nickname] #channel") 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 | 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") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone list")
return None
option = str(cmd[1]).lower() option = str(cmd[1]).lower()
@@ -317,8 +194,8 @@ class Clone():
connection_interval = int(cmd[4]) if len(cmd) == 5 else 0.2 connection_interval = int(cmd[4]) if len(cmd) == 5 else 0.2
self.Base.create_thread( self.Base.create_thread(
func=self.thread_connect_clones, func=self.Threads.thread_connect_clones,
func_args=(number_of_clones, group, False, connection_interval) func_args=(self, number_of_clones, group, False, connection_interval)
) )
except Exception as err: except Exception as err:
@@ -328,18 +205,28 @@ class Clone():
case 'kill': case 'kill':
try: try:
# clone kill [all | nickname] # clone kill [ALL | group name | nickname]
self.stop = True self.stop = True
clone_name = str(cmd[2]) option = str(cmd[2])
clone_to_kill: list[str] = []
if clone_name.lower() == 'all': if option.lower() == 'all':
self.Base.create_thread(func=self.thread_kill_clones, func_args=(fromuser, )) self.Base.create_thread(func=self.Threads.thread_kill_clones, func_args=(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}")
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)
self.Clone.delete(clone.uid)
else: else:
cloneObj = self.Clone.get_Clone(clone_name) clone_obj = self.Clone.get_clone(option)
if not cloneObj is None: if not clone_obj is None:
self.Protocol.send_quit(cloneObj.uid, 'Goood bye', print_log=False) self.Protocol.send_quit(clone_obj.uid, 'Goood bye', print_log=False)
self.Clone.delete(clone_obj.uid)
except Exception as err: except Exception as err:
self.Logs.error(f'{err}') self.Logs.error(f'{err}')
@@ -348,19 +235,28 @@ class Clone():
case 'join': case 'join':
try: try:
# clone join [all | nickname] #channel # clone join [all | group name | nickname] #channel
clone_name = str(cmd[2]) option = str(cmd[2])
clone_channel_to_join = str(cmd[3]) clone_channel_to_join = str(cmd[3])
if clone_name.lower() == 'all': if option.lower() == 'all':
for clone in self.Clone.UID_CLONE_DB: for clone in self.Clone.UID_CLONE_DB:
self.Protocol.send_join_chan(uidornickname=clone.uid, channel=clone_channel_to_join, print_log=False) self.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}")
for clone in list_of_clones_in_group:
self.Protocol.send_join_chan(uidornickname=clone.nickname, channel=clone_channel_to_join, print_log=False)
else: else:
if self.Clone.exists(clone_name): if self.Clone.nickname_exists(option):
if not self.Clone.get_uid(clone_name) is None: clone_uid = self.Clone.get_clone(option).uid
self.Protocol.send_join_chan(uidornickname=clone_name, channel=clone_channel_to_join, print_log=False) self.Protocol.send_join_chan(uidornickname=clone_uid, channel=clone_channel_to_join, print_log=False)
except Exception as err: except Exception as err:
self.Logs.error(f'{err}') self.Logs.error(f'{err}')
@@ -369,18 +265,27 @@ class Clone():
case 'part': case 'part':
try: try:
# clone part [all | nickname] #channel # clone part [all | nickname] #channel
clone_name = str(cmd[2]) option = str(cmd[2])
clone_channel_to_part = str(cmd[3]) clone_channel_to_part = str(cmd[3])
if clone_name.lower() == 'all': if option.lower() == 'all':
for clone in self.Clone.UID_CLONE_DB: for clone in self.Clone.UID_CLONE_DB:
self.Protocol.send_part_chan(uidornickname=clone.uid, channel=clone_channel_to_part, print_log=False) self.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}")
for clone in list_of_clones_in_group:
self.Protocol.send_part_chan(uidornickname=clone.uid, channel=clone_channel_to_part, print_log=False)
else: else:
if self.Clone.exists(clone_name): if self.Clone.nickname_exists(option):
clone_uid = self.Clone.get_uid(clone_name) clone_uid = self.Clone.get_uid(option)
if not clone_uid is None: if not clone_uid is None:
self.Protocol.send_part_chan(uidornickname=clone_uid, channel=clone_channel_to_part, print_log=False) self.Protocol.send_part_chan(uidornickname=clone_uid, channel=clone_channel_to_part, print_log=False)
@@ -391,11 +296,31 @@ class Clone():
case 'list': case 'list':
try: try:
clone_count = len(self.Clone.UID_CLONE_DB) # Syntax. /msg defender clone list <group_name>
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f">> Number of connected clones: {clone_count}") header = f" {'Nickname':<12}| {'Real name':<25}| {'Group name':<15}| {'Connected':<35}"
for clone_name in self.Clone.UID_CLONE_DB: line = "-"*67
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=header)
msg=f">> Nickname: {clone_name.nickname} | Username: {clone_name.username} | Realname: {clone_name.realname} | Vhost: {clone_name.vhost} | UID: {clone_name.uid} | Group: {clone_name.group} | Connected: {clone_name.connected}") self.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}")
for clone_name in self.Clone.UID_CLONE_DB:
self.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!")
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}")
for clone in clones:
self.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: except Exception as err:
self.Logs.error(f'{err}') self.Logs.error(f'{err}')
@@ -403,11 +328,11 @@ class Clone():
try: try:
# clone say clone_nickname #channel message # clone say clone_nickname #channel message
clone_name = str(cmd[2]) clone_name = str(cmd[2])
clone_channel = str(cmd[3]) if self.Channel.Is_Channel(str(cmd[3])) else None clone_channel = str(cmd[3]) if self.Channel.is_valid_channel(str(cmd[3])) else None
final_message = ' '.join(cmd[4:]) final_message = ' '.join(cmd[4:])
if clone_channel is None or not self.Clone.exists(clone_name): if clone_channel is None or not self.Clone.nickname_exists(clone_name):
self.Protocol.send_notice( self.Protocol.send_notice(
nick_from=dnickname, nick_from=dnickname,
nick_to=fromuser, nick_to=fromuser,
@@ -415,7 +340,7 @@ class Clone():
) )
return None return None
if self.Clone.exists(clone_name): if self.Clone.nickname_exists(clone_name):
self.Protocol.send_priv_msg(nick_from=clone_name, msg=final_message, channel=clone_channel) self.Protocol.send_priv_msg(nick_from=clone_name, msg=final_message, channel=clone_channel)
except Exception as err: except Exception as err:
@@ -428,12 +353,12 @@ class Clone():
case _: 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 connect NUMBER GROUP_NAME INTERVAL")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill [all | nickname]") 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 | nickname] #channel") 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 | 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") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone list")
except IndexError as ie: except IndexError as ie:
self.Logs.error(f'Index Error: {ie}') self.Logs.error(f'Index Error: {ie}')
except Exception as err: except Exception as err:
self.Logs.error(f'Index Error: {err}') self.Logs.error(f'General Error: {err}')

22
mods/clone/schemas.py Normal file
View File

@@ -0,0 +1,22 @@
from core.definition import MainModel, dataclass, field
@dataclass
class ModConfModel(MainModel):
clone_nicknames: list[str] = field(default_factory=list)
@dataclass
class MClone(MainModel):
"""Model Clone"""
connected: bool = False
uid: str = None
nickname: str = None
username: str = None
realname: str = None
channels: list = field(default_factory=list)
vhost: str = None
hostname: str = 'localhost'
umodes: str = None
remote_ip: str = '127.0.0.1'
group: str = 'Default'
DB_CLONES: list[MClone] = []

44
mods/clone/threads.py Normal file
View File

@@ -0,0 +1,44 @@
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',
number_of_clones:int ,
group: str = 'Default',
auto_remote_ip: bool = False,
interval: float = 0.2
):
for i in range(0, number_of_clones):
uplink.Utils.create_new_clone(
uplink=uplink,
faker_instance=uplink.Faker,
group=group,
auto_remote_ip=auto_remote_ip
)
for clone in uplink.Clone.UID_CLONE_DB:
if uplink.stop:
print(f"Stop creating clones ...")
uplink.stop = False
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)
sleep(interval)
clone.connected = True
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)
uplink.Clone.delete(clone.uid)
del clone_to_kill

198
mods/clone/utils.py Normal file
View File

@@ -0,0 +1,198 @@
import logging
import random
from typing import Optional, TYPE_CHECKING
from faker import Faker
logging.getLogger('faker').setLevel(logging.CRITICAL)
if TYPE_CHECKING:
from mods.clone.mod_clone import Clone
def create_faker_object(faker_local: Optional[str] = 'en_GB') -> Faker:
"""Create a new faker object
Args:
faker_local (Optional[str], optional): _description_. Defaults to 'en_GB'.
Returns:
Faker: The Faker Object
"""
if faker_local not in ['en_GB', 'fr_FR']:
faker_local = 'en_GB'
return Faker(faker_local)
def generate_uid_for_clone(faker_instance: 'Faker', server_id: str) -> str:
chaine = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
return server_id + ''.join(faker_instance.random_sample(chaine, 6))
def generate_vhost_for_clone(faker_instance: 'Faker') -> str:
"""Generate new vhost for the clone
Args:
faker_instance (Faker): The Faker instance
Returns:
str: _description_
"""
rand_1 = faker_instance.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
rand_2 = faker_instance.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
rand_3 = faker_instance.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
vhost = ''.join(rand_1) + '.' + ''.join(rand_2) + '.' + ''.join(rand_3) + '.IP'
return vhost
def generate_username_for_clone(faker_instance: 'Faker') -> str:
"""Generate vhosts for clones
Returns:
str: The vhost
"""
chaine = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
return ''.join(faker_instance.random_sample(chaine, 9))
def generate_realname_for_clone(faker_instance: 'Faker') -> tuple[int, str, str]:
"""Generate realname for clone
Ex: XX F|M Department
Args:
faker_instance (Faker): _description_
Returns:
tuple: Age | Gender | Department
"""
# Create realname XX F|M Department
gender = faker_instance.random_choices(['F','M'], 1)
gender = ''.join(gender)
age = random.randint(20, 60)
if faker_instance.locales[0] == 'fr_FR':
department = faker_instance.department_name()
else:
department = faker_instance.city()
return (age, gender, department)
def generate_nickname_for_clone(faker_instance: 'Faker', gender: Optional[str] = 'AUTO') -> str:
"""Generate nickname for clone
Args:
faker_instance (Faker): The Faker Instance
gender (str): The Gender.Default F
Returns:
str: Nickname Based on the Gender
"""
if gender.upper() == 'AUTO' or gender.upper() not in ['F', 'M']:
# Generate new gender
gender = faker_instance.random_choices(['F','M'], 1)
gender = ''.join(gender)
if gender.upper() == 'F':
return faker_instance.first_name_female()
elif gender.upper() == 'M':
return faker_instance.first_name_male()
def generate_ipv4_for_clone(faker_instance: 'Faker', auto: bool = True) -> str:
"""Generate remote ipv4 for clone
Args:
faker_instance (Faker): The Faker Instance
auto (bool): Set auto generation of ip or 127.0.0.1 will be returned
Returns:
str: Remote IPV4
"""
return faker_instance.ipv4_private() if auto else '127.0.0.1'
def generate_hostname_for_clone(faker_instance: 'Faker') -> str:
"""Generate hostname for clone
Args:
faker_instance (Faker): The Faker Instance
Returns:
str: New hostname
"""
return faker_instance.hostname()
def create_new_clone(uplink: 'Clone', faker_instance: 'Faker', group: str = 'Default', auto_remote_ip: bool = False) -> bool:
"""Create a new Clone object in the DB_CLONES.
Args:
faker_instance (Faker): The Faker instance
Returns:
bool: True if it was created
"""
faker = faker_instance
uid = generate_uid_for_clone(faker, uplink.Config.SERVEUR_ID)
umodes = uplink.Config.CLONE_UMODES
# Generate Username
username = generate_username_for_clone(faker)
# Generate realname (XX F|M Department)
age, gender, department = generate_realname_for_clone(faker)
realname = f'{age} {gender} {department}'
# Generate nickname
nickname = generate_nickname_for_clone(faker, gender)
# Generate decoded ipv4 and hostname
decoded_ip = generate_ipv4_for_clone(faker, auto_remote_ip)
hostname = generate_hostname_for_clone(faker)
vhost = generate_vhost_for_clone(faker)
checkNickname = uplink.Clone.nickname_exists(nickname)
checkUid = uplink.Clone.uid_exists(uid=uid)
while checkNickname:
caracteres = '0123456789'
randomize = ''.join(random.choice(caracteres) for _ in range(2))
nickname = nickname + str(randomize)
checkNickname = uplink.Clone.nickname_exists(nickname)
while checkUid:
uid = generate_uid_for_clone(faker, uplink.Config.SERVEUR_ID)
checkUid = uplink.Clone.uid_exists(uid=uid)
clone = uplink.Schemas.MClone(
connected=False,
nickname=nickname,
username=username,
realname=realname,
hostname=hostname,
umodes=umodes,
uid=uid,
remote_ip=decoded_ip,
vhost=vhost,
group=group,
channels=[]
)
uplink.Clone.insert(clone)
return True
def handle_on_privmsg(uplink: 'Clone', srvmsg: list[str]):
uid_sender = uplink.Irc.Utils.clean_uid(srvmsg[1])
senderObj = uplink.User.get_User(uid_sender)
if senderObj.hostname in uplink.Config.CLONE_LOG_HOST_EXEMPT:
return
if not senderObj is None:
senderMsg = ' '.join(srvmsg[4:])
clone_obj = uplink.Clone.get_clone(srvmsg[3])
if clone_obj is None:
return
if clone_obj.uid != uplink.Config.SERVICE_ID:
final_message = f"{senderObj.nickname}!{senderObj.username}@{senderObj.hostname} > {senderMsg.lstrip(':')}"
uplink.Protocol.send_priv_msg(
nick_from=clone_obj.uid,
msg=final_message,
channel=uplink.Config.CLONE_CHANNEL
)

View File

@@ -1,5 +1,6 @@
from typing import Union, TYPE_CHECKING from typing import Optional, TYPE_CHECKING
from dataclasses import dataclass from dataclasses import dataclass
import mods.command.utils as utils
if TYPE_CHECKING: if TYPE_CHECKING:
from core.irc import Irc from core.irc import Irc
@@ -34,8 +35,11 @@ class Command:
# Add Base object to the module (Mandatory) # Add Base object to the module (Mandatory)
self.Base = ircInstance.Base self.Base = ircInstance.Base
# Add main Utils to the module
self.MainUtils = ircInstance.Utils
# Add logs object to the module (Mandatory) # Add logs object to the module (Mandatory)
self.Logs = ircInstance.Base.logs self.Logs = ircInstance.Loader.Logs
# Add User object to the module (Mandatory) # Add User object to the module (Mandatory)
self.User = ircInstance.User self.User = ircInstance.User
@@ -46,6 +50,9 @@ class Command:
# Add Channel object to the module (Mandatory) # Add Channel object to the module (Mandatory)
self.Channel = ircInstance.Channel self.Channel = ircInstance.Channel
# Module Utils
self.mod_utils = utils
self.Irc.build_command(1, self.module_name, 'join', 'Join a channel') self.Irc.build_command(1, self.module_name, 'join', 'Join a channel')
self.Irc.build_command(1, self.module_name, 'assign', 'Assign a user to a role or task') self.Irc.build_command(1, self.module_name, 'assign', 'Assign a user to a role or task')
self.Irc.build_command(1, self.module_name, 'part', 'Leave a channel') self.Irc.build_command(1, self.module_name, 'part', 'Leave a channel')
@@ -271,7 +278,7 @@ class Command:
user_uid = self.User.clean_uid(cmd[5]) user_uid = self.User.clean_uid(cmd[5])
userObj: MUser = self.User.get_User(user_uid) userObj: MUser = self.User.get_User(user_uid)
channel_name = cmd[4] if self.Channel.Is_Channel(cmd[4]) else None channel_name = cmd[4] if self.Channel.is_valid_channel(cmd[4]) else None
client_obj = self.Client.get_Client(user_uid) client_obj = self.Client.get_Client(user_uid)
nickname = userObj.nickname if userObj is not None else None nickname = userObj.nickname if userObj is not None else None
@@ -296,7 +303,7 @@ class Command:
except Exception as err: except Exception as err:
self.Logs.error(f"General Error: {err}") self.Logs.error(f"General Error: {err}")
def hcmds(self, uidornickname: str, channel_name: Union[str, None], cmd: list, fullcmd: list = []) -> None: def hcmds(self, uidornickname: str, channel_name: Optional[str], cmd: list, fullcmd: list = []):
command = str(cmd[0]).lower() command = str(cmd[0]).lower()
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME
@@ -307,293 +314,78 @@ class Command:
fromchannel = channel_name fromchannel = channel_name
match command: match command:
case "automode":
# automode set nickname [+/-mode] #channel case 'automode':
# automode set adator +o #channel
try: try:
option: str = str(cmd[1]).lower() self.mod_utils.set_automode(self, cmd, fromuser)
match option:
case 'set':
allowed_modes: list[str] = self.Base.Settings.PROTOCTL_PREFIX # ['q','a','o','h','v']
if len(cmd) < 5:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} [nickname] [+/-mode] [#channel]")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"AutoModes available: {' / '.join(allowed_modes)}")
return None
# userObj: MUser = self.User.get_User(str(cmd[2]))
nickname = str(cmd[2])
mode = str(cmd[3])
chan: str = str(cmd[4]).lower() if self.Channel.Is_Channel(cmd[4]) else None
sign = mode[0] if mode.startswith( ('+', '-')) else None
clean_mode = mode[1:] if len(mode) > 0 else None
if sign is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You must provide the flag mode + or -")
return None
if clean_mode not in allowed_modes:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You should use one of those modes {' / '.join(allowed_modes)}")
return None
if chan is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You should use one of those modes {' / '.join(allowed_modes)}")
return None
db_data: dict[str, str] = {"nickname": nickname, "channel": chan}
db_query = self.Base.db_execute_query(query="SELECT id FROM command_automode WHERE nickname = :nickname and channel = :channel", params=db_data)
db_result = db_query.fetchone()
if db_result is not None:
if sign == '+':
db_data = {"updated_on": self.Base.get_datetime(), "nickname": nickname, "channel": chan, "mode": mode}
db_result = self.Base.db_execute_query(query="UPDATE command_automode SET mode = :mode, updated_on = :updated_on WHERE nickname = :nickname and channel = :channel",
params=db_data)
if db_result.rowcount > 0:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Automode {mode} edited for {nickname} in {chan}")
elif sign == '-':
db_data = {"nickname": nickname, "channel": chan, "mode": f"+{clean_mode}"}
db_result = self.Base.db_execute_query(query="DELETE FROM command_automode WHERE nickname = :nickname and channel = :channel and mode = :mode",
params=db_data)
if db_result.rowcount > 0:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Automode {mode} deleted for {nickname} in {chan}")
else:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"The mode [{mode}] has not been found for {nickname} in channel {chan}")
return None
# Instert a new automode
if sign == '+':
db_data = {"created_on": self.Base.get_datetime(), "updated_on": self.Base.get_datetime(), "nickname": nickname, "channel": chan, "mode": mode}
db_query = self.Base.db_execute_query(
query="INSERT INTO command_automode (created_on, updated_on, nickname, channel, mode) VALUES (:created_on, :updated_on, :nickname, :channel, :mode)",
params=db_data
)
if db_query.rowcount > 0:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Automode {mode} applied to {nickname} in {chan}")
if self.Channel.is_user_present_in_channel(chan, self.User.get_uid(nickname)):
self.Protocol.send2socket(f":{service_id} MODE {chan} {mode} {nickname}")
else:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"AUTOMODE {mode} cannot be added to {nickname} in {chan} because it doesn't exist")
case 'list':
db_query: CursorResult = self.Base.db_execute_query("SELECT nickname, channel, mode FROM command_automode")
db_results: Sequence[Row] = db_query.fetchall()
if not db_results:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,
msg="There is no automode to display.")
for db_result in db_results:
db_nickname, db_channel, db_mode = db_result
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,
msg=f"Nickname: {db_nickname} | Channel: {db_channel} | Mode: {db_mode}")
case _:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} SET [nickname] [+/-mode] [#channel]")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} LIST")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"[AUTOMODES AVAILABLE] are {' / '.join(allowed_modes)}")
except IndexError: 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()} 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"/msg {dnickname} {command.upper()} LIST")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"[AUTOMODES AVAILABLE] are {' / '.join(self.Base.Settings.PROTOCTL_PREFIX)}") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"[AUTOMODES AVAILABLE] are {' / '.join(self.Loader.Settings.PROTOCTL_PREFIX)}")
except Exception as err: except Exception as err:
self.Logs.error(f"General Error: {err}") self.Logs.error(f"General Error: {err}")
case 'deopall': case 'deopall':
try: try:
self.Protocol.send2socket(f":{service_id} SVSMODE {fromchannel} -o") self.mod_utils.set_deopall(self, fromchannel)
except IndexError as ie:
self.Logs.warning(f'_hcmd OP: {str(ie)}')
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.error(f'Unknown Error: {str(err)}')
case 'devoiceall': case 'devoiceall':
try: try:
self.Protocol.send2socket(f":{service_id} SVSMODE {fromchannel} -v") self.mod_utils.set_devoiceall(self, fromchannel)
except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}')
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.error(f'Unknown Error: {str(err)}')
case 'voiceall': case 'voiceall':
try: try:
chan_info = self.Channel.get_Channel(fromchannel) self.mod_utils.set_mode_to_all(self, fromchannel, '+', 'v')
set_mode = 'v'
mode:str = ''
users:str = ''
uids_split = [chan_info.uids[i:i + 6] for i in range(0, len(chan_info.uids), 6)]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +{set_mode} {dnickname}")
for uid in uids_split:
for i in range(0, len(uid)):
mode += set_mode
users += f'{self.User.get_nickname(self.Base.clean_uid(uid[i]))} '
if i == len(uid) - 1:
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +{mode} {users}")
mode = ''
users = ''
except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}')
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'opall': case 'opall':
try: try:
chan_info = self.Channel.get_Channel(fromchannel) self.mod_utils.set_mode_to_all(self, fromchannel, '+', 'o')
set_mode = 'o'
mode:str = ''
users:str = ''
uids_split = [chan_info.uids[i:i + 6] for i in range(0, len(chan_info.uids), 6)]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +{set_mode} {dnickname}")
for uid in uids_split:
for i in range(0, len(uid)):
mode += set_mode
users += f'{self.User.get_nickname(self.Base.clean_uid(uid[i]))} '
if i == len(uid) - 1:
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +{mode} {users}")
mode = ''
users = ''
except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}')
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'op': case 'op':
# /mode #channel +o user
# .op #channel user
# /msg dnickname op #channel user
# [':adator', 'PRIVMSG', '#services', ':.o', '#services', 'dktmb']
try: try:
if fromchannel is None: self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '+o')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} op [#SALON] [NICKNAME]")
return False
if len(cmd) == 1:
self.Protocol.send2socket(f":{dnickname} MODE {fromchannel} +o {fromuser}")
return True
# deop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +o {nickname}")
return True
nickname = cmd[2]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +o {nickname}")
except IndexError as e: 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} op [#SALON] [NICKNAME]") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} op [#SALON] [NICKNAME]")
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'deop': case 'deop':
# /mode #channel -o user
# .deop #channel user
try: try:
if fromchannel is None: self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '-o')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} deop [#SALON] [NICKNAME]")
return False
if len(cmd) == 1:
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} -o {fromuser}")
return True
# deop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} -o {nickname}")
return True
nickname = cmd[2]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} -o {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEOP: {str(e)}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} deop [#SALON] [NICKNAME]") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} deop [#SALON] [NICKNAME]")
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'owner': case 'owner':
# /mode #channel +q user
# .owner #channel user
try: try:
if fromchannel is None: self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '+q')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} owner [#SALON] [NICKNAME]")
return False
if len(cmd) == 1:
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +q {fromuser}")
return True
# owner nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +q {nickname}")
return True
nickname = cmd[2]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +q {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd OWNER: {str(e)}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} owner [#SALON] [NICKNAME]") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} owner [#SALON] [NICKNAME]")
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'deowner': case 'deowner':
# /mode #channel -q user
# .deowner #channel user
try: try:
if fromchannel is None: self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '-q')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]")
return False
if len(cmd) == 1:
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} -q {fromuser}")
return True
# deowner nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} -q {nickname}")
return True
nickname = cmd[2]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} -q {nickname}")
except IndexError as e: 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} deowner [#SALON] [NICKNAME]") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]")
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'protect': case 'protect':
# /mode #channel +a user
# .protect #channel user
try: try:
if fromchannel is None: self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '+a')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]")
return False
if len(cmd) == 1:
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +a {fromuser}")
return True
# deowner nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +a {nickname}")
return True
nickname = cmd[2]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +a {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEOWNER: {str(e)}') self.Logs.warning(f'_hcmd DEOWNER: {str(e)}')
@@ -602,25 +394,8 @@ class Command:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'deprotect': case 'deprotect':
# /mode #channel -a user
# .deprotect #channel user
try: try:
if fromchannel is None: self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '-a')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]")
return False
if len(cmd) == 1:
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} -a {fromuser}")
return True
# deowner nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} -a {nickname}")
return True
nickname = cmd[2]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} -a {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEOWNER: {str(e)}') self.Logs.warning(f'_hcmd DEOWNER: {str(e)}')
@@ -629,125 +404,42 @@ class Command:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'halfop': case 'halfop':
# /mode #channel +h user
# .halfop #channel user
try: try:
if fromchannel is None: self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '+h')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} halfop [#SALON] [NICKNAME]")
return False
if len(cmd) == 1:
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +h {fromuser}")
return True
# deop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +h {nickname}")
return True
nickname = cmd[2]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +h {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd halfop: {str(e)}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command} [#SALON] [NICKNAME]")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} halfop [#SALON] [NICKNAME]")
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'dehalfop': case 'dehalfop':
# /mode #channel -h user
# .dehalfop #channel user
try: try:
if fromchannel is None: self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '-h')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]")
return False
if len(cmd) == 1:
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} -h {fromuser}")
return True
# dehalfop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} -h {nickname}")
return True
nickname = cmd[2]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} -h {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEHALFOP: {str(e)}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]")
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'voice': case 'voice':
# /mode #channel +v user
# .voice #channel user
try: try:
if fromchannel is None: self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '+v')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} voice [#SALON] [NICKNAME]")
return False
if len(cmd) == 1:
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +v {fromuser}")
return True
# voice nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +v {nickname}")
return True
nickname = cmd[2]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} +v {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd VOICE: {str(e)}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} voice [#SALON] [NICKNAME]") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} voice [#SALON] [NICKNAME]")
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'devoice': case 'devoice':
# /mode #channel -v user
# .devoice #channel user
try: try:
if fromchannel is None: self.mod_utils.set_operation(self, cmd, fromchannel, fromuser, '-v')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]")
return False
if len(cmd) == 1:
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} -v {fromuser}")
return True
# dehalfop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} -v {nickname}")
return True
nickname = cmd[2]
self.Protocol.send2socket(f":{service_id} MODE {fromchannel} -v {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEVOICE: {str(e)}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]")
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'ban': case 'ban':
# .ban #channel nickname
try: try:
sentchannel = str(cmd[1]) if self.Channel.Is_Channel(cmd[1]) else None self.mod_utils.set_ban(self, cmd, '+', fromuser)
if sentchannel is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]")
return False
nickname = cmd[2]
self.Protocol.send2socket(f":{service_id} MODE {sentchannel} +b {nickname}!*@*")
self.Logs.debug(f'{fromuser} has banned {nickname} from {sentchannel}')
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd BAN: {str(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.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]")
@@ -755,97 +447,43 @@ class Command:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'unban': case 'unban':
# .unban #channel nickname
try: try:
sentchannel = str(cmd[1]) if self.Channel.Is_Channel(cmd[1]) else None self.mod_utils.set_ban(self, cmd, '-', fromuser)
if sentchannel is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} ban [#SALON] [NICKNAME]")
return False
nickname = cmd[2]
self.Protocol.send2socket(f":{service_id} MODE {sentchannel} -b {nickname}!*@*")
self.Logs.debug(f'{fromuser} has unbanned {nickname} from {sentchannel}')
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd UNBAN: {str(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} unban [#SALON] [NICKNAME]") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]")
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'kick': case 'kick':
# .kick #channel nickname reason
try: try:
sentchannel = str(cmd[1]) if self.Channel.Is_Channel(cmd[1]) else None self.mod_utils.set_kick(self, cmd, fromuser)
if sentchannel is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} ban [#SALON] [NICKNAME]")
return False
nickname = cmd[2]
final_reason = ' '.join(cmd[3:])
self.Protocol.send2socket(f":{service_id} KICK {sentchannel} {nickname} {final_reason}")
self.Logs.debug(f'{fromuser} has kicked {nickname} from {sentchannel} : {final_reason}')
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd KICK: {str(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} kick [#SALON] [NICKNAME] [REASON]") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME] [REASON]")
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'kickban': case 'kickban':
# .kickban #channel nickname reason
try: try:
sentchannel = str(cmd[1]) if self.Channel.Is_Channel(cmd[1]) else None self.mod_utils.set_kickban(self, cmd, fromuser)
if sentchannel is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} ban [#SALON] [NICKNAME]")
return False
nickname = cmd[2]
final_reason = ' '.join(cmd[3:])
self.Protocol.send2socket(f":{service_id} KICK {sentchannel} {nickname} {final_reason}")
self.Protocol.send2socket(f":{service_id} MODE {sentchannel} +b {nickname}!*@*")
self.Logs.debug(f'{fromuser} has kicked and banned {nickname} from {sentchannel} : {final_reason}')
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd KICKBAN: {str(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} kickban [#SALON] [NICKNAME] [REASON]") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME] [REASON]")
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'join' | 'assign': case 'join' | 'assign':
try: try:
sent_channel = str(cmd[1]) if self.Channel.Is_Channel(cmd[1]) else None self.mod_utils.set_assign_channel_to_service(self, cmd, fromuser)
if sent_channel is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{self.Config.SERVICE_PREFIX}JOIN #channel")
return False
# self.Protocol.send2socket(f':{service_id} JOIN {sent_channel}')
self.Protocol.send_join_chan(uidornickname=dnickname,channel=sent_channel)
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {dnickname} JOINED {sent_channel}")
self.Channel.db_query_channel('add', self.module_name, sent_channel)
except IndexError as ie: except IndexError as ie:
self.Logs.error(f'{ie}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [#SALON]")
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'part' | 'unassign': case 'part' | 'unassign':
try: try:
sent_channel = str(cmd[1]) if self.Channel.Is_Channel(cmd[1]) else None self.mod_utils.set_unassign_channel_to_service(self, cmd, fromuser)
if sent_channel is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{self.Config.SERVICE_PREFIX}PART #channel")
return False
if sent_channel == dchanlog:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {dnickname} CAN'T LEFT {sent_channel} AS IT IS LOG CHANNEL")
return False
self.Protocol.send_part_chan(uidornickname=dnickname, channel=sent_channel)
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {dnickname} LEFT {sent_channel}")
self.Channel.db_query_channel('del', self.module_name, sent_channel)
except IndexError as ie: except IndexError as ie:
self.Logs.error(f'{ie}') self.Logs.error(f'{ie}')
except Exception as err: except Exception as err:
@@ -859,7 +497,7 @@ class Command:
return None return None
chan = str(cmd[1]) chan = str(cmd[1])
if not self.Channel.Is_Channel(chan): 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="The channel must start with #")
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")
return None return None
@@ -959,7 +597,7 @@ class Command:
chan = str(cmd[1]) chan = str(cmd[1])
if not self.Channel.Is_Channel(chan): 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="The channel must start with #")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} #channel") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} #channel")
return None return None
@@ -980,7 +618,7 @@ class Command:
nickname = str(cmd[1]) nickname = str(cmd[1])
chan = str(cmd[2]) chan = str(cmd[2])
if not self.Channel.Is_Channel(chan): 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="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") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} NICKNAME #CHANNEL")
return None return None
@@ -1051,7 +689,7 @@ class Command:
if len(cmd) == 2: if len(cmd) == 2:
channel_mode = cmd[1] channel_mode = cmd[1]
if self.Channel.Is_Channel(fromchannel): if self.Channel.is_valid_channel(fromchannel):
self.Protocol.send2socket(f":{dnickname} MODE {fromchannel} {channel_mode}") self.Protocol.send2socket(f":{dnickname} MODE {fromchannel} {channel_mode}")
else: else:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : Channel [{fromchannel}] is not correct should start with #") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : Channel [{fromchannel}] is not correct should start with #")
@@ -1146,7 +784,7 @@ class Command:
# .svsnick nickname newnickname # .svsnick nickname newnickname
nickname = str(cmd[1]) nickname = str(cmd[1])
newnickname = str(cmd[2]) newnickname = str(cmd[2])
unixtime = self.Base.get_unixtime() unixtime = self.MainUtils.get_unixtime()
if self.User.get_nickname(nickname) is None: 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") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" This nickname do not exist")
@@ -1192,7 +830,7 @@ class Command:
nickname = str(cmd[1]) nickname = str(cmd[1])
hostname = str(cmd[2]) hostname = str(cmd[2])
set_at_timestamp = self.Base.get_unixtime() set_at_timestamp = self.MainUtils.get_unixtime()
expire_time = (60 * 60 * 24) + set_at_timestamp expire_time = (60 * 60 * 24) + set_at_timestamp
gline_reason = ' '.join(cmd[3:]) gline_reason = ' '.join(cmd[3:])
@@ -1201,7 +839,7 @@ class Command:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason")
return None return None
self.Protocol.gline(nickname=nickname, hostname=hostname, set_by=dnickname, expire_timestamp=expire_time, set_at_timestamp=set_at_timestamp, reason=gline_reason) self.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: except KeyError as ke:
self.Logs.error(ke) self.Logs.error(ke)
@@ -1222,7 +860,7 @@ class Command:
hostname = str(cmd[2]) hostname = str(cmd[2])
# self.Protocol.send2socket(f":{self.Config.SERVEUR_ID} TKL - G {nickname} {hostname} {dnickname}") # self.Protocol.send2socket(f":{self.Config.SERVEUR_ID} TKL - G {nickname} {hostname} {dnickname}")
self.Protocol.ungline(nickname=nickname, hostname=hostname) self.Protocol.send_ungline(nickname=nickname, hostname=hostname)
except KeyError as ke: except KeyError as ke:
self.Logs.error(ke) self.Logs.error(ke)
@@ -1240,7 +878,7 @@ class Command:
nickname = str(cmd[1]) nickname = str(cmd[1])
hostname = str(cmd[2]) hostname = str(cmd[2])
set_at_timestamp = self.Base.get_unixtime() set_at_timestamp = self.MainUtils.get_unixtime()
expire_time = (60 * 60 * 24) + set_at_timestamp expire_time = (60 * 60 * 24) + set_at_timestamp
gline_reason = ' '.join(cmd[3:]) gline_reason = ' '.join(cmd[3:])
@@ -1249,7 +887,7 @@ class Command:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname host reason")
return None return None
self.Protocol.kline(nickname=nickname, hostname=hostname, set_by=dnickname, expire_timestamp=expire_time, set_at_timestamp=set_at_timestamp, reason=gline_reason) self.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: except KeyError as ke:
self.Logs.error(ke) self.Logs.error(ke)
@@ -1269,7 +907,7 @@ class Command:
nickname = str(cmd[1]) nickname = str(cmd[1])
hostname = str(cmd[2]) hostname = str(cmd[2])
self.Protocol.unkline(nickname=nickname, hostname=hostname) self.Protocol.send_unkline(nickname=nickname, hostname=hostname)
except KeyError as ke: except KeyError as ke:
self.Logs.error(ke) self.Logs.error(ke)
@@ -1288,7 +926,7 @@ class Command:
nickname = str(cmd[1]) nickname = str(cmd[1])
hostname = str(cmd[2]) hostname = str(cmd[2])
set_at_timestamp = self.Base.get_unixtime() set_at_timestamp = self.MainUtils.get_unixtime()
expire_time = (60 * 60 * 24) + set_at_timestamp expire_time = (60 * 60 * 24) + set_at_timestamp
shun_reason = ' '.join(cmd[3:]) shun_reason = ' '.join(cmd[3:])

237
mods/command/utils.py Normal file
View File

@@ -0,0 +1,237 @@
from typing import TYPE_CHECKING, Literal, Optional
if TYPE_CHECKING:
from mods.command.mod_command import Command
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
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)}")
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
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 -")
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)}")
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)}")
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_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",
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}")
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",
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}")
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}")
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(
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}")
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")
case 'list':
db_query = uplink.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,
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,
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)}")
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")
return None
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")
return None
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
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}")
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]))} '
if i == len(uid) - 1:
uplink.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:
dnickname = uplink.Config.SERVICE_NICKNAME
service_id = uplink.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]")
return False
if len(cmd) == 1:
uplink.Protocol.send2socket(f":{dnickname} MODE {channel_name} {mode} {client}")
return None
# deop nickname
if len(cmd) == 2:
nickname = cmd[1]
uplink.Protocol.send2socket(f":{service_id} MODE {channel_name} {mode} {nickname}")
return None
nickname = cmd[2]
uplink.Protocol.send2socket(f":{service_id} MODE {channel_name} {mode} {nickname}")
return None
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
if sentchannel is None:
uplink.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}')
return None
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
sentchannel = str(cmd[1]) if uplink.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]")
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}')
return None
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
sentchannel = str(cmd[1]) if uplink.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]")
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}')
def set_assign_channel_to_service(uplink: 'Command', cmd: list[str], client: str) -> None:
command = str(cmd[0])
dnickname = uplink.Config.SERVICE_NICKNAME
sent_channel = str(cmd[1]) if uplink.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]")
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)
return None
def set_unassign_channel_to_service(uplink: 'Command', cmd: list[str], client: str) -> None:
command = str(cmd[0])
dnickname = uplink.Config.SERVICE_NICKNAME
dchanlog = uplink.Config.SERVICE_CHANLOG
sent_channel = str(cmd[1]) if uplink.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]")
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 [!]")
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}")
uplink.Channel.db_query_channel('del', uplink.module_name, sent_channel)
return None

File diff suppressed because it is too large Load Diff

35
mods/defender/schemas.py Normal file
View File

@@ -0,0 +1,35 @@
from core.definition import MainModel, dataclass, MUser
@dataclass
class ModConfModel(MainModel):
reputation: int = 0
reputation_timer: int = 1
reputation_seuil: int = 26
reputation_score_after_release: int = 27
reputation_ban_all_chan: int = 0
reputation_sg: int = 1
local_scan: int = 0
psutil_scan: int = 0
abuseipdb_scan: int = 0
freeipapi_scan: int = 0
cloudfilt_scan: int = 0
flood: int = 0
flood_message: int = 5
flood_time: int = 1
flood_timer: int = 20
autolimit: int = 0
autolimit_amount: int = 3
autolimit_interval: int = 3
@dataclass
class FloodUser(MainModel):
uid: str = None
nbr_msg: int = 0
first_msg_time: int = 0
DB_FLOOD_USERS: list[FloodUser] = []
DB_ABUSEIPDB_USERS: list[MUser] = []
DB_FREEIPAPI_USERS: list[MUser] = []
DB_CLOUDFILT_USERS: list[MUser] = []
DB_PSUTIL_USERS: list[MUser] = []
DB_LOCALSCAN_USERS: list[MUser] = []

167
mods/defender/threads.py Normal file
View File

@@ -0,0 +1,167 @@
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'):
while uplink.reputationTimer_isRunning:
uplink.Utils.action_apply_reputation_santions(uplink)
sleep(5)
def thread_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)
list_to_remove.append(user)
sleep(1)
for user_model in list_to_remove:
uplink.Schemas.DB_CLOUDFILT_USERS.remove(user_model)
sleep(1)
def thread_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)
list_to_remove.append(user)
sleep(1)
for user_model in list_to_remove:
uplink.Schemas.DB_FREEIPAPI_USERS.remove(user_model)
sleep(1)
def thread_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)
list_to_remove.append(user)
sleep(1)
for user_model in list_to_remove:
uplink.Schemas.DB_ABUSEIPDB_USERS.remove(user_model)
sleep(1)
def thread_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)
list_to_remove.append(user)
sleep(1)
for user_model in list_to_remove:
uplink.Schemas.DB_LOCALSCAN_USERS.remove(user_model)
sleep(1)
def thread_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)
list_to_remove.append(user)
sleep(1)
for user_model in list_to_remove:
uplink.Schemas.DB_PSUTIL_USERS.remove(user_model)
sleep(1)
def thread_autolimit(uplink: 'Defender'):
if uplink.ModConfig.autolimit == 0:
uplink.Logs.debug("autolimit deactivated ... canceling the thread")
return None
while uplink.Irc.autolimit_started:
sleep(0.2)
uplink.Irc.autolimit_started = True
init_amount = uplink.ModConfig.autolimit_amount
p = uplink.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]
while uplink.autolimit_isRunning:
if uplink.ModConfig.autolimit == 0:
uplink.Logs.debug("autolimit deactivated ... stopping the current thread")
break
for chan in uplink.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}")
chan_copy["uids_count"] = len(chan.uids)
if chan.name not in chan_list:
chan_list.append(chan.name)
chanObj_copy.append({"name": chan.name, "uids_count": 0})
# Verifier si un salon a été vidé
current_chan_in_list = [d.name for d in uplink.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}")
# 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}")
INIT = 0
if uplink.autolimit_isRunning:
sleep(uplink.ModConfig.autolimit_interval)
for chan in uplink.Channel.UID_CHANNEL_DB:
p.send2socket(f":{uplink.Config.SERVICE_ID} MODE {chan.name} -l")
uplink.Irc.autolimit_started = False
return None
def timer_release_mode_mute(uplink: 'Defender', action: str, channel: str):
"""DO NOT EXECUTE THIS FUNCTION WITHOUT THREADING
Args:
action (str): _description_
channel (str): The related channel
"""
service_id = uplink.Config.SERVICE_ID
if not uplink.Channel.is_valid_channel(channel):
uplink.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")
case _:
pass

710
mods/defender/utils.py Normal file
View File

@@ -0,0 +1,710 @@
from calendar import c
import socket
import psutil
import requests
import mods.defender.threads as dthreads
from json import loads
from re import match
from typing import TYPE_CHECKING, Optional
from mods.defender.schemas import FloodUser
if TYPE_CHECKING:
from core.definition import MUser
from mods.defender.mod_defender import Defender
def handle_on_reputation(uplink: 'Defender', srvmsg: list[str]):
"""Handle reputation server message
>>> srvmsg = [':001', 'REPUTATION', '128.128.128.128', '0']
>>> srvmsg = [':001', 'REPUTATION', '128.128.128.128', '*0']
Args:
irc_instance (Irc): The Irc instance
srvmsg (list[str]): The Server MSG
"""
ip = srvmsg[2]
score = srvmsg[3]
if str(ip).find('*') != -1:
# If the reputation changed, we do not need to scan the IP
return
# Possibilité de déclancher les bans a ce niveau.
if not uplink.Base.is_valid_ip(ip):
return
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']
Args:
irc_instance (Irc): The Irc instance
srvmsg (list[str]): The Server MSG
confmodel (ModConfModel): The Module Configuration
"""
irc = uplink.Irc
gconfig = uplink.Config
p = uplink.Protocol
confmodel = uplink.ModConfig
channel = str(srvmsg[3])
mode = str(srvmsg[4])
group_to_check = str(srvmsg[5:])
group_to_unban = '~security-group:unknown-users'
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}")
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")
def handle_on_privmsg(uplink: 'Defender', srvmsg: list[str]):
# ['@mtag....',':python', 'PRIVMSG', '#defender', ':zefzefzregreg', 'regg', 'aerg']
action_on_flood(uplink, srvmsg)
return None
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']
Args:
irc_instance (Irc): The Irc instance
srvmsg (list[str]): The Server MSG
confmodel (ModConfModel): The Module Configuration
"""
irc = uplink.Irc
p = irc.Protocol
gconfig = uplink.Config
confmodel = uplink.ModConfig
parsed_chan = srvmsg[4] if irc.Channel.is_valid_channel(srvmsg[4]) else None
parsed_UID = uplink.Loader.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)
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")
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)
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}")
irc.Logs.debug(f'SJOIN parsed_uid : {parsed_UID}')
def handle_on_slog(uplink: 'Defender', srvmsg: list[str]):
"""Handling SLOG messages
>>> srvmsg = ['@unrealircd...', ':001', 'SLOG', 'info', 'blacklist', 'BLACKLIST_HIT', ':[Blacklist]', 'IP', '162.x.x.x', 'matches', 'blacklist', 'dronebl', '(dnsbl.dronebl.org/reply=6)']
Args:
irc_instance (Irc): The Irc instance
srvmsg (list[str]): The Server MSG
confmodel (ModConfModel): The Module Configuration
"""
['@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]):
return None
# if self.ModConfig.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:
# self.psutil_remote_ip.append(cmd[7])
# if self.ModConfig.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:
# self.freeipapi_remote_ip.append(cmd[7])
# if self.ModConfig.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]):
"""_summary_
>>> srvmsg = ['@unrealircd.org...', ':001MZQ0RB', 'NICK', 'newnickname', '1754663712']
Args:
irc_instance (Irc): The Irc instance
srvmsg (list[str]): The Server MSG
confmodel (ModConfModel): The Module Configuration
"""
uid = uplink.Loader.Utils.clean_uid(str(srvmsg[1]))
p = uplink.Protocol
confmodel = uplink.ModConfig
get_reputation = uplink.Reputation.get_Reputation(uid)
jail_salon = uplink.Config.SALON_JAIL
service_id = uplink.Config.SERVICE_ID
if get_reputation is None:
uplink.Logs.debug(f'This UID: {uid} is not listed in the reputation dataclass')
return None
# Update the new nickname
oldnick = get_reputation.nickname
newnickname = srvmsg[3]
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:
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}!*@*")
def handle_on_quit(uplink: 'Defender', srvmsg: list[str]):
"""_summary_
>>> srvmsg = ['@unrealircd.org...', ':001MZQ0RB', 'QUIT', ':Quit:', 'quit message']
Args:
uplink (Irc): The Defender Module instance
srvmsg (list[str]): The Server MSG
"""
p = uplink.Protocol
confmodel = uplink.ModConfig
ban_all_chan = uplink.Base.int_if_possible(confmodel.reputation_ban_all_chan)
final_UID = uplink.Loader.Utils.clean_uid(str(srvmsg[1]))
jail_salon = uplink.Config.SALON_JAIL
service_id = uplink.Config.SERVICE_ID
get_user_reputation = uplink.Reputation.get_Reputation(final_UID)
if get_user_reputation is not None:
final_nickname = get_user_reputation.nickname
for chan in uplink.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}")
uplink.Reputation.delete(final_UID)
uplink.Logs.debug(f"Client {get_user_reputation.nickname} has been removed from Reputation local DB")
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']
Args:
uplink (Defender): The Defender instance
srvmsg (list[str]): The Server MSG
"""
gconfig = uplink.Config
irc = uplink.Irc
confmodel = uplink.ModConfig
# If Init then do nothing
if gconfig.DEFENDER_INIT == 1:
return None
# Get User information
_User = irc.User.get_User(str(srvmsg[8]))
if _User is None:
irc.Logs.warning(f'This UID: [{srvmsg[8]}] is not available please check why')
return
# If user is not service or IrcOp then scan them
if not match(r'^.*[S|o?].*$', _User.umodes):
uplink.Schemas.DB_ABUSEIPDB_USERS.append(_User) if confmodel.abuseipdb_scan == 1 and _User.remote_ip not in gconfig.WHITELISTED_IP else None
uplink.Schemas.DB_FREEIPAPI_USERS.append(_User) if confmodel.freeipapi_scan == 1 and _User.remote_ip not in gconfig.WHITELISTED_IP else None
uplink.Schemas.DB_CLOUDFILT_USERS.append(_User) if confmodel.cloudfilt_scan == 1 and _User.remote_ip not in gconfig.WHITELISTED_IP else None
uplink.Schemas.DB_PSUTIL_USERS.append(_User) if confmodel.psutil_scan == 1 and _User.remote_ip not in gconfig.WHITELISTED_IP else None
uplink.Schemas.DB_LOCALSCAN_USERS.append(_User) if confmodel.local_scan == 1 and _User.remote_ip not in gconfig.WHITELISTED_IP else None
reputation_flag = confmodel.reputation
reputation_seuil = confmodel.reputation_seuil
if gconfig.DEFENDER_INIT == 0:
# Si le user n'es pas un service ni un IrcOP
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(
**_User.to_dict(),
secret_code=irc.Utils.generate_random_string(8)
)
)
if irc.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})')
####################
# ACTION FUNCTIONS #
####################
def action_on_flood(uplink: 'Defender', srvmsg: list[str]):
confmodel = uplink.ModConfig
if confmodel.flood == 0:
return None
irc = uplink.Irc
gconfig = uplink.Config
p = uplink.Protocol
flood_users = uplink.Schemas.DB_FLOOD_USERS
user_trigger = str(srvmsg[1]).replace(':','')
channel = srvmsg[3]
User = irc.User.get_User(user_trigger)
if User is None or not irc.Channel.is_valid_channel(channel_to_check=channel):
return
flood_time = confmodel.flood_time
flood_message = confmodel.flood_message
flood_timer = confmodel.flood_timer
service_id = gconfig.SERVICE_ID
dnickname = gconfig.SERVICE_NICKNAME
color_red = gconfig.COLORS.red
color_bold = gconfig.COLORS.bold
get_detected_uid = User.uid
get_detected_nickname = User.nickname
unixtime = irc.Utils.get_unixtime()
get_diff_secondes = 0
def get_flood_user(uid: str) -> Optional[FloodUser]:
for flood_user in flood_users:
if flood_user.uid == uid:
return flood_user
fu = get_flood_user(get_detected_uid)
if fu is None:
fu = FloodUser(get_detected_uid, 0, unixtime)
flood_users.append(fu)
fu.nbr_msg += 1
get_diff_secondes = unixtime - fu.first_msg_time
if get_diff_secondes > flood_time:
fu.first_msg_time = unixtime
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(
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}')
fu.nbr_msg = 0
fu.first_msg_time = unixtime
irc.Base.create_timer(flood_timer, dthreads.timer_release_mode_mute, (uplink, 'mode-m', channel))
def action_add_reputation_sanctions(uplink: 'Defender', jailed_uid: str ):
irc = uplink.Irc
gconfig = uplink.Config
p = uplink.Protocol
confmodel = uplink.ModConfig
get_reputation = irc.Reputation.get_Reputation(jailed_uid)
if get_reputation is None:
irc.Logs.warning(f'UID {jailed_uid} has not been found')
return
salon_logs = gconfig.SERVICE_CHANLOG
salon_jail = gconfig.SALON_JAIL
code = get_reputation.secret_code
jailed_nickname = get_reputation.nickname
jailed_score = get_reputation.score_connexion
color_red = gconfig.COLORS.red
color_black = gconfig.COLORS.black
color_bold = gconfig.COLORS.bold
nogc = gconfig.COLORS.nogc
service_id = gconfig.SERVICE_ID
service_prefix = gconfig.SERVICE_PREFIX
reputation_ban_all_chan = confmodel.reputation_ban_all_chan
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,
msg=f" [{color_red} REPUTATION {nogc}] : Connexion de {jailed_nickname} ({jailed_score}) ==> {salon_jail}",
channel=salon_logs
)
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:
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}")
irc.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)
def action_apply_reputation_santions(uplink: 'Defender') -> None:
irc = uplink.Irc
gconfig = uplink.Config
p = uplink.Protocol
confmodel = uplink.ModConfig
reputation_flag = confmodel.reputation
reputation_timer = confmodel.reputation_timer
reputation_seuil = confmodel.reputation_seuil
ban_all_chan = confmodel.reputation_ban_all_chan
service_id = gconfig.SERVICE_ID
dchanlog = gconfig.SERVICE_CHANLOG
color_red = gconfig.COLORS.red
nogc = gconfig.COLORS.nogc
salon_jail = gconfig.SALON_JAIL
if reputation_flag == 0:
return None
elif reputation_timer == 0:
return None
uid_to_clean = []
for user in irc.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(
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")
irc.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:
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}!*@*")
# 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)
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:
uplink (Defender): Defender Instance
Returns:
dict[str, any] | None: les informations du provider
keys : 'countryCode', 'isProxy'
"""
remote_ip = user_model.remote_ip
username = user_model.username
hostname = user_model.hostname
nickname = user_model.nickname
p = uplink.Protocol
if remote_ip in uplink.Config.WHITELISTED_IP:
return None
if uplink.ModConfig.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
url = "https://developers18334.cloudfilt.com/"
data = {
'ip': remote_ip,
'key': uplink.cloudfilt_key
}
response = requests.post(url=url, data=data)
# Formatted output
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)}')
return
result = {
'countryiso': decoded_response.get('countryiso', None),
'listed': decoded_response.get('listed', None),
'listed_by': decoded_response.get('listed_by', None),
'host': decoded_response.get('host', None)
}
# pseudo!ident@host
fullname = f'{nickname}!{username}@{hostname}'
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']}")
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']}")
response.close()
return result
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:
uplink (Defender): The Defender object Instance
Returns:
dict[str, any] | None: les informations du provider
keys : 'countryCode', 'isProxy'
"""
p = uplink.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:
return None
if uplink.ModConfig.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
url = f'https://freeipapi.com/api/json/{remote_ip}'
headers = {
'Accept': 'application/json',
}
response = requests.request(method='GET', url=url, headers=headers, timeout=uplink.timeout)
# Formatted output
decoded_response: dict = loads(response.text)
status_code = response.status_code
if status_code == 429:
uplink.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)}')
return None
result = {
'countryCode': decoded_response.get('countryCode', None),
'isProxy': decoded_response.get('isProxy', None)
}
# pseudo!ident@host
fullname = f'{nickname}!{username}@{hostname}'
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']}")
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']}")
response.close()
return result
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:
uplink (Defender): Defender instance object
user_model (MUser): l'objet User qui contient l'ip
Returns:
dict[str, str] | None: les informations du provider
"""
p = uplink.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:
return None
if uplink.ModConfig.abuseipdb_scan == 0:
return None
if uplink.abuseipdb_key == '':
return None
url = 'https://api.abuseipdb.com/api/v2/check'
querystring = {
'ipAddress': remote_ip,
'maxAgeInDays': '90'
}
headers = {
'Accept': 'application/json',
'Key': uplink.abuseipdb_key
}
response = requests.request(method='GET', url=url, headers=headers, params=querystring, timeout=uplink.timeout)
# Formatted output
decoded_response: dict[str, dict] = loads(response.text)
if 'data' not in decoded_response:
return None
result = {
'score': decoded_response.get('data', {}).get('abuseConfidenceScore', 0),
'country': decoded_response.get('data', {}).get('countryCode', None),
'isTor': decoded_response.get('data', {}).get('isTor', None),
'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
# pseudo!ident@host
fullname = f'{nickname}!{username}@{hostname}'
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']}")
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']}")
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']}")
response.close()
return result
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
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:
return None
for port in uplink.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))
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
)
# print(f"=======> Le port {str(port)} est ouvert !!")
uplink.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é")
except AttributeError as ae:
uplink.Logs.warning(f"AttributeError ({remote_ip}): {ae}")
except socket.gaierror as err:
uplink.Logs.warning(f"Address Info Error ({remote_ip}): {err}")
finally:
# newSocket.shutdown(socket.SHUT_RDWR)
newSocket.close()
uplink.Logs.info('=======> Fermeture de la socket')
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:
userModel (UserModel): The User Model Object
Returns:
list[int]: list of ports
"""
p = uplink.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:
return None
try:
connections = psutil.net_connections(kind='inet')
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)}")
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
)
return matching_ports
except psutil.AccessDenied as ad:
uplink.Logs.critical(f'psutil_scan: Permission error: {ad}')

View File

@@ -1,7 +1,12 @@
import logging import logging
import asyncio
import mods.jsonrpc.utils as utils
import mods.jsonrpc.threads as thds
from time import sleep
from types import SimpleNamespace
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from dataclasses import dataclass from dataclasses import dataclass
from unrealircd_rpc_py.Live import LiveWebsocket from unrealircd_rpc_py.Live import LiveWebsocket, LiveUnixSocket
from unrealircd_rpc_py.Loader import Loader from unrealircd_rpc_py.Loader import Loader
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -32,8 +37,11 @@ class Jsonrpc():
# Add Base object to the module (Mandatory) # Add Base object to the module (Mandatory)
self.Base = ircInstance.Base self.Base = ircInstance.Base
# Add Main Utils (Mandatory)
self.MainUtils = ircInstance.Utils
# Add logs object to the module (Mandatory) # Add logs object to the module (Mandatory)
self.Logs = ircInstance.Base.logs self.Logs = ircInstance.Loader.Logs
# Add User object to the module (Mandatory) # Add User object to the module (Mandatory)
self.User = ircInstance.User self.User = ircInstance.User
@@ -41,9 +49,22 @@ class Jsonrpc():
# Add Channel object to the module (Mandatory) # Add Channel object to the module (Mandatory)
self.Channel = ircInstance.Channel 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) # 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, '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, '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 # Init the module
self.__init_module() self.__init_module()
@@ -55,6 +76,7 @@ class Jsonrpc():
logging.getLogger('websockets').setLevel(logging.WARNING) logging.getLogger('websockets').setLevel(logging.WARNING)
logging.getLogger('unrealircd-rpc-py').setLevel(logging.CRITICAL) logging.getLogger('unrealircd-rpc-py').setLevel(logging.CRITICAL)
logging.getLogger('unrealircd-liverpc-py').setLevel(logging.CRITICAL)
# Create you own tables (Mandatory) # Create you own tables (Mandatory)
# self.__create_tables() # self.__create_tables()
@@ -70,15 +92,15 @@ class Jsonrpc():
callback_object_instance=self, callback_object_instance=self,
callback_method_or_function_name='callback_sent_to_irc' callback_method_or_function_name='callback_sent_to_irc'
) )
if self.UnrealIrcdRpcLive.get_error.code != 0: if self.UnrealIrcdRpcLive.get_error.code != 0:
self.Logs.error(self.UnrealIrcdRpcLive.get_error.code, self.UnrealIrcdRpcLive.get_error.message) self.Logs.error(f"{self.UnrealIrcdRpcLive.get_error.message} ({self.UnrealIrcdRpcLive.get_error.code})")
self.Protocol.send_priv_msg( self.Protocol.send_priv_msg(
nick_from=self.Config.SERVICE_NICKNAME, nick_from=self.Config.SERVICE_NICKNAME,
msg=f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.UnrealIrcdRpcLive.get_error.message}", msg=f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.UnrealIrcdRpcLive.get_error.message}",
channel=self.Config.SERVICE_CHANLOG channel=self.Config.SERVICE_CHANLOG
) )
return raise Exception(f"[LIVE-JSONRPC ERROR] {self.UnrealIrcdRpcLive.get_error.message}")
self.Rpc: Loader = Loader( self.Rpc: Loader = Loader(
req_method=self.Config.JSONRPC_METHOD, req_method=self.Config.JSONRPC_METHOD,
@@ -88,18 +110,17 @@ class Jsonrpc():
) )
if self.Rpc.get_error.code != 0: if self.Rpc.get_error.code != 0:
self.Logs.error(self.Rpc.get_error.code, self.Rpc.get_error.message) self.Logs.error(f"{self.Rpc.get_error.message} ({self.Rpc.get_error.code})")
self.Protocol.send_priv_msg( self.Protocol.send_priv_msg(
nick_from=self.Config.SERVICE_NICKNAME, nick_from=self.Config.SERVICE_NICKNAME,
msg=f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.Rpc.get_error.message}", msg=f"[{self.Config.COLORS.red}JSONRPC ERROR{self.Config.COLORS.nogc}] {self.Rpc.get_error.message}",
channel=self.Config.SERVICE_CHANLOG channel=self.Config.SERVICE_CHANLOG
) )
raise Exception(f"[JSONRPC ERROR] {self.Rpc.get_error.message}")
self.subscribed = False
if self.ModConfig.jsonrpc == 1: if self.ModConfig.jsonrpc == 1:
self.Base.create_thread(self.thread_start_jsonrpc, run_once=True) self.Base.create_thread(func=self.Threads.thread_subscribe, func_args=(self, ), run_once=True)
return None return None
def __create_tables(self) -> None: def __create_tables(self) -> None:
@@ -122,7 +143,7 @@ class Jsonrpc():
self.Base.db_execute_query(table_logs) self.Base.db_execute_query(table_logs)
return None return None
def callback_sent_to_irc(self, response): def callback_sent_to_irc(self, response: SimpleNamespace) -> None:
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME
dchanlog = self.Config.SERVICE_CHANLOG dchanlog = self.Config.SERVICE_CHANLOG
@@ -131,13 +152,29 @@ class Jsonrpc():
bold = self.Config.COLORS.bold bold = self.Config.COLORS.bold
red = self.Config.COLORS.red red = self.Config.COLORS.red
if hasattr(response, 'result'): if self.UnrealIrcdRpcLive.get_error.code != 0:
if isinstance(response.result, bool) and response.result: self.Protocol.send_priv_msg(nick_from=dnickname,
msg=f"[{bold}{red}JSONRPC ERROR{nogc}{bold}] {self.UnrealIrcdRpcLive.get_error.message}",
channel=dchanlog)
return None
if hasattr(response, 'error'):
if response.error.code != 0:
self.Protocol.send_priv_msg( self.Protocol.send_priv_msg(
nick_from=self.Config.SERVICE_NICKNAME, nick_from=self.Config.SERVICE_NICKNAME,
msg=f"[{bold}{green}JSONRPC{nogc}{bold}] Event activated", msg=f"[{bold}{red}JSONRPC{nogc}{bold}] JSONRPC Event activated on {self.Config.JSONRPC_URL}",
channel=dchanlog) channel=dchanlog)
return None
return None
if hasattr(response, 'result'):
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}",
channel=dchanlog)
return None
level = response.result.level if hasattr(response.result, 'level') else '' level = response.result.level if hasattr(response.result, 'level') else ''
subsystem = response.result.subsystem if hasattr(response.result, 'subsystem') else '' subsystem = response.result.subsystem if hasattr(response.result, 'subsystem') else ''
@@ -146,24 +183,9 @@ class Jsonrpc():
msg = response.result.msg if hasattr(response.result, 'msg') else '' msg = response.result.msg if hasattr(response.result, 'msg') else ''
build_msg = f"{green}{log_source}{nogc}: [{bold}{level}{bold}] {subsystem}.{event_id} - {msg}" build_msg = f"{green}{log_source}{nogc}: [{bold}{level}{bold}] {subsystem}.{event_id} - {msg}"
# Check if there is an error
if self.UnrealIrcdRpcLive.get_error.code != 0:
self.Logs.error(f"RpcLiveError: {self.UnrealIrcdRpcLive.get_error.message}")
self.Protocol.send_priv_msg(nick_from=dnickname, msg=build_msg, channel=dchanlog) self.Protocol.send_priv_msg(nick_from=dnickname, msg=build_msg, channel=dchanlog)
def thread_start_jsonrpc(self): return None
if self.UnrealIrcdRpcLive.get_error.code == 0:
self.UnrealIrcdRpcLive.subscribe(["all"])
self.subscribed = True
else:
self.Protocol.send_priv_msg(
nick_from=self.Config.SERVICE_NICKNAME,
msg=f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.UnrealIrcdRpcLive.get_error.message}",
channel=self.Config.SERVICE_CHANLOG
)
def __load_module_configuration(self) -> None: def __load_module_configuration(self) -> None:
"""### Load Module Configuration """### Load Module Configuration
@@ -180,7 +202,7 @@ class Jsonrpc():
except TypeError as te: except TypeError as te:
self.Logs.critical(te) self.Logs.critical(te)
def __update_configuration(self, param_key: str, param_value: str): def update_configuration(self, param_key: str, param_value: str) -> None:
"""Update the local and core configuration """Update the local and core configuration
Args: Args:
@@ -190,11 +212,18 @@ class Jsonrpc():
self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value) self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
def unload(self) -> None: def unload(self) -> None:
if self.UnrealIrcdRpcLive.Error.code != -1: if self.is_streaming:
self.UnrealIrcdRpcLive.unsubscribe() 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
)
self.Base.create_thread(func=self.Threads.thread_unsubscribe, func_args=(self, ), run_once=True)
self.update_configuration('jsonrpc', 0)
self.Logs.debug(f"Unloading {self.module_name}")
return None return None
def cmd(self, data:list) -> None: def cmd(self, data: list) -> None:
return None return None
@@ -210,54 +239,42 @@ class Jsonrpc():
case 'jsonrpc': case 'jsonrpc':
try: try:
option = str(cmd[1]).lower() if len(cmd) < 2:
if len(command) == 1:
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 on')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jsonrpc off') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jsonrpc off')
return None
option = str(cmd[1]).lower()
match option: match option:
case 'on': 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
# for logger_name, logger in logging.root.manager.loggerDict.items(): self.Base.create_thread(func=self.Threads.thread_subscribe, func_args=(self, ), run_once=True)
# if isinstance(logger, logging.Logger): self.update_configuration('jsonrpc', 1)
# self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{logger_name} - {logger.level}")
for thread in self.Base.running_threads:
if thread.name == 'thread_start_jsonrpc':
if thread.is_alive():
self.Protocol.send_priv_msg(
nick_from=self.Config.SERVICE_NICKNAME,
msg=f"Thread {thread.name} is running",
channel=dchannel
)
else:
self.Protocol.send_priv_msg(
nick_from=self.Config.SERVICE_NICKNAME,
msg=f"Thread {thread.name} is not running, wait untill the process will be cleaned up",
channel=dchannel
)
self.Base.create_thread(self.thread_start_jsonrpc, run_once=True)
self.__update_configuration('jsonrpc', 1)
case 'off': case 'off':
self.UnrealIrcdRpcLive.unsubscribe() self.Base.create_thread(func=self.Threads.thread_unsubscribe, func_args=(self, ), run_once=True)
self.__update_configuration('jsonrpc', 0) self.update_configuration('jsonrpc', 0)
except IndexError as ie: except IndexError as ie:
self.Logs.error(ie) self.Logs.error(ie)
case 'jruser': case 'jruser':
try: try:
option = str(cmd[1]).lower() if len(cmd) < 2:
if len(command) == 1:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jruser get nickname') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jruser get nickname')
option = str(cmd[1]).lower()
match option: match option:
case 'get': case 'get':
nickname = str(cmd[2]) nickname = str(cmd[2])
uid_to_get = self.User.get_uid(nickname) uid_to_get = self.User.get_uid(nickname)
@@ -271,16 +288,13 @@ class Jsonrpc():
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'{rpc.get_error.message}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'{rpc.get_error.message}')
return None return None
chan_list = []
for chan in UserInfo.user.channels:
chan_list.append(chan.name)
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'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'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'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'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'MODES : {UserInfo.user.modes}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CHANNELS : {chan_list}') 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'SECURITY GROUP : {UserInfo.user.security_groups}')
self.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'REPUTATION : {UserInfo.user.reputation}')
@@ -303,22 +317,12 @@ class Jsonrpc():
except IndexError as ie: except IndexError as ie:
self.Logs.error(ie) self.Logs.error(ie)
case 'ia': case 'jrinstances':
try: try:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"GC Collect: {self.MainUtils.run_python_garbage_collector()}")
self.Base.create_thread(self.thread_ask_ia, ('',)) self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nombre d'instance LiveWebsock: {self.MainUtils.get_number_gc_objects(LiveWebsocket)}")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nombre d'instance LiveUnixSocket: {self.MainUtils.get_number_gc_objects(LiveUnixSocket)}")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" This is a notice to the sender ...") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nombre d'instance Loader: {self.MainUtils.get_number_gc_objects(Loader)}")
self.Protocol.send_priv_msg(nick_from=dnickname, msg="This is private message to the sender ...", nick_to=fromuser) self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nombre de toute les instances: {self.MainUtils.get_number_gc_objects()}")
if not fromchannel is None:
self.Protocol.send_priv_msg(nick_from=dnickname, msg="This is channel message to the sender ...", channel=fromchannel)
# How to update your module configuration
self.__update_configuration('param_exemple2', 7)
# Log if you want the result
self.Logs.debug(f"Test logs ready")
except Exception as err: except Exception as err:
self.Logs.error(f"Unknown Error: {err}") self.Logs.error(f"Unknown Error: {err}")

60
mods/jsonrpc/threads.py Normal file
View File

@@ -0,0 +1,60 @@
import asyncio
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from mods.jsonrpc.mod_jsonrpc import Jsonrpc
def thread_subscribe(uplink: 'Jsonrpc') -> None:
response: dict[str, dict] = {}
snickname = uplink.Config.SERVICE_NICKNAME
schannel = uplink.Config.SERVICE_CHANLOG
if uplink.UnrealIrcdRpcLive.get_error.code == 0:
uplink.is_streaming = True
response = asyncio.run(uplink.UnrealIrcdRpcLive.subscribe(["all"]))
else:
uplink.Protocol.send_priv_msg(nick_from=snickname,
msg=f"[{uplink.Config.COLORS.red}JSONRPC ERROR{uplink.Config.COLORS.nogc}] {uplink.UnrealIrcdRpcLive.get_error.message}",
channel=schannel
)
if response is None:
return
code = response.get('error', {}).get('code', 0)
message = response.get('error', {}).get('message', None)
if code == 0:
uplink.Protocol.send_priv_msg(
nick_from=snickname,
msg=f"[{uplink.Config.COLORS.green}JSONRPC{uplink.Config.COLORS.nogc}] Stream is OFF",
channel=schannel
)
else:
uplink.Protocol.send_priv_msg(
nick_from=snickname,
msg=f"[{uplink.Config.COLORS.red}JSONRPC{uplink.Config.COLORS.nogc}] Stream has crashed! {code} - {message}",
channel=schannel
)
def thread_unsubscribe(uplink: 'Jsonrpc') -> None:
response: dict[str, dict] = asyncio.run(uplink.UnrealIrcdRpcLive.unsubscribe())
uplink.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
if response is None:
return None
code = response.get('error', {}).get('code', 0)
message = response.get('error', {}).get('message', None)
if code != 0:
uplink.Protocol.send_priv_msg(
nick_from=snickname,
msg=f"[{uplink.Config.COLORS.red}JSONRPC ERROR{uplink.Config.COLORS.nogc}] {message} ({code})",
channel=schannel
)

0
mods/jsonrpc/utils.py Normal file
View File

View File

@@ -34,7 +34,7 @@ class Test():
self.Base = ircInstance.Base self.Base = ircInstance.Base
# Add logs object to the module (Mandatory) # Add logs object to the module (Mandatory)
self.Logs = ircInstance.Base.logs self.Logs = ircInstance.Loader.Logs
# Add User object to the module (Mandatory) # Add User object to the module (Mandatory)
self.User = ircInstance.User self.User = ircInstance.User

View File

@@ -1,59 +1,75 @@
from typing import TYPE_CHECKING """
File : mod_votekick.py
Version : 1.0.0
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
-----------------------------------------
"""
import re import re
from dataclasses import dataclass, field 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: if TYPE_CHECKING:
from core.irc import Irc from core.irc import Irc
# Activer le systeme sur un salon (activate #salon)
# Le service devra se connecter au salon
# Le service devra se mettre en op
# Soumettre un nom de user (submit nickname)
# voter pour un ban (vote_for)
# voter contre un ban (vote_against)
class Votekick:
def __init__(self, uplink: 'Irc') -> None:
class Votekick():
@dataclass
class VoteChannelModel:
channel_name: str
target_user: str
voter_users: list
vote_for: int
vote_against: int
VOTE_CHANNEL_DB:list[VoteChannelModel] = []
def __init__(self, ircInstance: 'Irc') -> None:
# Module name (Mandatory) # Module name (Mandatory)
self.module_name = 'mod_' + str(self.__class__.__name__).lower() self.module_name = 'mod_' + str(self.__class__.__name__).lower()
# Add Irc Object to the module # Add Irc Object to the module
self.Irc = ircInstance self.Irc = uplink
# Add Loader Object to the module (Mandatory) # Add Loader Object to the module (Mandatory)
self.Loader = ircInstance.Loader self.Loader = uplink.Loader
# Add server protocol Object to the module (Mandatory) # Add server protocol Object to the module (Mandatory)
self.Protocol = ircInstance.Protocol self.Protocol = uplink.Protocol
# Add Global Configuration to the module # Add Global Configuration to the module
self.Config = ircInstance.Config self.Config = uplink.Config
# Add Base object to the module # Add Base object to the module
self.Base = ircInstance.Base self.Base = uplink.Base
# Add logs object to the module # Add logs object to the module
self.Logs = ircInstance.Base.logs self.Logs = uplink.Logs
# Add User object to the module # Add User object to the module
self.User = ircInstance.User self.User = uplink.User
# Add Channel object to the module # Add Channel object to the module
self.Channel = ircInstance.Channel 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 # Créer les nouvelles commandes du module
self.Irc.build_command(1, self.module_name, 'vote', 'The kick vote module') self.Irc.build_command(1, self.module_name, 'vote', 'The kick vote module')
@@ -69,15 +85,13 @@ class Votekick():
# Add admin object to retrieve admin users # Add admin object to retrieve admin users
self.Admin = self.Irc.Admin self.Admin = self.Irc.Admin
self.__create_tables() self.__create_tables()
self.join_saved_channels() self.ModUtils.join_saved_channels(self)
return None 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. """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 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: Returns:
None: Aucun retour n'es attendu None: Aucun retour n'es attendu
@@ -103,11 +117,14 @@ class Votekick():
def unload(self) -> None: def unload(self) -> None:
try: try:
for chan in self.VOTE_CHANNEL_DB: # Cache the local DB with current votes.
self.Loader.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) self.Protocol.send_part_chan(uidornickname=self.Config.SERVICE_ID, channel=chan.channel_name)
self.VOTE_CHANNEL_DB = [] self.VoteKickManager.VOTE_CHANNEL_DB = []
self.Logs.debug(f'Delete memory DB VOTE_CHANNEL_DB: {self.VOTE_CHANNEL_DB}') self.Logs.debug(f'Delete memory DB VOTE_CHANNEL_DB: {self.VoteKickManager.VOTE_CHANNEL_DB}')
return None return None
except UnboundLocalError as ne: except UnboundLocalError as ne:
@@ -117,137 +134,28 @@ class Votekick():
except Exception as err: except Exception as err:
self.Logs.error(f'General Error: {err}') self.Logs.error(f'General Error: {err}')
def init_vote_system(self, channel: str) -> bool: def cmd(self, data: list) -> None:
response = False if not data or len(data) < 2:
for chan in self.VOTE_CHANNEL_DB: return None
if chan.channel_name == channel:
chan.target_user = ''
chan.voter_users = []
chan.vote_against = 0
chan.vote_for = 0
response = True
return response cmd = data.copy() if isinstance(data, list) else list(data).copy()
index, command = self.Irc.Protocol.get_ircd_protocol_poisition(cmd)
def insert_vote_channel(self, ChannelObject: VoteChannelModel) -> bool: if index == -1:
result = False
found = False
for chan in self.VOTE_CHANNEL_DB:
if chan.channel_name == ChannelObject.channel_name:
found = True
if not found:
self.VOTE_CHANNEL_DB.append(ChannelObject)
self.Logs.debug(f"The channel has been added {ChannelObject}")
# self.db_add_vote_channel(ChannelObject.channel_name)
return result
def db_add_vote_channel(self, channel:str) -> bool:
"""Cette fonction ajoute les salons ou seront autoriser les votes
Args:
channel (str): le salon à enregistrer.
"""
current_datetime = self.Base.get_datetime()
mes_donnees = {'channel': channel}
response = self.Base.db_execute_query("SELECT id FROM votekick_channel WHERE channel = :channel", mes_donnees)
isChannelExist = response.fetchone()
if isChannelExist is None:
mes_donnees = {'datetime': current_datetime, 'channel': channel}
insert = self.Base.db_execute_query(f"INSERT INTO votekick_channel (datetime, channel) VALUES (:datetime, :channel)", mes_donnees)
if insert.rowcount > 0:
return True
else:
return False
else:
return False
def db_delete_vote_channel(self, channel: str) -> bool:
"""Cette fonction supprime les salons de join de Defender
Args:
channel (str): le salon à enregistrer.
"""
mes_donnes = {'channel': channel}
response = self.Base.db_execute_query("DELETE FROM votekick_channel WHERE channel = :channel", mes_donnes)
affected_row = response.rowcount
if affected_row > 0:
return True
else:
return False
def join_saved_channels(self) -> None:
param = {'module_name': self.module_name}
result = self.Base.db_execute_query(f"SELECT id, channel_name FROM {self.Config.TABLE_CHANNEL} WHERE module_name = :module_name", param)
channels = result.fetchall()
unixtime = self.Base.get_unixtime()
for channel in channels:
id, chan = channel
self.insert_vote_channel(self.VoteChannelModel(channel_name=chan, target_user='', voter_users=[], vote_for=0, vote_against=0))
self.Protocol.sjoin(channel=chan)
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} SAMODE {chan} +o {self.Config.SERVICE_NICKNAME}")
return None
def is_vote_ongoing(self, channel: str) -> bool:
response = False
for vote in self.VOTE_CHANNEL_DB:
if vote.channel_name == channel:
if vote.target_user:
response = True
return response
def timer_vote_verdict(self, channel: str) -> None:
dnickname = self.Config.SERVICE_NICKNAME
if not self.is_vote_ongoing(channel):
return None return None
for chan in self.VOTE_CHANNEL_DB:
if chan.channel_name == channel:
target_user = self.User.get_nickname(chan.target_user)
if chan.vote_for > chan.vote_against:
self.Protocol.send_priv_msg(
nick_from=dnickname,
msg=f"User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.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}")
self.Channel.delete_user_from_channel(channel, self.User.get_uid(target_user))
elif chan.vote_for <= chan.vote_against:
self.Protocol.send_priv_msg(
nick_from=dnickname,
msg=f"User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel",
channel=channel
)
# Init the system
if self.init_vote_system(channel):
self.Protocol.send_priv_msg(
nick_from=dnickname,
msg="System vote re initiated",
channel=channel
)
return None
def cmd(self, data:list) -> None:
try: try:
cmd = list(data).copy()
return None match command:
case 'PRIVMSG':
return None
case 'QUIT':
return None
case _:
return None
except KeyError as ke: except KeyError as ke:
self.Logs.error(f"Key Error: {ke}") self.Logs.error(f"Key Error: {ke}")
@@ -256,11 +164,12 @@ class Votekick():
except Exception as err: except Exception as err:
self.Logs.error(f"General Error: {err}") self.Logs.error(f"General Error: {err}")
def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None: def hcmds(self, user:str, channel: Any, cmd: list, fullcmd: Optional[list] = None) -> None:
# cmd is the command starting from the user command # cmd is the command starting from the user command
# full cmd is sending the entire server response # full cmd is sending the entire server response
command = str(cmd[0]).lower() command = str(cmd[0]).lower()
fullcmd = fullcmd
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME
fromuser = user fromuser = user
fromchannel = channel fromchannel = channel
@@ -287,32 +196,25 @@ class Votekick():
case 'activate': case 'activate':
try: try:
# vote activate #channel # vote activate #channel
if self.Admin.get_Admin(fromuser) is None: 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') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' :Your are not allowed to execute this command')
return None return None
sentchannel = str(cmd[2]).lower() if self.Channel.Is_Channel(str(cmd[2]).lower()) else None sentchannel = str(cmd[2]).lower() if self.Channel.is_valid_channel(str(cmd[2]).lower()) else None
if sentchannel is 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") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" The correct command is {self.Config.SERVICE_PREFIX}{command} {option} #CHANNEL")
self.insert_vote_channel( if self.VoteKickManager.activate_new_channel(sentchannel):
self.VoteChannelModel( self.Channel.db_query_channel('add', self.module_name, sentchannel)
channel_name=sentchannel, self.Protocol.send_join_chan(uidornickname=dnickname, channel=sentchannel)
target_user='', self.Protocol.send2socket(f":{dnickname} SAMODE {sentchannel} +o {dnickname}")
voter_users=[], self.Protocol.send_priv_msg(nick_from=dnickname,
vote_for=0, msg="You can now use !submit <nickname> to decide if he will stay or not on this channel ",
vote_against=0 channel=sentchannel
) )
)
self.Channel.db_query_channel('add', self.module_name, sentchannel) return None
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,
msg="You can now use !submit <nickname> to decide if he will stay or not on this channel ",
channel=sentchannel
)
except Exception as err: except Exception as err:
self.Logs.error(f'{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' /msg {dnickname} {command} {option} #channel')
@@ -321,23 +223,21 @@ class Votekick():
case 'deactivate': case 'deactivate':
try: try:
# vote deactivate #channel # vote deactivate #channel
if self.Admin.get_Admin(fromuser) is None: 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") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" Your are not allowed to execute this command")
return None return None
sentchannel = str(cmd[2]).lower() if self.Channel.Is_Channel(str(cmd[2]).lower()) else None sentchannel = str(cmd[2]).lower() if self.Channel.is_valid_channel(str(cmd[2]).lower()) else None
if sentchannel is 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") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" The correct command is {self.Config.SERVICE_PREFIX}{command} {option} #CHANNEL")
self.Protocol.send2socket(f":{dnickname} SAMODE {sentchannel} -o {dnickname}") self.Protocol.send2socket(f":{dnickname} SAMODE {sentchannel} -o {dnickname}")
self.Protocol.send_part_chan(uidornickname=dnickname, channel=sentchannel) self.Protocol.send_part_chan(uidornickname=dnickname, channel=sentchannel)
for chan in self.VOTE_CHANNEL_DB: if self.VoteKickManager.drop_vote_channel_model(sentchannel):
if chan.channel_name == sentchannel: self.Channel.db_query_channel('del', self.module_name, sentchannel)
self.VOTE_CHANNEL_DB.remove(chan) return None
self.Channel.db_query_channel('del', self.module_name, chan.channel_name)
self.Logs.debug(f"The Channel {sentchannel} has been deactivated from the vote system")
except Exception as err: except Exception as err:
self.Logs.error(f'{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" /msg {dnickname} {command} {option} #channel")
@@ -347,20 +247,11 @@ class Votekick():
try: try:
# vote + # vote +
channel = fromchannel channel = fromchannel
for chan in self.VOTE_CHANNEL_DB: if self.VoteKickManager.action_vote(channel, fromuser, '+'):
if chan.channel_name == channel: self.Protocol.send_priv_msg(nick_from=dnickname, msg="Vote recorded, thank you",channel=channel)
if fromuser in chan.voter_users: else:
self.Protocol.send_priv_msg(nick_from=dnickname, self.Protocol.send_priv_msg(nick_from=dnickname, msg="You already submitted a vote", channel=channel)
msg="You already submitted a vote",
channel=channel
)
else:
chan.vote_for += 1
chan.voter_users.append(fromuser)
self.Protocol.send_priv_msg(nick_from=dnickname,
msg="Vote recorded, thank you",
channel=channel
)
except Exception as err: except Exception as err:
self.Logs.error(f'{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' /msg {dnickname} {command} {option}')
@@ -370,20 +261,11 @@ class Votekick():
try: try:
# vote - # vote -
channel = fromchannel channel = fromchannel
for chan in self.VOTE_CHANNEL_DB: if self.VoteKickManager.action_vote(channel, fromuser, '-'):
if chan.channel_name == channel: self.Protocol.send_priv_msg(nick_from=dnickname, msg="Vote recorded, thank you",channel=channel)
if fromuser in chan.voter_users: else:
self.Protocol.send_priv_msg(nick_from=dnickname, self.Protocol.send_priv_msg(nick_from=dnickname, msg="You already submitted a vote", channel=channel)
msg="You already submitted a vote",
channel=channel
)
else:
chan.vote_against += 1
chan.voter_users.append(fromuser)
self.Protocol.send_priv_msg(nick_from=dnickname,
msg="Vote recorded, thank you",
channel=channel
)
except Exception as err: except Exception as err:
self.Logs.error(f'{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' /msg {dnickname} {command} {option}')
@@ -392,7 +274,7 @@ class Votekick():
case 'cancel': case 'cancel':
try: try:
# vote cancel # vote cancel
if self.Admin.get_Admin(fromuser) is None: 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') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Your are not allowed to execute this command')
return None return None
@@ -400,13 +282,13 @@ class Votekick():
self.Logs.error(f"The channel is not known, defender can't cancel the vote") 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.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.VOTE_CHANNEL_DB: for vote in self.VoteKickManager.VOTE_CHANNEL_DB:
if vote.channel_name == channel: if vote.channel_name == channel:
self.init_vote_system(channel) if self.VoteKickManager.init_vote_system(channel):
self.Protocol.send_priv_msg(nick_from=dnickname, self.Protocol.send_priv_msg(nick_from=dnickname,
msg="Vote system re-initiated", msg="Vote system re-initiated",
channel=channel channel=channel
) )
except Exception as err: except Exception as err:
self.Logs.error(f'{err}') self.Logs.error(f'{err}')
@@ -416,7 +298,7 @@ class Votekick():
case 'status': case 'status':
try: try:
# vote status # vote status
for chan in self.VOTE_CHANNEL_DB: for chan in self.VoteKickManager.VOTE_CHANNEL_DB:
if chan.channel_name == channel: if chan.channel_name == channel:
self.Protocol.send_priv_msg(nick_from=dnickname, 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))}", 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))}",
@@ -430,25 +312,25 @@ class Votekick():
case 'submit': case 'submit':
try: try:
# vote submit nickname # vote submit nickname
if self.Admin.get_Admin(fromuser) is None: 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') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Your are not allowed to execute this command')
return None return None
nickname_submitted = cmd[2] nickname_submitted = cmd[2]
uid_submitted = self.User.get_uid(nickname_submitted) uid_submitted = self.User.get_uid(nickname_submitted)
user_submitted = self.User.get_User(nickname_submitted) user_submitted = self.User.get_User(nickname_submitted)
ongoing_user = None
# check if there is an ongoing vote # check if there is an ongoing vote
if self.is_vote_ongoing(channel): if self.VoteKickManager.is_vote_ongoing(channel):
for vote in self.VOTE_CHANNEL_DB: votec = self.VoteKickManager.get_vote_channel_model(channel)
if vote.channel_name == channel: if votec:
ongoing_user = self.User.get_nickname(vote.target_user) ongoing_user = self.User.get_nickname(votec.target_user)
self.Protocol.send_priv_msg(nick_from=dnickname,
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"There is an ongoing vote on {ongoing_user}",
msg=f"There is an ongoing vote on {ongoing_user}", channel=channel
channel=channel )
) return None
return False
# check if the user exist # check if the user exist
if user_submitted is None: if user_submitted is None:
@@ -456,24 +338,24 @@ class Votekick():
msg=f"This nickname <{nickname_submitted}> do not exist", msg=f"This nickname <{nickname_submitted}> do not exist",
channel=channel channel=channel
) )
return False return None
uid_cleaned = self.Base.clean_uid(uid_submitted) uid_cleaned = self.Loader.Utils.clean_uid(uid_submitted)
ChannelInfo = self.Channel.get_Channel(channel) channel_obj = self.Channel.get_channel(channel)
if ChannelInfo is None: 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') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' This channel [{channel}] do not exist in the Channel Object')
return False return None
clean_uids_in_channel: list = [] clean_uids_in_channel: list = []
for uid in ChannelInfo.uids: for uid in channel_obj.uids:
clean_uids_in_channel.append(self.Base.clean_uid(uid)) clean_uids_in_channel.append(self.Loader.Utils.clean_uid(uid))
if not uid_cleaned in clean_uids_in_channel: if not uid_cleaned in clean_uids_in_channel:
self.Protocol.send_priv_msg(nick_from=dnickname, self.Protocol.send_priv_msg(nick_from=dnickname,
msg=f"This nickname <{nickname_submitted}> is not available in this channel", msg=f"This nickname <{nickname_submitted}> is not available in this channel",
channel=channel channel=channel
) )
return False return None
# check if Ircop or Service or Bot # check if Ircop or Service or Bot
pattern = fr'[o|B|S]' pattern = fr'[o|B|S]'
@@ -483,9 +365,9 @@ class Votekick():
msg="You cant vote for this user ! he/she is protected", msg="You cant vote for this user ! he/she is protected",
channel=channel channel=channel
) )
return False return None
for chan in self.VOTE_CHANNEL_DB: for chan in self.VoteKickManager.VOTE_CHANNEL_DB:
if chan.channel_name == channel: if chan.channel_name == channel:
chan.target_user = self.User.get_uid(nickname_submitted) chan.target_user = self.User.get_uid(nickname_submitted)
@@ -494,7 +376,7 @@ class Votekick():
channel=channel channel=channel
) )
self.Base.create_timer(60, self.timer_vote_verdict, (channel, )) self.Base.create_timer(60, self.Threads.timer_vote_verdict, (self, channel))
self.Protocol.send_priv_msg(nick_from=dnickname, self.Protocol.send_priv_msg(nick_from=dnickname,
msg="This vote will end after 60 secondes", msg="This vote will end after 60 secondes",
channel=channel channel=channel
@@ -508,33 +390,34 @@ class Votekick():
case 'verdict': case 'verdict':
try: try:
# vote verdict # vote verdict
if self.Admin.get_Admin(fromuser) is None: 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') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f'Your are not allowed to execute this command')
return None return None
for chan in self.VOTE_CHANNEL_DB: votec = self.VoteKickManager.get_vote_channel_model(channel)
if chan.channel_name == channel: if votec:
target_user = self.User.get_nickname(chan.target_user) target_user = self.User.get_nickname(votec.target_user)
if chan.vote_for > chan.vote_against: if votec.vote_for >= votec.vote_against:
self.Protocol.send_priv_msg(nick_from=dnickname, self.Protocol.send_priv_msg(nick_from=dnickname,
msg=f"User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll be kicked from the channel", 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",
channel=channel channel=channel
) )
self.Protocol.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}") self.Protocol.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}")
elif chan.vote_for <= chan.vote_against: else:
self.Protocol.send_priv_msg( self.Protocol.send_priv_msg(
nick_from=dnickname, nick_from=dnickname,
msg=f"User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel", 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",
channel=channel channel=channel
) )
# Init the system if self.VoteKickManager.init_vote_system(channel):
if self.init_vote_system(channel): self.Protocol.send_priv_msg(
self.Protocol.send_priv_msg(
nick_from=dnickname, nick_from=dnickname,
msg="System vote re initiated", msg="System vote re initiated",
channel=channel channel=channel
) )
return None
except Exception as err: except Exception as err:
self.Logs.error(f'{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' /msg {dnickname} {command} {option}')
@@ -548,4 +431,8 @@ class Votekick():
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 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 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 submit nickname')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote verdict') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote verdict')
return None
case _:
return None

11
mods/votekick/schemas.py Normal file
View File

@@ -0,0 +1,11 @@
from typing import Optional
from core.definition import MainModel
from dataclasses import dataclass, field
@dataclass
class VoteChannelModel(MainModel):
channel_name: Optional[str] = None
target_user: Optional[str] = None
voter_users: list = field(default_factory=list)
vote_for: int = 0
vote_against: int = 0

40
mods/votekick/threads.py Normal file
View File

@@ -0,0 +1,40 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from mods.votekick.mod_votekick import Votekick
def timer_vote_verdict(uplink: 'Votekick', channel: str) -> None:
dnickname = uplink.Config.SERVICE_NICKNAME
if not uplink.VoteKickManager.is_vote_ongoing(channel):
return None
votec = uplink.VoteKickManager.get_vote_channel_model(channel)
if votec:
target_user = uplink.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",
channel=channel
)
uplink.Protocol.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}")
else:
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 remain in the channel",
channel=channel
)
if uplink.VoteKickManager.init_vote_system(channel):
uplink.Protocol.send_priv_msg(
nick_from=dnickname,
msg="System vote re initiated",
channel=channel
)
return None
return None

74
mods/votekick/utils.py Normal file
View File

@@ -0,0 +1,74 @@
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:
"""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
`votekick_channel` table. If not, it inserts a new entry with the current timestamp.
Args:
uplink (Votekick): The main votekick system instance that provides access to utilities and database operations.
channel (str): The name of the channel to be added to the database.
Returns:
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()
mes_donnees = {'channel': channel}
response = uplink.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)
if insert.rowcount > 0:
return True
else:
return False
else:
return False
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
if it exists. It returns True if the deletion was successful.
Args:
uplink (Votekick): The main votekick system instance used to execute the database operation.
channel (str): The name of the channel to be removed from the database.
Returns:
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)
affected_row = response.rowcount
if affected_row > 0:
return True
else:
return False
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)
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}")
return None

View File

@@ -0,0 +1,163 @@
from typing import TYPE_CHECKING, Literal, Optional
from mods.votekick.schemas import VoteChannelModel
if TYPE_CHECKING:
from mods.votekick.mod_votekick import Votekick
class VotekickManager:
VOTE_CHANNEL_DB:list[VoteChannelModel] = []
def __init__(self, uplink: 'Votekick'):
self.uplink = uplink
self.Logs = uplink.Logs
self.Utils = uplink.Utils
def activate_new_channel(self, channel_name: str) -> bool:
"""Activate a new channel in the votekick systeme
Args:
channel_name (str): The channel name you want to activate
Returns:
bool: True if it was activated
"""
votec = self.get_vote_channel_model(channel_name)
if votec is None:
self.VOTE_CHANNEL_DB.append(
VoteChannelModel(
channel_name=channel_name,
target_user='',
voter_users=[],
vote_for=0,
vote_against=0
)
)
self.Logs.debug(f"[VOTEKICK MANAGER] {channel_name} has been activated.")
return True
return False
def init_vote_system(self, channel_name: str) -> bool:
"""Initializes or resets the votekick system for a given channel.
This method clears the current target, voter list, and vote counts
in preparation for a new votekick session.
Args:
channel_name (str): The name of the channel for which the votekick system should be initialized.
Returns:
bool: True if the votekick system was successfully initialized, False if the channel is not found.
"""
votec = self.get_vote_channel_model(channel_name)
if votec is None:
self.Logs.debug(f"[VOTEKICK MANAGER] The channel ({channel_name}) is not active!")
return False
votec.target_user = ''
votec.voter_users = []
votec.vote_for = 0
votec.vote_against = 0
self.Logs.debug(f"[VOTEKICK MANAGER] The channel ({channel_name}) has been successfully initialized!")
return True
def get_vote_channel_model(self, channel_name: str) -> Optional[VoteChannelModel]:
"""Get Vote Channel Object model
Args:
channel_name (str): The channel name you want to activate
Returns:
(VoteChannelModel | None): The VoteChannelModel if exist
"""
for vote in self.VOTE_CHANNEL_DB:
if vote.channel_name.lower() == channel_name.lower():
self.Logs.debug(f"[VOTEKICK MANAGER] {channel_name} has been found in the VOTE_CHANNEL_DB")
return vote
return None
def drop_vote_channel_model(self, channel_name: str) -> bool:
"""Drop a channel from the votekick system.
Args:
channel_name (str): The channel name you want to drop
Returns:
bool: True if the channel has been droped.
"""
votec = self.get_vote_channel_model(channel_name)
if votec:
self.VOTE_CHANNEL_DB.remove(votec)
self.Logs.debug(f"[VOTEKICK MANAGER] {channel_name} has been removed from the VOTE_CHANNEL_DB")
return True
return False
def is_vote_ongoing(self, channel_name: str) -> bool:
"""Check if there is an angoing vote on the channel provided
Args:
channel_name (str): The channel name to check
Returns:
bool: True if there is an ongoing vote on the channel provided.
"""
votec = self.get_vote_channel_model(channel_name)
if votec is None:
self.Logs.debug(f"[VOTEKICK MANAGER] {channel_name} is not activated!")
return False
if votec.target_user:
self.Logs.debug(f'[VOTEKICK MANAGER] A vote is ongoing on {channel_name}')
return True
self.Logs.debug(f'[VOTEKICK MANAGER] {channel_name} is activated but there is no ongoing vote!')
return False
def action_vote(self, channel_name: str, nickname: str, action: Literal['+', '-']) -> bool:
"""
Registers a vote (for or against) in an active votekick session on a channel.
Args:
channel_name (str): The name of the channel where the votekick session is active.
nickname (str): The nickname of the user casting the vote.
action (Literal['+', '-']): The vote action. Use '+' to vote for kicking, '-' to vote against.
Returns:
bool: True if the vote was successfully registered, False otherwise.
This can fail if:
- The action is invalid (not '+' or '-')
- The user has already voted
- The channel has no active votekick session
"""
if action not in ['+', '-']:
self.Logs.debug(f"[VOTEKICK MANAGER] The action must be + or - while you have provided ({action})")
return False
votec = self.get_vote_channel_model(channel_name)
if votec:
client_obj = self.uplink.User.get_User(votec.target_user)
client_to_punish = votec.target_user if client_obj is None else client_obj.nickname
if nickname in votec.voter_users:
self.Logs.debug(f"[VOTEKICK MANAGER] This nickname ({nickname}) has already voted for ({client_to_punish})")
return False
else:
if action == '+':
votec.vote_for += 1
elif action == '-':
votec.vote_against += 1
votec.voter_users.append(nickname)
self.Logs.debug(f"[VOTEKICK MANAGER] The ({nickname}) has voted to ban ({client_to_punish})")
return True
else:
self.Logs.debug(f"[VOTEKICK MANAGER] This channel {channel_name} is not active!")
return False

14
requirements.txt Normal file
View File

@@ -0,0 +1,14 @@
certifi==2024.12.14
charset-normalizer==3.4.1
Faker==33.1.2
greenlet==3.1.1
idna==3.10
psutil==6.1.1
python-dateutil==2.9.0.post0
requests==2.32.3
six==1.17.0
SQLAlchemy==2.0.36
typing_extensions==4.12.2
unrealircd-rpc-py==2.0.4
urllib3==2.3.0
websockets==14.1

View File

@@ -1,9 +1,9 @@
{ {
"version": "6.1.4", "version": "6.2.0",
"requests": "2.32.3", "requests": "2.32.3",
"psutil": "6.0.0", "psutil": "6.0.0",
"unrealircd_rpc_py": "2.0.0", "unrealircd_rpc_py": "2.0.4",
"sqlalchemy": "2.0.35", "sqlalchemy": "2.0.35",
"faker": "30.1.0" "faker": "30.1.0"
} }