74 Commits

Author SHA1 Message Date
adator
860e265979 Latest release for version 5 2024-11-01 14:05:26 +01:00
adator
f7c80d190e V5.3.9 2024-10-13 22:21:01 +02:00
adator
9bfe5925f8 update websocket connection 2024-10-06 21:57:37 +02:00
adator
c3187e81dd V5.3.8 2024-10-06 21:46:16 +02:00
adator
f0c0a2d06a check packages versions just after windows check 2024-10-04 01:07:37 +02:00
adator
2be6ece27f Exclude windows from update packages 2024-10-04 01:03:36 +02:00
adator
6801c981ab V5.3.7 2024-10-04 00:54:31 +02:00
adator
fd88df1017 Exec pip from virtual env 2024-10-04 00:22:50 +02:00
adator
ad5b7ffbf2 Check if virtual env is available 2024-10-04 00:08:14 +02:00
adator
2b7f1d8bf3 V5.3.7 Defender can update packages 2024-10-03 23:56:01 +02:00
adator
f5ff9259e8 V5.3.6 2024-10-02 23:38:42 +02:00
adator
12c7e5e832 V5.3.5:
- core/irc.py : Nothing special except
        a notice sent to the user that the command
        is not available
    - mod_clone:
        When the user unload the module, the
        server will unset modes of #clone channel
    - mod_defender:
        * adding a try block to catch errors
        * adding a mode block to check if
        the jail channel is not having +b mode
        * ensure defender is +o in jail channel
    - mod_test:
        * adding some try block to catch errors
2024-10-02 00:01:13 +02:00
adator
3cdee5fddf V5.3.5 2024-10-01 01:17:00 +02:00
adator
f2b5c48fc9 You need to have unrealirc_rpc_py v1.0.3 2024-09-30 01:40:01 +02:00
adator
e2a1ec5866 V5.3.4 2024-09-29 22:42:28 +02:00
adator
cc53eae875 Adding more commands (V5.3.3) 2024-09-26 01:00:06 +02:00
adator
8a80840a6a V5.3.2 additional commands 2024-09-25 00:21:44 +02:00
adator
e25baea0ef unsubscribe before unload 2024-09-22 23:53:56 +02:00
adator
ee02566343 V5.3.1 2024-09-22 23:20:12 +02:00
adator
11eedbb191 Include mod_jsonrpc 2024-09-22 22:23:13 +02:00
adator
7e5e2d4643 Update the way to clean exceptions and bans 2024-09-22 16:45:21 +02:00
adator
7422bcad45 Send the ready msg to channel log 2024-09-22 16:33:21 +02:00
adator
0cf1262d31 Fix ConfModel error 2024-09-22 16:31:00 +02:00
adator
ff603ab2a4 V5.3.0 2024-09-22 16:20:02 +02:00
adator
d7503768b6 Update info command 2024-09-21 20:28:02 +02:00
adator
b5503d23d7 V5.2.9 2024-09-21 20:22:09 +02:00
adator
7be3f51bf4 V5.2.9 2024-09-21 16:28:50 +02:00
adator
2c0510b2a3 finetune clone connection 2024-09-20 23:12:57 +02:00
adator
ee039322d4 V5.2.7 2024-09-19 21:06:07 +02:00
adator
8f08a1e77f update readme.md 2024-09-18 20:29:33 +02:00
adator
c59dd16e87 V5.2.6 2024-09-18 20:21:44 +02:00
adator
0f31e67be6 check python version fix 2024-09-18 19:10:15 +02:00
adator
3cd2077f63 update installation module 2024-09-18 19:08:08 +02:00
adator
9c78ad0860 Same version, copyright changed 2024-09-17 01:46:09 +02:00
adator
487f9a2762 V5.2.5 2024-09-17 01:43:24 +02:00
adator
a7de16f7ad V5.2.4 multiple changes related to clones 2024-09-17 00:14:53 +02:00
adator
c1c0b480ce V5.2.3 If missing MOTD 2024-09-16 21:14:04 +02:00
adator
66ea492593 removing old code 2024-09-16 00:57:27 +02:00
adator
d459fd662f Changing hostname modification 2024-09-16 00:41:43 +02:00
adator
5d3a2b0e64 Changing Proxy_scan to PSUTIL_scan 2024-09-15 23:32:44 +02:00
adator
2f681db2d7 Adding Geoip to the UserModel 2024-09-15 23:29:32 +02:00
adator
7585db4f62 V5.2.2 2024-09-15 22:39:34 +02:00
adator
1984511db8 V5.2.1 2024-09-15 03:08:49 +02:00
adator
ce47739a93 update mode clone 2024-09-15 02:49:42 +02:00
adator
c7047ec3d6 V5.2.0 2024-09-15 02:03:38 +02:00
adator
eddba81cf0 remove mod_jsonrpc.py 2024-09-14 01:32:16 +02:00
adator
59e634951f V5.1.9 2024-09-14 01:25:13 +02:00
adator
37684eaede With unrealircd json rpc 2024-09-13 23:51:13 +02:00
adator
e6156fa301 V5.1.8 2024-09-08 00:42:18 +02:00
adator
58e3ebd287 V5.1.7 2024-09-03 00:19:13 +02:00
adator
322759c5ef V5.1.6 2024-09-01 22:15:24 +02:00
adator
3ba884216f Update vote kick commands 2024-09-01 18:55:30 +02:00
adator
2ce19ee877 mod_command update 2024-09-01 17:29:33 +02:00
adator
351fd6edaf adding Say command for clones 2024-09-01 16:39:49 +02:00
adator
d0c17d69de update mod_clone module 2024-09-01 15:54:23 +02:00
adator
eb9402dd8e update readme 2024-09-01 15:34:49 +02:00
adator
35d5e7a2b5 Update readme.md 2024-09-01 15:23:48 +02:00
adator
21a825c92d Remove install.py 2024-09-01 15:18:32 +02:00
adator
3fcfa0296d . 2024-09-01 14:40:28 +02:00
adator
bcf972d08b . 2024-09-01 14:34:57 +02:00
adator
1348ead6cd remove logging library 2024-09-01 14:33:41 +02:00
adator
f6ebab4780 . 2024-09-01 14:21:25 +02:00
adator
608ec57593 . 2024-09-01 13:59:26 +02:00
adator
f392f2fb2f . 2024-09-01 13:56:55 +02:00
adator
489e1e7b0a . 2024-09-01 13:47:18 +02:00
adator
3d79270ca0 . 2024-09-01 13:42:39 +02:00
adator
e60ada4260 Fix logging instance 2024-09-01 13:41:15 +02:00
adator
ccb9f307b4 Few changes see changelog 2024-09-01 13:39:02 +02:00
adator
2fc8f2d346 Fix issue unexpected argument 2024-09-01 13:14:51 +02:00
adator
e3ada04f2a daemon_reload cmd. 2024-09-01 13:09:47 +02:00
adator
6ba0551fee v5.1.5 2024-09-01 12:51:54 +02:00
adator
6142b4257f fix Installation 2024-08-29 01:36:12 +02:00
adator
ab15cce82b Fix Bug installation 2024-08-29 01:30:10 +02:00
adator
01dcc90d63 V5.1.0 2024-08-29 01:13:55 +02:00
17 changed files with 3129 additions and 1320 deletions

2
.gitignore vendored
View File

@@ -3,5 +3,5 @@ db/
logs/ logs/
__pycache__/ __pycache__/
configuration.json configuration.json
install.log *.log
test.py test.py

255
README.md
View File

@@ -1,4 +1,9 @@
# IRC-DEFENDER # IRC-DEFENDER
![Static Badge](https://img.shields.io/badge/UnrealIRCd-6.2.2%20or%20later-green)
![Static Badge](https://img.shields.io/badge/Python3-3.10%20or%20later-green)
![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fadator85%2FIRC_DEFENDER_MODULES%2Fmain%2Fversion.json&query=version&label=Current%20Version)
![Static Badge](https://img.shields.io/badge/Maintained-Yes-green)
Defender est un service IRC basé sur la sécurité des réseaux IRC ( UnrealIRCD ) Defender est un service IRC basé sur la sécurité des réseaux IRC ( UnrealIRCD )
Il permet d'ajouter une sécurité supplémentaire pour vérifier les users connectés au réseau Il permet d'ajouter une sécurité supplémentaire pour vérifier les users connectés au réseau
en demandant aux user un code de validation. en demandant aux user un code de validation.
@@ -9,9 +14,9 @@ Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux u
Kick: Expulser un utilisateur du canal. Kick: Expulser un utilisateur du canal.
Ban: Interdire définitivement l'accès au canal. Ban: Interdire définitivement l'accès au canal.
Unban: Lever une interdiction. Unban: Lever une interdiction.
Op/Deop: Attribuer ou retirer les droits d'opérateur. Op/Deop/Opall/Deopall: Attribuer ou retirer les droits d'opérateur.
Halfop/Dehalfop: Attribuer ou retirer les droits Halfop/Dehalfop: Attribuer ou retirer les droits
Voice/Devoice: Attribuer ou retirer les droits de voix. Voice/Devoice/VoiceAll/DevoiceAll: Attribuer ou retirer les droits de voix.
Système de quarantaine: Système de quarantaine:
Mise en quarantaine: Isoler temporairement un utilisateur dans un canal privé. Mise en quarantaine: Isoler temporairement un utilisateur dans un canal privé.
@@ -25,77 +30,209 @@ Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux u
Prérequis: Prérequis:
- Système d'exploitation Linux (Windows non supporté) - Système d'exploitation Linux (Windows non supporté)
- Droits d'administrateur (root) pour l'exécution du script - Un server UnrealIRCD corréctement configuré
- Python version 3.10 ou supérieure - Python version 3.10 ou supérieure
```bash
Bash: # Bash
$ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git $ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git
- Renommer le fichier exemple_configuration.json en configuration.json # Renommer le fichier exemple_configuration.json en configuration.json
- Configurer le fichier configuration.json # Configurer le fichier configuration.json
$ sudo python3 install.py $ python3 main.py
```
Si votre configuration est bonne, votre service est censé etre connecté a votre réseau IRC Si votre configuration est bonne, votre service est censé etre connecté a votre réseau IRC
Pour Les prochains lancement de defender vous devez utiliser la commande suivante:
```bash
# Bash
$ systemctl --user [start | stop | restart | status] defender
```
# Installation manuelle: # Installation manuelle:
Bash: ```bash
$ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git # Bash
$ cd IRC_DEFENDER_MODULES $ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git
$ python3 -m venv .pyenv $ cd IRC_DEFENDER_MODULES
$ source .pyenv/bin/activate $ python3 -m venv .pyenv
- Créer un service nommé "Defender.service" pour votre service et placer le dans "/etc/systemd/system/" $ source .pyenv/bin/activate
$ sudo systemctl start Defender (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 # Configuration
```
SERVEUR (Serveur) SERVEUR (Serveur)
SERVEUR_IP: Adresse IP du serveur IRC à rejoindre. * SERVEUR_IP: Adresse IP du serveur IRC à rejoindre. (default : 127.0.0.1)
SERVEUR_HOSTNAME: Nom d'hôte du serveur IRC à rejoindre (optionnel). * SERVEUR_HOSTNAME: Nom d'hôte du serveur IRC à rejoindre (optionnel).
SERVEUR_LINK: Lien vers le serveur IRC (optionnel). * SERVEUR_LINK: Lien vers le serveur IRC (optionnel).
SERVEUR_PORT: Port de connexion au serveur IRC. * SERVEUR_PORT: Port de connexion au serveur IRC.
SERVEUR_PASSWORD: Mot de passe d'enregistrement du service sur le serveur IRC. * SERVEUR_PASSWORD: Mot de passe d'enregistrement du service sur le serveur IRC.
SERVEUR_ID: Identifiant unique du service. SERVEUR_ID: Identifiant unique du service. (default : 19Z)
SERVEUR_SSL: Active la connexion SSL sécurisée au serveur IRC (true/false). SERVEUR_SSL: Active la connexion SSL sécurisée au serveur IRC (true/false) (default : false).
SERVICE (Service)
SERVICE_NAME: Nom du service IRC.
SERVICE_NICKNAME: Surnom utilisé par le service sur le serveur IRC.
SERVICE_REALNAME: Nom réel du service affiché sur le serveur IRC.
SERVICE_USERNAME: Nom d'utilisateur utilisé par le service pour se connecter au serveur IRC.
SERVICE_HOST: Nom d'hôte du service affiché sur le serveur IRC (optionnel).
SERVICE_INFO: Description du service.
SERVICE_CHANLOG: Canal utilisé pour la journalisation des actions du service.
SERVICE_SMODES: Modes serveur appliqués aux canaux rejoints par le service.
SERVICE_CMODES: Modes de canal appliqués aux canaux rejoints par le service.
SERVICE_UMODES: Modes utilisateur appliqués au service.
SERVICE_PREFIX: Caractère utilisé comme préfixe des commandes du service.
COMPTE (Compte)
OWNER: Nom d'utilisateur possédant les droits d'administration du service.
PASSWORD: Mot de passe de l'administrateur du service.
CANAUX (Canaux)
SALON_JAIL: Canal utilisé comme prison pour les utilisateurs sanctionnés.
SALON_JAIL_MODES: Modes appliqués au canal de prison.
SALON_LIBERER: Canal utilisé pour la libération des utilisateurs sanctionnés.
API (API)
API_TIMEOUT: Durée maximale d'attente d'une réponse de l'API en secondes.
SCANNER (Scanner)
PORTS_TO_SCAN: Liste des ports à scanner pour détecter des serveurs potentiellement malveillants.
SÉCURITÉ (Sécurité)
WHITELISTED_IP: Liste d'adresses IP autorisées à contourner certaines restrictions.
GLINE_DURATION: Durée de bannissement temporaire d'un utilisateur en minutes.
DEBUG (Debug)
DEBUG_LEVEL: Niveau de verbosité des messages de debug (plus grand est le nombre, plus il y a d'informations).
COULEURS (Couleurs)
CONFIG_COLOR: Dictionnaire contenant des codes de couleurs IRC pour un meilleur affichage des messages.
SERVICE (Service)
SERVICE_NAME: Nom du service IRC. (default : Defender)
SERVICE_NICKNAME: Surnom utilisé par le service sur le serveur IRC. (default : Defender)
SERVICE_REALNAME: Nom réel du service affiché sur le serveur IRC. (default : Defender Security)
SERVICE_USERNAME: Nom d'utilisateur utilisé par le service pour se connecter au serveur IRC. (default : IRCSecurity)
SERVICE_HOST: Nom d'hôte du service affiché sur le serveur IRC (optionnel). (default : defender.local.network)
SERVICE_INFO: Description du service. (default : Defender Network IRC Service)
SERVICE_CHANLOG: Canal utilisé pour la journalisation des actions du service. (default : #services)
SERVICE_SMODES: Modes serveur appliqués aux canaux rejoints par le service. (default : +ioqBS)
SERVICE_CMODES: Modes de canal appliqués aux canaux rejoints par le service. (default : ntsOP)
SERVICE_UMODES: Modes utilisateur appliqués au service. (default : o)
SERVICE_PREFIX: Caractère utilisé comme préfixe des commandes du service. (default : !)
COMPTE (Compte)
OWNER: Nom d'utilisateur possédant les droits d'administration du service. (default : admin)
PASSWORD: Mot de passe de l'administrateur du service. (default : admin)
CANAUX (Canaux)
SALON_JAIL: Canal utilisé comme prison pour les utilisateurs sanctionnés. (default : #jail)
SALON_JAIL_MODES: Modes appliqués au canal de prison. (default : sS)
SALON_LIBERER: Canal utilisé pour la libération des utilisateurs sanctionnés. (default : #welcome)
API (API)
API_TIMEOUT: Durée maximale d'attente d'une réponse de l'API en secondes. (default : 2)
SCANNER (Scanner)
PORTS_TO_SCAN: Liste des ports à scanner pour détecter des serveurs potentiellement malveillants. (default : [])
SÉCURITÉ (Sécurité)
WHITELISTED_IP: Liste d'adresses IP autorisées à contourner certaines restrictions. (default : ['127.0.0.1'])
GLINE_DURATION: Durée de bannissement temporaire d'un utilisateur en minutes. (default : "30")
DEBUG (Debug)
DEBUG_LEVEL: Niveau de verbosité des messages de debug (plus grand est le nombre, plus il y a d'informations). (default : 20) Pour une production
```
Modification de la configuration Modification de la configuration
Vous devez modifier le fichier config.json en remplaçant les valeurs par défaut avec vos propres informations. Assurez-vous de bien lire la description de chaque paramètre pour une configuration optimale du service. Vous devez modifier le fichier configuration.json en remplaçant les valeurs par défaut avec vos propres informations. Assurez-vous de bien lire la description de chaque paramètre pour une configuration optimale du service.
Attention ## Exemple de configuration de base
```json
{
"SERVEUR_IP": "IP.DE.TON.SERVER",
"SERVEUR_HOSTNAME": "HOST.DE.TON.SERVER",
"SERVEUR_LINK": "LINK.DE.TON.SERVER",
"SERVEUR_PORT": 6901,
"SERVEUR_PASSWORD": "MOT_DE_PASS_DE_TON_LINK",
"SERVEUR_ID": "10Z",
"SERVEUR_SSL": true,
Le mot de passe de l'administrateur et le mot de passe du service doivent être modifiés pour des raisons de sécurité. "SERVICE_NAME": "defender",
Ne partagez pas vos informations de connexion au serveur IRC avec des tiers. "SERVICE_NICKNAME": "PyDefender",
"SERVICE_REALNAME": "Python Defender Security",
"SERVICE_USERNAME": "PyDefender",
"SERVICE_HOST": "HOST.DE.TON.DEFENDER",
#Extension: "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; }
}
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). Le code est modulaire et conçu pour être facilement étendu. Vous pouvez ajouter de nouvelles commandes, de nouvelles fonctionnalités (mods/mod_test.py est un exemple pour bien demarrer la création de son module).
# Contributions: # Contributions:

View File

@@ -10,12 +10,15 @@ class User:
uid: str uid: str
nickname: str nickname: str
username: str username: str
realname: str
hostname: str hostname: str
umodes: str umodes: str
vhost: str vhost: str
isWebirc: bool isWebirc: bool
isWebsocket: bool
remote_ip: str remote_ip: str
score_connexion: int score_connexion: int
geoip: str = None
connexion_datetime: datetime = field(default=datetime.now()) connexion_datetime: datetime = field(default=datetime.now())
UID_DB: list[UserModel] = [] UID_DB: list[UserModel] = []
@@ -315,7 +318,7 @@ class Channel:
for user in newChan.uids: for user in newChan.uids:
record.uids.append(user) record.uids.append(user)
# Supprimer les doublons # Supprimer les doublons
del_duplicates = list(set(record.uids)) del_duplicates = list(set(record.uids))
record.uids = del_duplicates record.uids = del_duplicates
self.log.debug(f'Updating a new UID to the channel {record}') self.log.debug(f'Updating a new UID to the channel {record}')
@@ -402,3 +405,102 @@ class Channel:
self.log.debug(f'Search {name} -- result = {Channel}') self.log.debug(f'Search {name} -- result = {Channel}')
return Channel return Channel
class Clones:
@dataclass
class CloneModel:
alive: bool
nickname: str
username: str
realname: str
channels: list
vhost: str = None
init: bool = True
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

View File

@@ -1,18 +1,27 @@
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 dataclasses import fields
from typing import Union, Literal from typing import Union, Literal
from base64 import b64decode from base64 import b64decode, b64encode
from datetime import datetime from datetime import datetime, timedelta, timezone
from sqlalchemy import create_engine, Engine, Connection, CursorResult from sqlalchemy import create_engine, Engine, Connection, CursorResult
from sqlalchemy.sql import text from sqlalchemy.sql import text
from core.loadConf import ConfigDataModel from core.loadConf import ConfigDataModel
class Base: class Base:
CORE_DB_PATH = 'core' + os.sep + 'db' + os.sep # Le dossier bases de données core
MODS_DB_PATH = 'mods' + os.sep + 'db' + os.sep # Le dossier bases de données des modules
PYTHON_MIN_VERSION = '3.10' # Version min de python
def __init__(self, Config: ConfigDataModel) -> None: def __init__(self, Config: ConfigDataModel) -> None:
self.Config = Config # Assigner l'objet de configuration self.Config = Config # Assigner l'objet de configuration
@@ -26,11 +35,10 @@ class Base:
self.lock = threading.RLock() # Création du lock self.lock = threading.RLock() # Création du lock
self.install: bool = False # Initialisation de la variable d'installation
self.engine, self.cursor = self.db_init() # Initialisation de la connexion a la base de données self.engine, self.cursor = self.db_init() # Initialisation de la connexion a la base de données
self.__create_db() # Initialisation de la base de données self.__create_db() # Initialisation de la base de données
self.db_create_first_admin() # Créer un nouvel admin si la base de données est vide
def __set_current_defender_version(self) -> None: def __set_current_defender_version(self) -> None:
"""This will put the current version of Defender """This will put the current version of Defender
located in version.json located in version.json
@@ -40,7 +48,6 @@ class Base:
with open(version_filename, 'r') as version_data: with open(version_filename, 'r') as version_data:
current_version:dict[str, str] = json.load(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 return None
@@ -73,6 +80,14 @@ class Base:
self.logs.warning(f'Github not available to fetch latest version') self.logs.warning(f'Github not available to fetch latest version')
def check_for_new_version(self, online:bool) -> bool: 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: try:
self.logs.debug(f'Checking for a new service version') self.logs.debug(f'Checking for a new service version')
@@ -111,7 +126,11 @@ class Base:
Cette fonction retourne un UNIXTIME de type 12365456 Cette fonction retourne un UNIXTIME de type 12365456
Return: Current time in seconds since the Epoch (int) 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() ) unixtime = int( time.time() )
return unixtime return unixtime
def get_datetime(self) -> str: def get_datetime(self) -> str:
@@ -200,7 +219,7 @@ class Base:
else: else:
return False return False
def db_record_module(self, user_cmd:str, module_name:str) -> None: def db_record_module(self, user_cmd:str, module_name:str, isdefault:int = 0) -> None:
"""Enregistre les modules dans la base de données """Enregistre les modules dans la base de données
Args: Args:
@@ -210,18 +229,31 @@ class Base:
if not self.db_isModuleExist(module_name): if not self.db_isModuleExist(module_name):
self.logs.debug(f"Le module {module_name} n'existe pas alors ont le créer") self.logs.debug(f"Le module {module_name} n'existe pas alors ont le créer")
insert_cmd_query = f"INSERT INTO {self.Config.table_module} (datetime, user, module_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': 0} mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'module_name': module_name, 'isdefault': isdefault}
self.db_execute_query(insert_cmd_query, mes_donnees) self.db_execute_query(insert_cmd_query, mes_donnees)
else: else:
self.logs.debug(f"Le module {module_name} existe déja dans la base de données") self.logs.debug(f"Le module {module_name} existe déja dans la base de données")
return False return 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: def db_delete_module(self, module_name:str) -> None:
"""Supprime les modules de la base de données """Supprime les modules de la base de données
Args: Args:
cmd (str): le module a enregistrer cmd (str): le module a supprimer
""" """
insert_cmd_query = f"DELETE FROM {self.Config.table_module} WHERE module_name = :module_name" insert_cmd_query = f"DELETE FROM {self.Config.table_module} WHERE module_name = :module_name"
mes_donnees = {'module_name': module_name} mes_donnees = {'module_name': module_name}
@@ -310,7 +342,7 @@ class Base:
def db_update_core_config(self, module_name:str, dataclassObj: object, param_key:str, param_value: str) -> bool: def db_update_core_config(self, module_name:str, dataclassObj: object, param_key:str, param_value: str) -> bool:
core_table = 'core_config' core_table = self.Config.table_config
# Check if the param exist # Check if the param exist
if not hasattr(dataclassObj, param_key): if not hasattr(dataclassObj, param_key):
self.logs.error(f"Le parametre {param_key} n'existe pas dans la variable global") self.logs.error(f"Le parametre {param_key} n'existe pas dans la variable global")
@@ -333,6 +365,10 @@ class Base:
if updated_rows > 0: if updated_rows > 0:
setattr(dataclassObj, param_key, self.int_if_possible(param_value)) setattr(dataclassObj, param_key, self.int_if_possible(param_value))
self.logs.debug(f'Parameter updated : {param_key} - {param_value} | Module: {module_name}') self.logs.debug(f'Parameter updated : {param_key} - {param_value} | Module: {module_name}')
else:
self.logs.error(f'Parameter NOT updated : {param_key} - {param_value} | Module: {module_name}')
else:
self.logs.error(f'Parameter and Module do not exist: Param ({param_key}) - Value ({param_value}) | Module ({module_name})')
self.logs.debug(dataclassObj) self.logs.debug(dataclassObj)
@@ -428,7 +464,7 @@ class Base:
except AssertionError as ae: except AssertionError as ae:
self.logs.error(f'Assertion Error -> {ae}') self.logs.error(f'Assertion Error -> {ae}')
def create_thread(self, func:object, func_args: tuple = (), run_once:bool = False) -> None: def create_thread(self, func:object, func_args: tuple = (), run_once:bool = False, daemon: bool = True) -> None:
"""Create a new thread and store it into running_threads variable """Create a new thread and store it into running_threads variable
Args: Args:
@@ -444,7 +480,7 @@ class Base:
if thread.getName() == func_name: if thread.getName() == func_name:
return None return None
th = threading.Thread(target=func, args=func_args, name=str(func_name), daemon=True) th = threading.Thread(target=func, args=func_args, name=str(func_name), daemon=daemon)
th.start() th.start()
self.running_threads.append(th) self.running_threads.append(th)
@@ -532,6 +568,7 @@ class Base:
full_path_db = self.Config.db_path + self.Config.db_name full_path_db = self.Config.db_path + self.Config.db_name
if not os.path.exists(db_directory): if not os.path.exists(db_directory):
self.install = True
os.makedirs(db_directory) os.makedirs(db_directory)
engine = create_engine(f'sqlite:///{full_path_db}.db', echo=False) engine = create_engine(f'sqlite:///{full_path_db}.db', echo=False)
@@ -574,7 +611,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, id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT, datetime TEXT,
module_name TEXT, module_name TEXT,
@@ -600,6 +637,11 @@ class Base:
self.db_execute_query(table_core_channel) self.db_execute_query(table_core_channel)
self.db_execute_query(table_core_config) self.db_execute_query(table_core_config)
if self.install:
self.db_record_module('sys', 'mod_command', 1)
self.db_record_module('sys', 'mod_defender', 1)
self.install = False
return None return None
def db_execute_query(self, query:str, params:dict = {}) -> CursorResult: def db_execute_query(self, query:str, params:dict = {}) -> CursorResult:
@@ -675,6 +717,17 @@ class Base:
self.logs.critical(f'This remote ip is not valid : {ve}') self.logs.critical(f'This remote ip is not valid : {ve}')
return None return None
# def encode_ip(self, remote_ip_address: str) -> Union[str, None]:
# binary_ip = b64encode()
# try:
# decoded_ip = ipaddress.ip_address(binary_ip)
# return decoded_ip.exploded
# except ValueError as ve:
# self.logs.critical(f'This remote ip is not valid : {ve}')
# return None
def get_random(self, lenght:int) -> str: def get_random(self, lenght:int) -> str:
""" """
Retourn une chaîne aléatoire en fonction de la longueur spécifiée. Retourn une chaîne aléatoire en fonction de la longueur spécifiée.
@@ -742,4 +795,4 @@ class Base:
except TypeError as te: except TypeError as te:
self.logs.error(f'TypeError: [{channelToCheck}] - {te}') self.logs.error(f'TypeError: [{channelToCheck}] - {te}')
except Exception as err: except Exception as err:
self.logs.error(f'TypeError: {err}') self.logs.error(f'Error Not defined: {err}')

View File

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

View File

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

View File

@@ -1,18 +1,19 @@
from importlib.util import find_spec
from dataclasses import dataclass
from pathlib import Path
from subprocess import check_call, run, CalledProcessError, PIPE
from platform import python_version, python_version_tuple
from sys import exit
import os 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: class Install:
@dataclass @dataclass
class CoreConfig: class CoreConfig:
install_log_file: str
unix_systemd_folder: str unix_systemd_folder: str
service_file_name: str service_file_name: str
service_cmd_executable: list service_cmd_executable: list
service_cmd_daemon_reload: list
defender_main_executable: str defender_main_executable: str
python_min_version: str python_min_version: str
python_current_version_tuple: tuple[str, str, str] python_current_version_tuple: tuple[str, str, str]
@@ -24,27 +25,37 @@ class Install:
venv_pip_executable: str venv_pip_executable: str
venv_python_executable: str venv_python_executable: str
@dataclass
class Package:
name: str = None
version: str = None
DB_PACKAGES: list[Package] = []
def __init__(self) -> None: def __init__(self) -> None:
self.set_configuration() self.set_configuration()
if not self.check_python_version(): if self.skip_install:
# Tester si c'est la bonne version de python self.check_packages_version()
exit("Python Version Error") return None
else:
# Sinon tester les dependances python et les installer avec pip
if self.do_install():
self.install_dependencies() self.check_packages_version()
self.create_service_file() # Sinon tester les dependances python et les installer avec pip
if self.do_install():
self.print_final_message() self.install_dependencies()
self.create_service_file()
self.print_final_message()
return None return None
def set_configuration(self): def set_configuration(self):
self.skip_install = False
defender_install_folder = os.getcwd() defender_install_folder = os.getcwd()
venv_folder = '.pyenv' venv_folder = '.pyenv'
unix_user_home_directory = os.path.expanduser("~") unix_user_home_directory = os.path.expanduser("~")
@@ -52,9 +63,11 @@ class Install:
defender_main_executable = os.path.join(defender_install_folder, 'main.py') defender_main_executable = os.path.join(defender_install_folder, 'main.py')
self.config = self.CoreConfig( self.config = self.CoreConfig(
install_log_file='install.log',
unix_systemd_folder=unix_systemd_folder, unix_systemd_folder=unix_systemd_folder,
service_file_name='defender.service', service_file_name='defender.service',
service_cmd_executable=['systemctl', '--user', 'start', 'defender'], service_cmd_executable=['systemctl', '--user', 'start', 'defender'],
service_cmd_daemon_reload=['systemctl', '--user', 'daemon-reload'],
defender_main_executable=defender_main_executable, defender_main_executable=defender_main_executable,
python_min_version='3.10', python_min_version='3.10',
python_current_version_tuple=python_version_tuple(), python_current_version_tuple=python_version_tuple(),
@@ -62,11 +75,42 @@ class Install:
defender_install_folder=defender_install_folder, defender_install_folder=defender_install_folder,
venv_folder=venv_folder, venv_folder=venv_folder,
venv_cmd_installation=['python3', '-m', 'venv', 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_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' venv_python_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}python'
) )
if not self.check_python_version():
# If the Python version is not good then Exit
exit("/!\\ Python version error /!\\")
if not os.path.exists(os.path.join(self.config.defender_install_folder, 'core', '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':
# If windows, modify pip and python virtual environment executable
self.config.venv_pip_executable = f'{os.path.join(defender_install_folder, venv_folder, "Scripts")}{os.sep}pip.exe'
self.config.venv_python_executable = f'{os.path.join(defender_install_folder, venv_folder, "Scripts")}{os.sep}python.exe'
self.skip_install = True
return False
if self.is_root():
exit(f'/!\\ I highly not recommend running Defender as root /!\\')
self.skip_install = True
return False
def is_root(self) -> bool:
if os.geteuid() != 0:
print('> User without privileges ==> OK')
return False
elif os.geteuid() == 0:
print('/!\\ Do not use root to install Defender /!\\')
exit("Do not use root to install Defender")
return True
def do_install(self) -> bool: def do_install(self) -> bool:
full_service_file_path = os.path.join(self.config.unix_systemd_folder, self.config.service_file_name) full_service_file_path = os.path.join(self.config.unix_systemd_folder, self.config.service_file_name)
@@ -83,15 +127,84 @@ class Install:
def run_subprocess(self, command:list) -> None: def run_subprocess(self, command:list) -> None:
print(command) print(f'> {command}')
try: try:
check_call(command) check_call(command)
print("La commande s'est terminée avec succès.") print("The command completed successfully.")
except CalledProcessError as e: except CalledProcessError as e:
print(f"La commande a échoué avec le code de retour :{e.returncode}") print(f"The command failed with the return code: {e.returncode}")
print(f"Try to install dependencies ...") print(f"Try to install dependencies ...")
exit(5) 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: def check_python_version(self) -> bool:
"""Test si la version de python est autorisée ou non """Test si la version de python est autorisée ou non
@@ -106,14 +219,14 @@ class Install:
min_major, min_minor = tuple((python_required_version[0], python_required_version[1])) min_major, min_minor = tuple((python_required_version[0], python_required_version[1]))
if int(sys_major) < int(min_major): if int(sys_major) < int(min_major):
print(f"## Your python version must be greather than or equal to {self.config.python_current_version} ##") print(f"## Your python version must be greather than or equal to {self.config.python_min_version} ##")
return False return False
elif (int(sys_major) <= int(min_major)) and (int(sys_minor) < int(min_minor)): elif (int(sys_major) <= int(min_major)) and (int(sys_minor) < int(min_minor)):
print(f"## Your python version must be greather than or equal to {self.config.python_current_version} ##") print(f"## Your python version must be greather than or equal to {self.config.python_min_version} ##")
return False return False
print(f"===> Version of python : {self.config.python_current_version} ==> OK") print(f"> Version of python : {self.config.python_current_version} ==> OK")
return True return True
@@ -123,7 +236,8 @@ class Install:
# Run a command in the virtual environment's Python to check if the package is installed # Run a command in the virtual environment's Python to check if the package is installed
run([self.config.venv_python_executable, '-c', f'import {package_name}'], check=True, stdout=PIPE, stderr=PIPE) run([self.config.venv_python_executable, '-c', f'import {package_name}'], check=True, stdout=PIPE, stderr=PIPE)
return True return True
except CalledProcessError: except CalledProcessError as cpe:
print(cpe)
return False return False
def install_dependencies(self) -> None: def install_dependencies(self) -> None:
@@ -152,7 +266,7 @@ class Install:
print("===> Verifier si pip est a jour") print("===> Verifier si pip est a jour")
self.run_subprocess([self.config.venv_python_executable, '-m', 'pip', 'install', '--upgrade', 'pip']) self.run_subprocess([self.config.venv_python_executable, '-m', 'pip', 'install', '--upgrade', 'pip'])
if find_spec('greenlet') is None: if not self.check_package('greenlet'):
self.run_subprocess([self.config.venv_pip_executable, 'install', '--only-binary', ':all:', 'greenlet']) self.run_subprocess([self.config.venv_pip_executable, 'install', '--only-binary', ':all:', 'greenlet'])
print('====> Module Greenlet installé') print('====> Module Greenlet installé')
@@ -170,13 +284,10 @@ class Install:
if os.path.exists(full_service_file_path): if os.path.exists(full_service_file_path):
print(f'/!\\ Service file already exist /!\\') print(f'/!\\ Service file already exist /!\\')
self.run_subprocess(self.config.service_cmd_executable)
return None return None
# Check if user systemd is available (.config/systemd/user/) contain = f'''[Unit]
if not os.path.exists(self.config.unix_systemd_folder):
self.run_subprocess(['mkdir', '-p', self.config.unix_systemd_folder])
contain = f'''[Unit]
Description=Defender IRC Service Description=Defender IRC Service
[Service] [Service]
@@ -186,20 +297,34 @@ SyslogIdentifier=Defender
Restart=on-failure Restart=on-failure
[Install] [Install]
WantedBy=multi-user.target WantedBy=default.target
''' '''
# Check if user systemd is available (.config/systemd/user/)
if not os.path.exists(self.config.unix_systemd_folder):
self.run_subprocess(['mkdir', '-p', self.config.unix_systemd_folder])
with open(full_service_file_path, 'w+') as servicefile: with open(full_service_file_path, 'w+') as servicefile:
servicefile.write(contain) servicefile.write(contain)
servicefile.close() servicefile.close()
print(f'Service file generated with current configuration') print(f'Service file generated with current configuration')
print(f'Running Defender IRC Service ...') print(f'Running Defender IRC Service ...')
self.run_subprocess(self.config.service_cmd_daemon_reload)
self.run_subprocess(self.config.service_cmd_executable)
else:
with open(full_service_file_path, 'w+') as servicefile:
servicefile.write(contain)
servicefile.close()
print(f'Service file generated with current configuration')
print(f'Running Defender IRC Service ...')
self.run_subprocess(self.config.service_cmd_daemon_reload)
self.run_subprocess(self.config.service_cmd_executable) self.run_subprocess(self.config.service_cmd_executable)
def print_final_message(self) -> None: def print_final_message(self) -> None:
print(f"#"*24) print(f"#"*24)
print("Installation complete ...") print("Installation complete ...")
print("You must change environment using the command below") print("If the configuration is correct, then you must see your service connected to your irc server")
print(f"source {self.config.defender_install_folder}{os.sep}{self.config.venv_folder}{os.sep}bin{os.sep}activate") print(f"If any issue, you can see the log file for debug {self.config.defender_install_folder}{os.sep}logs{os.sep}defender.log")
print(f"#"*24) print(f"#"*24)
exit(1) exit(1)

File diff suppressed because it is too large Load Diff

View File

@@ -1,68 +1,181 @@
import json, sys import sys
import json
from os import sep from os import sep
from typing import Union from typing import Union, Literal
from dataclasses import dataclass, field from dataclasses import dataclass, field
########################################## ##########################################
# CONFIGURATION FILE # # CONFIGURATION FILE #
########################################## ##########################################
@dataclass
class ColorModel:
white: str = "\x0300"
black: str = "\x0301"
blue: str = "\x0302"
green: str = "\x0303"
red: str = "\x0304"
yellow: str = "\x0306"
bold: str = "\x02"
nogc: str = "\x03"
@dataclass @dataclass
class ConfigDataModel: class ConfigDataModel:
SERVEUR_IP: str SERVEUR_IP: str
SERVEUR_HOSTNAME: str # Le hostname du serveur IRC """Server public IP (could be 127.0.0.1 localhost)"""
SERVEUR_LINK: str # Host attendu par votre IRCd (ex. dans votre link block pour Unrealircd)
SERVEUR_PORT: int # Port du link
SERVEUR_PASSWORD: str # Mot de passe du link (Privilégiez argon2 sur Unrealircd)
SERVEUR_ID: str # SID (identification) du bot en tant que Services
SERVEUR_SSL: bool # Activer la connexion SSL
SERVICE_NAME: str # Le nom du service SERVEUR_HOSTNAME: str
SERVICE_NICKNAME: str # Nick du bot sur IRC """IRC Server Hostname (your.hostname.extension)"""
SERVICE_REALNAME: str # Realname du bot
SERVICE_USERNAME: str # Ident du bot
SERVICE_HOST: str # Host du bot
SERVICE_INFO: str # swhois du bot
SERVICE_CHANLOG: str # Salon des logs et autres messages issus du bot
SERVICE_SMODES: str # Mode du service
SERVICE_CMODES: str # Mode du salon (#ChanLog) que le bot appliquera à son entrée
SERVICE_UMODES: str # Mode que le bot pourra se donner à sa connexion au salon chanlog
SERVICE_PREFIX: str # Prefix pour envoyer les commandes au bot
SERVICE_ID: str = field(init=False) # L'identifiant du service
OWNER: str # Identifiant du compte admin SERVEUR_LINK: str
PASSWORD: str # Mot de passe du compte admin """The link hostname (should be the same as your unrealircd link block)"""
SALON_JAIL: str # Salon pot de miel SERVEUR_PORT: int
SALON_JAIL_MODES: str # Mode du salon pot de miel """Server port as configured in your unrealircd link block"""
SALON_LIBERER: str # Le salon ou sera envoyé l'utilisateur clean
API_TIMEOUT: int # Timeout des api's SERVEUR_PASSWORD: str
"""Your link password"""
PORTS_TO_SCAN: list # Liste des ports a scanné pour une detection de proxy SERVEUR_ID: str
WHITELISTED_IP: list # IP a ne pas scanner """Service identification could be Z01 should be unique"""
GLINE_DURATION: str # La durée du gline
DEBUG_LEVEL: int # Le niveau des logs DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50 SERVEUR_SSL: bool
"""Activate SSL connexion"""
CONFIG_COLOR: dict[str, str] 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"""
JSONRPC_URL: str
"""The RPC url, if local https://127.0.0.1:PORT/api should be fine"""
JSONRPC_PATH_TO_SOCKET_FILE: str
"""The full path of the socket file (/PATH/TO/YOUR/UNREALIRCD/SOCKET/FILE.socket)"""
JSONRPC_METHOD: str
"""3 methods are available; requests/socket/unixsocket"""
JSONRPC_USER: str
"""The RPC User defined in your unrealircd.conf"""
JSONRPC_PASSWORD: str
"""The RPC Password defined in your unrealircd.conf"""
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"""
CLONE_CHANNEL: str
"""Channel where clones are hosted and will log PRIVMSG"""
CLONE_CMODES: str
"""Clone channel modes"""
CLONE_LOG_HOST_EXEMPT: list[str]
"""Hosts that clones will not log"""
CLONE_CHANNEL_PASSWORD: str
"""Clone password channel"""
API_TIMEOUT: int
"""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"""
table_admin: str table_admin: str
"""Admin table"""
table_commande: str table_commande: str
"""Core command table"""
table_log: str table_log: str
"""Core log table"""
table_module: str table_module: str
"""Core module table"""
table_config: str table_config: str
"""Core configuration table"""
table_channel: str table_channel: str
"""Core channel table"""
current_version: str current_version: str
"""Current version of Defender"""
latest_version: str latest_version: str
"""The Latest version fetched from github"""
db_name: str db_name: str
"""The database name"""
db_path: str db_path: str
"""The database path"""
COLORS: ColorModel = field(default_factory=ColorModel)
"""Available colors in Defender"""
BATCH_SIZE: int = 400
"""The batch size used for privmsg and notice"""
def __post_init__(self): def __post_init__(self):
# Initialiser SERVICE_ID après la création de l'objet # Initialiser SERVICE_ID après la création de l'objet
self.SERVICE_ID:str = f"{self.SERVEUR_ID}AAAAAB" self.SERVICE_ID:str = f"{self.SERVEUR_ID}AAAAAB"
"""The service ID which is SERVEUR_ID and AAAAAB"""
class Config: class Config:
@@ -77,49 +190,61 @@ class Config:
with open(conf_filename, 'r') as configuration_data: with open(conf_filename, 'r') as configuration_data:
configuration:dict[str, Union[str, int, list, dict]] = json.load(configuration_data) configuration:dict[str, Union[str, int, list, dict]] = json.load(configuration_data)
for key, value in configuration['CONFIG_COLOR'].items():
configuration['CONFIG_COLOR'][key] = str(value).encode('utf-8').decode('unicode_escape')
return configuration return configuration
except FileNotFoundError as fe: except FileNotFoundError as fe:
print(f'FileNotFound: {fe}') print(f'FileNotFound: {fe}')
print('Configuration file not found please create core/configuration.json') print('Configuration file not found please create core/configuration.json')
sys.exit(0) sys.exit(0)
except KeyError as ke:
print(f'Key Error: {ke}')
print('The key must be defined in core/configuration.json')
def __load_service_configuration(self) -> ConfigDataModel: def __load_service_configuration(self) -> ConfigDataModel:
import_config = self.__load_json_service_configuration() import_config = self.__load_json_service_configuration()
ConfigObject: ConfigDataModel = ConfigDataModel( ConfigObject: ConfigDataModel = ConfigDataModel(
SERVEUR_IP=import_config["SERVEUR_IP"], SERVEUR_IP=import_config["SERVEUR_IP"] if "SERVEUR_IP" in import_config else '127.0.0.1',
SERVEUR_HOSTNAME=import_config["SERVEUR_HOSTNAME"], SERVEUR_HOSTNAME=import_config["SERVEUR_HOSTNAME"] if "SERVEUR_HOSTNAME" in import_config else None,
SERVEUR_LINK=import_config["SERVEUR_LINK"], SERVEUR_LINK=import_config["SERVEUR_LINK"] if "SERVEUR_LINK" in import_config else None,
SERVEUR_PORT=import_config["SERVEUR_PORT"], SERVEUR_PORT=import_config["SERVEUR_PORT"] if "SERVEUR_PORT" in import_config else 6667,
SERVEUR_PASSWORD=import_config["SERVEUR_PASSWORD"], SERVEUR_PASSWORD=import_config["SERVEUR_PASSWORD"] if "SERVEUR_PASSWORD" in import_config else None,
SERVEUR_ID=import_config["SERVEUR_ID"], SERVEUR_ID=import_config["SERVEUR_ID"] if "SERVEUR_ID" in import_config else '19Z',
SERVEUR_SSL=import_config["SERVEUR_SSL"], SERVEUR_SSL=import_config["SERVEUR_SSL"] if "SERVEUR_SSL" in import_config else False,
SERVICE_NAME=import_config["SERVICE_NAME"], SERVICE_NAME=import_config["SERVICE_NAME"] if "SERVICE_NAME" in import_config else 'Defender',
SERVICE_NICKNAME=import_config["SERVICE_NICKNAME"], SERVICE_NICKNAME=import_config["SERVICE_NICKNAME"] if "SERVICE_NICKNAME" in import_config else 'Defender',
SERVICE_REALNAME=import_config["SERVICE_REALNAME"], SERVICE_REALNAME=import_config["SERVICE_REALNAME"] if "SERVICE_REALNAME" in import_config else 'Defender Security',
SERVICE_USERNAME=import_config["SERVICE_USERNAME"], SERVICE_USERNAME=import_config["SERVICE_USERNAME"] if "SERVICE_USERNAME" in import_config else 'IRCSecurity',
SERVICE_HOST=import_config["SERVICE_HOST"], SERVICE_HOST=import_config["SERVICE_HOST"] if "SERVICE_HOST" in import_config else 'defender.local.network',
SERVICE_INFO=import_config["SERVICE_INFO"], SERVICE_INFO=import_config["SERVICE_INFO"] if "SERVICE_INFO" in import_config else 'Defender Network IRC Service',
SERVICE_CHANLOG=import_config["SERVICE_CHANLOG"], SERVICE_CHANLOG=import_config["SERVICE_CHANLOG"] if "SERVICE_CHANLOG" in import_config else '#services',
SERVICE_SMODES=import_config["SERVICE_SMODES"], SERVICE_SMODES=import_config["SERVICE_SMODES"] if "SERVICE_SMODES" in import_config else '+ioqBS',
SERVICE_CMODES=import_config["SERVICE_CMODES"], SERVICE_CMODES=import_config["SERVICE_CMODES"] if "SERVICE_CMODES" in import_config else 'ntsOP',
SERVICE_UMODES=import_config["SERVICE_UMODES"], SERVICE_UMODES=import_config["SERVICE_UMODES"] if "SERVICE_UMODES" in import_config else 'o',
SERVICE_PREFIX=import_config["SERVICE_PREFIX"], SERVICE_PREFIX=import_config["SERVICE_PREFIX"] if "SERVICE_PREFIX" in import_config else '!',
OWNER=import_config["OWNER"], OWNER=import_config["OWNER"] if "OWNER" in import_config else 'admin',
PASSWORD=import_config["PASSWORD"], PASSWORD=import_config["PASSWORD"] if "PASSWORD" in import_config else 'admin',
SALON_JAIL=import_config["SALON_JAIL"],
SALON_JAIL_MODES=import_config["SALON_JAIL_MODES"], JSONRPC_METHOD=import_config["JSONRPC_METHOD"] if "JSONRPC_METHOD" in import_config else 'socket',
SALON_LIBERER=import_config["SALON_LIBERER"], JSONRPC_URL=import_config["JSONRPC_URL"] if "JSONRPC_URL" in import_config else None,
API_TIMEOUT=import_config["API_TIMEOUT"], JSONRPC_PATH_TO_SOCKET_FILE=import_config["JSONRPC_PATH_TO_SOCKET_FILE"] if "JSONRPC_PATH_TO_SOCKET_FILE" in import_config else None,
PORTS_TO_SCAN=import_config["PORTS_TO_SCAN"], JSONRPC_USER=import_config["JSONRPC_USER"] if "JSONRPC_USER" in import_config else None,
WHITELISTED_IP=import_config["WHITELISTED_IP"], JSONRPC_PASSWORD=import_config["JSONRPC_PASSWORD"] if "JSONRPC_PASSWORD" in import_config else None,
GLINE_DURATION=import_config["GLINE_DURATION"],
DEBUG_LEVEL=import_config["DEBUG_LEVEL"], SALON_JAIL=import_config["SALON_JAIL"] if "SALON_JAIL" in import_config else '#jail',
CONFIG_COLOR=import_config["CONFIG_COLOR"], SALON_JAIL_MODES=import_config["SALON_JAIL_MODES"] if "SALON_JAIL_MODES" in import_config else 'sS',
SALON_LIBERER=import_config["SALON_LIBERER"] if "SALON_LIBERER" in import_config else '#welcome',
CLONE_CHANNEL=import_config["CLONE_CHANNEL"] if "CLONE_CHANNEL" in import_config else '#clones',
CLONE_CMODES=import_config["CLONE_CMODES"] if "CLONE_CMODES" in import_config else '+nts',
CLONE_LOG_HOST_EXEMPT=import_config["CLONE_LOG_HOST_EXEMPT"] if "CLONE_LOG_HOST_EXEMPT" in import_config else [],
CLONE_CHANNEL_PASSWORD=import_config["CLONE_CHANNEL_PASSWORD"] if "CLONE_CHANNEL_PASSWORD" in import_config else "clone_Password_1234",
API_TIMEOUT=import_config["API_TIMEOUT"] if "API_TIMEOUT" in import_config else 2,
PORTS_TO_SCAN=import_config["PORTS_TO_SCAN"] if "PORTS_TO_SCAN" in import_config else [],
WHITELISTED_IP=import_config["WHITELISTED_IP"] if "WHITELISTED_IP" in import_config else ['127.0.0.1'],
GLINE_DURATION=import_config["GLINE_DURATION"] if "GLINE_DURATION" in import_config else '30',
DEBUG_LEVEL=import_config["DEBUG_LEVEL"] if "DEBUG_LEVEL" in import_config else 20,
table_admin='core_admin', table_admin='core_admin',
table_commande='core_command', table_commande='core_command',
table_log='core_log', table_log='core_log',

View File

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

View File

@@ -1,5 +1,5 @@
from dataclasses import dataclass, fields, field from dataclasses import dataclass, fields, field
import random, faker, time import random, faker, time, logging
from datetime import datetime from datetime import datetime
from typing import Union from typing import Union
from core.irc import Irc from core.irc import Irc
@@ -9,7 +9,6 @@ class Clone():
@dataclass @dataclass
class ModConfModel: class ModConfModel:
clone_count: int
clone_nicknames: list[str] clone_nicknames: list[str]
def __init__(self, ircInstance:Irc) -> None: def __init__(self, ircInstance:Irc) -> None:
@@ -35,9 +34,11 @@ class Clone():
# Add Channel object to the module (Mandatory) # Add Channel object to the module (Mandatory)
self.Channel = ircInstance.Channel self.Channel = ircInstance.Channel
self.Clone = ircInstance.Clones
# Créer les nouvelles commandes du module # Créer les nouvelles commandes du module
self.commands_level = { self.commands_level = {
1: ['clone_connect', 'clone_join', 'clone_kill', 'clone_list'] 1: ['clone']
} }
# Init the module (Mandatory) # Init the module (Mandatory)
@@ -57,6 +58,12 @@ class Clone():
# Load module configuration (Mandatory) # Load module configuration (Mandatory)
self.__load_module_configuration() self.__load_module_configuration()
self.Base.db_query_channel(action='add', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} JOIN {self.Config.CLONE_CHANNEL}")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} SAMODE {self.Config.CLONE_CHANNEL} +o {self.Config.SERVICE_NICKNAME}")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +nts")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +k {self.Config.CLONE_CHANNEL_PASSWORD}")
def __set_commands(self, commands:dict[int, list[str]]) -> None: def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal """### Rajoute les commandes du module au programme principal
@@ -97,15 +104,13 @@ class Clone():
"""### Load Module Configuration """### Load Module Configuration
""" """
try: try:
# Variable qui va contenir les options de configuration du module Defender
# Variable qui va contenir les options de configuration du module Defender # Variable qui va contenir les options de configuration du module Defender
self.ModConfig = self.ModConfModel( self.ModConfig = self.ModConfModel(
clone_count=0,
clone_nicknames=[] clone_nicknames=[]
) )
# Sync the configuration with core configuration (Mandatory) # Sync the configuration with core configuration (Mandatory)
self.Base.db_sync_core_config(self.module_name, self.ModConfig) # self.Base.db_sync_core_config(self.module_name, self.ModConfig)
return None return None
@@ -121,40 +126,141 @@ class Clone():
for clone in self.ModConfig.clone_nicknames: for clone in self.ModConfig.clone_nicknames:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone} :KILL') self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone} :KILL')
self.Base.db_query_channel(action='del', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} -nts")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} -k {self.Config.CLONE_CHANNEL_PASSWORD}")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PART {self.Config.CLONE_CHANNEL}")
return None return None
def thread_create_clones(self, nickname: str, username: str, channels:list, server_port:int, ssl:bool) -> None: def thread_clone_clean_up(self, wait: float):
Connection(server_port=server_port, nickname=nickname, username=username, channels=channels, ssl=ssl) activated = True
while activated:
clone_to_kill: list[str] = []
for clone in self.Clone.UID_CLONE_DB:
if not clone.connected and clone.alive and not clone.init:
clone_to_kill.append(clone.nickname)
clone.alive = False
for clone_nickname in clone_to_kill:
if self.Clone.delete(clone_nickname):
self.Logs.debug(f'<<{clone_nickname}>> object has been deleted')
del clone_to_kill
# If no more clones then stop this thread
if not self.Clone.UID_CLONE_DB:
break
time.sleep(wait)
def thread_change_hostname(self):
fake = faker.Faker('en_GB')
for clone in self.Clone.UID_CLONE_DB:
if not clone.vhost is None:
continue
rand_1 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
rand_2 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
rand_3 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
rand_ip = ''.join(rand_1) + '.' + ''.join(rand_2) + '.' + ''.join(rand_3) + '.IP'
found = False
while not found:
if clone.connected:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} CHGHOST {clone.nickname} {rand_ip}')
found = True
clone.vhost = rand_ip
break
if not clone in self.Clone.UID_CLONE_DB:
found = True
break
def thread_create_clones_with_interval(self, number_of_clones:int, channels: list, connection_interval: float):
for i in range(number_of_clones):
nickname, username, realname = self.generate_names()
self.Base.create_thread(
self.thread_create_clones,
(nickname, username, realname, channels, 6697, True)
)
time.sleep(connection_interval)
self.Base.create_thread(
self.thread_change_hostname
)
# self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{str(number_of_clones)} clones joined the network')
self.Base.create_thread(self.thread_clone_clean_up, (5, ), run_once=True)
def thread_create_clones(self, nickname: str, username: str, realname: str, channels: list, server_port: int, ssl: bool) -> None:
Connection(server_port=server_port, nickname=nickname, username=username, realname=realname, channels=channels, CloneObject=self.Clone, ssl=ssl)
return None return None
def thread_join_channels(self, channel_name: str, wait: float, clone_name:str = None): def thread_join_channels(self, channel_name: str, wait: float, clone_name:str = None):
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Clones start to join {channel_name} with {wait} secondes frequency')
if clone_name is None: if clone_name is None:
for clone in self.ModConfig.clone_nicknames: for clone in self.Clone.UID_CLONE_DB:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone} :JOIN {channel_name}') if not channel_name in clone.channels:
time.sleep(wait) time.sleep(wait)
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}')
clone.channels.append(channel_name)
else: else:
for clone in self.ModConfig.clone_nicknames: for clone in self.Clone.UID_CLONE_DB:
if clone_name == clone: if clone_name == clone.nickname:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone} :JOIN {channel_name}') if not channel_name in clone.channels:
time.sleep(wait)
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}')
clone.channels.append(channel_name)
def generate_names(self) -> tuple[str, str]: def generate_names(self) -> tuple[str, str, str]:
try: try:
logging.getLogger('faker').setLevel(logging.CRITICAL)
fake = faker.Faker('en_GB') fake = faker.Faker('en_GB')
nickname = fake.first_name() # nickname = fake.first_name()
username = fake.last_name() # username = fake.last_name()
if not nickname in self.ModConfig.clone_nicknames: # Generate Username
self.ModConfig.clone_nicknames.append(nickname) chaine = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
new_username = fake.random_sample(chaine, 9)
username = ''.join(new_username)
# Create realname XX F|M Department
gender = fake.random_choices(['F','M'], 1)
gender = ''.join(gender)
if gender == 'F':
nickname = fake.first_name_female()
elif gender == 'M':
nickname = fake.first_name_male()
else: else:
nickname = fake.first_name()
age = random.randint(20, 60)
fake_fr = faker.Faker(['fr_FR', 'en_GB'])
department = fake_fr.department_name()
realname = f'{age} {gender} {department}'
if self.Clone.exists(nickname=nickname):
caracteres = '0123456789' caracteres = '0123456789'
randomize = ''.join(random.choice(caracteres) for _ in range(2)) randomize = ''.join(random.choice(caracteres) for _ in range(2))
nickname = nickname + str(randomize) nickname = nickname + str(randomize)
self.ModConfig.clone_nicknames.append(nickname) self.Clone.insert(
self.Clone.CloneModel(alive=True, nickname=nickname, username=username, realname=realname, channels=[])
)
else:
self.Clone.insert(
self.Clone.CloneModel(alive=True, nickname=nickname, username=username, realname=realname, channels=[])
)
return (nickname, username) return (nickname, username, realname)
except AttributeError as ae: except AttributeError as ae:
self.Logs.error(f'Attribute Error : {ae}') self.Logs.error(f'Attribute Error : {ae}')
@@ -176,67 +282,123 @@ class Clone():
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None: def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower() try:
fromuser = user command = str(cmd[0]).lower()
fromuser = user
dnickname = self.Config.SERVICE_NICKNAME # Defender nickname dnickname = self.Config.SERVICE_NICKNAME # Defender nickname
match command: match command:
case 'clone_connect': case 'clone':
# clone_connect 25
try:
number_of_clones = int(cmd[1])
for i in range(number_of_clones):
nickname, username = self.generate_names()
self.Base.create_thread(
self.thread_create_clones,
(nickname, username, [], 6697, True)
)
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{str(number_of_clones)} clones joined the network') if len(cmd) == 1:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect 6 2.5')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill [all | nickname]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join [all | nickname] #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list')
except Exception as err: option = str(cmd[1]).lower()
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_kill 6')
case 'clone_kill': match option:
try:
clone_name = str(cmd[1])
if clone_name.lower() == 'all': case 'connect':
for clone in self.ModConfig.clone_nicknames: try:
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone} :KILL') # clone connect 5
self.ModConfig.clone_nicknames.remove(clone) number_of_clones = int(cmd[2])
else: connection_interval = int(cmd[3]) if len(cmd) == 4 else 0.5
for clone in self.ModConfig.clone_nicknames: self.Base.create_thread(
if clone_name == clone: self.thread_create_clones_with_interval,
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone} :KILL') (number_of_clones, [], connection_interval)
self.ModConfig.clone_nicknames.remove(clone) )
except Exception as err: except Exception as err:
self.Logs.error(f'{err}') self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone_kill all') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect [number of clone you want to connect] [Connection Interval]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone_kill [clone_name]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} clone connect 6 2.5')
case 'clone_join': case 'kill':
try: try:
# clone_join nickname #channel # clone kill [all | nickname]
clone_name = str(cmd[1]) clone_name = str(cmd[2])
clone_channel_to_join = cmd[2] clone_to_kill: list[str] = []
if clone_name.lower() == 'all': if clone_name.lower() == 'all':
self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 4)) for clone in self.Clone.UID_CLONE_DB:
else: self.Irc.send2socket(f':{dnickname} PRIVMSG {clone.nickname} :KILL')
self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 4, clone_name)) clone_to_kill.append(clone.nickname)
clone.alive = False
except Exception as err: for clone_nickname in clone_to_kill:
self.Logs.error(f'{err}') self.Clone.delete(clone_nickname)
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone_join all #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone_join clone_nickname #channel')
case 'clone_list': del clone_to_kill
for clone_name in self.ModConfig.clone_nicknames: else:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> {clone_name}') if self.Clone.exists(clone_name):
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :KILL')
self.Clone.kill(clone_name)
self.Clone.delete(clone_name)
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill all')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill clone_nickname')
case 'join':
try:
# clone join [all | nickname] #channel
clone_name = str(cmd[2])
clone_channel_to_join = str(cmd[3])
if clone_name.lower() == 'all':
self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2))
else:
self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2, clone_name))
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join all #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join clone_nickname #channel')
case 'list':
try:
clone_count = len(self.Clone.UID_CLONE_DB)
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> Number of connected clones: {clone_count}')
for clone_name in self.Clone.UID_CLONE_DB:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> Nickname: {clone_name.nickname} | Username: {clone_name.username} | Realname: {clone_name.realname} | Vhost: {clone_name.vhost} | Init: {clone_name.init} | Live: {clone_name.alive} | Connected: {clone_name.connected}')
except Exception as err:
self.Logs.error(f'{err}')
case 'say':
try:
# clone say clone_nickname #channel message
clone_name = str(cmd[2])
clone_channel = str(cmd[3]) if self.Base.Is_Channel(str(cmd[3])) else None
message = []
for i in range(4, len(cmd)):
message.append(cmd[i])
final_message = ' '.join(message)
if clone_channel is None or not self.Clone.exists(clone_name):
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel message')
return None
if self.Clone.exists(clone_name):
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :SAY {clone_channel} {final_message}')
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel message')
case _:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect 6')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill [all | nickname]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join [all | nickname] #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel [message]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list')
except IndexError as ie:
self.Logs.error(f'Index Error: {ie}')
except Exception as err:
self.Logs.error(f'Index Error: {err}')

View File

@@ -35,7 +35,14 @@ class Command():
# Create module commands (Mandatory) # Create module commands (Mandatory)
self.commands_level = { self.commands_level = {
1: ['join', 'part'], 1: ['join', 'part'],
2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice', 'devoice', 'ban', 'unban','kick', 'kickban'] 2: ['owner', 'deowner', 'protect', 'deprotect', 'op', 'deop', 'halfop', 'dehalfop', 'voice',
'devoice', 'opall', 'deopall', 'devoiceall', 'voiceall', 'ban',
'unban','kick', 'kickban', 'umode', 'mode', 'svsjoin', 'svspart', 'svsnick', 'topic',
'wallops', 'globops','gnotice','whois', 'names', 'invite', 'inviteme',
'sajoin', 'sapart',
'kill', 'gline', 'ungline', 'kline', 'unkline', 'shun', 'unshun',
'glinelist', 'shunlist', 'klinelist'],
3: ['map']
} }
# Init the module # Init the module
@@ -56,6 +63,9 @@ class Command():
self.__load_module_configuration() self.__load_module_configuration()
# End of mandatory methods you can start your customization # # End of mandatory methods you can start your customization #
self.user_to_notice: str = ''
self.show_219: bool = True
return None return None
def __set_commands(self, commands:dict[int, list[str]]) -> None: def __set_commands(self, commands:dict[int, list[str]]) -> None:
@@ -89,7 +99,7 @@ class Command():
) )
''' '''
self.Base.db_execute_query(table_logs) # self.Base.db_execute_query(table_logs)
return None return None
def __load_module_configuration(self) -> None: def __load_module_configuration(self) -> None:
@@ -97,7 +107,7 @@ class Command():
""" """
try: try:
# Build the default configuration model (Mandatory) # Build the default configuration model (Mandatory)
self.ModConfig = self.ModConfModel(param_exemple1='param value 1', param_exemple2=1) self.ModConfig = self.ModConfModel()
# Sync the configuration with core configuration (Mandatory) # Sync the configuration with core configuration (Mandatory)
self.Base.db_sync_core_config(self.module_name, self.ModConfig) self.Base.db_sync_core_config(self.module_name, self.ModConfig)
@@ -120,45 +130,81 @@ class Command():
return None return None
def add_defender_channel(self, channel:str) -> bool:
"""Cette fonction ajoute les salons de join de Defender
Args:
channel (str): le salon à enregistrer.
"""
mes_donnees = {'channel': channel}
response = self.Base.db_execute_query("SELECT id FROM def_channels WHERE channel = :channel", mes_donnees)
isChannelExist = response.fetchone()
if isChannelExist is None:
mes_donnees = {'datetime': self.Base.get_datetime(), 'channel': channel}
insert = self.Base.db_execute_query(f"INSERT INTO def_channels (datetime, channel) VALUES (:datetime, :channel)", mes_donnees)
if insert.rowcount >=0:
return True
else:
return False
else:
return False
def delete_defender_channel(self, channel:str) -> bool:
"""Cette fonction supprime les salons de join de Defender
Args:
channel (str): le salon à enregistrer.
"""
mes_donnes = {'channel': channel}
response = self.Base.db_execute_query("DELETE FROM def_channels WHERE channel = :channel", mes_donnes)
affected_row = response.rowcount
if affected_row > 0:
return True
else:
return False
def cmd(self, data:list) -> None: def cmd(self, data:list) -> None:
service_id = self.Config.SERVICE_ID
dnickname = self.Config.SERVICE_NICKNAME
dchanlog = self.Config.SERVICE_CHANLOG
red = self.Config.COLORS.red
green = self.Config.COLORS.green
bold = self.Config.COLORS.bold
nogc = self.Config.COLORS.nogc
cmd = list(data).copy()
if len(cmd) < 2:
return None
match cmd[1]:
# [':irc.deb.biz.st', '403', 'Dev-PyDefender', '#Z', ':No', 'such', 'channel']
case '403' | '401':
try:
message = ' '.join(cmd[3:])
self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} :[{red}ERROR MSG{nogc}] {message}")
self.Base.logs.error(f"{cmd[1]} - {message}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case '006' | '018':
try:
# [':irc.deb.biz.st', '006', 'Dev-PyDefender', ':`-services.deb.biz.st', '------', '|', 'Users:', '9', '(47.37%)', '[00B]']
# [':irc.deb.biz.st', '018', 'Dev-PyDefender', ':4', 'servers', 'and', '19', 'users,', 'average', '4.75', 'users', 'per', 'server']
message = ' '.join(cmd[3:])
self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} : [{green}SERVER MSG{nogc}] {message}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case '219':
try:
# [':irc.deb.biz.st', '219', 'Dev-PyDefender', 's', ':End', 'of', '/STATS', 'report']
if not self.show_219:
# If there is a result in 223 then stop here
self.show_219 = True
return None
type_of_stats = str(cmd[3])
match type_of_stats:
case 's':
self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} : No shun")
case 'G':
self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} : No gline")
case 'k':
self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} : No kline")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case '223':
try:
# [':irc.deb.biz.st', '223', 'Dev-PyDefender', 'G', '*@162.142.125.217', '67624', '18776', 'irc.deb.biz.st', ':Proxy/Drone', 'detected.', 'Check', 'https://dronebl.org/lookup?ip=162.142.125.217', 'for', 'details.']
self.show_219 = False
host = str(cmd[4])
author = str(cmd[7])
reason = ' '.join(cmd[8:])
self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} : {bold}Author{nogc}: {author} - {bold}Host{nogc}: {host} - {bold}Reason{nogc}: {reason}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
return None return None
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None: def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
@@ -167,11 +213,74 @@ class Command():
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME
service_id = self.Config.SERVICE_ID service_id = self.Config.SERVICE_ID
dchanlog = self.Config.SERVICE_CHANLOG dchanlog = self.Config.SERVICE_CHANLOG
self.user_to_notice = user
fromuser = user fromuser = user
fromchannel = channel fromchannel = channel
match command: match command:
case 'deopall':
try:
self.Irc.send2socket(f":{service_id} SVSMODE {fromchannel} -o")
except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'devoiceall':
try:
self.Irc.send2socket(f":{service_id} SVSMODE {fromchannel} -v")
except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'voiceall':
try:
chan_info = self.Channel.get_Channel(fromchannel)
set_mode = 'v'
mode:str = ''
users:str = ''
uids_split = [chan_info.uids[i:i + 6] for i in range(0, len(chan_info.uids), 6)]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +{set_mode} {dnickname}")
for uid in uids_split:
for i in range(0, len(uid)):
mode += set_mode
users += f'{self.User.get_nickname(self.Base.clean_uid(uid[i]))} '
if i == len(uid) - 1:
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +{mode} {users}")
mode = ''
users = ''
except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'opall':
try:
chan_info = self.Channel.get_Channel(fromchannel)
set_mode = 'o'
mode:str = ''
users:str = ''
uids_split = [chan_info.uids[i:i + 6] for i in range(0, len(chan_info.uids), 6)]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +{set_mode} {dnickname}")
for uid in uids_split:
for i in range(0, len(uid)):
mode += set_mode
users += f'{self.User.get_nickname(self.Base.clean_uid(uid[i]))} '
if i == len(uid) - 1:
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +{mode} {users}")
mode = ''
users = ''
except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'op': case 'op':
# /mode #channel +o user # /mode #channel +o user
# .op #channel user # .op #channel user
@@ -198,6 +307,8 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}') self.Logs.warning(f'_hcmd OP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} op [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} op [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'deop': case 'deop':
# /mode #channel -o user # /mode #channel -o user
@@ -223,6 +334,8 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEOP: {str(e)}') self.Logs.warning(f'_hcmd DEOP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deop [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deop [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'owner': case 'owner':
# /mode #channel +q user # /mode #channel +q user
@@ -248,6 +361,8 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd OWNER: {str(e)}') self.Logs.warning(f'_hcmd OWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} owner [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} owner [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'deowner': case 'deowner':
# /mode #channel -q user # /mode #channel -q user
@@ -273,6 +388,62 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEOWNER: {str(e)}') self.Logs.warning(f'_hcmd DEOWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'protect':
# /mode #channel +a user
# .protect #channel user
try:
if fromchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +a {fromuser}")
return True
# deowner nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +a {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +a {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd DEOWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'deprotect':
# /mode #channel -a user
# .deprotect #channel user
try:
if fromchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -a {fromuser}")
return True
# deowner nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -a {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -a {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd DEOWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'halfop': case 'halfop':
# /mode #channel +h user # /mode #channel +h user
@@ -298,6 +469,8 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd halfop: {str(e)}') self.Logs.warning(f'_hcmd halfop: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} halfop [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} halfop [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'dehalfop': case 'dehalfop':
# /mode #channel -h user # /mode #channel -h user
@@ -323,6 +496,8 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEHALFOP: {str(e)}') self.Logs.warning(f'_hcmd DEHALFOP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'voice': case 'voice':
# /mode #channel +v user # /mode #channel +v user
@@ -348,6 +523,8 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd VOICE: {str(e)}') self.Logs.warning(f'_hcmd VOICE: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} voice [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} voice [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'devoice': case 'devoice':
# /mode #channel -v user # /mode #channel -v user
@@ -373,13 +550,15 @@ class Command():
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEVOICE: {str(e)}') self.Logs.warning(f'_hcmd DEVOICE: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'ban': case 'ban':
# .ban #channel nickname # .ban #channel nickname
try: try:
sentchannel = str(cmd[1]) if self.Base.Is_Channel(cmd[1]) else None sentchannel = str(cmd[1]) if self.Base.Is_Channel(cmd[1]) else None
if sentchannel is None: if sentchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} ban [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]')
return False return False
nickname = cmd[2] nickname = cmd[2]
@@ -388,7 +567,9 @@ class Command():
self.Logs.debug(f'{fromuser} has banned {nickname} from {sentchannel}') self.Logs.debug(f'{fromuser} has banned {nickname} from {sentchannel}')
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd BAN: {str(e)}') self.Logs.warning(f'_hcmd BAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} ban [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'unban': case 'unban':
# .unban #channel nickname # .unban #channel nickname
@@ -401,9 +582,12 @@ class Command():
self.Irc.send2socket(f":{service_id} MODE {sentchannel} -b {nickname}!*@*") self.Irc.send2socket(f":{service_id} MODE {sentchannel} -b {nickname}!*@*")
self.Logs.debug(f'{fromuser} has unbanned {nickname} from {sentchannel}') self.Logs.debug(f'{fromuser} has unbanned {nickname} from {sentchannel}')
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd UNBAN: {str(e)}') self.Logs.warning(f'_hcmd UNBAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} unban [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} unban [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'kick': case 'kick':
# .kick #channel nickname reason # .kick #channel nickname reason
@@ -422,9 +606,12 @@ class Command():
self.Irc.send2socket(f":{service_id} KICK {sentchannel} {nickname} {final_reason}") self.Irc.send2socket(f":{service_id} KICK {sentchannel} {nickname} {final_reason}")
self.Logs.debug(f'{fromuser} has kicked {nickname} from {sentchannel} : {final_reason}') self.Logs.debug(f'{fromuser} has kicked {nickname} from {sentchannel} : {final_reason}')
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd KICK: {str(e)}') self.Logs.warning(f'_hcmd KICK: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kick [#SALON] [NICKNAME] [REASON]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kick [#SALON] [NICKNAME] [REASON]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'kickban': case 'kickban':
# .kickban #channel nickname reason # .kickban #channel nickname reason
@@ -444,9 +631,12 @@ class Command():
self.Irc.send2socket(f":{service_id} KICK {sentchannel} {nickname} {final_reason}") self.Irc.send2socket(f":{service_id} KICK {sentchannel} {nickname} {final_reason}")
self.Irc.send2socket(f":{service_id} MODE {sentchannel} +b {nickname}!*@*") self.Irc.send2socket(f":{service_id} MODE {sentchannel} +b {nickname}!*@*")
self.Logs.debug(f'{fromuser} has kicked and banned {nickname} from {sentchannel} : {final_reason}') self.Logs.debug(f'{fromuser} has kicked and banned {nickname} from {sentchannel} : {final_reason}')
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd KICKBAN: {str(e)}') self.Logs.warning(f'_hcmd KICKBAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kickban [#SALON] [NICKNAME] [REASON]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kickban [#SALON] [NICKNAME] [REASON]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'join': case 'join':
@@ -462,6 +652,8 @@ class Command():
except IndexError as ie: except IndexError as ie:
self.Logs.error(f'{ie}') self.Logs.error(f'{ie}')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'part': case 'part':
@@ -481,3 +673,495 @@ class Command():
except IndexError as ie: except IndexError as ie:
self.Logs.error(f'{ie}') self.Logs.error(f'{ie}')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'topic':
try:
if len(cmd) == 1:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} TOPIC #channel THE_TOPIC_MESSAGE")
return None
chan = str(cmd[1])
if not self.Base.Is_Channel(chan):
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The channel must start with #")
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} TOPIC #channel THE_TOPIC_MESSAGE")
return None
topic_msg = ' '.join(cmd[2:]).strip()
if topic_msg:
self.Irc.send2socket(f':{dnickname} TOPIC {chan} :{topic_msg}')
else:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :You need to specify the topic")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'wallops':
try:
if len(cmd) == 1:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} WALLOPS THE_WALLOPS_MESSAGE")
return None
wallops_msg = ' '.join(cmd[1:]).strip()
if wallops_msg:
self.Irc.send2socket(f':{dnickname} WALLOPS {wallops_msg} ({dnickname})')
else:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :You need to specify the wallops message")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'globops':
try:
if len(cmd) == 1:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} GLOBOPS THE_GLOBOPS_MESSAGE")
return None
globops_msg = ' '.join(cmd[1:]).strip()
if globops_msg:
self.Irc.send2socket(f':{dnickname} GLOBOPS {globops_msg} ({dnickname})')
else:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :You need to specify the globops message")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'gnotice':
try:
if len(cmd) == 1:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} THE_GLOBAL_NOTICE_MESSAGE")
return None
gnotice_msg = ' '.join(cmd[1:]).strip()
if gnotice_msg:
self.Irc.send2socket(f':{dnickname} NOTICE $*.* :[{self.Config.COLORS.red}GLOBAL NOTICE{self.Config.COLORS.nogc}] {gnotice_msg}')
else:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :You need to specify the global notice message")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'whois':
try:
self.user_to_notice = fromuser
if len(cmd) == 1:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} NICKNAME")
return None
nickname = str(cmd[1])
if self.User.get_nickname(nickname) is None:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :Nickname not found !")
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} NICKNAME")
return None
self.Irc.send2socket(f':{dnickname} WHOIS {nickname}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'names':
try:
if len(cmd) == 1:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} #CHANNEL")
return None
chan = str(cmd[1])
if not self.Base.Is_Channel(chan):
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The channel must start with #")
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} #channel")
return None
self.Irc.send2socket(f':{dnickname} NAMES {chan}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'invite':
try:
if len(cmd) < 3:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} #CHANNEL NICKNAME")
return None
chan = str(cmd[1])
nickname = str(cmd[2])
if not self.Base.Is_Channel(chan):
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The channel must start with #")
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} #channel nickname")
return None
if self.User.get_nickname(nickname) is None:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :Nickname not found !")
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} #channel NICKNAME")
return None
self.Irc.send2socket(f':{dnickname} INVITE {nickname} {chan}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'inviteme':
try:
if len(cmd) == 0:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()}")
return None
self.Irc.send2socket(f':{dnickname} INVITE {fromuser} {self.Config.SERVICE_CHANLOG}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'map':
try:
self.user_to_notice = fromuser
self.Irc.send2socket(f':{dnickname} MAP')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'umode':
try:
# .umode nickname +mode
nickname = str(cmd[1])
umode = str(cmd[2])
self.Irc.send2socket(f':{dnickname} SVSMODE {nickname} {umode}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'mode':
# .mode #channel +/-mode
# .mode +/-mode
try:
if len(cmd) < 2:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#CHANNEL] [+/-]mode')
return None
if fromchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#CHANNEL] [+/-]mode')
return None
if len(cmd) == 2:
channel_mode = cmd[1]
if self.Base.Is_Channel(fromchannel):
self.Irc.send2socket(f":{dnickname} MODE {fromchannel} {channel_mode}")
else:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} : Right command : Channel [{fromchannel}] is not correct should start with #")
return None
if len(cmd) == 3:
provided_channel = cmd[1]
channel_mode = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {provided_channel} {channel_mode}")
return None
except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#CHANNEL] [+/-]mode')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'svsjoin':
try:
# .svsjoin nickname #channel
nickname = str(cmd[1])
channel = str(cmd[2])
if len(cmd) != 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSJOIN nickname #channel')
return None
self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SVSJOIN {nickname} {channel}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSJOIN nickname #channel')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'svspart':
try:
# .svspart nickname #channel
nickname = str(cmd[1])
channel = str(cmd[2])
if len(cmd) != 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSPART nickname #channel')
return None
self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SVSPART {nickname} {channel}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSPART nickname #channel')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'sajoin':
try:
# .sajoin nickname #channel
nickname = str(cmd[1])
channel = str(cmd[2])
if len(cmd) < 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname #channel')
return None
self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SAJOIN {nickname} {channel}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname #channel')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'sapart':
try:
# .sapart nickname #channel
nickname = str(cmd[1])
channel = str(cmd[2])
if len(cmd) < 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname #channel')
return None
self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SAPART {nickname} {channel}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname #channel')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'svsnick':
try:
# .svsnick nickname newnickname
nickname = str(cmd[1])
newnickname = str(cmd[2])
unixtime = self.Base.get_unixtime()
if self.User.get_nickname(nickname) is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : This nickname do not exist')
return None
if len(cmd) != 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname newnickname')
return None
self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SVSNICK {nickname} {newnickname} {unixtime}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname newnickname')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'kill':
try:
# 'kill', 'gline', 'ungline', 'shun', 'unshun'
# .kill nickname reason
if len(cmd) < 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname reason')
return None
nickname = str(cmd[1])
kill_reason = ' '.join(cmd[2:])
self.Irc.send2socket(f":{service_id} KILL {nickname} {kill_reason} ({self.Config.COLORS.red}{dnickname}{self.Config.COLORS.nogc})")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSNICK nickname newnickname')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'gline':
try:
# TKL + G user host set_by expire_timestamp set_at_timestamp :reason
# .gline [nickname] [host] [reason]
if len(cmd) < 4:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
return None
nickname = str(cmd[1])
hostname = str(cmd[2])
set_at_timestamp = self.Base.get_unixtime()
expire_time = (60 * 60 * 24) + set_at_timestamp
gline_reason = ' '.join(cmd[3:])
if nickname == '*' and hostname == '*':
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : You want to close the server ? i would recommand ./unrealircd stop :)')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
return None
self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL + G {nickname} {hostname} {dnickname} {expire_time} {set_at_timestamp} :{gline_reason}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'ungline':
try:
# 'shun', 'unshun'
# TKL + G user host set_by expire_timestamp set_at_timestamp :reason
# .ungline nickname host
if len(cmd) < 2:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname')
return None
nickname = str(cmd[1])
hostname = str(cmd[2])
self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL - G {nickname} {hostname} {dnickname}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'kline':
try:
# TKL + k user host set_by expire_timestamp set_at_timestamp :reason
# .gline [nickname] [host] [reason]
if len(cmd) < 4:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
return None
nickname = str(cmd[1])
hostname = str(cmd[2])
set_at_timestamp = self.Base.get_unixtime()
expire_time = (60 * 60 * 24) + set_at_timestamp
gline_reason = ' '.join(cmd[3:])
if nickname == '*' and hostname == '*':
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : You want to close the server ? i would recommand ./unrealircd stop :)')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
return None
self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL + k {nickname} {hostname} {dnickname} {expire_time} {set_at_timestamp} :{gline_reason}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'unkline':
try:
# 'shun', 'unshun'
# TKL + G user host set_by expire_timestamp set_at_timestamp :reason
# .ungline nickname host
if len(cmd) < 2:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname')
return None
nickname = str(cmd[1])
hostname = str(cmd[2])
self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL - k {nickname} {hostname} {dnickname}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'shun':
try:
# TKL + G user host set_by expire_timestamp set_at_timestamp :reason
# .shun [nickname] [host] [reason]
if len(cmd) < 4:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
return None
nickname = str(cmd[1])
hostname = str(cmd[2])
set_at_timestamp = self.Base.get_unixtime()
expire_time = (60 * 60 * 24) + set_at_timestamp
shun_reason = ' '.join(cmd[3:])
if nickname == '*' and hostname == '*':
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : You want to close the server ? i would recommand ./unrealircd stop :)')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
return None
self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL + s {nickname} {hostname} {dnickname} {expire_time} {set_at_timestamp} :{shun_reason}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'unshun':
try:
# 'shun', 'unshun'
# TKL + G user host set_by expire_timestamp set_at_timestamp :reason
# .unshun nickname host
if len(cmd) < 2:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname')
return None
nickname = str(cmd[1])
hostname = str(cmd[2])
self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL - s {nickname} {hostname} {dnickname}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'glinelist':
try:
self.user_to_notice = fromuser
self.Irc.send2socket(f":{self.Config.SERVICE_ID} STATS G")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()}')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'shunlist':
try:
self.user_to_notice = fromuser
self.Irc.send2socket(f":{self.Config.SERVICE_ID} STATS s")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()}')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'klinelist':
try:
self.user_to_notice = fromuser
self.Irc.send2socket(f":{self.Config.SERVICE_ID} STATS k")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()}')
self.Logs.warning(f'Unknown Error: {str(err)}')

File diff suppressed because it is too large Load Diff

306
mods/mod_jsonrpc.py Normal file
View File

@@ -0,0 +1,306 @@
from dataclasses import dataclass
from core.irc import Irc
from unrealircd_rpc_py.Live import Live
from unrealircd_rpc_py.Loader import Loader
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 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)
# 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',
debug_level=10
)
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.Irc.sendPrivMsg(f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.Rpc.Error.message}", self.Config.SERVICE_CHANLOG)
if self.UnrealIrcdRpcLive.Error.code != 0:
self.Irc.sendPrivMsg(f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.UnrealIrcdRpcLive.Error.message}", 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.Irc.sendPrivMsg(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.Irc.sendPrivMsg(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.Irc.sendPrivMsg(f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.UnrealIrcdRpcLive.Error.message}", 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.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} jsonrpc on')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/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.Irc.sendPrivMsg(f"Thread {thread.getName()} is running", dchannel)
else:
self.Irc.sendPrivMsg(f"Thread {thread.getName()} is not running, wait untill the process will be cleaned up", 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.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/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.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{rpc.Error.message}')
return None
chan_list = []
for chan in UserInfo.user.channels:
chan_list.append(chan.name)
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :UID : {UserInfo.id}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :NICKNAME : {UserInfo.name}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :USERNAME : {UserInfo.user.username}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :REALNAME : {UserInfo.user.realname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :MODES : {UserInfo.user.modes}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CHANNELS : {chan_list}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :SECURITY GROUP : {UserInfo.user.security_groups}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :REPUTATION : {UserInfo.user.reputation}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :IP : {UserInfo.ip}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :COUNTRY CODE : {UserInfo.geoip.country_code}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :ASN : {UserInfo.geoip.asn}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :ASNAME : {UserInfo.geoip.asname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CLOAKED HOST : {UserInfo.user.cloakedhost}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :HOSTNAME : {UserInfo.hostname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :VHOST : {UserInfo.user.vhost}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CLIENT PORT : {UserInfo.client_port}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :SERVER PORT : {UserInfo.server_port}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CERTFP : {UserInfo.tls.certfp}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CIPHER : {UserInfo.tls.cipher}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :IDLE SINCE : {UserInfo.idle_since}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :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.Irc.send2socket(f":{dnickname} NOTICE {fromuser} : This is a notice to the sender ...")
self.Irc.send2socket(f":{dnickname} PRIVMSG {fromuser} : This is private message to the sender ...")
if not fromchannel is None:
self.Irc.send2socket(f":{dnickname} PRIVMSG {fromchannel} : This is channel message to the sender ...")
# How to update your module configuration
self.__update_configuration('param_exemple2', 7)
# Log if you want the result
self.Logs.debug(f"Test logs ready")
except Exception as err:
self.Logs.error(f"Unknown Error: {err}")

View File

@@ -124,8 +124,16 @@ class Test():
return None return None
def cmd(self, data:list) -> None: def cmd(self, data:list) -> None:
try:
cmd = list(data).copy()
return None return None
except KeyError as ke:
self.Base.logs.error(f"Key Error: {ke}")
except IndexError as ie:
self.Base.logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
except Exception as err:
self.Base.logs.error(f"General Error: {err}")
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None: def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
@@ -152,4 +160,4 @@ class Test():
self.Logs.debug(f"Test logs ready") self.Logs.debug(f"Test logs ready")
except Exception as err: except Exception as err:
self.Logs.error(f"{err}") self.Logs.error(f"Unknown Error: {err}")

View File

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

View File

@@ -1,3 +1,9 @@
{ {
"version": "5.1.0" "version": "5.4.0",
"requests": "2.32.3",
"psutil": "6.0.0",
"unrealircd_rpc_py": "1.0.6",
"sqlalchemy": "2.0.35",
"faker": "30.1.0"
} }