37 Commits

Author SHA1 Message Date
adator
71053437a7 Merge pull request #33 from adator85/dev
V5.1.6
2024-09-01 22:15:54 +02:00
adator
322759c5ef V5.1.6 2024-09-01 22:15:24 +02:00
adator
7796d05206 Merge pull request #32 from adator85/dev
Update vote kick commands
2024-09-01 18:55:57 +02:00
adator
3ba884216f Update vote kick commands 2024-09-01 18:55:30 +02:00
adator
5f2567f9e5 Merge pull request #31 from adator85/dev
mod_command update
2024-09-01 17:30:05 +02:00
adator
2ce19ee877 mod_command update 2024-09-01 17:29:33 +02:00
adator
aaa1dd9a1a Merge pull request #30 from adator85/dev
adding Say command for clones
2024-09-01 16:40:25 +02:00
adator
351fd6edaf adding Say command for clones 2024-09-01 16:39:49 +02:00
adator
a02f2f9a26 Merge pull request #29 from adator85/dev
update mod_clone module
2024-09-01 15:54:50 +02:00
adator
d0c17d69de update mod_clone module 2024-09-01 15:54:23 +02:00
adator
d73adb6f0b Merge pull request #28 from adator85/dev
update readme
2024-09-01 15:35:59 +02:00
adator
eb9402dd8e update readme 2024-09-01 15:34:49 +02:00
adator
b812e64992 Merge pull request #27 from adator85/dev
Dev
2024-09-01 15:24:18 +02:00
adator
35d5e7a2b5 Update readme.md 2024-09-01 15:23:48 +02:00
adator
21a825c92d Remove install.py 2024-09-01 15:18:32 +02:00
adator
9bd1f68df2 Merge pull request #26 from adator85/dev
Dev
2024-09-01 14:59:38 +02:00
adator
3fcfa0296d . 2024-09-01 14:40:28 +02:00
adator
bcf972d08b . 2024-09-01 14:34:57 +02:00
adator
1348ead6cd remove logging library 2024-09-01 14:33:41 +02:00
adator
f6ebab4780 . 2024-09-01 14:21:25 +02:00
adator
608ec57593 . 2024-09-01 13:59:26 +02:00
adator
f392f2fb2f . 2024-09-01 13:56:55 +02:00
adator
489e1e7b0a . 2024-09-01 13:47:18 +02:00
adator
3d79270ca0 . 2024-09-01 13:42:39 +02:00
adator
e60ada4260 Fix logging instance 2024-09-01 13:41:15 +02:00
adator
ccb9f307b4 Few changes see changelog 2024-09-01 13:39:02 +02:00
adator
2fc8f2d346 Fix issue unexpected argument 2024-09-01 13:14:51 +02:00
adator
e3ada04f2a daemon_reload cmd. 2024-09-01 13:09:47 +02:00
adator
6ba0551fee v5.1.5 2024-09-01 12:51:54 +02:00
adator
f44b08bf36 Merge pull request #25 from adator85/dev
fix Installation
2024-08-29 01:36:38 +02:00
adator
6142b4257f fix Installation 2024-08-29 01:36:12 +02:00
adator
1a19e1613a Merge pull request #24 from adator85/dev
Fix Bug installation
2024-08-29 01:31:19 +02:00
adator
ab15cce82b Fix Bug installation 2024-08-29 01:30:10 +02:00
adator
cdc15b7b47 Merge pull request #23 from adator85/dev
Dev
2024-08-29 01:16:55 +02:00
adator
01dcc90d63 V5.1.0 2024-08-29 01:13:55 +02:00
adator
9cd089ee6e Fix some issues 2024-08-28 00:50:50 +02:00
adator
c635851d19 New version 2024-08-28 00:13:14 +02:00
14 changed files with 2309 additions and 1140 deletions

View File

@@ -25,16 +25,19 @@ Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux u
Prérequis: Prérequis:
- Système d'exploitation Linux (Windows non supporté) - Système d'exploitation Linux (Windows non supporté)
- Droits d'administrateur (root) pour l'exécution du script
- Python version 3.10 ou supérieure - Python version 3.10 ou supérieure
Bash: Bash:
$ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git $ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git
- Renommer le fichier exemple_configuration.json en configuration.json - Renommer le fichier exemple_configuration.json en configuration.json
- Configurer le fichier configuration.json - Configurer le fichier configuration.json
$ sudo python3 install.py $ python3 main.py
Si votre configuration est bonne, votre service est censé etre connecté a votre réseau IRC Si votre configuration est bonne, votre service est censé etre connecté a votre réseau IRC
Pour Les prochains lancement de defender vous devez utiliser la commande suivante:
Bash:
$ systemctl --user [start | stop | restart | status] defender
# Installation manuelle: # Installation manuelle:
Bash: Bash:
@@ -42,8 +45,10 @@ Si votre configuration est bonne, votre service est censé etre connecté a votr
$ cd IRC_DEFENDER_MODULES $ cd IRC_DEFENDER_MODULES
$ python3 -m venv .pyenv $ python3 -m venv .pyenv
$ source .pyenv/bin/activate $ source .pyenv/bin/activate
- Créer un service nommé "Defender.service" pour votre service et placer le dans "/etc/systemd/system/" (pyenv)$ pip install sqlalchemy, psutil, requests, faker
$ sudo systemctl start Defender - Créer un service nommé "defender.service" pour votre service et placer le dans "/PATH/TO/USER/.config/systemd/user/"
- Si le dossier n'existe pas il faut les créer
$ sudo systemctl --user start defender
# Configuration # Configuration
@@ -55,6 +60,7 @@ Si votre configuration est bonne, votre service est censé etre connecté a votr
SERVEUR_PASSWORD: Mot de passe d'enregistrement du service sur le serveur IRC. SERVEUR_PASSWORD: Mot de passe d'enregistrement du service sur le serveur IRC.
SERVEUR_ID: Identifiant unique du service. SERVEUR_ID: Identifiant unique du service.
SERVEUR_SSL: Active la connexion SSL sécurisée au serveur IRC (true/false). SERVEUR_SSL: Active la connexion SSL sécurisée au serveur IRC (true/false).
SERVICE (Service) SERVICE (Service)
SERVICE_NAME: Nom du service IRC. SERVICE_NAME: Nom du service IRC.
SERVICE_NICKNAME: Surnom utilisé par le service sur le serveur IRC. SERVICE_NICKNAME: Surnom utilisé par le service sur le serveur IRC.
@@ -67,35 +73,46 @@ Si votre configuration est bonne, votre service est censé etre connecté a votr
SERVICE_CMODES: Modes de canal appliqués aux canaux rejoints par le service. SERVICE_CMODES: Modes de canal appliqués aux canaux rejoints par le service.
SERVICE_UMODES: Modes utilisateur appliqués au service. SERVICE_UMODES: Modes utilisateur appliqués au service.
SERVICE_PREFIX: Caractère utilisé comme préfixe des commandes du service. SERVICE_PREFIX: Caractère utilisé comme préfixe des commandes du service.
COMPTE (Compte) COMPTE (Compte)
OWNER: Nom d'utilisateur possédant les droits d'administration du service. OWNER: Nom d'utilisateur possédant les droits d'administration du service.
PASSWORD: Mot de passe de l'administrateur du service. PASSWORD: Mot de passe de l'administrateur du service.
CANAUX (Canaux) CANAUX (Canaux)
SALON_JAIL: Canal utilisé comme prison pour les utilisateurs sanctionnés. SALON_JAIL: Canal utilisé comme prison pour les utilisateurs sanctionnés.
SALON_JAIL_MODES: Modes appliqués au canal de prison. SALON_JAIL_MODES: Modes appliqués au canal de prison.
SALON_LIBERER: Canal utilisé pour la libération des utilisateurs sanctionnés. SALON_LIBERER: Canal utilisé pour la libération des utilisateurs sanctionnés.
API (API) API (API)
API_TIMEOUT: Durée maximale d'attente d'une réponse de l'API en secondes. API_TIMEOUT: Durée maximale d'attente d'une réponse de l'API en secondes.
SCANNER (Scanner) SCANNER (Scanner)
PORTS_TO_SCAN: Liste des ports à scanner pour détecter des serveurs potentiellement malveillants. PORTS_TO_SCAN: Liste des ports à scanner pour détecter des serveurs potentiellement malveillants.
SÉCURITÉ (Sécurité) SÉCURITÉ (Sécurité)
WHITELISTED_IP: Liste d'adresses IP autorisées à contourner certaines restrictions. WHITELISTED_IP: Liste d'adresses IP autorisées à contourner certaines restrictions.
GLINE_DURATION: Durée de bannissement temporaire d'un utilisateur en minutes. GLINE_DURATION: Durée de bannissement temporaire d'un utilisateur en minutes.
DEBUG (Debug) DEBUG (Debug)
DEBUG_LEVEL: Niveau de verbosité des messages de debug (plus grand est le nombre, plus il y a d'informations). DEBUG_LEVEL: Niveau de verbosité des messages de debug (plus grand est le nombre, plus il y a d'informations).
COULEURS (Couleurs) COULEURS (Couleurs)
CONFIG_COLOR: Dictionnaire contenant des codes de couleurs IRC pour un meilleur affichage des messages. CONFIG_COLOR: Dictionnaire contenant des codes de couleurs IRC pour un meilleur affichage des messages.
Modification de la configuration Modification de la configuration
Vous devez modifier le fichier config.json en remplaçant les valeurs par défaut avec vos propres informations. Assurez-vous de bien lire la description de chaque paramètre pour une configuration optimale du service. Vous devez modifier le fichier configuration.json en remplaçant les valeurs par défaut avec vos propres informations. Assurez-vous de bien lire la description de chaque paramètre pour une configuration optimale du service.
Attention # \\!/ Attention \\!/
Le mot de passe de l'administrateur et le mot de passe du service doivent être modifiés pour des raisons de sécurité.
Ne partagez pas vos informations de connexion au serveur IRC avec des tiers.
a votre premiere connexion vous devez tapez
Le mot de passe de l'administrateur et le mot de passe du service doivent être modifiés pour des raisons de sécurité. /msg [NomDuService] auth [nickname] [password]
Ne partagez pas vos informations de connexion au serveur IRC avec des tiers. -- Une fois identifié tapez la commande suivante
/msg [NomDuService] editaccess [nickname] [Nouveau-Password] 5
#Extension: # Extension:
Le code est modulaire et conçu pour être facilement étendu. Vous pouvez ajouter de nouvelles commandes, de nouvelles fonctionnalités (mods/mod_test.py est un exemple pour bien demarrer la création de son module). Le code est modulaire et conçu pour être facilement étendu. Vous pouvez ajouter de nouvelles commandes, de nouvelles fonctionnalités (mods/mod_test.py est un exemple pour bien demarrer la création de son module).
# Contributions: # Contributions:

View File

@@ -402,3 +402,97 @@ class Channel:
self.log.debug(f'Search {name} -- result = {Channel}') self.log.debug(f'Search {name} -- result = {Channel}')
return Channel return Channel
class Clones:
@dataclass
class CloneModel:
alive: bool
nickname: str
username: str
UID_CLONE_DB: list[CloneModel] = []
def __init__(self, Base: Base) -> None:
self.log = Base.logs
def insert(self, newCloneObject: CloneModel) -> bool:
"""Create new Clone object
Args:
newCloneObject (CloneModel): New CloneModel object
Returns:
bool: True if inserted
"""
result = False
exist = False
for record in self.UID_CLONE_DB:
if record.nickname == newCloneObject.nickname:
# If the user exist then return False and do not go further
exist = True
self.log.debug(f'{record.nickname} already exist')
return result
if not exist:
self.UID_CLONE_DB.append(newCloneObject)
result = True
self.log.debug(f'New Clone Object Created: ({newCloneObject})')
if not result:
self.log.critical(f'The Clone Object was not inserted {newCloneObject}')
return result
def delete(self, nickname: str) -> bool:
"""Delete the Clone Object starting from the nickname
Args:
nickname (str): nickname of the clone
Returns:
bool: True if deleted
"""
result = False
for record in self.UID_CLONE_DB:
if record.nickname == nickname:
# If the user exist then remove and return True and do not go further
self.UID_CLONE_DB.remove(record)
result = True
self.log.debug(f'The clone ({record.nickname}) has been deleted')
return result
if not result:
self.log.critical(f'The UID {nickname} was not deleted')
return result
def exists(self, nickname: str) -> bool:
"""Check if the nickname exist
Args:
nickname (str): Nickname of the clone
Returns:
bool: True if the nickname exist
"""
response = False
for cloneObject in self.UID_CLONE_DB:
if cloneObject.nickname == nickname:
response = True
return response
def kill(self, nickname:str) -> bool:
response = False
for cloneObject in self.UID_CLONE_DB:
if cloneObject.nickname == nickname:
cloneObject.alive = False # Kill the clone
response = True
return response

View File

@@ -1,5 +1,6 @@
import time, threading, os, random, socket, hashlib, ipaddress, logging, requests, json, re import time, threading, os, random, socket, hashlib, ipaddress, logging, requests, json, re, ast
from typing import Union from dataclasses import fields
from typing import Union, Literal
from base64 import b64decode from base64 import b64decode
from datetime import datetime from datetime import datetime
from sqlalchemy import create_engine, Engine, Connection, CursorResult from sqlalchemy import create_engine, Engine, Connection, CursorResult
@@ -8,15 +9,11 @@ from core.loadConf import ConfigDataModel
class Base: class Base:
CORE_DB_PATH = 'core' + os.sep + 'db' + os.sep # Le dossier bases de données core
MODS_DB_PATH = 'mods' + os.sep + 'db' + os.sep # Le dossier bases de données des modules
PYTHON_MIN_VERSION = '3.10' # Version min de python
def __init__(self, Config: ConfigDataModel) -> None: def __init__(self, Config: ConfigDataModel) -> None:
self.Config = Config # Assigner l'objet de configuration self.Config = Config # Assigner l'objet de configuration
self.init_log_system() # Demarrer le systeme de log 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
self.running_timers:list[threading.Timer] = [] # Liste des timers en cours self.running_timers:list[threading.Timer] = [] # Liste des timers en cours
self.running_threads:list[threading.Thread] = [] # Liste des threads en cours self.running_threads:list[threading.Thread] = [] # Liste des threads en cours
@@ -25,6 +22,7 @@ class Base:
self.lock = threading.RLock() # Création du lock self.lock = threading.RLock() # Création du lock
self.install: bool = False # Initialisation de la variable d'installation
self.engine, self.cursor = self.db_init() # Initialisation de la connexion a la base de données self.engine, self.cursor = self.db_init() # Initialisation de la connexion a la base de données
self.__create_db() # Initialisation de la base de données self.__create_db() # Initialisation de la base de données
@@ -47,7 +45,7 @@ class Base:
def __get_latest_defender_version(self) -> None: def __get_latest_defender_version(self) -> None:
try: try:
self.logs.debug(f'Looking for a new version available on Github') self.logs.debug(f'Looking for a new version available on Github')
print(f'===> Looking for a new version available on Github') # print(f'===> Looking for a new version available on Github')
token = '' token = ''
json_url = f'https://raw.githubusercontent.com/adator85/IRC_DEFENDER_MODULES/main/version.json' json_url = f'https://raw.githubusercontent.com/adator85/IRC_DEFENDER_MODULES/main/version.json'
headers = { headers = {
@@ -158,7 +156,7 @@ class Base:
encoding='UTF-8', encoding='UTF-8',
format='%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s') format='%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s')
self.logs.info('#################### STARTING INTERCEPTOR HQ ####################') self.logs.info('#################### STARTING DEFENDER ####################')
return None return None
@@ -190,8 +188,8 @@ class Base:
Returns: Returns:
bool: True si le module existe déja dans la base de données sinon False bool: True si le module existe déja dans la base de données sinon False
""" """
query = f"SELECT id FROM {self.Config.table_module} WHERE module = :module" query = f"SELECT id FROM {self.Config.table_module} WHERE module_name = :module_name"
mes_donnes = {'module': module_name} mes_donnes = {'module_name': module_name}
results = self.db_execute_query(query, mes_donnes) results = self.db_execute_query(query, mes_donnes)
if results.fetchall(): if results.fetchall():
@@ -199,7 +197,7 @@ class Base:
else: else:
return False return False
def db_record_module(self, user_cmd:str, module_name:str) -> 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:
@@ -208,10 +206,9 @@ class Base:
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) VALUES (:datetime, :user, :module)" 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': module_name} mes_donnees = {'datetime': self.get_datetime(), '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)
# self.db_close_session(self.session)
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")
@@ -223,12 +220,174 @@ class Base:
Args: Args:
cmd (str): le module a enregistrer cmd (str): le module a enregistrer
""" """
insert_cmd_query = f"DELETE FROM {self.Config.table_module} WHERE module = :module" insert_cmd_query = f"DELETE FROM {self.Config.table_module} WHERE module_name = :module_name"
mes_donnees = {'module': 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 False
def db_sync_core_config(self, module_name: str, dataclassObj: object) -> bool:
"""Sync module local parameters with the database
if new module then local param will be stored in the database
if old module then db param will be moved to the local dataclassObj
if new local param it will be stored in the database
if local param was removed then it will also be removed from the database
Args:
module_name (str): The module name ex. mod_defender
dataclassObj (object): The Dataclass object
Returns:
bool: _description_
"""
try:
response = True
current_date = self.get_datetime()
core_table = self.Config.table_config
# Add local parameters to DB
for field in fields(dataclassObj):
param_key = field.name
param_value = str(getattr(dataclassObj, field.name))
param_to_search = {'module_name': module_name, 'param_key': param_key}
search_query = f'''SELECT id FROM {core_table} WHERE module_name = :module_name AND param_key = :param_key'''
excecute_search_query = self.db_execute_query(search_query, param_to_search)
result_search_query = excecute_search_query.fetchone()
if result_search_query is None:
# If param and module_name doesn't exist create the record
param_to_insert = {'datetime': current_date,'module_name': module_name,
'param_key': param_key,'param_value': param_value
}
insert_query = f'''INSERT INTO {core_table} (datetime, module_name, param_key, param_value)
VALUES (:datetime, :module_name, :param_key, :param_value)
'''
execution = self.db_execute_query(insert_query, param_to_insert)
if execution.rowcount > 0:
self.logs.debug(f'New parameter added to the database: {param_key} --> {param_value}')
# Delete from DB unused parameter
query_select = f"SELECT module_name, param_key, param_value FROM {core_table} WHERE module_name = :module_name"
parameter = {'module_name': module_name}
execute_query_select = self.db_execute_query(query_select, parameter)
result_query_select = execute_query_select.fetchall()
for result in result_query_select:
db_mod_name, db_param_key, db_param_value = result
if not hasattr(dataclassObj, db_param_key):
mes_donnees = {'param_key': db_param_key, 'module_name': db_mod_name}
execute_delete = self.db_execute_query(f'DELETE FROM {core_table} WHERE module_name = :module_name and param_key = :param_key', mes_donnees)
row_affected = execute_delete.rowcount
if row_affected > 0:
self.logs.debug(f'A parameter has been deleted from the database: {db_param_key} --> {db_param_value} | Mod: {db_mod_name}')
# Sync local variable with Database
query = f"SELECT param_key, param_value FROM {core_table} WHERE module_name = :module_name"
parameter = {'module_name': module_name}
response = self.db_execute_query(query, parameter)
result = response.fetchall()
for param, value in result:
if type(getattr(dataclassObj, param)) == list:
value = ast.literal_eval(value)
setattr(dataclassObj, param, self.int_if_possible(value))
return response
except AttributeError as attrerr:
self.logs.error(f'Attribute Error: {attrerr}')
except Exception as err:
self.logs.error(err)
return False
def db_update_core_config(self, module_name:str, dataclassObj: object, param_key:str, param_value: str) -> bool:
core_table = 'core_config'
# Check if the param exist
if not hasattr(dataclassObj, param_key):
self.logs.error(f"Le parametre {param_key} n'existe pas dans la variable global")
return False
mes_donnees = {'module_name': module_name, 'param_key': param_key, 'param_value': param_value}
search_param_query = f"SELECT id FROM {core_table} WHERE module_name = :module_name AND param_key = :param_key"
result = self.db_execute_query(search_param_query, mes_donnees)
isParamExist = result.fetchone()
if not isParamExist is None:
mes_donnees = {'datetime': self.get_datetime(),
'module_name': module_name,
'param_key': param_key,
'param_value': param_value
}
query = f'''UPDATE {core_table} SET datetime = :datetime, param_value = :param_value WHERE module_name = :module_name AND param_key = :param_key'''
update = self.db_execute_query(query, mes_donnees)
updated_rows = update.rowcount
if updated_rows > 0:
setattr(dataclassObj, param_key, self.int_if_possible(param_value))
self.logs.debug(f'Parameter updated : {param_key} - {param_value} | Module: {module_name}')
self.logs.debug(dataclassObj)
return True
def db_query_channel(self, action: Literal['add','del'], module_name: str, channel_name: str) -> bool:
"""You can add a channel or delete a channel.
Args:
action (Literal['add','del']): Action on the database
module_name (str): The module name (mod_test)
channel_name (str): The channel name (With #)
Returns:
bool: True if action done
"""
try:
channel_name = channel_name.lower() if self.Is_Channel(channel_name) else None
core_table = 'core_channel'
if not channel_name:
self.logs.warn(f'The channel [{channel_name}] is not correct')
return False
match action:
case 'add':
mes_donnees = {'module_name': module_name, 'channel_name': channel_name}
response = self.db_execute_query(f"SELECT id FROM {core_table} WHERE module_name = :module_name AND channel_name = :channel_name", mes_donnees)
isChannelExist = response.fetchone()
if isChannelExist is None:
mes_donnees = {'datetime': self.get_datetime(), 'channel_name': channel_name, 'module_name': module_name}
insert = self.db_execute_query(f"INSERT INTO {core_table} (datetime, channel_name, module_name) VALUES (:datetime, :channel_name, :module_name)", mes_donnees)
if insert.rowcount:
self.logs.debug(f'New channel added: channel={channel_name} / module_name={module_name}')
return True
else:
return False
pass
case 'del':
mes_donnes = {'channel_name': channel_name, 'module_name': module_name}
response = self.db_execute_query(f"DELETE FROM {core_table} WHERE channel_name = :channel_name AND module_name = :module_name", mes_donnes)
if response.rowcount > 0:
self.logs.debug(f'Channel deleted: channel={channel_name} / module: {module_name}')
return True
else:
return False
case _:
return False
except Exception as err:
self.logs.error(err)
def db_create_first_admin(self) -> None: def db_create_first_admin(self) -> None:
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}")
@@ -267,6 +426,13 @@ class Base:
self.logs.error(f'Assertion Error -> {ae}') self.logs.error(f'Assertion Error -> {ae}')
def create_thread(self, func:object, func_args: tuple = (), run_once:bool = False) -> None: def create_thread(self, func:object, func_args: tuple = (), run_once:bool = False) -> None:
"""Create a new thread and store it into running_threads variable
Args:
func (object): The method/function you want to execute via this thread
func_args (tuple, optional): Arguments of the function/method. Defaults to ().
run_once (bool, optional): If you want to ensure that this method/function run once. Defaults to False.
"""
try: try:
func_name = func.__name__ func_name = func.__name__
@@ -275,10 +441,6 @@ class Base:
if thread.getName() == func_name: if thread.getName() == func_name:
return None return None
# if func_name in self.running_threads:
# print(f"HeartBeat is running")
# return None
th = threading.Thread(target=func, args=func_args, name=str(func_name), daemon=True) th = threading.Thread(target=func, args=func_args, name=str(func_name), daemon=True)
th.start() th.start()
@@ -367,6 +529,7 @@ class Base:
full_path_db = self.Config.db_path + self.Config.db_name full_path_db = self.Config.db_path + self.Config.db_name
if not os.path.exists(db_directory): if not os.path.exists(db_directory):
self.install = True
os.makedirs(db_directory) os.makedirs(db_directory)
engine = create_engine(f'sqlite:///{full_path_db}.db', echo=False) engine = create_engine(f'sqlite:///{full_path_db}.db', echo=False)
@@ -376,14 +539,23 @@ class Base:
def __create_db(self) -> None: def __create_db(self) -> None:
table_logs = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_log} ( table_core_log = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_log} (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT, datetime TEXT,
server_msg TEXT server_msg TEXT
) )
''' '''
table_cmds = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_commande} ( table_core_config = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_config} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
module_name TEXT,
param_key TEXT,
param_value TEXT
)
'''
table_core_log_command = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_commande} (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT, datetime TEXT,
user TEXT, user TEXT,
@@ -391,15 +563,24 @@ class Base:
) )
''' '''
table_modules = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_module} ( table_core_module = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_module} (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT, datetime TEXT,
user TEXT, user TEXT,
module TEXT module_name TEXT,
isdefault INTEGER
) )
''' '''
table_admins = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_admin} ( table_core_channel = '''CREATE TABLE IF NOT EXISTS core_channel (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
module_name TEXT,
channel_name TEXT
)
'''
table_core_admin = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_admin} (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
createdOn TEXT, createdOn TEXT,
user TEXT, user TEXT,
@@ -410,10 +591,17 @@ class Base:
) )
''' '''
self.db_execute_query(table_logs) self.db_execute_query(table_core_log)
self.db_execute_query(table_cmds) self.db_execute_query(table_core_log_command)
self.db_execute_query(table_modules) self.db_execute_query(table_core_module)
self.db_execute_query(table_admins) self.db_execute_query(table_core_admin)
self.db_execute_query(table_core_channel)
self.db_execute_query(table_core_config)
if self.install:
self.db_record_module('sys', 'mod_command', 1)
self.db_record_module('sys', 'mod_defender', 1)
self.install = False
return None return None
@@ -519,7 +707,7 @@ class Base:
self.periodic_func.clear() self.periodic_func.clear()
def clean_uid(self, uid:str) -> str: def clean_uid(self, uid:str) -> str:
"""Clean UID by removing @ / % / + / Owner / and * """Clean UID by removing @ / % / + / ~ / * / :
Args: Args:
uid (str): The UID to clean uid (str): The UID to clean
@@ -528,7 +716,7 @@ class Base:
str: Clean UID without any sign str: Clean UID without any sign
""" """
pattern = fr'[@|%|\+|~|\*]*' pattern = fr'[:|@|%|\+|~|\*]*'
parsed_UID = re.sub(pattern, '', uid) parsed_UID = re.sub(pattern, '', uid)
return parsed_UID return parsed_UID
@@ -542,11 +730,19 @@ class Base:
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:
pattern = fr'^#' if channelToCheck is None:
isChannel = re.findall(pattern, channelToCheck) return False
if not isChannel: pattern = fr'^#'
return False isChannel = re.findall(pattern, channelToCheck)
else:
return True if not isChannel:
return False
else:
return True
except TypeError as te:
self.logs.error(f'TypeError: [{channelToCheck}] - {te}')
except Exception as err:
self.logs.error(f'Error Not defined: {err}')

222
core/connection.py Normal file
View File

@@ -0,0 +1,222 @@
import socket, ssl, time
from ssl import SSLSocket
from core.loadConf import Config
from core.Model import Clones
from core.base import Base
from typing import Union
class Connection:
def __init__(self, server_port: int, nickname: str, username: str, channels:list[str], CloneObject: Clones, ssl:bool = False) -> None:
self.Config = Config().ConfigObject
self.Base = Base(self.Config)
self.IrcSocket: Union[socket.socket, SSLSocket] = None
self.nickname = nickname
self.username = username
self.channels:list[str] = channels
self.CHARSET = ['utf-8', 'iso-8859-1']
self.Clones = CloneObject
self.signal: bool = True
for clone in self.Clones.UID_CLONE_DB:
if clone.nickname == nickname:
self.currentCloneObject = clone
self.create_socket(self.Config.SERVEUR_IP, self.Config.SERVEUR_HOSTNAME, server_port, ssl)
self.send_connection_information_to_server(self.IrcSocket)
self.connect()
def create_socket(self, server_ip: str, server_hostname: str, server_port: int, ssl: bool = False) -> bool:
try:
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK)
connexion_information = (server_ip, server_port)
if ssl:
# Créer un object ssl
ssl_context = self.__ssl_context()
ssl_connexion = ssl_context.wrap_socket(soc, server_hostname=server_hostname)
ssl_connexion.connect(connexion_information)
self.IrcSocket:SSLSocket = ssl_connexion
self.SSL_VERSION = self.IrcSocket.version()
self.Base.logs.debug(f'> Connexion en mode SSL : Version = {self.SSL_VERSION}')
else:
soc.connect(connexion_information)
self.IrcSocket:socket.socket = soc
self.Base.logs.debug(f'> Connexion en mode normal')
return True
except ssl.SSLEOFError as soe:
self.Base.logs.critical(f"SSLEOFError __create_socket: {soe} - {soc.fileno()}")
return False
except ssl.SSLError as se:
self.Base.logs.critical(f"SSLError __create_socket: {se} - {soc.fileno()}")
return False
except OSError as oe:
self.Base.logs.critical(f"OSError __create_socket: {oe} - {soc.fileno()}")
return False
except AttributeError as ae:
self.Base.logs.critical(f"AttributeError __create_socket: {ae} - {soc.fileno()}")
return False
def send2socket(self, send_message:str) -> None:
"""Envoit les commandes à envoyer au serveur.
Args:
string (Str): contient la commande à envoyer au serveur.
"""
try:
with self.Base.lock:
# print(f">{str(send_message)}")
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0]))
self.Base.logs.debug(f'{send_message}')
except UnicodeDecodeError:
self.Base.logs.error(f'Decode Error try iso-8859-1 - message: {send_message}')
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[1],'replace'))
except UnicodeEncodeError:
self.Base.logs.error(f'Encode Error try iso-8859-1 - message: {send_message}')
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[1],'replace'))
except AssertionError as ae:
self.Base.logs.warning(f'Assertion Error {ae} - message: {send_message}')
except ssl.SSLEOFError as soe:
self.Base.logs.error(f"SSLEOFError: {soe} - {send_message}")
except ssl.SSLError as se:
self.Base.logs.error(f"SSLError: {se} - {send_message}")
except OSError as oe:
self.Base.logs.error(f"OSError: {oe} - {send_message}")
def send_connection_information_to_server(self, writer:Union[socket.socket, SSLSocket]) -> None:
"""Créer le link et envoyer les informations nécessaires pour la
connexion au serveur.
Args:
writer (StreamWriter): permet l'envoi des informations au serveur.
"""
try:
nickname = self.nickname
username = self.username
# Envoyer un message d'identification
writer.send(f"USER {nickname} {username} {username} {nickname} {username} :{username}\r\n".encode('utf-8'))
writer.send(f"USER {username} {username} {username} :{username}\r\n".encode('utf-8'))
writer.send(f"NICK {nickname}\r\n".encode('utf-8'))
self.Base.logs.debug('Link information sent to the server')
return None
except AttributeError as ae:
self.Base.logs.critical(f'{ae}')
def connect(self):
try:
while self.signal:
try:
# 4072 max what the socket can grab
buffer_size = self.IrcSocket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)
data_in_bytes = self.IrcSocket.recv(buffer_size)
data = data_in_bytes.splitlines(True)
count_bytes = len(data_in_bytes)
while count_bytes > 4070:
# If the received message is > 4070 then loop and add the value to the variable
new_data = self.IrcSocket.recv(buffer_size)
data_in_bytes += new_data
count_bytes = len(new_data)
data = data_in_bytes.splitlines(True)
if not data:
break
self.parser(data)
except ssl.SSLEOFError as soe:
self.Base.logs.error(f"SSLEOFError __connect_to_irc: {soe} - {data}")
self.signal = False
except ssl.SSLError as se:
self.Base.logs.error(f"SSLError __connect_to_irc: {se} - {data}")
self.signal = False
except OSError as oe:
self.Base.logs.error(f"OSError __connect_to_irc: {oe} - {data}")
self.signal = False
self.IrcSocket.shutdown(socket.SHUT_RDWR)
self.IrcSocket.close()
self.Base.logs.info("--> Clone Disconnected ...")
except AssertionError as ae:
self.Base.logs.error(f'Assertion error : {ae}')
except ValueError as ve:
self.Base.logs.error(f'Value Error : {ve}')
except ssl.SSLEOFError as soe:
self.Base.logs.error(f"OS Error __connect_to_irc: {soe}")
except AttributeError as atte:
self.Base.logs.critical(f"{atte}")
except Exception as e:
self.Base.logs.error(f"Exception: {e}")
def parser(self, cmd:list[bytes]):
try:
for data in cmd:
response = data.decode(self.CHARSET[0]).split()
self.signal = self.currentCloneObject.alive
# print(response)
match response[0]:
case 'PING':
pong = str(response[1]).replace(':','')
self.send2socket(f"PONG :{pong}")
return None
case 'ERROR':
error_value = str(response[1]).replace(':','')
if error_value == 'Closing':
self.signal = False
match response[1]:
case '376':
for channel in self.channels:
self.send2socket(f"JOIN {channel}")
return None
case 'PRIVMSG':
self.Base.logs.debug(response)
self.Base.logs.debug(f'{self.currentCloneObject.nickname} - {self.currentCloneObject.alive}')
fullname = str(response[0]).replace(':', '')
nickname = fullname.split('!')[0].replace(':','')
if nickname == self.Config.SERVICE_NICKNAME:
command = str(response[3]).replace(':','')
if command == 'KILL':
self.send2socket(f'QUIT :Thanks and goodbye')
self.signal = self.currentCloneObject.alive
if command == 'JOIN':
channel_to_join = str(response[4])
self.send2socket(f"JOIN {channel_to_join}")
if command == 'SAY':
clone_channel = str(response[4])
message = []
for i in range(5, len(response)):
message.append(response[i])
final_message = ' '.join(message)
self.send2socket(f"PRIVMSG {clone_channel} :{final_message}")
except UnicodeEncodeError:
for data in cmd:
response = data.decode(self.CHARSET[1],'replace').split()
except UnicodeDecodeError:
for data in cmd:
response = data.decode(self.CHARSET[1],'replace').split()
except AssertionError as ae:
self.Base.logs.error(f"Assertion error : {ae}")
def __ssl_context(self) -> ssl.SSLContext:
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
self.Base.logs.debug(f'SSLContext initiated with verified mode {ctx.verify_mode}')
return ctx

View File

@@ -1,64 +1,160 @@
from importlib.util import find_spec from dataclasses import dataclass
from subprocess import check_call, run, CalledProcessError from subprocess import check_call, run, CalledProcessError, PIPE
from platform import python_version, python_version_tuple from platform import python_version, python_version_tuple
from sys import exit from sys import exit
import os import os
class Install: class Install:
@dataclass
class CoreConfig:
install_log_file: str
unix_systemd_folder: str
service_file_name: str
service_cmd_executable: list
service_cmd_daemon_reload: list
defender_main_executable: str
python_min_version: str
python_current_version_tuple: tuple[str, str, str]
python_current_version: str
defender_install_folder: str
venv_folder: str
venv_cmd_installation: list
venv_cmd_requirements: list
venv_pip_executable: str
venv_python_executable: str
def __init__(self) -> None: def __init__(self) -> None:
self.PYTHON_MIN_VERSION = '3.10'
self.venv_folder_name = '.pyenv' self.set_configuration()
self.cmd_venv_command = ['python3', '-m', 'venv', self.venv_folder_name]
self.module_to_install = ['sqlalchemy','psutil','requests']
if not self.checkPythonVersion(): if not self.check_python_version():
# Tester si c'est la bonne version de python # Tester si c'est la bonne version de python
exit("Python Version Error") exit("Python Version Error")
else: else:
if self.skip_install:
return None
print(f'Configuration loaded : {self.config}')
# Sinon tester les dependances python et les installer avec pip # Sinon tester les dependances python et les installer avec pip
self.checkDependencies() if self.do_install():
self.install_dependencies()
self.create_service_file()
self.print_final_message()
return None return None
def checkPythonVersion(self) -> bool: def set_configuration(self):
self.skip_install = False
defender_install_folder = os.getcwd()
venv_folder = '.pyenv'
unix_user_home_directory = os.path.expanduser("~")
unix_systemd_folder = os.path.join(unix_user_home_directory, '.config', 'systemd', 'user')
defender_main_executable = os.path.join(defender_install_folder, 'main.py')
self.config = self.CoreConfig(
install_log_file='install.log',
unix_systemd_folder=unix_systemd_folder,
service_file_name='defender.service',
service_cmd_executable=['systemctl', '--user', 'start', 'defender'],
service_cmd_daemon_reload=['systemctl', '--user', 'daemon-reload'],
defender_main_executable=defender_main_executable,
python_min_version='3.10',
python_current_version_tuple=python_version_tuple(),
python_current_version=python_version(),
defender_install_folder=defender_install_folder,
venv_folder=venv_folder,
venv_cmd_installation=['python3', '-m', 'venv', venv_folder],
venv_cmd_requirements=['sqlalchemy','psutil','requests','faker'],
venv_pip_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}pip',
venv_python_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}python'
)
# Exclude Windows OS
if os.name == 'nt':
#print('/!\\ Skip installation /!\\')
self.skip_install = True
else:
if self.is_root():
self.skip_install = True
def is_root(self) -> bool:
if os.geteuid() != 0:
print('User without privileges ==> PASS')
return False
elif os.geteuid() == 0:
print('/!\\ Do not use root to install Defender /!\\')
exit("Do not use root to install Defender")
return True
def do_install(self) -> bool:
full_service_file_path = os.path.join(self.config.unix_systemd_folder, self.config.service_file_name)
if not os.path.exists(full_service_file_path):
print(f'/!\\ Service file does not exist /!\\')
return True
# Check if virtual env exist
if not os.path.exists(f'{os.path.join(self.config.defender_install_folder, self.config.venv_folder)}'):
self.run_subprocess(self.config.venv_cmd_installation)
print(f'/!\\ Virtual env does not exist run the install /!\\')
return True
def run_subprocess(self, command:list) -> None:
print(f'> {command}')
try:
check_call(command)
print("The command completed successfully.")
except CalledProcessError as e:
print(f"The command failed with the return code: {e.returncode}")
print(f"Try to install dependencies ...")
exit(5)
def check_python_version(self) -> bool:
"""Test si la version de python est autorisée ou non """Test si la version de python est autorisée ou non
Returns: Returns:
bool: True si la version de python est autorisé sinon False bool: True si la version de python est autorisé sinon False
""" """
# Current system version # Current system version
sys_major, sys_minor, sys_patch = python_version_tuple() sys_major, sys_minor, sys_patch = self.config.python_current_version_tuple
# min python version required # min python version required
python_required_version = self.PYTHON_MIN_VERSION.split('.') python_required_version = self.config.python_min_version.split('.')
min_major, min_minor = tuple((python_required_version[0], python_required_version[1])) min_major, min_minor = tuple((python_required_version[0], python_required_version[1]))
if int(sys_major) < int(min_major): if int(sys_major) < int(min_major):
print(f"## Your python version must be greather than or equal to {self.PYTHON_MIN_VERSION} ##") print(f"## Your python version must be greather than or equal to {self.config.python_min_version} ##")
return False return False
elif (int(sys_major) <= int(min_major)) and (int(sys_minor) < int(min_minor)): elif (int(sys_major) <= int(min_major)) and (int(sys_minor) < int(min_minor)):
print(f"## Your python version must be greather than or equal to {self.PYTHON_MIN_VERSION} ##") print(f"## Your python version must be greather than or equal to {self.config.python_min_version} ##")
return False return False
print(f"===> Version of python : {python_version()} ==> OK") print(f"> Version of python : {self.config.python_current_version} ==> OK")
return True return True
def run_subprocess(self, command:list) -> None: def check_package(self, package_name) -> bool:
print(command)
try: try:
check_call(command) # Run a command in the virtual environment's Python to check if the package is installed
print("La commande s'est terminée avec succès.") run([self.config.venv_python_executable, '-c', f'import {package_name}'], check=True, stdout=PIPE, stderr=PIPE)
except CalledProcessError as e: return True
print(f"La commande a échoué avec le code de retour :{e.returncode}") except CalledProcessError as cpe:
print(f"Try to install dependencies ...") print(cpe)
exit(5) return False
def checkDependencies(self) -> None: def install_dependencies(self) -> None:
"""### Verifie les dépendances si elles sont installées """### Verifie les dépendances si elles sont installées
- Test si les modules sont installés - Test si les modules sont installés
- Met a jour pip - Met a jour pip
@@ -67,38 +163,82 @@ class Install:
do_install = False do_install = False
# Check if virtual env exist # Check if virtual env exist
if not os.path.exists(f'{self.venv_folder_name}'): if not os.path.exists(f'{os.path.join(self.config.defender_install_folder, self.config.venv_folder)}'):
self.run_subprocess(self.cmd_venv_command) self.run_subprocess(self.config.venv_cmd_installation)
do_install = True do_install = True
for module in self.module_to_install: for module in self.config.venv_cmd_requirements:
if find_spec(module) is None: if not self.check_package(module):
do_install = True do_install = True
if not do_install: if not do_install:
return None return None
print("===> Vider le cache de pip") print("===> Vider le cache de pip")
check_call(['pip','cache','purge']) self.run_subprocess([self.config.venv_pip_executable, 'cache', 'purge'])
print("===> Verifier si pip est a jour") print("===> Verifier si pip est a jour")
check_call(['python', '-m', 'pip', 'install', '--upgrade', 'pip']) self.run_subprocess([self.config.venv_python_executable, '-m', 'pip', 'install', '--upgrade', 'pip'])
if find_spec('greenlet') is None: if not self.check_package('greenlet'):
check_call(['pip','install', '--only-binary', ':all:', 'greenlet']) self.run_subprocess([self.config.venv_pip_executable, 'install', '--only-binary', ':all:', 'greenlet'])
print('====> Module Greenlet installé') print('====> Module Greenlet installé')
for module in self.module_to_install: for module in self.config.venv_cmd_requirements:
if find_spec(module) is None: if not self.check_package(module):
print("### Trying to install missing python packages ###") print("### Trying to install missing python packages ###")
check_call(['pip','install', module]) self.run_subprocess([self.config.venv_pip_executable, 'install', module])
print(f"====> Module {module} installé") print(f"====> Module {module} installé")
else: else:
print(f"==> {module} already installed") print(f"==> {module} already installed")
print(f"#"*12) def create_service_file(self) -> None:
full_service_file_path = os.path.join(self.config.unix_systemd_folder, self.config.service_file_name)
if os.path.exists(full_service_file_path):
print(f'/!\\ Service file already exist /!\\')
self.run_subprocess(self.config.service_cmd_executable)
return None
contain = f'''[Unit]
Description=Defender IRC Service
[Service]
ExecStart={self.config.venv_python_executable} {self.config.defender_main_executable}
WorkingDirectory={self.config.defender_install_folder}
SyslogIdentifier=Defender
Restart=on-failure
[Install]
WantedBy=default.target
'''
# Check if user systemd is available (.config/systemd/user/)
if not os.path.exists(self.config.unix_systemd_folder):
self.run_subprocess(['mkdir', '-p', self.config.unix_systemd_folder])
with open(full_service_file_path, 'w+') as servicefile:
servicefile.write(contain)
servicefile.close()
print(f'Service file generated with current configuration')
print(f'Running Defender IRC Service ...')
self.run_subprocess(self.config.service_cmd_daemon_reload)
self.run_subprocess(self.config.service_cmd_executable)
else:
with open(full_service_file_path, 'w+') as servicefile:
servicefile.write(contain)
servicefile.close()
print(f'Service file generated with current configuration')
print(f'Running Defender IRC Service ...')
self.run_subprocess(self.config.service_cmd_daemon_reload)
self.run_subprocess(self.config.service_cmd_executable)
def print_final_message(self) -> None:
print(f"#"*24)
print("Installation complete ...") print("Installation complete ...")
print("You must change environment using the command below") print("If the configuration is correct, then you must see your service connected to your irc server")
print(f"source {self.venv_folder_name}{os.sep}bin{os.sep}activate") print(f"If any issue, you can see the log file for debug {self.config.defender_install_folder}{os.sep}logs{os.sep}defender.log")
print(f"#"*12) print(f"#"*24)
exit(1) exit(1)

View File

@@ -1,9 +1,9 @@
import ssl, re, importlib, sys, time, threading, socket import ssl, re, importlib, sys, time, threading, socket
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 Union, Literal
from core.loadConf import Config from core.loadConf import Config
from core.Model import User, Admin, Channel from core.Model import User, Admin, Channel, Clones
from core.base import Base from core.base import Base
class Irc: class Irc:
@@ -12,7 +12,6 @@ class Irc:
self.defender_connexion_datetime = datetime.now() # Date et heure de la premiere connexion de Defender self.defender_connexion_datetime = datetime.now() # Date et heure de la premiere connexion de Defender
self.first_score: int = 100 self.first_score: int = 100
self.db_chan = [] # Definir la variable qui contiendra la liste des salons
self.loaded_classes:dict[str, 'Irc'] = {} # Definir la variable qui contiendra la liste modules chargés self.loaded_classes:dict[str, 'Irc'] = {} # Definir la variable qui contiendra la liste modules chargés
self.beat = 30 # Lancer toutes les 30 secondes des actions de nettoyages self.beat = 30 # Lancer toutes les 30 secondes des actions de nettoyages
self.hb_active = True # Heartbeat active self.hb_active = True # Heartbeat active
@@ -22,6 +21,8 @@ class Irc:
self.INIT = 1 # Variable d'intialisation | 1 -> indique si le programme est en cours d'initialisation self.INIT = 1 # Variable d'intialisation | 1 -> indique si le programme est en cours d'initialisation
self.RESTART = 0 # Variable pour le redemarrage du bot | 0 -> indique que le programme n'es pas en cours de redemarrage self.RESTART = 0 # Variable pour le redemarrage du bot | 0 -> indique que le programme n'es pas en cours de redemarrage
self.CHARSET = ['utf-8', 'iso-8859-1'] # Charset utiliser pour décoder/encoder les messages self.CHARSET = ['utf-8', 'iso-8859-1'] # Charset utiliser pour décoder/encoder les messages
"""0: utf-8 | 1: iso-8859-1"""
self.SSL_VERSION = None # Version SSL self.SSL_VERSION = None # Version SSL
self.Config = Config().ConfigObject self.Config = Config().ConfigObject
@@ -44,6 +45,9 @@ class Irc:
self.User = User(self.Base) self.User = User(self.Base)
self.Admin = Admin(self.Base) self.Admin = Admin(self.Base)
self.Channel = Channel(self.Base) self.Channel = Channel(self.Base)
self.Clones = Clones(self.Base)
self.__create_table()
self.Base.create_thread(func=self.heartbeat, func_args=(self.beat, )) self.Base.create_thread(func=self.heartbeat, func_args=(self.beat, ))
############################################## ##############################################
@@ -90,7 +94,7 @@ class Irc:
except OSError as oe: except OSError as oe:
self.Base.logs.critical(f"OSError __create_socket: {oe} - {soc.fileno()}") self.Base.logs.critical(f"OSError __create_socket: {oe} - {soc.fileno()}")
except AttributeError as ae: except AttributeError as ae:
self.Base.logs.critical(f"OSError __create_socket: {oe} - {soc.fileno()}") self.Base.logs.critical(f"AttributeError __create_socket: {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)
@@ -107,6 +111,7 @@ class Irc:
self.__link(self.IrcSocket) # établir la connexion au serveur IRC self.__link(self.IrcSocket) # établir la connexion au serveur IRC
self.signal = True # Une variable pour initier la boucle infinie self.signal = True # Une variable pour initier la boucle infinie
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
self.__join_saved_channels() # Join existing channels
while self.signal: while self.signal:
try: try:
@@ -158,17 +163,18 @@ class Irc:
self.IrcSocket.shutdown(socket.SHUT_RDWR) self.IrcSocket.shutdown(socket.SHUT_RDWR)
self.IrcSocket.close() self.IrcSocket.close()
self.Base.logs.info("--> Fermeture de Defender ...") self.Base.logs.info("--> Fermeture de Defender ...")
sys.exit(0)
except AssertionError as ae: except AssertionError as ae:
self.Base.logs.error(f'Assertion error : {ae}') self.Base.logs.error(f'AssertionError: {ae}')
except ValueError as ve: except ValueError as ve:
self.Base.logs.error(f'Value Error : {ve}') self.Base.logs.error(f'ValueError: {ve}')
except ssl.SSLEOFError as soe: except ssl.SSLEOFError as soe:
self.Base.logs.error(f"OS Error __connect_to_irc: {soe}") self.Base.logs.error(f"SSLEOFError: {soe}")
except AttributeError as atte: except AttributeError as atte:
self.Base.logs.critical(f"{atte}") self.Base.logs.critical(f"AttributeError: {atte}")
# except Exception as e: except Exception as e:
# self.debug(f"Exception: {e}") self.Base.logs.critical(f"Exception: {e}")
def __link(self, writer:Union[socket.socket, SSLSocket]) -> None: def __link(self, writer:Union[socket.socket, SSLSocket]) -> None:
"""Créer le link et envoyer les informations nécessaires pour la """Créer le link et envoyer les informations nécessaires pour la
@@ -196,18 +202,22 @@ class Irc:
version = self.Config.current_version version = self.Config.current_version
unixtime = self.Base.get_unixtime() unixtime = self.Base.get_unixtime()
charset = self.CHARSET[0]
# Envoyer un message d'identification # Envoyer un message d'identification
writer.send(f":{sid} PASS :{password}\r\n".encode('utf-8')) writer.send(f":{sid} PASS :{password}\r\n".encode(charset))
writer.send(f":{sid} PROTOCTL NICKv2 VHP UMODE2 NICKIP SJOIN SJOIN2 SJ3 NOQUIT TKLEXT MLOCK SID MTAGS\r\n".encode('utf-8')) writer.send(f":{sid} PROTOCTL SID NOQUIT NICKv2 SJOIN SJ3 NICKIP TKLEXT2 NEXTBANS CLK EXTSWHOIS MLOCK MTAGS\r\n".encode(charset))
writer.send(f":{sid} PROTOCTL EAUTH={link},,,{service_name}-v{version}\r\n".encode('utf-8')) # writer.send(f":{sid} PROTOCTL NICKv2 VHP UMODE2 NICKIP SJOIN SJOIN2 SJ3 NOQUIT TKLEXT MLOCK SID MTAGS\r\n".encode(charset))
writer.send(f":{sid} PROTOCTL SID={sid}\r\n".encode('utf-8')) writer.send(f":{sid} PROTOCTL EAUTH={link},,,{service_name}-v{version}\r\n".encode(charset))
writer.send(f":{sid} SERVER {link} 1 :{info}\r\n".encode('utf-8')) writer.send(f":{sid} PROTOCTL SID={sid}\r\n".encode(charset))
writer.send(f":{sid} {nickname} :Reserved for services\r\n".encode('utf-8')) writer.send(f":{sid} SERVER {link} 1 :{info}\r\n".encode(charset))
writer.send(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * * :{realname}\r\n".encode('utf-8')) writer.send(f":{sid} {nickname} :Reserved for services\r\n".encode(charset))
writer.send(f":{sid} SJOIN {unixtime} {chan} + :{service_id}\r\n".encode('utf-8')) writer.send(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * * :{realname}\r\n".encode(charset))
writer.send(f":{sid} MODE {chan} +{cmodes}\r\n".encode('utf-8')) writer.send(f":{sid} SJOIN {unixtime} {chan} + :{service_id}\r\n".encode(charset))
writer.send(f":{service_id} SAMODE {chan} +{umodes} {nickname}\r\n".encode('utf-8')) writer.send(f":{sid} TKL + Q * {nickname} {host} 0 {unixtime} :Reserved for services\r\n".encode(charset))
writer.send(f":{service_id} MODE {chan} +{cmodes}\r\n".encode(charset))
writer.send(f":{service_id} MODE {chan} +{umodes} {service_id}\r\n".encode(charset))
self.Base.logs.debug('Link information sent to the server') self.Base.logs.debug('Link information sent to the server')
@@ -215,6 +225,19 @@ class Irc:
except AttributeError as ae: except AttributeError as ae:
self.Base.logs.critical(f'{ae}') self.Base.logs.critical(f'{ae}')
def __join_saved_channels(self) -> None:
"""## Joining saved channels"""
core_table = self.Config.table_channel
query = f'''SELECT distinct channel_name FROM {core_table}'''
exec_query = self.Base.db_execute_query(query)
result_query = exec_query.fetchall()
if result_query:
for chan_name in result_query:
chan = chan_name[0]
self.send2socket(f":{self.Config.SERVEUR_ID} SJOIN {self.Base.get_unixtime()} {chan} + :{self.Config.SERVICE_ID}")
def send2socket(self, send_message:str) -> None: def send2socket(self, send_message:str) -> None:
"""Envoit les commandes à envoyer au serveur. """Envoit les commandes à envoyer au serveur.
@@ -268,13 +291,18 @@ class Irc:
# FIN CONNEXION IRC # # FIN CONNEXION IRC #
############################################## ##############################################
def __create_table(self):
"""## Create core tables
"""
pass
def load_existing_modules(self) -> None: def load_existing_modules(self) -> None:
"""Charge les modules qui existe déja dans la base de données """Charge les modules qui existe déja dans la base de données
Returns: Returns:
None: Aucun retour requis, elle charge puis c'est tout None: Aucun retour requis, elle charge puis c'est tout
""" """
result = self.Base.db_execute_query(f"SELECT module FROM {self.Config.table_module}") result = self.Base.db_execute_query(f"SELECT module_name FROM {self.Config.table_module}")
for r in result.fetchall(): for r in result.fetchall():
self.load_module('sys', r[0], True) self.load_module('sys', r[0], True)
@@ -663,8 +691,10 @@ class Irc:
else: else:
version = f'{current_version}' version = f'{current_version}'
self.send2socket(f"MODE {self.Config.SERVICE_NICKNAME} +B") # self.send2socket(f":{self.Config.SERVICE_NICKNAME} SVSJOIN {self.Config.SERVICE_NICKNAME} {self.Config.SERVICE_CHANLOG}")
self.send2socket(f"JOIN {self.Config.SERVICE_CHANLOG}") # self.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.SERVICE_CHANLOG} +o {self.Config.SERVICE_NICKNAME}")
# self.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.SERVICE_CHANLOG} +{self.Config.SERVICE_CMODES}")
print(f"################### DEFENDER ###################") print(f"################### DEFENDER ###################")
print(f"# SERVICE CONNECTE ") print(f"# SERVICE CONNECTE ")
print(f"# SERVEUR : {self.Config.SERVEUR_IP} ") print(f"# SERVEUR : {self.Config.SERVEUR_IP} ")
@@ -753,8 +783,8 @@ class Irc:
for i in range(start_boucle, len(cmd)): for i in range(start_boucle, len(cmd)):
parsed_UID = str(cmd[i]) parsed_UID = str(cmd[i])
# pattern = fr'[:|@|%|\+|~|\*]*' # pattern = fr'[:|@|%|\+|~|\*]*'
pattern = fr':' # pattern = fr':'
parsed_UID = re.sub(pattern, '', parsed_UID) # parsed_UID = re.sub(pattern, '', parsed_UID)
clean_uid = self.Base.clean_uid(parsed_UID) clean_uid = self.Base.clean_uid(parsed_UID)
if len(clean_uid) == 9: if len(clean_uid) == 9:
list_users.append(parsed_UID) list_users.append(parsed_UID)
@@ -819,8 +849,10 @@ class Irc:
try: try:
# Supprimer la premiere valeur # Supprimer la premiere valeur
cmd.pop(0) cmd.pop(0)
get_uid_or_nickname = str(cmd[0].replace(':','')) get_uid_or_nickname = str(cmd[0].replace(':',''))
user_trigger = self.User.get_nickname(get_uid_or_nickname)
dnickname = self.Config.SERVICE_NICKNAME
if len(cmd) == 6: if len(cmd) == 6:
if cmd[1] == 'PRIVMSG' and str(cmd[3]).replace('.','') == ':auth': if cmd[1] == 'PRIVMSG' and str(cmd[3]).replace('.','') == ':auth':
cmd_copy = cmd.copy() cmd_copy = cmd.copy()
@@ -830,10 +862,6 @@ class Irc:
self.Base.logs.info(cmd) self.Base.logs.info(cmd)
else: else:
self.Base.logs.info(f'{cmd}') self.Base.logs.info(f'{cmd}')
# user_trigger = get_user.split('!')[0]
# user_trigger = self.get_nickname(get_uid_or_nickname)
user_trigger = self.User.get_nickname(get_uid_or_nickname)
dnickname = self.Config.SERVICE_NICKNAME
pattern = fr'(:\{self.Config.SERVICE_PREFIX})(.*)$' pattern = fr'(:\{self.Config.SERVICE_PREFIX})(.*)$'
hcmds = re.search(pattern, ' '.join(cmd)) # va matcher avec tout les caractéres aprés le . hcmds = re.search(pattern, ' '.join(cmd)) # va matcher avec tout les caractéres aprés le .
@@ -850,7 +878,8 @@ class Irc:
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)
self._hcmds(user_trigger, arg, cmd) fromchannel = str(cmd[2]).lower() if self.Base.Is_Channel(cmd[2]) else None
self._hcmds(user_trigger, fromchannel, arg, cmd)
if cmd[2] == self.Config.SERVICE_ID: if cmd[2] == self.Config.SERVICE_ID:
pattern = fr'^:.*?:(.*)$' pattern = fr'^:.*?:(.*)$'
@@ -888,7 +917,11 @@ class Irc:
cmd_to_send = convert_to_string.replace(':','') cmd_to_send = convert_to_string.replace(':','')
self.Base.log_cmd(self.User.get_nickname(user_trigger), cmd_to_send) self.Base.log_cmd(self.User.get_nickname(user_trigger), cmd_to_send)
self._hcmds(user_trigger, arg, cmd) fromchannel = None
if len(arg) >= 2:
fromchannel = str(arg[1]).lower() if self.Base.Is_Channel(arg[1]) else None
self._hcmds(user_trigger, fromchannel, arg, cmd)
except IndexError as io: except IndexError as io:
self.Base.logs.error(f'{io}') self.Base.logs.error(f'{io}')
@@ -904,7 +937,7 @@ class Irc:
except IndexError as ie: except IndexError as ie:
self.Base.logs.error(f"{ie} / {cmd} / length {str(len(cmd))}") self.Base.logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
def _hcmds(self, user: str, cmd:list, fullcmd: list = []) -> None: def _hcmds(self, user: str, channel: Union[str, None], cmd:list, fullcmd: list = []) -> None:
fromuser = self.User.get_nickname(user) # Nickname qui a lancé la commande fromuser = self.User.get_nickname(user) # Nickname qui a lancé la commande
uid = self.User.get_uid(fromuser) # Récuperer le uid de l'utilisateur uid = self.User.get_uid(fromuser) # Récuperer le uid de l'utilisateur
@@ -925,7 +958,7 @@ class Irc:
# Envoyer la commande aux classes dynamiquement chargées # Envoyer la commande aux classes dynamiquement chargées
if command != 'notallowed': if command != 'notallowed':
for classe_name, classe_object in self.loaded_classes.items(): for classe_name, classe_object in self.loaded_classes.items():
classe_object._hcmds(user, cmd, fullcmd) classe_object._hcmds(user, channel, cmd, fullcmd)
match command: match command:
@@ -1229,9 +1262,6 @@ class Irc:
reason.append(cmd[i]) reason.append(cmd[i])
final_reason = ' '.join(reason) final_reason = ' '.join(reason)
# self.db_uid.clear() #Vider UID_DB
# self.db_chan = [] #Vider les salons
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
@@ -1250,7 +1280,7 @@ class Irc:
self.Base.logs.debug(self.loaded_classes) self.Base.logs.debug(self.loaded_classes)
all_modules = self.Base.get_all_modules() all_modules = self.Base.get_all_modules()
results = self.Base.db_execute_query(f'SELECT module FROM {self.Config.table_module}') results = self.Base.db_execute_query(f'SELECT module_name FROM {self.Config.table_module}')
results = results.fetchall() results = results.fetchall()
# if len(results) == 0: # if len(results) == 0:
@@ -1277,17 +1307,15 @@ class Irc:
case 'show_timers': case 'show_timers':
if self.Base.running_timers: if self.Base.running_timers:
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :{self.Base.running_timers}") for the_timer in self.Base.running_timers:
self.send2socket(f":{dnickname} NOTICE {fromuser} :>> {the_timer.getName()} - {the_timer.is_alive()}")
else: else:
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :Aucun timers en cours d'execution") self.send2socket(f":{dnickname} NOTICE {fromuser} :Aucun timers en cours d'execution")
case 'show_threads': case 'show_threads':
running_thread_name:list = []
for thread in self.Base.running_threads: for thread in self.Base.running_threads:
running_thread_name.append(f"{thread.getName()} ({thread.is_alive()})") self.send2socket(f":{dnickname} NOTICE {fromuser} :>> {thread.getName()} ({thread.is_alive()})")
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :{str(running_thread_name)}")
case 'show_channels': case 'show_channels':

View File

@@ -1,6 +1,6 @@
import json import json, sys
from os import sep from os import sep
from typing import Union from typing import Union, Literal
from dataclasses import dataclass, field from dataclasses import dataclass, field
########################################## ##########################################
@@ -11,56 +11,128 @@ from dataclasses import dataclass, field
class ConfigDataModel: class ConfigDataModel:
SERVEUR_IP: str SERVEUR_IP: str
SERVEUR_HOSTNAME: str # Le hostname du serveur IRC """Server public IP (could be 127.0.0.1 localhost)"""
SERVEUR_LINK: str # Host attendu par votre IRCd (ex. dans votre link block pour Unrealircd)
SERVEUR_PORT: int # Port du link
SERVEUR_PASSWORD: str # Mot de passe du link (Privilégiez argon2 sur Unrealircd)
SERVEUR_ID: str # SID (identification) du bot en tant que Services
SERVEUR_SSL: bool # Activer la connexion SSL
SERVICE_NAME: str # Le nom du service SERVEUR_HOSTNAME: str
SERVICE_NICKNAME: str # Nick du bot sur IRC """IRC Server Hostname (your.hostname.extension)"""
SERVICE_REALNAME: str # Realname du bot
SERVICE_USERNAME: str # Ident du bot
SERVICE_HOST: str # Host du bot
SERVICE_INFO: str # swhois du bot
SERVICE_CHANLOG: str # Salon des logs et autres messages issus du bot
SERVICE_SMODES: str # Mode du service
SERVICE_CMODES: str # Mode du salon (#ChanLog) que le bot appliquera à son entrée
SERVICE_UMODES: str # Mode que le bot pourra se donner à sa connexion au salon chanlog
SERVICE_PREFIX: str # Prefix pour envoyer les commandes au bot
SERVICE_ID: str = field(init=False) # L'identifiant du service
OWNER: str # Identifiant du compte admin SERVEUR_LINK: str
PASSWORD: str # Mot de passe du compte admin """The link hostname (should be the same as your unrealircd link block)"""
SALON_JAIL: str # Salon pot de miel SERVEUR_PORT: int
SALON_JAIL_MODES: str # Mode du salon pot de miel """Server port as configured in your unrealircd link block"""
SALON_LIBERER: str # Le salon ou sera envoyé l'utilisateur clean
API_TIMEOUT: int # Timeout des api's SERVEUR_PASSWORD: str
"""Your link password"""
PORTS_TO_SCAN: list # Liste des ports a scanné pour une detection de proxy SERVEUR_ID: str
WHITELISTED_IP: list # IP a ne pas scanner """Service identification could be Z01 should be unique"""
GLINE_DURATION: str # La durée du gline
DEBUG_LEVEL: int # Le niveau des logs DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50 SERVEUR_SSL: bool
"""Activate SSL connexion"""
SERVICE_NAME: str
"""Service name (Ex. Defender)"""
SERVICE_NICKNAME: str
"""Nickname of the service (Ex. Defender)"""
SERVICE_REALNAME: str
"""Realname of the service"""
SERVICE_USERNAME: str
"""Username of the service"""
SERVICE_HOST: str
"""The service hostname"""
SERVICE_INFO: str
"""Swhois of the service"""
SERVICE_CHANLOG: str
"""The channel used by the service (ex. #services)"""
SERVICE_SMODES: str
"""The service mode (ex. +ioqBS)"""
SERVICE_CMODES: str
"""The mode of the log channel (ex. ntsO)"""
SERVICE_UMODES: str
"""The mode of the service when joining chanlog (ex. o, the service will be operator in the chanlog)"""
SERVICE_PREFIX: str
"""The default prefix to communicate with the service"""
SERVICE_ID: str = field(init=False)
"""The service unique ID"""
OWNER: str
"""The nickname of the admin of the service"""
PASSWORD: str
"""The password of the admin of the service"""
SALON_JAIL: str
"""The JAIL channel (ex. #jail)"""
SALON_JAIL_MODES: str
"""The jail channel modes (ex. sS)"""
SALON_LIBERER: str
"""Channel where the nickname will be released"""
API_TIMEOUT: int
"""Default api timeout in second"""
PORTS_TO_SCAN: list
"""List of ports to scan available for proxy_scan in the mod_defender module"""
WHITELISTED_IP: list
"""List of remote IP to don't scan"""
GLINE_DURATION: str
"""Gline duration"""
DEBUG_LEVEL:Literal[10, 20, 30, 40, 50]
"""Logs level: DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50"""
CONFIG_COLOR: dict[str, str] CONFIG_COLOR: dict[str, str]
table_admin: str table_admin: str
"""Admin table"""
table_commande: str table_commande: str
"""Core command table"""
table_log: str table_log: str
"""Core log table"""
table_module: str table_module: str
"""Core module table"""
table_config: str
"""Core configuration table"""
table_channel: str
"""Core channel table"""
current_version: str current_version: str
"""Current version of Defender"""
latest_version: str latest_version: str
"""The Latest version fetched from github"""
db_name: str db_name: str
"""The database name"""
db_path: str db_path: str
"""The database path"""
def __post_init__(self): def __post_init__(self):
# Initialiser SERVICE_ID après la création de l'objet # Initialiser SERVICE_ID après la création de l'objet
self.SERVICE_ID:str = f"{self.SERVEUR_ID}AAAAAB" self.SERVICE_ID:str = f"{self.SERVEUR_ID}AAAAAB"
"""The service ID which is SERVEUR_ID and AAAAAB"""
class Config: class Config:
@@ -70,15 +142,20 @@ class Config:
return None return None
def __load_json_service_configuration(self): def __load_json_service_configuration(self):
try:
conf_filename = f'core{sep}configuration.json'
with open(conf_filename, 'r') as configuration_data:
configuration:dict[str, Union[str, int, list, dict]] = json.load(configuration_data)
conf_filename = f'core{sep}configuration.json' for key, value in configuration['CONFIG_COLOR'].items():
with open(conf_filename, 'r') as configuration_data: configuration['CONFIG_COLOR'][key] = str(value).encode('utf-8').decode('unicode_escape')
configuration:dict[str, Union[str, int, list, dict]] = json.load(configuration_data)
for key, value in configuration['CONFIG_COLOR'].items(): return configuration
configuration['CONFIG_COLOR'][key] = str(value).encode('utf-8').decode('unicode_escape')
return configuration except FileNotFoundError as fe:
print(f'FileNotFound: {fe}')
print('Configuration file not found please create core/configuration.json')
sys.exit(0)
def __load_service_configuration(self) -> ConfigDataModel: def __load_service_configuration(self) -> ConfigDataModel:
import_config = self.__load_json_service_configuration() import_config = self.__load_json_service_configuration()
@@ -113,10 +190,12 @@ class Config:
GLINE_DURATION=import_config["GLINE_DURATION"], GLINE_DURATION=import_config["GLINE_DURATION"],
DEBUG_LEVEL=import_config["DEBUG_LEVEL"], DEBUG_LEVEL=import_config["DEBUG_LEVEL"],
CONFIG_COLOR=import_config["CONFIG_COLOR"], CONFIG_COLOR=import_config["CONFIG_COLOR"],
table_admin='sys_admins', table_admin='core_admin',
table_commande='sys_commandes', table_commande='core_command',
table_log='sys_logs', table_log='core_log',
table_module='sys_modules', table_module='core_module',
table_config='core_config',
table_channel='core_channel',
current_version='', current_version='',
latest_version='', latest_version='',
db_name='defender', db_name='defender',

View File

@@ -1,275 +0,0 @@
from subprocess import check_call, run, CalledProcessError, PIPE
from platform import python_version, python_version_tuple, system
from sys import exit
import os, logging, shutil
try:
import pwd
except ModuleNotFoundError as err:
print(err)
class Install:
def __init__(self) -> None:
# Python required version
self.python_min_version = '3.10'
self.log_file = 'install.log'
self.ServiceName = 'Defender'
self.venv_name = '.pyenv'
self.venv_dependencies: list[str] = ['sqlalchemy','psutil','requests']
self.install_folder = os.getcwd()
self.osname = os.name
self.system_name = system()
self.cmd_linux_requirements: list[str] = ['apt', 'install', '-y', 'python3', 'python3-pip', 'python3-venv']
self.venv_pip_full_path = os.path.join(self.venv_name, f'bin{os.sep}pip')
self.venv_python_full_path = os.path.join(self.venv_name, f'bin{os.sep}python')
self.systemd_folder = '/etc/systemd/system/'
# Init log system
self.init_log_system()
# Exclude Windows OS
if self.osname == 'nt':
print('/!\\ Windows OS is not supported by this automatic installation /!\\')
self.Logs.critical('/!\\ Windows OS is not supported by this automatic install /!\\')
print(self.system_name)
exit(5)
if not self.is_root():
exit(5)
# Get the current user
self.system_username: str = input(f'What is the user ro run defender with ? [{os.getlogin()}] : ')
if str(self.system_username).strip() == '':
self.system_username = os.getlogin()
self.get_user_information(self.system_username)
self.Logs.debug(f'The user selected is: {self.system_username}')
self.Logs.debug(f'Operating system: {self.osname}')
# Install linux dependencies
self.install_linux_dependencies()
# Check python version
self.check_python_version()
# Create systemd service file
self.create_service_file()
# Check if Env Exist | install environment | Install python dependencies
self.check_venv()
# Create and start service
if self.osname != 'nt':
self.run_subprocess(['systemctl','daemon-reload'])
self.run_subprocess(['systemctl','start', self.ServiceName])
self.run_subprocess(['systemctl','status', self.ServiceName])
# Clean the Installation
self.clean_installation()
return None
def is_installed(self) -> bool:
is_installed = False
# Check logs folder
if os.path.exists('logs'):
is_installed = True
# Check db folder
if os.path.exists('db'):
is_installed = True
return is_installed
def is_root(self) -> bool:
if os.geteuid() != 0:
print('/!\\ user must run install.py as root /!\\')
self.Logs.critical('/!\\ user must run install.py as root /!\\')
return False
elif os.geteuid() == 0:
return True
def get_user_information(self, system_user: str) -> None:
try:
username: tuple = pwd.getpwnam(system_user)
self.system_uid = username.pw_uid
self.system_gid = username.pw_gid
return None
except KeyError as ke:
self.Logs.critical(f"This user [{system_user}] doesn't exist: {ke}")
print(f"This user [{system_user}] doesn't exist: {ke}")
exit(5)
def init_log_system(self) -> None:
# Init logs object
self.Logs = logging
self.Logs.basicConfig(level=logging.DEBUG,
filename=self.log_file,
encoding='UTF-8',
format='%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s')
self.Logs.debug('#################### STARTING INSTALLATION ####################')
return None
def clean_installation(self) -> None:
# Chown the Python Env to non user privilege
self.run_subprocess(['chown','-R', f'{self.system_username}:{self.system_username}',
f'{os.path.join(self.install_folder, self.venv_name)}'
]
)
# Chown the installation log file
self.run_subprocess(['chown','-R', f'{self.system_username}:{self.system_username}',
f'{os.path.join(self.install_folder, self.log_file)}'
]
)
return None
def run_subprocess(self, command:list) -> None:
try:
run_command = check_call(command)
self.Logs.debug(f'{command} - {run_command}')
print(f'{command} - {run_command}')
except CalledProcessError as e:
print(f"Command failed :{e.returncode}")
self.Logs.critical(f"Command failed :{e.returncode}")
exit(5)
def check_python_version(self) -> bool:
"""Test si la version de python est autorisée ou non
Returns:
bool: True si la version de python est autorisé sinon False
"""
self.Logs.debug(f'The current python version is: {python_version()}')
# Current system version
sys_major, sys_minor, sys_patch = python_version_tuple()
# min python version required
python_required_version = self.PYTHON_MIN_VERSION.split('.')
min_major, min_minor = tuple((python_required_version[0], python_required_version[1]))
if int(sys_major) < int(min_major):
print(f"## Your python version must be greather than or equal to {self.PYTHON_MIN_VERSION} ##")
self.Logs.critical(f'Your python version must be greather than or equal to {self.python_min_version}')
return False
elif (int(sys_major) <= int(min_major)) and (int(sys_minor) < int(min_minor)):
print(f"## Your python version must be greather than or equal to {self.PYTHON_MIN_VERSION} ##")
self.Logs.critical(f'Your python version must be greather than or equal to {self.python_min_version}')
return False
print(f"===> Version of python : {python_version()} ==> OK")
self.Logs.debug(f'Version of python : {python_version()} ==> OK')
return True
def check_packages(self, package_name) -> bool:
try:
# Run a command in the virtual environment's Python to check if the package is installed
run([self.venv_python_full_path, '-c', f'import {package_name}'], check=True, stdout=PIPE, stderr=PIPE)
return True
except CalledProcessError:
return False
def check_venv(self) -> bool:
if os.path.exists(self.venv_name):
# Installer les dependances
self.install_dependencies()
return True
else:
self.run_subprocess(['python3', '-m', 'venv', self.venv_name])
self.Logs.debug(f'Python Virtual env installed {self.venv_name}')
print(f'Python Virtual env installed {self.venv_name}')
self.install_dependencies()
return False
def create_service_file(self) -> None:
if self.systemd_folder is None:
# If Windows, do not install systemd
return None
if os.path.exists(f'{self.systemd_folder}{os.sep}{self.ServiceName}.service'):
print(f'/!\\ Service already created in the system /!\\')
self.Logs.warning('/!\\ Service already created in the system /!\\')
print(f'The service file will be regenerated')
self.Logs.warning('The service file will be regenerated')
contain = f'''[Unit]
Description={self.ServiceName} IRC Service
[Service]
User={self.system_username}
ExecStart={os.path.join(self.install_folder, self.venv_python_full_path)} {os.path.join(self.install_folder, 'main.py')}
WorkingDirectory={self.install_folder}
SyslogIdentifier={self.ServiceName}
Restart=on-failure
[Install]
WantedBy=multi-user.target
'''
with open(f'{self.ServiceName}.service.generated', 'w+') as servicefile:
servicefile.write(contain)
servicefile.close()
print('Service file generated with current configuration')
self.Logs.debug('Service file generated with current configuration')
source = f'{self.install_folder}{os.sep}{self.ServiceName}.service.generated'
self.run_subprocess(['chown','-R', f'{self.system_username}:{self.system_username}', source])
destination = f'{self.systemd_folder}'
shutil.copy(source, destination)
os.rename(f'{self.systemd_folder}{os.sep}{self.ServiceName}.service.generated', f'{self.systemd_folder}{os.sep}{self.ServiceName}.service')
print(f'Service file moved to systemd folder {self.systemd_folder}')
self.Logs.debug(f'Service file moved to systemd folder {self.systemd_folder}')
def install_linux_dependencies(self) -> None:
self.run_subprocess(self.cmd_linux_requirements)
return None
def install_dependencies(self) -> None:
try:
self.run_subprocess([self.venv_pip_full_path, 'cache', 'purge'])
self.run_subprocess([self.venv_python_full_path, '-m', 'pip', 'install', '--upgrade', 'pip'])
if self.check_packages('greenlet') is None:
self.run_subprocess(
[self.venv_pip_full_path, 'install', '--only-binary', ':all:', 'greenlet']
)
for module in self.venv_dependencies:
if not self.check_packages(module):
### Trying to install missing python packages ###
self.run_subprocess([self.venv_pip_full_path, 'install', module])
else:
self.Logs.debug(f'{module} already installed')
print(f"==> {module} already installed")
except CalledProcessError as cpe:
self.Logs.critical(f'{cpe}')
Install()

304
mods/mod_clone.py Normal file
View File

@@ -0,0 +1,304 @@
from dataclasses import dataclass, fields, field
import random, faker, time
from datetime import datetime
from typing import Union
from core.irc import Irc
from core.connection import Connection
class Clone():
@dataclass
class ModConfModel:
clone_nicknames: list[str]
def __init__(self, ircInstance:Irc) -> None:
# Module name (Mandatory)
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
# Add Irc Object to the module (Mandatory)
self.Irc = ircInstance
# Add Global Configuration to the module (Mandatory)
self.Config = ircInstance.Config
# Add Base object to the module (Mandatory)
self.Base = ircInstance.Base
# Add logs object to the module (Mandatory)
self.Logs = ircInstance.Base.logs
# Add User object to the module (Mandatory)
self.User = ircInstance.User
# Add Channel object to the module (Mandatory)
self.Channel = ircInstance.Channel
self.Clone = ircInstance.Clones
# Créer les nouvelles commandes du module
self.commands_level = {
1: ['clone']
}
# Init the module (Mandatory)
self.__init_module()
# Log the module
self.Logs.debug(f'Module {self.module_name} loaded ...')
def __init_module(self) -> None:
# Enrigstrer les nouvelles commandes dans le code
self.__set_commands(self.commands_level)
# Créer les tables necessaire a votre module (ce n'es pas obligatoire)
self.__create_tables()
# Load module configuration (Mandatory)
self.__load_module_configuration()
def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal
Args:
commands (list): Liste des commandes du module
"""
for level, com in commands.items():
for c in commands[level]:
if not c in self.Irc.commands:
self.Irc.commands_level[level].append(c)
self.Irc.commands.append(c)
return None
def __create_tables(self) -> None:
"""Methode qui va créer la base de donnée si elle n'existe pas.
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
Args:
database_name (str): Nom de la base de données ( pas d'espace dans le nom )
Returns:
None: Aucun retour n'es attendu
"""
table_channel = '''CREATE TABLE IF NOT EXISTS clone_list (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
nickname TEXT,
username TEXT
)
'''
self.Base.db_execute_query(table_channel)
return None
def __load_module_configuration(self) -> None:
"""### Load Module Configuration
"""
try:
# Variable qui va contenir les options de configuration du module Defender
self.ModConfig = self.ModConfModel(
clone_nicknames=[]
)
# Sync the configuration with core configuration (Mandatory)
# self.Base.db_sync_core_config(self.module_name, self.ModConfig)
return None
except TypeError as te:
self.Logs.critical(te)
def unload(self) -> None:
"""Cette methode sera executée a chaque désactivation ou
rechargement de module
"""
# kill all clones before unload
for clone in self.ModConfig.clone_nicknames:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone} :KILL')
return None
def thread_create_clones(self, nickname: str, username: str, channels: list, server_port: int, ssl: bool) -> None:
Connection(server_port=server_port, nickname=nickname, username=username, channels=channels, CloneObject=self.Clone, ssl=ssl)
return None
def thread_join_channels(self, channel_name: str, wait: float, clone_name:str = None):
if clone_name is None:
for clone in self.Clone.UID_CLONE_DB:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}')
time.sleep(wait)
else:
for clone in self.Clone.UID_CLONE_DB:
if clone_name == clone.nickname:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}')
time.sleep(wait)
def generate_names(self) -> tuple[str, str]:
try:
fake = faker.Faker('en_GB')
nickname = fake.first_name()
username = fake.last_name()
if self.Clone.exists(nickname=nickname):
caracteres = '0123456789'
randomize = ''.join(random.choice(caracteres) for _ in range(2))
nickname = nickname + str(randomize)
self.Clone.insert(
self.Clone.CloneModel(alive=True, nickname=nickname, username=username)
)
else:
self.Clone.insert(
self.Clone.CloneModel(alive=True, nickname=nickname, username=username)
)
# if not nickname in self.ModConfig.clone_nicknames:
# self.ModConfig.clone_nicknames.append(nickname)
# else:
# caracteres = '0123456789'
# randomize = ''.join(random.choice(caracteres) for _ in range(2))
# nickname = nickname + str(randomize)
# self.ModConfig.clone_nicknames.append(nickname)
return (nickname, username)
except AttributeError as ae:
self.Logs.error(f'Attribute Error : {ae}')
except Exception as err:
self.Logs.error(err)
def cmd(self, data:list) -> None:
service_id = self.Config.SERVICE_ID # Defender serveur id
cmd = list(data).copy()
if len(cmd) < 2:
return None
match cmd[1]:
case 'REPUTATION':
pass
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower()
fromuser = user
dnickname = self.Config.SERVICE_NICKNAME # Defender nickname
match command:
case 'clone':
option = str(cmd[1]).lower()
if len(command) == 1:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect 6')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill [all | nickname]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join [all | nickname] #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list')
match option:
case 'connect':
try:
number_of_clones = int(cmd[2])
for i in range(number_of_clones):
nickname, username = self.generate_names()
self.Base.create_thread(
self.thread_create_clones,
(nickname, username, [], 6697, True)
)
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{str(number_of_clones)} clones joined the network')
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect [number of clone you want to connect]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} clone connect 6')
case 'kill':
try:
# clone kill [all | nickname]
clone_name = str(cmd[2])
clone_to_kill: list[str] = []
if clone_name.lower() == 'all':
for clone in self.Clone.UID_CLONE_DB:
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone.nickname} :KILL')
clone_to_kill.append(clone.nickname)
clone.alive = False
for clone_nickname in clone_to_kill:
self.Clone.delete(clone_nickname)
del clone_to_kill
else:
if self.Clone.exists(clone_name):
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :KILL')
self.Clone.kill(clone_name)
self.Clone.delete(clone_name)
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill all')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill clone_nickname')
case 'join':
try:
# clone join [all | nickname] #channel
clone_name = str(cmd[2])
clone_channel_to_join = str(cmd[3])
if clone_name.lower() == 'all':
self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2))
else:
self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2, clone_name))
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join all #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join clone_nickname #channel')
case 'list':
try:
for clone_name in self.Clone.UID_CLONE_DB:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> {clone_name.nickname} | {clone_name.username}')
pass
except Exception as err:
self.Logs.error(f'{err}')
case 'say':
try:
# clone say clone_nickname #channel message
clone_name = str(cmd[2])
clone_channel = str(cmd[3]) if self.Base.Is_Channel(str(cmd[3])) else None
message = []
for i in range(4, len(cmd)):
message.append(cmd[i])
final_message = ' '.join(message)
if clone_channel is None or not self.Clone.exists(clone_name):
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel message')
return None
if self.Clone.exists(clone_name):
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :SAY {clone_channel} {final_message}')
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel message')
case _:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect 6')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill [all | nickname]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join [all | nickname] #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel [message]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list')

588
mods/mod_command.py Normal file
View File

@@ -0,0 +1,588 @@
from dataclasses import dataclass, fields
from core.irc import Irc
class Command():
@dataclass
class ModConfModel:
"""The Model containing the module parameters
"""
pass
def __init__(self, ircInstance:Irc) -> None:
# Module name (Mandatory)
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
# Add Irc Object to the module (Mandatory)
self.Irc = ircInstance
# Add Global Configuration to the module (Mandatory)
self.Config = ircInstance.Config
# Add Base object to the module (Mandatory)
self.Base = ircInstance.Base
# Add logs object to the module (Mandatory)
self.Logs = ircInstance.Base.logs
# Add User object to the module (Mandatory)
self.User = ircInstance.User
# Add Channel object to the module (Mandatory)
self.Channel = ircInstance.Channel
# Create module commands (Mandatory)
self.commands_level = {
1: ['join', 'part'],
2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice', 'devoice', 'opall', 'deopall', 'devoiceall', 'voiceall', 'ban', 'unban','kick', 'kickban', 'umode']
}
# Init the module
self.__init_module()
# Log the module
self.Logs.debug(f'Module {self.module_name} loaded ...')
def __init_module(self) -> None:
# Insert module commands into the core one (Mandatory)
self.__set_commands(self.commands_level)
# Create you own tables (Mandatory)
self.__create_tables()
# Load module configuration and sync with core one (Mandatory)
self.__load_module_configuration()
# End of mandatory methods you can start your customization #
return None
def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal
Args:
commands (list): Liste des commandes du module
"""
for level, com in commands.items():
for c in commands[level]:
if not c in self.Irc.commands:
self.Irc.commands_level[level].append(c)
self.Irc.commands.append(c)
return None
def __create_tables(self) -> None:
"""Methode qui va créer la base de donnée si elle n'existe pas.
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
Args:
database_name (str): Nom de la base de données ( pas d'espace dans le nom )
Returns:
None: Aucun retour n'es attendu
"""
table_logs = '''CREATE TABLE IF NOT EXISTS test_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
server_msg TEXT
)
'''
self.Base.db_execute_query(table_logs)
return None
def __load_module_configuration(self) -> None:
"""### Load Module Configuration
"""
try:
# Build the default configuration model (Mandatory)
self.ModConfig = self.ModConfModel()
# Sync the configuration with core configuration (Mandatory)
self.Base.db_sync_core_config(self.module_name, self.ModConfig)
return None
except TypeError as te:
self.Logs.critical(te)
def __update_configuration(self, param_key: str, param_value: str):
"""Update the local and core configuration
Args:
param_key (str): The parameter key
param_value (str): The parameter value
"""
self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
def unload(self) -> None:
return None
def add_defender_channel(self, channel:str) -> bool:
"""Cette fonction ajoute les salons de join de Defender
Args:
channel (str): le salon à enregistrer.
"""
mes_donnees = {'channel': channel}
response = self.Base.db_execute_query("SELECT id FROM def_channels WHERE channel = :channel", mes_donnees)
isChannelExist = response.fetchone()
if isChannelExist is None:
mes_donnees = {'datetime': self.Base.get_datetime(), 'channel': channel}
insert = self.Base.db_execute_query(f"INSERT INTO def_channels (datetime, channel) VALUES (:datetime, :channel)", mes_donnees)
if insert.rowcount >=0:
return True
else:
return False
else:
return False
def delete_defender_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 def_channels WHERE channel = :channel", mes_donnes)
affected_row = response.rowcount
if affected_row > 0:
return True
else:
return False
def cmd(self, data:list) -> None:
return None
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower()
dnickname = self.Config.SERVICE_NICKNAME
service_id = self.Config.SERVICE_ID
dchanlog = self.Config.SERVICE_CHANLOG
fromuser = user
fromchannel = channel
match command:
case 'deopall':
try:
self.Irc.send2socket(f":{service_id} SVSMODE {fromchannel} -o")
except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'devoiceall':
try:
self.Irc.send2socket(f":{service_id} SVSMODE {fromchannel} -v")
except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'voiceall':
try:
chan_info = self.Channel.get_Channel(fromchannel)
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.Irc.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.Irc.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:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'opall':
try:
chan_info = self.Channel.get_Channel(fromchannel)
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.Irc.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.Irc.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:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'op':
# /mode #channel +o user
# .op #channel user
# /msg dnickname op #channel user
# [':adator', 'PRIVMSG', '#services', ':.o', '#services', 'dktmb']
try:
if fromchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} op [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{dnickname} MODE {fromchannel} +o {fromuser}")
return True
# deop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +o {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +o {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} op [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'deop':
# /mode #channel -o user
# .deop #channel user
try:
if fromchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deop [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -o {fromuser}")
return True
# deop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -o {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -o {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd DEOP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deop [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'owner':
# /mode #channel +q user
# .owner #channel user
try:
if fromchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} owner [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +q {fromuser}")
return True
# owner nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +q {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +q {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd OWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} owner [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'deowner':
# /mode #channel -q user
# .deowner #channel user
try:
if fromchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -q {fromuser}")
return True
# deowner nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -q {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -q {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd DEOWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'halfop':
# /mode #channel +h user
# .halfop #channel user
try:
if fromchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} halfop [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +h {fromuser}")
return True
# deop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +h {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +h {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd halfop: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} halfop [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'dehalfop':
# /mode #channel -h user
# .dehalfop #channel user
try:
if fromchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -h {fromuser}")
return True
# dehalfop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -h {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -h {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd DEHALFOP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'voice':
# /mode #channel +v user
# .voice #channel user
try:
if fromchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} voice [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +v {fromuser}")
return True
# voice nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +v {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +v {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd VOICE: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} voice [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'devoice':
# /mode #channel -v user
# .devoice #channel user
try:
if fromchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -v {fromuser}")
return True
# dehalfop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -v {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -v {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd DEVOICE: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'ban':
# .ban #channel nickname
try:
sentchannel = str(cmd[1]) if self.Base.Is_Channel(cmd[1]) else None
if sentchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} ban [#SALON] [NICKNAME]')
return False
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {sentchannel} +b {nickname}!*@*")
self.Logs.debug(f'{fromuser} has banned {nickname} from {sentchannel}')
except IndexError as e:
self.Logs.warning(f'_hcmd BAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} ban [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'unban':
# .unban #channel nickname
try:
sentchannel = str(cmd[1]) if self.Base.Is_Channel(cmd[1]) else None
if sentchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} ban [#SALON] [NICKNAME]')
return False
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {sentchannel} -b {nickname}!*@*")
self.Logs.debug(f'{fromuser} has unbanned {nickname} from {sentchannel}')
except IndexError as e:
self.Logs.warning(f'_hcmd UNBAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} unban [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'kick':
# .kick #channel nickname reason
try:
sentchannel = str(cmd[1]) if self.Base.Is_Channel(cmd[1]) else None
if sentchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} ban [#SALON] [NICKNAME]')
return False
nickname = cmd[2]
reason = []
for i in range(3, len(cmd)):
reason.append(cmd[i])
final_reason = ' '.join(reason)
self.Irc.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:
self.Logs.warning(f'_hcmd KICK: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kick [#SALON] [NICKNAME] [REASON]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'kickban':
# .kickban #channel nickname reason
try:
sentchannel = str(cmd[1]) if self.Base.Is_Channel(cmd[1]) else None
if sentchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} ban [#SALON] [NICKNAME]')
return False
nickname = cmd[2]
reason = []
for i in range(3, len(cmd)):
reason.append(cmd[i])
final_reason = ' '.join(reason)
self.Irc.send2socket(f":{service_id} KICK {sentchannel} {nickname} {final_reason}")
self.Irc.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:
self.Logs.warning(f'_hcmd KICKBAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kickban [#SALON] [NICKNAME] [REASON]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'join':
try:
sent_channel = str(cmd[1]) if self.Base.Is_Channel(cmd[1]) else None
if sent_channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{self.Config.SERVICE_PREFIX}JOIN #channel')
return False
self.Irc.send2socket(f':{service_id} JOIN {sent_channel}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {dnickname} JOINED {sent_channel}')
self.Base.db_query_channel('add', self.module_name, sent_channel)
except IndexError as ie:
self.Logs.error(f'{ie}')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'part':
try:
sent_channel = str(cmd[1]) if self.Base.Is_Channel(cmd[1]) else None
if sent_channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{self.Config.SERVICE_PREFIX}PART #channel')
return False
if sent_channel == dchanlog:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} : {dnickname} CAN'T LEFT {sent_channel} AS IT IS LOG CHANNEL")
return False
self.Irc.send2socket(f':{service_id} PART {sent_channel}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {dnickname} LEFT {sent_channel}')
self.Base.db_query_channel('del', self.module_name, sent_channel)
except IndexError as ie:
self.Logs.error(f'{ie}')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'umode':
try:
# .umode nickname +mode
nickname = str(cmd[1])
umode = str(cmd[2])
self.send2socket(f':{dnickname} SVSMODE {nickname} {umode}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')

View File

@@ -2,6 +2,7 @@ from dataclasses import dataclass, fields, field
from datetime import datetime from datetime import datetime
from typing import Union from typing import Union
import re, socket, psutil, requests, json, time import re, socket, psutil, requests, json, time
from sys import exit
from core.irc import Irc from core.irc import Irc
# Le module crée devra réspecter quelques conditions # Le module crée devra réspecter quelques conditions
@@ -55,41 +56,91 @@ class Defender():
def __init__(self, ircInstance:Irc) -> None: def __init__(self, ircInstance:Irc) -> None:
self.Irc = ircInstance # Ajouter l'object mod_irc a la classe ( Obligatoire ) # Module name (Mandatory)
self.Config = ircInstance.Config # Ajouter la configuration a la classe ( Obligatoire ) self.module_name = 'mod_' + str(self.__class__.__name__).lower()
self.User = ircInstance.User # Importer les liste des User connectés ( Obligatoire )
self.Channel = ircInstance.Channel # Ajouter la liste des salons ( Obligatoire )
self.Base = ircInstance.Base # Ajouter l'objet Base au module ( Obligatoire )
self.Logs = ircInstance.Base.logs # Ajouter l'objet log ( Obligatoire )
self.timeout = self.Config.API_TIMEOUT # API Timeout
self.freeipapi_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec freeipapi # Add Irc Object to the module (Mandatory)
self.cloudfilt_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec cloudfilt self.Irc = ircInstance
self.abuseipdb_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec abuseipdb
self.psutil_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec psutil_scan
self.localscan_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec local_scan
self.abuseipdb_isRunning:bool = True # Add Global Configuration to the module (Mandatory)
self.freeipapi_isRunning:bool = True self.Config = ircInstance.Config
self.cloudfilt_isRunning:bool = True
self.psutil_isRunning:bool = True
self.localscan_isRunning:bool = True
self.reputationTimer_isRunning:bool = True
self.Logs.info(f'Module {self.__class__.__name__} loaded ...') # Add Base object to the module (Mandatory)
self.Base = ircInstance.Base
# Créer les nouvelles commandes du module # Add logs object to the module (Mandatory)
self.Logs = ircInstance.Base.logs
# Add User object to the module (Mandatory)
self.User = ircInstance.User
# Add Channel object to the module (Mandatory)
self.Channel = ircInstance.Channel
# Create module commands (Mandatory)
self.commands_level = { self.commands_level = {
0: ['code'], 0: ['code'],
1: ['join','part', 'info'], 1: ['info'],
2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice', 'devoice', 'ban', 'unban','kick', 'kickban'], 2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice', 'devoice', 'ban', 'unban','kick', 'kickban'],
3: ['reputation','proxy_scan', 'flood', 'status', 'timer','show_reputation', 'sentinel'] 3: ['reputation','proxy_scan', 'flood', 'status', 'timer','show_reputation', 'sentinel']
} }
self.__set_commands(self.commands_level) # Enrigstrer les nouvelles commandes dans le code
self.__create_tables() # Créer les tables necessaire a votre module (ce n'es pas obligatoire) # Init the module (Mandatory)
self.__init_module()
self.init_defender() # Créer une methode init ( ce n'es pas obligatoire ) # Log the module
self.Logs.debug(f'Module {self.module_name} loaded ...')
def __init_module(self) -> None:
# Insert module commands into the core one (Mandatory)
self.__set_commands(self.commands_level)
# Create you own tables if needed (Mandatory)
self.__create_tables()
# Load module configuration (Mandatory)
self.__load_module_configuration()
# End of mandatory methods you can start your customization #
# # Rejoindre les salons
# self.join_saved_channels()
self.timeout = self.Config.API_TIMEOUT
# Listes qui vont contenir les ip a scanner avec les différentes API
self.freeipapi_remote_ip:list = []
self.cloudfilt_remote_ip:list = []
self.abuseipdb_remote_ip:list = []
self.psutil_remote_ip:list = []
self.localscan_remote_ip:list = []
# Variables qui indique que les threads sont en cours d'éxecutions
self.abuseipdb_isRunning:bool = True
self.freeipapi_isRunning:bool = True
self.cloudfilt_isRunning:bool = True
self.psutil_isRunning:bool = True
self.localscan_isRunning:bool = True
self.reputationTimer_isRunning:bool = True
# Variable qui va contenir les users
self.flood_system = {}
# Contient les premieres informations de connexion
self.reputation_first_connexion = {'ip': '', 'score': -1}
# Laisser vide si aucune clé
self.abuseipdb_key = '13c34603fee4d2941a2c443cc5c77fd750757ca2a2c1b304bd0f418aff80c24be12651d1a3cfe674'
self.cloudfilt_key = 'r1gEtjtfgRQjtNBDMxsg'
# Démarrer les threads pour démarrer les api
self.Base.create_thread(func=self.thread_freeipapi_scan)
self.Base.create_thread(func=self.thread_cloudfilt_scan)
self.Base.create_thread(func=self.thread_abuseipdb_scan)
self.Base.create_thread(func=self.thread_local_scan)
self.Base.create_thread(func=self.thread_psutil_scan)
self.Base.create_thread(func=self.thread_reputation_timer)
return None
def __set_commands(self, commands:dict[int, list[str]]) -> None: def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal """### Rajoute les commandes du module au programme principal
@@ -105,24 +156,6 @@ class Defender():
return None return None
def unload(self) -> None:
"""Cette methode sera executée a chaque désactivation ou
rechargement de module
"""
self.abuseipdb_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec abuseipdb
self.freeipapi_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec freeipapi
self.cloudfilt_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec cloudfilt
self.psutil_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec psutil_scan
self.localscan_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec local_scan
self.abuseipdb_isRunning:bool = False
self.freeipapi_isRunning:bool = False
self.cloudfilt_isRunning:bool = False
self.psutil_isRunning:bool = False
self.localscan_isRunning:bool = False
self.reputationTimer_isRunning:bool = False
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
@@ -157,141 +190,52 @@ class Defender():
) )
''' '''
self.Base.db_execute_query(table_channel) # self.Base.db_execute_query(table_channel)
self.Base.db_execute_query(table_config) # self.Base.db_execute_query(table_config)
self.Base.db_execute_query(table_trusted) # self.Base.db_execute_query(table_trusted)
return None return None
def init_defender(self) -> bool: def __load_module_configuration(self) -> None:
"""### Load Module Configuration
# self.db_reputation = {} # Definir la variable qui contiendra la liste des user concerné par la réputation """
self.flood_system = {} # Variable qui va contenir les users
self.reputation_first_connexion = {'ip': '', 'score': -1} # Contient les premieres informations de connexion
# 13c34603fee4d2941a2c443cc5c77fd750757ca2a2c1b304bd0f418aff80c24be12651d1a3cfe674
self.abuseipdb_key = '13c34603fee4d2941a2c443cc5c77fd750757ca2a2c1b304bd0f418aff80c24be12651d1a3cfe674' # Laisser vide si aucune clé
# r1gEtjtfgRQjtNBDMxsg
self.cloudfilt_key = 'r1gEtjtfgRQjtNBDMxsg' # Laisser vide si aucune clé
# Rejoindre les salons
self.join_saved_channels()
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.ModConfModel(
reputation=0, reputation_timer=0, reputation_seuil=26, reputation_score_after_release=27, reputation=0, reputation_timer=1, reputation_seuil=26, reputation_score_after_release=27,
reputation_ban_all_chan=0,reputation_sg=1, reputation_ban_all_chan=0,reputation_sg=1,
local_scan=0, psutil_scan=0, abuseipdb_scan=0, freeipapi_scan=0, cloudfilt_scan=0, local_scan=0, psutil_scan=0, abuseipdb_scan=0, freeipapi_scan=0, cloudfilt_scan=0,
flood=0, flood_message=5, flood_time=1, flood_timer=20 flood=0, flood_message=5, flood_time=1, flood_timer=20
) )
# Sync the configuration with core configuration (Mandatory)
self.Base.db_sync_core_config(self.module_name, self.ModConfig)
return None
except TypeError as te: except TypeError as te:
self.Logs.critical(te) self.Logs.critical(te)
# Logger en debug la variable de configuration def __update_configuration(self, param_key: str, param_value: str):
self.Logs.debug(self.ModConfig)
# Syncrhoniser l'objet ModConfig avec la configuration de la base de données. self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
self.sync_db_configuration()
# Démarrer les threads pour démarrer les api def unload(self) -> None:
self.Base.create_thread(func=self.thread_freeipapi_scan) """Cette methode sera executée a chaque désactivation ou
self.Base.create_thread(func=self.thread_cloudfilt_scan) rechargement de module
self.Base.create_thread(func=self.thread_abuseipdb_scan)
self.Base.create_thread(func=self.thread_local_scan)
self.Base.create_thread(func=self.thread_psutil_scan)
self.Base.create_thread(func=self.thread_reputation_timer)
return True
def sync_db_configuration(self) -> None:
query = "SELECT parameter, value FROM def_config"
response = self.Base.db_execute_query(query)
result = response.fetchall()
# Si le resultat ne contient aucune valeur
if not result:
# Base de données vide Inserer la premiere configuration
for field in fields(self.ModConfig):
mes_donnees = {'datetime': self.Base.get_datetime(), 'parameter': field.name, 'value': getattr(self.ModConfig, field.name)}
insert = self.Base.db_execute_query('INSERT INTO def_config (datetime, parameter, value) VALUES (:datetime, :parameter, :value)', mes_donnees)
insert_rows = insert.rowcount
if insert_rows > 0:
self.Logs.debug(f'Row affected into def_config : {insert_rows}')
# Inserer une nouvelle configuration
for field in fields(self.ModConfig):
mes_donnees = {'parameter': field.name}
search_param_query = "SELECT parameter, value FROM def_config WHERE parameter = :parameter"
result = self.Base.db_execute_query(search_param_query, mes_donnees)
isParamExist = result.fetchone()
if isParamExist is None:
mes_donnees = {'datetime': self.Base.get_datetime(), 'parameter': field.name, 'value': getattr(self.ModConfig, field.name)}
insert = self.Base.db_execute_query('INSERT INTO def_config (datetime, parameter, value) VALUES (:datetime, :parameter, :value)', mes_donnees)
insert_rows = insert.rowcount
if insert_rows > 0:
self.Logs.debug(f'DB_Def_config - new param included : {insert_rows}')
# Supprimer un parameter si il n'existe plus dans la variable global
query = "SELECT parameter FROM def_config"
response = self.Base.db_execute_query(query)
dbresult = response.fetchall()
for dbparam in dbresult:
if not hasattr(self.ModConfig, dbparam[0]):
mes_donnees = {'parameter': dbparam[0]}
delete = self.Base.db_execute_query('DELETE FROM def_config WHERE parameter = :parameter', mes_donnees)
row_affected = delete.rowcount
if row_affected > 0:
self.Logs.debug(f'DB_Def_config - param [{dbparam[0]}] has been deleted')
# Synchroniser la base de données avec la variable global
query = "SELECT parameter, value FROM def_config"
response = self.Base.db_execute_query(query)
result = response.fetchall()
for param, value in result:
setattr(self.ModConfig, param, self.Base.int_if_possible(value))
self.Logs.debug(self.ModConfig)
return None
def update_db_configuration(self, param:str, value:str) -> bool:
"""Check the parameter if it exist and return True if success
Args:
param (str): The parameter name
value (str): The value
Returns:
bool: True if success or False
""" """
response = False self.abuseipdb_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec abuseipdb
self.freeipapi_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec freeipapi
self.cloudfilt_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec cloudfilt
self.psutil_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec psutil_scan
self.localscan_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec local_scan
# Check if the param exist self.abuseipdb_isRunning:bool = False
if not hasattr(self.ModConfig, param): self.freeipapi_isRunning:bool = False
self.Logs.error(f"Le parametre {param} n'existe pas dans la variable global") self.cloudfilt_isRunning:bool = False
return response self.psutil_isRunning:bool = False
self.localscan_isRunning:bool = False
mes_donnees = {'parameter': param} self.reputationTimer_isRunning:bool = False
search_param_query = "SELECT parameter, value FROM def_config WHERE parameter = :parameter" return None
result = self.Base.db_execute_query(search_param_query, mes_donnees)
isParamExist = result.fetchone()
if not isParamExist is None:
mes_donnees = {'datetime': self.Base.get_datetime(), 'parameter': param, 'value': value}
update = self.Base.db_execute_query('UPDATE def_config SET datetime = :datetime, value = :value WHERE parameter = :parameter', mes_donnees)
updated_rows = update.rowcount
if updated_rows > 0:
setattr(self.ModConfig, param, self.Base.int_if_possible(value))
self.Logs.debug(f'DB_Def_config - new param updated : {param} {value}')
response = True
self.Logs.debug(self.ModConfig)
return response
def add_defender_channel(self, channel:str) -> bool: def add_defender_channel(self, channel:str) -> bool:
"""Cette fonction ajoute les salons de join de Defender """Cette fonction ajoute les salons de join de Defender
@@ -1178,10 +1122,12 @@ class Defender():
self.Irc.send2socket(f":{service_id} MODE {chan.name} -b {final_nickname}!*@*") self.Irc.send2socket(f":{service_id} MODE {chan.name} -b {final_nickname}!*@*")
self.reputation_delete(final_UID) self.reputation_delete(final_UID)
def _hcmds(self, user:str, cmd: list, fullcmd: list = []) -> None: def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower() command = str(cmd[0]).lower()
fromuser = user fromuser = user
fromchannel = channel if self.Base.Is_Channel(channel) else None
channel = fromchannel
dnickname = self.Config.SERVICE_NICKNAME # Defender nickname dnickname = self.Config.SERVICE_NICKNAME # Defender nickname
dchanlog = self.Config.SERVICE_CHANLOG # Defender chan log dchanlog = self.Config.SERVICE_CHANLOG # Defender chan log
@@ -1190,23 +1136,6 @@ class Defender():
jail_chan = self.Config.SALON_JAIL # Salon pot de miel jail_chan = self.Config.SALON_JAIL # Salon pot de miel
jail_chan_mode = self.Config.SALON_JAIL_MODES # Mode du salon "pot de miel" jail_chan_mode = self.Config.SALON_JAIL_MODES # Mode du salon "pot de miel"
if len(fullcmd) >= 3:
fromchannel = str(fullcmd[2]).lower() if self.Base.Is_Channel(str(fullcmd[2]).lower()) else None
else:
fromchannel = None
if len(cmd) >= 2:
sentchannel = str(cmd[1]).lower() if self.Base.Is_Channel(str(cmd[1]).lower()) else None
else:
sentchannel = None
if not fromchannel is None:
channel = fromchannel
elif not sentchannel is None:
channel = sentchannel
else:
channel = None
match command: match command:
case 'timer': case 'timer':
@@ -1223,10 +1152,10 @@ class Defender():
case 'show_reputation': case 'show_reputation':
if not self.UID_REPUTATION_DB: if not self.UID_REPUTATION_DB:
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} : No one is suspected') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : No one is suspected')
for suspect in self.UID_REPUTATION_DB: for suspect in self.UID_REPUTATION_DB:
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} : Uid: {suspect.uid} | Nickname: {suspect.nickname} | Reputation: {suspect.score} | Secret code: {suspect.secret_code} | Connected on: {suspect.connected_datetime}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Uid: {suspect.uid} | Nickname: {suspect.nickname} | Reputation: {suspect.score} | Secret code: {suspect.secret_code} | Connected on: {suspect.connected_datetime}')
case 'code': case 'code':
try: try:
@@ -1294,7 +1223,9 @@ class Defender():
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}REPUTATION{self.Config.CONFIG_COLOR['noire']} ] : Already activated") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}REPUTATION{self.Config.CONFIG_COLOR['noire']} ] : Already activated")
return False return False
self.update_db_configuration('reputation', 1) # self.update_db_configuration('reputation', 1)
self.__update_configuration(key, 1)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}REPUTATION{self.Config.CONFIG_COLOR['noire']} ] : Activated by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}REPUTATION{self.Config.CONFIG_COLOR['noire']} ] : Activated by {fromuser}")
self.Irc.send2socket(f":{service_id} JOIN {jail_chan}") self.Irc.send2socket(f":{service_id} JOIN {jail_chan}")
self.Irc.send2socket(f":{service_id} SAMODE {jail_chan} +{dumodes} {dnickname}") self.Irc.send2socket(f":{service_id} SAMODE {jail_chan} +{dumodes} {dnickname}")
@@ -1308,7 +1239,7 @@ class Defender():
self.Irc.send2socket(f":{service_id} MODE {chan.name} +e ~security-group:known-users") self.Irc.send2socket(f":{service_id} MODE {chan.name} +e ~security-group:known-users")
self.Irc.send2socket(f":{service_id} MODE {chan.name} +e ~security-group:websocket-users") self.Irc.send2socket(f":{service_id} MODE {chan.name} +e ~security-group:websocket-users")
self.add_defender_channel(jail_chan) self.Base.db_query_channel('add', self.module_name, jail_chan)
if activation == 'off': if activation == 'off':
@@ -1316,7 +1247,8 @@ class Defender():
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}REPUTATION{self.Config.CONFIG_COLOR['noire']} ] : Already deactivated") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}REPUTATION{self.Config.CONFIG_COLOR['noire']} ] : Already deactivated")
return False return False
self.update_db_configuration('reputation', 0) self.__update_configuration(key, 0)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}REPUTATION{self.Config.CONFIG_COLOR['noire']} ] : Deactivated by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}REPUTATION{self.Config.CONFIG_COLOR['noire']} ] : Deactivated by {fromuser}")
self.Irc.send2socket(f":{service_id} SAMODE {jail_chan} -{dumodes} {dnickname}") self.Irc.send2socket(f":{service_id} SAMODE {jail_chan} -{dumodes} {dnickname}")
self.Irc.send2socket(f":{service_id} MODE {jail_chan} -sS") self.Irc.send2socket(f":{service_id} MODE {jail_chan} -sS")
@@ -1329,7 +1261,7 @@ class Defender():
self.Irc.send2socket(f":{service_id} MODE {chan.name} -e ~security-group:known-users") self.Irc.send2socket(f":{service_id} MODE {chan.name} -e ~security-group:known-users")
self.Irc.send2socket(f":{service_id} MODE {chan.name} -e ~security-group:websocket-users") self.Irc.send2socket(f":{service_id} MODE {chan.name} -e ~security-group:websocket-users")
self.delete_defender_channel(jail_chan) self.Base.db_query_channel('del', self.module_name, jail_chan)
if len_cmd == 4: if len_cmd == 4:
get_set = str(cmd[1]).lower() get_set = str(cmd[1]).lower()
@@ -1350,7 +1282,9 @@ class Defender():
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}BAN ON ALL CHANS{self.Config.CONFIG_COLOR['noire']} ] : Already activated") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}BAN ON ALL CHANS{self.Config.CONFIG_COLOR['noire']} ] : Already activated")
return False return False
self.update_db_configuration(key, 1) # self.update_db_configuration(key, 1)
self.__update_configuration(key, 1)
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}BAN ON ALL CHANS{self.Config.CONFIG_COLOR["noire"]} ] : Activated by {fromuser}') self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}BAN ON ALL CHANS{self.Config.CONFIG_COLOR["noire"]} ] : Activated by {fromuser}')
elif get_value == 'off': elif get_value == 'off':
@@ -1358,13 +1292,17 @@ class Defender():
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}BAN ON ALL CHANS{self.Config.CONFIG_COLOR['noire']} ] : Already deactivated") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}BAN ON ALL CHANS{self.Config.CONFIG_COLOR['noire']} ] : Already deactivated")
return False return False
self.update_db_configuration(key, 0) # self.update_db_configuration(key, 0)
self.__update_configuration(key, 0)
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}BAN ON ALL CHANS{self.Config.CONFIG_COLOR["noire"]} ] : Deactivated by {fromuser}') self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}BAN ON ALL CHANS{self.Config.CONFIG_COLOR["noire"]} ] : Deactivated by {fromuser}')
case 'limit': case 'limit':
reputation_seuil = int(cmd[3]) reputation_seuil = int(cmd[3])
key = 'reputation_seuil' key = 'reputation_seuil'
self.update_db_configuration(key, reputation_seuil)
# self.update_db_configuration(key, reputation_seuil)
self.__update_configuration(key, reputation_seuil)
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}REPUTATION SEUIL{self.Config.CONFIG_COLOR["noire"]} ] : Limit set to {str(reputation_seuil)} by {fromuser}') self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}REPUTATION SEUIL{self.Config.CONFIG_COLOR["noire"]} ] : Limit set to {str(reputation_seuil)} by {fromuser}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Reputation set to {reputation_seuil}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Reputation set to {reputation_seuil}')
@@ -1372,21 +1310,24 @@ class Defender():
case 'timer': case 'timer':
reputation_timer = int(cmd[3]) reputation_timer = int(cmd[3])
key = 'reputation_timer' key = 'reputation_timer'
self.update_db_configuration(key, reputation_timer) self.__update_configuration(key, reputation_timer)
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}REPUTATION TIMER{self.Config.CONFIG_COLOR["noire"]} ] : Timer set to {str(reputation_timer)} minute(s) by {fromuser}') self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}REPUTATION TIMER{self.Config.CONFIG_COLOR["noire"]} ] : Timer set to {str(reputation_timer)} minute(s) by {fromuser}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Reputation set to {reputation_timer}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Reputation set to {reputation_timer}')
case 'score_after_release': case 'score_after_release':
reputation_score_after_release = int(cmd[3]) reputation_score_after_release = int(cmd[3])
key = 'reputation_score_after_release' key = 'reputation_score_after_release'
self.update_db_configuration(key, reputation_score_after_release) self.__update_configuration(key, reputation_score_after_release)
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}REPUTATION SCORE AFTER RELEASE{self.Config.CONFIG_COLOR["noire"]} ] : Reputation score after release set to {str(reputation_score_after_release)} by {fromuser}') self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}REPUTATION SCORE AFTER RELEASE{self.Config.CONFIG_COLOR["noire"]} ] : Reputation score after release set to {str(reputation_score_after_release)} by {fromuser}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Reputation score after release set to {reputation_score_after_release}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Reputation score after release set to {reputation_score_after_release}')
case 'security_group': case 'security_group':
reputation_sg = int(cmd[3]) reputation_sg = int(cmd[3])
key = 'reputation_sg' key = 'reputation_sg'
self.update_db_configuration(key, reputation_sg) self.__update_configuration(key, reputation_sg)
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}REPUTATION SECURITY-GROUP{self.Config.CONFIG_COLOR["noire"]} ] : Reputation Security-group set to {str(reputation_sg)} by {fromuser}') self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}REPUTATION SECURITY-GROUP{self.Config.CONFIG_COLOR["noire"]} ] : Reputation Security-group set to {str(reputation_sg)} by {fromuser}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Reputation score after release set to {reputation_sg}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Reputation score after release set to {reputation_sg}')
@@ -1408,7 +1349,7 @@ class Defender():
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set action [kill|None]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set action [kill|None]')
except ValueError as ve: except ValueError as ve:
self.Logs.warning(f'{ie}') self.Logs.warning(f'{ve}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : La valeur devrait etre un entier >= 0') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : La valeur devrait etre un entier >= 0')
case 'proxy_scan': case 'proxy_scan':
@@ -1440,13 +1381,17 @@ class Defender():
if self.ModConfig.local_scan == 1: if self.ModConfig.local_scan == 1:
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated")
return None return None
self.update_db_configuration(option, 1)
self.__update_configuration(option, 1)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}")
elif action == 'off': elif action == 'off':
if self.ModConfig.local_scan == 0: if self.ModConfig.local_scan == 0:
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated")
return None return None
self.update_db_configuration(option, 0)
self.__update_configuration(option, 0)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}")
case 'psutil_scan': case 'psutil_scan':
@@ -1454,13 +1399,17 @@ class Defender():
if self.ModConfig.psutil_scan == 1: if self.ModConfig.psutil_scan == 1:
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated")
return None return None
self.update_db_configuration(option, 1)
self.__update_configuration(option, 1)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}")
elif action == 'off': elif action == 'off':
if self.ModConfig.psutil_scan == 0: if self.ModConfig.psutil_scan == 0:
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated")
return None return None
self.update_db_configuration(option, 0)
self.__update_configuration(option, 0)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}")
case 'abuseipdb_scan': case 'abuseipdb_scan':
@@ -1468,13 +1417,17 @@ class Defender():
if self.ModConfig.abuseipdb_scan == 1: if self.ModConfig.abuseipdb_scan == 1:
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated")
return None return None
self.update_db_configuration(option, 1)
self.__update_configuration(option, 1)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}")
elif action == 'off': elif action == 'off':
if self.ModConfig.abuseipdb_scan == 0: if self.ModConfig.abuseipdb_scan == 0:
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated")
return None return None
self.update_db_configuration(option, 0)
self.__update_configuration(option, 0)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}")
case 'freeipapi_scan': case 'freeipapi_scan':
@@ -1482,7 +1435,8 @@ class Defender():
if self.ModConfig.freeipapi_scan == 1: if self.ModConfig.freeipapi_scan == 1:
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated")
return None return None
self.update_db_configuration(option, 1)
self.__update_configuration(option, 1)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}")
elif action == 'off': elif action == 'off':
@@ -1490,7 +1444,7 @@ class Defender():
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated")
return None return None
self.update_db_configuration(option, 0) self.__update_configuration(option, 0)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}")
@@ -1499,13 +1453,17 @@ class Defender():
if self.ModConfig.cloudfilt_scan == 1: if self.ModConfig.cloudfilt_scan == 1:
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated")
return None return None
self.update_db_configuration(option, 1)
self.__update_configuration(option, 1)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}")
elif action == 'off': elif action == 'off':
if self.ModConfig.cloudfilt_scan == 0: if self.ModConfig.cloudfilt_scan == 0:
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated")
return None return None
self.update_db_configuration(option, 0)
self.__update_configuration(option, 0)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}")
case _: case _:
@@ -1537,7 +1495,8 @@ class Defender():
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Already activated") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Already activated")
return False return False
self.update_db_configuration(key, 1) self.__update_configuration(key, 1)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Activated by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Activated by {fromuser}")
if activation == 'off': if activation == 'off':
@@ -1545,7 +1504,8 @@ class Defender():
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Already Deactivated") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Already Deactivated")
return False return False
self.update_db_configuration(key, 0) self.__update_configuration(key, 0)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Deactivated by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Deactivated by {fromuser}")
if len_cmd == 4: if len_cmd == 4:
@@ -1556,20 +1516,22 @@ class Defender():
case 'flood_message': case 'flood_message':
key = 'flood_message' key = 'flood_message'
set_value = int(cmd[3]) set_value = int(cmd[3])
print(f"{str(set_value)} - {set_key}") self.__update_configuration(key, set_value)
self.update_db_configuration(key, set_value)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Flood message set to {set_value} by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Flood message set to {set_value} by {fromuser}")
case 'flood_time': case 'flood_time':
key = 'flood_time' key = 'flood_time'
set_value = int(cmd[3]) set_value = int(cmd[3])
self.update_db_configuration(key, set_value) self.__update_configuration(key, set_value)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Flood time set to {set_value} by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Flood time set to {set_value} by {fromuser}")
case 'flood_timer': case 'flood_timer':
key = 'flood_timer' key = 'flood_timer'
set_value = int(cmd[3]) set_value = int(cmd[3])
self.update_db_configuration(key, set_value) self.__update_configuration(key, set_value)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Flood timer set to {set_value} by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Flood timer set to {set_value} by {fromuser}")
case _: case _:
@@ -1602,293 +1564,6 @@ class Defender():
except KeyError as ke: except KeyError as ke:
self.Logs.error(f"Key Error : {ke}") self.Logs.error(f"Key Error : {ke}")
case 'join':
try:
channel = cmd[1]
self.Irc.send2socket(f':{service_id} JOIN {channel}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {dnickname} JOINED {channel}')
self.add_defender_channel(channel)
except IndexError as ie:
self.Logs.error(f'{ie}')
case 'part':
try:
channel = cmd[1]
if channel == dchanlog:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} : {dnickname} CAN'T LEFT {channel} AS IT IS LOG CHANNEL")
return False
self.Irc.send2socket(f':{service_id} PART {channel}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {dnickname} LEFT {channel}')
self.delete_defender_channel(channel)
except IndexError as ie:
self.Logs.error(f'{ie}')
case 'op':
# /mode #channel +o user
# .op #channel user
# /msg dnickname op #channel user
# [':adator', 'PRIVMSG', '#services', ':.o', '#services', 'dktmb']
try:
if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} op [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} +o {fromuser}")
return True
# deop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} +o {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +o {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} op [#SALON] [NICKNAME]')
case 'deop':
# /mode #channel -o user
# .deop #channel user
try:
if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deop [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} -o {fromuser}")
return True
# deop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} -o {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -o {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd DEOP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deop [#SALON] [NICKNAME]')
case 'owner':
# /mode #channel +q user
# .owner #channel user
try:
if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} owner [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} +q {fromuser}")
return True
# owner nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} +q {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +q {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd OWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} owner [#SALON] [NICKNAME]')
case 'deowner':
# /mode #channel -q user
# .deowner #channel user
try:
if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} -q {fromuser}")
return True
# deowner nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} -q {nickname}")
return True
channel = cmd[1]
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -q {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd DEOWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]')
case 'halfop':
# /mode #channel +h user
# .halfop #channel user
try:
if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} halfop [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} +h {fromuser}")
return True
# deop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} +h {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +h {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd halfop: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} halfop [#SALON] [NICKNAME]')
case 'dehalfop':
# /mode #channel -h user
# .dehalfop #channel user
try:
if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} -h {fromuser}")
return True
# dehalfop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} -h {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -h {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd DEHALFOP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]')
case 'voice':
# /mode #channel +v user
# .voice #channel user
try:
if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} voice [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} +v {fromuser}")
return True
# voice nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} +v {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +v {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd VOICE: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} voice [#SALON] [NICKNAME]')
case 'devoice':
# /mode #channel -v user
# .devoice #channel user
try:
if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} -v {fromuser}")
return True
# dehalfop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} -v {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -v {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd DEVOICE: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]')
case 'ban':
# .ban #channel nickname
try:
channel = cmd[1]
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +b {nickname}!*@*")
self.Logs.debug(f'{fromuser} has banned {nickname} from {channel}')
except IndexError as e:
self.Logs.warning(f'_hcmd BAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} ban [#SALON] [NICKNAME]')
case 'unban':
# .unban #channel nickname
try:
channel = cmd[1]
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -b {nickname}!*@*")
self.Logs.debug(f'{fromuser} has unbanned {nickname} from {channel}')
except IndexError as e:
self.Logs.warning(f'_hcmd UNBAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} unban [#SALON] [NICKNAME]')
case 'kick':
# .kick #channel nickname reason
try:
channel = cmd[1]
nickname = cmd[2]
reason = []
for i in range(3, len(cmd)):
reason.append(cmd[i])
final_reason = ' '.join(reason)
self.Irc.send2socket(f":{service_id} KICK {channel} {nickname} {final_reason}")
self.Logs.debug(f'{fromuser} has kicked {nickname} from {channel} : {final_reason}')
except IndexError as e:
self.Logs.warning(f'_hcmd KICK: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kick [#SALON] [NICKNAME] [REASON]')
case 'kickban':
# .kickban #channel nickname reason
try:
channel = cmd[1]
nickname = cmd[2]
reason = []
for i in range(3, len(cmd)):
reason.append(cmd[i])
final_reason = ' '.join(reason)
self.Irc.send2socket(f":{service_id} KICK {channel} {nickname} {final_reason}")
self.Irc.send2socket(f":{service_id} MODE {channel} +b {nickname}!*@*")
self.Logs.debug(f'{fromuser} has kicked and banned {nickname} from {channel} : {final_reason}')
except IndexError as e:
self.Logs.warning(f'_hcmd KICKBAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kickban [#SALON] [NICKNAME] [REASON]')
case 'info': case 'info':
try: try:
nickoruid = cmd[1] nickoruid = cmd[1]

View File

@@ -1,53 +1,64 @@
from dataclasses import dataclass, fields
from core.irc import Irc from core.irc import Irc
# Le module crée devra réspecter quelques conditions
# 1. Importer le module de configuration
# 2. Le nom de class devra toujours s'appeler comme le module exemple => nom de class Dktmb | nom du module mod_dktmb
# 3. la fonction __init__ devra toujours avoir les parametres suivant (self, irc:object)
# 1 . Créer la variable Irc dans le module
# 2 . Récuperer la configuration dans une variable
# 3 . Définir et enregistrer les nouvelles commandes
# 4. une fonction _hcmds(self, user:str, cmd: list) devra toujours etre crée.
class Test(): class Test():
@dataclass
class ModConfModel:
"""The Model containing the module parameters
"""
param_exemple1: str
param_exemple2: int
def __init__(self, ircInstance:Irc) -> None: def __init__(self, ircInstance:Irc) -> None:
# Add Irc Object to the module # Module name (Mandatory)
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
# Add Irc Object to the module (Mandatory)
self.Irc = ircInstance self.Irc = ircInstance
# Add Global Configuration to the module # Add Global Configuration to the module (Mandatory)
self.Config = ircInstance.Config self.Config = ircInstance.Config
# Add Base object to the module # Add Base object to the module (Mandatory)
self.Base = ircInstance.Base self.Base = ircInstance.Base
# Add logs object to the module # Add logs object to the module (Mandatory)
self.Logs = ircInstance.Base.logs self.Logs = ircInstance.Base.logs
# Add User object to the module # Add User object to the module (Mandatory)
self.User = ircInstance.User self.User = ircInstance.User
# Add Channel object to the module # Add Channel object to the module (Mandatory)
self.Channel = ircInstance.Channel self.Channel = ircInstance.Channel
# Créer les nouvelles commandes du module # Create module commands (Mandatory)
self.commands_level = { self.commands_level = {
0: ['test'], 0: ['test-command'],
1: ['test_level_1'] 1: ['test_level_1'],
2: ['test_level_2'],
3: ['test_level_3']
} }
# Init the module # Init the module
self.__init_module() self.__init_module()
# Log the module # Log the module
self.Logs.debug(f'Module {self.__class__.__name__} loaded ...') self.Logs.debug(f'Module {self.module_name} loaded ...')
def __init_module(self) -> None: def __init_module(self) -> None:
# Insert module commands into the core one (Mandatory)
self.__set_commands(self.commands_level) self.__set_commands(self.commands_level)
# Create you own tables (Mandatory)
self.__create_tables() self.__create_tables()
# Load module configuration and sync with core one (Mandatory)
self.__load_module_configuration()
# End of mandatory methods you can start your customization #
return None return None
def __set_commands(self, commands:dict[int, list[str]]) -> None: def __set_commands(self, commands:dict[int, list[str]]) -> None:
@@ -84,25 +95,61 @@ class Test():
self.Base.db_execute_query(table_logs) self.Base.db_execute_query(table_logs)
return None return None
def __load_module_configuration(self) -> None:
"""### Load Module Configuration
"""
try:
# Build the default configuration model (Mandatory)
self.ModConfig = self.ModConfModel(param_exemple1='param value 1', param_exemple2=1)
# Sync the configuration with core configuration (Mandatory)
self.Base.db_sync_core_config(self.module_name, self.ModConfig)
return None
except TypeError as te:
self.Logs.critical(te)
def __update_configuration(self, param_key: str, param_value: str):
"""Update the local and core configuration
Args:
param_key (str): The parameter key
param_value (str): The parameter value
"""
self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
def unload(self) -> None: def unload(self) -> None:
return None return None
def cmd(self, data:list) -> None: def cmd(self, data:list) -> None:
return None return None
def _hcmds(self, user:str, cmd: list, fullcmd: list = []) -> None: def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower() command = str(cmd[0]).lower()
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME
fromuser = user fromuser = user
fromchannel = str(channel) if not channel is None else None
match command: match command:
case 'test': case 'test-command':
try: try:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} : test command ready ...") self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} : This is a notice to the sender ...")
self.Irc.send2socket(f":{dnickname} PRIVMSG {fromuser} : This is private message to the sender ...")
if not fromchannel is None:
self.Irc.send2socket(f":{dnickname} PRIVMSG {fromchannel} : This is channel message to the sender ...")
# 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") self.Logs.debug(f"Test logs ready")
except KeyError as ke:
self.Logs.error(f"Key Error : {ke}") except Exception as err:
self.Logs.error(f"Unknown Error: {err}")

View File

@@ -24,6 +24,10 @@ class Votekick():
VOTE_CHANNEL_DB:list[VoteChannelModel] = [] VOTE_CHANNEL_DB:list[VoteChannelModel] = []
def __init__(self, ircInstance:Irc) -> None: def __init__(self, ircInstance:Irc) -> None:
# Module name (Mandatory)
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 = ircInstance
@@ -44,18 +48,20 @@ class Votekick():
# Créer les nouvelles commandes du module # Créer les nouvelles commandes du module
self.commands_level = { self.commands_level = {
0: ['vote_for', 'vote_against'], 0: ['vote']
1: ['activate', 'deactivate', 'submit', 'vote_stat', 'vote_verdict', 'vote_cancel']
} }
# Init the module # Init the module
self.__init_module() self.__init_module()
# Log the module # Log the module
self.Logs.debug(f'Module {self.__class__.__name__} loaded ...') self.Logs.debug(f'Module {self.module_name} loaded ...')
def __init_module(self) -> None: def __init_module(self) -> None:
# Add admin object to retrieve admin users
self.Admin = self.Irc.Admin
self.__set_commands(self.commands_level) self.__set_commands(self.commands_level)
self.__create_tables() self.__create_tables()
self.join_saved_channels() self.join_saved_channels()
@@ -143,7 +149,7 @@ class Votekick():
if not found: if not found:
self.VOTE_CHANNEL_DB.append(ChannelObject) self.VOTE_CHANNEL_DB.append(ChannelObject)
self.Logs.debug(f"The channel has been added {ChannelObject}") self.Logs.debug(f"The channel has been added {ChannelObject}")
self.db_add_vote_channel(ChannelObject.channel_name) # self.db_add_vote_channel(ChannelObject.channel_name)
return result return result
@@ -188,7 +194,9 @@ class Votekick():
def join_saved_channels(self) -> None: def join_saved_channels(self) -> None:
result = self.Base.db_execute_query("SELECT id, channel FROM votekick_channel") 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() channels = result.fetchall()
unixtime = self.Base.get_unixtime() unixtime = self.Base.get_unixtime()
@@ -214,15 +222,18 @@ class Votekick():
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME
if not self.is_vote_ongoing(channel):
return None
for chan in self.VOTE_CHANNEL_DB: for chan in self.VOTE_CHANNEL_DB:
if chan.channel_name == channel: if chan.channel_name == channel:
target_user = self.User.get_nickname(chan.target_user) target_user = self.User.get_nickname(chan.target_user)
if chan.vote_for > chan.vote_against: if chan.vote_for > chan.vote_against:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :The user {self.Config.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["nogc"]} will be kicked from this channel') self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["nogc"]} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll be kicked from the channel')
self.Irc.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}") self.Irc.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)) self.Channel.delete_user_from_channel(channel, self.User.get_uid(target_user))
elif chan.vote_for <= chan.vote_against: elif chan.vote_for <= chan.vote_against:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This user will stay on this channel') self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["nogc"]} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel')
# Init the system # Init the system
if self.init_vote_system(channel): if self.init_vote_system(channel):
@@ -241,201 +252,244 @@ class Votekick():
return None return None
def _hcmds(self, user:str, cmd: list, fullcmd: list = []) -> None: def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> 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()
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME
fromuser = user fromuser = user
fromchannel = channel
if len(fullcmd) >= 3:
fromchannel = str(fullcmd[2]).lower() if self.Base.Is_Channel(str(fullcmd[2]).lower()) else None
else:
fromchannel = None
if len(cmd) >= 2:
sentchannel = str(cmd[1]).lower() if self.Base.Is_Channel(str(cmd[1]).lower()) else None
else:
sentchannel = None
if not fromchannel is None:
channel = fromchannel
elif not sentchannel is None:
channel = sentchannel
else:
channel = None
match command: match command:
case 'vote':
option = str(cmd[1]).lower()
case 'vote_cancel': if len(command) == 1:
try: self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote activate #channel')
if channel is None: self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote deactivate #channel')
self.Logs.error(f"The channel is not known, defender can't cancel the vote") self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote +')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :You need to specify the channel => /msg {dnickname} vote_cancel #channel') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote -')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote cancel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote status')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote submit nickname')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote verdict')
for vote in self.VOTE_CHANNEL_DB: match option:
if vote.channel_name == channel:
self.init_vote_system(channel)
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote system re-initiated')
except IndexError as ke: case 'activate':
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote_cancel #channel') try:
self.Logs.error(f'Index Error: {ke}') # vote activate #channel
if self.Admin.get_Admin(fromuser) is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
return None
case 'vote_for': sentchannel = str(cmd[2]).lower() if self.Base.Is_Channel(str(cmd[2]).lower()) else None
try: if sentchannel is None:
# vote_for self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The correct command is {self.Config.SERVICE_PREFIX}{command} {option} #CHANNEL")
channel = str(fullcmd[2]).lower()
for chan in self.VOTE_CHANNEL_DB:
if chan.channel_name == channel:
if fromuser in chan.voter_users:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :You already submitted a vote')
else:
chan.vote_for += 1
chan.voter_users.append(fromuser)
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote recorded, thank you')
except KeyError as ke: self.insert_vote_channel(
self.Logs.error(f'Key Error: {ke}') self.VoteChannelModel(
except IndexError as ie: channel_name=sentchannel,
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote_cancel #channel') target_user='',
self.Logs.error(f'Index Error: {ie}') voter_users=[],
vote_for=0,
vote_against=0
)
)
case 'vote_against': self.Base.db_query_channel('add', self.module_name, sentchannel)
try:
# vote_against
channel = str(fullcmd[2]).lower()
for chan in self.VOTE_CHANNEL_DB:
if chan.channel_name == channel:
if fromuser in chan.voter_users:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :You already submitted a vote')
else:
chan.vote_against += 1
chan.voter_users.append(fromuser)
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote recorded, thank you')
except KeyError as ke: self.Irc.send2socket(f":{dnickname} JOIN {sentchannel}")
self.Logs.error(f'Key Error: {ke}') self.Irc.send2socket(f":{dnickname} SAMODE {sentchannel} +o {dnickname}")
self.Irc.send2socket(f":{dnickname} PRIVMSG {sentchannel} :You can now use !submit <nickname> to decide if he will stay or not on this channel ")
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option} #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option} #welcome')
case 'vote_stat': case 'deactivate':
try: try:
# channel = str(fullcmd[2]).lower() # vote deactivate #channel
for chan in self.VOTE_CHANNEL_DB: if self.Admin.get_Admin(fromuser) is None:
if chan.channel_name == channel: self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :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))}') return None
except KeyError as ke: sentchannel = str(cmd[2]).lower() if self.Base.Is_Channel(str(cmd[2]).lower()) else None
self.Logs.error(f'Key Error: {ke}') if sentchannel is None:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The correct command is {self.Config.SERVICE_PREFIX}{command} {option} #CHANNEL")
case 'vote_verdict': self.Irc.send2socket(f":{dnickname} SAMODE {sentchannel} -o {dnickname}")
try: self.Irc.send2socket(f":{dnickname} PART {sentchannel}")
# channel = str(fullcmd[2]).lower()
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.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :The user {self.Config.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["nogc"]} will be kicked from this channel')
self.Irc.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}")
elif chan.vote_for <= chan.vote_against:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This user will stay on this channel')
# Init the system for chan in self.VOTE_CHANNEL_DB:
if self.init_vote_system(channel): if chan.channel_name == sentchannel:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :System vote re initiated') self.VOTE_CHANNEL_DB.remove(chan)
self.Base.db_query_channel('del', self.module_name, chan.channel_name)
except KeyError as ke: self.Logs.debug(f"The Channel {sentchannel} has been deactivated from the vote system")
self.Logs.error(f'Key Error: {ke}') except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option} #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option} #welcome')
case 'submit': case '+':
# submit nickname try:
try: # vote +
nickname_submitted = cmd[1] channel = fromchannel
# channel = str(fullcmd[2]).lower() for chan in self.VOTE_CHANNEL_DB:
uid_submitted = self.User.get_uid(nickname_submitted) if chan.channel_name == channel:
user_submitted = self.User.get_User(nickname_submitted) if fromuser in chan.voter_users:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :You already submitted a vote')
else:
chan.vote_for += 1
chan.voter_users.append(fromuser)
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote recorded, thank you')
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option}')
# check if there is an ongoing vote case '-':
if self.is_vote_ongoing(channel): try:
for vote in self.VOTE_CHANNEL_DB: # vote -
if vote.channel_name == channel: channel = fromchannel
ongoing_user = self.User.get_nickname(vote.target_user) for chan in self.VOTE_CHANNEL_DB:
if chan.channel_name == channel:
if fromuser in chan.voter_users:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :You already submitted a vote')
else:
chan.vote_against += 1
chan.voter_users.append(fromuser)
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote recorded, thank you')
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option}')
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :There is an ongoing vote on {ongoing_user}') case 'cancel':
return False try:
# vote cancel
if self.Admin.get_Admin(fromuser) is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
return None
# check if the user exist if channel is None:
if user_submitted is None: self.Logs.error(f"The channel is not known, defender can't cancel the vote")
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This nickname <{nickname_submitted}> do not exist') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :You need to specify the channel => /msg {dnickname} vote_cancel #channel')
return False
uid_cleaned = self.Base.clean_uid(uid_submitted) for vote in self.VOTE_CHANNEL_DB:
ChannelInfo = self.Channel.get_Channel(channel) if vote.channel_name == channel:
self.init_vote_system(channel)
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote system re-initiated')
clean_uids_in_channel: list = [] except Exception as err:
for uid in ChannelInfo.uids: self.Logs.error(f'{err}')
clean_uids_in_channel.append(self.Base.clean_uid(uid)) self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option}')
if not uid_cleaned in clean_uids_in_channel: case 'status':
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This nickname <{nickname_submitted}> is not available in this channel') try:
return False # vote status
for chan in self.VOTE_CHANNEL_DB:
if chan.channel_name == channel:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :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))}')
# check if Ircop or Service or Bot except Exception as err:
pattern = fr'[o|B|S]' self.Logs.error(f'{err}')
operator_user = re.findall(pattern, user_submitted.umodes) self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}')
if operator_user: self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option}')
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :You cant vote for this user ! he/she is protected')
return False
for chan in self.VOTE_CHANNEL_DB: case 'submit':
if chan.channel_name == channel: try:
chan.target_user = self.User.get_uid(nickname_submitted) # vote submit nickname
if self.Admin.get_Admin(fromuser) is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
return None
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :{nickname_submitted} has been targeted for a vote') nickname_submitted = cmd[2]
uid_submitted = self.User.get_uid(nickname_submitted)
user_submitted = self.User.get_User(nickname_submitted)
self.Base.create_timer(60, self.timer_vote_verdict, (channel, )) # check if there is an ongoing vote
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This vote will end after 60 secondes') if self.is_vote_ongoing(channel):
for vote in self.VOTE_CHANNEL_DB:
if vote.channel_name == channel:
ongoing_user = self.User.get_nickname(vote.target_user)
except KeyError as ke: self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :There is an ongoing vote on {ongoing_user}')
self.Logs.error(f'Key Error: {ke}') return False
except TypeError as te:
self.Logs.error(te)
case 'activate': # check if the user exist
try: if user_submitted is None:
# activate #channel self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This nickname <{nickname_submitted}> do not exist')
# channel = str(cmd[1]).lower() return False
self.insert_vote_channel( uid_cleaned = self.Base.clean_uid(uid_submitted)
self.VoteChannelModel( ChannelInfo = self.Channel.get_Channel(channel)
channel_name=channel, if ChannelInfo is None:
target_user='', self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :This channel [{channel}] do not exist in the Channel Object')
voter_users=[], return False
vote_for=0,
vote_against=0
)
)
self.Irc.send2socket(f":{dnickname} JOIN {channel}") clean_uids_in_channel: list = []
self.Irc.send2socket(f":{dnickname} SAMODE {channel} +o {dnickname}") for uid in ChannelInfo.uids:
self.Irc.send2socket(f":{dnickname} PRIVMSG {channel} :You can now use !submit <nickname> to decide if he will stay or not on this channel ") clean_uids_in_channel.append(self.Base.clean_uid(uid))
except KeyError as ke: if not uid_cleaned in clean_uids_in_channel:
self.Logs.error(f"Key Error : {ke}") self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This nickname <{nickname_submitted}> is not available in this channel')
return False
case 'deactivate': # check if Ircop or Service or Bot
try: pattern = fr'[o|B|S]'
# deactivate #channel operator_user = re.findall(pattern, user_submitted.umodes)
# channel = str(cmd[1]).lower() if operator_user:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :You cant vote for this user ! he/she is protected')
return False
self.Irc.send2socket(f":{dnickname} SAMODE {channel} -o {dnickname}") for chan in self.VOTE_CHANNEL_DB:
self.Irc.send2socket(f":{dnickname} PART {channel}") if chan.channel_name == channel:
chan.target_user = self.User.get_uid(nickname_submitted)
for chan in self.VOTE_CHANNEL_DB: self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :{nickname_submitted} has been targeted for a vote')
if chan.channel_name == channel:
self.VOTE_CHANNEL_DB.remove(chan)
self.db_delete_vote_channel(chan.channel_name)
self.Logs.debug(f"Test logs ready") self.Base.create_timer(60, self.timer_vote_verdict, (channel, ))
except KeyError as ke: self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This vote will end after 60 secondes')
self.Logs.error(f"Key Error : {ke}")
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option} nickname')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option} adator')
case 'verdict':
try:
# vote verdict
if self.Admin.get_Admin(fromuser) is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
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.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["nogc"]} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll be kicked from the channel')
self.Irc.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}")
elif chan.vote_for <= chan.vote_against:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["nogc"]} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel')
# Init the system
if self.init_vote_system(channel):
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :System vote re initiated')
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option}')
case _:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote activate #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote deactivate #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote +')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote -')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote cancel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote status')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote submit nickname')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote verdict')

View File

@@ -1,3 +1,3 @@
{ {
"version": "5.0.7" "version": "5.1.6"
} }