40 Commits

Author SHA1 Message Date
adator
e14c97de03 Merge pull request #44 from adator85/dev
V5.3.0
2024-09-22 16:20:45 +02:00
adator
ff603ab2a4 V5.3.0 2024-09-22 16:20:02 +02:00
adator
69360be3ad Merge pull request #43 from adator85/dev
Update info command
2024-09-21 20:28:29 +02:00
adator
d7503768b6 Update info command 2024-09-21 20:28:02 +02:00
adator
bfa90c6bd5 Merge pull request #42 from adator85/dev
V5.2.9
2024-09-21 20:22:42 +02:00
adator
b5503d23d7 V5.2.9 2024-09-21 20:22:09 +02:00
adator
5c8378a0e7 Merge pull request #41 from adator85/dev
V5.2.9
2024-09-21 16:43:14 +02:00
adator
7be3f51bf4 V5.2.9 2024-09-21 16:28:50 +02:00
adator
e3b212ea88 Merge pull request #40 from adator85/dev
finetune clone connection
2024-09-20 23:13:33 +02:00
adator
2c0510b2a3 finetune clone connection 2024-09-20 23:12:57 +02:00
adator
0c2a350d38 Merge pull request #39 from adator85/dev
Dev
2024-09-20 21:12:59 +02:00
adator
ee039322d4 V5.2.7 2024-09-19 21:06:07 +02:00
adator
8f08a1e77f update readme.md 2024-09-18 20:29:33 +02:00
adator
c59dd16e87 V5.2.6 2024-09-18 20:21:44 +02:00
adator
0f31e67be6 check python version fix 2024-09-18 19:10:15 +02:00
adator
3cd2077f63 update installation module 2024-09-18 19:08:08 +02:00
adator
9c78ad0860 Same version, copyright changed 2024-09-17 01:46:09 +02:00
adator
487f9a2762 V5.2.5 2024-09-17 01:43:24 +02:00
adator
a7de16f7ad V5.2.4 multiple changes related to clones 2024-09-17 00:14:53 +02:00
adator
c1c0b480ce V5.2.3 If missing MOTD 2024-09-16 21:14:04 +02:00
adator
66ea492593 removing old code 2024-09-16 00:57:27 +02:00
adator
d459fd662f Changing hostname modification 2024-09-16 00:41:43 +02:00
adator
5d3a2b0e64 Changing Proxy_scan to PSUTIL_scan 2024-09-15 23:32:44 +02:00
adator
2f681db2d7 Adding Geoip to the UserModel 2024-09-15 23:29:32 +02:00
adator
7585db4f62 V5.2.2 2024-09-15 22:39:34 +02:00
adator
1cea8d0601 Merge pull request #38 from adator85/dev
V5.2.1
2024-09-15 03:09:22 +02:00
adator
1984511db8 V5.2.1 2024-09-15 03:08:49 +02:00
adator
652b400d5e Merge pull request #37 from adator85/dev
update mode clone
2024-09-15 02:50:27 +02:00
adator
ce47739a93 update mode clone 2024-09-15 02:49:42 +02:00
adator
2f8b965b59 Merge pull request #36 from adator85/dev
Dev
2024-09-15 02:04:32 +02:00
adator
c7047ec3d6 V5.2.0 2024-09-15 02:03:38 +02:00
adator
eddba81cf0 remove mod_jsonrpc.py 2024-09-14 01:32:16 +02:00
adator
59e634951f V5.1.9 2024-09-14 01:25:13 +02:00
adator
37684eaede With unrealircd json rpc 2024-09-13 23:51:13 +02:00
adator
3c043cefd8 Merge pull request #35 from adator85/dev
V5.1.8
2024-09-08 00:42:57 +02:00
adator
e6156fa301 V5.1.8 2024-09-08 00:42:18 +02:00
adator
59a75cecd8 Merge pull request #34 from adator85/dev
V5.1.7
2024-09-03 00:21:32 +02:00
adator
58e3ebd287 V5.1.7 2024-09-03 00:19:13 +02:00
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
15 changed files with 1245 additions and 586 deletions

3
.gitignore vendored
View File

@@ -2,6 +2,7 @@
db/ db/
logs/ logs/
__pycache__/ __pycache__/
mods/mod_jsonrpc.py
configuration.json configuration.json
install.log *.log
test.py test.py

206
README.md
View File

@@ -1,4 +1,9 @@
# IRC-DEFENDER # IRC-DEFENDER
![Static Badge](https://img.shields.io/badge/UnrealIRCd-6.2.2%20or%20later-green)
![Static Badge](https://img.shields.io/badge/Python3-3.10%20or%20later-green)
![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fadator85%2FIRC_DEFENDER_MODULES%2Fmain%2Fversion.json&query=version&label=Current%20Version)
![Static Badge](https://img.shields.io/badge/Maintained-Yes-green)
Defender est un service IRC basé sur la sécurité des réseaux IRC ( UnrealIRCD ) Defender est un service IRC basé sur la sécurité des réseaux IRC ( UnrealIRCD )
Il permet d'ajouter une sécurité supplémentaire pour vérifier les users connectés au réseau Il permet d'ajouter une sécurité supplémentaire pour vérifier les users connectés au réseau
en demandant aux user un code de validation. en demandant aux user un code de validation.
@@ -9,9 +14,9 @@ Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux u
Kick: Expulser un utilisateur du canal. Kick: Expulser un utilisateur du canal.
Ban: Interdire définitivement l'accès au canal. Ban: Interdire définitivement l'accès au canal.
Unban: Lever une interdiction. Unban: Lever une interdiction.
Op/Deop: Attribuer ou retirer les droits d'opérateur. Op/Deop/Opall/Deopall: Attribuer ou retirer les droits d'opérateur.
Halfop/Dehalfop: Attribuer ou retirer les droits Halfop/Dehalfop: Attribuer ou retirer les droits
Voice/Devoice: Attribuer ou retirer les droits de voix. Voice/Devoice/VoiceAll/DevoiceAll: Attribuer ou retirer les droits de voix.
Système de quarantaine: Système de quarantaine:
Mise en quarantaine: Isoler temporairement un utilisateur dans un canal privé. Mise en quarantaine: Isoler temporairement un utilisateur dans un canal privé.
@@ -25,6 +30,7 @@ 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é)
- Un server UnrealIRCD corréctement configuré
- Python version 3.10 ou supérieure - Python version 3.10 ou supérieure
Bash: Bash:
@@ -51,54 +57,170 @@ Pour Les prochains lancement de defender vous devez utiliser la commande suivant
$ sudo systemctl --user start defender $ sudo systemctl --user start defender
# Configuration # Configuration
```
SERVEUR (Serveur) SERVEUR (Serveur)
SERVEUR_IP: Adresse IP du serveur IRC à rejoindre. * SERVEUR_IP: Adresse IP du serveur IRC à rejoindre. (default : 127.0.0.1)
SERVEUR_HOSTNAME: Nom d'hôte du serveur IRC à rejoindre (optionnel). * SERVEUR_HOSTNAME: Nom d'hôte du serveur IRC à rejoindre (optionnel).
SERVEUR_LINK: Lien vers le serveur IRC (optionnel). * SERVEUR_LINK: Lien vers le serveur IRC (optionnel).
SERVEUR_PORT: Port de connexion au serveur IRC. * SERVEUR_PORT: Port de connexion au serveur IRC.
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. (default : 19Z)
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) (default : false).
SERVICE (Service)
SERVICE_NAME: Nom du service IRC.
SERVICE_NICKNAME: Surnom utilisé par le service sur le serveur IRC.
SERVICE_REALNAME: Nom réel du service affiché sur le serveur IRC.
SERVICE_USERNAME: Nom d'utilisateur utilisé par le service pour se connecter au serveur IRC.
SERVICE_HOST: Nom d'hôte du service affiché sur le serveur IRC (optionnel).
SERVICE_INFO: Description du service.
SERVICE_CHANLOG: Canal utilisé pour la journalisation des actions du service.
SERVICE_SMODES: Modes serveur 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_PREFIX: Caractère utilisé comme préfixe des commandes du service.
COMPTE (Compte)
OWNER: Nom d'utilisateur possédant les droits d'administration du service.
PASSWORD: Mot de passe de l'administrateur du service.
CANAUX (Canaux)
SALON_JAIL: Canal utilisé comme prison pour les utilisateurs sanctionnés.
SALON_JAIL_MODES: Modes appliqués au canal de prison.
SALON_LIBERER: Canal utilisé pour la libération des utilisateurs sanctionnés.
API (API)
API_TIMEOUT: Durée maximale d'attente d'une réponse de l'API en secondes.
SCANNER (Scanner)
PORTS_TO_SCAN: Liste des ports à scanner pour détecter des serveurs potentiellement malveillants.
SÉCURITÉ (Sécurité)
WHITELISTED_IP: Liste d'adresses IP autorisées à contourner certaines restrictions.
GLINE_DURATION: Durée de bannissement temporaire d'un utilisateur en minutes.
DEBUG (Debug)
DEBUG_LEVEL: Niveau de verbosité des messages de debug (plus grand est le nombre, plus il y a d'informations).
COULEURS (Couleurs)
CONFIG_COLOR: Dictionnaire contenant des codes de couleurs IRC pour un meilleur affichage des messages.
SERVICE (Service)
SERVICE_NAME: Nom du service IRC. (default : Defender)
SERVICE_NICKNAME: Surnom utilisé par le service sur le serveur IRC. (default : Defender)
SERVICE_REALNAME: Nom réel du service affiché sur le serveur IRC. (default : Defender Security)
SERVICE_USERNAME: Nom d'utilisateur utilisé par le service pour se connecter au serveur IRC. (default : IRCSecurity)
SERVICE_HOST: Nom d'hôte du service affiché sur le serveur IRC (optionnel). (default : defender.local.network)
SERVICE_INFO: Description du service. (default : Defender Network IRC Service)
SERVICE_CHANLOG: Canal utilisé pour la journalisation des actions du service. (default : #services)
SERVICE_SMODES: Modes serveur appliqués aux canaux rejoints par le service. (default : +ioqBS)
SERVICE_CMODES: Modes de canal appliqués aux canaux rejoints par le service. (default : ntsOP)
SERVICE_UMODES: Modes utilisateur appliqués au service. (default : o)
SERVICE_PREFIX: Caractère utilisé comme préfixe des commandes du service. (default : !)
COMPTE (Compte)
OWNER: Nom d'utilisateur possédant les droits d'administration du service. (default : admin)
PASSWORD: Mot de passe de l'administrateur du service. (default : admin)
CANAUX (Canaux)
SALON_JAIL: Canal utilisé comme prison pour les utilisateurs sanctionnés. (default : #jail)
SALON_JAIL_MODES: Modes appliqués au canal de prison. (default : sS)
SALON_LIBERER: Canal utilisé pour la libération des utilisateurs sanctionnés. (default : #welcome)
API (API)
API_TIMEOUT: Durée maximale d'attente d'une réponse de l'API en secondes. (default : 2)
SCANNER (Scanner)
PORTS_TO_SCAN: Liste des ports à scanner pour détecter des serveurs potentiellement malveillants. (default : [])
SÉCURITÉ (Sécurité)
WHITELISTED_IP: Liste d'adresses IP autorisées à contourner certaines restrictions. (default : ['127.0.0.1'])
GLINE_DURATION: Durée de bannissement temporaire d'un utilisateur en minutes. (default : "30")
DEBUG (Debug)
DEBUG_LEVEL: Niveau de verbosité des messages de debug (plus grand est le nombre, plus il y a d'informations). (default : 20) Pour une production
```
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 ## Exemple de configuration de base
```json
{
"SERVEUR_IP": "IP.DE.TON.SERVER",
"SERVEUR_HOSTNAME": "HOST.DE.TON.SERVER",
"SERVEUR_LINK": "LINK.DE.TON.SERVER",
"SERVEUR_PORT": 6901,
"SERVEUR_PASSWORD": "MOT_DE_PASS_DE_TON_LINK",
"SERVEUR_ID": "10Z",
"SERVEUR_SSL": true,
"SERVICE_NAME": "defender",
"SERVICE_NICKNAME": "PyDefender",
"SERVICE_REALNAME": "Python Defender Security",
"SERVICE_USERNAME": "PyDefender",
"SERVICE_HOST": "HOST.DE.TON.DEFENDER",
"OWNER": "TON_NICK_NAME",
"PASSWORD": "TON_PASSWORD"
}
```
## Exemple complet de configuration
```json
{
"SERVEUR_IP": "YOUR.SERVER.IP",
"SERVEUR_HOSTNAME": "YOUR.SERVER.HOST",
"SERVEUR_LINK": "LINK.DE.TON.SERVER",
"SERVEUR_PORT": 6901,
"SERVEUR_PASSWORD": "YOUR_LINK_PASSWORD",
"SERVEUR_ID": "10Z",
"SERVEUR_SSL": true,
"SERVICE_NAME": "defender",
"SERVICE_NICKNAME": "PyDefender",
"SERVICE_REALNAME": "Python Defender Security",
"SERVICE_USERNAME": "PyDefender",
"SERVICE_HOST": "HOST.DE.TON.DEFENDER",
"SERVICE_INFO": "Network IRC Service",
"SERVICE_CHANLOG": "#services",
"SERVICE_SMODES": "+ioqBS",
"SERVICE_CMODES": "ntsOP",
"SERVICE_UMODES": "o",
"SERVICE_PREFIX": "!",
"OWNER": "TON_NICK_NAME",
"PASSWORD": "TON_PASSWORD",
"SALON_JAIL": "#jail",
"SALON_JAIL_MODES": "sS",
"SALON_LIBERER": "#welcome",
"CLONE_CHANNEL": "#clones",
"CLONE_CMODES": "+nts",
"CLONE_LOG_HOST_EXEMPT": ["HOST.TO.SKIP"],
"CLONE_CHANNEL_PASSWORD": "YOUR_CHANNEL_PASSWORD",
"API_TIMEOUT": 2,
"PORTS_TO_SCAN": [3028, 8080, 1080, 1085, 4145, 9050],
"WHITELISTED_IP": ["127.0.0.1"],
"GLINE_DURATION": "30",
"DEBUG_LEVEL": 20
}
```
# \\!/ 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é. 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. Ne partagez pas vos informations de connexion au serveur IRC avec des tiers.
a votre premiere connexion vous devez tapez
```
/msg [NomDuService] auth [nickname] [password]
-- Une fois identifié tapez la commande suivante
/msg [NomDuService] editaccess [nickname] [Nouveau-Password] 5
```
# Unrealircd configuration
```
listen {
ip *;
port 6901;
options { tls; serversonly; }
}
link LINK.DE.TON.SERVER
{
incoming {
mask *;
bind-ip *;
port 6901;
//options { tls; };
}
outgoing {
bind-ip *; /* ou une IP précise */
hostname LINK.DE.TON.SERVER;
port 6901;
//options { tls; }
}
password "YOUR_LINK_PASSWORD";
class servers;
}
ulines {
LINK.DE.TON.SERVER;
}
```
# 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).

View File

@@ -10,12 +10,15 @@ class User:
uid: str uid: str
nickname: str nickname: str
username: str username: str
realname: str
hostname: str hostname: str
umodes: str umodes: str
vhost: str vhost: str
isWebirc: bool isWebirc: bool
isWebsocket: bool
remote_ip: str remote_ip: str
score_connexion: int score_connexion: int
geoip: str = None
connexion_datetime: datetime = field(default=datetime.now()) connexion_datetime: datetime = field(default=datetime.now())
UID_DB: list[UserModel] = [] UID_DB: list[UserModel] = []
@@ -410,6 +413,11 @@ class Clones:
alive: bool alive: bool
nickname: str nickname: str
username: str username: str
realname: str
channels: list
vhost: str = None
init: bool = True
connected: bool = False
UID_CLONE_DB: list[CloneModel] = [] UID_CLONE_DB: list[CloneModel] = []

View File

@@ -1,7 +1,7 @@
import time, threading, os, random, socket, hashlib, ipaddress, logging, requests, json, re, ast import time, threading, os, random, socket, hashlib, ipaddress, logging, requests, json, re, ast
from dataclasses import fields from dataclasses import fields
from typing import Union, Literal from typing import Union, Literal
from base64 import b64decode from base64 import b64decode, b64encode
from datetime import datetime from datetime import datetime
from sqlalchemy import create_engine, Engine, Connection, CursorResult from sqlalchemy import create_engine, Engine, Connection, CursorResult
from sqlalchemy.sql import text from sqlalchemy.sql import text
@@ -9,10 +9,6 @@ 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
@@ -30,8 +26,6 @@ class Base:
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
self.db_create_first_admin() # Créer un nouvel admin si la base de données est vide
def __set_current_defender_version(self) -> None: def __set_current_defender_version(self) -> None:
"""This will put the current version of Defender """This will put the current version of Defender
located in version.json located in version.json
@@ -222,7 +216,7 @@ class Base:
"""Supprime les modules de la base de données """Supprime les modules de la base de données
Args: Args:
cmd (str): le module a enregistrer cmd (str): le module a supprimer
""" """
insert_cmd_query = f"DELETE FROM {self.Config.table_module} WHERE module_name = :module_name" insert_cmd_query = f"DELETE FROM {self.Config.table_module} WHERE module_name = :module_name"
mes_donnees = {'module_name': module_name} mes_donnees = {'module_name': module_name}
@@ -311,7 +305,7 @@ class Base:
def db_update_core_config(self, module_name:str, dataclassObj: object, param_key:str, param_value: str) -> bool: def db_update_core_config(self, module_name:str, dataclassObj: object, param_key:str, param_value: str) -> bool:
core_table = 'core_config' core_table = self.Config.table_config
# Check if the param exist # Check if the param exist
if not hasattr(dataclassObj, param_key): if not hasattr(dataclassObj, param_key):
self.logs.error(f"Le parametre {param_key} n'existe pas dans la variable global") self.logs.error(f"Le parametre {param_key} n'existe pas dans la variable global")
@@ -334,6 +328,10 @@ class Base:
if updated_rows > 0: if updated_rows > 0:
setattr(dataclassObj, param_key, self.int_if_possible(param_value)) 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(f'Parameter updated : {param_key} - {param_value} | Module: {module_name}')
else:
self.logs.error(f'Parameter NOT updated : {param_key} - {param_value} | Module: {module_name}')
else:
self.logs.error(f'Parameter and Module do not exist: Param ({param_key}) - Value ({param_value}) | Module ({module_name})')
self.logs.debug(dataclassObj) self.logs.debug(dataclassObj)
@@ -429,7 +427,7 @@ class Base:
except AssertionError as ae: except AssertionError as ae:
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, daemon: bool = True) -> None:
"""Create a new thread and store it into running_threads variable """Create a new thread and store it into running_threads variable
Args: Args:
@@ -445,7 +443,7 @@ class Base:
if thread.getName() == func_name: if thread.getName() == func_name:
return None 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=daemon)
th.start() th.start()
self.running_threads.append(th) self.running_threads.append(th)
@@ -682,6 +680,17 @@ class Base:
self.logs.critical(f'This remote ip is not valid : {ve}') self.logs.critical(f'This remote ip is not valid : {ve}')
return None return None
# def encode_ip(self, remote_ip_address: str) -> Union[str, None]:
# binary_ip = b64encode()
# try:
# decoded_ip = ipaddress.ip_address(binary_ip)
# return decoded_ip.exploded
# except ValueError as ve:
# self.logs.critical(f'This remote ip is not valid : {ve}')
# return None
def get_random(self, lenght:int) -> str: def get_random(self, lenght:int) -> str:
""" """
Retourn une chaîne aléatoire en fonction de la longueur spécifiée. Retourn une chaîne aléatoire en fonction de la longueur spécifiée.
@@ -749,4 +758,4 @@ class Base:
except TypeError as te: except TypeError as te:
self.logs.error(f'TypeError: [{channelToCheck}] - {te}') self.logs.error(f'TypeError: [{channelToCheck}] - {te}')
except Exception as err: except Exception as err:
self.logs.error(f'TypeError: {err}') self.logs.error(f'Error Not defined: {err}')

View File

@@ -1,4 +1,6 @@
import socket, ssl, time import socket
import ssl
import traceback
from ssl import SSLSocket from ssl import SSLSocket
from core.loadConf import Config from core.loadConf import Config
from core.Model import Clones from core.Model import Clones
@@ -7,13 +9,16 @@ from typing import Union
class Connection: class Connection:
def __init__(self, server_port: int, nickname: str, username: str, channels:list[str], CloneObject: Clones, ssl:bool = False) -> None: def __init__(self, server_port: int, nickname: str, username: str, realname: str, channels:list[str], CloneObject: Clones, ssl:bool = False) -> None:
self.Config = Config().ConfigObject self.Config = Config().ConfigObject
self.Base = Base(self.Config) self.Base = Base(self.Config)
self.IrcSocket: Union[socket.socket, SSLSocket] = None self.IrcSocket: Union[socket.socket, SSLSocket] = None
self.nickname = nickname self.nickname = nickname
self.username = username self.username = username
self.realname = realname
self.clone_chanlog = self.Config.CLONE_CHANNEL
self.clone_log_exempt = self.Config.CLONE_LOG_HOST_EXEMPT
self.channels:list[str] = channels self.channels:list[str] = channels
self.CHARSET = ['utf-8', 'iso-8859-1'] self.CHARSET = ['utf-8', 'iso-8859-1']
self.Clones = CloneObject self.Clones = CloneObject
@@ -60,7 +65,7 @@ class Connection:
self.Base.logs.critical(f"AttributeError __create_socket: {ae} - {soc.fileno()}") self.Base.logs.critical(f"AttributeError __create_socket: {ae} - {soc.fileno()}")
return False return False
def send2socket(self, send_message:str) -> None: def send2socket(self, send_message:str, disconnect: bool = False) -> None:
"""Envoit les commandes à envoyer au serveur. """Envoit les commandes à envoyer au serveur.
Args: Args:
@@ -68,9 +73,8 @@ class Connection:
""" """
try: try:
with self.Base.lock: with self.Base.lock:
# print(f">{str(send_message)}")
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0])) self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0]))
self.Base.logs.debug(f'{send_message}') self.Base.logs.debug(f'<<{self.currentCloneObject.nickname}>>: {send_message}')
except UnicodeDecodeError: except UnicodeDecodeError:
self.Base.logs.error(f'Decode Error try iso-8859-1 - message: {send_message}') self.Base.logs.error(f'Decode Error try iso-8859-1 - message: {send_message}')
@@ -97,10 +101,11 @@ class Connection:
try: try:
nickname = self.nickname nickname = self.nickname
username = self.username username = self.username
realname = self.realname
# Envoyer un message d'identification # Envoyer un message d'identification
writer.send(f"USER {nickname} {username} {username} {nickname} {username} :{username}\r\n".encode('utf-8')) 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"USER {username} {username} {username} :{realname}\r\n".encode('utf-8'))
writer.send(f"NICK {nickname}\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') self.Base.logs.debug('Link information sent to the server')
@@ -111,7 +116,6 @@ class Connection:
def connect(self): def connect(self):
try: try:
while self.signal: while self.signal:
try: try:
# 4072 max what the socket can grab # 4072 max what the socket can grab
@@ -129,6 +133,7 @@ class Connection:
data = data_in_bytes.splitlines(True) data = data_in_bytes.splitlines(True)
if not data: if not data:
# If no data then quit the loop
break break
self.parser(data) self.parser(data)
@@ -142,9 +147,10 @@ class Connection:
self.Base.logs.error(f"OSError __connect_to_irc: {oe} - {data}") self.Base.logs.error(f"OSError __connect_to_irc: {oe} - {data}")
self.signal = False self.signal = False
self.IrcSocket.shutdown(socket.SHUT_RDWR) self.IrcSocket.shutdown(socket.SHUT_WR)
self.IrcSocket.close() self.IrcSocket.shutdown(socket.SHUT_RD)
self.Base.logs.info("--> Clone Disconnected ...") self.currentCloneObject.init = False
self.Base.logs.info(f"<<{self.currentCloneObject.nickname}>> Clone Disconnected ...")
except AssertionError as ae: except AssertionError as ae:
self.Base.logs.error(f'Assertion error : {ae}') self.Base.logs.error(f'Assertion error : {ae}')
@@ -154,14 +160,16 @@ class Connection:
self.Base.logs.error(f"OS Error __connect_to_irc: {soe}") self.Base.logs.error(f"OS Error __connect_to_irc: {soe}")
except AttributeError as atte: except AttributeError as atte:
self.Base.logs.critical(f"{atte}") self.Base.logs.critical(f"{atte}")
self.Base.logs.critical(f"{traceback.format_exc()}")
except Exception as e: except Exception as e:
self.Base.logs.error(f"Exception: {e}") self.Base.logs.error(f"Exception: {e}")
def parser(self, cmd:list[bytes]): def parser(self, cmd:list[bytes]):
try: try:
for data in cmd: for data in cmd:
response = data.decode(self.CHARSET[0]).split() response = data.decode(self.CHARSET[0]).split()
self.signal = self.currentCloneObject.alive current_clone_nickname = self.currentCloneObject.nickname
# print(response) # print(response)
match response[0]: match response[0]:
@@ -172,29 +180,74 @@ class Connection:
case 'ERROR': case 'ERROR':
error_value = str(response[1]).replace(':','') error_value = str(response[1]).replace(':','')
if error_value == 'Closing': if error_value == 'Closing':
self.signal = False self.Base.logs.info(f"<<{self.currentCloneObject.nickname}>> {response} ...")
self.currentCloneObject.connected = False
else:
self.Base.logs.info(f"<<{self.currentCloneObject.nickname}>> {response} ...")
# self.signal = False
match response[1]: match response[1]:
case '376': case '376':
# End of MOTD
self.currentCloneObject.connected = True
self.currentCloneObject.init = False
for channel in self.channels: for channel in self.channels:
self.send2socket(f"JOIN {channel}") self.send2socket(f"JOIN {channel}")
self.send2socket(f"JOIN {self.clone_chanlog} {self.Config.CLONE_CHANNEL_PASSWORD}")
return None return None
case '422':
# Missing MOTD
self.currentCloneObject.connected = True
self.currentCloneObject.init = False
for channel in self.channels:
self.send2socket(f"JOIN {channel}")
self.send2socket(f"JOIN {self.clone_chanlog} {self.Config.CLONE_CHANNEL_PASSWORD}")
return None
case '433':
# Nickname already in use
self.currentCloneObject.connected = False
self.currentCloneObject.init = False
self.send2socket(f'QUIT :Thanks and goodbye')
self.Base.logs.warning(f"Nickname {self.currentCloneObject.nickname} already in use >> Clone should be disconnected")
return None
case 'PRIVMSG': case 'PRIVMSG':
self.Base.logs.debug(response) self.Base.logs.debug(f'<<{self.currentCloneObject.nickname}>> Response: {response}')
self.Base.logs.debug(f'{self.currentCloneObject.nickname} - {self.currentCloneObject.alive}') self.Base.logs.debug(f'<<{self.currentCloneObject.nickname}>> Alive: {self.currentCloneObject.alive}')
fullname = str(response[0]).replace(':', '') fullname = str(response[0]).replace(':', '')
nickname = fullname.split('!')[0].replace(':','') nickname = fullname.split('!')[0].replace(':','')
if response[2] == current_clone_nickname and nickname != self.Config.SERVICE_NICKNAME:
message = []
for i in range(3, len(response)):
message.append(response[i])
final_message = ' '.join(message)
exampt = False
for log_exception in self.clone_log_exempt:
if log_exception in fullname:
exampt = True
if not exampt:
self.send2socket(f"PRIVMSG {self.clone_chanlog} :{fullname} => {final_message[1:]}")
if nickname == self.Config.SERVICE_NICKNAME: if nickname == self.Config.SERVICE_NICKNAME:
command = str(response[3]).replace(':','') command = str(response[3]).replace(':','')
if command == 'KILL': if command == 'KILL':
self.send2socket(f'QUIT :Thanks and goodbye') self.send2socket(f'QUIT :Thanks and goodbye')
self.signal = self.currentCloneObject.alive
if command == 'JOIN': if command == 'JOIN':
channel_to_join = str(response[4]) channel_to_join = str(response[4])
self.send2socket(f"JOIN {channel_to_join}") self.send2socket(f"JOIN {channel_to_join}")
if command == 'SAY': if command == 'SAY':
clone_channel = str(response[4]) clone_channel = str(response[4])
message = [] message = []
for i in range(5, len(response)): for i in range(5, len(response)):
message.append(response[i]) message.append(response[i])
@@ -202,7 +255,6 @@ class Connection:
self.send2socket(f"PRIVMSG {clone_channel} :{final_message}") self.send2socket(f"PRIVMSG {clone_channel} :{final_message}")
except UnicodeEncodeError: except UnicodeEncodeError:
for data in cmd: for data in cmd:
response = data.decode(self.CHARSET[1],'replace').split() response = data.decode(self.CHARSET[1],'replace').split()

View File

@@ -1,48 +1,42 @@
{ {
"SERVEUR_IP": "0.0.0.0", "SERVEUR_IP": "YOUR.SERVER.IP",
"SERVEUR_HOSTNAME": "your.host.name", "SERVEUR_HOSTNAME": "YOUR.SERVER.HOST",
"SERVEUR_LINK": "your.link.to.server", "SERVEUR_LINK": "LINK.DE.TON.SERVER",
"SERVEUR_PORT": 7002, "SERVEUR_PORT": 7002,
"SERVEUR_PASSWORD": "link_password", "SERVEUR_PASSWORD": "YOUR_LINK_PASSWORD",
"SERVEUR_ID": "006", "SERVEUR_ID": "006",
"SERVEUR_SSL": true, "SERVEUR_SSL": true,
"SERVICE_NAME": "defender", "SERVICE_NAME": "defender",
"SERVICE_NICKNAME": "BotNickname", "SERVICE_NICKNAME": "PyDefender",
"SERVICE_REALNAME": "BotRealname", "SERVICE_REALNAME": "Python Defender Security",
"SERVICE_USERNAME": "BotUsername", "SERVICE_USERNAME": "PyDefender",
"SERVICE_HOST": "your.service.hostname", "SERVICE_HOST": "HOST.DE.TON.DEFENDER",
"SERVICE_INFO": "Network IRC Service", "SERVICE_INFO": "Network IRC Service",
"SERVICE_CHANLOG": "#services", "SERVICE_CHANLOG": "#services",
"SERVICE_SMODES": "+ioqBS", "SERVICE_SMODES": "+ioqBS",
"SERVICE_CMODES": "ntsO", "SERVICE_CMODES": "ntsOP",
"SERVICE_UMODES": "o", "SERVICE_UMODES": "o",
"SERVICE_PREFIX": "!", "SERVICE_PREFIX": "!",
"OWNER": "admin", "OWNER": "TON_NICK_NAME",
"PASSWORD": "password", "PASSWORD": "TON_PASSWORD",
"SALON_JAIL": "#jail", "SALON_JAIL": "#jail",
"SALON_JAIL_MODES": "sS", "SALON_JAIL_MODES": "sS",
"SALON_LIBERER": "#welcome", "SALON_LIBERER": "#welcome",
"CLONE_CHANNEL": "#clones",
"CLONE_CMODES": "+nts",
"CLONE_LOG_HOST_EXEMPT": ["HOST.TO.SKIP"],
"CLONE_CHANNEL_PASSWORD": "YOUR_CHANNEL_PASSWORD",
"API_TIMEOUT": 2, "API_TIMEOUT": 2,
"PORTS_TO_SCAN": [3028, 8080, 1080, 1085, 4145, 9050], "PORTS_TO_SCAN": [3028, 8080, 1080, 1085, 4145, 9050],
"WHITELISTED_IP": ["127.0.0.1"], "WHITELISTED_IP": ["127.0.0.1"],
"GLINE_DURATION": "30", "GLINE_DURATION": "30",
"DEBUG_LEVEL": 20, "DEBUG_LEVEL": 20
"CONFIG_COLOR": {
"blanche": "\\u0003\\u0030",
"noire": "\\u0003\\u0031",
"bleue": "\\u0003\\u0020",
"verte": "\\u0003\\u0033",
"rouge": "\\u0003\\u0034",
"jaune": "\\u0003\\u0036",
"gras": "\\u0002",
"nogc": "\\u0002\\u0003"
}
} }

View File

@@ -28,16 +28,9 @@ class Install:
self.set_configuration() self.set_configuration()
if not self.check_python_version():
# Tester si c'est la bonne version de python
exit("Python Version Error")
else:
if self.skip_install: if self.skip_install:
return None 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
if self.do_install(): if self.do_install():
@@ -76,13 +69,24 @@ class Install:
venv_python_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}python' venv_python_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}python'
) )
# Exclude Windows OS if not self.check_python_version():
# If the Python version is not good then Exit
exit("/!\\ Python version error /!\\")
if not os.path.exists(os.path.join(self.config.defender_install_folder, 'core', 'configuration.json')):
# If configuration file do not exist
exit("/!\\ Configuration file (configuration.json) doesn't exist /!\\")
# Exclude Windows OS from the installation
if os.name == 'nt': if os.name == 'nt':
#print('/!\\ Skip installation /!\\') #print('/!\\ Skip installation /!\\')
self.skip_install = True self.skip_install = True
else: return False
if self.is_root(): if self.is_root():
exit(f'/!\\ I highly not recommend running Defender as root /!\\')
self.skip_install = True self.skip_install = True
return False
def is_root(self) -> bool: def is_root(self) -> bool:
@@ -133,11 +137,11 @@ class Install:
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.config.python_current_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.config.python_current_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 : {self.config.python_current_version} ==> OK") print(f"> Version of python : {self.config.python_current_version} ==> OK")

View File

@@ -1,4 +1,4 @@
import ssl, re, importlib, sys, time, threading, socket import ssl, re, importlib, sys, time, threading, socket, traceback
from ssl import SSLSocket from ssl import SSLSocket
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Union, Literal from typing import Union, Literal
@@ -29,7 +29,7 @@ class Irc:
# Liste des commandes internes du bot # Liste des commandes internes du bot
self.commands_level = { self.commands_level = {
0: ['help', 'auth', 'copyright', 'uptime'], 0: ['help', 'auth', 'copyright', 'uptime', 'firstauth'],
1: ['load','reload','unload', 'deauth', 'checkversion'], 1: ['load','reload','unload', 'deauth', 'checkversion'],
2: ['show_modules', 'show_timers', 'show_threads', 'show_channels', 'show_users', 'show_admins'], 2: ['show_modules', 'show_timers', 'show_threads', 'show_channels', 'show_users', 'show_admins'],
3: ['quit', 'restart','addaccess','editaccess', 'delaccess'] 3: ['quit', 'restart','addaccess','editaccess', 'delaccess']
@@ -110,8 +110,8 @@ class Irc:
self.ircObject = ircInstance # créer une copie de l'instance Irc self.ircObject = ircInstance # créer une copie de l'instance 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.__join_saved_channels() # Join existing channels self.__join_saved_channels() # Join existing channels
self.load_existing_modules() # Charger les modules existant dans la base de données
while self.signal: while self.signal:
try: try:
@@ -131,6 +131,7 @@ class Irc:
self.__create_socket() self.__create_socket()
self.__link(self.IrcSocket) self.__link(self.IrcSocket)
self.__join_saved_channels()
self.load_existing_modules() self.load_existing_modules()
self.RESTART = 0 self.RESTART = 0
@@ -174,7 +175,8 @@ class Irc:
except AttributeError as atte: except AttributeError as atte:
self.Base.logs.critical(f"AttributeError: {atte}") self.Base.logs.critical(f"AttributeError: {atte}")
except Exception as e: except Exception as e:
self.Base.logs.critical(f"Exception: {e}") self.Base.logs.critical(f"General Error: {e}")
self.Base.logs.critical(traceback.format_exc())
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
@@ -212,14 +214,15 @@ class Irc:
writer.send(f":{sid} PROTOCTL SID={sid}\r\n".encode(charset)) writer.send(f":{sid} PROTOCTL SID={sid}\r\n".encode(charset))
writer.send(f":{sid} SERVER {link} 1 :{info}\r\n".encode(charset)) writer.send(f":{sid} SERVER {link} 1 :{info}\r\n".encode(charset))
writer.send(f":{sid} {nickname} :Reserved for services\r\n".encode(charset)) writer.send(f":{sid} {nickname} :Reserved for services\r\n".encode(charset))
writer.send(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * * :{realname}\r\n".encode(charset)) #writer.send(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * * :{realname}\r\n".encode(charset))
writer.send(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * fwAAAQ== :{realname}\r\n".encode(charset))
writer.send(f":{sid} SJOIN {unixtime} {chan} + :{service_id}\r\n".encode(charset)) writer.send(f":{sid} SJOIN {unixtime} {chan} + :{service_id}\r\n".encode(charset))
writer.send(f":{sid} TKL + Q * {nickname} {host} 0 {unixtime} :Reserved for services\r\n".encode(charset)) 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} +{cmodes}\r\n".encode(charset))
writer.send(f":{service_id} MODE {chan} +{umodes} {service_id}\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')
return None return None
except AttributeError as ae: except AttributeError as ae:
@@ -246,7 +249,6 @@ class Irc:
""" """
try: try:
with self.Base.lock: with self.Base.lock:
# print(f">{str(send_message)}")
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0])) self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0]))
self.Base.logs.debug(f'{send_message}') self.Base.logs.debug(f'{send_message}')
@@ -265,6 +267,41 @@ class Irc:
except OSError as oe: except OSError as oe:
self.Base.logs.error(f"OSError: {oe} - {send_message}") self.Base.logs.error(f"OSError: {oe} - {send_message}")
def sendNotice(self, msg:str, nickname: str) -> None:
"""Sending NOTICE by batches
Args:
msg (str): The message to send to the server
nickname (str): The reciever Nickname
"""
batch_size = self.Config.BATCH_SIZE
service_nickname = self.Config.SERVICE_NICKNAME
for i in range(0, len(str(msg)), batch_size):
batch = str(msg)[i:i+batch_size]
self.send2socket(f":{service_nickname} NOTICE {nickname} :{batch}")
def sendPrivMsg(self, msg: str, channel: str = None, nickname: str = None):
"""Sending PRIVMSG to a channel or to a nickname by batches
could be either channel or nickname not both together
Args:
msg (str): The message to send
channel (str, optional): The receiver channel. Defaults to None.
nickname (str, optional): The reciever nickname. Defaults to None.
"""
batch_size = self.Config.BATCH_SIZE
service_nickname = self.Config.SERVICE_NICKNAME
if not channel is None:
for i in range(0, len(str(msg)), batch_size):
batch = str(msg)[i:i+batch_size]
self.send2socket(f":{service_nickname} PRIVMSG {channel} :{batch}")
if not nickname is None:
for i in range(0, len(str(msg)), batch_size):
batch = str(msg)[i:i+batch_size]
self.send2socket(f":{service_nickname} PRIVMSG {nickname} :{batch}")
def send_response(self, responses:list[bytes]) -> None: def send_response(self, responses:list[bytes]) -> None:
try: try:
# print(data) # print(data)
@@ -272,14 +309,20 @@ class Irc:
response = data.decode(self.CHARSET[0]).split() response = data.decode(self.CHARSET[0]).split()
self.cmd(response) self.cmd(response)
except UnicodeEncodeError: except UnicodeEncodeError as ue:
for data in responses: for data in responses:
response = data.decode(self.CHARSET[1],'replace').split() response = data.decode(self.CHARSET[1],'replace').split()
self.cmd(response) self.cmd(response)
except UnicodeDecodeError: self.Base.logs.error(f'UnicodeEncodeError: {ue}')
self.Base.logs.error(response)
except UnicodeDecodeError as ud:
for data in responses: for data in responses:
response = data.decode(self.CHARSET[1],'replace').split() response = data.decode(self.CHARSET[1],'replace').split()
self.cmd(response) self.cmd(response)
self.Base.logs.error(f'UnicodeDecodeError: {ud}')
self.Base.logs.error(response)
except AssertionError as ae: except AssertionError as ae:
self.Base.logs.error(f"Assertion error : {ae}") self.Base.logs.error(f"Assertion error : {ae}")
@@ -443,10 +486,11 @@ class Irc:
except ModuleNotFoundError as moduleNotFound: except ModuleNotFoundError as moduleNotFound:
self.Base.logs.error(f"MODULE_NOT_FOUND: {moduleNotFound}") self.Base.logs.error(f"MODULE_NOT_FOUND: {moduleNotFound}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}MODULE_NOT_FOUND{self.Config.CONFIG_COLOR['noire']} ]: {moduleNotFound}") self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.COLORS.red}MODULE_NOT_FOUND{self.Config.COLORS.black} ]: {moduleNotFound}")
self.Base.db_delete_module(module_name)
except Exception as e: except Exception as e:
self.Base.logs.error(f"Something went wrong with a module you want to load : {e}") self.Base.logs.error(f"Something went wrong with a module you want to load : {e}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}ERROR{self.Config.CONFIG_COLOR['noire']} ]: {e}") self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.COLORS.red}ERROR{self.Config.COLORS.black} ]: {e}")
def insert_db_admin(self, uid:str, level:int) -> None: def insert_db_admin(self, uid:str, level:int) -> None:
@@ -574,7 +618,6 @@ class Irc:
return None return None
def thread_check_for_new_version(self, fromuser: str) -> None: def thread_check_for_new_version(self, fromuser: str) -> None:
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME
if self.Base.check_for_new_version(True): if self.Base.check_for_new_version(True):
@@ -585,35 +628,39 @@ class Irc:
return None return None
def cmd(self, data:list) -> None: def cmd(self, data: list[str]) -> None:
"""Parse server response
Args:
data (list[str]): Server response splitted in a list
"""
try: try:
original_response: list[str] = data.copy()
cmd_to_send:list[str] = data.copy() interm_response: list[str] = data.copy()
cmd = data.copy() """This the original without first value"""
cmd_to_debug = data.copy() interm_response.pop(0)
cmd_to_debug.pop(0)
if len(cmd) == 0 or len(cmd) == 1: if len(original_response) == 0 or len(original_response) == 1:
self.Base.logs.warning(f'Size ({str(len(cmd))}) - {cmd}') self.Base.logs.warning(f'Size ({str(len(original_response))}) - {original_response}')
return False return False
# self.debug(cmd_to_debug) if len(original_response) == 7:
if len(data) == 7: if original_response[2] == 'PRIVMSG' and original_response[4] == ':auth':
if data[2] == 'PRIVMSG' and data[4] == ':auth': data_copy = original_response.copy()
data_copy = data.copy()
data_copy[6] = '**********' data_copy[6] = '**********'
self.Base.logs.debug(data_copy) self.Base.logs.debug(data_copy)
else: else:
self.Base.logs.debug(data) self.Base.logs.debug(original_response)
else: else:
self.Base.logs.debug(data) self.Base.logs.debug(original_response)
match cmd[0]: match original_response[0]:
case 'PING': case 'PING':
# Sending PONG response to the serveur # Sending PONG response to the serveur
pong = str(cmd[1]).replace(':','') pong = str(original_response[1]).replace(':','')
self.send2socket(f"PONG :{pong}") self.send2socket(f"PONG :{pong}")
return None return None
@@ -622,19 +669,19 @@ class Irc:
# 'PREFIX=(qaohv)~&@%+', 'SID=001', 'MLOCK', 'TS=1703793941', 'EXTSWHOIS'] # 'PREFIX=(qaohv)~&@%+', 'SID=001', 'MLOCK', 'TS=1703793941', 'EXTSWHOIS']
# GET SERVER ID HOST # GET SERVER ID HOST
if len(cmd) > 5: if len(original_response) > 5:
if '=' in cmd[5]: if '=' in original_response[5]:
serveur_hosting_id = str(cmd[5]).split('=') serveur_hosting_id = str(original_response[5]).split('=')
self.HSID = serveur_hosting_id[1] self.HSID = serveur_hosting_id[1]
return False return False
case _: case _:
pass pass
if len(cmd) < 2: if len(original_response) < 2:
return False return False
match cmd[1]: match original_response[1]:
case 'SLOG': case 'SLOG':
# self.Base.scan_ports(cmd[7]) # self.Base.scan_ports(cmd[7])
@@ -642,23 +689,45 @@ class Irc:
# self.Base.create_thread(self.abuseipdb_scan, (cmd[7], )) # self.Base.create_thread(self.abuseipdb_scan, (cmd[7], ))
pass pass
case 'SQUIT':
# ['@msgid=QOEolbRxdhpVW5c8qLkbAU;time=2024-09-21T17:33:16.547Z', 'SQUIT', 'defender.deb.biz.st', ':Connection', 'closed']
server_hostname = interm_response[1]
uid_to_delete = ''
for s_user in self.User.UID_DB:
if s_user.hostname == server_hostname and 'S' in s_user.umodes:
uid_to_delete = s_user.uid
self.User.delete(uid_to_delete)
self.Channel.delete_user_from_all_channel(uid_to_delete)
case 'SJOIN':
# If Server Join channels
# [':11Z', 'SJOIN', '1726940687', '#welcome', '+', ':11ZAAAAAB']
channel_joined = original_response[3]
server_uid = self.Base.clean_uid(original_response[5])
self.Channel.insert(
self.Channel.ChannelModel(
name=channel_joined,
uids=[server_uid]
)
)
case 'REPUTATION': case 'REPUTATION':
# :001 REPUTATION 91.168.141.239 118 # :001 REPUTATION 91.168.141.239 118
try: try:
# if self.Config.ABUSEIPDB == 1: self.first_connexion_ip = original_response[2]
# self.Base.create_thread(self.abuseipdb_scan, (cmd[2], ))
self.first_connexion_ip = cmd[2]
self.first_score = 0 self.first_score = 0
if str(cmd[3]).find('*') != -1: if str(original_response[3]).find('*') != -1:
# If * available, it means that an ircop changed the repurtation score # If * available, it means that an ircop changed the repurtation score
# means also that the user exist will try to update all users with same IP # means also that the user exist will try to update all users with same IP
self.first_score = int(str(cmd[3]).replace('*','')) self.first_score = int(str(original_response[3]).replace('*',''))
for user in self.User.UID_DB: for user in self.User.UID_DB:
if user.remote_ip == self.first_connexion_ip: if user.remote_ip == self.first_connexion_ip:
user.score_connexion = self.first_score user.score_connexion = self.first_score
else: else:
self.first_score = int(cmd[3]) self.first_score = int(original_response[3])
# Possibilité de déclancher les bans a ce niveau. # Possibilité de déclancher les bans a ce niveau.
except IndexError as ie: except IndexError as ie:
@@ -681,7 +750,7 @@ class Irc:
case 'EOS': case 'EOS':
hsid = str(cmd[0]).replace(':','') hsid = str(original_response[0]).replace(':','')
if hsid == self.HSID: if hsid == self.HSID:
if self.INIT == 1: if self.INIT == 1:
current_version = self.Config.current_version current_version = self.Config.current_version
@@ -691,10 +760,6 @@ class Irc:
else: else:
version = f'{current_version}' version = f'{current_version}'
# self.send2socket(f":{self.Config.SERVICE_NICKNAME} SVSJOIN {self.Config.SERVICE_NICKNAME} {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} ")
@@ -721,20 +786,21 @@ class Irc:
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} : New Version available {version}") self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} : New Version available {version}")
# Initialisation terminé aprés le premier PING # Initialisation terminé aprés le premier PING
self.sendPrivMsg(msg=f'[{self.Config.COLORS.green}INFORMATION{self.Config.COLORS.nogc}] >> Defender is ready', channel='#devservices')
self.INIT = 0 self.INIT = 0
case _: case _:
pass pass
if len(cmd) < 3: if len(original_response) < 3:
return False return False
match cmd[2]: match original_response[2]:
case 'QUIT': case 'QUIT':
# :001N1WD7L QUIT :Quit: free_znc_1 # :001N1WD7L QUIT :Quit: free_znc_1
cmd.pop(0)
uid_who_quit = str(cmd[0]).replace(':', '') uid_who_quit = str(interm_response[0]).replace(':', '')
self.User.delete(uid_who_quit) self.User.delete(uid_who_quit)
self.Channel.delete_user_from_all_channel(uid_who_quit) self.Channel.delete_user_from_all_channel(uid_who_quit)
@@ -746,10 +812,8 @@ class Irc:
# ['@unrealircd.org/geoip=FR;unrealircd.org/', ':001OOU2H3', 'NICK', 'WebIrc', '1703795844'] # ['@unrealircd.org/geoip=FR;unrealircd.org/', ':001OOU2H3', 'NICK', 'WebIrc', '1703795844']
# Changement de nickname # Changement de nickname
# Supprimer la premiere valeur de la liste uid = str(interm_response[0]).replace(':','')
cmd.pop(0) newnickname = interm_response[2]
uid = str(cmd[0]).replace(':','')
newnickname = cmd[2]
self.User.update(uid, newnickname) self.User.update(uid, newnickname)
case 'MODE': case 'MODE':
@@ -764,24 +828,24 @@ class Irc:
# ':001T6VU3F', '001JGWB2K', '@11ZAAAAAB', # ':001T6VU3F', '001JGWB2K', '@11ZAAAAAB',
# '001F16WGR', '001X9YMGQ', '*+001DYPFGP', '@00BAAAAAJ', '001AAGOG9', '001FMFVG8', '001DAEEG7', # '001F16WGR', '001X9YMGQ', '*+001DYPFGP', '@00BAAAAAJ', '001AAGOG9', '001FMFVG8', '001DAEEG7',
# '&~G:unknown-users', '"~G:websocket-users', '"~G:known-users', '"~G:webirc-users'] # '&~G:unknown-users', '"~G:websocket-users', '"~G:known-users', '"~G:webirc-users']
cmd.pop(0)
channel = str(cmd[3]).lower() channel = str(interm_response[3]).lower()
len_cmd = len(cmd) len_cmd = len(interm_response)
list_users:list = [] list_users:list = []
occurence = 0 occurence = 0
start_boucle = 0 start_boucle = 0
# Trouver le premier user # Trouver le premier user
for i in range(len_cmd): for i in range(len_cmd):
s: list = re.findall(fr':', cmd[i]) s: list = re.findall(fr':', interm_response[i])
if s: if s:
occurence += 1 occurence += 1
if occurence == 2: if occurence == 2:
start_boucle = i start_boucle = i
# Boucle qui va ajouter l'ensemble des users (UID) # Boucle qui va ajouter l'ensemble des users (UID)
for i in range(start_boucle, len(cmd)): for i in range(start_boucle, len(interm_response)):
parsed_UID = str(cmd[i]) parsed_UID = str(interm_response[i])
# pattern = fr'[:|@|%|\+|~|\*]*' # pattern = fr'[:|@|%|\+|~|\*]*'
# pattern = fr':' # pattern = fr':'
# parsed_UID = re.sub(pattern, '', parsed_UID) # parsed_UID = re.sub(pattern, '', parsed_UID)
@@ -799,32 +863,51 @@ class Irc:
case 'PART': case 'PART':
# ['@unrealircd.org/geoip=FR;unrealircd.org/userhost=50d6492c@80.214.73.44;unrealircd.org/userip=50d6492c@80.214.73.44;msgid=YSIPB9q4PcRu0EVfC9ci7y-/mZT0+Gj5FLiDSZshH5NCw;time=2024-08-15T15:35:53.772Z', # ['@unrealircd.org/geoip=FR;unrealircd.org/userhost=50d6492c@80.214.73.44;unrealircd.org/userip=50d6492c@80.214.73.44;msgid=YSIPB9q4PcRu0EVfC9ci7y-/mZT0+Gj5FLiDSZshH5NCw;time=2024-08-15T15:35:53.772Z',
# ':001EPFBRD', 'PART', '#welcome', ':WEB', 'IRC', 'Paris'] # ':001EPFBRD', 'PART', '#welcome', ':WEB', 'IRC', 'Paris']
uid = str(cmd[1]).replace(':','') try:
channel = str(cmd[3]).lower() uid = str(interm_response[0]).replace(':','')
channel = str(interm_response[2]).lower()
self.Channel.delete_user_from_channel(channel, uid) self.Channel.delete_user_from_channel(channel, uid)
pass except IndexError as ie:
self.Base.logs.error(f'Index Error: {ie}')
case 'UID': case 'UID':
try:
# ['@s2s-md/geoip=cc=GB|cd=United\\sKingdom|asn=16276|asname=OVH\\sSAS;s2s-md/tls_cipher=TLSv1.3-TLS_CHACHA20_POLY1305_SHA256;s2s-md/creationtime=1721564601', # ['@s2s-md/geoip=cc=GB|cd=United\\sKingdom|asn=16276|asname=OVH\\sSAS;s2s-md/tls_cipher=TLSv1.3-TLS_CHACHA20_POLY1305_SHA256;s2s-md/creationtime=1721564601',
# ':001', 'UID', 'albatros', '0', '1721564597', 'albatros', 'vps-91b2f28b.vps.ovh.net', # ':001', 'UID', 'albatros', '0', '1721564597', 'albatros', 'vps-91b2f28b.vps.ovh.net',
# '001HB8G04', '0', '+iwxz', 'Clk-A62F1D18.vps.ovh.net', 'Clk-A62F1D18.vps.ovh.net', 'MyZBwg==', ':...'] # '001HB8G04', '0', '+iwxz', 'Clk-A62F1D18.vps.ovh.net', 'Clk-A62F1D18.vps.ovh.net', 'MyZBwg==', ':...']
if 'webirc' in cmd[0]:
isWebirc = True
else:
isWebirc = False
uid = str(cmd[8]) isWebirc = True if 'webirc' in original_response[0] else False
nickname = str(cmd[3]) isWebsocket = True if 'websocket' in original_response[0] else False
username = str(cmd[6])
hostname = str(cmd[7]) uid = str(original_response[8])
umodes = str(cmd[10]) nickname = str(original_response[3])
vhost = str(cmd[11]) username = str(original_response[6])
hostname = str(original_response[7])
umodes = str(original_response[10])
vhost = str(original_response[11])
if not 'S' in umodes: if not 'S' in umodes:
remote_ip = self.Base.decode_ip(str(cmd[13])) remote_ip = self.Base.decode_ip(str(original_response[13]))
else: else:
remote_ip = '127.0.0.1' remote_ip = '127.0.0.1'
# extract realname
realname_list = []
for i in range(14, len(original_response)):
realname_list.append(original_response[i])
realname = ' '.join(realname_list)[1:]
# Extract Geoip information
pattern = r'^.*geoip=cc=(\S{2}).*$'
geoip_match = re.match(pattern, original_response[0])
if geoip_match:
geoip = geoip_match.group(1)
else:
geoip = None
score_connexion = self.first_score score_connexion = self.first_score
self.User.insert( self.User.insert(
@@ -832,29 +915,36 @@ class Irc:
uid=uid, uid=uid,
nickname=nickname, nickname=nickname,
username=username, username=username,
realname=realname,
hostname=hostname, hostname=hostname,
umodes=umodes, umodes=umodes,
vhost=vhost, vhost=vhost,
isWebirc=isWebirc, isWebirc=isWebirc,
isWebsocket=isWebsocket,
remote_ip=remote_ip, remote_ip=remote_ip,
geoip=geoip,
score_connexion=score_connexion, score_connexion=score_connexion,
connexion_datetime=datetime.now() connexion_datetime=datetime.now()
) )
) )
for classe_name, classe_object in self.loaded_classes.items(): for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(cmd_to_send) classe_object.cmd(original_response)
except Exception as err:
self.Base.logs.error(f'General Error: {err}')
case 'PRIVMSG': case 'PRIVMSG':
try: try:
# Supprimer la premiere valeur # Supprimer la premiere valeur
cmd.pop(0) cmd = interm_response.copy()
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) user_trigger = self.User.get_nickname(get_uid_or_nickname)
dnickname = self.Config.SERVICE_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(self.Config.SERVICE_PREFIX,'') == ':auth':
cmd_copy = cmd.copy() cmd_copy = cmd.copy()
cmd_copy[5] = '**********' cmd_copy[5] = '**********'
self.Base.logs.info(cmd_copy) self.Base.logs.info(cmd_copy)
@@ -911,11 +1001,11 @@ class Irc:
return False return False
if not arg[0].lower() in self.commands: if not arg[0].lower() in self.commands:
self.debug(f"This command {arg[0]} is not available") self.Base.logs.debug(f"This command {arg[0]} sent by {user_trigger} is not available")
return False return False
cmd_to_send = convert_to_string.replace(':','') cmd_to_send = convert_to_string.replace(':','')
self.Base.log_cmd(self.User.get_nickname(user_trigger), cmd_to_send) self.Base.log_cmd(user_trigger, cmd_to_send)
fromchannel = None fromchannel = None
if len(arg) >= 2: if len(arg) >= 2:
@@ -929,15 +1019,29 @@ class Irc:
case _: case _:
pass pass
if cmd[2] != 'UID': if original_response[2] != 'UID':
# Envoyer la commande aux classes dynamiquement chargées # Envoyer la commande aux classes dynamiquement chargées
for classe_name, classe_object in self.loaded_classes.items(): for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(cmd_to_send) classe_object.cmd(original_response)
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} / {original_response} / length {str(len(original_response))}")
except Exception as err:
self.Base.logs.error(f"General Error: {err}")
self.Base.logs.error(f"General Error: {traceback.format_exc()}")
def _hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None: def _hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None:
"""_summary_
Args:
user (str): The user who sent the query
channel (Union[str, None]): If the command contain the channel
cmd (list): The defender cmd
fullcmd (list, optional): The full list of the cmd coming from PRIVMS. Defaults to [].
Returns:
None: Nothing to return
"""
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
@@ -965,7 +1069,7 @@ class Irc:
case 'notallowed': case 'notallowed':
try: try:
current_command = cmd[0] current_command = cmd[0]
self.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["rouge"]}{current_command}{self.Config.CONFIG_COLOR["noire"]} ] - Accès Refusé à {self.User.get_nickname(fromuser)}') self.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.red}{current_command}{self.Config.COLORS.black} ] - Accès Refusé à {self.User.get_nickname(fromuser)}')
self.send2socket(f':{dnickname} NOTICE {fromuser} : Accès Refusé') self.send2socket(f':{dnickname} NOTICE {fromuser} : Accès Refusé')
except IndexError as ie: except IndexError as ie:
self.Base.logs.error(f'{ie}') self.Base.logs.error(f'{ie}')
@@ -975,7 +1079,57 @@ class Irc:
current_command = cmd[0] current_command = cmd[0]
uid_to_deauth = self.User.get_uid(fromuser) uid_to_deauth = self.User.get_uid(fromuser)
self.delete_db_admin(uid_to_deauth) self.delete_db_admin(uid_to_deauth)
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}{current_command}{self.Config.CONFIG_COLOR['noire']} ] - {self.User.get_nickname(fromuser)} est désormais déconnecter de {dnickname}") self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.red}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.get_nickname(fromuser)} est désormais déconnecter de {dnickname}")
case 'firstauth':
# firstauth OWNER_NICKNAME OWNER_PASSWORD
current_nickname = self.User.get_nickname(fromuser)
current_uid = self.User.get_uid(fromuser)
current_command = str(cmd[0])
query = f"SELECT count(id) as c FROM {self.Config.table_admin}"
result = self.Base.db_execute_query(query)
result_db = result.fetchone()
if result_db[0] > 0:
self.send2socket(f":{dnickname} NOTICE {fromuser} :You can't use this command anymore ! Please use [{self.Config.SERVICE_PREFIX}auth] instead")
return False
if current_nickname is None:
self.Base.logs.critical(f"This nickname [{fromuser}] don't exist")
return False
# Credentials sent from the user
cmd_owner = str(cmd[1])
cmd_password = str(cmd[2])
# Credentials coming from the Configuration
config_owner = self.Config.OWNER
config_password = self.Config.PASSWORD
if current_nickname != cmd_owner:
self.Base.logs.critical(f"The current nickname [{fromuser}] is different than the nickname sent [{cmd_owner}] !")
self.send2socket(f":{dnickname} NOTICE {fromuser} :The current nickname [{fromuser}] is different than the nickname sent [{cmd_owner}] !")
return False
if current_nickname != config_owner:
self.Base.logs.critical(f"The current nickname [{current_nickname}] is different than the configuration owner [{config_owner}] !")
self.send2socket(f":{dnickname} NOTICE {fromuser} :The current nickname [{current_nickname}] is different than the configuration owner [{config_owner}] !")
return False
if cmd_owner != config_owner:
self.Base.logs.critical(f"The nickname sent [{cmd_owner}] is different than the configuration owner [{config_owner}] !")
self.send2socket(f":{dnickname} NOTICE {fromuser} :The nickname sent [{cmd_owner}] is different than the configuration owner [{config_owner}] !")
return False
if cmd_owner == config_owner and cmd_password == config_password:
self.Base.db_create_first_admin()
self.insert_db_admin(current_uid, 5)
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.get_nickname(fromuser)} est désormais connecté a {dnickname}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Connexion a {dnickname} réussie!")
else:
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.red}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.get_nickname(fromuser)} a tapé un mauvais mot de pass")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Mot de passe incorrecte")
case 'auth': case 'auth':
# ['auth', 'adator', 'password'] # ['auth', 'adator', 'password']
@@ -997,10 +1151,10 @@ class Irc:
if not user_from_db is None: if not user_from_db is None:
uid_user = self.User.get_uid(user_to_log) uid_user = self.User.get_uid(user_to_log)
self.insert_db_admin(uid_user, user_from_db[1]) self.insert_db_admin(uid_user, user_from_db[1])
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}{current_command}{self.Config.CONFIG_COLOR['noire']} ] - {self.User.get_nickname(fromuser)} est désormais connecté a {dnickname}") self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.get_nickname(fromuser)} est désormais connecté a {dnickname}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Connexion a {dnickname} réussie!") self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Connexion a {dnickname} réussie!")
else: else:
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}{current_command}{self.Config.CONFIG_COLOR['noire']} ] - {self.User.get_nickname(fromuser)} a tapé un mauvais mot de pass") self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.red}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.get_nickname(fromuser)} a tapé un mauvais mot de pass")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Mot de passe incorrecte") self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Mot de passe incorrecte")
else: else:
@@ -1009,6 +1163,10 @@ class Irc:
case 'addaccess': case 'addaccess':
try: try:
# .addaccess adator 5 password # .addaccess adator 5 password
if len(cmd) < 4:
self.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} addaccess [nickname] [level] [password]')
self.send2socket(f':{dnickname} NOTICE {fromuser} : level: from 1 to 4')
newnickname = cmd[1] newnickname = cmd[1]
newlevel = self.Base.int_if_possible(cmd[2]) newlevel = self.Base.int_if_possible(cmd[2])
password = cmd[3] password = cmd[3]
@@ -1135,13 +1293,13 @@ class Irc:
else: else:
user_level = 0 user_level = 0
self.send2socket(f':{dnickname} NOTICE {fromuser} : **************** LIST DES COMMANDES *****************') self.send2socket(f':{dnickname} NOTICE {fromuser} : ***************** LISTE DES COMMANDES *****************')
self.send2socket(f':{dnickname} NOTICE {fromuser} : ') self.send2socket(f':{dnickname} NOTICE {fromuser} : ')
for levDef in self.commands_level: for levDef in self.commands_level:
if int(user_level) >= int(count_level_definition): if int(user_level) >= int(count_level_definition):
self.send2socket(f':{dnickname} NOTICE {fromuser} : **************** {self.Config.CONFIG_COLOR["noire"]}[ {self.Config.CONFIG_COLOR["verte"]}LEVEL {str(levDef)} {self.Config.CONFIG_COLOR["noire"]}] ****************') self.send2socket(f':{dnickname} NOTICE {fromuser} : ***************** {self.Config.COLORS.nogc}[ {self.Config.COLORS.green}LEVEL {str(levDef)} {self.Config.COLORS.nogc}] *****************')
count_commands = 0 count_commands = 0
help = '' help = ''
for comm in self.commands_level[count_level_definition]: for comm in self.commands_level[count_level_definition]:
@@ -1155,7 +1313,7 @@ class Irc:
count_level_definition += 1 count_level_definition += 1
self.send2socket(f':{dnickname} NOTICE {fromuser} : **************** FIN DES COMMANDES *****************') self.send2socket(f':{dnickname} NOTICE {fromuser} : ***************** FIN DES COMMANDES *****************')
case 'load': case 'load':
@@ -1283,10 +1441,6 @@ class Irc:
results = self.Base.db_execute_query(f'SELECT module_name 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:
# self.send2socket(f":{dnickname} NOTICE {fromuser} :There is no module loaded")
# return False
found = False found = False
for module in all_modules: for module in all_modules:
@@ -1295,15 +1449,12 @@ class Irc:
found = True found = True
if found: if found:
self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.CONFIG_COLOR['verte']}Loaded{self.Config.CONFIG_COLOR['nogc']}") self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.COLORS.green}Loaded{self.Config.COLORS.nogc}")
else: else:
self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.CONFIG_COLOR['rouge']}Not Loaded{self.Config.CONFIG_COLOR['nogc']}") self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.COLORS.red}Not Loaded{self.Config.COLORS.nogc}")
found = False found = False
# for r in results:
# self.send2socket(f":{dnickname} NOTICE {fromuser} :{r[0]} - {self.Config.CONFIG_COLOR['verte']}Loaded{self.Config.CONFIG_COLOR['nogc']}")
case 'show_timers': case 'show_timers':
if self.Base.running_timers: if self.Base.running_timers:
@@ -1330,7 +1481,7 @@ class Irc:
case 'show_users': case 'show_users':
for db_user in self.User.UID_DB: for db_user in self.User.UID_DB:
self.send2socket(f":{dnickname} NOTICE {fromuser} :UID : {db_user.uid} - isWebirc: {db_user.isWebirc} - Nickname: {db_user.nickname} - Connection: {db_user.connexion_datetime}") self.send2socket(f":{dnickname} NOTICE {fromuser} :UID : {db_user.uid} - isWebirc: {db_user.isWebirc} - isWebSocket: {db_user.isWebsocket} - Nickname: {db_user.nickname} - Connection: {db_user.connexion_datetime}")
case 'show_admins': case 'show_admins':
for db_admin in self.Admin.UID_ADMIN_DB: for db_admin in self.Admin.UID_ADMIN_DB:
@@ -1341,7 +1492,7 @@ class Irc:
self.send2socket(f':{dnickname} NOTICE {fromuser} : {uptime}') self.send2socket(f':{dnickname} NOTICE {fromuser} : {uptime}')
case 'copyright': case 'copyright':
self.send2socket(f':{dnickname} NOTICE {fromuser} : # Defender V.{self.Config.current_version} Developped by adator® and dktmb® #') self.send2socket(f':{dnickname} NOTICE {fromuser} : # Defender V.{self.Config.current_version} Developped by adator® #')
case 'checkversion': case 'checkversion':

View File

@@ -7,6 +7,17 @@ from dataclasses import dataclass, field
# CONFIGURATION FILE # # CONFIGURATION FILE #
########################################## ##########################################
@dataclass
class ColorModel:
white: str = "\x0300"
black: str = "\x0301"
blue: str = "\x0302"
green: str = "\x0303"
red: str = "\x0304"
yellow: str = "\x0306"
bold: str = "\x02"
nogc: str = "\x03"
@dataclass @dataclass
class ConfigDataModel: class ConfigDataModel:
@@ -82,6 +93,18 @@ class ConfigDataModel:
SALON_LIBERER: str SALON_LIBERER: str
"""Channel where the nickname will be released""" """Channel where the nickname will be released"""
CLONE_CHANNEL: str
"""Channel where clones are hosted and will log PRIVMSG"""
CLONE_CMODES: str
"""Clone channel modes"""
CLONE_LOG_HOST_EXEMPT: list[str]
"""Hosts that clones will not log"""
CLONE_CHANNEL_PASSWORD: str
"""Clone password channel"""
API_TIMEOUT: int API_TIMEOUT: int
"""Default api timeout in second""" """Default api timeout in second"""
@@ -94,11 +117,9 @@ class ConfigDataModel:
GLINE_DURATION: str GLINE_DURATION: str
"""Gline duration""" """Gline duration"""
DEBUG_LEVEL:Literal[10, 20, 30, 40, 50] # Le niveau des logs DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50 DEBUG_LEVEL:Literal[10, 20, 30, 40, 50]
"""Logs level: DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50""" """Logs level: DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50"""
CONFIG_COLOR: dict[str, str]
table_admin: str table_admin: str
"""Admin table""" """Admin table"""
@@ -129,6 +150,12 @@ class ConfigDataModel:
db_path: str db_path: str
"""The database path""" """The database path"""
COLORS: ColorModel = ColorModel()
"""Available colors in Defender"""
BATCH_SIZE: int = 400
"""The batch size used for privmsg and notice"""
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"
@@ -147,49 +174,52 @@ class Config:
with open(conf_filename, 'r') as configuration_data: with open(conf_filename, 'r') as configuration_data:
configuration:dict[str, Union[str, int, list, dict]] = json.load(configuration_data) configuration:dict[str, Union[str, int, list, dict]] = json.load(configuration_data)
for key, value in configuration['CONFIG_COLOR'].items():
configuration['CONFIG_COLOR'][key] = str(value).encode('utf-8').decode('unicode_escape')
return configuration return configuration
except FileNotFoundError as fe: except FileNotFoundError as fe:
print(f'FileNotFound: {fe}') print(f'FileNotFound: {fe}')
print('Configuration file not found please create core/configuration.json') print('Configuration file not found please create core/configuration.json')
sys.exit(0) sys.exit(0)
except KeyError as ke:
print(f'Key Error: {ke}')
print('The key must be defined in core/configuration.json')
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()
ConfigObject: ConfigDataModel = ConfigDataModel( ConfigObject: ConfigDataModel = ConfigDataModel(
SERVEUR_IP=import_config["SERVEUR_IP"], SERVEUR_IP=import_config["SERVEUR_IP"] if "SERVEUR_IP" in import_config else '127.0.0.1',
SERVEUR_HOSTNAME=import_config["SERVEUR_HOSTNAME"], SERVEUR_HOSTNAME=import_config["SERVEUR_HOSTNAME"] if "SERVEUR_HOSTNAME" in import_config else None,
SERVEUR_LINK=import_config["SERVEUR_LINK"], SERVEUR_LINK=import_config["SERVEUR_LINK"] if "SERVEUR_LINK" in import_config else None,
SERVEUR_PORT=import_config["SERVEUR_PORT"], SERVEUR_PORT=import_config["SERVEUR_PORT"] if "SERVEUR_PORT" in import_config else 6667,
SERVEUR_PASSWORD=import_config["SERVEUR_PASSWORD"], SERVEUR_PASSWORD=import_config["SERVEUR_PASSWORD"] if "SERVEUR_PASSWORD" in import_config else None,
SERVEUR_ID=import_config["SERVEUR_ID"], SERVEUR_ID=import_config["SERVEUR_ID"] if "SERVEUR_ID" in import_config else '19Z',
SERVEUR_SSL=import_config["SERVEUR_SSL"], SERVEUR_SSL=import_config["SERVEUR_SSL"] if "SERVEUR_SSL" in import_config else False,
SERVICE_NAME=import_config["SERVICE_NAME"], SERVICE_NAME=import_config["SERVICE_NAME"] if "SERVICE_NAME" in import_config else 'Defender',
SERVICE_NICKNAME=import_config["SERVICE_NICKNAME"], SERVICE_NICKNAME=import_config["SERVICE_NICKNAME"] if "SERVICE_NICKNAME" in import_config else 'Defender',
SERVICE_REALNAME=import_config["SERVICE_REALNAME"], SERVICE_REALNAME=import_config["SERVICE_REALNAME"] if "SERVICE_REALNAME" in import_config else 'Defender Security',
SERVICE_USERNAME=import_config["SERVICE_USERNAME"], SERVICE_USERNAME=import_config["SERVICE_USERNAME"] if "SERVICE_USERNAME" in import_config else 'IRCSecurity',
SERVICE_HOST=import_config["SERVICE_HOST"], SERVICE_HOST=import_config["SERVICE_HOST"] if "SERVICE_HOST" in import_config else 'defender.local.network',
SERVICE_INFO=import_config["SERVICE_INFO"], SERVICE_INFO=import_config["SERVICE_INFO"] if "SERVICE_INFO" in import_config else 'Defender Network IRC Service',
SERVICE_CHANLOG=import_config["SERVICE_CHANLOG"], SERVICE_CHANLOG=import_config["SERVICE_CHANLOG"] if "SERVICE_CHANLOG" in import_config else '#services',
SERVICE_SMODES=import_config["SERVICE_SMODES"], SERVICE_SMODES=import_config["SERVICE_SMODES"] if "SERVICE_SMODES" in import_config else '+ioqBS',
SERVICE_CMODES=import_config["SERVICE_CMODES"], SERVICE_CMODES=import_config["SERVICE_CMODES"] if "SERVICE_CMODES" in import_config else 'ntsOP',
SERVICE_UMODES=import_config["SERVICE_UMODES"], SERVICE_UMODES=import_config["SERVICE_UMODES"] if "SERVICE_UMODES" in import_config else 'o',
SERVICE_PREFIX=import_config["SERVICE_PREFIX"], SERVICE_PREFIX=import_config["SERVICE_PREFIX"] if "SERVICE_PREFIX" in import_config else '!',
OWNER=import_config["OWNER"], OWNER=import_config["OWNER"] if "OWNER" in import_config else 'admin',
PASSWORD=import_config["PASSWORD"], PASSWORD=import_config["PASSWORD"] if "PASSWORD" in import_config else 'admin',
SALON_JAIL=import_config["SALON_JAIL"], SALON_JAIL=import_config["SALON_JAIL"] if "SALON_JAIL" in import_config else '#jail',
SALON_JAIL_MODES=import_config["SALON_JAIL_MODES"], SALON_JAIL_MODES=import_config["SALON_JAIL_MODES"] if "SALON_JAIL_MODES" in import_config else 'sS',
SALON_LIBERER=import_config["SALON_LIBERER"], SALON_LIBERER=import_config["SALON_LIBERER"] if "SALON_LIBERER" in import_config else '#welcome',
API_TIMEOUT=import_config["API_TIMEOUT"], CLONE_CHANNEL=import_config["CLONE_CHANNEL"] if "CLONE_CHANNEL" in import_config else '#clones',
PORTS_TO_SCAN=import_config["PORTS_TO_SCAN"], CLONE_CMODES=import_config["CLONE_CMODES"] if "CLONE_CMODES" in import_config else '+nts',
WHITELISTED_IP=import_config["WHITELISTED_IP"], CLONE_LOG_HOST_EXEMPT=import_config["CLONE_LOG_HOST_EXEMPT"] if "CLONE_LOG_HOST_EXEMPT" in import_config else [],
GLINE_DURATION=import_config["GLINE_DURATION"], CLONE_CHANNEL_PASSWORD=import_config["CLONE_CHANNEL_PASSWORD"] if "CLONE_CHANNEL_PASSWORD" in import_config else "clone_Password_1234",
DEBUG_LEVEL=import_config["DEBUG_LEVEL"], API_TIMEOUT=import_config["API_TIMEOUT"] if "API_TIMEOUT" in import_config else 2,
CONFIG_COLOR=import_config["CONFIG_COLOR"], PORTS_TO_SCAN=import_config["PORTS_TO_SCAN"] if "PORTS_TO_SCAN" in import_config else [],
WHITELISTED_IP=import_config["WHITELISTED_IP"] if "WHITELISTED_IP" in import_config else ['127.0.0.1'],
GLINE_DURATION=import_config["GLINE_DURATION"] if "GLINE_DURATION" in import_config else '30',
DEBUG_LEVEL=import_config["DEBUG_LEVEL"] if "DEBUG_LEVEL" in import_config else 20,
table_admin='core_admin', table_admin='core_admin',
table_commande='core_command', table_commande='core_command',
table_log='core_log', table_log='core_log',

View File

@@ -1,5 +1,5 @@
from dataclasses import dataclass, fields, field from dataclasses import dataclass, fields, field
import random, faker, time import random, faker, time, logging
from datetime import datetime from datetime import datetime
from typing import Union from typing import Union
from core.irc import Irc from core.irc import Irc
@@ -58,6 +58,11 @@ class Clone():
# Load module configuration (Mandatory) # Load module configuration (Mandatory)
self.__load_module_configuration() self.__load_module_configuration()
self.Base.db_query_channel(action='add', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} JOIN {self.Config.CLONE_CHANNEL}")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +nts")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +k {self.Config.CLONE_CHANNEL_PASSWORD}")
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
@@ -120,53 +125,139 @@ class Clone():
for clone in self.ModConfig.clone_nicknames: for clone in self.ModConfig.clone_nicknames:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone} :KILL') self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone} :KILL')
self.Base.db_query_channel(action='del', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PART {self.Config.CLONE_CHANNEL}")
return None return None
def thread_create_clones(self, nickname: str, username: str, channels: list, server_port: int, ssl: bool) -> None: def thread_clone_clean_up(self, wait: float):
Connection(server_port=server_port, nickname=nickname, username=username, channels=channels, CloneObject=self.Clone, ssl=ssl) activated = True
while activated:
clone_to_kill: list[str] = []
for clone in self.Clone.UID_CLONE_DB:
if not clone.connected and clone.alive and not clone.init:
clone_to_kill.append(clone.nickname)
clone.alive = False
for clone_nickname in clone_to_kill:
if self.Clone.delete(clone_nickname):
self.Logs.debug(f'<<{clone_nickname}>> object has been deleted')
del clone_to_kill
# If LIST empty then stop this thread
if not self.Clone.UID_CLONE_DB:
break
time.sleep(wait)
def thread_change_hostname(self):
fake = faker.Faker('en_GB')
for clone in self.Clone.UID_CLONE_DB:
if not clone.vhost is None:
continue
rand_1 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
rand_2 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
rand_3 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
rand_ip = ''.join(rand_1) + '.' + ''.join(rand_2) + '.' + ''.join(rand_3) + '.IP'
found = False
while not found:
if clone.connected:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} CHGHOST {clone.nickname} {rand_ip}')
found = True
clone.vhost = rand_ip
break
if not clone in self.Clone.UID_CLONE_DB:
found = True
break
def thread_create_clones_with_interval(self, number_of_clones:int, channels: list, connection_interval: float):
for i in range(number_of_clones):
nickname, username, realname = self.generate_names()
self.Base.create_thread(
self.thread_create_clones,
(nickname, username, realname, channels, 6697, True)
)
time.sleep(connection_interval)
self.Base.create_thread(
self.thread_change_hostname
)
# self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{str(number_of_clones)} clones joined the network')
self.Base.create_thread(self.thread_clone_clean_up, (5, ), run_once=True)
def thread_create_clones(self, nickname: str, username: str, realname: str, channels: list, server_port: int, ssl: bool) -> None:
Connection(server_port=server_port, nickname=nickname, username=username, realname=realname, channels=channels, CloneObject=self.Clone, ssl=ssl)
return None return None
def thread_join_channels(self, channel_name: str, wait: float, clone_name:str = None): def thread_join_channels(self, channel_name: str, wait: float, clone_name:str = None):
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Clones start to join {channel_name} with {wait} secondes frequency')
if clone_name is None: if clone_name is None:
for clone in self.Clone.UID_CLONE_DB: for clone in self.Clone.UID_CLONE_DB:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}') if not channel_name in clone.channels:
time.sleep(wait) time.sleep(wait)
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}')
clone.channels.append(channel_name)
else: else:
for clone in self.Clone.UID_CLONE_DB: for clone in self.Clone.UID_CLONE_DB:
if clone_name == clone.nickname: if clone_name == clone.nickname:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}') if not channel_name in clone.channels:
time.sleep(wait) time.sleep(wait)
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}')
clone.channels.append(channel_name)
def generate_names(self) -> tuple[str, str]: def generate_names(self) -> tuple[str, str, str]:
try: try:
logging.getLogger('faker').setLevel(logging.CRITICAL)
fake = faker.Faker('en_GB') fake = faker.Faker('en_GB')
# nickname = fake.first_name()
# username = fake.last_name()
# Generate Username
chaine = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
new_username = fake.random_sample(chaine, 9)
username = ''.join(new_username)
# Create realname XX F|M Department
gender = fake.random_choices(['F','M'], 1)
gender = ''.join(gender)
if gender == 'F':
nickname = fake.first_name_female()
elif gender == 'M':
nickname = fake.first_name_male()
else:
nickname = fake.first_name() nickname = fake.first_name()
username = fake.last_name()
age = random.randint(20, 60)
fake_fr = faker.Faker(['fr_FR', 'en_GB'])
department = fake_fr.department_name()
realname = f'{age} {gender} {department}'
if self.Clone.exists(nickname=nickname): if self.Clone.exists(nickname=nickname):
caracteres = '0123456789' caracteres = '0123456789'
randomize = ''.join(random.choice(caracteres) for _ in range(2)) randomize = ''.join(random.choice(caracteres) for _ in range(2))
nickname = nickname + str(randomize) nickname = nickname + str(randomize)
self.Clone.insert( self.Clone.insert(
self.Clone.CloneModel(alive=True, nickname=nickname, username=username) self.Clone.CloneModel(alive=True, nickname=nickname, username=username, realname=realname, channels=[])
) )
else: else:
self.Clone.insert( self.Clone.insert(
self.Clone.CloneModel(alive=True, nickname=nickname, username=username) self.Clone.CloneModel(alive=True, nickname=nickname, username=username, realname=realname, channels=[])
) )
# if not nickname in self.ModConfig.clone_nicknames: return (nickname, username, realname)
# 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: except AttributeError as ae:
self.Logs.error(f'Attribute Error : {ae}') self.Logs.error(f'Attribute Error : {ae}')
@@ -188,6 +279,7 @@ class Clone():
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None: def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
try:
command = str(cmd[0]).lower() command = str(cmd[0]).lower()
fromuser = user fromuser = user
@@ -196,31 +288,31 @@ class Clone():
match command: match command:
case 'clone': case 'clone':
option = str(cmd[1]).lower()
if len(command) == 1: if len(cmd) == 1:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect 6') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect 6 2.5')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill [all | nickname]') 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 join [all | nickname] #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list')
option = str(cmd[1]).lower()
match option: match option:
case 'connect': case 'connect':
try: try:
# clone connect 5
number_of_clones = int(cmd[2]) number_of_clones = int(cmd[2])
for i in range(number_of_clones): connection_interval = int(cmd[3]) if len(cmd) == 4 else 0.5
nickname, username = self.generate_names()
self.Base.create_thread( self.Base.create_thread(
self.thread_create_clones, self.thread_create_clones_with_interval,
(nickname, username, [], 6697, True) (number_of_clones, [], connection_interval)
) )
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{str(number_of_clones)} clones joined the network')
except Exception as err: except Exception as err:
self.Logs.error(f'{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} :/msg {dnickname} clone connect [number of clone you want to connect] [Connection Interval]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} clone connect 6') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} clone connect 6 2.5')
case 'kill': case 'kill':
try: try:
@@ -268,9 +360,10 @@ class Clone():
case 'list': case 'list':
try: try:
clone_count = len(self.Clone.UID_CLONE_DB)
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> Number of connected clones: {clone_count}')
for clone_name in self.Clone.UID_CLONE_DB: for clone_name in self.Clone.UID_CLONE_DB:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> {clone_name.nickname} | {clone_name.username}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> Nickname: {clone_name.nickname} | Username: {clone_name.username} | Realname: {clone_name.realname} | Vhost: {clone_name.vhost} | Init: {clone_name.init} | Live: {clone_name.alive} | Connected: {clone_name.connected}')
pass
except Exception as err: except Exception as err:
self.Logs.error(f'{err}') self.Logs.error(f'{err}')
@@ -302,3 +395,7 @@ class Clone():
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join [all | nickname] #channel') 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 say [clone_nickname] #channel [message]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list')
except IndexError as ie:
self.Logs.error(f'Index Error: {ie}')
except Exception as err:
self.Logs.error(f'Index Error: {err}')

View File

@@ -35,7 +35,9 @@ class Command():
# Create module commands (Mandatory) # Create module commands (Mandatory)
self.commands_level = { self.commands_level = {
1: ['join', 'part'], 1: ['join', 'part'],
2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice', 'devoice', 'opall', 'deopall', 'devoiceall', 'voiceall', 'ban', 'unban','kick', 'kickban', 'umode'] 2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice',
'devoice', 'opall', 'deopall', 'devoiceall', 'voiceall', 'ban',
'unban','kick', 'kickban', 'umode', 'svsjoin', 'svspart', 'svsnick']
} }
# Init the module # Init the module
@@ -178,6 +180,8 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}') self.Logs.warning(f'_hcmd OP: {str(e)}')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'devoiceall': case 'devoiceall':
try: try:
@@ -185,8 +189,11 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}') self.Logs.warning(f'_hcmd OP: {str(e)}')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'voiceall': case 'voiceall':
try:
chan_info = self.Channel.get_Channel(fromchannel) chan_info = self.Channel.get_Channel(fromchannel)
set_mode = 'v' set_mode = 'v'
mode:str = '' mode:str = ''
@@ -202,8 +209,13 @@ class Command():
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +{mode} {users}") self.Irc.send2socket(f":{service_id} MODE {fromchannel} +{mode} {users}")
mode = '' mode = ''
users = '' 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': case 'opall':
try:
chan_info = self.Channel.get_Channel(fromchannel) chan_info = self.Channel.get_Channel(fromchannel)
set_mode = 'o' set_mode = 'o'
mode:str = '' mode:str = ''
@@ -219,6 +231,10 @@ class Command():
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +{mode} {users}") self.Irc.send2socket(f":{service_id} MODE {fromchannel} +{mode} {users}")
mode = '' mode = ''
users = '' 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': case 'op':
# /mode #channel +o user # /mode #channel +o user
@@ -246,6 +262,8 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}') self.Logs.warning(f'_hcmd OP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} op [#SALON] [NICKNAME]') 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': case 'deop':
# /mode #channel -o user # /mode #channel -o user
@@ -271,6 +289,8 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEOP: {str(e)}') self.Logs.warning(f'_hcmd DEOP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deop [#SALON] [NICKNAME]') 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': case 'owner':
# /mode #channel +q user # /mode #channel +q user
@@ -296,6 +316,8 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd OWNER: {str(e)}') self.Logs.warning(f'_hcmd OWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} owner [#SALON] [NICKNAME]') 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': case 'deowner':
# /mode #channel -q user # /mode #channel -q user
@@ -321,6 +343,8 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEOWNER: {str(e)}') self.Logs.warning(f'_hcmd DEOWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]') 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': case 'halfop':
# /mode #channel +h user # /mode #channel +h user
@@ -346,6 +370,8 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd halfop: {str(e)}') self.Logs.warning(f'_hcmd halfop: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} halfop [#SALON] [NICKNAME]') 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': case 'dehalfop':
# /mode #channel -h user # /mode #channel -h user
@@ -371,6 +397,8 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEHALFOP: {str(e)}') self.Logs.warning(f'_hcmd DEHALFOP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]') 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': case 'voice':
# /mode #channel +v user # /mode #channel +v user
@@ -396,6 +424,8 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd VOICE: {str(e)}') self.Logs.warning(f'_hcmd VOICE: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} voice [#SALON] [NICKNAME]') 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': case 'devoice':
# /mode #channel -v user # /mode #channel -v user
@@ -421,6 +451,8 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEVOICE: {str(e)}') self.Logs.warning(f'_hcmd DEVOICE: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]') 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': case 'ban':
# .ban #channel nickname # .ban #channel nickname
@@ -437,6 +469,8 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd BAN: {str(e)}') self.Logs.warning(f'_hcmd BAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} ban [#SALON] [NICKNAME]') 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': case 'unban':
# .unban #channel nickname # .unban #channel nickname
@@ -449,9 +483,12 @@ class Command():
self.Irc.send2socket(f":{service_id} MODE {sentchannel} -b {nickname}!*@*") self.Irc.send2socket(f":{service_id} MODE {sentchannel} -b {nickname}!*@*")
self.Logs.debug(f'{fromuser} has unbanned {nickname} from {sentchannel}') self.Logs.debug(f'{fromuser} has unbanned {nickname} from {sentchannel}')
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd UNBAN: {str(e)}') self.Logs.warning(f'_hcmd UNBAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} unban [#SALON] [NICKNAME]') 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': case 'kick':
# .kick #channel nickname reason # .kick #channel nickname reason
@@ -470,9 +507,12 @@ class Command():
self.Irc.send2socket(f":{service_id} KICK {sentchannel} {nickname} {final_reason}") self.Irc.send2socket(f":{service_id} KICK {sentchannel} {nickname} {final_reason}")
self.Logs.debug(f'{fromuser} has kicked {nickname} from {sentchannel} : {final_reason}') self.Logs.debug(f'{fromuser} has kicked {nickname} from {sentchannel} : {final_reason}')
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd KICK: {str(e)}') self.Logs.warning(f'_hcmd KICK: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kick [#SALON] [NICKNAME] [REASON]') 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': case 'kickban':
# .kickban #channel nickname reason # .kickban #channel nickname reason
@@ -492,9 +532,12 @@ class Command():
self.Irc.send2socket(f":{service_id} KICK {sentchannel} {nickname} {final_reason}") self.Irc.send2socket(f":{service_id} KICK {sentchannel} {nickname} {final_reason}")
self.Irc.send2socket(f":{service_id} MODE {sentchannel} +b {nickname}!*@*") self.Irc.send2socket(f":{service_id} MODE {sentchannel} +b {nickname}!*@*")
self.Logs.debug(f'{fromuser} has kicked and banned {nickname} from {sentchannel} : {final_reason}') self.Logs.debug(f'{fromuser} has kicked and banned {nickname} from {sentchannel} : {final_reason}')
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd KICKBAN: {str(e)}') self.Logs.warning(f'_hcmd KICKBAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kickban [#SALON] [NICKNAME] [REASON]') 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': case 'join':
@@ -510,6 +553,8 @@ class Command():
except IndexError as ie: except IndexError as ie:
self.Logs.error(f'{ie}') self.Logs.error(f'{ie}')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'part': case 'part':
@@ -529,6 +574,8 @@ class Command():
except IndexError as ie: except IndexError as ie:
self.Logs.error(f'{ie}') self.Logs.error(f'{ie}')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'umode': case 'umode':
try: try:
@@ -536,6 +583,62 @@ class Command():
nickname = str(cmd[1]) nickname = str(cmd[1])
umode = str(cmd[2]) umode = str(cmd[2])
self.send2socket(f':{dnickname} SVSMODE {nickname} {umode}') self.Irc.send2socket(f':{dnickname} SVSMODE {nickname} {umode}')
except KeyError as ke: except KeyError as ke:
self.Base.logs.error(ke) self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'svsjoin':
try:
# .svsjoin nickname #channel
nickname = str(cmd[1])
channel = str(cmd[2])
if len(cmd) != 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSJOIN nickname #channel')
return None
self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SVSJOIN {nickname} {channel}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSJOIN nickname #channel')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'svspart':
try:
# .svspart nickname #channel
nickname = str(cmd[1])
channel = str(cmd[2])
if len(cmd) != 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSPART nickname #channel')
return None
self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SVSPART {nickname} {channel}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSPART nickname #channel')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'svsnick':
try:
# .svsnick nickname newnickname
nickname = str(cmd[1])
newnickname = str(cmd[2])
unixtime = self.Base.get_unixtime()
if self.User.get_nickname(nickname) is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : This nickname do not exist')
return None
if len(cmd) != 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSNICK nickname newnickname')
return None
self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SVSNICK {nickname} {newnickname} {unixtime}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSNICK nickname newnickname')
self.Logs.warning(f'Unknown Error: {str(err)}')

View File

@@ -4,6 +4,7 @@ from typing import Union
import re, socket, psutil, requests, json, time import re, socket, psutil, requests, json, time
from sys import exit from sys import exit
from core.irc import Irc from core.irc import Irc
from core.Model import User
# Le module crée devra réspecter quelques conditions # Le module crée devra réspecter quelques conditions
# 1. Le nom de la classe devra toujours s'appeler comme le module. Exemple => nom de class Defender | nom du module mod_defender # 1. Le nom de la classe devra toujours s'appeler comme le module. Exemple => nom de class Defender | nom du module mod_defender
@@ -43,11 +44,13 @@ class Defender():
nickname: str nickname: str
username: str username: str
hostname: str hostname: str
realname: str
umodes: str umodes: str
vhost: str vhost: str
ip: str ip: str
score: int score: int
isWebirc: bool isWebirc: bool
isWebsocket: bool
secret_code: str secret_code: str
connected_datetime: str connected_datetime: str
updated_datetime: str updated_datetime: str
@@ -107,12 +110,13 @@ class Defender():
# self.join_saved_channels() # self.join_saved_channels()
self.timeout = self.Config.API_TIMEOUT self.timeout = self.Config.API_TIMEOUT
# Listes qui vont contenir les ip a scanner avec les différentes API # Listes qui vont contenir les ip a scanner avec les différentes API
self.freeipapi_remote_ip:list = [] self.abuseipdb_UserModel: list[User.UserModel] = []
self.cloudfilt_remote_ip:list = [] self.freeipapi_UserModel: list[User.UserModel] = []
self.abuseipdb_remote_ip:list = [] self.cloudfilt_UserModel: list[User.UserModel] = []
self.psutil_remote_ip:list = [] self.psutil_UserModel: list[User.UserModel] = []
self.localscan_remote_ip:list = [] self.localscan_UserModel: list[User.UserModel] = []
# Variables qui indique que les threads sont en cours d'éxecutions # Variables qui indique que les threads sont en cours d'éxecutions
self.abuseipdb_isRunning:bool = True self.abuseipdb_isRunning:bool = True
@@ -140,6 +144,9 @@ class Defender():
self.Base.create_thread(func=self.thread_psutil_scan) self.Base.create_thread(func=self.thread_psutil_scan)
self.Base.create_thread(func=self.thread_reputation_timer) self.Base.create_thread(func=self.thread_reputation_timer)
if self.ModConfig.reputation == 1:
self.Irc.send2socket(f":{self.Config.SERVICE_ID} SAMODE {self.Config.SALON_JAIL} +{self.Config.SERVICE_UMODES} {self.Config.SERVICE_NICKNAME}")
return None return None
def __set_commands(self, commands:dict[int, list[str]]) -> None: def __set_commands(self, commands:dict[int, list[str]]) -> None:
@@ -223,11 +230,11 @@ class Defender():
"""Cette methode sera executée a chaque désactivation ou """Cette methode sera executée a chaque désactivation ou
rechargement de module rechargement de module
""" """
self.abuseipdb_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec abuseipdb self.abuseipdb_UserModel: list[User.UserModel] = []
self.freeipapi_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec freeipapi self.freeipapi_UserModel: list[User.UserModel] = []
self.cloudfilt_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec cloudfilt self.cloudfilt_UserModel: list[User.UserModel] = []
self.psutil_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec psutil_scan self.psutil_UserModel: list[User.UserModel] = []
self.localscan_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec local_scan self.localscan_UserModel: list[User.UserModel] = []
self.abuseipdb_isRunning:bool = False self.abuseipdb_isRunning:bool = False
self.freeipapi_isRunning:bool = False self.freeipapi_isRunning:bool = False
@@ -434,9 +441,9 @@ class Defender():
jailed_nickname = get_reputation.nickname jailed_nickname = get_reputation.nickname
jailed_score = get_reputation.score jailed_score = get_reputation.score
color_red = self.Config.CONFIG_COLOR['rouge'] color_red = self.Config.COLORS.red
color_black = self.Config.CONFIG_COLOR['noire'] color_black = self.Config.COLORS.black
color_bold = self.Config.CONFIG_COLOR['gras'] color_bold = self.Config.COLORS.bold
service_id = self.Config.SERVICE_ID service_id = self.Config.SERVICE_ID
service_prefix = self.Config.SERVICE_PREFIX service_prefix = self.Config.SERVICE_PREFIX
reputation_ban_all_chan = self.ModConfig.reputation_ban_all_chan reputation_ban_all_chan = self.ModConfig.reputation_ban_all_chan
@@ -471,8 +478,8 @@ class Defender():
ban_all_chan = self.ModConfig.reputation_ban_all_chan ban_all_chan = self.ModConfig.reputation_ban_all_chan
service_id = self.Config.SERVICE_ID service_id = self.Config.SERVICE_ID
dchanlog = self.Config.SERVICE_CHANLOG dchanlog = self.Config.SERVICE_CHANLOG
color_red = self.Config.CONFIG_COLOR['rouge'] color_red = self.Config.COLORS.red
color_black = self.Config.CONFIG_COLOR['noire'] color_black = self.Config.COLORS.black
salon_jail = self.Config.SALON_JAIL salon_jail = self.Config.SALON_JAIL
if reputation_flag == 0: if reputation_flag == 0:
@@ -487,6 +494,7 @@ class Defender():
if self.get_user_uptime_in_minutes(user.uid) >= reputation_timer and int(user.score) <= int(reputation_seuil): if self.get_user_uptime_in_minutes(user.uid) >= reputation_timer and int(user.score) <= int(reputation_seuil):
self.Irc.send2socket(f":{service_id} PRIVMSG {dchanlog} :[{color_red} REPUTATION {color_black}] : Action sur {user.nickname} aprés {str(reputation_timer)} minutes d'inactivité") self.Irc.send2socket(f":{service_id} PRIVMSG {dchanlog} :[{color_red} REPUTATION {color_black}] : Action sur {user.nickname} aprés {str(reputation_timer)} minutes d'inactivité")
self.Irc.send2socket(f":{service_id} KILL {user.nickname} After {str(reputation_timer)} minutes of inactivity you should reconnect and type the password code ") self.Irc.send2socket(f":{service_id} KILL {user.nickname} After {str(reputation_timer)} minutes of inactivity you should reconnect and type the password code ")
self.Irc.send2socket(f":{self.Config.SERVEUR_LINK} REPUTATION {user.ip} 0")
self.Logs.info(f"Nickname: {user.nickname} KILLED after {str(reputation_timer)} minutes of inactivity") self.Logs.info(f"Nickname: {user.nickname} KILLED after {str(reputation_timer)} minutes of inactivity")
uid_to_clean.append(user.uid) uid_to_clean.append(user.uid)
@@ -551,8 +559,8 @@ class Defender():
flood_timer = self.ModConfig.flood_timer flood_timer = self.ModConfig.flood_timer
service_id = self.Config.SERVICE_ID service_id = self.Config.SERVICE_ID
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME
color_red = self.Config.CONFIG_COLOR['rouge'] color_red = self.Config.COLORS.red
color_bold = self.Config.CONFIG_COLOR['gras'] color_bold = self.Config.COLORS.bold
get_detected_uid = self.User.get_uid(detected_user) get_detected_uid = self.User.get_uid(detected_user)
get_detected_nickname = self.User.get_nickname(detected_user) get_detected_nickname = self.User.get_nickname(detected_user)
@@ -601,28 +609,38 @@ class Defender():
return None return None
def scan_ports(self, remote_ip: str) -> None: def scan_ports(self, userModel: User.UserModel) -> None:
"""local_scan """local_scan
Args: Args:
remote_ip (str): _description_ userModel (UserModel): _description_
""" """
User = userModel
remote_ip = User.remote_ip
username = User.username
hostname = User.hostname
nickname = User.nickname
fullname = f'{nickname}!{username}@{hostname}'
if remote_ip in self.Config.WHITELISTED_IP: if remote_ip in self.Config.WHITELISTED_IP:
return None return None
for port in self.Config.PORTS_TO_SCAN: for port in self.Config.PORTS_TO_SCAN:
try:
newSocket = '' newSocket = ''
newSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK) newSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK)
newSocket.settimeout(0.5) newSocket.settimeout(0.5)
try:
connection = (remote_ip, self.Base.int_if_possible(port)) connection = (remote_ip, self.Base.int_if_possible(port))
newSocket.connect(connection) newSocket.connect(connection)
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}PROXY_SCAN{self.Config.CONFIG_COLOR['noire']} ] : Port [{str(port)}] ouvert sur l'adresse ip [{remote_ip}]")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.COLORS.red}PROXY_SCAN{self.Config.COLORS.black} ] {fullname} ({remote_ip}) : Port [{str(port)}] ouvert sur l'adresse ip [{remote_ip}]")
# print(f"=======> Le port {str(port)} est ouvert !!") # print(f"=======> Le port {str(port)} est ouvert !!")
self.Base.running_sockets.append(newSocket) self.Base.running_sockets.append(newSocket)
# print(newSocket) # print(newSocket)
newSocket.shutdown(socket.SHUT_RDWR) newSocket.shutdown(socket.SHUT_RDWR)
newSocket.close() newSocket.close()
except (socket.timeout, ConnectionRefusedError): except (socket.timeout, ConnectionRefusedError):
self.Logs.info(f"Le port {remote_ip}:{str(port)} est fermé") self.Logs.info(f"Le port {remote_ip}:{str(port)} est fermé")
except AttributeError as ae: except AttributeError as ae:
@@ -634,20 +652,18 @@ class Defender():
newSocket.close() newSocket.close()
self.Logs.info('=======> Fermeture de la socket') self.Logs.info('=======> Fermeture de la socket')
pass
def thread_local_scan(self) -> None: def thread_local_scan(self) -> None:
try: try:
while self.localscan_isRunning: while self.localscan_isRunning:
list_to_remove:list = [] list_to_remove:list = []
for ip in self.localscan_remote_ip: for user in self.localscan_UserModel:
self.scan_ports(ip) self.scan_ports(user)
list_to_remove.append(ip) list_to_remove.append(user)
time.sleep(1) time.sleep(1)
for ip_to_remove in list_to_remove: for user_model in list_to_remove:
self.localscan_remote_ip.remove(ip_to_remove) self.localscan_UserModel.remove(user_model)
time.sleep(1) time.sleep(1)
@@ -655,23 +671,33 @@ class Defender():
except ValueError as ve: except ValueError as ve:
self.Logs.warning(f"thread_local_scan Error : {ve}") self.Logs.warning(f"thread_local_scan Error : {ve}")
def get_ports_connexion(self, remote_ip: str) -> list[int]: def get_ports_connexion(self, userModel: User.UserModel) -> list[int]:
"""psutil_scan for Linux """psutil_scan for Linux (should be run on the same location as the unrealircd server)
Args: Args:
remote_ip (str): The remote ip address userModel (UserModel): The User Model Object
Returns: Returns:
list[int]: list of ports list[int]: list of ports
""" """
try: try:
User = userModel
remote_ip = User.remote_ip
username = User.username
hostname = User.hostname
nickname = User.nickname
if remote_ip in self.Config.WHITELISTED_IP: if remote_ip in self.Config.WHITELISTED_IP:
return None return None
connections = psutil.net_connections(kind='inet') connections = psutil.net_connections(kind='inet')
fullname = f'{nickname}!{username}@{hostname}'
matching_ports = [conn.raddr.port for conn in connections if conn.raddr and conn.raddr.ip == remote_ip] matching_ports = [conn.raddr.port for conn in connections if conn.raddr and conn.raddr.ip == remote_ip]
self.Logs.info(f"Connexion of {remote_ip} using ports : {str(matching_ports)}") self.Logs.info(f"Connexion of {fullname} ({remote_ip}) using ports : {str(matching_ports)}")
if matching_ports:
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.COLORS.red}PSUTIL_SCAN{self.Config.COLORS.black} ] {fullname} ({remote_ip}) : is using ports {matching_ports}")
return matching_ports return matching_ports
@@ -684,13 +710,13 @@ class Defender():
while self.psutil_isRunning: while self.psutil_isRunning:
list_to_remove:list = [] list_to_remove:list = []
for ip in self.psutil_remote_ip: for user in self.psutil_UserModel:
self.get_ports_connexion(ip) self.get_ports_connexion(user)
list_to_remove.append(ip) list_to_remove.append(user)
time.sleep(1) time.sleep(1)
for ip_to_remove in list_to_remove: for user_model in list_to_remove:
self.psutil_remote_ip.remove(ip_to_remove) self.psutil_UserModel.remove(user_model)
time.sleep(1) time.sleep(1)
@@ -698,16 +724,22 @@ class Defender():
except ValueError as ve: except ValueError as ve:
self.Logs.warning(f"thread_psutil_scan Error : {ve}") self.Logs.warning(f"thread_psutil_scan Error : {ve}")
def abuseipdb_scan(self, remote_ip:str) -> Union[dict[str, any], None]: def abuseipdb_scan(self, userModel: User.UserModel) -> Union[dict[str, any], None]:
"""Analyse l'ip avec AbuseIpDB """Analyse l'ip avec AbuseIpDB
Cette methode devra etre lancer toujours via un thread ou un timer. Cette methode devra etre lancer toujours via un thread ou un timer.
Args: Args:
remote_ip (_type_): l'ip a analyser userModel (UserModel): l'objet User qui contient l'ip
Returns: Returns:
dict[str, any] | None: les informations du provider dict[str, any] | None: les informations du provider
keys : 'score', 'country', 'isTor', 'totalReports' keys : 'score', 'country', 'isTor', 'totalReports'
""" """
User = userModel
remote_ip = User.remote_ip
username = User.username
hostname = User.hostname
nickname = User.nickname
if remote_ip in self.Config.WHITELISTED_IP: if remote_ip in self.Config.WHITELISTED_IP:
return None return None
if self.ModConfig.abuseipdb_scan == 0: if self.ModConfig.abuseipdb_scan == 0:
@@ -727,11 +759,12 @@ class Defender():
'Key': self.abuseipdb_key 'Key': self.abuseipdb_key
} }
try:
response = requests.request(method='GET', url=url, headers=headers, params=querystring, timeout=self.timeout) response = requests.request(method='GET', url=url, headers=headers, params=querystring, timeout=self.timeout)
# Formatted output # Formatted output
decodedResponse = json.loads(response.text) decodedResponse = json.loads(response.text)
try:
if not 'data' in decodedResponse: if not 'data' in decodedResponse:
return None return None
@@ -744,10 +777,13 @@ class Defender():
service_id = self.Config.SERVICE_ID service_id = self.Config.SERVICE_ID
service_chanlog = self.Config.SERVICE_CHANLOG service_chanlog = self.Config.SERVICE_CHANLOG
color_red = self.Config.CONFIG_COLOR['rouge'] color_red = self.Config.COLORS.red
color_black = self.Config.CONFIG_COLOR['noire'] color_black = self.Config.COLORS.black
self.Irc.send2socket(f":{service_id} PRIVMSG {service_chanlog} :[ {color_red}ABUSEIPDB_SCAN{color_black} ] : Connexion de {remote_ip} ==> Score: {str(result['score'])} | Country : {result['country']} | Tor : {str(result['isTor'])} | Total Reports : {str(result['totalReports'])}") # pseudo!ident@host
fullname = f'{nickname}!{username}@{hostname}'
self.Irc.send2socket(f":{service_id} PRIVMSG {service_chanlog} :[ {color_red}ABUSEIPDB_SCAN{color_black} ] : Connexion de {fullname} ({remote_ip}) ==> Score: {str(result['score'])} | Country : {result['country']} | Tor : {str(result['isTor'])} | Total Reports : {str(result['totalReports'])}")
if result['isTor']: if result['isTor']:
self.Irc.send2socket(f":{service_id} GLINE +*@{remote_ip} {self.Config.GLINE_DURATION} This server do not allow Tor connexions {str(result['isTor'])} - Detected by Abuseipdb") self.Irc.send2socket(f":{service_id} GLINE +*@{remote_ip} {self.Config.GLINE_DURATION} This server do not allow Tor connexions {str(result['isTor'])} - Detected by Abuseipdb")
@@ -763,6 +799,8 @@ class Defender():
self.Logs.error(f"AbuseIpDb Timeout : {rt}") self.Logs.error(f"AbuseIpDb Timeout : {rt}")
except requests.ConnectionError as ce: except requests.ConnectionError as ce:
self.Logs.error(f"AbuseIpDb Connection Error : {ce}") self.Logs.error(f"AbuseIpDb Connection Error : {ce}")
except Exception as err:
self.Logs.error(f"General Error Abuseipdb : {err}")
def thread_abuseipdb_scan(self) -> None: def thread_abuseipdb_scan(self) -> None:
try: try:
@@ -770,13 +808,13 @@ class Defender():
while self.abuseipdb_isRunning: while self.abuseipdb_isRunning:
list_to_remove: list = [] list_to_remove: list = []
for ip in self.abuseipdb_remote_ip: for user in self.abuseipdb_UserModel:
self.abuseipdb_scan(ip) self.abuseipdb_scan(user)
list_to_remove.append(ip) list_to_remove.append(user)
time.sleep(1) time.sleep(1)
for ip_to_remove in list_to_remove: for user_model in list_to_remove:
self.abuseipdb_remote_ip.remove(ip_to_remove) self.abuseipdb_UserModel.remove(user_model)
time.sleep(1) time.sleep(1)
@@ -784,7 +822,7 @@ class Defender():
except ValueError as ve: except ValueError as ve:
self.Logs.error(f"thread_abuseipdb_scan Error : {ve}") self.Logs.error(f"thread_abuseipdb_scan Error : {ve}")
def freeipapi_scan(self, remote_ip:str) -> Union[dict[str, any], None]: def freeipapi_scan(self, userModel: User.UserModel) -> Union[dict[str, any], None]:
"""Analyse l'ip avec Freeipapi """Analyse l'ip avec Freeipapi
Cette methode devra etre lancer toujours via un thread ou un timer. Cette methode devra etre lancer toujours via un thread ou un timer.
Args: Args:
@@ -794,6 +832,12 @@ class Defender():
dict[str, any] | None: les informations du provider dict[str, any] | None: les informations du provider
keys : 'countryCode', 'isProxy' keys : 'countryCode', 'isProxy'
""" """
User = userModel
remote_ip = User.remote_ip
username = User.username
hostname = User.hostname
nickname = User.nickname
if remote_ip in self.Config.WHITELISTED_IP: if remote_ip in self.Config.WHITELISTED_IP:
return None return None
if self.ModConfig.freeipapi_scan == 0: if self.ModConfig.freeipapi_scan == 0:
@@ -801,8 +845,8 @@ class Defender():
service_id = self.Config.SERVICE_ID service_id = self.Config.SERVICE_ID
service_chanlog = self.Config.SERVICE_CHANLOG service_chanlog = self.Config.SERVICE_CHANLOG
color_red = self.Config.CONFIG_COLOR['rouge'] color_red = self.Config.COLORS.red
color_black = self.Config.CONFIG_COLOR['noire'] color_black = self.Config.COLORS.black
url = f'https://freeipapi.com/api/json/{remote_ip}' url = f'https://freeipapi.com/api/json/{remote_ip}'
@@ -810,11 +854,12 @@ class Defender():
'Accept': 'application/json', 'Accept': 'application/json',
} }
try:
response = requests.request(method='GET', url=url, headers=headers, timeout=self.timeout) response = requests.request(method='GET', url=url, headers=headers, timeout=self.timeout)
# Formatted output # Formatted output
decodedResponse = json.loads(response.text) decodedResponse = json.loads(response.text)
try:
status_code = response.status_code status_code = response.status_code
if status_code == 429: if status_code == 429:
self.Logs.warning(f'Too Many Requests - The rate limit for the API has been exceeded.') self.Logs.warning(f'Too Many Requests - The rate limit for the API has been exceeded.')
@@ -828,7 +873,10 @@ class Defender():
'isProxy': decodedResponse['isProxy'] if 'isProxy' in decodedResponse else None 'isProxy': decodedResponse['isProxy'] if 'isProxy' in decodedResponse else None
} }
self.Irc.send2socket(f":{service_id} PRIVMSG {service_chanlog} :[ {color_red}FREEIPAPI_SCAN{color_black} ] : Connexion de {remote_ip} ==> Proxy: {str(result['isProxy'])} | Country : {str(result['countryCode'])}") # pseudo!ident@host
fullname = f'{nickname}!{username}@{hostname}'
self.Irc.send2socket(f":{service_id} PRIVMSG {service_chanlog} :[ {color_red}FREEIPAPI_SCAN{color_black} ] : Connexion de {fullname} ({remote_ip}) ==> Proxy: {str(result['isProxy'])} | Country : {str(result['countryCode'])}")
if result['isProxy']: if result['isProxy']:
self.Irc.send2socket(f":{service_id} GLINE +*@{remote_ip} {self.Config.GLINE_DURATION} This server do not allow proxy connexions {str(result['isProxy'])} - detected by freeipapi") self.Irc.send2socket(f":{service_id} GLINE +*@{remote_ip} {self.Config.GLINE_DURATION} This server do not allow proxy connexions {str(result['isProxy'])} - detected by freeipapi")
@@ -837,20 +885,22 @@ class Defender():
return result return result
except KeyError as ke: except KeyError as ke:
self.Logs.error(f"FREEIPAPI_SCAN KeyError : {ke}") self.Logs.error(f"FREEIPAPI_SCAN KeyError : {ke}")
except Exception as err:
self.Logs.error(f"General Error Freeipapi : {err}")
def thread_freeipapi_scan(self) -> None: def thread_freeipapi_scan(self) -> None:
try: try:
while self.freeipapi_isRunning: while self.freeipapi_isRunning:
list_to_remove:list = [] list_to_remove: list[User.UserModel] = []
for ip in self.freeipapi_remote_ip: for user in self.freeipapi_UserModel:
self.freeipapi_scan(ip) self.freeipapi_scan(user)
list_to_remove.append(ip) list_to_remove.append(user)
time.sleep(1) time.sleep(1)
for ip_to_remove in list_to_remove: for user_model in list_to_remove:
self.freeipapi_remote_ip.remove(ip_to_remove) self.freeipapi_UserModel.remove(user_model)
time.sleep(1) time.sleep(1)
@@ -858,7 +908,7 @@ class Defender():
except ValueError as ve: except ValueError as ve:
self.Logs.error(f"thread_freeipapi_scan Error : {ve}") self.Logs.error(f"thread_freeipapi_scan Error : {ve}")
def cloudfilt_scan(self, remote_ip:str) -> Union[dict[str, any], None]: def cloudfilt_scan(self, userModel: User.UserModel) -> Union[dict[str, any], None]:
"""Analyse l'ip avec cloudfilt """Analyse l'ip avec cloudfilt
Cette methode devra etre lancer toujours via un thread ou un timer. Cette methode devra etre lancer toujours via un thread ou un timer.
Args: Args:
@@ -868,6 +918,12 @@ class Defender():
dict[str, any] | None: les informations du provider dict[str, any] | None: les informations du provider
keys : 'countryCode', 'isProxy' keys : 'countryCode', 'isProxy'
""" """
User = userModel
remote_ip = User.remote_ip
username = User.username
hostname = User.hostname
nickname = User.nickname
if remote_ip in self.Config.WHITELISTED_IP: if remote_ip in self.Config.WHITELISTED_IP:
return None return None
if self.ModConfig.cloudfilt_scan == 0: if self.ModConfig.cloudfilt_scan == 0:
@@ -877,8 +933,8 @@ class Defender():
service_id = self.Config.SERVICE_ID service_id = self.Config.SERVICE_ID
service_chanlog = self.Config.SERVICE_CHANLOG service_chanlog = self.Config.SERVICE_CHANLOG
color_red = self.Config.CONFIG_COLOR['rouge'] color_red = self.Config.COLORS.red
color_black = self.Config.CONFIG_COLOR['noire'] color_black = self.Config.COLORS.black
url = f"https://developers18334.cloudfilt.com/" url = f"https://developers18334.cloudfilt.com/"
@@ -887,11 +943,10 @@ class Defender():
'key': self.cloudfilt_key 'key': self.cloudfilt_key
} }
try:
response = requests.post(url=url, data=data) response = requests.post(url=url, data=data)
# Formatted output # Formatted output
decodedResponse = json.loads(response.text) decodedResponse = json.loads(response.text)
try:
status_code = response.status_code status_code = response.status_code
if status_code != 200: if status_code != 200:
self.Logs.warning(f'Error connecting to cloudfilt API | Code: {str(status_code)}') self.Logs.warning(f'Error connecting to cloudfilt API | Code: {str(status_code)}')
@@ -904,7 +959,10 @@ class Defender():
'host': decodedResponse['host'] if 'host' in decodedResponse else None 'host': decodedResponse['host'] if 'host' in decodedResponse else None
} }
self.Irc.send2socket(f":{service_id} PRIVMSG {service_chanlog} :[ {color_red}CLOUDFILT_SCAN{color_black} ] : Connexion de {str(remote_ip)} ==> Host: {str(result['host'])} | country: {str(result['countryiso'])} | listed: {str(result['listed'])} | listed by : {str(result['listed_by'])}") # pseudo!ident@host
fullname = f'{nickname}!{username}@{hostname}'
self.Irc.send2socket(f":{service_id} PRIVMSG {service_chanlog} :[ {color_red}CLOUDFILT_SCAN{color_black} ] : Connexion de {fullname} ({remote_ip}) ==> Host: {str(result['host'])} | country: {str(result['countryiso'])} | listed: {str(result['listed'])} | listed by : {str(result['listed_by'])}")
if result['listed']: if result['listed']:
self.Irc.send2socket(f":{service_id} GLINE +*@{remote_ip} {self.Config.GLINE_DURATION} You connexion is listed as dangerous {str(result['listed'])} {str(result['listed_by'])} - detected by cloudfilt") self.Irc.send2socket(f":{service_id} GLINE +*@{remote_ip} {self.Config.GLINE_DURATION} You connexion is listed as dangerous {str(result['listed'])} {str(result['listed_by'])} - detected by cloudfilt")
@@ -922,13 +980,13 @@ class Defender():
while self.cloudfilt_isRunning: while self.cloudfilt_isRunning:
list_to_remove:list = [] list_to_remove:list = []
for ip in self.cloudfilt_remote_ip: for user in self.cloudfilt_UserModel:
self.cloudfilt_scan(ip) self.cloudfilt_scan(user)
list_to_remove.append(ip) list_to_remove.append(user)
time.sleep(1) time.sleep(1)
for ip_to_remove in list_to_remove: for user_model in list_to_remove:
self.cloudfilt_remote_ip.remove(ip_to_remove) self.cloudfilt_UserModel.remove(user_model)
time.sleep(1) time.sleep(1)
@@ -946,6 +1004,10 @@ class Defender():
match cmd[1]: match cmd[1]:
case 'EOS':
if self.Irc.INIT == 0:
self.Irc.send2socket(f":{service_id} SAMODE {self.Config.SALON_JAIL} +{self.Config.SERVICE_UMODES} {self.Config.SERVICE_NICKNAME}")
case 'REPUTATION': case 'REPUTATION':
# :001 REPUTATION 91.168.141.239 118 # :001 REPUTATION 91.168.141.239 118
try: try:
@@ -958,22 +1020,6 @@ class Defender():
if not self.Base.is_valid_ip(cmd[2]): if not self.Base.is_valid_ip(cmd[2]):
return None return None
# self.Base.scan_ports(cmd[2])
if self.ModConfig.local_scan == 1 and not cmd[2] in self.Config.WHITELISTED_IP:
self.localscan_remote_ip.append(cmd[2])
if self.ModConfig.psutil_scan == 1 and not cmd[2] in self.Config.WHITELISTED_IP:
self.psutil_remote_ip.append(cmd[2])
if self.ModConfig.abuseipdb_scan == 1 and not cmd[2] in self.Config.WHITELISTED_IP:
self.abuseipdb_remote_ip.append(cmd[2])
if self.ModConfig.freeipapi_scan == 1 and not cmd[2] in self.Config.WHITELISTED_IP:
self.freeipapi_remote_ip.append(cmd[2])
if self.ModConfig.cloudfilt_scan == 1 and not cmd[2] in self.Config.WHITELISTED_IP:
self.cloudfilt_remote_ip.append(cmd[2])
# Possibilité de déclancher les bans a ce niveau. # Possibilité de déclancher les bans a ce niveau.
except IndexError as ie: except IndexError as ie:
self.Logs.error(f'cmd reputation: index error: {ie}') self.Logs.error(f'cmd reputation: index error: {ie}')
@@ -997,6 +1043,15 @@ class Defender():
# Get User information # Get User information
_User = self.User.get_User(str(cmd[7])) _User = self.User.get_User(str(cmd[7]))
# If user is not service or IrcOp then scan them
if not re.match(fr'^.*[S|o?].*$', _User.umodes):
self.abuseipdb_UserModel.append(_User) if self.ModConfig.abuseipdb_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.freeipapi_UserModel.append(_User) if self.ModConfig.freeipapi_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.cloudfilt_UserModel.append(_User) if self.ModConfig.cloudfilt_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.psutil_UserModel.append(_User) if self.ModConfig.psutil_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.localscan_UserModel.append(_User) if self.ModConfig.local_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
if _User is None: if _User is None:
self.Logs.critical(f'This UID: [{cmd[7]}] is not available please check why') self.Logs.critical(f'This UID: [{cmd[7]}] is not available please check why')
return None return None
@@ -1011,9 +1066,9 @@ class Defender():
currentDateTime = self.Base.get_datetime() currentDateTime = self.Base.get_datetime()
self.reputation_insert( self.reputation_insert(
self.ReputationModel( self.ReputationModel(
uid=_User.uid, nickname=_User.nickname, username=_User.username, hostname=_User.hostname, uid=_User.uid, nickname=_User.nickname, username=_User.username, realname=_User.realname,
umodes=_User.umodes, vhost=_User.vhost, ip=_User.remote_ip, score=_User.score_connexion, hostname=_User.hostname, umodes=_User.umodes, vhost=_User.vhost, ip=_User.remote_ip, score=_User.score_connexion,
secret_code=self.Base.get_random(8), isWebirc=_User.isWebirc, connected_datetime=currentDateTime, secret_code=self.Base.get_random(8), isWebirc=_User.isWebirc, isWebsocket=_User.isWebsocket, connected_datetime=currentDateTime,
updated_datetime=currentDateTime updated_datetime=currentDateTime
) )
) )
@@ -1036,6 +1091,9 @@ class Defender():
get_reputation = self.reputation_get_Reputation(parsed_UID) get_reputation = self.reputation_get_Reputation(parsed_UID)
self.Irc.send2socket(f":{service_id} MODE {parsed_chan} +b ~security-group:unknown-users")
self.Irc.send2socket(f":{service_id} MODE {parsed_chan} +eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users")
if not get_reputation is None: if not get_reputation is None:
isWebirc = get_reputation.isWebirc isWebirc = get_reputation.isWebirc
@@ -1059,20 +1117,20 @@ class Defender():
if not self.Base.is_valid_ip(cmd[7]): if not self.Base.is_valid_ip(cmd[7]):
return None return None
if self.ModConfig.local_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP: # if self.ModConfig.local_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
self.localscan_remote_ip.append(cmd[7]) # self.localscan_remote_ip.append(cmd[7])
if self.ModConfig.psutil_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP: # if self.ModConfig.psutil_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
self.psutil_remote_ip.append(cmd[7]) # self.psutil_remote_ip.append(cmd[7])
if self.ModConfig.abuseipdb_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP: # if self.ModConfig.abuseipdb_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
self.abuseipdb_remote_ip.append(cmd[7]) # self.abuseipdb_remote_ip.append(cmd[7])
if self.ModConfig.freeipapi_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP: # if self.ModConfig.freeipapi_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
self.freeipapi_remote_ip.append(cmd[7]) # self.freeipapi_remote_ip.append(cmd[7])
if self.ModConfig.cloudfilt_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP: # if self.ModConfig.cloudfilt_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
self.cloudfilt_remote_ip.append(cmd[7]) # self.cloudfilt_remote_ip.append(cmd[7])
case 'NICK': case 'NICK':
# :0010BS24L NICK [NEWNICK] 1697917711 # :0010BS24L NICK [NEWNICK] 1697917711
@@ -1175,8 +1233,8 @@ class Defender():
self.Logs.debug(f"IP de {jailed_nickname} : {jailed_IP}") self.Logs.debug(f"IP de {jailed_nickname} : {jailed_IP}")
link = self.Config.SERVEUR_LINK link = self.Config.SERVEUR_LINK
color_green = self.Config.CONFIG_COLOR['verte'] color_green = self.Config.COLORS.green
color_black = self.Config.CONFIG_COLOR['noire'] color_black = self.Config.COLORS.black
if release_code == get_reputation.secret_code: if release_code == get_reputation.secret_code:
self.Irc.send2socket(f':{dnickname} PRIVMSG {jailed_salon} : Bon mot de passe. Allez du vent !') self.Irc.send2socket(f':{dnickname} PRIVMSG {jailed_salon} : Bon mot de passe. Allez du vent !')
@@ -1220,13 +1278,13 @@ class Defender():
if activation == 'on': if activation == 'on':
if self.ModConfig.reputation == 1: if self.ModConfig.reputation == 1:
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.COLORS.green}REPUTATION{self.Config.COLORS.black} ] : Already activated")
return False return False
# self.update_db_configuration('reputation', 1) # self.update_db_configuration('reputation', 1)
self.__update_configuration(key, 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.COLORS.green}REPUTATION{self.Config.COLORS.black} ] : 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}")
self.Irc.send2socket(f":{service_id} MODE {jail_chan} +{jail_chan_mode}") self.Irc.send2socket(f":{service_id} MODE {jail_chan} +{jail_chan_mode}")
@@ -1235,21 +1293,19 @@ class Defender():
for chan in self.Channel.UID_CHANNEL_DB: for chan in self.Channel.UID_CHANNEL_DB:
if chan.name != jail_chan: if chan.name != jail_chan:
self.Irc.send2socket(f":{service_id} MODE {chan.name} +b ~security-group:unknown-users") self.Irc.send2socket(f":{service_id} MODE {chan.name} +b ~security-group:unknown-users")
self.Irc.send2socket(f":{service_id} MODE {chan.name} +e ~security-group:webirc-users") self.Irc.send2socket(f":{service_id} MODE {chan.name} +eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-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.Base.db_query_channel('add', self.module_name, jail_chan) self.Base.db_query_channel('add', self.module_name, jail_chan)
if activation == 'off': if activation == 'off':
if self.ModConfig.reputation == 0: if self.ModConfig.reputation == 0:
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.COLORS.green}REPUTATION{self.Config.COLORS.black} ] : Already deactivated")
return False return False
self.__update_configuration(key, 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.COLORS.red}REPUTATION{self.Config.COLORS.black} ] : 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")
self.Irc.send2socket(f":{service_id} PART {jail_chan}") self.Irc.send2socket(f":{service_id} PART {jail_chan}")
@@ -1279,23 +1335,23 @@ class Defender():
if get_value == 'on': if get_value == 'on':
if self.ModConfig.reputation_ban_all_chan == 1: if self.ModConfig.reputation_ban_all_chan == 1:
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.COLORS.red}BAN ON ALL CHANS{self.Config.COLORS.black} ] : Already activated")
return False return False
# self.update_db_configuration(key, 1) # self.update_db_configuration(key, 1)
self.__update_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.COLORS.green}BAN ON ALL CHANS{self.Config.COLORS.black} ] : Activated by {fromuser}')
elif get_value == 'off': elif get_value == 'off':
if self.ModConfig.reputation_ban_all_chan == 0: if self.ModConfig.reputation_ban_all_chan == 0:
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.COLORS.red}BAN ON ALL CHANS{self.Config.COLORS.black} ] : Already deactivated")
return False return False
# self.update_db_configuration(key, 0) # self.update_db_configuration(key, 0)
self.__update_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.COLORS.green}BAN ON ALL CHANS{self.Config.COLORS.black} ] : Deactivated by {fromuser}')
case 'limit': case 'limit':
reputation_seuil = int(cmd[3]) reputation_seuil = int(cmd[3])
@@ -1304,7 +1360,7 @@ class Defender():
# self.update_db_configuration(key, reputation_seuil) # self.update_db_configuration(key, reputation_seuil)
self.__update_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.COLORS.green}REPUTATION SEUIL{self.Config.COLORS.black} ] : 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}')
case 'timer': case 'timer':
@@ -1312,7 +1368,7 @@ class Defender():
key = 'reputation_timer' key = 'reputation_timer'
self.__update_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.COLORS.green}REPUTATION TIMER{self.Config.COLORS.black} ] : 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':
@@ -1320,7 +1376,7 @@ class Defender():
key = 'reputation_score_after_release' key = 'reputation_score_after_release'
self.__update_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.COLORS.green}REPUTATION SCORE AFTER RELEASE{self.Config.COLORS.black} ] : 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':
@@ -1328,7 +1384,7 @@ class Defender():
key = 'reputation_sg' key = 'reputation_sg'
self.__update_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.COLORS.green}REPUTATION SECURITY-GROUP{self.Config.COLORS.black} ] : 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}')
case _: case _:
@@ -1358,9 +1414,9 @@ class Defender():
# .proxy_scan set psutil_scan on/off --> Active les informations de connexion a la machine locale # .proxy_scan set psutil_scan on/off --> Active les informations de connexion a la machine locale
# .proxy_scan set abuseipdb_scan on/off --> Active le scan via l'api abuseipdb # .proxy_scan set abuseipdb_scan on/off --> Active le scan via l'api abuseipdb
len_cmd = len(cmd) len_cmd = len(cmd)
color_green = self.Config.CONFIG_COLOR['verte'] color_green = self.Config.COLORS.green
color_red = self.Config.CONFIG_COLOR['rouge'] color_red = self.Config.COLORS.red
color_black = self.Config.CONFIG_COLOR['noire'] color_black = self.Config.COLORS.black
if len_cmd == 4: if len_cmd == 4:
set_key = str(cmd[1]).lower() set_key = str(cmd[1]).lower()
@@ -1492,21 +1548,21 @@ class Defender():
key = 'flood' key = 'flood'
if activation == 'on': if activation == 'on':
if self.ModConfig.flood == 1: if self.ModConfig.flood == 1:
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.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Already activated")
return False return False
self.__update_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.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Activated by {fromuser}")
if activation == 'off': if activation == 'off':
if self.ModConfig.flood == 0: if self.ModConfig.flood == 0:
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.COLORS.red}FLOOD{self.Config.COLORS.black} ] : Already Deactivated")
return False return False
self.__update_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.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Deactivated by {fromuser}")
if len_cmd == 4: if len_cmd == 4:
set_key = str(cmd[2]).lower() set_key = str(cmd[2]).lower()
@@ -1518,21 +1574,21 @@ class Defender():
set_value = int(cmd[3]) set_value = int(cmd[3])
self.__update_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 message set to {set_value} by {fromuser}") self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : 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_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.COLORS.green}FLOOD{self.Config.COLORS.black} ] : 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_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.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Flood timer set to {set_value} by {fromuser}")
case _: case _:
pass pass
@@ -1541,9 +1597,9 @@ class Defender():
self.Logs.error(f"{self.__class__.__name__} Value Error : {ve}") self.Logs.error(f"{self.__class__.__name__} Value Error : {ve}")
case 'status': case 'status':
color_green = self.Config.CONFIG_COLOR['verte'] color_green = self.Config.COLORS.green
color_red = self.Config.CONFIG_COLOR['rouge'] color_red = self.Config.COLORS.red
color_black = self.Config.CONFIG_COLOR['noire'] color_black = self.Config.COLORS.black
try: try:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : [{color_green if self.ModConfig.reputation == 1 else color_red}Reputation{color_black}] ==> {self.ModConfig.reputation}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : [{color_green if self.ModConfig.reputation == 1 else color_red}Reputation{color_black}] ==> {self.ModConfig.reputation}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : reputation_seuil ==> {self.ModConfig.reputation_seuil}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : reputation_seuil ==> {self.ModConfig.reputation_seuil}')
@@ -1570,14 +1626,25 @@ class Defender():
UserObject = self.User.get_User(nickoruid) UserObject = self.User.get_User(nickoruid)
if not UserObject is None: if not UserObject is None:
channels: list = []
for chan in self.Channel.UID_CHANNEL_DB:
for uid_in_chan in chan.uids:
if self.Base.clean_uid(uid_in_chan) == UserObject.uid:
channels.append(chan.name)
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : UID : {UserObject.uid}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : UID : {UserObject.uid}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : NICKNAME : {UserObject.nickname}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : NICKNAME : {UserObject.nickname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : USERNAME : {UserObject.username}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : USERNAME : {UserObject.username}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : REALNAME : {UserObject.realname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : HOSTNAME : {UserObject.hostname}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : HOSTNAME : {UserObject.hostname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : IP : {UserObject.remote_ip}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : REPUTATION : {UserObject.score_connexion}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : VHOST : {UserObject.vhost}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : VHOST : {UserObject.vhost}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : IP : {UserObject.remote_ip}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Country : {UserObject.geoip}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : WebIrc : {UserObject.isWebirc}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : WebWebsocket : {UserObject.isWebsocket}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : REPUTATION : {UserObject.score_connexion}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : MODES : {UserObject.umodes}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : MODES : {UserObject.umodes}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : CHANNELS : {channels}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : CONNECTION TIME : {UserObject.connexion_datetime}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : CONNECTION TIME : {UserObject.connexion_datetime}')
else: else:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} : This user {nickoruid} doesn't exist") self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} : This user {nickoruid} doesn't exist")

View File

@@ -152,4 +152,4 @@ class Test():
self.Logs.debug(f"Test logs ready") self.Logs.debug(f"Test logs ready")
except Exception as err: except Exception as err:
self.Logs.error(f"{err}") self.Logs.error(f"Unknown Error: {err}")

View File

@@ -48,8 +48,7 @@ class Votekick():
# Créer les nouvelles commandes du module # Créer les nouvelles commandes du module
self.commands_level = { self.commands_level = {
0: ['vote'], 0: ['vote']
1: ['activate', 'deactivate', 'submit', 'vote_stat', 'vote_verdict', 'vote_cancel']
} }
# Init the module # Init the module
@@ -60,6 +59,7 @@ class Votekick():
def __init_module(self) -> None: def __init_module(self) -> None:
# Add admin object to retrieve admin users
self.Admin = self.Irc.Admin self.Admin = self.Irc.Admin
self.__set_commands(self.commands_level) self.__set_commands(self.commands_level)
@@ -229,11 +229,11 @@ class Votekick():
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} :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} PRIVMSG {channel} :User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll be kicked from the channel')
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} :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') self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel')
# Init the system # Init the system
if self.init_vote_system(channel): if self.init_vote_system(channel):
@@ -264,6 +264,17 @@ class Votekick():
match command: match command:
case 'vote': case 'vote':
option = str(cmd[1]).lower() option = str(cmd[1]).lower()
if len(command) == 1:
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')
match option: match option:
case 'activate': case 'activate':
@@ -460,10 +471,10 @@ class Votekick():
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} :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} PRIVMSG {channel} :User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll be kicked from the channel')
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}")
elif chan.vote_for <= chan.vote_against: 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') self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel')
# Init the system # Init the system
if self.init_vote_system(channel): if self.init_vote_system(channel):
@@ -472,3 +483,13 @@ class Votekick():
self.Logs.error(f'{err}') self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}') 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} 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.1.5" "version": "5.3.0"
} }