mirror of
https://github.com/iio612/DEFENDER.git
synced 2026-02-13 19:24:23 +00:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd9713006a | ||
|
|
4825775b73 | ||
|
|
96ebf0511b | ||
|
|
91a2eafd82 | ||
|
|
a93d69214e | ||
|
|
b8cd2f244b | ||
|
|
395dec47be | ||
|
|
709e8d4419 | ||
|
|
e07b047b6a | ||
|
|
cbae3dce96 | ||
|
|
9d9ede0e80 | ||
|
|
860e265979 | ||
|
|
f7c80d190e | ||
|
|
9bfe5925f8 | ||
|
|
c3187e81dd | ||
|
|
f0c0a2d06a | ||
|
|
2be6ece27f | ||
|
|
6801c981ab | ||
|
|
fd88df1017 | ||
|
|
ad5b7ffbf2 | ||
|
|
2b7f1d8bf3 | ||
|
|
f5ff9259e8 | ||
|
|
12c7e5e832 | ||
|
|
3cdee5fddf | ||
|
|
f2b5c48fc9 | ||
|
|
e2a1ec5866 | ||
|
|
cc53eae875 | ||
|
|
8a80840a6a | ||
|
|
e25baea0ef | ||
|
|
ee02566343 | ||
|
|
11eedbb191 | ||
|
|
7e5e2d4643 | ||
|
|
7422bcad45 | ||
|
|
0cf1262d31 | ||
|
|
ff603ab2a4 | ||
|
|
d7503768b6 | ||
|
|
b5503d23d7 | ||
|
|
7be3f51bf4 | ||
|
|
2c0510b2a3 | ||
|
|
ee039322d4 | ||
|
|
8f08a1e77f | ||
|
|
c59dd16e87 | ||
|
|
0f31e67be6 | ||
|
|
3cd2077f63 | ||
|
|
9c78ad0860 | ||
|
|
487f9a2762 | ||
|
|
a7de16f7ad | ||
|
|
c1c0b480ce |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,7 +2,6 @@
|
||||
db/
|
||||
logs/
|
||||
__pycache__/
|
||||
mods/mod_jsonrpc.py
|
||||
configuration.json
|
||||
*.log
|
||||
test.py
|
||||
218
README.md
218
README.md
@@ -32,91 +32,205 @@ Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux u
|
||||
- Système d'exploitation Linux (Windows non supporté)
|
||||
- Un server UnrealIRCD corréctement configuré
|
||||
- Python version 3.10 ou supérieure
|
||||
|
||||
Bash:
|
||||
```bash
|
||||
# Bash
|
||||
$ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git
|
||||
- Renommer le fichier exemple_configuration.json en configuration.json
|
||||
- Configurer le fichier configuration.json
|
||||
# Renommer le fichier exemple_configuration.json en configuration.json
|
||||
# Configurer le fichier configuration.json
|
||||
$ python3 main.py
|
||||
|
||||
```
|
||||
Si votre configuration est bonne, votre service est censé etre connecté a votre réseau IRC
|
||||
Pour Les prochains lancement de defender vous devez utiliser la commande suivante:
|
||||
|
||||
Bash:
|
||||
$ systemctl --user [start | stop | restart | status] defender
|
||||
|
||||
```bash
|
||||
# Bash
|
||||
$ systemctl --user [start | stop | restart | status] defender
|
||||
```
|
||||
# Installation manuelle:
|
||||
Bash:
|
||||
$ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git
|
||||
$ cd IRC_DEFENDER_MODULES
|
||||
$ python3 -m venv .pyenv
|
||||
$ source .pyenv/bin/activate
|
||||
(pyenv)$ pip install sqlalchemy, psutil, requests, faker
|
||||
- Créer un service nommé "defender.service" pour votre service et placer le dans "/PATH/TO/USER/.config/systemd/user/"
|
||||
- Si le dossier n'existe pas il faut les créer
|
||||
$ sudo systemctl --user start defender
|
||||
```bash
|
||||
# Bash
|
||||
$ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git
|
||||
$ cd IRC_DEFENDER_MODULES
|
||||
$ python3 -m venv .pyenv
|
||||
$ source .pyenv/bin/activate
|
||||
(pyenv)$ pip install sqlalchemy, psutil, requests, faker, unrealircd_rpc_py
|
||||
|
||||
# Créer un service nommé "defender.service"
|
||||
# pour votre service et placer le dans "/PATH/TO/USER/.config/systemd/user/"
|
||||
# Si le dossier n'existe pas il faut les créer
|
||||
$ sudo systemctl --user start defender
|
||||
```
|
||||
# Configuration
|
||||
|
||||
```
|
||||
SERVEUR (Serveur)
|
||||
SERVEUR_IP: Adresse IP du serveur IRC à rejoindre.
|
||||
SERVEUR_HOSTNAME: Nom d'hôte du serveur IRC à rejoindre (optionnel).
|
||||
SERVEUR_LINK: Lien vers le serveur IRC (optionnel).
|
||||
SERVEUR_PORT: Port de connexion au serveur IRC.
|
||||
SERVEUR_PASSWORD: Mot de passe d'enregistrement du service sur le serveur IRC.
|
||||
SERVEUR_ID: Identifiant unique du service.
|
||||
SERVEUR_SSL: Active la connexion SSL sécurisée au serveur IRC (true/false).
|
||||
* 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_LINK: Lien vers le serveur IRC (optionnel).
|
||||
* SERVEUR_PORT: Port de connexion au serveur IRC.
|
||||
* SERVEUR_PASSWORD: Mot de passe d'enregistrement du service sur le serveur IRC.
|
||||
SERVEUR_ID: Identifiant unique du service. (default : 19Z)
|
||||
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.
|
||||
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.
|
||||
PASSWORD: Mot de passe de l'administrateur du service.
|
||||
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.
|
||||
SALON_JAIL_MODES: Modes appliqués au canal de prison.
|
||||
SALON_LIBERER: Canal utilisé pour la libération des utilisateurs sanctionnés.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
GLINE_DURATION: Durée de bannissement temporaire d'un utilisateur en minutes.
|
||||
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).
|
||||
|
||||
COULEURS (Couleurs)
|
||||
CONFIG_COLOR: Dictionnaire contenant des codes de couleurs IRC pour un meilleur affichage des messages.
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
## 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",
|
||||
|
||||
"JSONRPC_URL": "https://your.domaine.com:8600/api",
|
||||
"JSONRPC_PATH_TO_SOCKET_FILE": "/PATH/TO/YOUR/IRCD/data/rpc.socket",
|
||||
"JSONRPC_METHOD": "socket",
|
||||
"JSONRPC_USER": "YOUR_RPC_USER",
|
||||
"JSONRPC_PASSWORD": "YOUR_RPC_PASSWORD",
|
||||
|
||||
"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é.
|
||||
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; }
|
||||
}
|
||||
|
||||
/msg [NomDuService] auth [nickname] [password]
|
||||
-- Une fois identifié tapez la commande suivante
|
||||
/msg [NomDuService] editaccess [nickname] [Nouveau-Password] 5
|
||||
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:
|
||||
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).
|
||||
|
||||
48
config/exemple_configuration.json
Normal file
48
config/exemple_configuration.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"SERVEUR_IP": "YOUR.SERVER.IP",
|
||||
"SERVEUR_HOSTNAME": "YOUR.SERVER.HOST",
|
||||
"SERVEUR_LINK": "LINK.DE.TON.SERVER",
|
||||
"SERVEUR_PORT": 7002,
|
||||
"SERVEUR_PASSWORD": "YOUR_LINK_PASSWORD",
|
||||
"SERVEUR_ID": "006",
|
||||
"SERVEUR_SSL": true,
|
||||
|
||||
"SERVICE_NAME": "defender",
|
||||
"SERVICE_NICKNAME": "PyDefender",
|
||||
"SERVICE_REALNAME": "Python Defender Security",
|
||||
"SERVICE_USERNAME": "PyDefender",
|
||||
"SERVICE_HOST": "HOST.DE.TON.DEFENDER",
|
||||
"SERVICE_INFO": "Network IRC Service",
|
||||
"SERVICE_CHANLOG": "#services",
|
||||
"SERVICE_SMODES": "+ioqBS",
|
||||
"SERVICE_CMODES": "ntsOP",
|
||||
"SERVICE_UMODES": "o",
|
||||
"SERVICE_PREFIX": "!",
|
||||
|
||||
"OWNER": "TON_NICK_NAME",
|
||||
"PASSWORD": "TON_PASSWORD",
|
||||
|
||||
"JSONRPC_URL": "https://your.domaine.com:8600/api",
|
||||
"JSONRPC_PATH_TO_SOCKET_FILE": "/PATH/TO/YOUR/IRCD/data/rpc.socket",
|
||||
"JSONRPC_METHOD": "socket",
|
||||
"JSONRPC_USER": "YOUR_RPC_USER",
|
||||
"JSONRPC_PASSWORD": "YOUR_RPC_PASSWORD",
|
||||
|
||||
"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
|
||||
|
||||
}
|
||||
503
core/Model.py
503
core/Model.py
@@ -1,503 +0,0 @@
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from typing import Union
|
||||
from core.base import Base
|
||||
|
||||
class User:
|
||||
|
||||
@dataclass
|
||||
class UserModel:
|
||||
uid: str
|
||||
nickname: str
|
||||
username: str
|
||||
realname: str
|
||||
hostname: str
|
||||
umodes: str
|
||||
vhost: str
|
||||
isWebirc: bool
|
||||
isWebsocket: bool
|
||||
remote_ip: str
|
||||
score_connexion: int
|
||||
geoip: str = None
|
||||
connexion_datetime: datetime = field(default=datetime.now())
|
||||
|
||||
UID_DB: list[UserModel] = []
|
||||
|
||||
def __init__(self, Base: Base) -> None:
|
||||
self.log = Base.logs
|
||||
pass
|
||||
|
||||
def insert(self, newUser: UserModel) -> bool:
|
||||
"""Insert a new User object
|
||||
|
||||
Args:
|
||||
newUser (UserModel): New userModel object
|
||||
|
||||
Returns:
|
||||
bool: True if inserted
|
||||
"""
|
||||
result = False
|
||||
exist = False
|
||||
|
||||
for record in self.UID_DB:
|
||||
if record.uid == newUser.uid:
|
||||
# If the user exist then return False and do not go further
|
||||
exist = True
|
||||
self.log.debug(f'{record.uid} already exist')
|
||||
return result
|
||||
|
||||
if not exist:
|
||||
self.UID_DB.append(newUser)
|
||||
result = True
|
||||
self.log.debug(f'New User Created: ({newUser})')
|
||||
|
||||
if not result:
|
||||
self.log.critical(f'The User Object was not inserted {newUser}')
|
||||
|
||||
return result
|
||||
|
||||
def update(self, uid: str, newNickname: str) -> bool:
|
||||
"""Update the nickname starting from the UID
|
||||
|
||||
Args:
|
||||
uid (str): UID of the user
|
||||
newNickname (str): New nickname
|
||||
|
||||
Returns:
|
||||
bool: True if updated
|
||||
"""
|
||||
result = False
|
||||
|
||||
for record in self.UID_DB:
|
||||
if record.uid == uid:
|
||||
# If the user exist then update and return True and do not go further
|
||||
record.nickname = newNickname
|
||||
result = True
|
||||
self.log.debug(f'UID ({record.uid}) has been updated with new nickname {newNickname}')
|
||||
return result
|
||||
|
||||
if not result:
|
||||
self.log.critical(f'The new nickname {newNickname} was not updated, uid = {uid}')
|
||||
|
||||
return result
|
||||
|
||||
def delete(self, uid: str) -> bool:
|
||||
"""Delete the User starting from the UID
|
||||
|
||||
Args:
|
||||
uid (str): UID of the user
|
||||
|
||||
Returns:
|
||||
bool: True if deleted
|
||||
"""
|
||||
result = False
|
||||
|
||||
for record in self.UID_DB:
|
||||
if record.uid == uid:
|
||||
# If the user exist then remove and return True and do not go further
|
||||
self.UID_DB.remove(record)
|
||||
result = True
|
||||
self.log.debug(f'UID ({record.uid}) has been deleted')
|
||||
return result
|
||||
|
||||
if not result:
|
||||
self.log.critical(f'The UID {uid} was not deleted')
|
||||
|
||||
return result
|
||||
|
||||
def get_User(self, uidornickname: str) -> Union[UserModel, None]:
|
||||
"""Get The User Object model
|
||||
|
||||
Args:
|
||||
uidornickname (str): UID or Nickname
|
||||
|
||||
Returns:
|
||||
UserModel|None: The UserModel Object | None
|
||||
"""
|
||||
User = None
|
||||
for record in self.UID_DB:
|
||||
if record.uid == uidornickname:
|
||||
User = record
|
||||
elif record.nickname == uidornickname:
|
||||
User = record
|
||||
|
||||
self.log.debug(f'Search {uidornickname} -- result = {User}')
|
||||
|
||||
return User
|
||||
|
||||
def get_uid(self, uidornickname:str) -> Union[str, None]:
|
||||
"""Get the UID of the user starting from the UID or the Nickname
|
||||
|
||||
Args:
|
||||
uidornickname (str): UID or Nickname
|
||||
|
||||
Returns:
|
||||
str|None: Return the UID
|
||||
"""
|
||||
uid = None
|
||||
for record in self.UID_DB:
|
||||
if record.uid == uidornickname:
|
||||
uid = record.uid
|
||||
if record.nickname == uidornickname:
|
||||
uid = record.uid
|
||||
|
||||
self.log.debug(f'The UID that you are looking for {uidornickname} has been found {uid}')
|
||||
return uid
|
||||
|
||||
def get_nickname(self, uidornickname:str) -> Union[str, None]:
|
||||
"""Get the Nickname starting from UID or the nickname
|
||||
|
||||
Args:
|
||||
uidornickname (str): UID or Nickname of the user
|
||||
|
||||
Returns:
|
||||
str|None: the nickname
|
||||
"""
|
||||
nickname = None
|
||||
for record in self.UID_DB:
|
||||
if record.nickname == uidornickname:
|
||||
nickname = record.nickname
|
||||
if record.uid == uidornickname:
|
||||
nickname = record.nickname
|
||||
self.log.debug(f'The value to check {uidornickname} -> {nickname}')
|
||||
return nickname
|
||||
|
||||
class Admin:
|
||||
|
||||
@dataclass
|
||||
class AdminModel:
|
||||
uid: str
|
||||
nickname: str
|
||||
username: str
|
||||
hostname: str
|
||||
umodes: str
|
||||
vhost: str
|
||||
level: int
|
||||
connexion_datetime: datetime = field(default=datetime.now())
|
||||
|
||||
UID_ADMIN_DB: list[AdminModel] = []
|
||||
|
||||
def __init__(self, Base: Base) -> None:
|
||||
self.log = Base.logs
|
||||
pass
|
||||
|
||||
def insert(self, newAdmin: AdminModel) -> bool:
|
||||
|
||||
result = False
|
||||
exist = False
|
||||
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.uid == newAdmin.uid:
|
||||
# If the admin exist then return False and do not go further
|
||||
exist = True
|
||||
self.log.debug(f'{record.uid} already exist')
|
||||
return result
|
||||
|
||||
if not exist:
|
||||
self.UID_ADMIN_DB.append(newAdmin)
|
||||
result = True
|
||||
self.log.debug(f'UID ({newAdmin.uid}) has been created')
|
||||
|
||||
if not result:
|
||||
self.log.critical(f'The User Object was not inserted {newAdmin}')
|
||||
|
||||
return result
|
||||
|
||||
def update(self, uid: str, newNickname: str) -> bool:
|
||||
|
||||
result = False
|
||||
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.uid == uid:
|
||||
# If the admin exist, update and do not go further
|
||||
record.nickname = newNickname
|
||||
result = True
|
||||
self.log.debug(f'UID ({record.uid}) has been updated with new nickname {newNickname}')
|
||||
return result
|
||||
|
||||
if not result:
|
||||
self.log.critical(f'The new nickname {newNickname} was not updated, uid = {uid}')
|
||||
|
||||
return result
|
||||
|
||||
def delete(self, uid: str) -> bool:
|
||||
|
||||
result = False
|
||||
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.uid == uid:
|
||||
# If the admin exist, delete and do not go further
|
||||
self.UID_ADMIN_DB.remove(record)
|
||||
result = True
|
||||
self.log.debug(f'UID ({record.uid}) has been created')
|
||||
return result
|
||||
|
||||
if not result:
|
||||
self.log.critical(f'The UID {uid} was not deleted')
|
||||
|
||||
return result
|
||||
|
||||
def get_Admin(self, uidornickname: str) -> Union[AdminModel, None]:
|
||||
|
||||
Admin = None
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.uid == uidornickname:
|
||||
Admin = record
|
||||
elif record.nickname == uidornickname:
|
||||
Admin = record
|
||||
|
||||
self.log.debug(f'Search {uidornickname} -- result = {Admin}')
|
||||
|
||||
return Admin
|
||||
|
||||
def get_uid(self, uidornickname:str) -> Union[str, None]:
|
||||
|
||||
uid = None
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.uid == uidornickname:
|
||||
uid = record.uid
|
||||
if record.nickname == uidornickname:
|
||||
uid = record.uid
|
||||
|
||||
self.log.debug(f'The UID that you are looking for {uidornickname} has been found {uid}')
|
||||
return uid
|
||||
|
||||
def get_nickname(self, uidornickname:str) -> Union[str, None]:
|
||||
|
||||
nickname = None
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.nickname == uidornickname:
|
||||
nickname = record.nickname
|
||||
if record.uid == uidornickname:
|
||||
nickname = record.nickname
|
||||
self.log.debug(f'The value {uidornickname} -- {nickname}')
|
||||
return nickname
|
||||
|
||||
class Channel:
|
||||
|
||||
@dataclass
|
||||
class ChannelModel:
|
||||
name: str
|
||||
"""### Channel name
|
||||
It include the #"""
|
||||
uids: list
|
||||
"""### List of UID available in the channel
|
||||
including their modes ~ @ % + *
|
||||
|
||||
Returns:
|
||||
list: The list of UID's including theirs modes
|
||||
"""
|
||||
|
||||
UID_CHANNEL_DB: list[ChannelModel] = []
|
||||
"""List that contains all the Channels objects (ChannelModel)
|
||||
"""
|
||||
|
||||
def __init__(self, Base: Base) -> None:
|
||||
self.log = Base.logs
|
||||
self.Base = Base
|
||||
pass
|
||||
|
||||
def insert(self, newChan: ChannelModel) -> bool:
|
||||
"""This method will insert a new channel and if the channel exist it will update the user list (uids)
|
||||
|
||||
Args:
|
||||
newChan (ChannelModel): The channel model object
|
||||
|
||||
Returns:
|
||||
bool: True if new channel, False if channel exist (However UID could be updated)
|
||||
"""
|
||||
result = False
|
||||
exist = False
|
||||
|
||||
for record in self.UID_CHANNEL_DB:
|
||||
if record.name == newChan.name:
|
||||
# If the channel exist, update the user list and do not go further
|
||||
exist = True
|
||||
self.log.debug(f'{record.name} already exist')
|
||||
|
||||
for user in newChan.uids:
|
||||
record.uids.append(user)
|
||||
|
||||
# Supprimer les doublons
|
||||
del_duplicates = list(set(record.uids))
|
||||
record.uids = del_duplicates
|
||||
self.log.debug(f'Updating a new UID to the channel {record}')
|
||||
return result
|
||||
|
||||
|
||||
if not exist:
|
||||
# If the channel don't exist, then create it
|
||||
self.UID_CHANNEL_DB.append(newChan)
|
||||
result = True
|
||||
self.log.debug(f'New Channel Created: ({newChan})')
|
||||
|
||||
if not result:
|
||||
self.log.critical(f'The Channel Object was not inserted {newChan}')
|
||||
|
||||
return result
|
||||
|
||||
def delete(self, name: str) -> bool:
|
||||
|
||||
result = False
|
||||
|
||||
for record in self.UID_CHANNEL_DB:
|
||||
if record.name == name:
|
||||
# If the channel exist, then remove it and return True.
|
||||
# As soon as the channel found, return True and stop the loop
|
||||
self.UID_CHANNEL_DB.remove(record)
|
||||
result = True
|
||||
self.log.debug(f'Channel ({record.name}) has been created')
|
||||
return result
|
||||
|
||||
if not result:
|
||||
self.log.critical(f'The Channel {name} was not deleted')
|
||||
|
||||
return result
|
||||
|
||||
def delete_user_from_channel(self, chan_name: str, uid:str) -> bool:
|
||||
try:
|
||||
result = False
|
||||
|
||||
for record in self.UID_CHANNEL_DB:
|
||||
if record.name == chan_name:
|
||||
for user_id in record.uids:
|
||||
if self.Base.clean_uid(user_id) == uid:
|
||||
record.uids.remove(user_id)
|
||||
self.log.debug(f'The UID {uid} has been removed, here is the new object: {record}')
|
||||
result = True
|
||||
|
||||
for record in self.UID_CHANNEL_DB:
|
||||
if not record.uids:
|
||||
self.UID_CHANNEL_DB.remove(record)
|
||||
self.log.debug(f'The Channel {record.name} has been removed, here is the new object: {record}')
|
||||
|
||||
return result
|
||||
except ValueError as ve:
|
||||
self.log.error(f'{ve}')
|
||||
|
||||
def delete_user_from_all_channel(self, uid:str) -> bool:
|
||||
try:
|
||||
result = False
|
||||
|
||||
for record in self.UID_CHANNEL_DB:
|
||||
for user_id in record.uids:
|
||||
if self.Base.clean_uid(user_id) == self.Base.clean_uid(uid):
|
||||
record.uids.remove(user_id)
|
||||
self.log.debug(f'The UID {uid} has been removed, here is the new object: {record}')
|
||||
result = True
|
||||
|
||||
for record in self.UID_CHANNEL_DB:
|
||||
if not record.uids:
|
||||
self.UID_CHANNEL_DB.remove(record)
|
||||
self.log.debug(f'The Channel {record.name} has been removed, here is the new object: {record}')
|
||||
|
||||
return result
|
||||
except ValueError as ve:
|
||||
self.log.error(f'{ve}')
|
||||
|
||||
def get_Channel(self, name: str) -> Union[ChannelModel, None]:
|
||||
|
||||
Channel = None
|
||||
for record in self.UID_CHANNEL_DB:
|
||||
if record.name == name:
|
||||
Channel = record
|
||||
|
||||
self.log.debug(f'Search {name} -- result = {Channel}')
|
||||
|
||||
return Channel
|
||||
|
||||
class Clones:
|
||||
|
||||
@dataclass
|
||||
class CloneModel:
|
||||
alive: bool
|
||||
nickname: str
|
||||
username: str
|
||||
realname: str
|
||||
connected: bool = False
|
||||
|
||||
UID_CLONE_DB: list[CloneModel] = []
|
||||
|
||||
def __init__(self, Base: Base) -> None:
|
||||
self.log = Base.logs
|
||||
|
||||
def insert(self, newCloneObject: CloneModel) -> bool:
|
||||
"""Create new Clone object
|
||||
|
||||
Args:
|
||||
newCloneObject (CloneModel): New CloneModel object
|
||||
|
||||
Returns:
|
||||
bool: True if inserted
|
||||
"""
|
||||
result = False
|
||||
exist = False
|
||||
|
||||
for record in self.UID_CLONE_DB:
|
||||
if record.nickname == newCloneObject.nickname:
|
||||
# If the user exist then return False and do not go further
|
||||
exist = True
|
||||
self.log.debug(f'{record.nickname} already exist')
|
||||
return result
|
||||
|
||||
if not exist:
|
||||
self.UID_CLONE_DB.append(newCloneObject)
|
||||
result = True
|
||||
self.log.debug(f'New Clone Object Created: ({newCloneObject})')
|
||||
|
||||
if not result:
|
||||
self.log.critical(f'The Clone Object was not inserted {newCloneObject}')
|
||||
|
||||
return result
|
||||
|
||||
def delete(self, nickname: str) -> bool:
|
||||
"""Delete the Clone Object starting from the nickname
|
||||
|
||||
Args:
|
||||
nickname (str): nickname of the clone
|
||||
|
||||
Returns:
|
||||
bool: True if deleted
|
||||
"""
|
||||
result = False
|
||||
|
||||
for record in self.UID_CLONE_DB:
|
||||
if record.nickname == nickname:
|
||||
# If the user exist then remove and return True and do not go further
|
||||
self.UID_CLONE_DB.remove(record)
|
||||
result = True
|
||||
self.log.debug(f'The clone ({record.nickname}) has been deleted')
|
||||
return result
|
||||
|
||||
if not result:
|
||||
self.log.critical(f'The UID {nickname} was not deleted')
|
||||
|
||||
return result
|
||||
|
||||
def exists(self, nickname: str) -> bool:
|
||||
"""Check if the nickname exist
|
||||
|
||||
Args:
|
||||
nickname (str): Nickname of the clone
|
||||
|
||||
Returns:
|
||||
bool: True if the nickname exist
|
||||
"""
|
||||
response = False
|
||||
|
||||
for cloneObject in self.UID_CLONE_DB:
|
||||
if cloneObject.nickname == nickname:
|
||||
response = True
|
||||
|
||||
return response
|
||||
|
||||
def kill(self, nickname:str) -> bool:
|
||||
|
||||
response = False
|
||||
|
||||
for cloneObject in self.UID_CLONE_DB:
|
||||
if cloneObject.nickname == nickname:
|
||||
cloneObject.alive = False # Kill the clone
|
||||
response = True
|
||||
|
||||
return response
|
||||
348
core/base.py
348
core/base.py
@@ -1,33 +1,56 @@
|
||||
import time, threading, os, random, socket, hashlib, ipaddress, logging, requests, json, re, ast
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
import time
|
||||
import random
|
||||
import socket
|
||||
import hashlib
|
||||
import logging
|
||||
import threading
|
||||
import ipaddress
|
||||
|
||||
import ast
|
||||
import requests
|
||||
|
||||
from dataclasses import fields
|
||||
from typing import Union, Literal
|
||||
from typing import Union, Literal, TYPE_CHECKING
|
||||
from base64 import b64decode, b64encode
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from sqlalchemy import create_engine, Engine, Connection, CursorResult
|
||||
from sqlalchemy.sql import text
|
||||
from core.loadConf import ConfigDataModel
|
||||
from core.definition import MConfig
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.classes.settings import Settings
|
||||
|
||||
class Base:
|
||||
|
||||
def __init__(self, Config: ConfigDataModel) -> None:
|
||||
def __init__(self, Config: MConfig, settings: 'Settings') -> None:
|
||||
|
||||
self.Config = Config # Assigner l'objet de configuration
|
||||
self.Settings: Settings = settings
|
||||
self.init_log_system() # Demarrer le systeme de log
|
||||
self.check_for_new_version(True) # Verifier si une nouvelle version est disponible
|
||||
|
||||
self.running_timers:list[threading.Timer] = [] # Liste des timers en cours
|
||||
self.running_threads:list[threading.Thread] = [] # Liste des threads en cours
|
||||
self.running_sockets: list[socket.socket] = [] # Les sockets ouvert
|
||||
self.periodic_func:dict[object] = {} # Liste des fonctions en attentes
|
||||
# Liste des timers en cours
|
||||
self.running_timers:list[threading.Timer] = self.Settings.RUNNING_TIMERS
|
||||
|
||||
self.lock = threading.RLock() # Création du lock
|
||||
# Liste des threads en cours
|
||||
self.running_threads:list[threading.Thread] = self.Settings.RUNNING_THREADS
|
||||
|
||||
# Les sockets ouvert
|
||||
self.running_sockets: list[socket.socket] = self.Settings.RUNNING_SOCKETS
|
||||
|
||||
# Liste des fonctions en attentes
|
||||
self.periodic_func:dict[object] = self.Settings.PERIODIC_FUNC
|
||||
|
||||
# Création du lock
|
||||
self.lock = self.Settings.LOCK
|
||||
|
||||
self.install: bool = False # Initialisation de la variable d'installation
|
||||
self.engine, self.cursor = self.db_init() # Initialisation de la connexion a la base de données
|
||||
self.__create_db() # Initialisation de la base de données
|
||||
|
||||
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:
|
||||
"""This will put the current version of Defender
|
||||
located in version.json
|
||||
@@ -37,17 +60,15 @@ class Base:
|
||||
with open(version_filename, 'r') as version_data:
|
||||
current_version:dict[str, str] = json.load(version_data)
|
||||
|
||||
# self.DEFENDER_VERSION = current_version["version"]
|
||||
self.Config.current_version = current_version['version']
|
||||
self.Config.CURRENT_VERSION = current_version['version']
|
||||
|
||||
return None
|
||||
|
||||
def __get_latest_defender_version(self) -> None:
|
||||
try:
|
||||
self.logs.debug(f'Looking for a new version available on Github')
|
||||
# print(f'===> Looking for a new version available on Github')
|
||||
self.logs.debug(f'-- Looking for a new version available on Github')
|
||||
token = ''
|
||||
json_url = f'https://raw.githubusercontent.com/adator85/IRC_DEFENDER_MODULES/main/version.json'
|
||||
json_url = f'https://raw.githubusercontent.com/adator85/DEFENDER/main/version.json'
|
||||
headers = {
|
||||
'Authorization': f'token {token}',
|
||||
'Accept': 'application/vnd.github.v3.raw' # Indique à GitHub que nous voulons le contenu brut du fichier
|
||||
@@ -61,7 +82,7 @@ class Base:
|
||||
response.raise_for_status() # Vérifie si la requête a réussi
|
||||
json_response:dict = response.json()
|
||||
# self.LATEST_DEFENDER_VERSION = json_response["version"]
|
||||
self.Config.latest_version = json_response['version']
|
||||
self.Config.LATEST_VERSION = json_response['version']
|
||||
|
||||
return None
|
||||
except requests.HTTPError as err:
|
||||
@@ -70,19 +91,27 @@ class Base:
|
||||
self.logs.warning(f'Github not available to fetch latest version')
|
||||
|
||||
def check_for_new_version(self, online:bool) -> bool:
|
||||
"""Check if there is a new version available
|
||||
|
||||
Args:
|
||||
online (bool): True if you want to get the version from github (main branch)
|
||||
|
||||
Returns:
|
||||
bool: True if there is a new version available
|
||||
"""
|
||||
try:
|
||||
self.logs.debug(f'Checking for a new service version')
|
||||
self.logs.debug(f'-- Checking for a new service version')
|
||||
|
||||
# Assigner la version actuelle de Defender
|
||||
self.__set_current_defender_version()
|
||||
# Récuperer la dernier version disponible dans github
|
||||
if online:
|
||||
self.logs.debug(f'Retrieve the latest version from Github')
|
||||
self.logs.debug(f'-- Retrieve the latest version from Github')
|
||||
self.__get_latest_defender_version()
|
||||
|
||||
isNewVersion = False
|
||||
latest_version = self.Config.latest_version
|
||||
current_version = self.Config.current_version
|
||||
latest_version = self.Config.LATEST_VERSION
|
||||
current_version = self.Config.CURRENT_VERSION
|
||||
|
||||
curr_major , curr_minor, curr_patch = current_version.split('.')
|
||||
last_major, last_minor, last_patch = latest_version.split('.')
|
||||
@@ -102,13 +131,21 @@ class Base:
|
||||
return isNewVersion
|
||||
except ValueError as ve:
|
||||
self.logs.error(f'Impossible to convert in version number : {ve}')
|
||||
except AttributeError as atterr:
|
||||
self.logs.error(f'Attribute Error: {atterr}')
|
||||
except Exception as err:
|
||||
self.logs.error(f'General Error: {err}')
|
||||
|
||||
def get_unixtime(self) -> int:
|
||||
"""
|
||||
Cette fonction retourne un UNIXTIME de type 12365456
|
||||
Return: Current time in seconds since the Epoch (int)
|
||||
"""
|
||||
cet_offset = timezone(timedelta(hours=2))
|
||||
now_cet = datetime.now(cet_offset)
|
||||
unixtime_cet = int(now_cet.timestamp())
|
||||
unixtime = int( time.time() )
|
||||
|
||||
return unixtime
|
||||
|
||||
def get_datetime(self) -> str:
|
||||
@@ -137,7 +174,7 @@ class Base:
|
||||
Returns:
|
||||
None: Aucun retour
|
||||
"""
|
||||
sql_insert = f"INSERT INTO {self.Config.table_log} (datetime, server_msg) VALUES (:datetime, :server_msg)"
|
||||
sql_insert = f"INSERT INTO {self.Config.TABLE_LOG} (datetime, server_msg) VALUES (:datetime, :server_msg)"
|
||||
mes_donnees = {'datetime': str(self.get_datetime()),'server_msg': f'{log_message}'}
|
||||
self.db_execute_query(sql_insert, mes_donnees)
|
||||
|
||||
@@ -145,21 +182,65 @@ class Base:
|
||||
|
||||
def init_log_system(self) -> None:
|
||||
# Create folder if not available
|
||||
logs_directory = f'logs{os.sep}'
|
||||
logs_directory = f'logs{self.Config.OS_SEP}'
|
||||
if not os.path.exists(f'{logs_directory}'):
|
||||
os.makedirs(logs_directory)
|
||||
|
||||
# Init logs object
|
||||
self.logs = logging
|
||||
self.logs.basicConfig(level=self.Config.DEBUG_LEVEL,
|
||||
filename='logs/defender.log',
|
||||
encoding='UTF-8',
|
||||
format='%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s')
|
||||
self.logs = logging.getLogger(self.Config.LOGGING_NAME)
|
||||
self.logs.setLevel(self.Config.DEBUG_LEVEL)
|
||||
|
||||
# Add Handlers
|
||||
file_hanlder = logging.FileHandler(f'logs{self.Config.OS_SEP}defender.log',encoding='UTF-8')
|
||||
file_hanlder.setLevel(self.Config.DEBUG_LEVEL)
|
||||
|
||||
# Define log format
|
||||
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s')
|
||||
|
||||
# Apply log format
|
||||
file_hanlder.setFormatter(formatter)
|
||||
|
||||
# Add handler to logs
|
||||
self.logs.addHandler(file_hanlder)
|
||||
|
||||
# Apply the filter
|
||||
self.logs.addFilter(self.replace_filter)
|
||||
|
||||
# self.logs.Logger('defender').addFilter(self.replace_filter)
|
||||
self.logs.info('#################### STARTING DEFENDER ####################')
|
||||
|
||||
return None
|
||||
|
||||
def replace_filter(self, record: logging.LogRecord) -> bool:
|
||||
|
||||
response = True
|
||||
filter: list[str] = ['PING', f":{self.Config.SERVICE_PREFIX}auth"]
|
||||
|
||||
# record.msg = record.getMessage().replace("PING", "[REDACTED]")
|
||||
if self.Settings.CONSOLE:
|
||||
print(record.getMessage())
|
||||
|
||||
for f in filter:
|
||||
if f in record.getMessage():
|
||||
response = False
|
||||
|
||||
return response # Retourne True pour permettre l'affichage du message
|
||||
|
||||
def delete_logger(self, logger_name: str) -> None:
|
||||
|
||||
# Récupérer le logger
|
||||
logger = logging.getLogger(logger_name)
|
||||
|
||||
# Retirer tous les gestionnaires du logger et les fermer
|
||||
for handler in logger.handlers[:]: # Utiliser une copie de la liste
|
||||
logger.removeHandler(handler)
|
||||
handler.close()
|
||||
|
||||
# Supprimer le logger du dictionnaire global
|
||||
logging.Logger.manager.loggerDict.pop(logger_name, None)
|
||||
|
||||
return None
|
||||
|
||||
def log_cmd(self, user_cmd:str, cmd:str) -> None:
|
||||
"""Enregistre les commandes envoyées par les utilisateurs
|
||||
|
||||
@@ -168,12 +249,12 @@ class Base:
|
||||
"""
|
||||
cmd_list = cmd.split()
|
||||
if len(cmd_list) == 3:
|
||||
if cmd_list[0].replace('.', '') == 'auth':
|
||||
if cmd_list[0].replace(self.Config.SERVICE_PREFIX, '') == 'auth':
|
||||
cmd_list[1] = '*******'
|
||||
cmd_list[2] = '*******'
|
||||
cmd = ' '.join(cmd_list)
|
||||
|
||||
insert_cmd_query = f"INSERT INTO {self.Config.table_commande} (datetime, user, commande) VALUES (:datetime, :user, :commande)"
|
||||
insert_cmd_query = f"INSERT INTO {self.Config.TABLE_COMMAND} (datetime, user, commande) VALUES (:datetime, :user, :commande)"
|
||||
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'commande': cmd}
|
||||
self.db_execute_query(insert_cmd_query, mes_donnees)
|
||||
|
||||
@@ -188,7 +269,7 @@ class Base:
|
||||
Returns:
|
||||
bool: True si le module existe déja dans la base de données sinon False
|
||||
"""
|
||||
query = f"SELECT id FROM {self.Config.table_module} WHERE module_name = :module_name"
|
||||
query = f"SELECT id FROM {self.Config.TABLE_MODULE} WHERE module_name = :module_name"
|
||||
mes_donnes = {'module_name': module_name}
|
||||
results = self.db_execute_query(query, mes_donnes)
|
||||
|
||||
@@ -206,7 +287,7 @@ class Base:
|
||||
|
||||
if not self.db_isModuleExist(module_name):
|
||||
self.logs.debug(f"Le module {module_name} n'existe pas alors ont le créer")
|
||||
insert_cmd_query = f"INSERT INTO {self.Config.table_module} (datetime, user, module_name, isdefault) VALUES (:datetime, :user, :module_name, :isdefault)"
|
||||
insert_cmd_query = f"INSERT INTO {self.Config.TABLE_MODULE} (datetime, user, module_name, isdefault) VALUES (:datetime, :user, :module_name, :isdefault)"
|
||||
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'module_name': module_name, 'isdefault': isdefault}
|
||||
self.db_execute_query(insert_cmd_query, mes_donnees)
|
||||
else:
|
||||
@@ -214,13 +295,26 @@ class Base:
|
||||
|
||||
return False
|
||||
|
||||
def db_update_module(self, user_cmd: str, module_name: str) -> None:
|
||||
"""Modifie la date et le user qui a rechargé le module
|
||||
|
||||
Args:
|
||||
user_cmd (str): le user qui a rechargé le module
|
||||
module_name (str): le module a rechargé
|
||||
"""
|
||||
update_cmd_query = f"UPDATE {self.Config.TABLE_MODULE} SET datetime = :datetime, user = :user WHERE module_name = :module_name"
|
||||
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'module_name': module_name}
|
||||
self.db_execute_query(update_cmd_query, mes_donnees)
|
||||
|
||||
return False
|
||||
|
||||
def db_delete_module(self, module_name:str) -> None:
|
||||
"""Supprime les modules de la base de données
|
||||
|
||||
Args:
|
||||
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}
|
||||
self.db_execute_query(insert_cmd_query, mes_donnees)
|
||||
|
||||
@@ -243,7 +337,7 @@ class Base:
|
||||
try:
|
||||
response = True
|
||||
current_date = self.get_datetime()
|
||||
core_table = self.Config.table_config
|
||||
core_table = self.Config.TABLE_CONFIG
|
||||
|
||||
# Add local parameters to DB
|
||||
for field in fields(dataclassObj):
|
||||
@@ -307,7 +401,7 @@ class Base:
|
||||
|
||||
def db_update_core_config(self, module_name:str, dataclassObj: object, param_key:str, param_value: str) -> bool:
|
||||
|
||||
core_table = self.Config.table_config
|
||||
core_table = self.Config.TABLE_CONFIG
|
||||
# Check if the param exist
|
||||
if not hasattr(dataclassObj, param_key):
|
||||
self.logs.error(f"Le parametre {param_key} n'existe pas dans la variable global")
|
||||
@@ -339,62 +433,9 @@ class Base:
|
||||
|
||||
return True
|
||||
|
||||
def db_query_channel(self, action: Literal['add','del'], module_name: str, channel_name: str) -> bool:
|
||||
"""You can add a channel or delete a channel.
|
||||
|
||||
Args:
|
||||
action (Literal['add','del']): Action on the database
|
||||
module_name (str): The module name (mod_test)
|
||||
channel_name (str): The channel name (With #)
|
||||
|
||||
Returns:
|
||||
bool: True if action done
|
||||
"""
|
||||
try:
|
||||
channel_name = channel_name.lower() if self.Is_Channel(channel_name) else None
|
||||
core_table = 'core_channel'
|
||||
|
||||
if not channel_name:
|
||||
self.logs.warn(f'The channel [{channel_name}] is not correct')
|
||||
return False
|
||||
|
||||
match action:
|
||||
|
||||
case 'add':
|
||||
mes_donnees = {'module_name': module_name, 'channel_name': channel_name}
|
||||
response = self.db_execute_query(f"SELECT id FROM {core_table} WHERE module_name = :module_name AND channel_name = :channel_name", mes_donnees)
|
||||
isChannelExist = response.fetchone()
|
||||
|
||||
if isChannelExist is None:
|
||||
mes_donnees = {'datetime': self.get_datetime(), 'channel_name': channel_name, 'module_name': module_name}
|
||||
insert = self.db_execute_query(f"INSERT INTO {core_table} (datetime, channel_name, module_name) VALUES (:datetime, :channel_name, :module_name)", mes_donnees)
|
||||
if insert.rowcount:
|
||||
self.logs.debug(f'New channel added: channel={channel_name} / module_name={module_name}')
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
pass
|
||||
|
||||
case 'del':
|
||||
mes_donnes = {'channel_name': channel_name, 'module_name': module_name}
|
||||
response = self.db_execute_query(f"DELETE FROM {core_table} WHERE channel_name = :channel_name AND module_name = :module_name", mes_donnes)
|
||||
|
||||
if response.rowcount > 0:
|
||||
self.logs.debug(f'Channel deleted: channel={channel_name} / module: {module_name}')
|
||||
return True
|
||||
else:
|
||||
|
||||
return False
|
||||
|
||||
case _:
|
||||
return False
|
||||
|
||||
except Exception as err:
|
||||
self.logs.error(err)
|
||||
|
||||
def db_create_first_admin(self) -> None:
|
||||
|
||||
user = self.db_execute_query(f"SELECT id FROM {self.Config.table_admin}")
|
||||
user = self.db_execute_query(f"SELECT id FROM {self.Config.TABLE_ADMIN}")
|
||||
if not user.fetchall():
|
||||
admin = self.Config.OWNER
|
||||
password = self.crypt_password(self.Config.PASSWORD)
|
||||
@@ -407,7 +448,7 @@ class Base:
|
||||
'level': 5
|
||||
}
|
||||
self.db_execute_query(f"""
|
||||
INSERT INTO {self.Config.table_admin}
|
||||
INSERT INTO {self.Config.TABLE_ADMIN}
|
||||
(createdOn, user, password, hostname, vhost, level)
|
||||
VALUES
|
||||
(:createdOn, :user, :password, :hostname, :vhost, :level)"""
|
||||
@@ -424,12 +465,12 @@ class Base:
|
||||
|
||||
self.running_timers.append(t)
|
||||
|
||||
self.logs.debug(f"Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}")
|
||||
self.logs.debug(f"-- Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}")
|
||||
|
||||
except AssertionError as 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
|
||||
|
||||
Args:
|
||||
@@ -445,15 +486,34 @@ class Base:
|
||||
if thread.getName() == func_name:
|
||||
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()
|
||||
|
||||
self.running_threads.append(th)
|
||||
self.logs.debug(f"Thread ID : {str(th.ident)} | Thread name : {th.getName()} | Running Threads : {len(threading.enumerate())}")
|
||||
self.logs.debug(f"-- Thread ID : {str(th.ident)} | Thread name : {th.getName()} | Running Threads : {len(threading.enumerate())}")
|
||||
|
||||
except AssertionError as ae:
|
||||
self.logs.error(f'{ae}')
|
||||
|
||||
def thread_count(self, thread_name: str) -> int:
|
||||
"""This method return the number of existing threads
|
||||
currently running or not running
|
||||
|
||||
Args:
|
||||
thread_name (str): The name of the thread
|
||||
|
||||
Returns:
|
||||
int: Number of threads
|
||||
"""
|
||||
with self.lock:
|
||||
count = 0
|
||||
|
||||
for thr in self.running_threads:
|
||||
if thread_name == thr.getName():
|
||||
count += 1
|
||||
|
||||
return count
|
||||
|
||||
def garbage_collector_timer(self) -> None:
|
||||
"""Methode qui supprime les timers qui ont finis leurs job
|
||||
"""
|
||||
@@ -463,9 +523,9 @@ class Base:
|
||||
if not timer.is_alive():
|
||||
timer.cancel()
|
||||
self.running_timers.remove(timer)
|
||||
self.logs.info(f"Timer {str(timer)} removed")
|
||||
self.logs.info(f"-- Timer {str(timer)} removed")
|
||||
else:
|
||||
self.logs.debug(f"===> Timer {str(timer)} Still running ...")
|
||||
self.logs.debug(f"--* Timer {str(timer)} Still running ...")
|
||||
|
||||
except AssertionError as ae:
|
||||
self.logs.error(f'Assertion Error -> {ae}')
|
||||
@@ -476,9 +536,10 @@ class Base:
|
||||
try:
|
||||
for thread in self.running_threads:
|
||||
if thread.getName() != 'heartbeat':
|
||||
# print(thread.getName(), thread.is_alive(), sep=' / ')
|
||||
if not thread.is_alive():
|
||||
self.running_threads.remove(thread)
|
||||
self.logs.info(f"Thread {str(thread.getName())} {str(thread.native_id)} removed")
|
||||
self.logs.info(f"-- Thread {str(thread.getName())} {str(thread.native_id)} removed")
|
||||
|
||||
# print(threading.enumerate())
|
||||
except AssertionError as ae:
|
||||
@@ -493,7 +554,7 @@ class Base:
|
||||
|
||||
soc.close()
|
||||
self.running_sockets.remove(soc)
|
||||
self.logs.debug(f"Socket ==> closed {str(soc.fileno())}")
|
||||
self.logs.debug(f"-- Socket ==> closed {str(soc.fileno())}")
|
||||
|
||||
def shutdown(self) -> None:
|
||||
"""Methode qui va préparer l'arrêt complêt du service
|
||||
@@ -529,8 +590,8 @@ class Base:
|
||||
|
||||
def db_init(self) -> tuple[Engine, Connection]:
|
||||
|
||||
db_directory = self.Config.db_path
|
||||
full_path_db = self.Config.db_path + self.Config.db_name
|
||||
db_directory = self.Config.DB_PATH
|
||||
full_path_db = self.Config.DB_PATH + self.Config.DB_NAME
|
||||
|
||||
if not os.path.exists(db_directory):
|
||||
self.install = True
|
||||
@@ -538,19 +599,19 @@ class Base:
|
||||
|
||||
engine = create_engine(f'sqlite:///{full_path_db}.db', echo=False)
|
||||
cursor = engine.connect()
|
||||
self.logs.info("database connexion has been initiated")
|
||||
self.logs.info("-- database connexion has been initiated")
|
||||
return engine, cursor
|
||||
|
||||
def __create_db(self) -> None:
|
||||
|
||||
table_core_log = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_log} (
|
||||
table_core_log = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_LOG} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
datetime TEXT,
|
||||
server_msg TEXT
|
||||
)
|
||||
'''
|
||||
|
||||
table_core_config = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_config} (
|
||||
table_core_config = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_CONFIG} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
datetime TEXT,
|
||||
module_name TEXT,
|
||||
@@ -559,7 +620,7 @@ class Base:
|
||||
)
|
||||
'''
|
||||
|
||||
table_core_log_command = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_commande} (
|
||||
table_core_log_command = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_COMMAND} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
datetime TEXT,
|
||||
user TEXT,
|
||||
@@ -567,7 +628,7 @@ class Base:
|
||||
)
|
||||
'''
|
||||
|
||||
table_core_module = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_module} (
|
||||
table_core_module = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_MODULE} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
datetime TEXT,
|
||||
user TEXT,
|
||||
@@ -576,7 +637,7 @@ class Base:
|
||||
)
|
||||
'''
|
||||
|
||||
table_core_channel = '''CREATE TABLE IF NOT EXISTS core_channel (
|
||||
table_core_channel = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_CHANNEL} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
datetime TEXT,
|
||||
module_name TEXT,
|
||||
@@ -584,7 +645,7 @@ class Base:
|
||||
)
|
||||
'''
|
||||
|
||||
table_core_admin = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_admin} (
|
||||
table_core_admin = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_ADMIN} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
createdOn TEXT,
|
||||
user TEXT,
|
||||
@@ -660,6 +721,23 @@ class Base:
|
||||
except TypeError:
|
||||
return value
|
||||
|
||||
def convert_to_int(self, value: any) -> Union[int, None]:
|
||||
"""Convert a value to int
|
||||
|
||||
Args:
|
||||
value (any): Value to convert to int if possible
|
||||
|
||||
Returns:
|
||||
Union[int, None]: Return the int value or None if not possible
|
||||
"""
|
||||
try:
|
||||
response = int(value)
|
||||
return response
|
||||
except ValueError:
|
||||
return None
|
||||
except TypeError:
|
||||
return None
|
||||
|
||||
def is_valid_ip(self, ip_to_control:str) -> bool:
|
||||
|
||||
try:
|
||||
@@ -682,16 +760,19 @@ class Base:
|
||||
self.logs.critical(f'This remote ip is not valid : {ve}')
|
||||
return None
|
||||
|
||||
# def encode_ip(self, remote_ip_address: str) -> Union[str, None]:
|
||||
def encode_ip(self, remote_ip_address: str) -> Union[str, None]:
|
||||
|
||||
# binary_ip = b64encode()
|
||||
# try:
|
||||
# decoded_ip = ipaddress.ip_address(binary_ip)
|
||||
binary_ip = socket.inet_aton(remote_ip_address)
|
||||
try:
|
||||
encoded_ip = b64encode(binary_ip).decode()
|
||||
|
||||
# return decoded_ip.exploded
|
||||
# except ValueError as ve:
|
||||
# self.logs.critical(f'This remote ip is not valid : {ve}')
|
||||
# return None
|
||||
return encoded_ip
|
||||
except ValueError as ve:
|
||||
self.logs.critical(f'This remote ip is not valid : {ve}')
|
||||
return None
|
||||
except Exception as err:
|
||||
self.logs.critical(f'General Error: {err}')
|
||||
return None
|
||||
|
||||
def get_random(self, lenght:int) -> str:
|
||||
"""
|
||||
@@ -721,7 +802,7 @@ class Base:
|
||||
# Vider le dictionnaire de fonction
|
||||
self.periodic_func.clear()
|
||||
|
||||
def clean_uid(self, uid:str) -> str:
|
||||
def clean_uid(self, uid:str) -> Union[str, None]:
|
||||
"""Clean UID by removing @ / % / + / ~ / * / :
|
||||
|
||||
Args:
|
||||
@@ -730,34 +811,13 @@ class Base:
|
||||
Returns:
|
||||
str: Clean UID without any sign
|
||||
"""
|
||||
|
||||
pattern = fr'[:|@|%|\+|~|\*]*'
|
||||
parsed_UID = re.sub(pattern, '', uid)
|
||||
|
||||
return parsed_UID
|
||||
|
||||
def Is_Channel(self, channelToCheck: str) -> bool:
|
||||
"""Check if the string has the # caractere and return True if this is a channel
|
||||
|
||||
Args:
|
||||
channelToCheck (str): The string to test if it is a channel or not
|
||||
|
||||
Returns:
|
||||
bool: True if the string is a channel / False if this is not a channel
|
||||
"""
|
||||
try:
|
||||
|
||||
if channelToCheck is None:
|
||||
return False
|
||||
if uid is None:
|
||||
return None
|
||||
|
||||
pattern = fr'^#'
|
||||
isChannel = re.findall(pattern, channelToCheck)
|
||||
pattern = fr'[:|@|%|\+|~|\*]*'
|
||||
parsed_UID = re.sub(pattern, '', uid)
|
||||
|
||||
if not isChannel:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
return parsed_UID
|
||||
except TypeError as te:
|
||||
self.logs.error(f'TypeError: [{channelToCheck}] - {te}')
|
||||
except Exception as err:
|
||||
self.logs.error(f'Error Not defined: {err}')
|
||||
self.logs.error(f'Type Error: {te}')
|
||||
|
||||
127
core/classes/admin.py
Normal file
127
core/classes/admin.py
Normal file
@@ -0,0 +1,127 @@
|
||||
from typing import Union
|
||||
import core.definition as df
|
||||
from core.base import Base
|
||||
|
||||
|
||||
class Admin:
|
||||
|
||||
UID_ADMIN_DB: list[df.MAdmin] = []
|
||||
|
||||
def __init__(self, baseObj: Base) -> None:
|
||||
self.Logs = baseObj.logs
|
||||
pass
|
||||
|
||||
def insert(self, newAdmin: df.MAdmin) -> bool:
|
||||
|
||||
result = False
|
||||
exist = False
|
||||
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.uid == newAdmin.uid:
|
||||
# If the admin exist then return False and do not go further
|
||||
exist = True
|
||||
self.Logs.debug(f'{record.uid} already exist')
|
||||
return result
|
||||
|
||||
if not exist:
|
||||
self.UID_ADMIN_DB.append(newAdmin)
|
||||
result = True
|
||||
self.Logs.debug(f'UID ({newAdmin.uid}) has been created')
|
||||
|
||||
if not result:
|
||||
self.Logs.critical(f'The User Object was not inserted {newAdmin}')
|
||||
|
||||
return result
|
||||
|
||||
def update_nickname(self, uid: str, newNickname: str) -> bool:
|
||||
|
||||
result = False
|
||||
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.uid == uid:
|
||||
# If the admin exist, update and do not go further
|
||||
record.nickname = newNickname
|
||||
result = True
|
||||
self.Logs.debug(f'UID ({record.uid}) has been updated with new nickname {newNickname}')
|
||||
return result
|
||||
|
||||
if not result:
|
||||
self.Logs.critical(f'The new nickname {newNickname} was not updated, uid = {uid}')
|
||||
|
||||
return result
|
||||
|
||||
def update_level(self, nickname: str, newLevel: int) -> bool:
|
||||
|
||||
result = False
|
||||
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.nickname == nickname:
|
||||
# If the admin exist, update and do not go further
|
||||
record.level = newLevel
|
||||
result = True
|
||||
self.Logs.debug(f'Admin ({record.nickname}) has been updated with new level {newLevel}')
|
||||
return result
|
||||
|
||||
if not result:
|
||||
self.Logs.critical(f'The new level {newLevel} was not updated, nickname = {nickname}')
|
||||
|
||||
return result
|
||||
|
||||
def delete(self, uidornickname: str) -> bool:
|
||||
|
||||
result = False
|
||||
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.uid == uidornickname:
|
||||
# If the admin exist, delete and do not go further
|
||||
self.UID_ADMIN_DB.remove(record)
|
||||
result = True
|
||||
self.Logs.debug(f'UID ({record.uid}) has been deleted')
|
||||
return result
|
||||
if record.nickname == uidornickname:
|
||||
# If the admin exist, delete and do not go further
|
||||
self.UID_ADMIN_DB.remove(record)
|
||||
result = True
|
||||
self.Logs.debug(f'nickname ({record.nickname}) has been deleted')
|
||||
return result
|
||||
|
||||
if not result:
|
||||
self.Logs.critical(f'The UID {uidornickname} was not deleted')
|
||||
|
||||
return result
|
||||
|
||||
def get_Admin(self, uidornickname: str) -> Union[df.MAdmin, None]:
|
||||
|
||||
Admin = None
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.uid == uidornickname:
|
||||
Admin = record
|
||||
elif record.nickname == uidornickname:
|
||||
Admin = record
|
||||
|
||||
#self.Logs.debug(f'Search {uidornickname} -- result = {Admin}')
|
||||
|
||||
return Admin
|
||||
|
||||
def get_uid(self, uidornickname:str) -> Union[str, None]:
|
||||
|
||||
uid = None
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.uid == uidornickname:
|
||||
uid = record.uid
|
||||
if record.nickname == uidornickname:
|
||||
uid = record.uid
|
||||
|
||||
self.Logs.debug(f'The UID that you are looking for {uidornickname} has been found {uid}')
|
||||
return uid
|
||||
|
||||
def get_nickname(self, uidornickname:str) -> Union[str, None]:
|
||||
|
||||
nickname = None
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.nickname == uidornickname:
|
||||
nickname = record.nickname
|
||||
if record.uid == uidornickname:
|
||||
nickname = record.nickname
|
||||
self.Logs.debug(f'The value {uidornickname} -- {nickname}')
|
||||
return nickname
|
||||
240
core/classes/channel.py
Normal file
240
core/classes/channel.py
Normal file
@@ -0,0 +1,240 @@
|
||||
from re import findall
|
||||
from typing import Union, Literal, TYPE_CHECKING
|
||||
from dataclasses import asdict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.definition import MChannel
|
||||
from core.base import Base
|
||||
|
||||
class Channel:
|
||||
|
||||
UID_CHANNEL_DB: list['MChannel'] = []
|
||||
"""List that contains all the Channels objects (ChannelModel)
|
||||
"""
|
||||
|
||||
def __init__(self, baseObj: 'Base') -> None:
|
||||
|
||||
self.Logs = baseObj.logs
|
||||
self.Base = baseObj
|
||||
|
||||
return None
|
||||
|
||||
def insert(self, newChan: 'MChannel') -> bool:
|
||||
"""This method will insert a new channel and if the channel exist it will update the user list (uids)
|
||||
|
||||
Args:
|
||||
newChan (ChannelModel): The channel model object
|
||||
|
||||
Returns:
|
||||
bool: True if new channel, False if channel exist (However UID could be updated)
|
||||
"""
|
||||
result = False
|
||||
exist = False
|
||||
|
||||
for record in self.UID_CHANNEL_DB:
|
||||
if record.name.lower() == newChan.name.lower():
|
||||
# If the channel exist, update the user list and do not go further
|
||||
exist = True
|
||||
# self.Logs.debug(f'{record.name} already exist')
|
||||
|
||||
for user in newChan.uids:
|
||||
record.uids.append(user)
|
||||
|
||||
# Supprimer les doublons
|
||||
del_duplicates = list(set(record.uids))
|
||||
record.uids = del_duplicates
|
||||
# self.Logs.debug(f'Updating a new UID to the channel {record}')
|
||||
return result
|
||||
|
||||
if not exist:
|
||||
# If the channel don't exist, then create it
|
||||
newChan.name = newChan.name.lower()
|
||||
self.UID_CHANNEL_DB.append(newChan)
|
||||
result = True
|
||||
# self.Logs.debug(f'New Channel Created: ({newChan})')
|
||||
|
||||
if not result:
|
||||
self.Logs.critical(f'The Channel Object was not inserted {newChan}')
|
||||
|
||||
self.clean_channel()
|
||||
|
||||
return result
|
||||
|
||||
def delete(self, channel_name: str) -> bool:
|
||||
|
||||
chanObj = self.get_Channel(channel_name)
|
||||
|
||||
if chanObj is None:
|
||||
return False
|
||||
|
||||
self.UID_CHANNEL_DB.remove(chanObj)
|
||||
|
||||
return True
|
||||
|
||||
def delete_user_from_channel(self, channel_name: str, uid:str) -> bool:
|
||||
try:
|
||||
result = False
|
||||
|
||||
chanObj = self.get_Channel(channel_name.lower())
|
||||
|
||||
if chanObj is None:
|
||||
return result
|
||||
|
||||
for userid in chanObj.uids:
|
||||
if self.Base.clean_uid(userid) == self.Base.clean_uid(uid):
|
||||
chanObj.uids.remove(userid)
|
||||
result = True
|
||||
|
||||
self.clean_channel()
|
||||
|
||||
return result
|
||||
except ValueError as ve:
|
||||
self.Logs.error(f'{ve}')
|
||||
|
||||
def delete_user_from_all_channel(self, uid:str) -> bool:
|
||||
try:
|
||||
result = False
|
||||
|
||||
for record in self.UID_CHANNEL_DB:
|
||||
for user_id in record.uids:
|
||||
if self.Base.clean_uid(user_id) == self.Base.clean_uid(uid):
|
||||
record.uids.remove(user_id)
|
||||
# self.Logs.debug(f'The UID {uid} has been removed, here is the new object: {record}')
|
||||
result = True
|
||||
|
||||
self.clean_channel()
|
||||
|
||||
return result
|
||||
except ValueError as ve:
|
||||
self.Logs.error(f'{ve}')
|
||||
|
||||
def add_user_to_a_channel(self, channel_name: str, uid: str) -> bool:
|
||||
try:
|
||||
result = False
|
||||
chanObj = self.get_Channel(channel_name)
|
||||
self.Logs.debug(f"** {__name__}")
|
||||
|
||||
if chanObj is None:
|
||||
result = self.insert(MChannel(channel_name, uids=[uid]))
|
||||
# self.Logs.debug(f"** {__name__} - result: {result}")
|
||||
# self.Logs.debug(f'New Channel Created: ({chanObj})')
|
||||
return result
|
||||
|
||||
chanObj.uids.append(uid)
|
||||
del_duplicates = list(set(chanObj.uids))
|
||||
chanObj.uids = del_duplicates
|
||||
# self.Logs.debug(f'New Channel Created: ({chanObj})')
|
||||
|
||||
return True
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
|
||||
def clean_channel(self) -> None:
|
||||
"""Remove Channels if empty
|
||||
"""
|
||||
try:
|
||||
for record in self.UID_CHANNEL_DB:
|
||||
if not record.uids:
|
||||
self.UID_CHANNEL_DB.remove(record)
|
||||
# self.Logs.debug(f'The Channel {record.name} has been removed, here is the new object: {record}')
|
||||
return None
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
|
||||
def get_Channel(self, channel_name: str) -> Union['MChannel', None]:
|
||||
|
||||
Channel = None
|
||||
|
||||
for record in self.UID_CHANNEL_DB:
|
||||
if record.name == channel_name:
|
||||
Channel = record
|
||||
|
||||
return Channel
|
||||
|
||||
def get_Channel_AsDict(self, chan_name: str) -> Union[dict[str, any], None]:
|
||||
|
||||
chanObj = self.get_Channel(chan_name=chan_name)
|
||||
|
||||
if not chanObj is None:
|
||||
chan_as_dict = asdict(chanObj)
|
||||
return chan_as_dict
|
||||
else:
|
||||
return None
|
||||
|
||||
def Is_Channel(self, channelToCheck: str) -> bool:
|
||||
"""Check if the string has the # caractere and return True if this is a channel
|
||||
|
||||
Args:
|
||||
channelToCheck (str): The string to test if it is a channel or not
|
||||
|
||||
Returns:
|
||||
bool: True if the string is a channel / False if this is not a channel
|
||||
"""
|
||||
try:
|
||||
|
||||
if channelToCheck is None:
|
||||
return False
|
||||
|
||||
pattern = fr'^#'
|
||||
isChannel = findall(pattern, channelToCheck)
|
||||
|
||||
if not isChannel:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
except TypeError as te:
|
||||
self.Logs.error(f'TypeError: [{channelToCheck}] - {te}')
|
||||
except Exception as err:
|
||||
self.Logs.error(f'Error Not defined: {err}')
|
||||
|
||||
def db_query_channel(self, action: Literal['add','del'], module_name: str, channel_name: str) -> bool:
|
||||
"""You can add a channel or delete a channel.
|
||||
|
||||
Args:
|
||||
action (Literal['add','del']): Action on the database
|
||||
module_name (str): The module name (mod_test)
|
||||
channel_name (str): The channel name (With #)
|
||||
|
||||
Returns:
|
||||
bool: True if action done
|
||||
"""
|
||||
try:
|
||||
channel_name = channel_name.lower() if self.Is_Channel(channel_name) else None
|
||||
core_table = self.Base.Config.TABLE_CHANNEL
|
||||
|
||||
if not channel_name:
|
||||
self.Logs.warning(f'The channel [{channel_name}] is not correct')
|
||||
return False
|
||||
|
||||
match action:
|
||||
|
||||
case 'add':
|
||||
mes_donnees = {'module_name': module_name, 'channel_name': channel_name}
|
||||
response = self.Base.db_execute_query(f"SELECT id FROM {core_table} WHERE module_name = :module_name AND channel_name = :channel_name", mes_donnees)
|
||||
isChannelExist = response.fetchone()
|
||||
|
||||
if isChannelExist is None:
|
||||
mes_donnees = {'datetime': self.Base.get_datetime(), 'channel_name': channel_name, 'module_name': module_name}
|
||||
insert = self.Base.db_execute_query(f"INSERT INTO {core_table} (datetime, channel_name, module_name) VALUES (:datetime, :channel_name, :module_name)", mes_donnees)
|
||||
if insert.rowcount:
|
||||
self.Logs.debug(f'New channel added: channel={channel_name} / module_name={module_name}')
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
case 'del':
|
||||
mes_donnes = {'channel_name': channel_name, 'module_name': module_name}
|
||||
response = self.Base.db_execute_query(f"DELETE FROM {core_table} WHERE channel_name = :channel_name AND module_name = :module_name", mes_donnes)
|
||||
|
||||
if response.rowcount > 0:
|
||||
self.Logs.debug(f'Channel deleted: channel={channel_name} / module: {module_name}')
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
case _:
|
||||
return False
|
||||
|
||||
except Exception as err:
|
||||
self.Logs.error(err)
|
||||
|
||||
161
core/classes/clone.py
Normal file
161
core/classes/clone.py
Normal file
@@ -0,0 +1,161 @@
|
||||
from dataclasses import asdict
|
||||
from core.definition import MClone
|
||||
from typing import Union
|
||||
from core.base import Base
|
||||
|
||||
class Clone:
|
||||
|
||||
UID_CLONE_DB: list[MClone] = []
|
||||
|
||||
def __init__(self, baseObj: Base) -> None:
|
||||
|
||||
self.Logs = baseObj.logs
|
||||
|
||||
return None
|
||||
|
||||
def insert(self, newCloneObject: MClone) -> bool:
|
||||
"""Create new Clone object
|
||||
|
||||
Args:
|
||||
newCloneObject (CloneModel): New CloneModel object
|
||||
|
||||
Returns:
|
||||
bool: True if inserted
|
||||
"""
|
||||
result = False
|
||||
exist = False
|
||||
|
||||
for record in self.UID_CLONE_DB:
|
||||
if record.nickname == newCloneObject.nickname:
|
||||
# If the user exist then return False and do not go further
|
||||
exist = True
|
||||
self.Logs.warning(f'Nickname {record.nickname} already exist')
|
||||
return result
|
||||
if record.uid == newCloneObject.uid:
|
||||
exist = True
|
||||
self.Logs.warning(f'UID: {record.uid} already exist')
|
||||
return result
|
||||
|
||||
if not exist:
|
||||
self.UID_CLONE_DB.append(newCloneObject)
|
||||
result = True
|
||||
# self.Logs.debug(f'New Clone Object Created: ({newCloneObject})')
|
||||
|
||||
if not result:
|
||||
self.Logs.critical(f'The Clone Object was not inserted {newCloneObject}')
|
||||
|
||||
return result
|
||||
|
||||
def delete(self, uidornickname: str) -> bool:
|
||||
"""Delete the Clone Object starting from the nickname or the UID
|
||||
|
||||
Args:
|
||||
uidornickname (str): UID or nickname of the clone
|
||||
|
||||
Returns:
|
||||
bool: True if deleted
|
||||
"""
|
||||
|
||||
cloneObj = self.get_Clone(uidornickname=uidornickname)
|
||||
|
||||
if cloneObj is None:
|
||||
return False
|
||||
|
||||
self.UID_CLONE_DB.remove(cloneObj)
|
||||
|
||||
return True
|
||||
|
||||
def exists(self, nickname: str) -> bool:
|
||||
"""Check if the nickname exist
|
||||
|
||||
Args:
|
||||
nickname (str): Nickname of the clone
|
||||
|
||||
Returns:
|
||||
bool: True if the nickname exist
|
||||
"""
|
||||
response = False
|
||||
|
||||
for cloneObject in self.UID_CLONE_DB:
|
||||
if cloneObject.nickname == nickname:
|
||||
response = True
|
||||
|
||||
return response
|
||||
|
||||
def uid_exists(self, uid: str) -> bool:
|
||||
"""Check if the nickname exist
|
||||
|
||||
Args:
|
||||
uid (str): uid of the clone
|
||||
|
||||
Returns:
|
||||
bool: True if the nickname exist
|
||||
"""
|
||||
response = False
|
||||
|
||||
for cloneObject in self.UID_CLONE_DB:
|
||||
if cloneObject.uid == uid:
|
||||
response = True
|
||||
|
||||
return response
|
||||
|
||||
def get_Clone(self, uidornickname: str) -> Union[MClone, None]:
|
||||
"""Get MClone object or None
|
||||
|
||||
Args:
|
||||
uidornickname (str): The UID or the Nickname
|
||||
|
||||
Returns:
|
||||
Union[MClone, None]: Return MClone object or None
|
||||
"""
|
||||
cloneObj = None
|
||||
|
||||
for clone in self.UID_CLONE_DB:
|
||||
if clone.uid == uidornickname:
|
||||
cloneObj = clone
|
||||
if clone.nickname == uidornickname:
|
||||
cloneObj = clone
|
||||
|
||||
return cloneObj
|
||||
|
||||
def get_uid(self, uidornickname: str) -> Union[str, None]:
|
||||
"""Get the UID of the clone starting from the UID or the Nickname
|
||||
|
||||
Args:
|
||||
uidornickname (str): UID or Nickname
|
||||
|
||||
Returns:
|
||||
str|None: Return the UID
|
||||
"""
|
||||
uid = None
|
||||
for record in self.UID_CLONE_DB:
|
||||
if record.uid == uidornickname:
|
||||
uid = record.uid
|
||||
if record.nickname == uidornickname:
|
||||
uid = record.uid
|
||||
|
||||
# if not uid is None:
|
||||
# self.Logs.debug(f'The UID that you are looking for {uidornickname} has been found {uid}')
|
||||
|
||||
return uid
|
||||
|
||||
def get_Clone_AsDict(self, uidornickname: str) -> Union[dict[str, any], None]:
|
||||
|
||||
cloneObj = self.get_Clone(uidornickname=uidornickname)
|
||||
|
||||
if not cloneObj is None:
|
||||
cloneObj_as_dict = asdict(cloneObj)
|
||||
return cloneObj_as_dict
|
||||
else:
|
||||
return None
|
||||
|
||||
def kill(self, nickname:str) -> bool:
|
||||
|
||||
response = False
|
||||
|
||||
for cloneObject in self.UID_CLONE_DB:
|
||||
if cloneObject.nickname == nickname:
|
||||
cloneObject.alive = False # Kill the clone
|
||||
response = True
|
||||
|
||||
return response
|
||||
57
core/classes/config.py
Normal file
57
core/classes/config.py
Normal file
@@ -0,0 +1,57 @@
|
||||
from json import load
|
||||
from sys import exit
|
||||
from os import sep
|
||||
from typing import Union
|
||||
from core.definition import MConfig
|
||||
|
||||
|
||||
|
||||
class Configuration:
|
||||
|
||||
def __init__(self) -> None:
|
||||
|
||||
self.ConfigObject: MConfig = self.__load_service_configuration()
|
||||
return None
|
||||
|
||||
def __load_json_service_configuration(self):
|
||||
try:
|
||||
conf_filename = f'config{sep}configuration.json'
|
||||
with open(conf_filename, 'r') as configuration_data:
|
||||
configuration:dict[str, Union[str, int, list, dict]] = load(configuration_data)
|
||||
|
||||
return configuration
|
||||
|
||||
except FileNotFoundError as fe:
|
||||
print(f'FileNotFound: {fe}')
|
||||
print('Configuration file not found please create config/configuration.json')
|
||||
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) -> MConfig:
|
||||
try:
|
||||
import_config = self.__load_json_service_configuration()
|
||||
|
||||
Model_keys = MConfig().__dict__
|
||||
model_key_list: list = []
|
||||
json_config_key_list: list = []
|
||||
|
||||
for key in Model_keys:
|
||||
model_key_list.append(key)
|
||||
|
||||
for key in import_config:
|
||||
json_config_key_list.append(key)
|
||||
|
||||
for json_conf in json_config_key_list:
|
||||
if not json_conf in model_key_list:
|
||||
import_config.pop(json_conf, None)
|
||||
print(f"\!/ The key {json_conf} is not expected, it has been removed from the system ! please remove it from configuration.json file \!/")
|
||||
|
||||
ConfigObject: MConfig = MConfig(
|
||||
**import_config
|
||||
)
|
||||
|
||||
return ConfigObject
|
||||
except TypeError as te:
|
||||
print(te)
|
||||
19
core/classes/protocol.py
Normal file
19
core/classes/protocol.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from typing import Literal, TYPE_CHECKING
|
||||
from .protocols.unreal6 import Unrealircd6
|
||||
from .protocols.inspircd import Inspircd
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
|
||||
class Protocol:
|
||||
|
||||
def __init__(self, protocol: Literal['unreal6','inspircd'], ircInstance: 'Irc'):
|
||||
|
||||
self.Protocol = None
|
||||
match protocol:
|
||||
case 'unreal6':
|
||||
self.Protocol: Unrealircd6 = Unrealircd6(ircInstance)
|
||||
case 'inspircd':
|
||||
self.Protocol: Inspircd = Inspircd(ircInstance)
|
||||
case _:
|
||||
self.Protocol: Unrealircd6 = Unrealircd6(ircInstance)
|
||||
669
core/classes/protocols/inspircd.py
Normal file
669
core/classes/protocols/inspircd.py
Normal file
@@ -0,0 +1,669 @@
|
||||
from re import match, findall
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
from ssl import SSLEOFError, SSLError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
|
||||
class Inspircd:
|
||||
|
||||
def __init__(self, ircInstance: 'Irc'):
|
||||
self.name = 'InspIRCd-4'
|
||||
|
||||
self.__Irc = ircInstance
|
||||
self.__Config = ircInstance.Config
|
||||
self.__Base = ircInstance.Base
|
||||
|
||||
self.__Base.logs.info(f"** Loading protocol [{__name__}]")
|
||||
|
||||
def send2socket(self, message: str, print_log: bool = True) -> None:
|
||||
"""Envoit les commandes à envoyer au serveur.
|
||||
|
||||
Args:
|
||||
string (Str): contient la commande à envoyer au serveur.
|
||||
"""
|
||||
try:
|
||||
with self.__Base.lock:
|
||||
self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[0]))
|
||||
if print_log:
|
||||
self.__Base.logs.debug(f'<< {message}')
|
||||
|
||||
except UnicodeDecodeError as ude:
|
||||
self.__Base.logs.error(f'Decode Error try iso-8859-1 - {ude} - {message}')
|
||||
self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[1],'replace'))
|
||||
except UnicodeEncodeError as uee:
|
||||
self.__Base.logs.error(f'Encode Error try iso-8859-1 - {uee} - {message}')
|
||||
self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[1],'replace'))
|
||||
except AssertionError as ae:
|
||||
self.__Base.logs.warning(f'Assertion Error {ae} - message: {message}')
|
||||
except SSLEOFError as soe:
|
||||
self.__Base.logs.error(f"SSLEOFError: {soe} - {message}")
|
||||
except SSLError as se:
|
||||
self.__Base.logs.error(f"SSLError: {se} - {message}")
|
||||
except OSError as oe:
|
||||
self.__Base.logs.error(f"OSError: {oe} - {message}")
|
||||
except AttributeError as ae:
|
||||
self.__Base.logs.critical(f"Attribute Error: {ae}")
|
||||
|
||||
def send_priv_msg(self, nick_from: str, msg: str, channel: str = None, nick_to: str = None):
|
||||
"""Sending PRIVMSG to a channel or to a nickname by batches
|
||||
could be either channel or nickname not both together
|
||||
Args:
|
||||
msg (str): The message to send
|
||||
nick_from (str): The sender nickname
|
||||
channel (str, optional): The receiver channel. Defaults to None.
|
||||
nick_to (str, optional): The reciever nickname. Defaults to None.
|
||||
"""
|
||||
try:
|
||||
batch_size = self.__Config.BATCH_SIZE
|
||||
User_from = self.__Irc.User.get_User(nick_from)
|
||||
User_to = self.__Irc.User.get_User(nick_to) if nick_to is None else None
|
||||
|
||||
if User_from is None:
|
||||
self.__Base.logs.error(f"The sender nickname [{nick_from}] do not exist")
|
||||
return None
|
||||
|
||||
if not channel is None:
|
||||
for i in range(0, len(str(msg)), batch_size):
|
||||
batch = str(msg)[i:i+batch_size]
|
||||
self.send2socket(f":{User_from.uid} PRIVMSG {channel} :{batch}")
|
||||
|
||||
if not nick_to is None:
|
||||
for i in range(0, len(str(msg)), batch_size):
|
||||
batch = str(msg)[i:i+batch_size]
|
||||
self.send2socket(f":{nick_from} PRIVMSG {User_to.uid} :{batch}")
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"General Error: {err}")
|
||||
|
||||
def send_notice(self, nick_from: str, nick_to: str, msg: str) -> None:
|
||||
"""Sending NOTICE by batches
|
||||
|
||||
Args:
|
||||
msg (str): The message to send to the server
|
||||
nick_from (str): The sender Nickname
|
||||
nick_to (str): The reciever nickname
|
||||
"""
|
||||
try:
|
||||
batch_size = self.__Config.BATCH_SIZE
|
||||
User_from = self.__Irc.User.get_User(nick_from)
|
||||
User_to = self.__Irc.User.get_User(nick_to)
|
||||
|
||||
if User_from is None or User_to is None:
|
||||
self.__Base.logs.error(f"The sender [{nick_from}] or the Reciever [{nick_to}] do not exist")
|
||||
return None
|
||||
|
||||
for i in range(0, len(str(msg)), batch_size):
|
||||
batch = str(msg)[i:i+batch_size]
|
||||
self.send2socket(f":{User_from.uid} NOTICE {User_to.uid} :{batch}")
|
||||
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"General Error: {err}")
|
||||
|
||||
def link(self):
|
||||
"""Créer le link et envoyer les informations nécessaires pour la
|
||||
connexion au serveur.
|
||||
"""
|
||||
|
||||
nickname = self.__Config.SERVICE_NICKNAME
|
||||
username = self.__Config.SERVICE_USERNAME
|
||||
realname = self.__Config.SERVICE_REALNAME
|
||||
chan = self.__Config.SERVICE_CHANLOG
|
||||
info = self.__Config.SERVICE_INFO
|
||||
smodes = self.__Config.SERVICE_SMODES
|
||||
cmodes = self.__Config.SERVICE_CMODES
|
||||
umodes = self.__Config.SERVICE_UMODES
|
||||
host = self.__Config.SERVICE_HOST
|
||||
service_name = self.__Config.SERVICE_NAME
|
||||
|
||||
password = self.__Config.SERVEUR_PASSWORD
|
||||
link = self.__Config.SERVEUR_LINK
|
||||
server_id = self.__Config.SERVEUR_ID
|
||||
service_id = self.__Config.SERVICE_ID
|
||||
|
||||
version = self.__Config.CURRENT_VERSION
|
||||
unixtime = self.__Base.get_unixtime()
|
||||
|
||||
|
||||
self.send2socket(f"CAPAB START 1206")
|
||||
self.send2socket(f"CAPAB CAPABILITIES :NICKMAX=30 CHANMAX=64 MAXMODES=20 IDENTMAX=10 MAXQUIT=255 MAXTOPIC=307 MAXKICK=255 MAXREAL=128 MAXAWAY=200 MAXHOST=64 MAXLINE=512 CASEMAPPING=ascii GLOBOPS=0")
|
||||
self.send2socket(f"CAPAB END")
|
||||
self.send2socket(f"SERVER {link} {password} {server_id} :{info}")
|
||||
self.send2socket(f"BURST {unixtime}")
|
||||
self.send2socket(f":{server_id} ENDBURST")
|
||||
|
||||
self.__Base.logs.debug(f'>> {__name__} Link information sent to the server')
|
||||
|
||||
def gline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None:
|
||||
# TKL + G user host set_by expire_timestamp set_at_timestamp :reason
|
||||
|
||||
self.send2socket(f":{self.__Config.SERVEUR_ID} TKL + G {nickname} {hostname} {set_by} {expire_timestamp} {set_at_timestamp} :{reason}")
|
||||
|
||||
return None
|
||||
|
||||
def set_nick(self, newnickname: str) -> None:
|
||||
|
||||
self.send2socket(f":{self.__Config.SERVICE_NICKNAME} NICK {newnickname}")
|
||||
return None
|
||||
|
||||
def squit(self, server_id: str, server_link: str, reason: str) -> None:
|
||||
|
||||
if not reason:
|
||||
reason = 'Service Shutdown'
|
||||
|
||||
self.send2socket(f":{server_id} SQUIT {server_link} :{reason}")
|
||||
return None
|
||||
|
||||
def ungline(self, nickname:str, hostname: str) -> None:
|
||||
|
||||
self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - G {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}")
|
||||
|
||||
return None
|
||||
|
||||
def kline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None:
|
||||
# TKL + k user host set_by expire_timestamp set_at_timestamp :reason
|
||||
|
||||
self.send2socket(f":{self.__Config.SERVEUR_ID} TKL + k {nickname} {hostname} {set_by} {expire_timestamp} {set_at_timestamp} :{reason}")
|
||||
|
||||
return None
|
||||
|
||||
def sjoin(self, channel: str) -> None:
|
||||
|
||||
if not self.__Irc.Channel.Is_Channel(channel):
|
||||
self.__Base.logs.error(f"The channel [{channel}] is not valid")
|
||||
return None
|
||||
|
||||
self.send2socket(f":{self.__Config.SERVEUR_ID} SJOIN {self.__Base.get_unixtime()} {channel} + :{self.__Config.SERVICE_ID}")
|
||||
|
||||
# Add defender to the channel uids list
|
||||
self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[self.__Config.SERVICE_ID]))
|
||||
return None
|
||||
|
||||
def send_quit(self, uid: str, reason: str, print_log: True) -> None:
|
||||
"""Send quit message
|
||||
|
||||
Args:
|
||||
uidornickname (str): The UID or the Nickname
|
||||
reason (str): The reason for the quit
|
||||
"""
|
||||
userObj = self.__Irc.User.get_User(uidornickname=uid)
|
||||
cloneObj = self.__Irc.Clone.get_Clone(uidornickname=uid)
|
||||
reputationObj = self.__Irc.Reputation.get_Reputation(uidornickname=uid)
|
||||
|
||||
if not userObj is None:
|
||||
self.send2socket(f":{userObj.uid} QUIT :{reason}", print_log=print_log)
|
||||
self.__Irc.User.delete(userObj.uid)
|
||||
|
||||
if not cloneObj is None:
|
||||
self.__Irc.Clone.delete(cloneObj.uid)
|
||||
|
||||
if not reputationObj is None:
|
||||
self.__Irc.Reputation.delete(reputationObj.uid)
|
||||
|
||||
if not self.__Irc.Channel.delete_user_from_all_channel(uid):
|
||||
self.__Base.logs.error(f"The UID [{uid}] has not been deleted from all channels")
|
||||
|
||||
return None
|
||||
|
||||
def send_uid(self, nickname:str, username: str, hostname: str, uid:str, umodes: str, vhost: str, remote_ip: str, realname: str, print_log: bool = True) -> None:
|
||||
"""Send UID to the server
|
||||
|
||||
Args:
|
||||
nickname (str): Nickname of the client
|
||||
username (str): Username of the client
|
||||
hostname (str): Hostname of the client you want to create
|
||||
uid (str): UID of the client you want to create
|
||||
umodes (str): umodes of the client you want to create
|
||||
vhost (str): vhost of the client you want to create
|
||||
remote_ip (str): remote_ip of the client you want to create
|
||||
realname (str): realname of the client you want to create
|
||||
print_log (bool, optional): print logs if true. Defaults to True.
|
||||
"""
|
||||
# {self.Config.SERVEUR_ID} UID
|
||||
# {clone.nickname} 1 {self.Base.get_unixtime()} {clone.username} {clone.hostname} {clone.uid} * {clone.umodes} {clone.vhost} * {self.Base.encode_ip(clone.remote_ip)} :{clone.realname}
|
||||
try:
|
||||
unixtime = self.__Base.get_unixtime()
|
||||
encoded_ip = self.__Base.encode_ip(remote_ip)
|
||||
|
||||
# Create the user
|
||||
self.__Irc.User.insert(
|
||||
self.__Irc.Loader.Definition.MUser(
|
||||
uid=uid, nickname=nickname, username=username,
|
||||
realname=realname,hostname=hostname, umodes=umodes,
|
||||
vhost=vhost, remote_ip=remote_ip
|
||||
)
|
||||
)
|
||||
|
||||
uid_msg = f":{self.__Config.SERVEUR_ID} UID {nickname} 1 {unixtime} {username} {hostname} {uid} * {umodes} {vhost} * {encoded_ip} :{realname}"
|
||||
|
||||
self.send2socket(uid_msg, print_log=print_log)
|
||||
|
||||
return None
|
||||
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def send_join_chan(self, uidornickname: str, channel: str, password: str = None, print_log: bool = True) -> None:
|
||||
"""Joining a channel
|
||||
|
||||
Args:
|
||||
uidornickname (str): UID or nickname that need to join
|
||||
channel (str): channel to join
|
||||
password (str, optional): The password of the channel to join. Default to None
|
||||
print_log (bool, optional): Write logs. Defaults to True.
|
||||
"""
|
||||
|
||||
userObj = self.__Irc.User.get_User(uidornickname)
|
||||
passwordChannel = password if not password is None else ''
|
||||
|
||||
if userObj is None:
|
||||
return None
|
||||
|
||||
if not self.__Irc.Channel.Is_Channel(channel):
|
||||
self.__Base.logs.error(f"The channel [{channel}] is not valid")
|
||||
return None
|
||||
|
||||
self.send2socket(f":{userObj.uid} JOIN {channel} {passwordChannel}", print_log=print_log)
|
||||
|
||||
# Add defender to the channel uids list
|
||||
self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[userObj.uid]))
|
||||
return None
|
||||
|
||||
def send_part_chan(self, uidornickname:str, channel: str, print_log: bool = True) -> None:
|
||||
"""Part from a channel
|
||||
|
||||
Args:
|
||||
uidornickname (str): UID or nickname that need to join
|
||||
channel (str): channel to join
|
||||
print_log (bool, optional): Write logs. Defaults to True.
|
||||
"""
|
||||
|
||||
userObj = self.__Irc.User.get_User(uidornickname)
|
||||
|
||||
if userObj is None:
|
||||
self.__Base.logs.error(f"The user [{uidornickname}] is not valid")
|
||||
return None
|
||||
|
||||
if not self.__Irc.Channel.Is_Channel(channel):
|
||||
self.__Base.logs.error(f"The channel [{channel}] is not valid")
|
||||
return None
|
||||
|
||||
self.send2socket(f":{userObj.uid} PART {channel}", print_log=print_log)
|
||||
|
||||
# Add defender to the channel uids list
|
||||
self.__Irc.Channel.delete_user_from_channel(channel, userObj.uid)
|
||||
return None
|
||||
|
||||
def unkline(self, nickname:str, hostname: str) -> None:
|
||||
|
||||
self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - K {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}")
|
||||
|
||||
return None
|
||||
|
||||
def on_umode2(self, serverMsg: list[str]) -> None:
|
||||
"""Handle umode2 coming from a server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original server message
|
||||
"""
|
||||
try:
|
||||
# [':adator_', 'UMODE2', '-iwx']
|
||||
|
||||
userObj = self.__Irc.User.get_User(str(serverMsg[0]).lstrip(':'))
|
||||
userMode = serverMsg[2]
|
||||
|
||||
if userObj is None: # If user is not created
|
||||
return None
|
||||
|
||||
# save previous user modes
|
||||
old_umodes = userObj.umodes
|
||||
|
||||
# TODO : User object should be able to update user modes
|
||||
if self.__Irc.User.update_mode(userObj.uid, userMode):
|
||||
return None
|
||||
# self.__Base.logs.debug(f"Updating user mode for [{userObj.nickname}] [{old_umodes}] => [{userObj.umodes}]")
|
||||
|
||||
return None
|
||||
|
||||
except IndexError as ie:
|
||||
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_quit(self, serverMsg: list[str]) -> None:
|
||||
"""Handle quit coming from a server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original server message
|
||||
"""
|
||||
try:
|
||||
# ['@unrealircd.org/userhost=...@192.168.1.10;unrealircd.org/userip=...@192.168.1.10;msgid=CssUrV08BzekYuq7BfvPHn;time=2024-11-02T15:03:33.182Z', ':001JKNY0N', 'QUIT', ':Quit:', '....']
|
||||
|
||||
uid_who_quit = str(serverMsg[1]).lstrip(':')
|
||||
|
||||
self.__Irc.Channel.delete_user_from_all_channel(uid_who_quit)
|
||||
self.__Irc.User.delete(uid_who_quit)
|
||||
self.__Irc.Reputation.delete(uid_who_quit)
|
||||
self.__Irc.Clone.delete(uid_who_quit)
|
||||
|
||||
return None
|
||||
|
||||
except IndexError as ie:
|
||||
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_squit(self, serverMsg: list[str]) -> None:
|
||||
"""Handle squit coming from a server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original server message
|
||||
"""
|
||||
# ['@msgid=QOEolbRxdhpVW5c8qLkbAU;time=2024-09-21T17:33:16.547Z', 'SQUIT', 'defender.deb.biz.st', ':Connection', 'closed']
|
||||
|
||||
server_hostname = serverMsg[2]
|
||||
uid_to_delete = None
|
||||
for s_user in self.__Irc.User.UID_DB:
|
||||
if s_user.hostname == server_hostname and 'S' in s_user.umodes:
|
||||
uid_to_delete = s_user.uid
|
||||
|
||||
if uid_to_delete is None:
|
||||
return None
|
||||
|
||||
self.__Irc.User.delete(uid_to_delete)
|
||||
self.__Irc.Channel.delete_user_from_all_channel(uid_to_delete)
|
||||
|
||||
return None
|
||||
|
||||
def on_protoctl(self, serverMsg: list[str]) -> None:
|
||||
"""Handle protoctl coming from a server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original server message
|
||||
"""
|
||||
if len(serverMsg) > 5:
|
||||
if '=' in serverMsg[5]:
|
||||
serveur_hosting_id = str(serverMsg[5]).split('=')
|
||||
self.__Config.HSID = serveur_hosting_id[1]
|
||||
|
||||
return None
|
||||
|
||||
def on_nick(self, serverMsg: list[str]) -> None:
|
||||
"""Handle nick coming from a server
|
||||
new nickname
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original server message
|
||||
"""
|
||||
try:
|
||||
# ['@unrealircd.org/geoip=FR;unrealircd.org/', ':001OOU2H3', 'NICK', 'WebIrc', '1703795844']
|
||||
# Changement de nickname
|
||||
|
||||
uid = str(serverMsg[1]).lstrip(':')
|
||||
newnickname = serverMsg[3]
|
||||
self.__Irc.User.update(uid, newnickname)
|
||||
|
||||
return None
|
||||
|
||||
except IndexError as ie:
|
||||
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_sjoin(self, serverMsg: list[str]) -> None:
|
||||
"""Handle sjoin coming from a server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original server message
|
||||
"""
|
||||
try:
|
||||
# ['@msgid=5sTwGdj349D82L96p749SY;time=2024-08-15T09:50:23.528Z', ':001', 'SJOIN', '1721564574', '#welcome', ':001JD94QH']
|
||||
# ['@msgid=bvceb6HthbLJapgGLXn1b0;time=2024-08-15T09:50:11.464Z', ':001', 'SJOIN', '1721564574', '#welcome', '+lnrt', '13', ':001CIVLQF', '+11ZAAAAAB', '001QGR10C', '*@0014UE10B', '001NL1O07', '001SWZR05', '001HB8G04', '@00BAAAAAJ', '0019M7101']
|
||||
# ['@msgid=SKUeuVzOrTShRDduq8VerX;time=2024-08-23T19:37:04.266Z', ':001', 'SJOIN', '1723993047', '#welcome', '+lnrt', '13',
|
||||
# ':001T6VU3F', '001JGWB2K', '@11ZAAAAAB',
|
||||
# '001F16WGR', '001X9YMGQ', '*+001DYPFGP', '@00BAAAAAJ', '001AAGOG9', '001FMFVG8', '001DAEEG7',
|
||||
# '&~G:unknown-users', '"~G:websocket-users', '"~G:known-users', '"~G:webirc-users']
|
||||
serverMsg.pop(0)
|
||||
channel = str(serverMsg[3]).lower()
|
||||
len_cmd = len(serverMsg)
|
||||
list_users:list = []
|
||||
occurence = 0
|
||||
start_boucle = 0
|
||||
|
||||
# Trouver le premier user
|
||||
for i in range(len_cmd):
|
||||
s: list = findall(fr':', serverMsg[i])
|
||||
if s:
|
||||
occurence += 1
|
||||
if occurence == 2:
|
||||
start_boucle = i
|
||||
|
||||
# Boucle qui va ajouter l'ensemble des users (UID)
|
||||
for i in range(start_boucle, len(serverMsg)):
|
||||
parsed_UID = str(serverMsg[i])
|
||||
clean_uid = self.__Irc.User.clean_uid(parsed_UID)
|
||||
if not clean_uid is None and len(clean_uid) == 9:
|
||||
list_users.append(parsed_UID)
|
||||
|
||||
if list_users:
|
||||
self.__Irc.Channel.insert(
|
||||
self.__Irc.Loader.Definition.MChannel(
|
||||
name=channel,
|
||||
uids=list_users
|
||||
)
|
||||
)
|
||||
return None
|
||||
|
||||
except IndexError as ie:
|
||||
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_part(self, serverMsg: list[str]) -> None:
|
||||
"""Handle part coming from a server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original server message
|
||||
"""
|
||||
try:
|
||||
# ['@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']
|
||||
|
||||
uid = str(serverMsg[1]).lstrip(':')
|
||||
channel = str(serverMsg[3]).lower()
|
||||
self.__Irc.Channel.delete_user_from_channel(channel, uid)
|
||||
|
||||
return None
|
||||
|
||||
except IndexError as ie:
|
||||
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_uid(self, serverMsg: list[str]) -> None:
|
||||
"""Handle uid message coming from the server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original server message
|
||||
"""
|
||||
# ['@s2s-md/geoip=cc=GB|cd=United\\sKingdom|asn=16276|asname=OVH\\sSAS;s2s-md/tls_cipher=TLSv1.3-TLS_CHACHA20_POLY1305_SHA256;s2s-md/creationtime=1721564601',
|
||||
# ':001', 'UID', 'albatros', '0', '1721564597', 'albatros', 'vps-91b2f28b.vps.ovh.net',
|
||||
# '001HB8G04', '0', '+iwxz', 'Clk-A62F1D18.vps.ovh.net', 'Clk-A62F1D18.vps.ovh.net', 'MyZBwg==', ':...']
|
||||
try:
|
||||
|
||||
isWebirc = True if 'webirc' in serverMsg[0] else False
|
||||
isWebsocket = True if 'websocket' in serverMsg[0] else False
|
||||
|
||||
uid = str(serverMsg[8])
|
||||
nickname = str(serverMsg[3])
|
||||
username = str(serverMsg[6])
|
||||
hostname = str(serverMsg[7])
|
||||
umodes = str(serverMsg[10])
|
||||
vhost = str(serverMsg[11])
|
||||
|
||||
if not 'S' in umodes:
|
||||
remote_ip = self.__Base.decode_ip(str(serverMsg[13]))
|
||||
else:
|
||||
remote_ip = '127.0.0.1'
|
||||
|
||||
# extract realname
|
||||
realname = ' '.join(serverMsg[14:]).lstrip(':')
|
||||
|
||||
# Extract Geoip information
|
||||
pattern = r'^.*geoip=cc=(\S{2}).*$'
|
||||
geoip_match = match(pattern, serverMsg[0])
|
||||
|
||||
if geoip_match:
|
||||
geoip = geoip_match.group(1)
|
||||
else:
|
||||
geoip = None
|
||||
|
||||
score_connexion = 0
|
||||
|
||||
self.__Irc.User.insert(
|
||||
self.__Irc.Loader.Definition.MUser(
|
||||
uid=uid,
|
||||
nickname=nickname,
|
||||
username=username,
|
||||
realname=realname,
|
||||
hostname=hostname,
|
||||
umodes=umodes,
|
||||
vhost=vhost,
|
||||
isWebirc=isWebirc,
|
||||
isWebsocket=isWebsocket,
|
||||
remote_ip=remote_ip,
|
||||
geoip=geoip,
|
||||
score_connexion=score_connexion,
|
||||
connexion_datetime=datetime.now()
|
||||
)
|
||||
)
|
||||
return None
|
||||
except IndexError as ie:
|
||||
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_server_ping(self, serverMsg: list[str]) -> None:
|
||||
"""Send a PONG message to the server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): List of str coming from the server
|
||||
"""
|
||||
try:
|
||||
# InspIRCd 3:
|
||||
# <- :3IN PING 808
|
||||
# -> :808 PONG 3IN
|
||||
|
||||
hsid = str(serverMsg[0]).replace(':','')
|
||||
self.send2socket(f":{self.__Config.SERVEUR_ID} PONG {hsid}", print_log=True)
|
||||
|
||||
return None
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_version(self, serverMsg: list[str]) -> None:
|
||||
"""Sending Server Version to the server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): List of str coming from the server
|
||||
"""
|
||||
# ['@unrealircd.org/userhost=StatServ@stats.deb.biz.st;draft/bot;bot;msgid=ehfAq3m2yjMjhgWEfi1UCS;time=2024-10-26T13:49:06.299Z', ':00BAAAAAI', 'PRIVMSG', '12ZAAAAAB', ':\x01VERSION\x01']
|
||||
# Réponse a un CTCP VERSION
|
||||
try:
|
||||
|
||||
nickname = self.__Irc.User.get_nickname(self.__Base.clean_uid(serverMsg[1]))
|
||||
dnickname = self.__Config.SERVICE_NICKNAME
|
||||
arg = serverMsg[4].replace(':', '')
|
||||
|
||||
if nickname is None:
|
||||
return None
|
||||
|
||||
if arg == '\x01VERSION\x01':
|
||||
self.send2socket(f':{dnickname} NOTICE {nickname} :\x01VERSION Service {self.__Config.SERVICE_NICKNAME} V{self.__Config.CURRENT_VERSION}\x01')
|
||||
|
||||
return None
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_time(self, serverMsg: list[str]) -> None:
|
||||
"""Sending TIME answer to a requestor
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): List of str coming from the server
|
||||
"""
|
||||
# ['@unrealircd.org/userhost=StatServ@stats.deb.biz.st;draft/bot;bot;msgid=ehfAq3m2yjMjhgWEfi1UCS;time=2024-10-26T13:49:06.299Z', ':00BAAAAAI', 'PRIVMSG', '12ZAAAAAB', ':\x01TIME\x01']
|
||||
# Réponse a un CTCP VERSION
|
||||
try:
|
||||
|
||||
nickname = self.__Irc.User.get_nickname(self.__Base.clean_uid(serverMsg[1]))
|
||||
dnickname = self.__Config.SERVICE_NICKNAME
|
||||
arg = serverMsg[4].replace(':', '')
|
||||
current_datetime = self.__Base.get_datetime()
|
||||
|
||||
if nickname is None:
|
||||
return None
|
||||
|
||||
if arg == '\x01TIME\x01':
|
||||
self.send2socket(f':{dnickname} NOTICE {nickname} :\x01TIME {current_datetime}\x01')
|
||||
|
||||
return None
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_ping(self, serverMsg: list[str]) -> None:
|
||||
"""Sending a PING answer to requestor
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): List of str coming from the server
|
||||
"""
|
||||
# ['@unrealircd.org/userhost=StatServ@stats.deb.biz.st;draft/bot;bot;msgid=ehfAq3m2yjMjhgWEfi1UCS;time=2024-10-26T13:49:06.299Z', ':001INC60B', 'PRIVMSG', '12ZAAAAAB', ':\x01PING', '762382207\x01']
|
||||
# Réponse a un CTCP VERSION
|
||||
try:
|
||||
|
||||
nickname = self.__Irc.User.get_nickname(self.__Base.clean_uid(serverMsg[1]))
|
||||
dnickname = self.__Config.SERVICE_NICKNAME
|
||||
arg = serverMsg[4].replace(':', '')
|
||||
|
||||
if nickname is None:
|
||||
return None
|
||||
|
||||
if arg == '\x01PING':
|
||||
recieved_unixtime = int(serverMsg[5].replace('\x01',''))
|
||||
current_unixtime = self.__Base.get_unixtime()
|
||||
ping_response = current_unixtime - recieved_unixtime
|
||||
|
||||
# self.__Irc.send2socket(f':{dnickname} NOTICE {nickname} :\x01PING {ping_response} secs\x01')
|
||||
self.send_notice(
|
||||
nick_from=dnickname,
|
||||
nick_to=nickname,
|
||||
msg=f"\x01PING {ping_response} secs\x01"
|
||||
)
|
||||
|
||||
return None
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_version_msg(self, serverMsg: list[str]) -> None:
|
||||
"""Handle version coming from the server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original message from the server
|
||||
"""
|
||||
try:
|
||||
# ['@label=0073', ':0014E7P06', 'VERSION', 'PyDefender']
|
||||
getUser = self.__Irc.User.get_User(self.__Irc.User.clean_uid(serverMsg[1]))
|
||||
|
||||
if getUser is None:
|
||||
return None
|
||||
|
||||
response_351 = f"{self.__Config.SERVICE_NAME.capitalize()}-{self.__Config.CURRENT_VERSION} {self.__Config.SERVICE_HOST} {self.name}"
|
||||
self.send2socket(f':{self.__Config.SERVICE_HOST} 351 {getUser.nickname} {response_351}')
|
||||
|
||||
modules = self.__Base.get_all_modules()
|
||||
response_005 = ' | '.join(modules)
|
||||
self.send2socket(f':{self.__Config.SERVICE_HOST} 005 {getUser.nickname} {response_005} are supported by this server')
|
||||
|
||||
return None
|
||||
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
785
core/classes/protocols/unreal6.py
Normal file
785
core/classes/protocols/unreal6.py
Normal file
@@ -0,0 +1,785 @@
|
||||
from re import match, findall
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
from ssl import SSLEOFError, SSLError
|
||||
from dataclasses import dataclass
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
|
||||
class Unrealircd6:
|
||||
|
||||
def __init__(self, ircInstance: 'Irc'):
|
||||
self.name = 'UnrealIRCD-6'
|
||||
|
||||
self.__Irc = ircInstance
|
||||
self.__Config = ircInstance.Config
|
||||
self.__Base = ircInstance.Base
|
||||
self.__Settings = ircInstance.Base.Settings
|
||||
|
||||
self.__Base.logs.info(f"** Loading protocol [{__name__}]")
|
||||
|
||||
def send2socket(self, message: str, print_log: bool = True) -> None:
|
||||
"""Envoit les commandes à envoyer au serveur.
|
||||
|
||||
Args:
|
||||
string (Str): contient la commande à envoyer au serveur.
|
||||
"""
|
||||
try:
|
||||
with self.__Base.lock:
|
||||
self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[0]))
|
||||
if print_log:
|
||||
self.__Base.logs.debug(f'<< {message}')
|
||||
|
||||
except UnicodeDecodeError as ude:
|
||||
self.__Base.logs.error(f'Decode Error try iso-8859-1 - {ude} - {message}')
|
||||
self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[1],'replace'))
|
||||
except UnicodeEncodeError as uee:
|
||||
self.__Base.logs.error(f'Encode Error try iso-8859-1 - {uee} - {message}')
|
||||
self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[1],'replace'))
|
||||
except AssertionError as ae:
|
||||
self.__Base.logs.warning(f'Assertion Error {ae} - message: {message}')
|
||||
except SSLEOFError as soe:
|
||||
self.__Base.logs.error(f"SSLEOFError: {soe} - {message}")
|
||||
except SSLError as se:
|
||||
self.__Base.logs.error(f"SSLError: {se} - {message}")
|
||||
except OSError as oe:
|
||||
self.__Base.logs.error(f"OSError: {oe} - {message}")
|
||||
except AttributeError as ae:
|
||||
self.__Base.logs.critical(f"Attribute Error: {ae}")
|
||||
|
||||
def send_priv_msg(self, nick_from: str, msg: str, channel: str = None, nick_to: str = None):
|
||||
"""Sending PRIVMSG to a channel or to a nickname by batches
|
||||
could be either channel or nickname not both together
|
||||
Args:
|
||||
msg (str): The message to send
|
||||
nick_from (str): The sender nickname
|
||||
channel (str, optional): The receiver channel. Defaults to None.
|
||||
nick_to (str, optional): The reciever nickname. Defaults to None.
|
||||
"""
|
||||
try:
|
||||
batch_size = self.__Config.BATCH_SIZE
|
||||
User_from = self.__Irc.User.get_User(nick_from)
|
||||
User_to = self.__Irc.User.get_User(nick_to) if not nick_to is None else None
|
||||
|
||||
if User_from is None:
|
||||
self.__Base.logs.error(f"The sender nickname [{nick_from}] do not exist")
|
||||
return None
|
||||
|
||||
if not channel is None:
|
||||
for i in range(0, len(str(msg)), batch_size):
|
||||
batch = str(msg)[i:i+batch_size]
|
||||
self.send2socket(f":{User_from.uid} PRIVMSG {channel} :{batch}")
|
||||
|
||||
if not nick_to is None:
|
||||
for i in range(0, len(str(msg)), batch_size):
|
||||
batch = str(msg)[i:i+batch_size]
|
||||
self.send2socket(f":{nick_from} PRIVMSG {User_to.uid} :{batch}")
|
||||
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"General Error: {err}")
|
||||
self.__Base.logs.error(f"General Error: {nick_from} - {channel} - {nick_to}")
|
||||
|
||||
def send_notice(self, nick_from: str, nick_to: str, msg: str) -> None:
|
||||
"""Sending NOTICE by batches
|
||||
|
||||
Args:
|
||||
msg (str): The message to send to the server
|
||||
nick_from (str): The sender Nickname
|
||||
nick_to (str): The reciever nickname
|
||||
"""
|
||||
try:
|
||||
batch_size = self.__Config.BATCH_SIZE
|
||||
User_from = self.__Irc.User.get_User(nick_from)
|
||||
User_to = self.__Irc.User.get_User(nick_to)
|
||||
|
||||
if User_from is None or User_to is None:
|
||||
self.__Base.logs.error(f"The sender [{nick_from}] or the Reciever [{nick_to}] do not exist")
|
||||
return None
|
||||
|
||||
for i in range(0, len(str(msg)), batch_size):
|
||||
batch = str(msg)[i:i+batch_size]
|
||||
self.send2socket(f":{User_from.uid} NOTICE {User_to.uid} :{batch}")
|
||||
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"General Error: {err}")
|
||||
|
||||
def link(self):
|
||||
"""Créer le link et envoyer les informations nécessaires pour la
|
||||
connexion au serveur.
|
||||
"""
|
||||
|
||||
nickname = self.__Config.SERVICE_NICKNAME
|
||||
username = self.__Config.SERVICE_USERNAME
|
||||
realname = self.__Config.SERVICE_REALNAME
|
||||
chan = self.__Config.SERVICE_CHANLOG
|
||||
info = self.__Config.SERVICE_INFO
|
||||
smodes = self.__Config.SERVICE_SMODES
|
||||
cmodes = self.__Config.SERVICE_CMODES
|
||||
umodes = self.__Config.SERVICE_UMODES
|
||||
host = self.__Config.SERVICE_HOST
|
||||
service_name = self.__Config.SERVICE_NAME
|
||||
|
||||
password = self.__Config.SERVEUR_PASSWORD
|
||||
link = self.__Config.SERVEUR_LINK
|
||||
server_id = self.__Config.SERVEUR_ID
|
||||
service_id = self.__Config.SERVICE_ID
|
||||
|
||||
version = self.__Config.CURRENT_VERSION
|
||||
unixtime = self.__Base.get_unixtime()
|
||||
|
||||
self.send2socket(f":{server_id} PASS :{password}", print_log=False)
|
||||
self.send2socket(f":{server_id} PROTOCTL SID NOQUIT NICKv2 SJOIN SJ3 NICKIP TKLEXT2 NEXTBANS CLK EXTSWHOIS MLOCK MTAGS")
|
||||
# self.__Irc.send2socket(f":{sid} PROTOCTL NICKv2 VHP UMODE2 NICKIP SJOIN SJOIN2 SJ3 NOQUIT TKLEXT MLOCK SID MTAGS")
|
||||
self.send2socket(f":{server_id} PROTOCTL EAUTH={link},,,{service_name}-v{version}")
|
||||
self.send2socket(f":{server_id} PROTOCTL SID={server_id}")
|
||||
self.send2socket(f":{server_id} SERVER {link} 1 :{info}")
|
||||
self.send2socket(f":{server_id} {nickname} :Reserved for services")
|
||||
self.send2socket(f":{server_id} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * fwAAAQ== :{realname}")
|
||||
self.sjoin(chan)
|
||||
self.send2socket(f":{server_id} TKL + Q * {nickname} {host} 0 {unixtime} :Reserved for services")
|
||||
self.send2socket(f":{service_id} MODE {chan} {cmodes}")
|
||||
# self.send2socket(f":{service_id} MODE {chan} {umodes} {service_id}")
|
||||
|
||||
self.__Base.logs.debug(f'>> {__name__} Link information sent to the server')
|
||||
|
||||
def gline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None:
|
||||
# TKL + G user host set_by expire_timestamp set_at_timestamp :reason
|
||||
|
||||
self.send2socket(f":{self.__Config.SERVEUR_ID} TKL + G {nickname} {hostname} {set_by} {expire_timestamp} {set_at_timestamp} :{reason}")
|
||||
|
||||
return None
|
||||
|
||||
def set_nick(self, newnickname: str) -> None:
|
||||
"""Change nickname of the server
|
||||
\n This method will also update the User object
|
||||
Args:
|
||||
newnickname (str): New nickname of the server
|
||||
"""
|
||||
self.send2socket(f":{self.__Config.SERVICE_NICKNAME} NICK {newnickname}")
|
||||
|
||||
userObj = self.__Irc.User.get_User(self.__Config.SERVICE_NICKNAME)
|
||||
self.__Irc.User.update(userObj.uid, newnickname)
|
||||
return None
|
||||
|
||||
def squit(self, server_id: str, server_link: str, reason: str) -> None:
|
||||
|
||||
if not reason:
|
||||
reason = 'Service Shutdown'
|
||||
|
||||
self.send2socket(f":{server_id} SQUIT {server_link} :{reason}")
|
||||
return None
|
||||
|
||||
def ungline(self, nickname:str, hostname: str) -> None:
|
||||
|
||||
self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - G {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}")
|
||||
|
||||
return None
|
||||
|
||||
def kline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None:
|
||||
# TKL + k user host set_by expire_timestamp set_at_timestamp :reason
|
||||
|
||||
self.send2socket(f":{self.__Config.SERVEUR_ID} TKL + k {nickname} {hostname} {set_by} {expire_timestamp} {set_at_timestamp} :{reason}")
|
||||
|
||||
return None
|
||||
|
||||
def unkline(self, nickname:str, hostname: str) -> None:
|
||||
|
||||
self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - K {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}")
|
||||
|
||||
return None
|
||||
|
||||
def sjoin(self, channel: str) -> None:
|
||||
"""Server will join a channel with pre defined umodes
|
||||
|
||||
Args:
|
||||
channel (str): Channel to join
|
||||
"""
|
||||
if not self.__Irc.Channel.Is_Channel(channel):
|
||||
self.__Base.logs.error(f"The channel [{channel}] is not valid")
|
||||
return None
|
||||
|
||||
self.send2socket(f":{self.__Config.SERVEUR_ID} SJOIN {self.__Base.get_unixtime()} {channel} {self.__Config.SERVICE_UMODES} :{self.__Config.SERVICE_ID}")
|
||||
self.send2socket(f":{self.__Config.SERVICE_ID} MODE {channel} {self.__Config.SERVICE_UMODES} {self.__Config.SERVICE_ID}")
|
||||
|
||||
# Add defender to the channel uids list
|
||||
self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[self.__Config.SERVICE_ID]))
|
||||
return None
|
||||
|
||||
def send_sapart(self, nick_to_sapart: str, channel_name: str) -> None:
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
from_nick (str): _description_
|
||||
nick_to (str): _description_
|
||||
channel_name (str): _description_
|
||||
"""
|
||||
try:
|
||||
|
||||
userObj = self.__Irc.User.get_User(uidornickname=nick_to_sapart)
|
||||
chanObj = self.__Irc.Channel.get_Channel(channel_name)
|
||||
service_uid = self.__Config.SERVICE_ID
|
||||
|
||||
if userObj is None or chanObj is None:
|
||||
return None
|
||||
|
||||
self.send2socket(f":{service_uid} SAPART {userObj.nickname} {chanObj.name}")
|
||||
self.__Irc.Channel.delete_user_from_channel(chanObj.name, userObj.uid)
|
||||
|
||||
return None
|
||||
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def send_sajoin(self, nick_to_sajoin: str, channel_name: str) -> None:
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
nick_to_sajoin (str): _description_
|
||||
channel_name (str): _description_
|
||||
"""
|
||||
try:
|
||||
|
||||
userObj = self.__Irc.User.get_User(uidornickname=nick_to_sajoin)
|
||||
chanObj = self.__Irc.Channel.get_Channel(channel_name)
|
||||
service_uid = self.__Config.SERVICE_ID
|
||||
|
||||
if userObj is None:
|
||||
# User not exist: leave
|
||||
return None
|
||||
|
||||
if chanObj is None:
|
||||
# Channel not exist
|
||||
if not self.__Irc.Channel.Is_Channel(channel_name):
|
||||
# Incorrect channel: leave
|
||||
return None
|
||||
|
||||
# Create the new channel with the uid
|
||||
newChanObj = self.__Irc.Loader.Definition.MChannel(name=channel_name, uids=[userObj.uid])
|
||||
self.__Irc.Channel.insert(newChanObj)
|
||||
self.send2socket(f":{service_uid} SAJOIN {userObj.nickname} {newChanObj.name}")
|
||||
|
||||
else:
|
||||
self.__Irc.Channel.add_user_to_a_channel(channel_name=channel_name, uid=userObj.uid)
|
||||
self.send2socket(f":{service_uid} SAJOIN {userObj.nickname} {chanObj.name}")
|
||||
|
||||
return None
|
||||
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def send_svs_mode(self, nickname: str, user_mode: str) -> None:
|
||||
try:
|
||||
|
||||
userObj = self.__Irc.User.get_User(uidornickname=nickname)
|
||||
service_uid = self.__Config.SERVICE_ID
|
||||
|
||||
if userObj is None:
|
||||
# User not exist: leave
|
||||
return None
|
||||
|
||||
self.send2socket(f':{service_uid} SVSMODE {nickname} {user_mode}')
|
||||
|
||||
# Update new mode
|
||||
self.__Irc.User.update_mode(userObj.uid, user_mode)
|
||||
|
||||
return None
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def send_quit(self, uid: str, reason: str, print_log: True) -> None:
|
||||
"""Send quit message
|
||||
- Delete uid from User object
|
||||
- Delete uid from Clone object
|
||||
- Delete uid from Reputation object
|
||||
|
||||
Args:
|
||||
uidornickname (str): The UID or the Nickname
|
||||
reason (str): The reason for the quit
|
||||
"""
|
||||
userObj = self.__Irc.User.get_User(uidornickname=uid)
|
||||
cloneObj = self.__Irc.Clone.get_Clone(uidornickname=uid)
|
||||
reputationObj = self.__Irc.Reputation.get_Reputation(uidornickname=uid)
|
||||
|
||||
if not userObj is None:
|
||||
self.send2socket(f":{userObj.uid} QUIT :{reason}", print_log=print_log)
|
||||
self.__Irc.User.delete(userObj.uid)
|
||||
|
||||
if not cloneObj is None:
|
||||
self.__Irc.Clone.delete(cloneObj.uid)
|
||||
|
||||
if not reputationObj is None:
|
||||
self.__Irc.Reputation.delete(reputationObj.uid)
|
||||
|
||||
if not self.__Irc.Channel.delete_user_from_all_channel(uid):
|
||||
self.__Base.logs.error(f"The UID [{uid}] has not been deleted from all channels")
|
||||
|
||||
return None
|
||||
|
||||
def send_uid(self, nickname:str, username: str, hostname: str, uid:str, umodes: str, vhost: str, remote_ip: str, realname: str, print_log: bool = True) -> None:
|
||||
"""Send UID to the server
|
||||
- Insert User to User Object
|
||||
Args:
|
||||
nickname (str): Nickname of the client
|
||||
username (str): Username of the client
|
||||
hostname (str): Hostname of the client you want to create
|
||||
uid (str): UID of the client you want to create
|
||||
umodes (str): umodes of the client you want to create
|
||||
vhost (str): vhost of the client you want to create
|
||||
remote_ip (str): remote_ip of the client you want to create
|
||||
realname (str): realname of the client you want to create
|
||||
print_log (bool, optional): print logs if true. Defaults to True.
|
||||
"""
|
||||
# {self.Config.SERVEUR_ID} UID
|
||||
# {clone.nickname} 1 {self.Base.get_unixtime()} {clone.username} {clone.hostname} {clone.uid} * {clone.umodes} {clone.vhost} * {self.Base.encode_ip(clone.remote_ip)} :{clone.realname}
|
||||
try:
|
||||
unixtime = self.__Base.get_unixtime()
|
||||
encoded_ip = self.__Base.encode_ip(remote_ip)
|
||||
|
||||
# Create the user
|
||||
self.__Irc.User.insert(
|
||||
self.__Irc.Loader.Definition.MUser(
|
||||
uid=uid, nickname=nickname, username=username,
|
||||
realname=realname,hostname=hostname, umodes=umodes,
|
||||
vhost=vhost, remote_ip=remote_ip
|
||||
)
|
||||
)
|
||||
|
||||
uid_msg = f":{self.__Config.SERVEUR_ID} UID {nickname} 1 {unixtime} {username} {hostname} {uid} * {umodes} {vhost} * {encoded_ip} :{realname}"
|
||||
|
||||
self.send2socket(uid_msg, print_log=print_log)
|
||||
|
||||
return None
|
||||
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def send_join_chan(self, uidornickname: str, channel: str, password: str = None, print_log: bool = True) -> None:
|
||||
"""Joining a channel
|
||||
|
||||
Args:
|
||||
uidornickname (str): UID or nickname that need to join
|
||||
channel (str): channel to join
|
||||
password (str, optional): The password of the channel to join. Default to None
|
||||
print_log (bool, optional): Write logs. Defaults to True.
|
||||
"""
|
||||
|
||||
userObj = self.__Irc.User.get_User(uidornickname)
|
||||
passwordChannel = password if not password is None else ''
|
||||
|
||||
if userObj is None:
|
||||
return None
|
||||
|
||||
if not self.__Irc.Channel.Is_Channel(channel):
|
||||
self.__Base.logs.error(f"The channel [{channel}] is not valid")
|
||||
return None
|
||||
|
||||
self.send2socket(f":{userObj.uid} JOIN {channel} {passwordChannel}", print_log=print_log)
|
||||
|
||||
if uidornickname == self.__Config.SERVICE_NICKNAME or uidornickname == self.__Config.SERVICE_ID:
|
||||
self.send2socket(f":{self.__Config.SERVICE_ID} MODE {channel} {self.__Config.SERVICE_UMODES} {self.__Config.SERVICE_ID}")
|
||||
|
||||
# Add defender to the channel uids list
|
||||
self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[userObj.uid]))
|
||||
return None
|
||||
|
||||
def send_part_chan(self, uidornickname:str, channel: str, print_log: bool = True) -> None:
|
||||
"""Part from a channel
|
||||
|
||||
Args:
|
||||
uidornickname (str): UID or nickname that need to join
|
||||
channel (str): channel to join
|
||||
print_log (bool, optional): Write logs. Defaults to True.
|
||||
"""
|
||||
|
||||
userObj = self.__Irc.User.get_User(uidornickname)
|
||||
|
||||
if userObj is None:
|
||||
self.__Base.logs.error(f"The user [{uidornickname}] is not valid")
|
||||
return None
|
||||
|
||||
if not self.__Irc.Channel.Is_Channel(channel):
|
||||
self.__Base.logs.error(f"The channel [{channel}] is not valid")
|
||||
return None
|
||||
|
||||
self.send2socket(f":{userObj.uid} PART {channel}", print_log=print_log)
|
||||
|
||||
# Add defender to the channel uids list
|
||||
self.__Irc.Channel.delete_user_from_channel(channel, userObj.uid)
|
||||
return None
|
||||
|
||||
def on_umode2(self, serverMsg: list[str]) -> None:
|
||||
"""Handle umode2 coming from a server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original server message
|
||||
"""
|
||||
try:
|
||||
# [':adator_', 'UMODE2', '-iwx']
|
||||
|
||||
userObj = self.__Irc.User.get_User(str(serverMsg[0]).lstrip(':'))
|
||||
userMode = serverMsg[2]
|
||||
|
||||
if userObj is None: # If user is not created
|
||||
return None
|
||||
|
||||
# save previous user modes
|
||||
old_umodes = userObj.umodes
|
||||
|
||||
# TODO : User object should be able to update user modes
|
||||
if self.__Irc.User.update_mode(userObj.uid, userMode):
|
||||
return None
|
||||
# self.__Base.logs.debug(f"Updating user mode for [{userObj.nickname}] [{old_umodes}] => [{userObj.umodes}]")
|
||||
|
||||
return None
|
||||
|
||||
except IndexError as ie:
|
||||
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_quit(self, serverMsg: list[str]) -> None:
|
||||
"""Handle quit coming from a server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original server message
|
||||
"""
|
||||
try:
|
||||
# ['@unrealircd.org/userhost=...@192.168.1.10;unrealircd.org/userip=...@192.168.1.10;msgid=CssUrV08BzekYuq7BfvPHn;time=2024-11-02T15:03:33.182Z', ':001JKNY0N', 'QUIT', ':Quit:', '....']
|
||||
|
||||
uid_who_quit = str(serverMsg[1]).lstrip(':')
|
||||
|
||||
self.__Irc.Channel.delete_user_from_all_channel(uid_who_quit)
|
||||
self.__Irc.User.delete(uid_who_quit)
|
||||
self.__Irc.Reputation.delete(uid_who_quit)
|
||||
self.__Irc.Clone.delete(uid_who_quit)
|
||||
|
||||
return None
|
||||
|
||||
except IndexError as ie:
|
||||
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_squit(self, serverMsg: list[str]) -> None:
|
||||
"""Handle squit coming from a server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original server message
|
||||
"""
|
||||
# ['@msgid=QOEolbRxdhpVW5c8qLkbAU;time=2024-09-21T17:33:16.547Z', 'SQUIT', 'defender.deb.biz.st', ':Connection', 'closed']
|
||||
|
||||
server_hostname = serverMsg[2]
|
||||
uid_to_delete = None
|
||||
for s_user in self.__Irc.User.UID_DB:
|
||||
if s_user.hostname == server_hostname and 'S' in s_user.umodes:
|
||||
uid_to_delete = s_user.uid
|
||||
|
||||
if uid_to_delete is None:
|
||||
return None
|
||||
|
||||
self.__Irc.User.delete(uid_to_delete)
|
||||
self.__Irc.Channel.delete_user_from_all_channel(uid_to_delete)
|
||||
|
||||
return None
|
||||
|
||||
def on_protoctl(self, serverMsg: list[str]) -> None:
|
||||
"""Handle protoctl coming from a server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original server message
|
||||
"""
|
||||
# ['PROTOCTL', 'CHANMODES=beI,fkL,lFH,cdimnprstzCDGKMNOPQRSTVZ', 'USERMODES=diopqrstwxzBDGHIRSTWZ', 'BOOTED=1728815798', 'PREFIX=(qaohv)~&@%+', 'SID=001', 'MLOCK', 'TS=1730662755', 'EXTSWHOIS']
|
||||
if len(serverMsg) > 5:
|
||||
if '=' in serverMsg[5]:
|
||||
serveur_hosting_id = str(serverMsg[5]).split('=')
|
||||
self.__Config.HSID = serveur_hosting_id[1]
|
||||
if 'USERMODES=' in serverMsg[2]:
|
||||
self.__Settings.USER_MODES = list(serverMsg[2].split('=')[1])
|
||||
|
||||
return None
|
||||
|
||||
def on_nick(self, serverMsg: list[str]) -> None:
|
||||
"""Handle nick coming from a server
|
||||
new nickname
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original server message
|
||||
"""
|
||||
try:
|
||||
# ['@unrealircd.org/geoip=FR;unrealircd.org/', ':001OOU2H3', 'NICK', 'WebIrc', '1703795844']
|
||||
# Changement de nickname
|
||||
|
||||
uid = str(serverMsg[1]).lstrip(':')
|
||||
newnickname = serverMsg[3]
|
||||
self.__Irc.User.update(uid, newnickname)
|
||||
|
||||
return None
|
||||
|
||||
except IndexError as ie:
|
||||
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_sjoin(self, serverMsg: list[str]) -> None:
|
||||
"""Handle sjoin coming from a server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original server message
|
||||
"""
|
||||
try:
|
||||
# ['@msgid=5sTwGdj349D82L96p749SY;time=2024-08-15T09:50:23.528Z', ':001', 'SJOIN', '1721564574', '#welcome', ':001JD94QH']
|
||||
# ['@msgid=bvceb6HthbLJapgGLXn1b0;time=2024-08-15T09:50:11.464Z', ':001', 'SJOIN', '1721564574', '#welcome', '+lnrt', '13', ':001CIVLQF', '+11ZAAAAAB', '001QGR10C', '*@0014UE10B', '001NL1O07', '001SWZR05', '001HB8G04', '@00BAAAAAJ', '0019M7101']
|
||||
# ['@msgid=SKUeuVzOrTShRDduq8VerX;time=2024-08-23T19:37:04.266Z', ':001', 'SJOIN', '1723993047', '#welcome', '+lnrt', '13',
|
||||
# ':001T6VU3F', '001JGWB2K', '@11ZAAAAAB',
|
||||
# '001F16WGR', '001X9YMGQ', '*+001DYPFGP', '@00BAAAAAJ', '001AAGOG9', '001FMFVG8', '001DAEEG7',
|
||||
# '&~G:unknown-users', '"~G:websocket-users', '"~G:known-users', '"~G:webirc-users']
|
||||
serverMsg_copy = serverMsg.copy()
|
||||
serverMsg_copy.pop(0)
|
||||
channel = str(serverMsg_copy[3]).lower()
|
||||
len_cmd = len(serverMsg_copy)
|
||||
list_users:list = []
|
||||
occurence = 0
|
||||
start_boucle = 0
|
||||
|
||||
# Trouver le premier user
|
||||
for i in range(len_cmd):
|
||||
s: list = findall(fr':', serverMsg_copy[i])
|
||||
if s:
|
||||
occurence += 1
|
||||
if occurence == 2:
|
||||
start_boucle = i
|
||||
|
||||
# Boucle qui va ajouter l'ensemble des users (UID)
|
||||
for i in range(start_boucle, len(serverMsg_copy)):
|
||||
parsed_UID = str(serverMsg_copy[i])
|
||||
clean_uid = self.__Irc.User.clean_uid(parsed_UID)
|
||||
if not clean_uid is None and len(clean_uid) == 9:
|
||||
list_users.append(clean_uid)
|
||||
|
||||
if list_users:
|
||||
self.__Irc.Channel.insert(
|
||||
self.__Irc.Loader.Definition.MChannel(
|
||||
name=channel,
|
||||
uids=list_users
|
||||
)
|
||||
)
|
||||
return None
|
||||
|
||||
except IndexError as ie:
|
||||
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_part(self, serverMsg: list[str]) -> None:
|
||||
"""Handle part coming from a server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original server message
|
||||
"""
|
||||
try:
|
||||
# ['@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']
|
||||
|
||||
uid = str(serverMsg[1]).lstrip(':')
|
||||
channel = str(serverMsg[3]).lower()
|
||||
self.__Irc.Channel.delete_user_from_channel(channel, uid)
|
||||
|
||||
return None
|
||||
|
||||
except IndexError as ie:
|
||||
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_uid(self, serverMsg: list[str]) -> None:
|
||||
"""Handle uid message coming from the server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original server message
|
||||
"""
|
||||
# ['@s2s-md/geoip=cc=GB|cd=United\\sKingdom|asn=16276|asname=OVH\\sSAS;s2s-md/tls_cipher=TLSv1.3-TLS_CHACHA20_POLY1305_SHA256;s2s-md/creationtime=1721564601',
|
||||
# ':001', 'UID', 'albatros', '0', '1721564597', 'albatros', 'vps-91b2f28b.vps.ovh.net',
|
||||
# '001HB8G04', '0', '+iwxz', 'Clk-A62F1D18.vps.ovh.net', 'Clk-A62F1D18.vps.ovh.net', 'MyZBwg==', ':...']
|
||||
try:
|
||||
|
||||
isWebirc = True if 'webirc' in serverMsg[0] else False
|
||||
isWebsocket = True if 'websocket' in serverMsg[0] else False
|
||||
|
||||
uid = str(serverMsg[8])
|
||||
nickname = str(serverMsg[3])
|
||||
username = str(serverMsg[6])
|
||||
hostname = str(serverMsg[7])
|
||||
umodes = str(serverMsg[10])
|
||||
vhost = str(serverMsg[11])
|
||||
|
||||
if not 'S' in umodes:
|
||||
remote_ip = self.__Base.decode_ip(str(serverMsg[13]))
|
||||
else:
|
||||
remote_ip = '127.0.0.1'
|
||||
|
||||
# extract realname
|
||||
realname = ' '.join(serverMsg[14:]).lstrip(':')
|
||||
|
||||
# Extract Geoip information
|
||||
pattern = r'^.*geoip=cc=(\S{2}).*$'
|
||||
geoip_match = match(pattern, serverMsg[0])
|
||||
|
||||
if geoip_match:
|
||||
geoip = geoip_match.group(1)
|
||||
else:
|
||||
geoip = None
|
||||
|
||||
score_connexion = 0
|
||||
|
||||
self.__Irc.User.insert(
|
||||
self.__Irc.Loader.Definition.MUser(
|
||||
uid=uid,
|
||||
nickname=nickname,
|
||||
username=username,
|
||||
realname=realname,
|
||||
hostname=hostname,
|
||||
umodes=umodes,
|
||||
vhost=vhost,
|
||||
isWebirc=isWebirc,
|
||||
isWebsocket=isWebsocket,
|
||||
remote_ip=remote_ip,
|
||||
geoip=geoip,
|
||||
score_connexion=score_connexion,
|
||||
connexion_datetime=datetime.now()
|
||||
)
|
||||
)
|
||||
return None
|
||||
except IndexError as ie:
|
||||
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_server_ping(self, serverMsg: list[str]) -> None:
|
||||
"""Send a PONG message to the server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): List of str coming from the server
|
||||
"""
|
||||
try:
|
||||
|
||||
pong = str(serverMsg[1]).replace(':','')
|
||||
self.send2socket(f"PONG :{pong}", print_log=False)
|
||||
|
||||
return None
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_version(self, serverMsg: list[str]) -> None:
|
||||
"""Sending Server Version to the server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): List of str coming from the server
|
||||
"""
|
||||
# ['@unrealircd.org/userhost=StatServ@stats.deb.biz.st;draft/bot;bot;msgid=ehfAq3m2yjMjhgWEfi1UCS;time=2024-10-26T13:49:06.299Z', ':00BAAAAAI', 'PRIVMSG', '12ZAAAAAB', ':\x01VERSION\x01']
|
||||
# Réponse a un CTCP VERSION
|
||||
try:
|
||||
|
||||
nickname = self.__Irc.User.get_nickname(self.__Base.clean_uid(serverMsg[1]))
|
||||
dnickname = self.__Config.SERVICE_NICKNAME
|
||||
arg = serverMsg[4].replace(':', '')
|
||||
|
||||
if nickname is None:
|
||||
return None
|
||||
|
||||
if arg == '\x01VERSION\x01':
|
||||
self.send2socket(f':{dnickname} NOTICE {nickname} :\x01VERSION Service {self.__Config.SERVICE_NICKNAME} V{self.__Config.CURRENT_VERSION}\x01')
|
||||
|
||||
return None
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_time(self, serverMsg: list[str]) -> None:
|
||||
"""Sending TIME answer to a requestor
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): List of str coming from the server
|
||||
"""
|
||||
# ['@unrealircd.org/userhost=StatServ@stats.deb.biz.st;draft/bot;bot;msgid=ehfAq3m2yjMjhgWEfi1UCS;time=2024-10-26T13:49:06.299Z', ':00BAAAAAI', 'PRIVMSG', '12ZAAAAAB', ':\x01TIME\x01']
|
||||
# Réponse a un CTCP VERSION
|
||||
try:
|
||||
|
||||
nickname = self.__Irc.User.get_nickname(self.__Base.clean_uid(serverMsg[1]))
|
||||
dnickname = self.__Config.SERVICE_NICKNAME
|
||||
arg = serverMsg[4].replace(':', '')
|
||||
current_datetime = self.__Base.get_datetime()
|
||||
|
||||
if nickname is None:
|
||||
return None
|
||||
|
||||
if arg == '\x01TIME\x01':
|
||||
self.send2socket(f':{dnickname} NOTICE {nickname} :\x01TIME {current_datetime}\x01')
|
||||
|
||||
return None
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_ping(self, serverMsg: list[str]) -> None:
|
||||
"""Sending a PING answer to requestor
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): List of str coming from the server
|
||||
"""
|
||||
# ['@unrealircd.org/userhost=StatServ@stats.deb.biz.st;draft/bot;bot;msgid=ehfAq3m2yjMjhgWEfi1UCS;time=2024-10-26T13:49:06.299Z', ':001INC60B', 'PRIVMSG', '12ZAAAAAB', ':\x01PING', '762382207\x01']
|
||||
# Réponse a un CTCP VERSION
|
||||
try:
|
||||
|
||||
nickname = self.__Irc.User.get_nickname(self.__Base.clean_uid(serverMsg[1]))
|
||||
dnickname = self.__Config.SERVICE_NICKNAME
|
||||
arg = serverMsg[4].replace(':', '')
|
||||
|
||||
if nickname is None:
|
||||
return None
|
||||
|
||||
if arg == '\x01PING':
|
||||
recieved_unixtime = int(serverMsg[5].replace('\x01',''))
|
||||
current_unixtime = self.__Base.get_unixtime()
|
||||
ping_response = current_unixtime - recieved_unixtime
|
||||
|
||||
# self.__Irc.send2socket(f':{dnickname} NOTICE {nickname} :\x01PING {ping_response} secs\x01')
|
||||
self.send_notice(
|
||||
nick_from=dnickname,
|
||||
nick_to=nickname,
|
||||
msg=f"\x01PING {ping_response} secs\x01"
|
||||
)
|
||||
|
||||
return None
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
|
||||
def on_version_msg(self, serverMsg: list[str]) -> None:
|
||||
"""Handle version coming from the server
|
||||
|
||||
Args:
|
||||
serverMsg (list[str]): Original message from the server
|
||||
"""
|
||||
try:
|
||||
# ['@label=0073', ':0014E7P06', 'VERSION', 'PyDefender']
|
||||
serverMsg_copy = serverMsg.copy()
|
||||
if '@' in list(serverMsg_copy[0])[0]:
|
||||
serverMsg_copy.pop(0)
|
||||
|
||||
getUser = self.__Irc.User.get_User(self.__Irc.User.clean_uid(serverMsg_copy[0]))
|
||||
|
||||
if getUser is None:
|
||||
return None
|
||||
|
||||
response_351 = f"{self.__Config.SERVICE_NAME.capitalize()}-{self.__Config.CURRENT_VERSION} {self.__Config.SERVICE_HOST} {self.name}"
|
||||
self.send2socket(f':{self.__Config.SERVICE_HOST} 351 {getUser.nickname} {response_351}')
|
||||
|
||||
modules = self.__Base.get_all_modules()
|
||||
response_005 = ' | '.join(modules)
|
||||
self.send2socket(f':{self.__Config.SERVICE_HOST} 005 {getUser.nickname} {response_005} are supported by this server')
|
||||
|
||||
response_005 = ''.join(self.__Settings.USER_MODES)
|
||||
self.send2socket(f":{self.__Config.SERVICE_HOST} 005 {getUser.nickname} {response_005} are supported by this server")
|
||||
|
||||
return None
|
||||
|
||||
except Exception as err:
|
||||
self.__Base.logs.error(f"{__name__} - General Error: {err}")
|
||||
158
core/classes/reputation.py
Normal file
158
core/classes/reputation.py
Normal file
@@ -0,0 +1,158 @@
|
||||
from typing import Union
|
||||
from core.definition import MReputation
|
||||
from core.base import Base
|
||||
|
||||
class Reputation:
|
||||
|
||||
UID_REPUTATION_DB: list[MReputation] = []
|
||||
|
||||
def __init__(self, baseObj: Base) -> None:
|
||||
|
||||
self.Logs = baseObj.logs
|
||||
self.MReputation: MReputation = MReputation
|
||||
|
||||
return None
|
||||
|
||||
def insert(self, newReputationUser: MReputation) -> bool:
|
||||
"""Insert a new Reputation User object
|
||||
|
||||
Args:
|
||||
newReputationUser (MReputation): New Reputation Model object
|
||||
|
||||
Returns:
|
||||
bool: True if inserted
|
||||
"""
|
||||
result = False
|
||||
exist = False
|
||||
|
||||
for record in self.UID_REPUTATION_DB:
|
||||
if record.uid == newReputationUser.uid:
|
||||
# If the user exist then return False and do not go further
|
||||
exist = True
|
||||
self.Logs.debug(f'{record.uid} already exist')
|
||||
return result
|
||||
|
||||
if not exist:
|
||||
self.UID_REPUTATION_DB.append(newReputationUser)
|
||||
result = True
|
||||
self.Logs.debug(f'New Reputation User Captured: ({newReputationUser})')
|
||||
|
||||
if not result:
|
||||
self.Logs.critical(f'The Reputation User Object was not inserted {newReputationUser}')
|
||||
|
||||
return result
|
||||
|
||||
def update(self, uid: str, newNickname: str) -> bool:
|
||||
"""Update the nickname starting from the UID
|
||||
|
||||
Args:
|
||||
uid (str): UID of the user
|
||||
newNickname (str): New nickname
|
||||
|
||||
Returns:
|
||||
bool: True if updated
|
||||
"""
|
||||
|
||||
reputationObj = self.get_Reputation(uid)
|
||||
|
||||
if reputationObj is None:
|
||||
return False
|
||||
|
||||
reputationObj.nickname = newNickname
|
||||
|
||||
return True
|
||||
|
||||
def delete(self, uid: str) -> bool:
|
||||
"""Delete the User starting from the UID
|
||||
|
||||
Args:
|
||||
uid (str): UID of the user
|
||||
|
||||
Returns:
|
||||
bool: True if deleted
|
||||
"""
|
||||
result = False
|
||||
|
||||
if not self.is_exist(uid):
|
||||
return result
|
||||
|
||||
for record in self.UID_REPUTATION_DB:
|
||||
if record.uid == uid:
|
||||
# If the user exist then remove and return True and do not go further
|
||||
self.UID_REPUTATION_DB.remove(record)
|
||||
result = True
|
||||
self.Logs.debug(f'UID ({record.uid}) has been deleted')
|
||||
return result
|
||||
|
||||
if not result:
|
||||
self.Logs.critical(f'The UID {uid} was not deleted')
|
||||
|
||||
return result
|
||||
|
||||
def get_Reputation(self, uidornickname: str) -> Union[MReputation, None]:
|
||||
"""Get The User Object model
|
||||
|
||||
Args:
|
||||
uidornickname (str): UID or Nickname
|
||||
|
||||
Returns:
|
||||
UserModel|None: The UserModel Object | None
|
||||
"""
|
||||
User = None
|
||||
for record in self.UID_REPUTATION_DB:
|
||||
if record.uid == uidornickname:
|
||||
User = record
|
||||
elif record.nickname == uidornickname:
|
||||
User = record
|
||||
|
||||
return User
|
||||
|
||||
def get_uid(self, uidornickname:str) -> Union[str, None]:
|
||||
"""Get the UID of the user starting from the UID or the Nickname
|
||||
|
||||
Args:
|
||||
uidornickname (str): UID or Nickname
|
||||
|
||||
Returns:
|
||||
str|None: Return the UID
|
||||
"""
|
||||
|
||||
reputationObj = self.get_Reputation(uidornickname)
|
||||
|
||||
if reputationObj is None:
|
||||
return None
|
||||
|
||||
return reputationObj.uid
|
||||
|
||||
def get_nickname(self, uidornickname:str) -> Union[str, None]:
|
||||
"""Get the Nickname starting from UID or the nickname
|
||||
|
||||
Args:
|
||||
uidornickname (str): UID or Nickname of the user
|
||||
|
||||
Returns:
|
||||
str|None: the nickname
|
||||
"""
|
||||
reputationObj = self.get_Reputation(uidornickname)
|
||||
|
||||
if reputationObj is None:
|
||||
return None
|
||||
|
||||
return reputationObj.nickname
|
||||
|
||||
def is_exist(self, uidornickname: str) -> bool:
|
||||
"""Check if the UID or the nickname exist in the reputation DB
|
||||
|
||||
Args:
|
||||
uidornickname (str): The UID or the NICKNAME
|
||||
|
||||
Returns:
|
||||
bool: True if exist
|
||||
"""
|
||||
|
||||
reputationObj = self.get_Reputation(uidornickname)
|
||||
|
||||
if reputationObj is None:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
14
core/classes/settings.py
Normal file
14
core/classes/settings.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from threading import Timer, Thread, RLock
|
||||
from socket import socket
|
||||
|
||||
class Settings:
|
||||
|
||||
RUNNING_TIMERS: list[Timer] = []
|
||||
RUNNING_THREADS: list[Thread] = []
|
||||
RUNNING_SOCKETS: list[socket] = []
|
||||
PERIODIC_FUNC: dict[object] = {}
|
||||
LOCK: RLock = RLock()
|
||||
|
||||
CONSOLE: bool = False
|
||||
|
||||
USER_MODES: list[str] = []
|
||||
220
core/classes/user.py
Normal file
220
core/classes/user.py
Normal file
@@ -0,0 +1,220 @@
|
||||
from re import sub
|
||||
from typing import Union, TYPE_CHECKING
|
||||
from dataclasses import asdict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.base import Base
|
||||
from core.definition import MUser
|
||||
|
||||
class User:
|
||||
|
||||
UID_DB: list['MUser'] = []
|
||||
|
||||
def __init__(self, baseObj: 'Base') -> None:
|
||||
|
||||
self.Logs = baseObj.logs
|
||||
self.Base = baseObj
|
||||
|
||||
return None
|
||||
|
||||
def insert(self, newUser: 'MUser') -> bool:
|
||||
"""Insert a new User object
|
||||
|
||||
Args:
|
||||
newUser (UserModel): New userModel object
|
||||
|
||||
Returns:
|
||||
bool: True if inserted
|
||||
"""
|
||||
|
||||
userObj = self.get_User(newUser.uid)
|
||||
|
||||
if not userObj is None:
|
||||
# User already created return False
|
||||
return False
|
||||
|
||||
self.UID_DB.append(newUser)
|
||||
|
||||
return True
|
||||
|
||||
def update(self, uid: str, newNickname: str) -> bool:
|
||||
"""Update the nickname starting from the UID
|
||||
|
||||
Args:
|
||||
uid (str): UID of the user
|
||||
newNickname (str): New nickname
|
||||
|
||||
Returns:
|
||||
bool: True if updated
|
||||
"""
|
||||
userObj = self.get_User(uidornickname=uid)
|
||||
|
||||
if userObj is None:
|
||||
return False
|
||||
|
||||
userObj.nickname = newNickname
|
||||
|
||||
return True
|
||||
|
||||
def update_mode(self, uidornickname: str, modes: str) -> bool:
|
||||
"""Updating user mode
|
||||
|
||||
Args:
|
||||
uidornickname (str): The UID or Nickname of the user
|
||||
modes (str): new modes to update
|
||||
|
||||
Returns:
|
||||
bool: True if user mode has been updaed
|
||||
"""
|
||||
response = True
|
||||
userObj = self.get_User(uidornickname=uidornickname)
|
||||
|
||||
if userObj is None:
|
||||
return False
|
||||
|
||||
action = modes[0]
|
||||
new_modes = modes[1:]
|
||||
|
||||
existing_umodes = userObj.umodes
|
||||
umodes = userObj.umodes
|
||||
|
||||
if action == '+':
|
||||
|
||||
for nm in new_modes:
|
||||
if nm not in existing_umodes:
|
||||
umodes += nm
|
||||
|
||||
elif action == '-':
|
||||
for nm in new_modes:
|
||||
if nm in existing_umodes:
|
||||
umodes = umodes.replace(nm, '')
|
||||
else:
|
||||
return False
|
||||
|
||||
liste_umodes = list(umodes)
|
||||
final_umodes_liste = [x for x in self.Base.Settings.USER_MODES if x in liste_umodes]
|
||||
final_umodes = ''.join(final_umodes_liste)
|
||||
|
||||
userObj.umodes = f"+{final_umodes}"
|
||||
|
||||
return response
|
||||
|
||||
def delete(self, uid: str) -> bool:
|
||||
"""Delete the User starting from the UID
|
||||
|
||||
Args:
|
||||
uid (str): UID of the user
|
||||
|
||||
Returns:
|
||||
bool: True if deleted
|
||||
"""
|
||||
|
||||
userObj = self.get_User(uidornickname=uid)
|
||||
|
||||
if userObj is None:
|
||||
return False
|
||||
|
||||
self.UID_DB.remove(userObj)
|
||||
|
||||
return True
|
||||
|
||||
def get_User(self, uidornickname: str) -> Union['MUser', None]:
|
||||
"""Get The User Object model
|
||||
|
||||
Args:
|
||||
uidornickname (str): UID or Nickname
|
||||
|
||||
Returns:
|
||||
UserModel|None: The UserModel Object | None
|
||||
"""
|
||||
User = None
|
||||
for record in self.UID_DB:
|
||||
if record.uid == uidornickname:
|
||||
User = record
|
||||
elif record.nickname == uidornickname:
|
||||
User = record
|
||||
|
||||
return User
|
||||
|
||||
def get_uid(self, uidornickname:str) -> Union[str, None]:
|
||||
"""Get the UID of the user starting from the UID or the Nickname
|
||||
|
||||
Args:
|
||||
uidornickname (str): UID or Nickname
|
||||
|
||||
Returns:
|
||||
str|None: Return the UID
|
||||
"""
|
||||
|
||||
userObj = self.get_User(uidornickname=uidornickname)
|
||||
|
||||
if userObj is None:
|
||||
return None
|
||||
|
||||
return userObj.uid
|
||||
|
||||
def get_nickname(self, uidornickname:str) -> Union[str, None]:
|
||||
"""Get the Nickname starting from UID or the nickname
|
||||
|
||||
Args:
|
||||
uidornickname (str): UID or Nickname of the user
|
||||
|
||||
Returns:
|
||||
str|None: the nickname
|
||||
"""
|
||||
userObj = self.get_User(uidornickname=uidornickname)
|
||||
|
||||
if userObj is None:
|
||||
return None
|
||||
|
||||
return userObj.nickname
|
||||
|
||||
def get_User_AsDict(self, uidornickname: str) -> Union[dict[str, any], None]:
|
||||
"""Transform User Object to a dictionary
|
||||
|
||||
Args:
|
||||
uidornickname (str): The UID or The nickname
|
||||
|
||||
Returns:
|
||||
Union[dict[str, any], None]: User Object as a dictionary or None
|
||||
"""
|
||||
userObj = self.get_User(uidornickname=uidornickname)
|
||||
|
||||
if userObj is None:
|
||||
return None
|
||||
|
||||
return asdict(userObj)
|
||||
|
||||
def is_exist(self, uidornikname: str) -> bool:
|
||||
"""Check if the UID or the nickname exist in the USER DB
|
||||
|
||||
Args:
|
||||
uidornickname (str): The UID or the NICKNAME
|
||||
|
||||
Returns:
|
||||
bool: True if exist
|
||||
"""
|
||||
userObj = self.get_User(uidornickname=uidornikname)
|
||||
|
||||
if userObj is None:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def clean_uid(self, uid: str) -> Union[str, None]:
|
||||
"""Clean UID by removing @ / % / + / ~ / * / :
|
||||
|
||||
Args:
|
||||
uid (str): The UID to clean
|
||||
|
||||
Returns:
|
||||
str: Clean UID without any sign
|
||||
"""
|
||||
|
||||
pattern = fr'[:|@|%|\+|~|\*]*'
|
||||
parsed_UID = sub(pattern, '', uid)
|
||||
|
||||
if not parsed_UID:
|
||||
return None
|
||||
|
||||
return parsed_UID
|
||||
@@ -1,236 +0,0 @@
|
||||
import socket, ssl
|
||||
from ssl import SSLSocket
|
||||
from core.loadConf import Config
|
||||
from core.Model import Clones
|
||||
from core.base import Base
|
||||
from typing import Union
|
||||
|
||||
class Connection:
|
||||
|
||||
def __init__(self, server_port: int, nickname: str, username: str, realname: str, channels:list[str], CloneObject: Clones, ssl:bool = False) -> None:
|
||||
|
||||
self.Config = Config().ConfigObject
|
||||
self.Base = Base(self.Config)
|
||||
self.IrcSocket: Union[socket.socket, SSLSocket] = None
|
||||
self.nickname = nickname
|
||||
self.username = username
|
||||
self.realname = realname
|
||||
self.chanlog = '#clones'
|
||||
self.channels:list[str] = channels
|
||||
self.CHARSET = ['utf-8', 'iso-8859-1']
|
||||
self.Clones = CloneObject
|
||||
self.signal: bool = True
|
||||
for clone in self.Clones.UID_CLONE_DB:
|
||||
if clone.nickname == nickname:
|
||||
self.currentCloneObject = clone
|
||||
|
||||
self.create_socket(self.Config.SERVEUR_IP, self.Config.SERVEUR_HOSTNAME, server_port, ssl)
|
||||
self.send_connection_information_to_server(self.IrcSocket)
|
||||
self.connect()
|
||||
|
||||
def create_socket(self, server_ip: str, server_hostname: str, server_port: int, ssl: bool = False) -> bool:
|
||||
|
||||
try:
|
||||
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK)
|
||||
connexion_information = (server_ip, server_port)
|
||||
|
||||
if ssl:
|
||||
# Créer un object ssl
|
||||
ssl_context = self.__ssl_context()
|
||||
ssl_connexion = ssl_context.wrap_socket(soc, server_hostname=server_hostname)
|
||||
ssl_connexion.connect(connexion_information)
|
||||
self.IrcSocket:SSLSocket = ssl_connexion
|
||||
self.SSL_VERSION = self.IrcSocket.version()
|
||||
self.Base.logs.debug(f'> Connexion en mode SSL : Version = {self.SSL_VERSION}')
|
||||
else:
|
||||
soc.connect(connexion_information)
|
||||
self.IrcSocket:socket.socket = soc
|
||||
self.Base.logs.debug(f'> Connexion en mode normal')
|
||||
|
||||
return True
|
||||
|
||||
except ssl.SSLEOFError as soe:
|
||||
self.Base.logs.critical(f"SSLEOFError __create_socket: {soe} - {soc.fileno()}")
|
||||
return False
|
||||
except ssl.SSLError as se:
|
||||
self.Base.logs.critical(f"SSLError __create_socket: {se} - {soc.fileno()}")
|
||||
return False
|
||||
except OSError as oe:
|
||||
self.Base.logs.critical(f"OSError __create_socket: {oe} - {soc.fileno()}")
|
||||
return False
|
||||
except AttributeError as ae:
|
||||
self.Base.logs.critical(f"AttributeError __create_socket: {ae} - {soc.fileno()}")
|
||||
return False
|
||||
|
||||
def send2socket(self, send_message:str) -> None:
|
||||
"""Envoit les commandes à envoyer au serveur.
|
||||
|
||||
Args:
|
||||
string (Str): contient la commande à envoyer au serveur.
|
||||
"""
|
||||
try:
|
||||
with self.Base.lock:
|
||||
# print(f">{str(send_message)}")
|
||||
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0]))
|
||||
self.Base.logs.debug(f'{send_message}')
|
||||
|
||||
except UnicodeDecodeError:
|
||||
self.Base.logs.error(f'Decode Error try iso-8859-1 - message: {send_message}')
|
||||
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[1],'replace'))
|
||||
except UnicodeEncodeError:
|
||||
self.Base.logs.error(f'Encode Error try iso-8859-1 - message: {send_message}')
|
||||
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[1],'replace'))
|
||||
except AssertionError as ae:
|
||||
self.Base.logs.warning(f'Assertion Error {ae} - message: {send_message}')
|
||||
except ssl.SSLEOFError as soe:
|
||||
self.Base.logs.error(f"SSLEOFError: {soe} - {send_message}")
|
||||
except ssl.SSLError as se:
|
||||
self.Base.logs.error(f"SSLError: {se} - {send_message}")
|
||||
except OSError as oe:
|
||||
self.Base.logs.error(f"OSError: {oe} - {send_message}")
|
||||
|
||||
def send_connection_information_to_server(self, writer:Union[socket.socket, SSLSocket]) -> None:
|
||||
"""Créer le link et envoyer les informations nécessaires pour la
|
||||
connexion au serveur.
|
||||
|
||||
Args:
|
||||
writer (StreamWriter): permet l'envoi des informations au serveur.
|
||||
"""
|
||||
try:
|
||||
nickname = self.nickname
|
||||
username = self.username
|
||||
realname = self.realname
|
||||
|
||||
# Envoyer un message d'identification
|
||||
writer.send(f"USER {nickname} {username} {username} {nickname} {username} :{username}\r\n".encode('utf-8'))
|
||||
writer.send(f"USER {username} {username} {username} :{realname}\r\n".encode('utf-8'))
|
||||
writer.send(f"NICK {nickname}\r\n".encode('utf-8'))
|
||||
|
||||
self.Base.logs.debug('Link information sent to the server')
|
||||
|
||||
return None
|
||||
except AttributeError as ae:
|
||||
self.Base.logs.critical(f'{ae}')
|
||||
|
||||
def connect(self):
|
||||
try:
|
||||
|
||||
while self.signal:
|
||||
try:
|
||||
# 4072 max what the socket can grab
|
||||
buffer_size = self.IrcSocket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)
|
||||
data_in_bytes = self.IrcSocket.recv(buffer_size)
|
||||
data = data_in_bytes.splitlines(True)
|
||||
count_bytes = len(data_in_bytes)
|
||||
|
||||
while count_bytes > 4070:
|
||||
# If the received message is > 4070 then loop and add the value to the variable
|
||||
new_data = self.IrcSocket.recv(buffer_size)
|
||||
data_in_bytes += new_data
|
||||
count_bytes = len(new_data)
|
||||
|
||||
data = data_in_bytes.splitlines(True)
|
||||
|
||||
if not data:
|
||||
break
|
||||
|
||||
self.parser(data)
|
||||
except ssl.SSLEOFError as soe:
|
||||
self.Base.logs.error(f"SSLEOFError __connect_to_irc: {soe} - {data}")
|
||||
self.signal = False
|
||||
except ssl.SSLError as se:
|
||||
self.Base.logs.error(f"SSLError __connect_to_irc: {se} - {data}")
|
||||
self.signal = False
|
||||
except OSError as oe:
|
||||
self.Base.logs.error(f"OSError __connect_to_irc: {oe} - {data}")
|
||||
self.signal = False
|
||||
|
||||
self.IrcSocket.shutdown(socket.SHUT_RDWR)
|
||||
self.IrcSocket.close()
|
||||
self.Base.logs.info("--> Clone Disconnected ...")
|
||||
|
||||
except AssertionError as ae:
|
||||
self.Base.logs.error(f'Assertion error : {ae}')
|
||||
except ValueError as ve:
|
||||
self.Base.logs.error(f'Value Error : {ve}')
|
||||
except ssl.SSLEOFError as soe:
|
||||
self.Base.logs.error(f"OS Error __connect_to_irc: {soe}")
|
||||
except AttributeError as atte:
|
||||
self.Base.logs.critical(f"{atte}")
|
||||
except Exception as e:
|
||||
self.Base.logs.error(f"Exception: {e}")
|
||||
|
||||
def parser(self, cmd:list[bytes]):
|
||||
try:
|
||||
for data in cmd:
|
||||
response = data.decode(self.CHARSET[0]).split()
|
||||
self.signal = self.currentCloneObject.alive
|
||||
current_clone_nickname = self.currentCloneObject.nickname
|
||||
# print(response)
|
||||
|
||||
match response[0]:
|
||||
case 'PING':
|
||||
pong = str(response[1]).replace(':','')
|
||||
self.send2socket(f"PONG :{pong}")
|
||||
return None
|
||||
case 'ERROR':
|
||||
error_value = str(response[1]).replace(':','')
|
||||
if error_value == 'Closing':
|
||||
self.signal = False
|
||||
|
||||
match response[1]:
|
||||
case '376':
|
||||
self.currentCloneObject.connected = True
|
||||
for channel in self.channels:
|
||||
self.send2socket(f"JOIN {channel}")
|
||||
return None
|
||||
case 'PRIVMSG':
|
||||
self.Base.logs.debug(response)
|
||||
self.Base.logs.debug(f'{self.currentCloneObject.nickname} - {self.currentCloneObject.alive}')
|
||||
fullname = str(response[0]).replace(':', '')
|
||||
nickname = fullname.split('!')[0].replace(':','')
|
||||
|
||||
if response[2] == current_clone_nickname:
|
||||
message = []
|
||||
for i in range(3, len(response)):
|
||||
message.append(response[i])
|
||||
final_message = ' '.join(message)
|
||||
self.send2socket(f"PRIVMSG {self.chanlog} :{fullname} => {final_message[1:]}")
|
||||
|
||||
if nickname == self.Config.SERVICE_NICKNAME:
|
||||
command = str(response[3]).replace(':','')
|
||||
|
||||
if command == 'KILL':
|
||||
self.send2socket(f'QUIT :Thanks and goodbye')
|
||||
self.signal = self.currentCloneObject.alive
|
||||
|
||||
if command == 'JOIN':
|
||||
channel_to_join = str(response[4])
|
||||
self.send2socket(f"JOIN {channel_to_join}")
|
||||
|
||||
if command == 'SAY':
|
||||
clone_channel = str(response[4])
|
||||
message = []
|
||||
for i in range(5, len(response)):
|
||||
message.append(response[i])
|
||||
final_message = ' '.join(message)
|
||||
|
||||
self.send2socket(f"PRIVMSG {clone_channel} :{final_message}")
|
||||
|
||||
except UnicodeEncodeError:
|
||||
for data in cmd:
|
||||
response = data.decode(self.CHARSET[1],'replace').split()
|
||||
except UnicodeDecodeError:
|
||||
for data in cmd:
|
||||
response = data.decode(self.CHARSET[1],'replace').split()
|
||||
except AssertionError as ae:
|
||||
self.Base.logs.error(f"Assertion error : {ae}")
|
||||
|
||||
def __ssl_context(self) -> ssl.SSLContext:
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
|
||||
self.Base.logs.debug(f'SSLContext initiated with verified mode {ctx.verify_mode}')
|
||||
|
||||
return ctx
|
||||
@@ -1,274 +0,0 @@
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from typing import Union
|
||||
|
||||
class User:
|
||||
|
||||
@dataclass
|
||||
class UserDataClass:
|
||||
uid: str
|
||||
nickname: str
|
||||
username: str
|
||||
hostname: str
|
||||
umodes: str
|
||||
vhost: str
|
||||
isWebirc: bool
|
||||
connexion_datetime: datetime = field(default=datetime.now())
|
||||
|
||||
UID_DB:list[UserDataClass] = []
|
||||
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
def insert(self, user: UserDataClass) -> bool:
|
||||
"""Insert new user
|
||||
|
||||
Args:
|
||||
user (UserDataClass): The User dataclass
|
||||
|
||||
Returns:
|
||||
bool: True if the record has been created
|
||||
"""
|
||||
exists = False
|
||||
inserted = False
|
||||
|
||||
for record in self.UID_DB:
|
||||
if record.uid == user.uid:
|
||||
exists = True
|
||||
print(f'{user.uid} already exist')
|
||||
|
||||
if not exists:
|
||||
self.UID_DB.append(user)
|
||||
print(f'New record with uid: {user.uid}')
|
||||
inserted = True
|
||||
|
||||
return inserted
|
||||
|
||||
def update(self, uid: str, newnickname: str) -> bool:
|
||||
"""Updating a single record with a new nickname
|
||||
|
||||
Args:
|
||||
uid (str): the uid of the user
|
||||
newnickname (str): the new nickname
|
||||
|
||||
Returns:
|
||||
bool: True if the record has been updated
|
||||
"""
|
||||
status = False
|
||||
for user in self.UID_DB:
|
||||
if user.uid == uid:
|
||||
user.nickname = newnickname
|
||||
status = True
|
||||
print(f'Updating record with uid: {uid}')
|
||||
|
||||
return status
|
||||
|
||||
def delete(self, uid: str) -> bool:
|
||||
"""Delete a user based on his uid
|
||||
|
||||
Args:
|
||||
uid (str): The UID of the user
|
||||
|
||||
Returns:
|
||||
bool: True if the record has been deleted
|
||||
"""
|
||||
status = False
|
||||
for user in self.UID_DB:
|
||||
if user.uid == uid:
|
||||
self.UID_DB.remove(user)
|
||||
status = True
|
||||
print(f'Removing record with uid: {uid}')
|
||||
|
||||
return status
|
||||
|
||||
def isexist(self, uidornickname:str) -> bool:
|
||||
"""do the UID or Nickname exist ?
|
||||
|
||||
Args:
|
||||
uidornickname (str): The UID or the Nickname
|
||||
|
||||
Returns:
|
||||
bool: True if exist or False if don't exist
|
||||
"""
|
||||
result = False
|
||||
for record in self.UID_DB:
|
||||
if record.uid == uidornickname:
|
||||
result = True
|
||||
if record.nickname == uidornickname:
|
||||
result = True
|
||||
|
||||
return result
|
||||
|
||||
def get_User(self, uidornickname) -> Union[UserDataClass, None]:
|
||||
|
||||
UserObject = None
|
||||
for record in self.UID_DB:
|
||||
if record.uid == uidornickname:
|
||||
UserObject = record
|
||||
elif record.nickname == uidornickname:
|
||||
UserObject = record
|
||||
|
||||
return UserObject
|
||||
|
||||
def get_uid(self, uidornickname:str) -> Union[str, None]:
|
||||
|
||||
uid = None
|
||||
for record in self.UID_DB:
|
||||
if record.uid == uidornickname:
|
||||
uid = record.uid
|
||||
if record.nickname == uidornickname:
|
||||
uid = record.uid
|
||||
|
||||
return uid
|
||||
|
||||
def get_nickname(self, uidornickname:str) -> Union[str, None]:
|
||||
|
||||
nickname = None
|
||||
for record in self.UID_DB:
|
||||
if record.nickname == uidornickname:
|
||||
nickname = record.nickname
|
||||
if record.uid == uidornickname:
|
||||
nickname = record.nickname
|
||||
|
||||
return nickname
|
||||
|
||||
class Admin:
|
||||
@dataclass
|
||||
class AdminDataClass:
|
||||
uid: str
|
||||
nickname: str
|
||||
username: str
|
||||
hostname: str
|
||||
umodes: str
|
||||
vhost: str
|
||||
level: int
|
||||
connexion_datetime: datetime = field(default=datetime.now())
|
||||
|
||||
UID_ADMIN_DB:list[AdminDataClass] = []
|
||||
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
def insert(self, admin: AdminDataClass) -> bool:
|
||||
"""Insert new user
|
||||
|
||||
Args:
|
||||
user (UserDataClass): The User dataclass
|
||||
|
||||
Returns:
|
||||
bool: True if the record has been created
|
||||
"""
|
||||
exists = False
|
||||
inserted = False
|
||||
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.uid == admin.uid:
|
||||
exists = True
|
||||
print(f'{admin.uid} already exist')
|
||||
|
||||
if not exists:
|
||||
self.UID_ADMIN_DB.append(admin)
|
||||
print(f'New record with uid: {admin.uid}')
|
||||
inserted = True
|
||||
|
||||
return inserted
|
||||
|
||||
def update(self, uid: str, newnickname: str) -> bool:
|
||||
"""Updating a single record with a new nickname
|
||||
|
||||
Args:
|
||||
uid (str): the uid of the user
|
||||
newnickname (str): the new nickname
|
||||
|
||||
Returns:
|
||||
bool: True if the record has been updated
|
||||
"""
|
||||
status = False
|
||||
for admin in self.UID_ADMIN_DB:
|
||||
if admin.uid == uid:
|
||||
admin.nickname = newnickname
|
||||
status = True
|
||||
print(f'Updating record with uid: {uid}')
|
||||
|
||||
return status
|
||||
|
||||
def delete(self, uid: str) -> bool:
|
||||
"""Delete a user based on his uid
|
||||
|
||||
Args:
|
||||
uid (str): The UID of the user
|
||||
|
||||
Returns:
|
||||
bool: True if the record has been deleted
|
||||
"""
|
||||
status = False
|
||||
for admin in self.UID_ADMIN_DB:
|
||||
if admin.uid == uid:
|
||||
self.UID_ADMIN_DB.remove(admin)
|
||||
status = True
|
||||
print(f'Removing record with uid: {uid}')
|
||||
|
||||
return status
|
||||
|
||||
def isexist(self, uidornickname:str) -> bool:
|
||||
"""do the UID or Nickname exist ?
|
||||
|
||||
Args:
|
||||
uidornickname (str): The UID or the Nickname
|
||||
|
||||
Returns:
|
||||
bool: True if exist or False if don't exist
|
||||
"""
|
||||
result = False
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.uid == uidornickname:
|
||||
result = True
|
||||
if record.nickname == uidornickname:
|
||||
result = True
|
||||
|
||||
return result
|
||||
|
||||
def get_Admin(self, uidornickname) -> Union[AdminDataClass, None]:
|
||||
|
||||
AdminObject = None
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.uid == uidornickname:
|
||||
AdminObject = record
|
||||
elif record.nickname == uidornickname:
|
||||
AdminObject = record
|
||||
|
||||
return AdminObject
|
||||
|
||||
def get_uid(self, uidornickname:str) -> Union[str, None]:
|
||||
|
||||
uid = None
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.uid == uidornickname:
|
||||
uid = record.uid
|
||||
if record.nickname == uidornickname:
|
||||
uid = record.uid
|
||||
|
||||
return uid
|
||||
|
||||
def get_nickname(self, uidornickname:str) -> Union[str, None]:
|
||||
|
||||
nickname = None
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.nickname == uidornickname:
|
||||
nickname = record.nickname
|
||||
if record.uid == uidornickname:
|
||||
nickname = record.nickname
|
||||
|
||||
return nickname
|
||||
|
||||
def get_level(self, uidornickname:str) -> int:
|
||||
|
||||
level = 0
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.uid == uidornickname:
|
||||
level = record.level
|
||||
if record.nickname == uidornickname:
|
||||
level = record.level
|
||||
|
||||
return level
|
||||
|
||||
298
core/definition.py
Normal file
298
core/definition.py
Normal file
@@ -0,0 +1,298 @@
|
||||
from datetime import datetime
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Literal
|
||||
from os import sep
|
||||
|
||||
@dataclass
|
||||
class MUser:
|
||||
"""Model User"""
|
||||
|
||||
uid: str = None
|
||||
nickname: str = None
|
||||
username: str = None
|
||||
realname: str = None
|
||||
hostname: str = None
|
||||
umodes: str = None
|
||||
vhost: str = None
|
||||
isWebirc: bool = False
|
||||
isWebsocket: bool = False
|
||||
remote_ip: str = None
|
||||
score_connexion: int = 0
|
||||
geoip: str = None
|
||||
connexion_datetime: datetime = field(default=datetime.now())
|
||||
|
||||
@dataclass
|
||||
class MAdmin:
|
||||
"""Model Admin"""
|
||||
|
||||
uid: str = None
|
||||
nickname: str = None
|
||||
username: str = None
|
||||
realname: str = None
|
||||
hostname: str = None
|
||||
umodes: str = None
|
||||
vhost: str = None
|
||||
isWebirc: bool = False
|
||||
isWebsocket: bool = False
|
||||
remote_ip: str = None
|
||||
score_connexion: int = 0
|
||||
geoip: str = None
|
||||
connexion_datetime: datetime = field(default=datetime.now())
|
||||
level: int = 0
|
||||
|
||||
@dataclass
|
||||
class MReputation:
|
||||
"""Model Reputation"""
|
||||
uid: str = None
|
||||
nickname: str = None
|
||||
username: str = None
|
||||
realname: str = None
|
||||
hostname: str = None
|
||||
umodes: str = None
|
||||
vhost: str = None
|
||||
isWebirc: bool = False
|
||||
isWebsocket: bool = False
|
||||
remote_ip: str = None
|
||||
score_connexion: int = 0
|
||||
geoip: str = None
|
||||
connexion_datetime: datetime = field(default=datetime.now())
|
||||
secret_code: str = None
|
||||
|
||||
@dataclass
|
||||
class MChannel:
|
||||
"""Model Channel"""
|
||||
|
||||
name: str = None
|
||||
"""### Channel name
|
||||
It include the #"""
|
||||
uids: list[str] = field(default_factory=list[str])
|
||||
"""### List of UID available in the channel
|
||||
including their modes ~ @ % + *
|
||||
|
||||
Returns:
|
||||
list: The list of UID's including theirs modes
|
||||
"""
|
||||
|
||||
@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
|
||||
class MConfig:
|
||||
"""Model Configuration"""
|
||||
|
||||
SERVEUR_IP: str = "127.0.0.1"
|
||||
"""Server public IP (could be 127.0.0.1 localhost)"""
|
||||
|
||||
SERVEUR_HOSTNAME: str = "your.host.name"
|
||||
"""IRC Server Hostname (your.hostname.extension)"""
|
||||
|
||||
SERVEUR_LINK: str = "your.link.url"
|
||||
"""The link hostname (should be the same as your unrealircd link block)"""
|
||||
|
||||
SERVEUR_PORT: int = 6697
|
||||
"""Server port as configured in your unrealircd link block"""
|
||||
|
||||
SERVEUR_PASSWORD: str = "YOUR.STRONG.PASSWORD"
|
||||
"""Your link password"""
|
||||
|
||||
SERVEUR_ID: str = "Z01"
|
||||
"""Service identification could be Z01 should be unique"""
|
||||
|
||||
SERVEUR_SSL: bool = True
|
||||
"""Activate SSL connexion"""
|
||||
|
||||
SERVEUR_PROTOCOL: str = "unreal6"
|
||||
"""Which server are you going to use. (default: unreal6)"""
|
||||
|
||||
SERVEUR_CHARSET: list[str] = field(default_factory=list[str])
|
||||
"""0: utf-8 | 1: iso-8859-1"""
|
||||
|
||||
SERVICE_NAME: str = "Defender"
|
||||
"""Service name (Ex. Defender)"""
|
||||
|
||||
SERVICE_NICKNAME: str = "Defender"
|
||||
"""Nickname of the service (Ex. Defender)"""
|
||||
|
||||
SERVICE_REALNAME: str = "Defender IRC Service"
|
||||
"""Realname of the service"""
|
||||
|
||||
SERVICE_USERNAME: str = "Security"
|
||||
"""Username of the service"""
|
||||
|
||||
SERVICE_HOST: str = "Your.Service.Hostname"
|
||||
"""The service hostname"""
|
||||
|
||||
SERVICE_INFO: str = "Defender IRC Service"
|
||||
"""Swhois of the service"""
|
||||
|
||||
SERVICE_CHANLOG: str = "#services"
|
||||
"""The channel used by the service (ex. #services)"""
|
||||
|
||||
SERVICE_SMODES: str = "+ioqBS"
|
||||
"""The service mode (ex. +ioqBS)"""
|
||||
|
||||
SERVICE_CMODES: str = "ntsO"
|
||||
"""The mode of the log channel (ex. ntsO)"""
|
||||
|
||||
SERVICE_UMODES: str = "o"
|
||||
"""The mode of the service when joining chanlog (ex. o, the service will be operator in the chanlog)"""
|
||||
|
||||
SERVICE_PREFIX: str = "!"
|
||||
"""The default prefix to communicate with the service"""
|
||||
|
||||
SERVICE_ID: str = field(init=False)
|
||||
"""The service unique ID"""
|
||||
|
||||
OWNER: str = "admin"
|
||||
"""The nickname of the admin of the service"""
|
||||
|
||||
PASSWORD: str = "password"
|
||||
"""The password of the admin of the service"""
|
||||
|
||||
JSONRPC_URL: str = None
|
||||
"""The RPC url, if local https://127.0.0.1:PORT/api should be fine"""
|
||||
|
||||
JSONRPC_PATH_TO_SOCKET_FILE: str = None
|
||||
"""The full path of the socket file (/PATH/TO/YOUR/UNREALIRCD/SOCKET/FILE.socket)"""
|
||||
|
||||
JSONRPC_METHOD: str = None
|
||||
"""3 methods are available; requests/socket/unixsocket"""
|
||||
|
||||
JSONRPC_USER: str = None
|
||||
"""The RPC User defined in your unrealircd.conf"""
|
||||
|
||||
JSONRPC_PASSWORD: str = None
|
||||
"""The RPC Password defined in your unrealircd.conf"""
|
||||
|
||||
SALON_JAIL: str = "#jail"
|
||||
"""The JAIL channel (ex. #jail)"""
|
||||
|
||||
SALON_JAIL_MODES: str = "sS"
|
||||
"""The jail channel modes (ex. sS)"""
|
||||
|
||||
SALON_LIBERER: str = "#welcome"
|
||||
"""Channel where the nickname will be released"""
|
||||
|
||||
CLONE_CHANNEL: str = "clones"
|
||||
"""Channel where clones are hosted and will log PRIVMSG"""
|
||||
|
||||
CLONE_CMODES: str = "+nts"
|
||||
"""Clone channel modes (ex. +nts)"""
|
||||
|
||||
CLONE_UMODES: str = '+iwxz'
|
||||
"""Clone User modes (ex. +iwxz)"""
|
||||
|
||||
CLONE_LOG_HOST_EXEMPT: list[str] = field(default_factory=list[str])
|
||||
"""Hosts that clones will not log"""
|
||||
|
||||
CLONE_CHANNEL_PASSWORD: str = "clone_Password_1234"
|
||||
"""Clone password channel"""
|
||||
|
||||
API_TIMEOUT: int = 60
|
||||
"""Default api timeout in second. (default: 60)"""
|
||||
|
||||
PORTS_TO_SCAN: list[int] = field(default_factory=list[int])
|
||||
"""List of ports to scan available for proxy_scan in the mod_defender module"""
|
||||
|
||||
WHITELISTED_IP: list[str] = field(default_factory=list[str])
|
||||
"""List of remote IP to don't scan"""
|
||||
|
||||
GLINE_DURATION: str = "30"
|
||||
"""Gline duration"""
|
||||
|
||||
DEBUG_LEVEL:Literal[10, 20, 30, 40, 50] = 20
|
||||
"""Logs level: DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50. (default: 20)"""
|
||||
|
||||
LOGGING_NAME: str = "defender"
|
||||
"""The name of the Logging instance"""
|
||||
|
||||
TABLE_ADMIN: str = "core_admin"
|
||||
"""Admin table"""
|
||||
|
||||
TABLE_COMMAND: str = "core_command"
|
||||
"""Core command table"""
|
||||
|
||||
TABLE_LOG: str = "core_log"
|
||||
"""Core log table"""
|
||||
|
||||
TABLE_MODULE: str = "core_module"
|
||||
"""Core module table"""
|
||||
|
||||
TABLE_CONFIG: str = "core_config"
|
||||
"""Core configuration table"""
|
||||
|
||||
TABLE_CHANNEL: str = "core_channel"
|
||||
"""Core channel table"""
|
||||
|
||||
CURRENT_VERSION: str = None
|
||||
"""Current version of Defender"""
|
||||
|
||||
LATEST_VERSION: str = None
|
||||
"""The Latest version fetched from github"""
|
||||
|
||||
DB_NAME: str = "defender"
|
||||
"""The database name"""
|
||||
|
||||
DB_PATH: str = f"db{sep}"
|
||||
"""The database path"""
|
||||
|
||||
COLORS: ColorModel = field(default_factory=ColorModel)
|
||||
"""Available colors in Defender"""
|
||||
|
||||
BATCH_SIZE: int = 400
|
||||
"""The batch size used for privmsg and notice"""
|
||||
|
||||
DEFENDER_CONNEXION_DATETIME: datetime = field(default=datetime.now())
|
||||
"""First Connexion datetime of the service"""
|
||||
|
||||
DEFENDER_INIT: int = 1
|
||||
"""Init flag. When Defender is ready, this variable will be set to 0. (default: 1)"""
|
||||
|
||||
DEFENDER_RESTART: int = 0
|
||||
"""Restart flag. When Defender should restart this variable should be set to 1 (default: 0)"""
|
||||
|
||||
DEFENDER_HEARTBEAT: bool = True
|
||||
"""Activate the hearbeat pulse (default: True)"""
|
||||
|
||||
DEFENDER_HEARTBEAT_FREQUENCY: int = 2
|
||||
"""Frequency in seconds between every pulse (default: 30 seconds)"""
|
||||
|
||||
OS_SEP: str = sep
|
||||
"""The OS Separator. (default: os.sep)"""
|
||||
|
||||
HSID: str = None
|
||||
"""Host Server ID. The Server ID of the server who is hosting Defender. (Default: None)"""
|
||||
|
||||
SSL_VERSION: str = None
|
||||
"""If SSL is used. This variable will be filled out by the system. (Default: None)"""
|
||||
|
||||
def __post_init__(self):
|
||||
# Initialiser SERVICE_ID après la création de l'objet
|
||||
self.SERVICE_ID: str = f"{self.SERVEUR_ID}AAAAAB"
|
||||
"""The service ID which is SERVEUR_ID and AAAAAB"""
|
||||
|
||||
self.SERVEUR_CHARSET: list = ["utf-8", "iso-8859-1"]
|
||||
"""0: utf-8 | 1: iso-8859-1"""
|
||||
|
||||
@dataclass
|
||||
class MClone:
|
||||
"""Model Clone"""
|
||||
connected: bool = False
|
||||
uid: str = None
|
||||
nickname: str = None
|
||||
username: str = None
|
||||
realname: str = None
|
||||
channels: list = field(default_factory=list)
|
||||
vhost: str = None
|
||||
hostname: str = 'localhost'
|
||||
umodes: str = None
|
||||
remote_ip: str = '127.0.0.1'
|
||||
group: str = 'Default'
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"SERVEUR_IP": "0.0.0.0",
|
||||
"SERVEUR_HOSTNAME": "your.host.name",
|
||||
"SERVEUR_LINK": "your.link.to.server",
|
||||
"SERVEUR_PORT": 7002,
|
||||
"SERVEUR_PASSWORD": "link_password",
|
||||
"SERVEUR_ID": "006",
|
||||
"SERVEUR_SSL": true,
|
||||
|
||||
"SERVICE_NAME": "defender",
|
||||
"SERVICE_NICKNAME": "BotNickname",
|
||||
"SERVICE_REALNAME": "BotRealname",
|
||||
"SERVICE_USERNAME": "BotUsername",
|
||||
"SERVICE_HOST": "your.service.hostname",
|
||||
"SERVICE_INFO": "Network IRC Service",
|
||||
"SERVICE_CHANLOG": "#services",
|
||||
"SERVICE_SMODES": "+ioqBS",
|
||||
"SERVICE_CMODES": "ntsO",
|
||||
"SERVICE_UMODES": "o",
|
||||
"SERVICE_PREFIX": "!",
|
||||
|
||||
"OWNER": "admin",
|
||||
"PASSWORD": "password",
|
||||
|
||||
"SALON_JAIL": "#jail",
|
||||
"SALON_JAIL_MODES": "sS",
|
||||
"SALON_LIBERER": "#welcome",
|
||||
|
||||
"API_TIMEOUT": 2,
|
||||
|
||||
"PORTS_TO_SCAN": [3028, 8080, 1080, 1085, 4145, 9050],
|
||||
"WHITELISTED_IP": ["127.0.0.1"],
|
||||
"GLINE_DURATION": "30",
|
||||
|
||||
"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"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
from dataclasses import dataclass
|
||||
from subprocess import check_call, run, CalledProcessError, PIPE
|
||||
from platform import python_version, python_version_tuple
|
||||
from sys import exit
|
||||
import os
|
||||
import json
|
||||
from sys import exit, prefix
|
||||
from dataclasses import dataclass
|
||||
from subprocess import check_call, run, CalledProcessError, PIPE, check_output
|
||||
from platform import python_version, python_version_tuple
|
||||
|
||||
class Install:
|
||||
|
||||
@@ -24,26 +25,32 @@ class Install:
|
||||
venv_pip_executable: str
|
||||
venv_python_executable: str
|
||||
|
||||
@dataclass
|
||||
class Package:
|
||||
name: str = None
|
||||
version: str = None
|
||||
|
||||
DB_PACKAGES: list[Package] = []
|
||||
|
||||
def __init__(self) -> None:
|
||||
|
||||
self.set_configuration()
|
||||
|
||||
if not self.check_python_version():
|
||||
# Tester si c'est la bonne version de python
|
||||
exit("Python Version Error")
|
||||
else:
|
||||
if self.skip_install:
|
||||
self.install_dependencies()
|
||||
self.check_packages_version()
|
||||
return None
|
||||
|
||||
if self.skip_install:
|
||||
return None
|
||||
self.check_packages_version()
|
||||
|
||||
# Sinon tester les dependances python et les installer avec pip
|
||||
if self.do_install():
|
||||
# Sinon tester les dependances python et les installer avec pip
|
||||
if self.do_install():
|
||||
|
||||
self.install_dependencies()
|
||||
self.install_dependencies()
|
||||
|
||||
self.create_service_file()
|
||||
self.create_service_file()
|
||||
|
||||
self.print_final_message()
|
||||
self.print_final_message()
|
||||
|
||||
return None
|
||||
|
||||
@@ -54,7 +61,7 @@ class Install:
|
||||
venv_folder = '.pyenv'
|
||||
unix_user_home_directory = os.path.expanduser("~")
|
||||
unix_systemd_folder = os.path.join(unix_user_home_directory, '.config', 'systemd', 'user')
|
||||
defender_main_executable = os.path.join(defender_install_folder, 'main.py')
|
||||
defender_main_executable = os.path.join(defender_install_folder, 'defender.py')
|
||||
|
||||
self.config = self.CoreConfig(
|
||||
install_log_file='install.log',
|
||||
@@ -69,23 +76,36 @@ class Install:
|
||||
defender_install_folder=defender_install_folder,
|
||||
venv_folder=venv_folder,
|
||||
venv_cmd_installation=['python3', '-m', 'venv', venv_folder],
|
||||
venv_cmd_requirements=['sqlalchemy','psutil','requests','faker'],
|
||||
venv_cmd_requirements=['sqlalchemy','psutil','requests','faker','unrealircd_rpc_py'],
|
||||
venv_pip_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}pip',
|
||||
venv_python_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}python'
|
||||
)
|
||||
|
||||
# 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, 'config', 'configuration.json')):
|
||||
# If configuration file do not exist
|
||||
exit("/!\\ Configuration file (core/configuration.json) doesn't exist! please create it /!\\")
|
||||
|
||||
# Exclude Windows OS from the installation
|
||||
if os.name == 'nt':
|
||||
#print('/!\\ Skip installation /!\\')
|
||||
# If windows, modify pip and python virtual environment executable
|
||||
self.config.venv_pip_executable = f'{os.path.join(defender_install_folder, venv_folder, "Scripts")}{os.sep}pip.exe'
|
||||
self.config.venv_python_executable = f'{os.path.join(defender_install_folder, venv_folder, "Scripts")}{os.sep}python.exe'
|
||||
self.skip_install = True
|
||||
else:
|
||||
if self.is_root():
|
||||
self.skip_install = True
|
||||
return False
|
||||
|
||||
if self.is_root():
|
||||
exit(f'/!\\ I highly not recommend running Defender as root /!\\')
|
||||
self.skip_install = True
|
||||
return False
|
||||
|
||||
def is_root(self) -> bool:
|
||||
|
||||
if os.geteuid() != 0:
|
||||
print('User without privileges ==> PASS')
|
||||
print('> User without privileges ==> OK')
|
||||
return False
|
||||
elif os.geteuid() == 0:
|
||||
print('/!\\ Do not use root to install Defender /!\\')
|
||||
@@ -117,6 +137,75 @@ class Install:
|
||||
print(f"Try to install dependencies ...")
|
||||
exit(5)
|
||||
|
||||
def get_packages_version_from_json(self) -> None:
|
||||
"""This will create Package model with package names and required version
|
||||
"""
|
||||
try:
|
||||
|
||||
version_filename = f'.{os.sep}version.json'
|
||||
with open(version_filename, 'r') as version_data:
|
||||
package_info:dict[str, str] = json.load(version_data)
|
||||
|
||||
for name, version in package_info.items():
|
||||
if name == 'version':
|
||||
continue
|
||||
|
||||
self.DB_PACKAGES.append(
|
||||
self.Package(name=name, version=version)
|
||||
)
|
||||
|
||||
return None
|
||||
except FileNotFoundError as fe:
|
||||
print(f"File not found: {fe}")
|
||||
except Exception as err:
|
||||
print(f"General Error: {err}")
|
||||
|
||||
def check_packages_version(self) -> bool:
|
||||
|
||||
try:
|
||||
newVersion = False
|
||||
self.get_packages_version_from_json()
|
||||
|
||||
if not self.config.venv_folder in prefix:
|
||||
print(f"You are probably running a new installation or you are not using your virtual env {self.config.venv_folder}")
|
||||
return newVersion
|
||||
|
||||
print(f"> Checking for dependencies versions ==> WAIT")
|
||||
for package in self.DB_PACKAGES:
|
||||
newVersion = False
|
||||
required_version = package.version
|
||||
installed_version = None
|
||||
|
||||
output = check_output([self.config.venv_pip_executable, 'show', package.name])
|
||||
for line in output.decode().splitlines():
|
||||
if line.startswith('Version:'):
|
||||
installed_version = line.split(':')[1].strip()
|
||||
break
|
||||
|
||||
required_major, required_minor, required_patch = required_version.split('.')
|
||||
installed_major, installed_minor, installed_patch = installed_version.split('.')
|
||||
|
||||
if required_major > installed_major:
|
||||
print(f'> New version of {package.name} is available {installed_version} ==> {required_version}')
|
||||
newVersion = True
|
||||
elif required_major == installed_major and required_minor > installed_minor:
|
||||
print(f'> New version of {package.name} is available {installed_version} ==> {required_version}')
|
||||
newVersion = True
|
||||
elif required_major == installed_major and required_minor == installed_minor and required_patch > installed_patch:
|
||||
print(f'> New version of {package.name} is available {installed_version} ==> {required_version}')
|
||||
newVersion = True
|
||||
|
||||
if newVersion:
|
||||
self.run_subprocess([self.config.venv_pip_executable, 'install', '--upgrade', package.name])
|
||||
|
||||
print(f"> Dependencies versions ==> OK")
|
||||
return newVersion
|
||||
|
||||
except CalledProcessError:
|
||||
print(f"/!\\ Package {package.name} not installed /!\\")
|
||||
except Exception as err:
|
||||
print(f"General Error: {err}")
|
||||
|
||||
def check_python_version(self) -> bool:
|
||||
"""Test si la version de python est autorisée ou non
|
||||
|
||||
|
||||
1309
core/irc.py
1309
core/irc.py
File diff suppressed because it is too large
Load Diff
205
core/loadConf.py
205
core/loadConf.py
@@ -1,205 +0,0 @@
|
||||
import json, sys
|
||||
from os import sep
|
||||
from typing import Union, Literal
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
##########################################
|
||||
# CONFIGURATION FILE #
|
||||
##########################################
|
||||
|
||||
@dataclass
|
||||
class ConfigDataModel:
|
||||
|
||||
SERVEUR_IP: str
|
||||
"""Server public IP (could be 127.0.0.1 localhost)"""
|
||||
|
||||
SERVEUR_HOSTNAME: str
|
||||
"""IRC Server Hostname (your.hostname.extension)"""
|
||||
|
||||
SERVEUR_LINK: str
|
||||
"""The link hostname (should be the same as your unrealircd link block)"""
|
||||
|
||||
SERVEUR_PORT: int
|
||||
"""Server port as configured in your unrealircd link block"""
|
||||
|
||||
SERVEUR_PASSWORD: str
|
||||
"""Your link password"""
|
||||
|
||||
SERVEUR_ID: str
|
||||
"""Service identification could be Z01 should be unique"""
|
||||
|
||||
SERVEUR_SSL: bool
|
||||
"""Activate SSL connexion"""
|
||||
|
||||
SERVICE_NAME: str
|
||||
"""Service name (Ex. Defender)"""
|
||||
|
||||
SERVICE_NICKNAME: str
|
||||
"""Nickname of the service (Ex. Defender)"""
|
||||
|
||||
SERVICE_REALNAME: str
|
||||
"""Realname of the service"""
|
||||
|
||||
SERVICE_USERNAME: str
|
||||
"""Username of the service"""
|
||||
|
||||
SERVICE_HOST: str
|
||||
"""The service hostname"""
|
||||
|
||||
SERVICE_INFO: str
|
||||
"""Swhois of the service"""
|
||||
|
||||
SERVICE_CHANLOG: str
|
||||
"""The channel used by the service (ex. #services)"""
|
||||
|
||||
SERVICE_SMODES: str
|
||||
"""The service mode (ex. +ioqBS)"""
|
||||
|
||||
SERVICE_CMODES: str
|
||||
"""The mode of the log channel (ex. ntsO)"""
|
||||
|
||||
SERVICE_UMODES: str
|
||||
"""The mode of the service when joining chanlog (ex. o, the service will be operator in the chanlog)"""
|
||||
|
||||
SERVICE_PREFIX: str
|
||||
"""The default prefix to communicate with the service"""
|
||||
|
||||
SERVICE_ID: str = field(init=False)
|
||||
"""The service unique ID"""
|
||||
|
||||
OWNER: str
|
||||
"""The nickname of the admin of the service"""
|
||||
|
||||
PASSWORD: str
|
||||
"""The password of the admin of the service"""
|
||||
|
||||
SALON_JAIL: str
|
||||
"""The JAIL channel (ex. #jail)"""
|
||||
|
||||
SALON_JAIL_MODES: str
|
||||
"""The jail channel modes (ex. sS)"""
|
||||
|
||||
SALON_LIBERER: str
|
||||
"""Channel where the nickname will be released"""
|
||||
|
||||
API_TIMEOUT: int
|
||||
"""Default api timeout in second"""
|
||||
|
||||
PORTS_TO_SCAN: list
|
||||
"""List of ports to scan available for proxy_scan in the mod_defender module"""
|
||||
|
||||
WHITELISTED_IP: list
|
||||
"""List of remote IP to don't scan"""
|
||||
|
||||
GLINE_DURATION: str
|
||||
"""Gline duration"""
|
||||
|
||||
DEBUG_LEVEL:Literal[10, 20, 30, 40, 50]
|
||||
"""Logs level: DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50"""
|
||||
|
||||
CONFIG_COLOR: dict[str, str]
|
||||
|
||||
table_admin: str
|
||||
"""Admin table"""
|
||||
|
||||
table_commande: str
|
||||
"""Core command table"""
|
||||
|
||||
table_log: str
|
||||
"""Core log table"""
|
||||
|
||||
table_module: str
|
||||
"""Core module table"""
|
||||
|
||||
table_config: str
|
||||
"""Core configuration table"""
|
||||
|
||||
table_channel: str
|
||||
"""Core channel table"""
|
||||
|
||||
current_version: str
|
||||
"""Current version of Defender"""
|
||||
|
||||
latest_version: str
|
||||
"""The Latest version fetched from github"""
|
||||
|
||||
db_name: str
|
||||
"""The database name"""
|
||||
|
||||
db_path: str
|
||||
"""The database path"""
|
||||
|
||||
def __post_init__(self):
|
||||
# Initialiser SERVICE_ID après la création de l'objet
|
||||
self.SERVICE_ID:str = f"{self.SERVEUR_ID}AAAAAB"
|
||||
"""The service ID which is SERVEUR_ID and AAAAAB"""
|
||||
|
||||
class Config:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.ConfigObject: ConfigDataModel = self.__load_service_configuration()
|
||||
return None
|
||||
|
||||
def __load_json_service_configuration(self):
|
||||
try:
|
||||
conf_filename = f'core{sep}configuration.json'
|
||||
with open(conf_filename, 'r') as configuration_data:
|
||||
configuration:dict[str, Union[str, int, list, dict]] = json.load(configuration_data)
|
||||
|
||||
for key, value in configuration['CONFIG_COLOR'].items():
|
||||
configuration['CONFIG_COLOR'][key] = str(value).encode('utf-8').decode('unicode_escape')
|
||||
|
||||
return configuration
|
||||
|
||||
except FileNotFoundError as fe:
|
||||
print(f'FileNotFound: {fe}')
|
||||
print('Configuration file not found please create core/configuration.json')
|
||||
sys.exit(0)
|
||||
|
||||
def __load_service_configuration(self) -> ConfigDataModel:
|
||||
import_config = self.__load_json_service_configuration()
|
||||
|
||||
ConfigObject: ConfigDataModel = ConfigDataModel(
|
||||
SERVEUR_IP=import_config["SERVEUR_IP"],
|
||||
SERVEUR_HOSTNAME=import_config["SERVEUR_HOSTNAME"],
|
||||
SERVEUR_LINK=import_config["SERVEUR_LINK"],
|
||||
SERVEUR_PORT=import_config["SERVEUR_PORT"],
|
||||
SERVEUR_PASSWORD=import_config["SERVEUR_PASSWORD"],
|
||||
SERVEUR_ID=import_config["SERVEUR_ID"],
|
||||
SERVEUR_SSL=import_config["SERVEUR_SSL"],
|
||||
SERVICE_NAME=import_config["SERVICE_NAME"],
|
||||
SERVICE_NICKNAME=import_config["SERVICE_NICKNAME"],
|
||||
SERVICE_REALNAME=import_config["SERVICE_REALNAME"],
|
||||
SERVICE_USERNAME=import_config["SERVICE_USERNAME"],
|
||||
SERVICE_HOST=import_config["SERVICE_HOST"],
|
||||
SERVICE_INFO=import_config["SERVICE_INFO"],
|
||||
SERVICE_CHANLOG=import_config["SERVICE_CHANLOG"],
|
||||
SERVICE_SMODES=import_config["SERVICE_SMODES"],
|
||||
SERVICE_CMODES=import_config["SERVICE_CMODES"],
|
||||
SERVICE_UMODES=import_config["SERVICE_UMODES"],
|
||||
SERVICE_PREFIX=import_config["SERVICE_PREFIX"],
|
||||
OWNER=import_config["OWNER"],
|
||||
PASSWORD=import_config["PASSWORD"],
|
||||
SALON_JAIL=import_config["SALON_JAIL"],
|
||||
SALON_JAIL_MODES=import_config["SALON_JAIL_MODES"],
|
||||
SALON_LIBERER=import_config["SALON_LIBERER"],
|
||||
API_TIMEOUT=import_config["API_TIMEOUT"],
|
||||
PORTS_TO_SCAN=import_config["PORTS_TO_SCAN"],
|
||||
WHITELISTED_IP=import_config["WHITELISTED_IP"],
|
||||
GLINE_DURATION=import_config["GLINE_DURATION"],
|
||||
DEBUG_LEVEL=import_config["DEBUG_LEVEL"],
|
||||
CONFIG_COLOR=import_config["CONFIG_COLOR"],
|
||||
table_admin='core_admin',
|
||||
table_commande='core_command',
|
||||
table_log='core_log',
|
||||
table_module='core_module',
|
||||
table_config='core_config',
|
||||
table_channel='core_channel',
|
||||
current_version='',
|
||||
latest_version='',
|
||||
db_name='defender',
|
||||
db_path=f'db{sep}'
|
||||
)
|
||||
|
||||
return ConfigObject
|
||||
35
core/loader.py
Normal file
35
core/loader.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from core.classes import user, admin, channel, clone, reputation, settings
|
||||
import utils
|
||||
import core.definition as df
|
||||
import core.base as baseModule
|
||||
import core.classes.config as confModule
|
||||
|
||||
class Loader:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
# Load Modules
|
||||
self.Definition: df = df
|
||||
|
||||
self.ConfModule: confModule = confModule
|
||||
|
||||
self.BaseModule: baseModule = baseModule
|
||||
|
||||
self.Utils: utils
|
||||
|
||||
# Load Classes
|
||||
self.Settings: settings = settings.Settings()
|
||||
|
||||
self.Config: df.MConfig = self.ConfModule.Configuration().ConfigObject
|
||||
|
||||
self.Base: baseModule.Base = self.BaseModule.Base(self.Config, self.Settings)
|
||||
|
||||
self.User: user.User = user.User(self.Base)
|
||||
|
||||
self.Admin: admin.Admin = admin.Admin(self.Base)
|
||||
|
||||
self.Channel: channel.Channel = channel.Channel(self.Base)
|
||||
|
||||
self.Clone: clone.Clone = clone.Clone(self.Base)
|
||||
|
||||
self.Reputation: reputation.Reputation = reputation.Reputation(self.Base)
|
||||
74
core/utils.py
Normal file
74
core/utils.py
Normal file
@@ -0,0 +1,74 @@
|
||||
from typing import Literal, Union
|
||||
from datetime import datetime
|
||||
from time import time
|
||||
from random import choice
|
||||
from hashlib import md5, sha3_512
|
||||
|
||||
def convert_to_int(value: any) -> Union[int, None]:
|
||||
"""Convert a value to int
|
||||
|
||||
Args:
|
||||
value (any): Value to convert to int if possible
|
||||
|
||||
Returns:
|
||||
Union[int, None]: Return the int value or None if not possible
|
||||
"""
|
||||
try:
|
||||
value_to_int = int(value)
|
||||
return value_to_int
|
||||
except ValueError:
|
||||
return None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def get_unixtime() -> int:
|
||||
"""Cette fonction retourne un UNIXTIME de type 12365456
|
||||
|
||||
Returns:
|
||||
int: Current time in seconds since the Epoch (int)
|
||||
"""
|
||||
return int(time())
|
||||
|
||||
def get_datetime() -> str:
|
||||
"""Retourne une date au format string (24-12-2023 20:50:59)
|
||||
|
||||
Returns:
|
||||
str: Current datetime in this format %d-%m-%Y %H:%M:%S
|
||||
"""
|
||||
currentdate = datetime.now().strftime('%d-%m-%Y %H:%M:%S')
|
||||
return currentdate
|
||||
|
||||
def generate_random_string(lenght: int) -> str:
|
||||
"""Retourn une chaîne aléatoire en fonction de la longueur spécifiée.
|
||||
|
||||
Returns:
|
||||
str: The random string
|
||||
"""
|
||||
caracteres = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
||||
randomize = ''.join(choice(caracteres) for _ in range(lenght))
|
||||
|
||||
return randomize
|
||||
|
||||
def hash(password: str, algorithm: Literal["md5, sha3_512"] = 'md5') -> str:
|
||||
"""Retourne un mot de passe chiffré en fonction de l'algorithme utilisé
|
||||
|
||||
Args:
|
||||
password (str): Le password en clair
|
||||
algorithm (str): L'algorithm a utilisé
|
||||
|
||||
Returns:
|
||||
str: Le password haché
|
||||
"""
|
||||
|
||||
match algorithm:
|
||||
case 'md5':
|
||||
password = md5(password.encode()).hexdigest()
|
||||
return password
|
||||
|
||||
case 'sha3_512':
|
||||
password = sha3_512(password.encode()).hexdigest()
|
||||
return password
|
||||
|
||||
case _:
|
||||
password = md5(password.encode()).hexdigest()
|
||||
return password
|
||||
@@ -12,12 +12,19 @@ from core import installation
|
||||
# LANCEMENT DE DEFENDER #
|
||||
#########################
|
||||
|
||||
# 1. Chargement de la configuration
|
||||
# 2. Chargement de l'ensemble des classes
|
||||
# 3.
|
||||
#
|
||||
|
||||
try:
|
||||
|
||||
installation.Install()
|
||||
|
||||
from core.loader import Loader
|
||||
from core.irc import Irc
|
||||
ircInstance = Irc()
|
||||
loader = Loader()
|
||||
ircInstance = Irc(loader)
|
||||
ircInstance.init_irc(ircInstance)
|
||||
|
||||
except AssertionError as ae:
|
||||
@@ -1,9 +1,11 @@
|
||||
from dataclasses import dataclass, fields, field
|
||||
import random, faker, time
|
||||
import copy
|
||||
import random, faker, time, logging
|
||||
from datetime import datetime
|
||||
from typing import Union
|
||||
from core.irc import Irc
|
||||
from core.connection import Connection
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
|
||||
class Clone():
|
||||
|
||||
@@ -11,7 +13,7 @@ class Clone():
|
||||
class ModConfModel:
|
||||
clone_nicknames: list[str]
|
||||
|
||||
def __init__(self, ircInstance:Irc) -> None:
|
||||
def __init__(self, ircInstance: 'Irc') -> None:
|
||||
|
||||
# Module name (Mandatory)
|
||||
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
|
||||
@@ -19,6 +21,9 @@ class Clone():
|
||||
# Add Irc Object to the module (Mandatory)
|
||||
self.Irc = ircInstance
|
||||
|
||||
# Add Irc Protocol Object to the module (Mandatory)
|
||||
self.Protocol = ircInstance.Protocol
|
||||
|
||||
# Add Global Configuration to the module (Mandatory)
|
||||
self.Config = ircInstance.Config
|
||||
|
||||
@@ -34,7 +39,10 @@ class Clone():
|
||||
# Add Channel object to the module (Mandatory)
|
||||
self.Channel = ircInstance.Channel
|
||||
|
||||
self.Clone = ircInstance.Clones
|
||||
# Add clone object to the module (Optionnal)
|
||||
self.Clone = ircInstance.Clone
|
||||
|
||||
self.Definition = ircInstance.Loader.Definition
|
||||
|
||||
# Créer les nouvelles commandes du module
|
||||
self.commands_level = {
|
||||
@@ -55,9 +63,22 @@ class Clone():
|
||||
# Créer les tables necessaire a votre module (ce n'es pas obligatoire)
|
||||
self.__create_tables()
|
||||
|
||||
self.stop = False
|
||||
logging.getLogger('faker').setLevel(logging.CRITICAL)
|
||||
|
||||
self.fakeEN = faker.Faker('en_GB')
|
||||
self.fakeFR = faker.Faker('fr_FR')
|
||||
|
||||
# Load module configuration (Mandatory)
|
||||
self.__load_module_configuration()
|
||||
|
||||
self.Channel.db_query_channel(action='add', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
|
||||
self.Protocol.send_join_chan(self.Config.SERVICE_NICKNAME, self.Config.CLONE_CHANNEL)
|
||||
|
||||
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} SAMODE {self.Config.CLONE_CHANNEL} +o {self.Config.SERVICE_NICKNAME}")
|
||||
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +nts")
|
||||
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +k {self.Config.CLONE_CHANNEL_PASSWORD}")
|
||||
|
||||
def __set_commands(self, commands:dict[int, list[str]]) -> None:
|
||||
"""### Rajoute les commandes du module au programme principal
|
||||
|
||||
@@ -90,7 +111,7 @@ class Clone():
|
||||
)
|
||||
'''
|
||||
|
||||
self.Base.db_execute_query(table_channel)
|
||||
# self.Base.db_execute_query(table_channel)
|
||||
|
||||
return None
|
||||
|
||||
@@ -116,97 +137,174 @@ class Clone():
|
||||
rechargement de module
|
||||
"""
|
||||
|
||||
# kill all clones before unload
|
||||
for clone in self.ModConfig.clone_nicknames:
|
||||
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone} :KILL')
|
||||
self.Channel.db_query_channel(action='del', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
|
||||
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} -nts")
|
||||
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} -k {self.Config.CLONE_CHANNEL_PASSWORD}")
|
||||
self.Protocol.send_part_chan(self.Config.SERVICE_NICKNAME, self.Config.CLONE_CHANNEL)
|
||||
|
||||
return None
|
||||
|
||||
def thread_change_hostname(self):
|
||||
def generate_vhost(self) -> str:
|
||||
|
||||
fake = faker.Faker('en_GB')
|
||||
for clone in self.Clone.UID_CLONE_DB:
|
||||
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)
|
||||
fake = self.fakeEN
|
||||
|
||||
rand_ip = ''.join(rand_1) + '.' + ''.join(rand_2) + '.' + ''.join(rand_3) + '.IP'
|
||||
found = False
|
||||
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)
|
||||
|
||||
while not found:
|
||||
if clone.connected:
|
||||
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} CHGHOST {clone.nickname} {rand_ip}')
|
||||
found = True
|
||||
break
|
||||
vhost = ''.join(rand_1) + '.' + ''.join(rand_2) + '.' + ''.join(rand_3) + '.IP'
|
||||
return vhost
|
||||
|
||||
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
|
||||
|
||||
def thread_join_channels(self, channel_name: str, wait: float, clone_name:str = None):
|
||||
|
||||
if clone_name is None:
|
||||
for clone in self.Clone.UID_CLONE_DB:
|
||||
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}')
|
||||
time.sleep(wait)
|
||||
else:
|
||||
for clone in self.Clone.UID_CLONE_DB:
|
||||
if clone_name == clone.nickname:
|
||||
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}')
|
||||
time.sleep(wait)
|
||||
|
||||
def generate_names(self) -> tuple[str, str, str]:
|
||||
def generate_clones(self, group: str = 'Default') -> None:
|
||||
try:
|
||||
fake = faker.Faker('en_GB')
|
||||
nickname = fake.first_name()
|
||||
# username = fake.last_name()
|
||||
|
||||
fakeEN = self.fakeEN
|
||||
fakeFR = self.fakeFR
|
||||
unixtime = self.Base.get_unixtime()
|
||||
|
||||
chaine = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
||||
generate_uid = fakeEN.random_sample(chaine, 6)
|
||||
uid = self.Config.SERVEUR_ID + ''.join(generate_uid)
|
||||
|
||||
umodes = self.Config.CLONE_UMODES
|
||||
|
||||
# Generate Username
|
||||
chaine = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
||||
new_username = fake.random_sample(chaine, 9)
|
||||
new_username = fakeEN.random_sample(chaine, 9)
|
||||
username = ''.join(new_username)
|
||||
|
||||
# Create realname XX F|M Department
|
||||
gender = fake.random_choices(['F','M'], 1)
|
||||
gender = fakeEN.random_choices(['F','M'], 1)
|
||||
gender = ''.join(gender)
|
||||
|
||||
if gender == 'F':
|
||||
nickname = fakeEN.first_name_female()
|
||||
elif gender == 'M':
|
||||
nickname = fakeEN.first_name_male()
|
||||
else:
|
||||
nickname = fakeEN.first_name()
|
||||
|
||||
age = random.randint(20, 60)
|
||||
fake_fr = faker.Faker(['fr_FR', 'en_GB'])
|
||||
department = fake_fr.department_name()
|
||||
department = fakeFR.department_name()
|
||||
realname = f'{age} {gender} {department}'
|
||||
|
||||
if self.Clone.exists(nickname=nickname):
|
||||
decoded_ip = fakeEN.ipv4_private()
|
||||
hostname = fakeEN.hostname()
|
||||
|
||||
vhost = self.generate_vhost()
|
||||
|
||||
checkNickname = self.Clone.exists(nickname=nickname)
|
||||
checkUid = self.Clone.uid_exists(uid=uid)
|
||||
|
||||
while checkNickname:
|
||||
caracteres = '0123456789'
|
||||
randomize = ''.join(random.choice(caracteres) for _ in range(2))
|
||||
nickname = nickname + str(randomize)
|
||||
self.Clone.insert(
|
||||
self.Clone.CloneModel(alive=True, nickname=nickname, username=username, realname=realname)
|
||||
)
|
||||
else:
|
||||
self.Clone.insert(
|
||||
self.Clone.CloneModel(alive=True, nickname=nickname, username=username, realname=realname)
|
||||
)
|
||||
checkNickname = self.Clone.exists(nickname=nickname)
|
||||
|
||||
return (nickname, username, realname)
|
||||
while checkUid:
|
||||
chaine = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
||||
generate_uid = fakeEN.random_sample(chaine, 6)
|
||||
uid = self.Config.SERVEUR_ID + ''.join(generate_uid)
|
||||
checkUid = self.Clone.uid_exists(uid=uid)
|
||||
|
||||
clone = self.Definition.MClone(
|
||||
connected=False,
|
||||
nickname=nickname,
|
||||
username=username,
|
||||
realname=realname,
|
||||
hostname=hostname,
|
||||
umodes=umodes,
|
||||
uid=uid,
|
||||
remote_ip=decoded_ip,
|
||||
vhost=vhost,
|
||||
group=group,
|
||||
channels=[]
|
||||
)
|
||||
|
||||
self.Clone.insert(clone)
|
||||
|
||||
return None
|
||||
|
||||
except AttributeError as ae:
|
||||
self.Logs.error(f'Attribute Error : {ae}')
|
||||
except Exception as err:
|
||||
self.Logs.error(err)
|
||||
self.Logs.error(f"General Error: {err}")
|
||||
|
||||
def thread_connect_clones(self, number_of_clones:int , group: str, interval: float = 0.2) -> None:
|
||||
|
||||
for i in range(0, number_of_clones):
|
||||
self.generate_clones(group=group)
|
||||
|
||||
for clone in self.Clone.UID_CLONE_DB:
|
||||
|
||||
if self.stop:
|
||||
print(f"Stop creating clones ...")
|
||||
self.stop = False
|
||||
break
|
||||
|
||||
if not clone.connected:
|
||||
self.Protocol.send_uid(clone.nickname, clone.username, clone.hostname, clone.uid, clone.umodes, clone.vhost, clone.remote_ip, clone.realname, print_log=False)
|
||||
self.Protocol.send_join_chan(uidornickname=clone.uid, channel=self.Config.CLONE_CHANNEL, password=self.Config.CLONE_CHANNEL_PASSWORD, print_log=False)
|
||||
|
||||
time.sleep(interval)
|
||||
clone.connected = True
|
||||
|
||||
def thread_kill_clones(self, fromuser: str) -> None:
|
||||
|
||||
clone_to_kill: list[str] = []
|
||||
for clone in self.Clone.UID_CLONE_DB:
|
||||
clone_to_kill.append(clone.uid)
|
||||
|
||||
for clone_uid in clone_to_kill:
|
||||
self.Protocol.send_quit(clone_uid, 'Gooood bye', print_log=False)
|
||||
|
||||
del clone_to_kill
|
||||
|
||||
return None
|
||||
|
||||
def cmd(self, data:list) -> None:
|
||||
try:
|
||||
service_id = self.Config.SERVICE_ID # Defender serveur id
|
||||
cmd = list(data).copy()
|
||||
|
||||
service_id = self.Config.SERVICE_ID # Defender serveur id
|
||||
cmd = list(data).copy()
|
||||
if len(cmd) < 2:
|
||||
return None
|
||||
|
||||
if len(cmd) < 2:
|
||||
return None
|
||||
match cmd[1]:
|
||||
|
||||
match cmd[1]:
|
||||
case 'REPUTATION':
|
||||
pass
|
||||
|
||||
case 'REPUTATION':
|
||||
pass
|
||||
if len(cmd) < 3:
|
||||
return None
|
||||
|
||||
match cmd[2]:
|
||||
case 'PRIVMSG':
|
||||
# print(cmd)
|
||||
uid_sender = self.User.clean_uid(cmd[1])
|
||||
senderObj = self.User.get_User(uid_sender)
|
||||
|
||||
if senderObj.hostname in self.Config.CLONE_LOG_HOST_EXEMPT:
|
||||
return None
|
||||
|
||||
if not senderObj is None:
|
||||
senderMsg = ' '.join(cmd[4:])
|
||||
getClone = self.Clone.get_Clone(cmd[3])
|
||||
|
||||
if getClone is None:
|
||||
return None
|
||||
|
||||
if getClone.uid != self.Config.SERVICE_ID:
|
||||
final_message = f"{senderObj.nickname}!{senderObj.username}@{senderObj.hostname} > {senderMsg.lstrip(':')}"
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=getClone.uid,
|
||||
msg=final_message,
|
||||
channel=self.Config.CLONE_CHANNEL
|
||||
)
|
||||
|
||||
except Exception as err:
|
||||
self.Logs.error(f'General Error: {err}')
|
||||
|
||||
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
|
||||
|
||||
@@ -221,10 +319,11 @@ class Clone():
|
||||
case 'clone':
|
||||
|
||||
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 kill [all | nickname]')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join [all | nickname] #channel')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone connect NUMBER GROUP_NAME INTERVAL")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill [all | nickname]")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join [all | nickname] #channel")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part [all | nickname] #channel")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone list")
|
||||
|
||||
option = str(cmd[1]).lower()
|
||||
|
||||
@@ -232,53 +331,41 @@ class Clone():
|
||||
|
||||
case 'connect':
|
||||
try:
|
||||
# clone connect 5 Group 3
|
||||
self.stop = False
|
||||
number_of_clones = int(cmd[2])
|
||||
for i in range(number_of_clones):
|
||||
nickname, username, realname = self.generate_names()
|
||||
self.Base.create_thread(
|
||||
self.thread_create_clones,
|
||||
(nickname, username, realname, ['#clones'], 6697, True)
|
||||
)
|
||||
group = str(cmd[3]).lower()
|
||||
connection_interval = int(cmd[4]) if len(cmd) == 5 else 0.5
|
||||
|
||||
self.Base.create_thread(
|
||||
self.thread_change_hostname,
|
||||
run_once=True
|
||||
func=self.thread_connect_clones,
|
||||
func_args=(number_of_clones, group, connection_interval)
|
||||
)
|
||||
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{str(number_of_clones)} clones joined the network')
|
||||
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect [number of clone you want to connect]')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} clone connect 6')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone connect [number of clone you want to connect] [Group]")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Exemple /msg {dnickname} clone connect 6 Ambiance")
|
||||
|
||||
case 'kill':
|
||||
try:
|
||||
# clone kill [all | nickname]
|
||||
self.stop = True
|
||||
clone_name = str(cmd[2])
|
||||
clone_to_kill: list[str] = []
|
||||
|
||||
if clone_name.lower() == 'all':
|
||||
for clone in self.Clone.UID_CLONE_DB:
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone.nickname} :KILL')
|
||||
clone_to_kill.append(clone.nickname)
|
||||
clone.alive = False
|
||||
|
||||
for clone_nickname in clone_to_kill:
|
||||
self.Clone.delete(clone_nickname)
|
||||
|
||||
del clone_to_kill
|
||||
self.Base.create_thread(func=self.thread_kill_clones, func_args=(fromuser, ))
|
||||
|
||||
else:
|
||||
if self.Clone.exists(clone_name):
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :KILL')
|
||||
self.Clone.kill(clone_name)
|
||||
self.Clone.delete(clone_name)
|
||||
cloneObj = self.Clone.get_Clone(clone_name)
|
||||
if not cloneObj is None:
|
||||
self.Protocol.send_quit(cloneObj.uid, 'Goood bye', print_log=False)
|
||||
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill all')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill clone_nickname')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill all")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill clone_nickname")
|
||||
|
||||
case 'join':
|
||||
try:
|
||||
@@ -287,19 +374,49 @@ class Clone():
|
||||
clone_channel_to_join = str(cmd[3])
|
||||
|
||||
if clone_name.lower() == 'all':
|
||||
self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2))
|
||||
|
||||
for clone in self.Clone.UID_CLONE_DB:
|
||||
self.Protocol.send_join_chan(uidornickname=clone.uid, channel=clone_channel_to_join, print_log=False)
|
||||
|
||||
else:
|
||||
self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2, clone_name))
|
||||
if self.Clone.exists(clone_name):
|
||||
if not self.Clone.get_uid(clone_name) is None:
|
||||
self.Protocol.send_join_chan(uidornickname=clone_name, channel=clone_channel_to_join, print_log=False)
|
||||
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join all #channel')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join clone_nickname #channel')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join all #channel")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join clone_nickname #channel")
|
||||
|
||||
case 'part':
|
||||
try:
|
||||
# clone part [all | nickname] #channel
|
||||
clone_name = str(cmd[2])
|
||||
clone_channel_to_part = str(cmd[3])
|
||||
|
||||
if clone_name.lower() == 'all':
|
||||
|
||||
for clone in self.Clone.UID_CLONE_DB:
|
||||
self.Protocol.send_part_chan(uidornickname=clone.uid, channel=clone_channel_to_part, print_log=False)
|
||||
|
||||
else:
|
||||
if self.Clone.exists(clone_name):
|
||||
clone_uid = self.Clone.get_uid(clone_name)
|
||||
if not clone_uid is None:
|
||||
self.Protocol.send_part_chan(uidornickname=clone_uid, channel=clone_channel_to_part, print_log=False)
|
||||
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part all #channel")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part clone_nickname #channel")
|
||||
|
||||
case 'list':
|
||||
try:
|
||||
clone_count = len(self.Clone.UID_CLONE_DB)
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f">> Number of connected clones: {clone_count}")
|
||||
for clone_name in self.Clone.UID_CLONE_DB:
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> Nickname: {clone_name.nickname} | Username: {clone_name.username}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,
|
||||
msg=f">> Nickname: {clone_name.nickname} | Username: {clone_name.username} | Realname: {clone_name.realname} | Vhost: {clone_name.vhost} | UID: {clone_name.uid} | Group: {clone_name.group} | Connected: {clone_name.connected}")
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
|
||||
@@ -307,30 +424,36 @@ class Clone():
|
||||
try:
|
||||
# clone say clone_nickname #channel message
|
||||
clone_name = str(cmd[2])
|
||||
clone_channel = str(cmd[3]) if self.Base.Is_Channel(str(cmd[3])) else None
|
||||
clone_channel = str(cmd[3]) if self.Channel.Is_Channel(str(cmd[3])) else None
|
||||
|
||||
message = []
|
||||
for i in range(4, len(cmd)):
|
||||
message.append(cmd[i])
|
||||
final_message = ' '.join(message)
|
||||
final_message = ' '.join(cmd[4:])
|
||||
|
||||
if clone_channel is None or not self.Clone.exists(clone_name):
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel message')
|
||||
self.Protocol.send_notice(
|
||||
nick_from=dnickname,
|
||||
nick_to=fromuser,
|
||||
msg=f"/msg {dnickname} clone say [clone_nickname] #channel message"
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
if self.Clone.exists(clone_name):
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :SAY {clone_channel} {final_message}')
|
||||
self.Protocol.send_priv_msg(nick_from=clone_name, msg=final_message, channel=clone_channel)
|
||||
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel message')
|
||||
self.Protocol.send_notice(
|
||||
nick_from=dnickname,
|
||||
nick_to=fromuser,
|
||||
msg=f"/msg {dnickname} clone say [clone_nickname] #channel message"
|
||||
)
|
||||
|
||||
case _:
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect 6')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill [all | nickname]')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join [all | nickname] #channel')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel [message]')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone connect NUMBER GROUP_NAME INTERVAL")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill [all | nickname]")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join [all | nickname] #channel")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part [all | nickname] #channel")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone list")
|
||||
|
||||
except IndexError as ie:
|
||||
self.Logs.error(f'Index Error: {ie}')
|
||||
except Exception as err:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1174
mods/mod_defender.py
1174
mods/mod_defender.py
File diff suppressed because it is too large
Load Diff
335
mods/mod_jsonrpc.py
Normal file
335
mods/mod_jsonrpc.py
Normal file
@@ -0,0 +1,335 @@
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
from dataclasses import dataclass
|
||||
from unrealircd_rpc_py.Live import Live
|
||||
from unrealircd_rpc_py.Loader import Loader
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
|
||||
class Jsonrpc():
|
||||
|
||||
@dataclass
|
||||
class ModConfModel:
|
||||
"""The Model containing the module parameters
|
||||
"""
|
||||
jsonrpc: int = 0
|
||||
|
||||
def __init__(self, ircInstance: 'Irc') -> None:
|
||||
|
||||
# Module name (Mandatory)
|
||||
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
|
||||
|
||||
# Add Irc Object to the module (Mandatory)
|
||||
self.Irc = ircInstance
|
||||
|
||||
# Add Protocol to the module (Mandatory)
|
||||
self.Protocol = ircInstance.Protocol
|
||||
|
||||
# Add Global Configuration to the module (Mandatory)
|
||||
self.Config = ircInstance.Config
|
||||
|
||||
# Add Base object to the module (Mandatory)
|
||||
self.Base = ircInstance.Base
|
||||
|
||||
# Add logs object to the module (Mandatory)
|
||||
self.Logs = ircInstance.Base.logs
|
||||
|
||||
# Add User object to the module (Mandatory)
|
||||
self.User = ircInstance.User
|
||||
|
||||
# Add Channel object to the module (Mandatory)
|
||||
self.Channel = ircInstance.Channel
|
||||
|
||||
# Create module commands (Mandatory)
|
||||
self.commands_level = {
|
||||
1: ['jsonrpc', 'jruser']
|
||||
}
|
||||
|
||||
# Init the module
|
||||
self.__init_module()
|
||||
|
||||
# Log the module
|
||||
self.Logs.debug(f'Module {self.module_name} loaded ...')
|
||||
|
||||
def __init_module(self) -> None:
|
||||
|
||||
# Insert module commands into the core one (Mandatory)
|
||||
self.__set_commands(self.commands_level)
|
||||
logging.getLogger('websockets').setLevel(logging.WARNING)
|
||||
|
||||
# Create you own tables (Mandatory)
|
||||
# self.__create_tables()
|
||||
|
||||
# Load module configuration and sync with core one (Mandatory)
|
||||
self.__load_module_configuration()
|
||||
# End of mandatory methods you can start your customization #
|
||||
|
||||
# self.UnrealIrcdRpcLive: Live = Live(
|
||||
# req_method='unixsocket',
|
||||
# path_to_socket_file=self.Config.JSONRPC_PATH_TO_SOCKET_FILE,
|
||||
# callback_object_instance=self,
|
||||
# callback_method_name='callback_sent_to_irc'
|
||||
# )
|
||||
|
||||
self.UnrealIrcdRpcLive: Live = Live(
|
||||
req_method='websocket',
|
||||
url=self.Config.JSONRPC_URL,
|
||||
username=self.Config.JSONRPC_USER,
|
||||
password=self.Config.JSONRPC_PASSWORD,
|
||||
callback_object_instance=self,
|
||||
callback_method_name='callback_sent_to_irc'
|
||||
)
|
||||
|
||||
self.Rpc: Loader = Loader(
|
||||
req_method=self.Config.JSONRPC_METHOD,
|
||||
url=self.Config.JSONRPC_URL,
|
||||
username=self.Config.JSONRPC_USER,
|
||||
password=self.Config.JSONRPC_PASSWORD
|
||||
)
|
||||
|
||||
self.subscribed = False
|
||||
|
||||
if self.Rpc.Error.code != 0:
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=self.Config.SERVICE_NICKNAME,
|
||||
msg=f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.Rpc.Error.message}",
|
||||
channel=self.Config.SERVICE_CHANLOG
|
||||
)
|
||||
|
||||
if self.UnrealIrcdRpcLive.Error.code != 0:
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=self.Config.SERVICE_NICKNAME,
|
||||
msg=f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.UnrealIrcdRpcLive.Error.message}",
|
||||
channel=self.Config.SERVICE_CHANLOG
|
||||
)
|
||||
|
||||
if self.ModConfig.jsonrpc == 1:
|
||||
self.Base.create_thread(self.thread_start_jsonrpc, run_once=True)
|
||||
|
||||
return None
|
||||
|
||||
def __set_commands(self, commands:dict[int, list[str]]) -> None:
|
||||
"""### Rajoute les commandes du module au programme principal
|
||||
|
||||
Args:
|
||||
commands (list): Liste des commandes du module
|
||||
"""
|
||||
for level, com in commands.items():
|
||||
for c in commands[level]:
|
||||
if not c in self.Irc.commands:
|
||||
self.Irc.commands_level[level].append(c)
|
||||
self.Irc.commands.append(c)
|
||||
|
||||
return None
|
||||
|
||||
def __create_tables(self) -> None:
|
||||
"""Methode qui va créer la base de donnée si elle n'existe pas.
|
||||
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
|
||||
Args:
|
||||
database_name (str): Nom de la base de données ( pas d'espace dans le nom )
|
||||
|
||||
Returns:
|
||||
None: Aucun retour n'es attendu
|
||||
"""
|
||||
|
||||
table_logs = '''CREATE TABLE IF NOT EXISTS test_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
datetime TEXT,
|
||||
server_msg TEXT
|
||||
)
|
||||
'''
|
||||
|
||||
self.Base.db_execute_query(table_logs)
|
||||
return None
|
||||
|
||||
def callback_sent_to_irc(self, json_response: str):
|
||||
|
||||
dnickname = self.Config.SERVICE_NICKNAME
|
||||
dchanlog = self.Config.SERVICE_CHANLOG
|
||||
green = self.Config.COLORS.green
|
||||
nogc = self.Config.COLORS.nogc
|
||||
bold = self.Config.COLORS.bold
|
||||
red = self.Config.COLORS.red
|
||||
|
||||
if json_response.result == True:
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=self.Config.SERVICE_NICKNAME,
|
||||
msg=f"[{bold}{green}JSONRPC{nogc}{bold}] Event activated",
|
||||
channel=dchanlog)
|
||||
return None
|
||||
|
||||
level = json_response.result.level
|
||||
subsystem = json_response.result.subsystem
|
||||
event_id = json_response.result.event_id
|
||||
log_source = json_response.result.log_source
|
||||
msg = json_response.result.msg
|
||||
|
||||
build_msg = f"{green}{log_source}{nogc}: [{bold}{level}{bold}] {subsystem}.{event_id} - {msg}"
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=build_msg, channel=dchanlog)
|
||||
|
||||
def thread_start_jsonrpc(self):
|
||||
|
||||
if self.UnrealIrcdRpcLive.Error.code == 0:
|
||||
self.UnrealIrcdRpcLive.subscribe(["all"])
|
||||
self.subscribed = True
|
||||
else:
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=self.Config.SERVICE_NICKNAME,
|
||||
msg=f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.UnrealIrcdRpcLive.Error.message}",
|
||||
channel=self.Config.SERVICE_CHANLOG
|
||||
)
|
||||
|
||||
def __load_module_configuration(self) -> None:
|
||||
"""### Load Module Configuration
|
||||
"""
|
||||
try:
|
||||
# Build the default configuration model (Mandatory)
|
||||
self.ModConfig = self.ModConfModel(jsonrpc=0)
|
||||
|
||||
# Sync the configuration with core configuration (Mandatory)
|
||||
self.Base.db_sync_core_config(self.module_name, self.ModConfig)
|
||||
|
||||
return None
|
||||
|
||||
except TypeError as te:
|
||||
self.Logs.critical(te)
|
||||
|
||||
def __update_configuration(self, param_key: str, param_value: str):
|
||||
"""Update the local and core configuration
|
||||
|
||||
Args:
|
||||
param_key (str): The parameter key
|
||||
param_value (str): The parameter value
|
||||
"""
|
||||
self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
|
||||
|
||||
def unload(self) -> None:
|
||||
if self.UnrealIrcdRpcLive.Error.code != -1:
|
||||
self.UnrealIrcdRpcLive.unsubscribe()
|
||||
return None
|
||||
|
||||
def cmd(self, data:list) -> None:
|
||||
|
||||
return None
|
||||
|
||||
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
|
||||
|
||||
command = str(cmd[0]).lower()
|
||||
dnickname = self.Config.SERVICE_NICKNAME
|
||||
dchannel = self.Config.SERVICE_CHANLOG
|
||||
fromuser = user
|
||||
fromchannel = str(channel) if not channel is None else None
|
||||
|
||||
match command:
|
||||
|
||||
case 'jsonrpc':
|
||||
try:
|
||||
option = str(cmd[1]).lower()
|
||||
|
||||
if len(command) == 1:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jsonrpc on')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jsonrpc off')
|
||||
|
||||
match option:
|
||||
|
||||
case 'on':
|
||||
for thread in self.Base.running_threads:
|
||||
if thread.getName() == 'thread_start_jsonrpc':
|
||||
if thread.is_alive():
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=self.Config.SERVICE_NICKNAME,
|
||||
msg=f"Thread {thread.getName()} is running",
|
||||
channel=dchannel
|
||||
)
|
||||
else:
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=self.Config.SERVICE_NICKNAME,
|
||||
msg=f"Thread {thread.getName()} is not running, wait untill the process will be cleaned up",
|
||||
channel=dchannel
|
||||
)
|
||||
|
||||
self.Base.create_thread(self.thread_start_jsonrpc, run_once=True)
|
||||
self.__update_configuration('jsonrpc', 1)
|
||||
|
||||
case 'off':
|
||||
self.UnrealIrcdRpcLive.unsubscribe()
|
||||
self.__update_configuration('jsonrpc', 0)
|
||||
|
||||
except IndexError as ie:
|
||||
self.Logs.error(ie)
|
||||
|
||||
case 'jruser':
|
||||
try:
|
||||
option = str(cmd[1]).lower()
|
||||
|
||||
if len(command) == 1:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jruser get nickname')
|
||||
|
||||
match option:
|
||||
|
||||
case 'get':
|
||||
nickname = str(cmd[2])
|
||||
uid_to_get = self.User.get_uid(nickname)
|
||||
if uid_to_get is None:
|
||||
return None
|
||||
|
||||
rpc = self.Rpc
|
||||
|
||||
UserInfo = rpc.User.get(uid_to_get)
|
||||
if rpc.Error.code != 0:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'{rpc.Error.message}')
|
||||
return None
|
||||
|
||||
chan_list = []
|
||||
for chan in UserInfo.user.channels:
|
||||
chan_list.append(chan.name)
|
||||
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'UID : {UserInfo.id}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'NICKNAME : {UserInfo.name}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'USERNAME : {UserInfo.user.username}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'REALNAME : {UserInfo.user.realname}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'MODES : {UserInfo.user.modes}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CHANNELS : {chan_list}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'SECURITY GROUP : {UserInfo.user.security_groups}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'REPUTATION : {UserInfo.user.reputation}')
|
||||
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'IP : {UserInfo.ip}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'COUNTRY CODE : {UserInfo.geoip.country_code}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'ASN : {UserInfo.geoip.asn}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'ASNAME : {UserInfo.geoip.asname}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CLOAKED HOST : {UserInfo.user.cloakedhost}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'HOSTNAME : {UserInfo.hostname}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'VHOST : {UserInfo.user.vhost}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CLIENT PORT : {UserInfo.client_port}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'SERVER PORT : {UserInfo.server_port}')
|
||||
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CERTFP : {UserInfo.tls.certfp}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CIPHER : {UserInfo.tls.cipher}')
|
||||
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'IDLE SINCE : {UserInfo.idle_since}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CONNECTED SINCE : {UserInfo.connected_since}')
|
||||
|
||||
except IndexError as ie:
|
||||
self.Logs.error(ie)
|
||||
|
||||
case 'ia':
|
||||
try:
|
||||
|
||||
self.Base.create_thread(self.thread_ask_ia, ('',))
|
||||
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" This is a notice to the sender ...")
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg="This is private message to the sender ...", nick_to=fromuser)
|
||||
|
||||
if not fromchannel is None:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg="This is channel message to the sender ...", channel=fromchannel)
|
||||
|
||||
# How to update your module configuration
|
||||
self.__update_configuration('param_exemple2', 7)
|
||||
|
||||
# Log if you want the result
|
||||
self.Logs.debug(f"Test logs ready")
|
||||
|
||||
except Exception as err:
|
||||
self.Logs.error(f"Unknown Error: {err}")
|
||||
@@ -1,5 +1,8 @@
|
||||
from typing import TYPE_CHECKING
|
||||
from dataclasses import dataclass, fields
|
||||
from core.irc import Irc
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
|
||||
class Test():
|
||||
|
||||
@@ -10,7 +13,7 @@ class Test():
|
||||
param_exemple1: str
|
||||
param_exemple2: int
|
||||
|
||||
def __init__(self, ircInstance:Irc) -> None:
|
||||
def __init__(self, ircInstance: 'Irc') -> None:
|
||||
|
||||
# Module name (Mandatory)
|
||||
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
|
||||
@@ -18,6 +21,12 @@ class Test():
|
||||
# Add Irc Object to the module (Mandatory)
|
||||
self.Irc = ircInstance
|
||||
|
||||
# Add Loader Object to the module (Mandatory)
|
||||
self.Loader = ircInstance.Loader
|
||||
|
||||
# Add server protocol Object to the module (Mandatory)
|
||||
self.Protocol = ircInstance.Protocol
|
||||
|
||||
# Add Global Configuration to the module (Mandatory)
|
||||
self.Config = ircInstance.Config
|
||||
|
||||
@@ -33,6 +42,9 @@ class Test():
|
||||
# Add Channel object to the module (Mandatory)
|
||||
self.Channel = ircInstance.Channel
|
||||
|
||||
# Add Reputation object to the module (Optional)
|
||||
self.Reputation = ircInstance.Reputation
|
||||
|
||||
# Create module commands (Mandatory)
|
||||
self.commands_level = {
|
||||
0: ['test-command'],
|
||||
@@ -124,8 +136,16 @@ class Test():
|
||||
return None
|
||||
|
||||
def cmd(self, data:list) -> None:
|
||||
try:
|
||||
cmd = list(data).copy()
|
||||
|
||||
return None
|
||||
return None
|
||||
except KeyError as ke:
|
||||
self.Logs.error(f"Key Error: {ke}")
|
||||
except IndexError as ie:
|
||||
self.Logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
|
||||
except Exception as err:
|
||||
self.Logs.error(f"General Error: {err}")
|
||||
|
||||
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
|
||||
|
||||
@@ -139,11 +159,11 @@ class Test():
|
||||
case 'test-command':
|
||||
try:
|
||||
|
||||
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} : This is a notice to the sender ...")
|
||||
self.Irc.send2socket(f":{dnickname} PRIVMSG {fromuser} : This is private message to the sender ...")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="This is a notice to the sender ...")
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"This is private message to the sender ...", nick_to=fromuser)
|
||||
|
||||
if not fromchannel is None:
|
||||
self.Irc.send2socket(f":{dnickname} PRIVMSG {fromchannel} : This is channel message to the sender ...")
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"This is private message to the sender ...", channel=fromchannel)
|
||||
|
||||
# How to update your module configuration
|
||||
self.__update_configuration('param_exemple2', 7)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from core.irc import Irc
|
||||
from typing import TYPE_CHECKING
|
||||
import re
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
|
||||
# Activer le systeme sur un salon (activate #salon)
|
||||
# Le service devra se connecter au salon
|
||||
# Le service devra se mettre en op
|
||||
@@ -23,7 +26,7 @@ class Votekick():
|
||||
|
||||
VOTE_CHANNEL_DB:list[VoteChannelModel] = []
|
||||
|
||||
def __init__(self, ircInstance:Irc) -> None:
|
||||
def __init__(self, ircInstance: 'Irc') -> None:
|
||||
|
||||
# Module name (Mandatory)
|
||||
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
|
||||
@@ -31,6 +34,12 @@ class Votekick():
|
||||
# Add Irc Object to the module
|
||||
self.Irc = ircInstance
|
||||
|
||||
# Add Loader Object to the module (Mandatory)
|
||||
self.Loader = ircInstance.Loader
|
||||
|
||||
# Add server protocol Object to the module (Mandatory)
|
||||
self.Protocol = ircInstance.Protocol
|
||||
|
||||
# Add Global Configuration to the module
|
||||
self.Config = ircInstance.Config
|
||||
|
||||
@@ -55,7 +64,7 @@ class Votekick():
|
||||
self.__init_module()
|
||||
|
||||
# Log the module
|
||||
self.Logs.debug(f'Module {self.module_name} loaded ...')
|
||||
self.Logs.debug(f'-- Module {self.module_name} loaded ...')
|
||||
|
||||
def __init_module(self) -> None:
|
||||
|
||||
@@ -113,7 +122,7 @@ class Votekick():
|
||||
def unload(self) -> None:
|
||||
try:
|
||||
for chan in self.VOTE_CHANNEL_DB:
|
||||
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PART {chan.channel_name}")
|
||||
self.Protocol.send_part_chan(uidornickname=self.Config.SERVICE_ID, channel=chan.channel_name)
|
||||
|
||||
self.VOTE_CHANNEL_DB = []
|
||||
self.Logs.debug(f'Delete memory DB VOTE_CHANNEL_DB: {self.VOTE_CHANNEL_DB}')
|
||||
@@ -123,8 +132,8 @@ class Votekick():
|
||||
self.Logs.error(f'{ne}')
|
||||
except NameError as ue:
|
||||
self.Logs.error(f'{ue}')
|
||||
except:
|
||||
self.Logs.error('Error on the module')
|
||||
except Exception as err:
|
||||
self.Logs.error(f'General Error: {err}')
|
||||
|
||||
def init_vote_system(self, channel: str) -> bool:
|
||||
|
||||
@@ -195,7 +204,7 @@ class Votekick():
|
||||
def join_saved_channels(self) -> None:
|
||||
|
||||
param = {'module_name': self.module_name}
|
||||
result = self.Base.db_execute_query(f"SELECT id, channel_name FROM {self.Config.table_channel} WHERE module_name = :module_name", param)
|
||||
result = self.Base.db_execute_query(f"SELECT id, channel_name FROM {self.Config.TABLE_CHANNEL} WHERE module_name = :module_name", param)
|
||||
|
||||
channels = result.fetchall()
|
||||
unixtime = self.Base.get_unixtime()
|
||||
@@ -203,8 +212,8 @@ class Votekick():
|
||||
for channel in channels:
|
||||
id, chan = channel
|
||||
self.insert_vote_channel(self.VoteChannelModel(channel_name=chan, target_user='', voter_users=[], vote_for=0, vote_against=0))
|
||||
self.Irc.send2socket(f":{self.Config.SERVEUR_ID} SJOIN {unixtime} {chan} + :{self.Config.SERVICE_ID}")
|
||||
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} SAMODE {chan} +o {self.Config.SERVICE_NICKNAME}")
|
||||
self.Protocol.sjoin(channel=chan)
|
||||
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} SAMODE {chan} +o {self.Config.SERVICE_NICKNAME}")
|
||||
|
||||
return None
|
||||
|
||||
@@ -229,28 +238,41 @@ class Votekick():
|
||||
if chan.channel_name == channel:
|
||||
target_user = self.User.get_nickname(chan.target_user)
|
||||
if chan.vote_for > chan.vote_against:
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["nogc"]} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll be kicked from the channel')
|
||||
self.Irc.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}")
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=dnickname,
|
||||
msg=f"User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it'll be kicked from the channel",
|
||||
channel=channel
|
||||
)
|
||||
self.Protocol.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}")
|
||||
self.Channel.delete_user_from_channel(channel, self.User.get_uid(target_user))
|
||||
elif chan.vote_for <= chan.vote_against:
|
||||
self.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.Protocol.send_priv_msg(
|
||||
nick_from=dnickname,
|
||||
msg=f"User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel",
|
||||
channel=channel
|
||||
)
|
||||
|
||||
# Init the system
|
||||
if self.init_vote_system(channel):
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :System vote re initiated')
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=dnickname,
|
||||
msg="System vote re initiated",
|
||||
channel=channel
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
def cmd(self, data:list) -> None:
|
||||
cmd = list(data).copy()
|
||||
try:
|
||||
cmd = list(data).copy()
|
||||
return None
|
||||
|
||||
match cmd[2]:
|
||||
case 'SJOIN':
|
||||
pass
|
||||
case _:
|
||||
pass
|
||||
|
||||
return None
|
||||
except KeyError as ke:
|
||||
self.Logs.error(f"Key Error: {ke}")
|
||||
except IndexError as ie:
|
||||
self.Logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
|
||||
except Exception as err:
|
||||
self.Logs.error(f"General Error: {err}")
|
||||
|
||||
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
|
||||
# cmd is the command starting from the user command
|
||||
@@ -262,18 +284,21 @@ class Votekick():
|
||||
fromchannel = channel
|
||||
|
||||
match command:
|
||||
case 'vote':
|
||||
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')
|
||||
case 'vote':
|
||||
|
||||
if len(cmd) == 1:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote activate #channel')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote deactivate #channel')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote +')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote -')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote cancel')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote status')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote submit nickname')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote verdict')
|
||||
return None
|
||||
|
||||
option = str(cmd[1]).lower()
|
||||
|
||||
match option:
|
||||
|
||||
@@ -281,12 +306,12 @@ class Votekick():
|
||||
try:
|
||||
# vote activate #channel
|
||||
if self.Admin.get_Admin(fromuser) is None:
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' :Your are not allowed to execute this command')
|
||||
return None
|
||||
|
||||
sentchannel = str(cmd[2]).lower() if self.Base.Is_Channel(str(cmd[2]).lower()) else None
|
||||
sentchannel = str(cmd[2]).lower() if self.Channel.Is_Channel(str(cmd[2]).lower()) else None
|
||||
if sentchannel is None:
|
||||
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The correct command is {self.Config.SERVICE_PREFIX}{command} {option} #CHANNEL")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" The correct command is {self.Config.SERVICE_PREFIX}{command} {option} #CHANNEL")
|
||||
|
||||
self.insert_vote_channel(
|
||||
self.VoteChannelModel(
|
||||
@@ -298,40 +323,43 @@ class Votekick():
|
||||
)
|
||||
)
|
||||
|
||||
self.Base.db_query_channel('add', self.module_name, sentchannel)
|
||||
self.Channel.db_query_channel('add', self.module_name, sentchannel)
|
||||
|
||||
self.Irc.send2socket(f":{dnickname} JOIN {sentchannel}")
|
||||
self.Irc.send2socket(f":{dnickname} SAMODE {sentchannel} +o {dnickname}")
|
||||
self.Irc.send2socket(f":{dnickname} PRIVMSG {sentchannel} :You can now use !submit <nickname> to decide if he will stay or not on this channel ")
|
||||
self.Protocol.send_join_chan(uidornickname=dnickname, channel=sentchannel)
|
||||
self.Protocol.send2socket(f":{dnickname} SAMODE {sentchannel} +o {dnickname}")
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg="You can now use !submit <nickname> to decide if he will stay or not on this channel ",
|
||||
channel=sentchannel
|
||||
)
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option} #channel')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option} #welcome')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option} #channel')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option} #welcome')
|
||||
|
||||
case 'deactivate':
|
||||
try:
|
||||
# vote deactivate #channel
|
||||
if self.Admin.get_Admin(fromuser) is None:
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" Your are not allowed to execute this command")
|
||||
return None
|
||||
|
||||
sentchannel = str(cmd[2]).lower() if self.Base.Is_Channel(str(cmd[2]).lower()) else None
|
||||
sentchannel = str(cmd[2]).lower() if self.Channel.Is_Channel(str(cmd[2]).lower()) else None
|
||||
if sentchannel is None:
|
||||
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The correct command is {self.Config.SERVICE_PREFIX}{command} {option} #CHANNEL")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" The correct command is {self.Config.SERVICE_PREFIX}{command} {option} #CHANNEL")
|
||||
|
||||
self.Irc.send2socket(f":{dnickname} SAMODE {sentchannel} -o {dnickname}")
|
||||
self.Irc.send2socket(f":{dnickname} PART {sentchannel}")
|
||||
self.Protocol.send2socket(f":{dnickname} SAMODE {sentchannel} -o {dnickname}")
|
||||
self.Protocol.send_part_chan(uidornickname=dnickname, channel=sentchannel)
|
||||
|
||||
for chan in self.VOTE_CHANNEL_DB:
|
||||
if chan.channel_name == sentchannel:
|
||||
self.VOTE_CHANNEL_DB.remove(chan)
|
||||
self.Base.db_query_channel('del', self.module_name, chan.channel_name)
|
||||
self.Channel.db_query_channel('del', self.module_name, chan.channel_name)
|
||||
|
||||
self.Logs.debug(f"The Channel {sentchannel} has been deactivated from the vote system")
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option} #channel')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option} #welcome')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" /msg {dnickname} {command} {option} #channel")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" Exemple /msg {dnickname} {command} {option} #welcome")
|
||||
|
||||
case '+':
|
||||
try:
|
||||
@@ -340,15 +368,21 @@ class Votekick():
|
||||
for chan in self.VOTE_CHANNEL_DB:
|
||||
if chan.channel_name == channel:
|
||||
if fromuser in chan.voter_users:
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :You already submitted a vote')
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg="You already submitted a vote",
|
||||
channel=channel
|
||||
)
|
||||
else:
|
||||
chan.vote_for += 1
|
||||
chan.voter_users.append(fromuser)
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote recorded, thank you')
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg="Vote recorded, thank you",
|
||||
channel=channel
|
||||
)
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}')
|
||||
|
||||
case '-':
|
||||
try:
|
||||
@@ -357,54 +391,65 @@ class Votekick():
|
||||
for chan in self.VOTE_CHANNEL_DB:
|
||||
if chan.channel_name == channel:
|
||||
if fromuser in chan.voter_users:
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :You already submitted a vote')
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg="You already submitted a vote",
|
||||
channel=channel
|
||||
)
|
||||
else:
|
||||
chan.vote_against += 1
|
||||
chan.voter_users.append(fromuser)
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote recorded, thank you')
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg="Vote recorded, thank you",
|
||||
channel=channel
|
||||
)
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}')
|
||||
|
||||
case 'cancel':
|
||||
try:
|
||||
# vote cancel
|
||||
if self.Admin.get_Admin(fromuser) is None:
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Your are not allowed to execute this command')
|
||||
return None
|
||||
|
||||
if channel is None:
|
||||
self.Logs.error(f"The channel is not known, defender can't cancel the vote")
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :You need to specify the channel => /msg {dnickname} vote_cancel #channel')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' You need to specify the channel => /msg {dnickname} vote_cancel #channel')
|
||||
|
||||
for vote in self.VOTE_CHANNEL_DB:
|
||||
if vote.channel_name == channel:
|
||||
self.init_vote_system(channel)
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote system re-initiated')
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg="Vote system re-initiated",
|
||||
channel=channel
|
||||
)
|
||||
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}')
|
||||
|
||||
case 'status':
|
||||
try:
|
||||
# vote status
|
||||
for chan in self.VOTE_CHANNEL_DB:
|
||||
if chan.channel_name == channel:
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Channel: {chan.channel_name} | Target: {self.User.get_nickname(chan.target_user)} | For: {chan.vote_for} | Against: {chan.vote_against} | Number of voters: {str(len(chan.voter_users))}')
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"Channel: {chan.channel_name} | Target: {self.User.get_nickname(chan.target_user)} | For: {chan.vote_for} | Against: {chan.vote_against} | Number of voters: {str(len(chan.voter_users))}",
|
||||
channel=channel
|
||||
)
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}')
|
||||
|
||||
case 'submit':
|
||||
try:
|
||||
# vote submit nickname
|
||||
if self.Admin.get_Admin(fromuser) is None:
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Your are not allowed to execute this command')
|
||||
return None
|
||||
|
||||
nickname_submitted = cmd[2]
|
||||
@@ -417,18 +462,24 @@ class Votekick():
|
||||
if vote.channel_name == channel:
|
||||
ongoing_user = self.User.get_nickname(vote.target_user)
|
||||
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :There is an ongoing vote on {ongoing_user}')
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"There is an ongoing vote on {ongoing_user}",
|
||||
channel=channel
|
||||
)
|
||||
return False
|
||||
|
||||
# check if the user exist
|
||||
if user_submitted is None:
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This nickname <{nickname_submitted}> do not exist')
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"This nickname <{nickname_submitted}> do not exist",
|
||||
channel=channel
|
||||
)
|
||||
return False
|
||||
|
||||
uid_cleaned = self.Base.clean_uid(uid_submitted)
|
||||
ChannelInfo = self.Channel.get_Channel(channel)
|
||||
if ChannelInfo is None:
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :This channel [{channel}] do not exist in the Channel Object')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' This channel [{channel}] do not exist in the Channel Object')
|
||||
return False
|
||||
|
||||
clean_uids_in_channel: list = []
|
||||
@@ -436,60 +487,83 @@ class Votekick():
|
||||
clean_uids_in_channel.append(self.Base.clean_uid(uid))
|
||||
|
||||
if not uid_cleaned in clean_uids_in_channel:
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This nickname <{nickname_submitted}> is not available in this channel')
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"This nickname <{nickname_submitted}> is not available in this channel",
|
||||
channel=channel
|
||||
)
|
||||
return False
|
||||
|
||||
# check if Ircop or Service or Bot
|
||||
pattern = fr'[o|B|S]'
|
||||
operator_user = re.findall(pattern, user_submitted.umodes)
|
||||
if operator_user:
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :You cant vote for this user ! he/she is protected')
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg="You cant vote for this user ! he/she is protected",
|
||||
channel=channel
|
||||
)
|
||||
return False
|
||||
|
||||
for chan in self.VOTE_CHANNEL_DB:
|
||||
if chan.channel_name == channel:
|
||||
chan.target_user = self.User.get_uid(nickname_submitted)
|
||||
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :{nickname_submitted} has been targeted for a vote')
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"{nickname_submitted} has been targeted for a vote",
|
||||
channel=channel
|
||||
)
|
||||
|
||||
self.Base.create_timer(60, self.timer_vote_verdict, (channel, ))
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This vote will end after 60 secondes')
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg="This vote will end after 60 secondes",
|
||||
channel=channel
|
||||
)
|
||||
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option} nickname')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option} adator')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option} nickname')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option} adator')
|
||||
|
||||
case 'verdict':
|
||||
try:
|
||||
# vote verdict
|
||||
if self.Admin.get_Admin(fromuser) is None:
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f'Your are not allowed to execute this command')
|
||||
return None
|
||||
|
||||
for chan in self.VOTE_CHANNEL_DB:
|
||||
if chan.channel_name == channel:
|
||||
target_user = self.User.get_nickname(chan.target_user)
|
||||
if chan.vote_for > chan.vote_against:
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["nogc"]} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll be kicked from the channel')
|
||||
self.Irc.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}")
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll be kicked from the channel",
|
||||
channel=channel
|
||||
)
|
||||
self.Protocol.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}")
|
||||
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.Protocol.send_priv_msg(
|
||||
nick_from=dnickname,
|
||||
msg=f"User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel",
|
||||
channel=channel
|
||||
)
|
||||
|
||||
# Init the system
|
||||
if self.init_vote_system(channel):
|
||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :System vote re initiated')
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=dnickname,
|
||||
msg="System vote re initiated",
|
||||
channel=channel
|
||||
)
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}')
|
||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}')
|
||||
|
||||
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')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote activate #channel')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote deactivate #channel')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote +')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote -')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote cancel')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote status')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote submit nickname')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote verdict')
|
||||
@@ -1,3 +1,9 @@
|
||||
{
|
||||
"version": "5.2.2"
|
||||
"version": "6.0.0",
|
||||
|
||||
"requests": "2.32.3",
|
||||
"psutil": "6.0.0",
|
||||
"unrealircd_rpc_py": "1.0.6",
|
||||
"sqlalchemy": "2.0.35",
|
||||
"faker": "30.1.0"
|
||||
}
|
||||
Reference in New Issue
Block a user