mirror of
https://github.com/iio612/DEFENDER.git
synced 2026-02-14 11:44:23 +00:00
Compare commits
73 Commits
V6.2.2
...
interface-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c371910066 | ||
|
|
2e422c93e5 | ||
|
|
ba989b7f26 | ||
|
|
fc01de34b2 | ||
|
|
a3dcc20a06 | ||
|
|
7ffc58d4ff | ||
|
|
6a0d4e2286 | ||
|
|
7dd15f2dac | ||
|
|
10cad7cda6 | ||
|
|
999072a88a | ||
|
|
8932e1441a | ||
|
|
9cee758b6f | ||
|
|
511e0c0715 | ||
|
|
371c8fb5f1 | ||
|
|
401e785383 | ||
|
|
a7efede75e | ||
|
|
a1254c7a39 | ||
|
|
c05990f862 | ||
|
|
de2b5fa8e2 | ||
|
|
371645149d | ||
|
|
a6cf11ae2a | ||
|
|
445cbc27b0 | ||
|
|
f9eb374798 | ||
|
|
17cb2ada5f | ||
|
|
b52a57f95a | ||
|
|
1bfd95c291 | ||
|
|
0c6c3cd6ac | ||
|
|
0e6384c26c | ||
|
|
79c1b94a92 | ||
|
|
5a1432c1e6 | ||
|
|
34b5b4204e | ||
|
|
ff58cbb022 | ||
|
|
6450418859 | ||
|
|
9f2da13f88 | ||
|
|
0117e1dd3a | ||
|
|
deb76baf30 | ||
|
|
29f049b3c3 | ||
|
|
fb41a13d0a | ||
|
|
769ab8b632 | ||
|
|
2fbe75b83e | ||
|
|
8abae5df3e | ||
|
|
1a71a6eb4d | ||
|
|
b182aa8bcb | ||
|
|
e5a5f01603 | ||
|
|
99f8949681 | ||
|
|
05b15f2f18 | ||
|
|
35c3faf68c | ||
|
|
2e9bfd2c3b | ||
|
|
80131b7b7a | ||
|
|
ffb30f12ec | ||
|
|
b7b61081be | ||
|
|
030b706b65 | ||
|
|
c428ea2b41 | ||
|
|
9036e4f626 | ||
|
|
fd79ada13d | ||
|
|
8323f6cc9b | ||
|
|
6fcd553481 | ||
|
|
5cd82a174d | ||
|
|
beec16f39d | ||
|
|
a043a58f45 | ||
|
|
fd9643eddc | ||
|
|
ed1a048603 | ||
|
|
3dfde9b1aa | ||
|
|
5e35a10193 | ||
|
|
ff776541d7 | ||
|
|
6b7fd16a44 | ||
|
|
e79c15188e | ||
|
|
b306968115 | ||
|
|
184e90adce | ||
|
|
c7b88150b5 | ||
|
|
02f0608b75 | ||
|
|
25bbddf459 | ||
|
|
0c6fcb7710 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,9 +1,15 @@
|
||||
.pyenv/
|
||||
.vscode/
|
||||
.venv/
|
||||
.idea/
|
||||
db/
|
||||
logs/
|
||||
__pycache__/
|
||||
configuration.json
|
||||
configuration.yaml
|
||||
configuration_inspircd.json
|
||||
configuration_unreal6.json
|
||||
*.log
|
||||
test.py
|
||||
users.txt
|
||||
modules.txt
|
||||
65
Makefile
Normal file
65
Makefile
Normal file
@@ -0,0 +1,65 @@
|
||||
OS := $(shell uname -s)
|
||||
CURRENT_USER := $(shell whoami)
|
||||
PYTHON_VERSION := $(shell python3 -V)
|
||||
HOME_DIR := $(shell echo $$HOME)
|
||||
SHELL := /bin/bash
|
||||
|
||||
install:
|
||||
ifeq ($(wildcard config/configuration.yaml),)
|
||||
$(error You must provide the Configuration file: config/configuration.yaml)
|
||||
endif
|
||||
|
||||
ifeq ($(OS), Linux)
|
||||
$(info Installation for os : $(OS))
|
||||
$(info Python version: $(PYTHON_VERSION))
|
||||
$(info Home directory: $(HOME_DIR))
|
||||
|
||||
@python3 core/install.py --check-version
|
||||
@if [ $$? -eq 0 ]; then \
|
||||
echo "Python Version OK! Well done :)"; \
|
||||
else \
|
||||
echo "Error: Script failed with exit code $$?"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
$(info Creating the systemd user folder...)
|
||||
mkdir -p $(HOME_DIR)/.config/systemd/user
|
||||
|
||||
$(info Creating Python Virtual Environment...)
|
||||
python3 -m venv .pyenv
|
||||
@. .pyenv/bin/activate && \
|
||||
python -m pip install --upgrade pip && \
|
||||
pip cache purge && \
|
||||
pip install -r requirements.txt
|
||||
|
||||
@. .pyenv/bin/activate && python core/install.py --install
|
||||
loginctl enable-linger $(CURRENT_USER)
|
||||
@sleep 2
|
||||
@export echo $DBUS_SESSION_BUS_ADDRESS && \
|
||||
systemctl --user daemon-reload && \
|
||||
systemctl --user start defender
|
||||
|
||||
endif
|
||||
|
||||
clean:
|
||||
ifeq ($(OS), Linux)
|
||||
@export echo $DBUS_SESSION_BUS_ADDRESS && \
|
||||
systemctl --user stop defender
|
||||
$(info Defender has been stopped...)
|
||||
@if [ -e .pyenv ]; then \
|
||||
rm -rf .pyenv; \
|
||||
echo "Virtual Env has been removed!"; \
|
||||
fi
|
||||
@if [ -e $(HOME_DIR)/.config/systemd/user/defender.service ]; then \
|
||||
rm $(HOME_DIR)/.config/systemd/user/defender.service; \
|
||||
echo "Systemd file has been removed!"; \
|
||||
fi
|
||||
@export echo $DBUS_SESSION_BUS_ADDRESS && systemctl --user daemon-reload && echo "Systemd Daemon reloaded!"
|
||||
endif
|
||||
|
||||
update:
|
||||
ifeq ($(OS), Linux)
|
||||
$(info Starting update from the main repository...)
|
||||
@. .pyenv/bin/activate && python core/install.py --git-update
|
||||
$(info Update done!)
|
||||
endif
|
||||
139
README.md
139
README.md
@@ -34,10 +34,11 @@ Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux u
|
||||
- Python version 3.10 ou supérieure
|
||||
```bash
|
||||
# Bash
|
||||
$ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git
|
||||
$ git clone https://github.com/adator85/DEFENDER.git defender
|
||||
$ cd defender/
|
||||
# Renommer le fichier exemple_configuration.json en configuration.json
|
||||
# Configurer le fichier configuration.json
|
||||
$ python3 main.py
|
||||
$ make install
|
||||
```
|
||||
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:
|
||||
@@ -49,11 +50,11 @@ Pour Les prochains lancement de defender vous devez utiliser la commande suivant
|
||||
# Installation manuelle:
|
||||
```bash
|
||||
# Bash
|
||||
$ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git
|
||||
$ cd IRC_DEFENDER_MODULES
|
||||
$ git clone https://github.com/adator85/DEFENDER.git defender
|
||||
$ cd defender/
|
||||
$ python3 -m venv .pyenv
|
||||
$ source .pyenv/bin/activate
|
||||
(pyenv)$ pip install sqlalchemy, psutil, requests, faker, unrealircd_rpc_py
|
||||
(pyenv)$ pip install -r requirements.txt
|
||||
|
||||
# Créer un service nommé "defender.service"
|
||||
# pour votre service et placer le dans "/PATH/TO/USER/.config/systemd/user/"
|
||||
@@ -104,87 +105,91 @@ Pour Les prochains lancement de defender vous devez utiliser la commande suivant
|
||||
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
|
||||
DEBUG_LEVEL: Niveau de verbosité des messages de debug (plus petit est le nombre, plus il y a d'informations). (default : 20) Pour une production
|
||||
DEBUG_HARD: Généralement utiliser pour les developpeurs.
|
||||
|
||||
```
|
||||
Modification de la configuration
|
||||
|
||||
Vous devez modifier le fichier configuration.json en remplaçant les valeurs par défaut avec vos propres informations. Assurez-vous de bien lire la description de chaque paramètre pour une configuration optimale du service.
|
||||
Vous devez modifier le fichier configuration.yaml en remplaçant les valeurs par défaut avec vos propres informations. Assurez-vous de bien lire la description de chaque paramètre pour une configuration optimale du service.
|
||||
|
||||
## Exemple de configuration de base
|
||||
```json
|
||||
{
|
||||
"SERVEUR_IP": "IP.DE.TON.SERVER",
|
||||
"SERVEUR_HOSTNAME": "HOST.DE.TON.SERVER",
|
||||
"SERVEUR_LINK": "LINK.DE.TON.SERVER",
|
||||
"SERVEUR_PORT": 6901,
|
||||
"SERVEUR_PASSWORD": "MOT_DE_PASS_DE_TON_LINK",
|
||||
"SERVEUR_ID": "10Z",
|
||||
"SERVEUR_SSL": true,
|
||||
```yaml
|
||||
configuration:
|
||||
SERVEUR_IP: "YOUR.SERVER.IP"
|
||||
SERVEUR_HOSTNAME: "YOUR.SERVER.HOST"
|
||||
SERVEUR_LINK: "LINK.DE.TON.SERVER"
|
||||
SERVEUR_PORT: 7002
|
||||
SERVEUR_PASSWORD: "YOUR_LINK_PASSWORD"
|
||||
SERVEUR_ID: "006"
|
||||
SERVEUR_SSL: true
|
||||
|
||||
"SERVICE_NAME": "defender",
|
||||
"SERVICE_NICKNAME": "PyDefender",
|
||||
"SERVICE_REALNAME": "Python Defender Security",
|
||||
"SERVICE_USERNAME": "PyDefender",
|
||||
"SERVICE_HOST": "HOST.DE.TON.DEFENDER",
|
||||
SERVICE_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"
|
||||
|
||||
}
|
||||
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,
|
||||
```yaml
|
||||
configuration:
|
||||
SERVEUR_IP: "YOUR.SERVER.IP"
|
||||
SERVEUR_HOSTNAME: "YOUR.SERVER.HOST"
|
||||
SERVEUR_LINK: "LINK.DE.TON.SERVER"
|
||||
SERVEUR_PORT: 7002
|
||||
SERVEUR_PASSWORD: "YOUR_LINK_PASSWORD"
|
||||
SERVEUR_ID: "006"
|
||||
SERVEUR_SSL: true
|
||||
|
||||
"SERVICE_NAME": "defender",
|
||||
"SERVICE_NICKNAME": "PyDefender",
|
||||
"SERVICE_REALNAME": "Python Defender Security",
|
||||
"SERVICE_USERNAME": "PyDefender",
|
||||
"SERVICE_HOST": "HOST.DE.TON.DEFENDER",
|
||||
"SERVICE_INFO": "Network IRC Service",
|
||||
"SERVICE_CHANLOG": "#services",
|
||||
"SERVICE_SMODES": "+ioqBS",
|
||||
"SERVICE_CMODES": "ntsOP",
|
||||
"SERVICE_UMODES": "o",
|
||||
"SERVICE_PREFIX": "!",
|
||||
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",
|
||||
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",
|
||||
JSONRPC_URL: "https://your.domaine.com:8600/api"
|
||||
JSONRPC_PATH_TO_SOCKET_FILE: "/PATH/TO/YOUR/IRCD/data/rpc.socket"
|
||||
JSONRPC_METHOD: "unixsocket"
|
||||
JSONRPC_USER: "YOUR_RPC_USER"
|
||||
JSONRPC_PASSWORD: "YOUR_RPC_PASSWORD"
|
||||
|
||||
"SALON_JAIL": "#jail",
|
||||
"SALON_JAIL_MODES": "sS",
|
||||
"SALON_LIBERER": "#welcome",
|
||||
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",
|
||||
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],
|
||||
"WHITELISTED_IP": ["127.0.0.1"],
|
||||
"GLINE_DURATION": "30",
|
||||
PORTS_TO_SCAN: [3028 8080 1080 1085 4145 9050]
|
||||
WHITELISTED_IP: ["127.0.0.1"]
|
||||
GLINE_DURATION: "30"
|
||||
|
||||
"DEBUG_LEVEL": 20
|
||||
|
||||
}
|
||||
DEBUG_LEVEL: 20
|
||||
DEBUG_HARD: true
|
||||
```
|
||||
|
||||
# \\!/ Attention \\!/
|
||||
@@ -192,7 +197,7 @@ Le mot de passe de l'administrateur et le mot de passe du service doivent être
|
||||
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]
|
||||
/msg [NomDuService] firstauth [nickname] [password]
|
||||
-- Une fois identifié tapez la commande suivante
|
||||
/msg [NomDuService] editaccess [nickname] [Nouveau-Password] 5
|
||||
```
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"SERVEUR_IP": "YOUR.SERVER.IP",
|
||||
"SERVEUR_HOSTNAME": "YOUR.SERVER.HOST",
|
||||
"SERVEUR_LINK": "LINK.DE.TON.SERVER",
|
||||
"SERVEUR_PORT": 7002,
|
||||
"SERVEUR_PASSWORD": "YOUR_LINK_PASSWORD",
|
||||
"SERVEUR_ID": "006",
|
||||
"SERVEUR_SSL": true,
|
||||
|
||||
"SERVICE_NAME": "defender",
|
||||
"SERVICE_NICKNAME": "PyDefender",
|
||||
"SERVICE_REALNAME": "Python Defender Security",
|
||||
"SERVICE_USERNAME": "PyDefender",
|
||||
"SERVICE_HOST": "HOST.DE.TON.DEFENDER",
|
||||
"SERVICE_INFO": "Network IRC Service",
|
||||
"SERVICE_CHANLOG": "#services",
|
||||
"SERVICE_SMODES": "+ioqBS",
|
||||
"SERVICE_CMODES": "ntsOP",
|
||||
"SERVICE_UMODES": "o",
|
||||
"SERVICE_PREFIX": "!",
|
||||
|
||||
"OWNER": "TON_NICK_NAME",
|
||||
"PASSWORD": "TON_PASSWORD",
|
||||
|
||||
"JSONRPC_URL": "https://your.domaine.com:8600/api",
|
||||
"JSONRPC_PATH_TO_SOCKET_FILE": "/PATH/TO/YOUR/IRCD/data/rpc.socket",
|
||||
"JSONRPC_METHOD": "socket",
|
||||
"JSONRPC_USER": "YOUR_RPC_USER",
|
||||
"JSONRPC_PASSWORD": "YOUR_RPC_PASSWORD",
|
||||
|
||||
"SALON_JAIL": "#jail",
|
||||
"SALON_JAIL_MODES": "sS",
|
||||
"SALON_LIBERER": "#welcome",
|
||||
|
||||
"CLONE_CHANNEL": "#clones",
|
||||
"CLONE_CMODES": "+nts",
|
||||
"CLONE_LOG_HOST_EXEMPT": ["HOST.TO.SKIP"],
|
||||
"CLONE_CHANNEL_PASSWORD": "YOUR_CHANNEL_PASSWORD",
|
||||
|
||||
"API_TIMEOUT": 2,
|
||||
|
||||
"PORTS_TO_SCAN": [3028, 8080, 1080, 1085, 4145, 9050],
|
||||
"WHITELISTED_IP": ["127.0.0.1"],
|
||||
"GLINE_DURATION": "30",
|
||||
|
||||
"DEBUG_LEVEL": 20
|
||||
|
||||
}
|
||||
48
config/exemple_configuration.yaml
Normal file
48
config/exemple_configuration.yaml
Normal file
@@ -0,0 +1,48 @@
|
||||
configuration:
|
||||
SERVEUR_IP: "YOUR.SERVER.IP"
|
||||
SERVEUR_HOSTNAME: "YOUR.SERVER.HOST"
|
||||
SERVEUR_LINK: "LINK.DE.TON.SERVER"
|
||||
SERVEUR_PORT: 7002
|
||||
SERVEUR_PASSWORD: "YOUR_LINK_PASSWORD"
|
||||
SERVEUR_ID: "006"
|
||||
SERVEUR_SSL: true
|
||||
SERVEUR_PROTOCOL: "unreal6" # unreal6 or inspircd
|
||||
|
||||
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: "unixsocket"
|
||||
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", "192.168.1.1"]
|
||||
GLINE_DURATION: "30"
|
||||
|
||||
DEBUG_LEVEL: 20
|
||||
DEBUG_HARD: true
|
||||
71
core/base.py
71
core/base.py
@@ -1,16 +1,12 @@
|
||||
import importlib
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
import socket
|
||||
import threading
|
||||
import ipaddress
|
||||
import ast
|
||||
import requests
|
||||
from pathlib import Path
|
||||
from types import ModuleType
|
||||
from dataclasses import fields
|
||||
from typing import Any, Optional, TYPE_CHECKING
|
||||
from base64 import b64decode, b64encode
|
||||
@@ -30,7 +26,8 @@ class Base:
|
||||
self.Utils = loader.Utils
|
||||
self.logs = loader.Logs
|
||||
|
||||
self.check_for_new_version(True) # Verifier si une nouvelle version est disponible
|
||||
# Check if new Defender version is available
|
||||
self.check_for_new_version(True)
|
||||
|
||||
# Liste des timers en cours
|
||||
self.running_timers: list[threading.Timer] = self.Settings.RUNNING_TIMERS
|
||||
@@ -47,9 +44,17 @@ class Base:
|
||||
# Création du lock
|
||||
self.lock = self.Settings.LOCK
|
||||
|
||||
self.install: bool = False # Initialisation de la variable d'installation
|
||||
self.engine, self.cursor = self.db_init() # Initialisation de la connexion a la base de données
|
||||
self.__create_db() # Initialisation de la base de données
|
||||
# Init install variable
|
||||
self.install: bool = False
|
||||
|
||||
# Init database connection
|
||||
self.engine, self.cursor = self.db_init()
|
||||
|
||||
# Create the database
|
||||
# self.__create_db()
|
||||
|
||||
def init(self) -> None:
|
||||
self.__create_db()
|
||||
|
||||
def __set_current_defender_version(self) -> None:
|
||||
"""This will put the current version of Defender
|
||||
@@ -296,13 +301,14 @@ class Base:
|
||||
'password': password,
|
||||
'hostname': '*',
|
||||
'vhost': '*',
|
||||
'language': 'EN',
|
||||
'level': 5
|
||||
}
|
||||
self.db_execute_query(f"""
|
||||
INSERT INTO {self.Config.TABLE_ADMIN}
|
||||
(createdOn, user, password, hostname, vhost, level)
|
||||
(createdOn, user, password, hostname, vhost, language, level)
|
||||
VALUES
|
||||
(:createdOn, :user, :password, :hostname, :vhost, :level)"""
|
||||
(:createdOn, :user, :password, :hostname, :vhost, :language, :level)"""
|
||||
, mes_donnees)
|
||||
|
||||
return None
|
||||
@@ -333,6 +339,9 @@ class Base:
|
||||
run_once (bool, optional): If you want to ensure that this method/function run once. Defaults to False.
|
||||
"""
|
||||
try:
|
||||
# Clean unused threads first
|
||||
self.garbage_collector_thread()
|
||||
|
||||
func_name = func.__name__
|
||||
|
||||
if run_once:
|
||||
@@ -346,8 +355,8 @@ class Base:
|
||||
self.running_threads.append(th)
|
||||
self.logs.debug(f"-- Thread ID : {str(th.ident)} | Thread name : {th.name} | Running Threads : {len(threading.enumerate())}")
|
||||
|
||||
except AssertionError as ae:
|
||||
self.logs.error(f'{ae}')
|
||||
except Exception as err:
|
||||
self.logs.error(err, exc_info=True)
|
||||
|
||||
def is_thread_alive(self, thread_name: str) -> bool:
|
||||
"""Check if the thread is still running! using the is_alive method of Threads.
|
||||
@@ -424,11 +433,11 @@ class Base:
|
||||
if thread.name != 'heartbeat':
|
||||
if not thread.is_alive():
|
||||
self.running_threads.remove(thread)
|
||||
self.logs.info(f"-- Thread {str(thread.name)} {str(thread.native_id)} removed")
|
||||
self.logs.debug(f"-- Thread {str(thread.name)} {str(thread.native_id)} has been removed!")
|
||||
|
||||
# print(threading.enumerate())
|
||||
except AssertionError as ae:
|
||||
self.logs.error(f'Assertion Error -> {ae}')
|
||||
except Exception as err:
|
||||
self.logs.error(err, exc_info=True)
|
||||
|
||||
def garbage_collector_sockets(self) -> None:
|
||||
|
||||
@@ -484,7 +493,7 @@ class Base:
|
||||
|
||||
engine = create_engine(f'sqlite:///{full_path_db}.db', echo=False)
|
||||
cursor = engine.connect()
|
||||
self.logs.info("-- database connexion has been initiated")
|
||||
self.logs.info("-- Database connexion has been initiated")
|
||||
return engine, cursor
|
||||
|
||||
def __create_db(self) -> None:
|
||||
@@ -538,6 +547,7 @@ class Base:
|
||||
vhost TEXT,
|
||||
password TEXT,
|
||||
fingerprint TEXT,
|
||||
language TEXT,
|
||||
level INTEGER
|
||||
)
|
||||
'''
|
||||
@@ -564,6 +574,9 @@ class Base:
|
||||
self.db_execute_query(table_core_channel)
|
||||
self.db_execute_query(table_core_config)
|
||||
|
||||
# Patch database
|
||||
self.db_patch(self.Config.TABLE_ADMIN, "language", "TEXT")
|
||||
|
||||
if self.install:
|
||||
self.Loader.ModuleUtils.db_register_module('mod_command', 'sys', True)
|
||||
self.Loader.ModuleUtils.db_register_module('mod_defender', 'sys', True)
|
||||
@@ -584,6 +597,27 @@ class Base:
|
||||
|
||||
return response
|
||||
|
||||
def db_is_column_exist(self, table_name: str, column_name: str) -> bool:
|
||||
q = self.db_execute_query(f"PRAGMA table_info({table_name})")
|
||||
existing_columns = [col[1] for col in q.fetchall()]
|
||||
|
||||
if column_name in existing_columns:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def db_patch(self, table_name: str, column_name: str, column_type: str) -> bool:
|
||||
if not self.db_is_column_exist(table_name, column_name):
|
||||
patch = f"ALTER TABLE {table_name} ADD COLUMN {column_name} {column_type}"
|
||||
update_row = f"UPDATE {table_name} SET language = 'EN' WHERE language is null"
|
||||
self.db_execute_query(patch)
|
||||
self.db_execute_query(update_row)
|
||||
self.logs.debug(f"The patch has been applied")
|
||||
self.logs.debug(f"Table name: {table_name}, Column name: {column_name}, Column type: {column_type}")
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def db_close(self) -> None:
|
||||
|
||||
try:
|
||||
@@ -658,14 +692,13 @@ class Base:
|
||||
return False
|
||||
|
||||
def decode_ip(self, ip_b64encoded: str) -> Optional[str]:
|
||||
|
||||
binary_ip = b64decode(ip_b64encoded)
|
||||
try:
|
||||
binary_ip = b64decode(ip_b64encoded)
|
||||
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}')
|
||||
self.logs.critical(f'This remote ip ({ip_b64encoded}) is not valid : {ve}')
|
||||
return None
|
||||
|
||||
def encode_ip(self, remote_ip_address: str) -> Optional[str]:
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
from json import load
|
||||
from sys import exit
|
||||
from os import sep
|
||||
from typing import Any, Optional, Union, TYPE_CHECKING
|
||||
from core.definition import MConfig
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.loader import Loader
|
||||
|
||||
class Configuration:
|
||||
|
||||
def __init__(self, loader: 'Loader') -> None:
|
||||
|
||||
self.Loader = loader
|
||||
self.Logs = loader.Logs
|
||||
self._config_model: MConfig = self.__load_service_configuration()
|
||||
loader.ServiceLogging.set_file_handler_level(self._config_model.DEBUG_LEVEL)
|
||||
loader.ServiceLogging.set_stdout_handler_level(self._config_model.DEBUG_LEVEL)
|
||||
loader.ServiceLogging.update_handler_format(self._config_model.DEBUG_HARD)
|
||||
return None
|
||||
|
||||
def get_config_model(self) -> MConfig:
|
||||
return self._config_model
|
||||
|
||||
def __load_json_service_configuration(self) -> Optional[dict[str, Any]]:
|
||||
try:
|
||||
conf_filename = f'config{sep}configuration.json'
|
||||
with open(conf_filename, 'r') as configuration_data:
|
||||
configuration: dict[str, Union[str, int, list, dict]] = load(configuration_data)
|
||||
|
||||
return configuration
|
||||
|
||||
except FileNotFoundError as fe:
|
||||
self.Logs.error(f'FileNotFound: {fe}')
|
||||
self.Logs.error('Configuration file not found please create config/configuration.json')
|
||||
exit(0)
|
||||
except KeyError as ke:
|
||||
self.Logs.error(f'Key Error: {ke}')
|
||||
self.Logs.error('The key must be defined in core/configuration.json')
|
||||
|
||||
def __load_service_configuration(self) -> MConfig:
|
||||
try:
|
||||
import_config = self.__load_json_service_configuration()
|
||||
|
||||
Model_keys = MConfig().to_dict()
|
||||
model_key_list: list = []
|
||||
json_config_key_list: list = []
|
||||
|
||||
for key in Model_keys:
|
||||
model_key_list.append(key)
|
||||
|
||||
for key in import_config:
|
||||
json_config_key_list.append(key)
|
||||
|
||||
for json_conf in json_config_key_list:
|
||||
if not json_conf in model_key_list:
|
||||
import_config.pop(json_conf, None)
|
||||
self.Logs.warning(f"[!] The key {json_conf} is not expected, it has been removed from the system ! please remove it from configuration.json file [!]")
|
||||
|
||||
self.Logs.debug(f"[LOADING CONFIGURATION]: Loading configuration with {len(import_config)} parameters!")
|
||||
return MConfig(**import_config)
|
||||
|
||||
except TypeError as te:
|
||||
self.Logs.error(te)
|
||||
125
core/classes/interfaces/imodule.py
Normal file
125
core/classes/interfaces/imodule.py
Normal file
@@ -0,0 +1,125 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
from dataclasses import dataclass
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
|
||||
class IModule(ABC):
|
||||
|
||||
@abstractmethod
|
||||
@dataclass
|
||||
class ModConfModel:
|
||||
"""The Model containing the module parameters
|
||||
"""
|
||||
|
||||
def __init__(self, uplink: 'Irc') -> None:
|
||||
|
||||
# Module name (Mandatory)
|
||||
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
|
||||
|
||||
# Add Irc Object to the module (Mandatory)
|
||||
self.Irc = uplink
|
||||
|
||||
# Add Loader object to the module (Mandatory)
|
||||
self.Loader = uplink.Loader
|
||||
|
||||
# Add Protocol to the module (Mandatory)
|
||||
self.Protocol = uplink.Protocol
|
||||
|
||||
# Add Global Configuration to the module (Mandatory)
|
||||
self.Config = uplink.Config
|
||||
|
||||
# Add Settings to the module (Mandatory)
|
||||
self.Settings = uplink.Settings
|
||||
|
||||
# Add Base object to the module (Mandatory)
|
||||
self.Base = uplink.Base
|
||||
|
||||
# Add Main Utils (Mandatory)
|
||||
self.MainUtils = uplink.Utils
|
||||
|
||||
# Add logs object to the module (Mandatory)
|
||||
self.Logs = uplink.Loader.Logs
|
||||
|
||||
# Add User object to the module (Mandatory)
|
||||
self.User = uplink.User
|
||||
|
||||
# Add Client object to the module (Mandatory)
|
||||
self.Client = uplink.Client
|
||||
|
||||
# Add Admin object to the module (Mandatory)
|
||||
self.Admin = uplink.Admin
|
||||
|
||||
# Add Channel object to the module (Mandatory)
|
||||
self.Channel = uplink.Channel
|
||||
|
||||
# Add Reputation object to the module (Optional)
|
||||
self.Reputation = uplink.Reputation
|
||||
|
||||
# Load the child classes
|
||||
self.load()
|
||||
|
||||
# Inspect child classes
|
||||
self.inspect_class()
|
||||
|
||||
self.create_tables()
|
||||
|
||||
# Sync the configuration with core configuration (Mandatory)
|
||||
uplink.Base.db_sync_core_config(self.module_name, self.ModConfig)
|
||||
|
||||
# Log the module
|
||||
self.Logs.debug(f'Loading Module {self.module_name} ...')
|
||||
|
||||
def update_configuration(self, param_key: str, param_value: str) -> None:
|
||||
"""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 inspect_class(self):
|
||||
if not hasattr(self, 'ModConfig'):
|
||||
raise AttributeError("The Module must init ModConfig attribute in the load method!")
|
||||
if not hasattr(self, 'MOD_HEADER'):
|
||||
raise NotImplementedError(f"You must declare the header of the module in {self.__class__.__name__}!")
|
||||
|
||||
@abstractmethod
|
||||
def create_tables(self) -> None:
|
||||
"""Method that will create the database if it does not exist.
|
||||
A single Session for this class will be created, which will be used within this class/module.
|
||||
|
||||
Returns:
|
||||
None: No return is expected
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def load(self) -> None:
|
||||
"""This method is executed when the module is loaded or reloaded.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def unload(self) -> None:
|
||||
"""This method is executed when the module is unloaded or reloaded.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def cmd(self, data: list) -> None:
|
||||
"""When recieving server messages.
|
||||
|
||||
Args:
|
||||
data (list): The recieved message
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def hcmds(self, user: str, channel: Optional[str], cmd: list[str], fullcmd: Optional[list[str]] = None) -> None:
|
||||
"""These are the commands recieved from a client
|
||||
|
||||
Args:
|
||||
user (str): The client
|
||||
channel (str|None): The channel if available
|
||||
cmd (list): The user command sent
|
||||
fullcmd (list, optional): The full server message. Defaults to [].
|
||||
"""
|
||||
576
core/classes/interfaces/iprotocol.py
Normal file
576
core/classes/interfaces/iprotocol.py
Normal file
@@ -0,0 +1,576 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
from core.classes.protocols.command_handler import CommandHandler
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.definition import MClient, MSasl, MUser, MChannel
|
||||
from core.irc import Irc
|
||||
|
||||
class IProtocol(ABC):
|
||||
|
||||
Handler: Optional[CommandHandler] = None
|
||||
|
||||
def __init__(self, uplink: 'Irc'):
|
||||
self.name: Optional[str] = None
|
||||
self.protocol_version: int = -1
|
||||
self.known_protocol: set[str] = set()
|
||||
|
||||
self._Irc = uplink
|
||||
self._Config = uplink.Config
|
||||
self._Base = uplink.Base
|
||||
self._Settings = uplink.Base.Settings
|
||||
self._Utils = uplink.Loader.Utils
|
||||
self._Logs = uplink.Loader.Logs
|
||||
self._User = uplink.User
|
||||
self._Channel = uplink.Channel
|
||||
|
||||
self.Handler = CommandHandler(uplink.Loader)
|
||||
|
||||
self.init_protocol()
|
||||
|
||||
self._Logs.info(f"[PROTOCOL] Protocol [{self.__class__.__name__}] loaded!")
|
||||
|
||||
@abstractmethod
|
||||
def init_protocol(self):
|
||||
"""Init protocol
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_ircd_protocol_poisition(self, cmd: list[str], log: bool = False) -> tuple[int, Optional[str]]:
|
||||
"""Get the position of known commands
|
||||
|
||||
Args:
|
||||
cmd (list[str]): The server response
|
||||
log (bool): If true it will log in the logger
|
||||
|
||||
Returns:
|
||||
tuple[int, Optional[str]]: The position and the command.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def register_command(self):
|
||||
"""Register all commands that you need to handle
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send2socket(self, message: str, print_log: bool = True) -> None:
|
||||
"""Envoit les commandes à envoyer au serveur.
|
||||
|
||||
Args:
|
||||
message (str): contient la commande à envoyer au serveur.
|
||||
print_log (bool): If True then print logs
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_priv_msg(self, nick_from: str, msg: str, channel: str = None, nick_to: str = None):
|
||||
"""Sending PRIVMSG to a channel or to a nickname by batches
|
||||
could be either channel or nickname not both together
|
||||
Args:
|
||||
msg (str): The message to send
|
||||
nick_from (str): The sender nickname
|
||||
channel (str, optional): The receiver channel. Defaults to None.
|
||||
nick_to (str, optional): The reciever nickname. Defaults to None.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_notice(self, nick_from: str, nick_to: str, msg: str) -> None:
|
||||
"""Sending NOTICE by batches
|
||||
|
||||
Args:
|
||||
msg (str): The message to send to the server
|
||||
nick_from (str): The sender Nickname
|
||||
nick_to (str): The reciever nickname
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_link(self) -> None:
|
||||
"""Créer le link et envoyer les informations nécessaires pour la
|
||||
connexion au serveur.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_gline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None:
|
||||
"""Send a gline command to the server
|
||||
|
||||
Args:
|
||||
nickname (str): The nickname of the client.
|
||||
hostname (str): The hostname of the client.
|
||||
set_by (str): The nickname who send the gline
|
||||
expire_timestamp (int): Expire timestamp
|
||||
set_at_timestamp (int): Set at timestamp
|
||||
reason (str): The reason of the gline.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_set_nick(self, newnickname: str) -> None:
|
||||
"""Change nickname of the server
|
||||
\n This method will also update the User object
|
||||
Args:
|
||||
newnickname (str): New nickname of the server
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_set_mode(self, modes: str, *, nickname: Optional[str] = None, channel_name: Optional[str] = None, params: Optional[str] = None) -> None:
|
||||
"""Set a mode to channel or to a nickname or for a user in a channel
|
||||
|
||||
Args:
|
||||
modes (str): The selected mode
|
||||
nickname (Optional[str]): The nickname
|
||||
channel_name (Optional[str]): The channel name
|
||||
params (Optional[str]): Parameters like password.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_squit(self, server_id: str, server_link: str, reason: str) -> None:
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
server_id (str): _description_
|
||||
server_link (str): _description_
|
||||
reason (str): _description_
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_ungline(self, nickname:str, hostname: str) -> None:
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
nickname (str): _description_
|
||||
hostname (str): _description_
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_kline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None:
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
nickname (str): _description_
|
||||
hostname (str): _description_
|
||||
set_by (str): _description_
|
||||
expire_timestamp (int): _description_
|
||||
set_at_timestamp (int): _description_
|
||||
reason (str): _description_
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_unkline(self, nickname:str, hostname: str) -> None:
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
nickname (str): _description_
|
||||
hostname (str): _description_
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_sjoin(self, channel: str) -> None:
|
||||
"""Server will join a channel with pre defined umodes
|
||||
|
||||
Args:
|
||||
channel (str): Channel to join
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_sapart(self, nick_to_sapart: str, channel_name: str) -> None:
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
nick_to_sapart (str): _description_
|
||||
channel_name (str): _description_
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_sajoin(self, nick_to_sajoin: str, channel_name: str) -> None:
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
nick_to_sajoin (str): _description_
|
||||
channel_name (str): _description_
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_svspart(self, nick_to_part: str, channels: list[str], reason: str) -> None:
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
nick_to_part (str): _description_
|
||||
channels (list[str]): _description_
|
||||
reason (str): _description_
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_svsjoin(self, nick_to_part: str, channels: list[str], keys: list[str]) -> None:
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
nick_to_part (str): _description_
|
||||
channels (list[str]): _description_
|
||||
keys (list[str]): _description_
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_svsmode(self, nickname: str, user_mode: str) -> None:
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
nickname (str): _description_
|
||||
user_mode (str): _description_
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_svs2mode(self, nickname: str, user_mode: str) -> None:
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
nickname (str): _description_
|
||||
user_mode (str): _description_
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_svslogin(self, client_uid: str, user_account: str) -> None:
|
||||
"""Log a client into his account.
|
||||
|
||||
Args:
|
||||
client_uid (str): Client UID
|
||||
user_account (str): The account of the user
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_svslogout(self, client_obj: 'MClient') -> None:
|
||||
"""Logout a client from his account
|
||||
|
||||
Args:
|
||||
client_obj (MClient): The Client UID
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_quit(self, uid: str, reason: str, print_log: bool = True) -> None:
|
||||
"""Send quit message
|
||||
- Delete uid from User object
|
||||
- Delete uid from Reputation object
|
||||
|
||||
Args:
|
||||
uid (str): The UID or the Nickname
|
||||
reason (str): The reason for the quit
|
||||
print_log (bool): If True then print logs
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_uid(self, nickname:str, username: str, hostname: str, uid:str, umodes: str, vhost: str, remote_ip: str, realname: str, print_log: bool = True) -> None:
|
||||
"""Send UID to the server
|
||||
- Insert User to User Object
|
||||
Args:
|
||||
nickname (str): Nickname of the client
|
||||
username (str): Username of the client
|
||||
hostname (str): Hostname of the client you want to create
|
||||
uid (str): UID of the client you want to create
|
||||
umodes (str): umodes of the client you want to create
|
||||
vhost (str): vhost of the client you want to create
|
||||
remote_ip (str): remote_ip of the client you want to create
|
||||
realname (str): realname of the client you want to create
|
||||
print_log (bool, optional): print logs if true. Defaults to True.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_join_chan(self, uidornickname: str, channel: str, password: str = None, print_log: bool = True) -> None:
|
||||
"""Joining a channel
|
||||
|
||||
Args:
|
||||
uidornickname (str): UID or nickname that need to join
|
||||
channel (str): channel to join
|
||||
password (str, optional): The password of the channel to join. Default to None
|
||||
print_log (bool, optional): Write logs. Defaults to True.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_part_chan(self, uidornickname:str, channel: str, print_log: bool = True) -> None:
|
||||
"""Part from a channel
|
||||
|
||||
Args:
|
||||
uidornickname (str): UID or nickname that need to join
|
||||
channel (str): channel to join
|
||||
print_log (bool, optional): Write logs. Defaults to True.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_mode_chan(self, channel_name: str, channel_mode: str) -> None:
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
channel_name (str): _description_
|
||||
channel_mode (str): _description_
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_raw(self, raw_command: str) -> None:
|
||||
"""Send raw message to the server
|
||||
|
||||
Args:
|
||||
raw_command (str): The raw command you want to send.
|
||||
"""
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# COMMON IRC PARSER
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
@abstractmethod
|
||||
def parse_uid(self, server_msg: list[str]) -> Optional['MUser']:
|
||||
"""Parse UID and return dictionary.
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): The UID IRCD message
|
||||
|
||||
Returns:
|
||||
Optional[MUser]: The MUser object or None
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def parse_quit(self, server_msg: list[str]) -> tuple[Optional['MUser'], str]:
|
||||
"""Parse quit and return dictionary.
|
||||
>>> [':97KAAAAAB', 'QUIT', ':Quit:', 'this', 'is', 'my', 'reason', 'to', 'quit']
|
||||
Args:
|
||||
server_msg (list[str]): The server message to parse
|
||||
|
||||
Returns:
|
||||
tuple[MUser, str]: The User Who Quit Object and the reason.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def parse_nick(self, server_msg: list[str]) -> tuple[Optional['MUser'], str, str]:
|
||||
"""Parse nick changes and return dictionary.
|
||||
>>> [':97KAAAAAC', 'NICK', 'testinspir', '1757360740']
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): The server message to parse
|
||||
|
||||
Returns:
|
||||
tuple(MUser, newnickname(str), timestamp(str)): Tuple of the response.
|
||||
|
||||
>>> MUser, newnickname, timestamp
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def parse_privmsg(self, server_msg: list[str]) -> tuple[Optional['MUser'], Optional['MUser'], Optional['MChannel'], str]:
|
||||
"""Parse PRIVMSG message.
|
||||
>>> [':97KAAAAAE', 'PRIVMSG', '#welcome', ':This', 'is', 'my', 'public', 'message']
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): The server message to parse
|
||||
|
||||
Returns:
|
||||
tuple[MUser(Sender), MUser(Reciever), MChannel, str]: Sender user model, reciever user model, Channel model, messgae.
|
||||
"""
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# EVENT HANDLER
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
@abstractmethod
|
||||
def on_svs2mode(self, server_msg: list[str]) -> None:
|
||||
"""Handle svs2mode coming from a server
|
||||
>>> [':00BAAAAAG', 'SVS2MODE', '001U01R03', '-r']
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): Original server message
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_mode(self, server_msg: list[str]) -> None:
|
||||
"""Handle mode coming from a server
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): Original server message
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_umode2(self, server_msg: list[str]) -> None:
|
||||
"""Handle umode2 coming from a server
|
||||
>>> [':adator_', 'UMODE2', '-i']
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): Original server message
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_quit(self, server_msg: list[str]) -> None:
|
||||
"""Handle quit coming from a server
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): Original server message
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_squit(self, server_msg: list[str]) -> None:
|
||||
"""Handle squit coming from a server
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): Original server message
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_protoctl(self, server_msg: list[str]) -> None:
|
||||
"""Handle protoctl coming from a server
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): Original server message
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_nick(self, server_msg: list[str]) -> None:
|
||||
"""Handle nick coming from a server
|
||||
new nickname
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): Original server message
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_sjoin(self, server_msg: list[str]) -> None:
|
||||
"""Handle sjoin coming from a server
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): Original server message
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_part(self, server_msg: list[str]) -> None:
|
||||
"""Handle part coming from a server
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): Original server message
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_eos(self, server_msg: list[str]) -> None:
|
||||
"""Handle EOS coming from a server
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): Original server message
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_reputation(self, server_msg: list[str]) -> None:
|
||||
"""Handle REPUTATION coming from a server
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): Original server message
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_uid(self, server_msg: list[str]) -> None:
|
||||
"""Handle uid message coming from the server
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): Original server message
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_privmsg(self, server_msg: list[str]) -> None:
|
||||
"""Handle PRIVMSG message coming from the server
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): Original server message
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_server_ping(self, server_msg: list[str]) -> None:
|
||||
"""Send a PONG message to the server
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): List of str coming from the server
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_server(self, server_msg: list[str]) -> None:
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): _description_
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_version(self, server_msg: list[str]) -> None:
|
||||
"""Sending Server Version to the server
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): List of str coming from the server
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_time(self, server_msg: list[str]) -> None:
|
||||
"""Sending TIME answer to a requestor
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): List of str coming from the server
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_ping(self, server_msg: list[str]) -> None:
|
||||
"""Sending a PING answer to requestor
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): List of str coming from the server
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_version_msg(self, server_msg: list[str]) -> None:
|
||||
"""Handle version coming from the server
|
||||
\n ex. /version Defender
|
||||
Args:
|
||||
server_msg (list[str]): Original message from the server
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_smod(self, server_msg: list[str]) -> None:
|
||||
"""Handle SMOD message coming from the server
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): Original server message
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_sasl(self, server_msg: list[str]) -> Optional['MSasl']:
|
||||
"""Handle SASL coming from a server
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): Original server message
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_sasl_authentication_process(self, sasl_model: 'MSasl') -> bool:
|
||||
"""Finalize sasl authentication
|
||||
|
||||
Args:
|
||||
sasl_model (MSasl): The sasl dataclass model
|
||||
|
||||
Returns:
|
||||
bool: True if success
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_md(self, server_msg: list[str]) -> None:
|
||||
"""Handle MD responses
|
||||
[':001', 'MD', 'client', '001MYIZ03', 'certfp', ':d1235648...']
|
||||
Args:
|
||||
server_msg (list[str]): The server reply
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_kick(self, server_msg: list[str]) -> None:
|
||||
"""When a user is kicked out from a channel
|
||||
|
||||
Eg. ['@unrealircd.org...', ':001', 'KICK', '#jsonrpc', '001ELW13T', ':Kicked', 'from', 'JSONRPC', 'User']
|
||||
Args:
|
||||
server_msg (list[str]): The server message
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def on_sethost(self, server_msg: list[str]) -> None:
|
||||
"""On SETHOST command
|
||||
>>> [':001DN7305', 'SETHOST', ':netadmin.example.org']
|
||||
|
||||
Args:
|
||||
server_msg (list[str]): _description_
|
||||
"""
|
||||
@@ -9,7 +9,17 @@ class Admin:
|
||||
UID_ADMIN_DB: list[MAdmin] = []
|
||||
|
||||
def __init__(self, loader: 'Loader') -> None:
|
||||
"""
|
||||
|
||||
Args:
|
||||
loader (Loader): The Loader Instance.
|
||||
"""
|
||||
self.Logs = loader.Logs
|
||||
self.Base = loader.Base
|
||||
self.Setting = loader.Settings
|
||||
self.Config = loader.Config
|
||||
self.User = loader.User
|
||||
self.Definition = loader.Definition
|
||||
|
||||
def insert(self, new_admin: MAdmin) -> bool:
|
||||
"""Insert a new admin object model
|
||||
@@ -83,17 +93,10 @@ class Admin:
|
||||
Returns:
|
||||
bool: True if the admin has been deleted
|
||||
"""
|
||||
|
||||
for record in self.UID_ADMIN_DB:
|
||||
if record.uid == uidornickname:
|
||||
# If the admin exist, delete and do not go further
|
||||
self.UID_ADMIN_DB.remove(record)
|
||||
self.Logs.debug(f'UID ({record.uid}) has been deleted')
|
||||
return True
|
||||
if record.nickname.lower() == uidornickname.lower():
|
||||
# If the admin exist, delete and do not go further
|
||||
self.UID_ADMIN_DB.remove(record)
|
||||
self.Logs.debug(f'nickname ({record.nickname}) has been deleted')
|
||||
admin_obj = self.get_admin(uidornickname)
|
||||
if admin_obj:
|
||||
self.UID_ADMIN_DB.remove(admin_obj)
|
||||
self.Logs.debug(f'UID ({admin_obj.uid}) has been deleted')
|
||||
return True
|
||||
|
||||
self.Logs.debug(f'The UID {uidornickname} was not deleted')
|
||||
@@ -153,3 +156,69 @@ class Admin:
|
||||
return record.nickname
|
||||
|
||||
return None
|
||||
|
||||
def get_language(self, uidornickname: str) -> Optional[str]:
|
||||
"""Get the language of the admin
|
||||
|
||||
Args:
|
||||
uidornickname (str): The user ID or the Nickname of the admin
|
||||
|
||||
Returns:
|
||||
Optional[str]: The language selected by the admin.
|
||||
"""
|
||||
admin = self.get_admin(uidornickname)
|
||||
|
||||
if admin is None:
|
||||
return None
|
||||
|
||||
return admin.language
|
||||
|
||||
def db_auth_admin_via_fingerprint(self, fp: str, uidornickname: str) -> bool:
|
||||
"""Check the fingerprint
|
||||
|
||||
Args:
|
||||
fp (str): The unique fingerprint of the user
|
||||
uidornickname (str): The UID or the Nickname of the user
|
||||
|
||||
Returns:
|
||||
bool: True if found
|
||||
"""
|
||||
if fp is None:
|
||||
return False
|
||||
|
||||
query = f"SELECT user, level, language FROM {self.Config.TABLE_ADMIN} WHERE fingerprint = :fp"
|
||||
data = {'fp': fp}
|
||||
exe = self.Base.db_execute_query(query, data)
|
||||
result = exe.fetchone()
|
||||
if result:
|
||||
account = result[0]
|
||||
level = result[1]
|
||||
language = result[2]
|
||||
user_obj = self.User.get_user(uidornickname)
|
||||
if user_obj:
|
||||
admin_obj = self.Definition.MAdmin(**user_obj.to_dict(), account=account, level=level, language=language)
|
||||
if self.insert(admin_obj):
|
||||
self.Setting.current_admin = admin_obj
|
||||
self.Logs.debug(f"[Fingerprint login] {user_obj.nickname} ({admin_obj.account}) has been logged in successfully!")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def db_is_admin_exist(self, admin_nickname: str) -> bool:
|
||||
"""Verify if the admin exist in the database!
|
||||
|
||||
Args:
|
||||
admin_nickname (str): The nickname admin to check.
|
||||
|
||||
Returns:
|
||||
bool: True if the admin exist otherwise False.
|
||||
"""
|
||||
|
||||
mes_donnees = {'admin': admin_nickname}
|
||||
query_search_user = f"SELECT id FROM {self.Config.TABLE_ADMIN} WHERE user = :admin"
|
||||
r = self.Base.db_execute_query(query_search_user, mes_donnees)
|
||||
exist_user = r.fetchone()
|
||||
if exist_user:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -11,14 +11,16 @@ class Channel:
|
||||
"""List that contains all the Channels objects (ChannelModel)
|
||||
"""
|
||||
|
||||
def __init__(self, loader: 'Loader') -> None:
|
||||
def __init__(self, loader: 'Loader'):
|
||||
"""
|
||||
|
||||
Args:
|
||||
loader (Loader): The Loader Instance
|
||||
"""
|
||||
self.Logs = loader.Logs
|
||||
self.Base = loader.Base
|
||||
self.Utils = loader.Utils
|
||||
|
||||
return None
|
||||
|
||||
def insert(self, new_channel: 'MChannel') -> bool:
|
||||
"""This method will insert a new channel and if the channel exist it will update the user list (uids)
|
||||
|
||||
@@ -110,6 +112,7 @@ class Channel:
|
||||
return result
|
||||
except ValueError as ve:
|
||||
self.Logs.error(f'{ve}')
|
||||
return False
|
||||
|
||||
def delete_user_from_all_channel(self, uid:str) -> bool:
|
||||
"""Delete a client from all channels
|
||||
@@ -134,6 +137,7 @@ class Channel:
|
||||
return result
|
||||
except ValueError as ve:
|
||||
self.Logs.error(f'{ve}')
|
||||
return False
|
||||
|
||||
def add_user_to_a_channel(self, channel_name: str, uid: str) -> bool:
|
||||
"""Add a client to a channel
|
||||
@@ -211,15 +215,6 @@ class Channel:
|
||||
|
||||
return None
|
||||
|
||||
def get_channel_asdict(self, channel_name: str) -> Optional[dict[str, Any]]:
|
||||
|
||||
channel_obj: Optional['MChannel'] = self.get_channel(channel_name)
|
||||
|
||||
if channel_obj is None:
|
||||
return None
|
||||
|
||||
return channel_obj.to_dict()
|
||||
|
||||
def is_valid_channel(self, channel_to_check: str) -> bool:
|
||||
"""Check if the string has the # caractere and return True if this is a valid channel
|
||||
|
||||
@@ -235,16 +230,18 @@ class Channel:
|
||||
return False
|
||||
|
||||
pattern = fr'^#'
|
||||
isChannel = findall(pattern, channel_to_check)
|
||||
is_channel = findall(pattern, channel_to_check)
|
||||
|
||||
if not isChannel:
|
||||
if not is_channel:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
except TypeError as te:
|
||||
self.Logs.error(f'TypeError: [{channel_to_check}] - {te}')
|
||||
return False
|
||||
except Exception as err:
|
||||
self.Logs.error(f'Error Not defined: {err}')
|
||||
return False
|
||||
|
||||
def db_query_channel(self, action: Literal['add','del'], module_name: str, channel_name: str) -> bool:
|
||||
"""You can add a channel or delete a channel.
|
||||
@@ -276,7 +273,7 @@ class Channel:
|
||||
mes_donnees = {'datetime': self.Utils.get_sdatetime(), 'channel_name': channel_name, 'module_name': module_name}
|
||||
insert = self.Base.db_execute_query(f"INSERT INTO {core_table} (datetime, channel_name, module_name) VALUES (:datetime, :channel_name, :module_name)", mes_donnees)
|
||||
if insert.rowcount:
|
||||
self.Logs.debug(f'New channel added: channel={channel_name} / module_name={module_name}')
|
||||
self.Logs.debug(f'Channel added to DB: channel={channel_name} / module_name={module_name}')
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -286,13 +283,12 @@ class Channel:
|
||||
response = self.Base.db_execute_query(f"DELETE FROM {core_table} WHERE channel_name = :channel_name AND module_name = :module_name", mes_donnes)
|
||||
|
||||
if response.rowcount > 0:
|
||||
self.Logs.debug(f'Channel deleted: channel={channel_name} / module: {module_name}')
|
||||
self.Logs.debug(f'Channel deleted from DB: channel={channel_name} / module: {module_name}')
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
case _:
|
||||
return False
|
||||
|
||||
except Exception as err:
|
||||
self.Logs.error(err)
|
||||
return False
|
||||
@@ -10,7 +10,11 @@ class Client:
|
||||
CLIENT_DB: list['MClient'] = []
|
||||
|
||||
def __init__(self, loader: 'Loader'):
|
||||
"""
|
||||
|
||||
Args:
|
||||
loader (Loader): The Loader instance.
|
||||
"""
|
||||
self.Logs = loader.Logs
|
||||
self.Base = loader.Base
|
||||
|
||||
@@ -34,12 +38,12 @@ class Client:
|
||||
|
||||
return True
|
||||
|
||||
def update_nickname(self, uid: str, newNickname: str) -> bool:
|
||||
def update_nickname(self, uid: str, new_nickname: str) -> bool:
|
||||
"""Update the nickname starting from the UID
|
||||
|
||||
Args:
|
||||
uid (str): UID of the user
|
||||
newNickname (str): New nickname
|
||||
new_nickname (str): New nickname
|
||||
|
||||
Returns:
|
||||
bool: True if updated
|
||||
@@ -49,7 +53,7 @@ class Client:
|
||||
if user_obj is None:
|
||||
return False
|
||||
|
||||
user_obj.nickname = newNickname
|
||||
user_obj.nickname = new_nickname
|
||||
|
||||
return True
|
||||
|
||||
@@ -181,7 +185,7 @@ class Client:
|
||||
|
||||
return client_obj.to_dict()
|
||||
|
||||
def is_exist(self, uidornikname: str) -> bool:
|
||||
def is_exist(self, uidornickname: str) -> bool:
|
||||
"""Check if the UID or the nickname exist in the USER DB
|
||||
|
||||
Args:
|
||||
@@ -190,7 +194,7 @@ class Client:
|
||||
Returns:
|
||||
bool: True if exist
|
||||
"""
|
||||
user_obj = self.get_Client(uidornickname=uidornikname)
|
||||
user_obj = self.get_Client(uidornickname=uidornickname)
|
||||
|
||||
if user_obj is None:
|
||||
return False
|
||||
@@ -231,9 +235,9 @@ class Client:
|
||||
"""
|
||||
|
||||
pattern = fr'[:|@|%|\+|~|\*]*'
|
||||
parsed_UID = sub(pattern, '', uid)
|
||||
parsed_uid = sub(pattern, '', uid)
|
||||
|
||||
if not parsed_UID:
|
||||
if not parsed_uid:
|
||||
return None
|
||||
|
||||
return parsed_UID
|
||||
return parsed_uid
|
||||
@@ -9,8 +9,13 @@ class Command:
|
||||
DB_COMMANDS: list['MCommand'] = []
|
||||
|
||||
def __init__(self, loader: 'Loader'):
|
||||
"""
|
||||
Args:
|
||||
loader (Loader): The Loader instance.
|
||||
"""
|
||||
self.Loader = loader
|
||||
self.Base = loader.Base
|
||||
self.Logs = loader.Logs
|
||||
|
||||
def build(self, new_command_obj: MCommand) -> bool:
|
||||
|
||||
@@ -31,7 +36,7 @@ class Command:
|
||||
def get_command(self, command_name: str, module_name: str) -> Optional[MCommand]:
|
||||
|
||||
for command in self.DB_COMMANDS:
|
||||
if command.command_name.lower() == command_name and command.module_name == module_name:
|
||||
if command.command_name.lower() == command_name.lower() and command.module_name.lower() == module_name.lower():
|
||||
return command
|
||||
|
||||
return None
|
||||
@@ -45,6 +50,27 @@ class Command:
|
||||
|
||||
return False
|
||||
|
||||
def drop_command_by_module(self, module_name: str) -> bool:
|
||||
"""Drop all command by module
|
||||
|
||||
Args:
|
||||
module_name (str): The module name
|
||||
|
||||
Returns:
|
||||
bool: True
|
||||
"""
|
||||
tmp_model: list[MCommand] = []
|
||||
|
||||
for command in self.DB_COMMANDS:
|
||||
if command.module_name.lower() == module_name.lower():
|
||||
tmp_model.append(command)
|
||||
|
||||
for c in tmp_model:
|
||||
self.DB_COMMANDS.remove(c)
|
||||
|
||||
self.Logs.debug(f"[COMMAND] Drop command for module {module_name}")
|
||||
return True
|
||||
|
||||
def get_ordered_commands(self) -> list[MCommand]:
|
||||
return sorted(self.DB_COMMANDS, key=lambda c: (c.command_level, c.module_name))
|
||||
|
||||
@@ -64,7 +90,7 @@ class Command:
|
||||
admin_level = admin.level if admin else 0
|
||||
commands = self.get_commands_by_level(admin_level)
|
||||
|
||||
if command_name in [command.command_name for command in commands]:
|
||||
if command_name.lower() in [command.command_name.lower() for command in commands]:
|
||||
return True
|
||||
|
||||
return False
|
||||
60
core/classes/modules/config.py
Normal file
60
core/classes/modules/config.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import sys
|
||||
import yaml
|
||||
from json import load
|
||||
from sys import exit
|
||||
from os import sep
|
||||
from typing import Any, Optional, Union, TYPE_CHECKING
|
||||
from core.definition import MConfig
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.loader import Loader
|
||||
|
||||
class Configuration:
|
||||
|
||||
def __init__(self, loader: 'Loader') -> None:
|
||||
|
||||
self.Loader = loader
|
||||
self.Logs = loader.Logs
|
||||
self.configuration_model = self.__load_service_configuration()
|
||||
loader.ServiceLogging.set_file_handler_level(self._config_model.DEBUG_LEVEL)
|
||||
loader.ServiceLogging.set_stdout_handler_level(self._config_model.DEBUG_LEVEL)
|
||||
loader.ServiceLogging.update_handler_format(self._config_model.DEBUG_HARD)
|
||||
return None
|
||||
|
||||
@property
|
||||
def configuration_model(self) -> MConfig:
|
||||
return self._config_model
|
||||
|
||||
@configuration_model.setter
|
||||
def configuration_model(self, conf_model: MConfig):
|
||||
self._config_model = conf_model
|
||||
|
||||
def __load_config_file(self) -> Optional[dict[str, Any]]:
|
||||
try:
|
||||
conf_filename = f'config{sep}configuration.yaml'
|
||||
with open(conf_filename, 'r') as conf:
|
||||
configuration: dict[str, dict[str, Any]] = yaml.safe_load(conf)
|
||||
|
||||
return configuration.get('configuration', None)
|
||||
except FileNotFoundError as fe:
|
||||
self.Logs.error(f'FileNotFound: {fe}')
|
||||
self.Logs.error('Configuration file not found please create config/configuration.yaml')
|
||||
exit("Configuration file not found please create config/configuration.yaml")
|
||||
|
||||
def __load_service_configuration(self) -> MConfig:
|
||||
try:
|
||||
import_config = self.__load_config_file()
|
||||
if import_config is None:
|
||||
self.Logs.error("Error While importing configuration file!", exc_info=True)
|
||||
raise Exception("Error While importing yaml configuration")
|
||||
|
||||
list_key_to_remove: list[str] = [key_to_del for key_to_del in import_config if key_to_del not in MConfig().get_attributes()]
|
||||
for key_to_remove in list_key_to_remove:
|
||||
import_config.pop(key_to_remove, None)
|
||||
self.Logs.warning(f"[!] The key {key_to_remove} is not expected, it has been removed from the system ! please remove it from configuration.json file [!]")
|
||||
|
||||
self.Logs.debug(f"[LOADING CONFIGURATION]: Loading configuration with {len(import_config)} parameters!")
|
||||
return MConfig(**import_config)
|
||||
|
||||
except TypeError as te:
|
||||
self.Logs.error(te)
|
||||
@@ -3,7 +3,6 @@ import sys
|
||||
import time
|
||||
from typing import TYPE_CHECKING
|
||||
import socket
|
||||
from core.classes.protocol import Protocol
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
@@ -12,35 +11,34 @@ if TYPE_CHECKING:
|
||||
REHASH_MODULES = [
|
||||
'core.definition',
|
||||
'core.utils',
|
||||
'core.classes.config',
|
||||
'core.classes.modules.config',
|
||||
'core.base',
|
||||
'core.classes.commands',
|
||||
'core.classes.modules.commands',
|
||||
'core.classes.modules.rpc',
|
||||
'core.classes.interfaces.iprotocol',
|
||||
'core.classes.interfaces.imodule',
|
||||
'core.classes.protocols.command_handler',
|
||||
'core.classes.protocols.factory',
|
||||
'core.classes.protocols.unreal6',
|
||||
'core.classes.protocols.inspircd',
|
||||
'core.classes.protocol'
|
||||
'core.classes.protocols.inspircd'
|
||||
]
|
||||
|
||||
|
||||
def restart_service(uplink: 'Irc', reason: str = "Restarting with no reason!") -> None:
|
||||
"""
|
||||
|
||||
Args:
|
||||
uplink (Irc): The Irc instance
|
||||
reason (str): The reason of the restart.
|
||||
"""
|
||||
# reload modules.
|
||||
for module in uplink.ModuleUtils.model_get_loaded_modules().copy():
|
||||
uplink.ModuleUtils.unload_one_module(uplink, module.module_name)
|
||||
|
||||
uplink.ModuleUtils.model_clear() # Clear loaded modules.
|
||||
uplink.User.UID_DB.clear() # Clear User Object
|
||||
uplink.Channel.UID_CHANNEL_DB.clear() # Clear Channel Object
|
||||
uplink.Client.CLIENT_DB.clear() # Clear Client object
|
||||
uplink.Base.garbage_collector_thread()
|
||||
|
||||
# Reload configuration
|
||||
uplink.Config = uplink.Loader.ConfModule.Configuration(uplink.Loader).get_config_model()
|
||||
uplink.Base = uplink.Loader.BaseModule.Base(uplink.Loader)
|
||||
uplink.Protocol = Protocol(uplink.Config.SERVEUR_PROTOCOL, uplink.ircObject).Protocol
|
||||
uplink.Logs.debug(f'[{uplink.Config.SERVICE_NICKNAME} RESTART]: Reloading configuration!')
|
||||
|
||||
uplink.Protocol.send_squit(server_id=uplink.Config.SERVEUR_ID, server_link=uplink.Config.SERVEUR_LINK, reason="Defender Power off")
|
||||
|
||||
uplink.Protocol.send_squit(server_id=uplink.Config.SERVEUR_ID, server_link=uplink.Config.SERVEUR_LINK, reason=reason)
|
||||
uplink.Logs.debug('Restarting Defender ...')
|
||||
uplink.IrcSocket.shutdown(socket.SHUT_RDWR)
|
||||
uplink.IrcSocket.close()
|
||||
@@ -49,16 +47,31 @@ def restart_service(uplink: 'Irc', reason: str = "Restarting with no reason!") -
|
||||
time.sleep(0.5)
|
||||
uplink.Logs.warning('-- Waiting for socket to close ...')
|
||||
|
||||
# Reload configuration
|
||||
uplink.Loader.Config = uplink.Loader.ConfModule.Configuration(uplink.Loader).configuration_model
|
||||
uplink.Loader.Base = uplink.Loader.BaseModule.Base(uplink.Loader)
|
||||
|
||||
for mod in REHASH_MODULES:
|
||||
importlib.reload(sys.modules[mod])
|
||||
|
||||
uplink.Protocol = uplink.Loader.PFactory.get()
|
||||
uplink.Protocol.register_command()
|
||||
|
||||
uplink.ModuleUtils.model_clear() # Clear loaded modules.
|
||||
uplink.User.UID_DB.clear() # Clear User Object
|
||||
uplink.Channel.UID_CHANNEL_DB.clear() # Clear Channel Object
|
||||
uplink.Client.CLIENT_DB.clear() # Clear Client object
|
||||
|
||||
uplink.init_service_user()
|
||||
uplink.Utils.create_socket(uplink)
|
||||
uplink.Protocol.send_link()
|
||||
uplink.join_saved_channels()
|
||||
uplink.ModuleUtils.db_load_all_existing_modules(uplink)
|
||||
uplink.Config.DEFENDER_RESTART = 0
|
||||
|
||||
def rehash_service(uplink: 'Irc', nickname: str) -> None:
|
||||
need_a_restart = ["SERVEUR_ID"]
|
||||
uplink.Settings.set_cache('db_commands', uplink.Commands.DB_COMMANDS)
|
||||
uplink.Loader.RpcServer.stop_server()
|
||||
|
||||
restart_flag = False
|
||||
config_model_bakcup = uplink.Config
|
||||
mods = REHASH_MODULES
|
||||
@@ -69,8 +82,8 @@ def rehash_service(uplink: 'Irc', nickname: str) -> None:
|
||||
msg=f'[REHASH] Module [{mod}] reloaded',
|
||||
channel=uplink.Config.SERVICE_CHANLOG
|
||||
)
|
||||
|
||||
uplink.Config = uplink.Loader.ConfModule.Configuration(uplink.Loader).get_config_model()
|
||||
uplink.Utils = sys.modules['core.utils']
|
||||
uplink.Config = uplink.Loader.Config = uplink.Loader.ConfModule.Configuration(uplink.Loader).configuration_model
|
||||
uplink.Config.HSID = config_model_bakcup.HSID
|
||||
uplink.Config.DEFENDER_INIT = config_model_bakcup.DEFENDER_INIT
|
||||
uplink.Config.DEFENDER_RESTART = config_model_bakcup.DEFENDER_RESTART
|
||||
@@ -103,10 +116,13 @@ def rehash_service(uplink: 'Irc', nickname: str) -> None:
|
||||
|
||||
# Reload Main Commands Module
|
||||
uplink.Commands = uplink.Loader.CommandModule.Command(uplink.Loader)
|
||||
uplink.Loader.RpcServer = uplink.Loader.RpcServerModule.JSONRPCServer(uplink.Loader)
|
||||
uplink.Loader.RpcServer.start_server()
|
||||
uplink.Commands.DB_COMMANDS = uplink.Settings.get_cache('db_commands')
|
||||
|
||||
uplink.Base = uplink.Loader.BaseModule.Base(uplink.Loader)
|
||||
uplink.Protocol = Protocol(uplink.Config.SERVEUR_PROTOCOL, uplink.ircObject).Protocol
|
||||
uplink.Loader.Base = uplink.Loader.BaseModule.Base(uplink.Loader)
|
||||
uplink.Protocol = uplink.Loader.PFactory.get()
|
||||
uplink.Protocol.register_command()
|
||||
|
||||
# Reload Service modules
|
||||
for module in uplink.ModuleUtils.model_get_loaded_modules().copy():
|
||||
@@ -9,9 +9,14 @@ class Reputation:
|
||||
UID_REPUTATION_DB: list[MReputation] = []
|
||||
|
||||
def __init__(self, loader: 'Loader'):
|
||||
"""
|
||||
|
||||
Args:
|
||||
loader (Loader): The Loader instance.
|
||||
"""
|
||||
|
||||
self.Logs = loader.Logs
|
||||
self.MReputation: MReputation = MReputation
|
||||
self.MReputation: Optional[MReputation] = None
|
||||
|
||||
def insert(self, new_reputation_user: MReputation) -> bool:
|
||||
"""Insert a new Reputation User object
|
||||
@@ -47,13 +52,13 @@ class Reputation:
|
||||
|
||||
Args:
|
||||
uid (str): UID of the user
|
||||
newNickname (str): New nickname
|
||||
new_nickname (str): New nickname
|
||||
|
||||
Returns:
|
||||
bool: True if updated
|
||||
"""
|
||||
|
||||
reputation_obj = self.get_Reputation(uid)
|
||||
reputation_obj = self.get_reputation(uid)
|
||||
|
||||
if reputation_obj is None:
|
||||
return False
|
||||
@@ -89,7 +94,7 @@ class Reputation:
|
||||
|
||||
return result
|
||||
|
||||
def get_Reputation(self, uidornickname: str) -> Optional[MReputation]:
|
||||
def get_reputation(self, uidornickname: str) -> Optional[MReputation]:
|
||||
"""Get The User Object model
|
||||
|
||||
Args:
|
||||
@@ -116,7 +121,7 @@ class Reputation:
|
||||
str|None: Return the UID
|
||||
"""
|
||||
|
||||
reputation_obj = self.get_Reputation(uidornickname)
|
||||
reputation_obj = self.get_reputation(uidornickname)
|
||||
|
||||
if reputation_obj is None:
|
||||
return None
|
||||
@@ -132,7 +137,7 @@ class Reputation:
|
||||
Returns:
|
||||
str|None: the nickname
|
||||
"""
|
||||
reputation_obj = self.get_Reputation(uidornickname)
|
||||
reputation_obj = self.get_reputation(uidornickname)
|
||||
|
||||
if reputation_obj is None:
|
||||
return None
|
||||
@@ -149,7 +154,7 @@ class Reputation:
|
||||
bool: True if exist
|
||||
"""
|
||||
|
||||
reputation_obj = self.get_Reputation(uidornickname)
|
||||
reputation_obj = self.get_reputation(uidornickname)
|
||||
|
||||
if isinstance(reputation_obj, MReputation):
|
||||
return True
|
||||
240
core/classes/modules/rpc/rpc.py
Normal file
240
core/classes/modules/rpc/rpc.py
Normal file
@@ -0,0 +1,240 @@
|
||||
import base64
|
||||
import json
|
||||
import logging
|
||||
from enum import Enum
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
from typing import TYPE_CHECKING, Any, Optional
|
||||
from core.classes.modules.rpc.rpc_user import RPCUser
|
||||
from core.classes.modules.rpc.rpc_channel import RPCChannel
|
||||
from core.classes.modules.rpc.rpc_command import RPCCommand
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.loader import Loader
|
||||
|
||||
ProxyLoader: Optional['Loader'] = None
|
||||
|
||||
class RPCRequestHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def log_message(self, format, *args):
|
||||
pass
|
||||
|
||||
def do_POST(self):
|
||||
logs = ProxyLoader.Logs
|
||||
self.server_version = 'Defender6'
|
||||
self.sys_version = ProxyLoader.Config.CURRENT_VERSION
|
||||
content_length = int(self.headers['Content-Length'])
|
||||
body = self.rfile.read(content_length)
|
||||
request_data: dict = json.loads(body)
|
||||
rip, rport = self.client_address
|
||||
|
||||
if not self.authenticate(request_data):
|
||||
return None
|
||||
|
||||
response_data = {
|
||||
'jsonrpc': '2.0',
|
||||
'id': request_data.get('id', 123)
|
||||
}
|
||||
|
||||
method = request_data.get("method")
|
||||
params: dict[str, Any] = request_data.get("params", {})
|
||||
response_data['method'] = method
|
||||
http_code = 200
|
||||
|
||||
match method:
|
||||
case 'user.list':
|
||||
user = RPCUser(ProxyLoader)
|
||||
response_data['result'] = user.user_list()
|
||||
logs.debug(f'[RPC] {method} recieved from {rip}:{rport}')
|
||||
del user
|
||||
|
||||
case 'user.get':
|
||||
user = RPCUser(ProxyLoader)
|
||||
uid_or_nickname = params.get('uid_or_nickname', None)
|
||||
response_data['result'] = user.user_get(uid_or_nickname)
|
||||
logs.debug(f'[RPC] {method} recieved from {rip}:{rport}')
|
||||
del user
|
||||
|
||||
case 'channel.list':
|
||||
channel = RPCChannel(ProxyLoader)
|
||||
response_data['result'] = channel.channel_list()
|
||||
logs.debug(f'[RPC] {method} recieved from {rip}:{rport}')
|
||||
del channel
|
||||
|
||||
case 'command.list':
|
||||
command = RPCCommand(ProxyLoader)
|
||||
response_data['result'] = command.command_list()
|
||||
logs.debug(f'[RPC] {method} recieved from {rip}:{rport}')
|
||||
del command
|
||||
|
||||
case 'command.get.by.module':
|
||||
command = RPCCommand(ProxyLoader)
|
||||
module_name = params.get('name', None)
|
||||
response_data['result'] = command.command_get_by_module(module_name)
|
||||
logs.debug(f'[RPC] {method} recieved from {rip}:{rport}')
|
||||
del command
|
||||
|
||||
case 'command.get.by.name':
|
||||
command = RPCCommand(ProxyLoader)
|
||||
command_name = params.get('name', None)
|
||||
response_data['result'] = command.command_get_by_name(command_name)
|
||||
logs.debug(f'[RPC] {method} recieved from {rip}:{rport}')
|
||||
del command
|
||||
|
||||
case _:
|
||||
response_data['error'] = create_error_response(JSONRPCErrorCode.METHOD_NOT_FOUND)
|
||||
logs.debug(f'[RPC ERROR] {method} recieved from {rip}:{rport}')
|
||||
http_code = 404
|
||||
|
||||
self.send_response(http_code)
|
||||
self.send_header('Content-Type', 'application/json')
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps(response_data).encode('utf-8'))
|
||||
|
||||
return None
|
||||
|
||||
def do_GET(self):
|
||||
self.server_version = 'Defender6'
|
||||
self.sys_version = ProxyLoader.Config.CURRENT_VERSION
|
||||
content_length = int(self.headers['Content-Length'])
|
||||
body = self.rfile.read(content_length)
|
||||
request_data: dict = json.loads(body)
|
||||
|
||||
if not self.authenticate(request_data):
|
||||
return None
|
||||
|
||||
response_data = {'jsonrpc': '2.0', 'id': request_data.get('id', 321),
|
||||
'error': create_error_response(JSONRPCErrorCode.INVALID_REQUEST)}
|
||||
|
||||
self.send_response(404)
|
||||
self.send_header('Content-Type', 'application/json')
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps(response_data).encode('utf-8'))
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def authenticate(self, request_data: dict) -> bool:
|
||||
logs = ProxyLoader.Logs
|
||||
auth = self.headers.get('Authorization', None)
|
||||
if auth is None:
|
||||
self.send_auth_error(request_data)
|
||||
return False
|
||||
|
||||
# Authorization header format: Basic base64(username:password)
|
||||
auth_type, auth_string = auth.split(' ', 1)
|
||||
if auth_type.lower() != 'basic':
|
||||
self.send_auth_error(request_data)
|
||||
return False
|
||||
|
||||
try:
|
||||
# Decode the base64-encoded username:password
|
||||
decoded_credentials = base64.b64decode(auth_string).decode('utf-8')
|
||||
username, password = decoded_credentials.split(":", 1)
|
||||
|
||||
# Check the username and password.
|
||||
for rpcuser in ProxyLoader.Irc.Config.RPC_USERS:
|
||||
if rpcuser.get('USERNAME', None) == username and rpcuser.get('PASSWORD', None) == password:
|
||||
return True
|
||||
|
||||
self.send_auth_error(request_data)
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
self.send_auth_error(request_data)
|
||||
logs.error(e)
|
||||
return False
|
||||
|
||||
def send_auth_error(self, request_data: dict) -> None:
|
||||
|
||||
response_data = {
|
||||
'jsonrpc': '2.0',
|
||||
'id': request_data.get('id', 123),
|
||||
'error': create_error_response(JSONRPCErrorCode.AUTHENTICATION_ERROR)
|
||||
}
|
||||
|
||||
self.send_response(401)
|
||||
self.send_header('WWW-Authenticate', 'Basic realm="Authorization Required"')
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps(response_data).encode('utf-8'))
|
||||
|
||||
class JSONRPCServer:
|
||||
def __init__(self, loader: 'Loader'):
|
||||
global ProxyLoader
|
||||
|
||||
ProxyLoader = loader
|
||||
self._Loader = loader
|
||||
self._Base = loader.Base
|
||||
self._Logs = loader.Logs
|
||||
self.rpc_server: Optional[HTTPServer] = None
|
||||
self.connected: bool = False
|
||||
|
||||
def start_server(self, server_class=HTTPServer, handler_class=RPCRequestHandler, *, hostname: str = 'localhost', port: int = 5000):
|
||||
logging.getLogger('http.server').setLevel(logging.CRITICAL)
|
||||
server_address = (hostname, port)
|
||||
self.rpc_server = server_class(server_address, handler_class)
|
||||
self._Logs.debug(f"Server ready on http://{hostname}:{port}...")
|
||||
self._Base.create_thread(self.thread_start_rpc_server, (), True)
|
||||
|
||||
def thread_start_rpc_server(self) -> None:
|
||||
self._Loader.Irc.Protocol.send_priv_msg(
|
||||
self._Loader.Config.SERVICE_NICKNAME, "Defender RPC Server has started successfuly!", self._Loader.Config.SERVICE_CHANLOG
|
||||
)
|
||||
self.connected = True
|
||||
self.rpc_server.serve_forever()
|
||||
ProxyLoader.Logs.debug(f"RPC Server down!")
|
||||
|
||||
def stop_server(self):
|
||||
self._Base.create_thread(self.thread_stop_rpc_server)
|
||||
|
||||
def thread_stop_rpc_server(self):
|
||||
self.rpc_server.shutdown()
|
||||
ProxyLoader.Logs.debug(f"RPC Server shutdown!")
|
||||
self.rpc_server.server_close()
|
||||
ProxyLoader.Logs.debug(f"RPC Server clean-up!")
|
||||
self._Base.garbage_collector_thread()
|
||||
self._Loader.Irc.Protocol.send_priv_msg(
|
||||
self._Loader.Config.SERVICE_NICKNAME, "Defender RPC Server has stopped successfuly!", self._Loader.Config.SERVICE_CHANLOG
|
||||
)
|
||||
self.connected = False
|
||||
|
||||
class JSONRPCErrorCode(Enum):
|
||||
PARSE_ERROR = -32700 # Syntax error in the request (malformed JSON)
|
||||
INVALID_REQUEST = -32600 # Invalid Request (incorrect structure or missing fields)
|
||||
METHOD_NOT_FOUND = -32601 # Method not found (the requested method does not exist)
|
||||
INVALID_PARAMS = -32602 # Invalid Params (the parameters provided are incorrect)
|
||||
INTERNAL_ERROR = -32603 # Internal Error (an internal server error occurred)
|
||||
|
||||
# Custom application-specific errors (beyond standard JSON-RPC codes)
|
||||
CUSTOM_ERROR = 1001 # Custom application-defined error (e.g., user not found)
|
||||
AUTHENTICATION_ERROR = 1002 # Authentication failure (e.g., invalid credentials)
|
||||
PERMISSION_ERROR = 1003 # Permission error (e.g., user does not have access to this method)
|
||||
RESOURCE_NOT_FOUND = 1004 # Resource not found (e.g., the requested resource does not exist)
|
||||
DUPLICATE_REQUEST = 1005 # Duplicate request (e.g., a similar request has already been processed)
|
||||
|
||||
def description(self):
|
||||
"""Returns a description associated with each error code"""
|
||||
descriptions = {
|
||||
JSONRPCErrorCode.PARSE_ERROR: "The JSON request is malformed.",
|
||||
JSONRPCErrorCode.INVALID_REQUEST: "The request is invalid (missing or incorrect fields).",
|
||||
JSONRPCErrorCode.METHOD_NOT_FOUND: "The requested method could not be found.",
|
||||
JSONRPCErrorCode.INVALID_PARAMS: "The parameters provided are invalid.",
|
||||
JSONRPCErrorCode.INTERNAL_ERROR: "An internal error occurred on the server.",
|
||||
JSONRPCErrorCode.CUSTOM_ERROR: "A custom error defined by the application.",
|
||||
JSONRPCErrorCode.AUTHENTICATION_ERROR: "User authentication failed.",
|
||||
JSONRPCErrorCode.PERMISSION_ERROR: "User does not have permission to access this method.",
|
||||
JSONRPCErrorCode.RESOURCE_NOT_FOUND: "The requested resource could not be found.",
|
||||
JSONRPCErrorCode.DUPLICATE_REQUEST: "The request is a duplicate or is already being processed.",
|
||||
}
|
||||
return descriptions.get(self, "Unknown error")
|
||||
|
||||
def create_error_response(error_code: JSONRPCErrorCode, details: dict = None) -> dict[str, str]:
|
||||
"""Create a JSON-RPC error!"""
|
||||
response = {
|
||||
"code": error_code.value,
|
||||
"message": error_code.description(),
|
||||
}
|
||||
|
||||
if details:
|
||||
response["data"] = details
|
||||
|
||||
return response
|
||||
12
core/classes/modules/rpc/rpc_channel.py
Normal file
12
core/classes/modules/rpc/rpc_channel.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.loader import Loader
|
||||
|
||||
class RPCChannel:
|
||||
def __init__(self, loader: 'Loader'):
|
||||
self._Loader = loader
|
||||
self._Channel = loader.Channel
|
||||
|
||||
def channel_list(self) -> list[dict]:
|
||||
return [chan.to_dict() for chan in self._Channel.UID_CHANNEL_DB]
|
||||
21
core/classes/modules/rpc/rpc_command.py
Normal file
21
core/classes/modules/rpc/rpc_command.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.loader import Loader
|
||||
|
||||
class RPCCommand:
|
||||
def __init__(self, loader: 'Loader'):
|
||||
self._Loader = loader
|
||||
self._Command = loader.Commands
|
||||
|
||||
def command_list(self) -> list[dict]:
|
||||
return [command.to_dict() for command in self._Command.DB_COMMANDS]
|
||||
|
||||
def command_get_by_module(self, module_name: str) -> list[dict]:
|
||||
return [command.to_dict() for command in self._Command.DB_COMMANDS if command.module_name.lower() == module_name.lower()]
|
||||
|
||||
def command_get_by_name(self, command_name: str) -> dict:
|
||||
for command in self._Command.DB_COMMANDS:
|
||||
if command.command_name.lower() == command_name.lower():
|
||||
return command.to_dict()
|
||||
return {}
|
||||
30
core/classes/modules/rpc/rpc_user.py
Normal file
30
core/classes/modules/rpc/rpc_user.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.loader import Loader
|
||||
from core.definition import MUser
|
||||
|
||||
class RPCUser:
|
||||
def __init__(self, loader: 'Loader'):
|
||||
self._Loader = loader
|
||||
self._User = loader.User
|
||||
|
||||
def user_list(self) -> list[dict]:
|
||||
users = self._User.UID_DB.copy()
|
||||
copy_users: list['MUser'] = []
|
||||
|
||||
for user in users:
|
||||
copy_user = user.copy()
|
||||
copy_user.connexion_datetime = copy_user.connexion_datetime.strftime('%d-%m-%Y')
|
||||
copy_users.append(copy_user)
|
||||
|
||||
return [user.to_dict() for user in copy_users]
|
||||
|
||||
def user_get(self, uidornickname: str) -> Optional[dict]:
|
||||
user = self._User.get_user(uidornickname)
|
||||
if user:
|
||||
user_copy = user.copy()
|
||||
user_copy.connexion_datetime = user_copy.connexion_datetime.strftime('%d-%m-%Y')
|
||||
return user_copy.to_dict()
|
||||
|
||||
return None
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Optional, Union, TYPE_CHECKING
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.definition import MSasl
|
||||
@@ -9,13 +9,18 @@ class Sasl:
|
||||
DB_SASL: list['MSasl'] = []
|
||||
|
||||
def __init__(self, loader: 'Loader'):
|
||||
"""
|
||||
|
||||
Args:
|
||||
loader (Loader): The Loader instance.
|
||||
"""
|
||||
self.Logs = loader.Logs # logger
|
||||
|
||||
def insert_sasl_client(self, psasl: 'MSasl') -> bool:
|
||||
"""Insert a new Sasl authentication
|
||||
|
||||
Args:
|
||||
new_user (UserModel): New userModel object
|
||||
psasl (MSasl): New userModel object
|
||||
|
||||
Returns:
|
||||
bool: True if inserted
|
||||
@@ -38,7 +43,7 @@ class Sasl:
|
||||
"""Delete the User starting from the UID
|
||||
|
||||
Args:
|
||||
uid (str): UID of the user
|
||||
client_uid (str): UID of the user
|
||||
|
||||
Returns:
|
||||
bool: True if deleted
|
||||
127
core/classes/modules/settings.py
Normal file
127
core/classes/modules/settings.py
Normal file
@@ -0,0 +1,127 @@
|
||||
"""This class should never be reloaded.
|
||||
"""
|
||||
from logging import Logger
|
||||
from threading import Timer, Thread, RLock
|
||||
from socket import socket
|
||||
from typing import Any, Optional, TYPE_CHECKING
|
||||
from core.definition import MSModule, MAdmin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.classes.modules.user import User
|
||||
|
||||
class Settings:
|
||||
"""This Class will never be reloaded.
|
||||
Means that the variables are available during
|
||||
the whole life of the app
|
||||
"""
|
||||
|
||||
RUNNING_TIMERS: list[Timer] = []
|
||||
RUNNING_THREADS: list[Thread] = []
|
||||
RUNNING_SOCKETS: list[socket] = []
|
||||
PERIODIC_FUNC: dict[str, Any] = {}
|
||||
LOCK: RLock = RLock()
|
||||
|
||||
CONSOLE: bool = False
|
||||
|
||||
MAIN_SERVER_HOSTNAME: str = None
|
||||
MAIN_SERVER_ID: str = None
|
||||
PROTOCTL_PREFIX_MODES_SIGNES : dict[str, str] = {}
|
||||
PROTOCTL_PREFIX_SIGNES_MODES : dict[str, str] = {}
|
||||
PROTOCTL_USER_MODES: list[str] = []
|
||||
PROTOCTL_CHANNEL_MODES: list[str] = []
|
||||
PROTOCTL_PREFIX: list[str] = []
|
||||
|
||||
SMOD_MODULES: list[MSModule] = []
|
||||
"""List contains all Server modules"""
|
||||
|
||||
__CACHE: dict[str, Any] = {}
|
||||
"""Use set_cache or get_cache instead"""
|
||||
|
||||
__TRANSLATION: dict[str, list[list[str]]] = dict()
|
||||
"""Translation Varibale"""
|
||||
|
||||
__LANG: str = "EN"
|
||||
|
||||
__INSTANCE_OF_USER_UTILS: Optional['User'] = None
|
||||
"""Instance of the User Utils class"""
|
||||
|
||||
__CURRENT_ADMIN: Optional['MAdmin'] = None
|
||||
"""The Current Admin Object Model"""
|
||||
|
||||
__LOGGER: Optional[Logger] = None
|
||||
"""Instance of the logger"""
|
||||
|
||||
def set_cache(self, key: str, value_to_cache: Any):
|
||||
"""When you want to store a variable
|
||||
|
||||
Ex.
|
||||
```python
|
||||
set_cache('MY_KEY', {'key1': 'value1', 'key2', 'value2'})
|
||||
```
|
||||
Args:
|
||||
key (str): The key you want to add.
|
||||
value_to_cache (Any): The Value you want to store.
|
||||
"""
|
||||
self.__CACHE[key] = value_to_cache
|
||||
|
||||
def get_cache(self, key) -> Optional[Any]:
|
||||
"""It returns the value associated to the key and finally it removes the entry"""
|
||||
if self.__CACHE.get(key, None) is not None:
|
||||
return self.__CACHE.pop(key)
|
||||
|
||||
return None
|
||||
|
||||
def get_cache_size(self) -> int:
|
||||
return len(self.__CACHE)
|
||||
|
||||
def clear_cache(self) -> None:
|
||||
self.__CACHE.clear()
|
||||
|
||||
def show_cache(self) -> dict[str, Any]:
|
||||
return self.__CACHE.copy()
|
||||
|
||||
@property
|
||||
def global_translation(self) -> dict[str, list[list[str]]]:
|
||||
"""Get/set global translation variable"""
|
||||
return self.__TRANSLATION
|
||||
|
||||
@global_translation.setter
|
||||
def global_translation(self, translation_var: dict) -> None:
|
||||
self.__TRANSLATION = translation_var
|
||||
|
||||
@property
|
||||
def global_lang(self) -> str:
|
||||
"""Global default language."""
|
||||
return self.__LANG
|
||||
|
||||
@global_lang.setter
|
||||
def global_lang(self, lang: str) -> None:
|
||||
self.__LANG = lang
|
||||
|
||||
@property
|
||||
def global_user(self) -> 'User':
|
||||
return self.__INSTANCE_OF_USER_UTILS
|
||||
|
||||
@global_user.setter
|
||||
def global_user(self, user_utils_instance: 'User') -> None:
|
||||
self.__INSTANCE_OF_USER_UTILS = user_utils_instance
|
||||
|
||||
@property
|
||||
def current_admin(self) -> MAdmin:
|
||||
"""Current admin data model."""
|
||||
return self.__CURRENT_ADMIN
|
||||
|
||||
@current_admin.setter
|
||||
def current_admin(self, current_admin: MAdmin) -> None:
|
||||
self.__CURRENT_ADMIN = current_admin
|
||||
|
||||
@property
|
||||
def global_logger(self) -> Logger:
|
||||
"""Global logger Instance"""
|
||||
return self.__LOGGER
|
||||
|
||||
@global_logger.setter
|
||||
def global_logger(self, logger: Logger) -> None:
|
||||
self.__LOGGER = logger
|
||||
|
||||
global_settings = Settings()
|
||||
96
core/classes/modules/translation.py
Normal file
96
core/classes/modules/translation.py
Normal file
@@ -0,0 +1,96 @@
|
||||
import yaml
|
||||
import yaml.scanner
|
||||
from os import sep
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.loader import Loader
|
||||
|
||||
|
||||
class Translation:
|
||||
|
||||
def __init__(self, loader: 'Loader') -> None:
|
||||
"""
|
||||
|
||||
Args:
|
||||
loader (Loader): The Loader instance.
|
||||
"""
|
||||
self.Logs = loader.Logs
|
||||
self.Settings = loader.Settings
|
||||
|
||||
def get_translation(self) -> dict[str, list[list[str]]]:
|
||||
try:
|
||||
translation: dict[str, list[list[str]]] = dict()
|
||||
sfs: dict[str, list[list[str]]] = {}
|
||||
|
||||
module_translation_directory = Path("mods")
|
||||
core_translation_directory = Path("core")
|
||||
sfs_core = self.get_subfolders_name(core_translation_directory.__str__())
|
||||
sfs_module = self.get_subfolders_name(module_translation_directory.__str__())
|
||||
|
||||
# Combine the 2 dict
|
||||
for d in (sfs_core, sfs_module):
|
||||
for k, v in d.items():
|
||||
sfs.setdefault(k, []).extend(v)
|
||||
|
||||
loaded_files: list[str] = []
|
||||
|
||||
for module, filenames in sfs.items():
|
||||
translation[module] = []
|
||||
for filename in filenames:
|
||||
with open(f"{filename}", "r", encoding="utf-8") as fyaml:
|
||||
data: dict[str, list[dict[str, str]]] = yaml.safe_load(fyaml)
|
||||
|
||||
if not isinstance(data, dict):
|
||||
continue
|
||||
|
||||
for key, list_trad in data.items():
|
||||
for vlist in list_trad:
|
||||
translation[module].append([vlist["orig"], vlist["trad"]])
|
||||
|
||||
loaded_files.append(f"{filename}")
|
||||
|
||||
return translation
|
||||
|
||||
except yaml.scanner.ScannerError as se:
|
||||
self.Logs.error(f"[!] {se} [!]")
|
||||
return {}
|
||||
except yaml.YAMLError as ye:
|
||||
if hasattr(ye, 'problem_mark'):
|
||||
mark = ye.problem_mark
|
||||
self.Logs.error(f"Error YAML: {ye.with_traceback(None)}")
|
||||
self.Logs.error("Error position: (%s:%s)" % (mark.line+1, mark.column+1))
|
||||
return {}
|
||||
except yaml.error.MarkedYAMLError as me:
|
||||
self.Logs.error(f"[!] {me} [!]")
|
||||
return {}
|
||||
except Exception as err:
|
||||
self.Logs.error(f'General Error: {err}', exc_info=True)
|
||||
return {}
|
||||
|
||||
finally:
|
||||
self.Logs.debug("Translation files loaded")
|
||||
for f in loaded_files:
|
||||
self.Logs.debug(f" - {f}")
|
||||
|
||||
def get_subfolders_name(self, directory: str) -> dict[str, list[str]]:
|
||||
try:
|
||||
translation_information: dict[str, list[str]] = dict()
|
||||
main_directory = Path(directory)
|
||||
|
||||
# Init the dictionnary
|
||||
for subfolder in main_directory.rglob(f'*language{sep}*{sep}*.yaml'):
|
||||
if subfolder.name != '__pycache__':
|
||||
translation_information[subfolder.parent.name.lower()] = []
|
||||
|
||||
|
||||
for subfolder in main_directory.rglob(f'*language{sep}*{sep}*.yaml'):
|
||||
if subfolder.name != '__pycache__':
|
||||
translation_information[subfolder.parent.name.lower()].append(subfolder)
|
||||
|
||||
return translation_information
|
||||
|
||||
except Exception as err:
|
||||
self.Logs.error(f'General Error: {err}')
|
||||
return {}
|
||||
@@ -10,10 +10,15 @@ class User:
|
||||
|
||||
UID_DB: list['MUser'] = []
|
||||
|
||||
@property
|
||||
def get_current_user(self) -> 'MUser':
|
||||
return self.current_user
|
||||
|
||||
def __init__(self, loader: 'Loader'):
|
||||
|
||||
self.Logs = loader.Logs
|
||||
self.Base = loader.Base
|
||||
self.current_user: Optional['MUser'] = None
|
||||
|
||||
def insert(self, new_user: 'MUser') -> bool:
|
||||
"""Insert a new User object
|
||||
@@ -50,7 +55,7 @@ class User:
|
||||
return False
|
||||
|
||||
user_obj.nickname = new_nickname
|
||||
|
||||
self.Logs.debug(f"UID ({uid}) has benn update with new nickname ({new_nickname}).")
|
||||
return True
|
||||
|
||||
def update_mode(self, uidornickname: str, modes: str) -> bool:
|
||||
@@ -126,8 +131,10 @@ class User:
|
||||
"""
|
||||
for record in self.UID_DB:
|
||||
if record.uid == uidornickname:
|
||||
self.current_user = record
|
||||
return record
|
||||
elif record.nickname == uidornickname:
|
||||
self.current_user = record
|
||||
return record
|
||||
|
||||
return None
|
||||
@@ -147,6 +154,7 @@ class User:
|
||||
if user_obj is None:
|
||||
return None
|
||||
|
||||
self.current_user = user_obj
|
||||
return user_obj.uid
|
||||
|
||||
def get_nickname(self, uidornickname:str) -> Optional[str]:
|
||||
@@ -163,6 +171,7 @@ class User:
|
||||
if user_obj is None:
|
||||
return None
|
||||
|
||||
self.current_user = user_obj
|
||||
return user_obj.nickname
|
||||
|
||||
def get_user_asdict(self, uidornickname: str) -> Optional[dict[str, Any]]:
|
||||
@@ -1,19 +0,0 @@
|
||||
from typing import Literal, TYPE_CHECKING
|
||||
from .protocols.unreal6 import Unrealircd6
|
||||
from .protocols.inspircd import Inspircd
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
|
||||
class Protocol:
|
||||
|
||||
def __init__(self, protocol: Literal['unreal6','inspircd'], ircInstance: 'Irc'):
|
||||
|
||||
self.Protocol = None
|
||||
match protocol:
|
||||
case 'unreal6':
|
||||
self.Protocol: Unrealircd6 = Unrealircd6(ircInstance)
|
||||
case 'inspircd':
|
||||
self.Protocol: Inspircd = Inspircd(ircInstance)
|
||||
case _:
|
||||
self.Protocol: Unrealircd6 = Unrealircd6(ircInstance)
|
||||
54
core/classes/protocols/command_handler.py
Normal file
54
core/classes/protocols/command_handler.py
Normal file
@@ -0,0 +1,54 @@
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.definition import MIrcdCommand
|
||||
from core.loader import Loader
|
||||
|
||||
class CommandHandler:
|
||||
|
||||
DB_IRCDCOMMS: list['MIrcdCommand'] = []
|
||||
DB_SUBSCRIBE: list = []
|
||||
|
||||
def __init__(self, loader: 'Loader'):
|
||||
"""Init method
|
||||
|
||||
Args:
|
||||
loader (Loader): The loader Object
|
||||
"""
|
||||
self.__Logs = loader.Logs
|
||||
|
||||
def register(self, ircd_command_model: 'MIrcdCommand') -> None:
|
||||
"""Register a new command in the Handler
|
||||
|
||||
Args:
|
||||
ircd_command_model (MIrcdCommand): The IRCD Command Object
|
||||
"""
|
||||
ircd_command = self.get_registred_ircd_command(ircd_command_model.command_name)
|
||||
if ircd_command is None:
|
||||
self.__Logs.debug(f'[IRCD COMMAND HANDLER] New IRCD command ({ircd_command_model.command_name}) added to the handler.')
|
||||
self.DB_IRCDCOMMS.append(ircd_command_model)
|
||||
return None
|
||||
else:
|
||||
self.__Logs.debug(f'[IRCD COMMAND HANDLER] This IRCD command ({ircd_command.command_name}) already exist in the handler.')
|
||||
return None
|
||||
|
||||
def get_registred_ircd_command(self, command_name: str) -> Optional['MIrcdCommand']:
|
||||
"""Get the registred IRCD command model
|
||||
|
||||
Returns:
|
||||
MIrcdCommand: The IRCD Command object
|
||||
"""
|
||||
com = command_name.upper()
|
||||
for ircd_com in self.DB_IRCDCOMMS:
|
||||
if com == ircd_com.command_name.upper():
|
||||
return ircd_com
|
||||
|
||||
return None
|
||||
|
||||
def get_ircd_commands(self) -> list['MIrcdCommand']:
|
||||
"""Get the list of IRCD Commands
|
||||
|
||||
Returns:
|
||||
list[MIrcdCommand]: a list of all registred commands
|
||||
"""
|
||||
return self.DB_IRCDCOMMS.copy()
|
||||
33
core/classes/protocols/factory.py
Normal file
33
core/classes/protocols/factory.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
from .unreal6 import Unrealircd6
|
||||
from .inspircd import Inspircd
|
||||
from ..interfaces.iprotocol import IProtocol
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
|
||||
class ProtocolFactorty:
|
||||
|
||||
def __init__(self, uplink: 'Irc'):
|
||||
"""ProtocolFactory init.
|
||||
|
||||
Args:
|
||||
uplink (Irc): The Irc object
|
||||
"""
|
||||
self.__Config = uplink.Config
|
||||
self.__uplink = uplink
|
||||
|
||||
def get(self) -> Optional[IProtocol]:
|
||||
|
||||
protocol = self.__Config.SERVEUR_PROTOCOL
|
||||
|
||||
match protocol:
|
||||
case 'unreal6':
|
||||
self.__uplink.Logs.debug(f"[PROTOCOL] {protocol} has been loaded")
|
||||
return Unrealircd6(self.__uplink)
|
||||
case 'inspircd':
|
||||
self.__uplink.Logs.debug(f"[PROTOCOL] {protocol} has been loaded")
|
||||
return Inspircd(self.__uplink)
|
||||
case _:
|
||||
self.__uplink.Logs.critical(f"[PROTOCOL ERROR] This protocol name ({protocol} is not valid!)")
|
||||
raise Exception("Unknown protocol!")
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,59 +0,0 @@
|
||||
'''This class should never be reloaded.
|
||||
'''
|
||||
from threading import Timer, Thread, RLock
|
||||
from socket import socket
|
||||
from typing import Any, Optional
|
||||
from core.definition import MSModule
|
||||
|
||||
class Settings:
|
||||
"""This Class will never be reloaded.
|
||||
Means that the variables are available during
|
||||
the whole life of the app
|
||||
"""
|
||||
|
||||
RUNNING_TIMERS: list[Timer] = []
|
||||
RUNNING_THREADS: list[Thread] = []
|
||||
RUNNING_SOCKETS: list[socket] = []
|
||||
PERIODIC_FUNC: dict[object] = {}
|
||||
LOCK: RLock = RLock()
|
||||
|
||||
CONSOLE: bool = False
|
||||
|
||||
MAIN_SERVER_HOSTNAME: str = None
|
||||
PROTOCTL_USER_MODES: list[str] = []
|
||||
PROTOCTL_PREFIX: list[str] = []
|
||||
|
||||
SMOD_MODULES: list[MSModule] = []
|
||||
"""List contains all Server modules"""
|
||||
|
||||
__CACHE: dict[str, Any] = {}
|
||||
"""Use set_cache or get_cache instead"""
|
||||
|
||||
def set_cache(self, key: str, value_to_cache: Any):
|
||||
"""When you want to store a variable
|
||||
|
||||
Ex.
|
||||
```python
|
||||
set_cache('MY_KEY', {'key1': 'value1', 'key2', 'value2'})
|
||||
```
|
||||
Args:
|
||||
key (str): The key you want to add.
|
||||
value_to_cache (Any): The Value you want to store.
|
||||
"""
|
||||
self.__CACHE[key] = value_to_cache
|
||||
|
||||
def get_cache(self, key) -> Optional[Any]:
|
||||
"""It returns the value associated to the key and finally it removes the entry"""
|
||||
if self.__CACHE.get(key, None) is not None:
|
||||
return self.__CACHE.pop(key)
|
||||
|
||||
return None
|
||||
|
||||
def get_cache_size(self) -> int:
|
||||
return len(self.__CACHE)
|
||||
|
||||
def clear_cache(self) -> None:
|
||||
self.__CACHE.clear()
|
||||
|
||||
def show_cache(self) -> dict[str, Any]:
|
||||
return self.__CACHE.copy()
|
||||
@@ -1,6 +1,6 @@
|
||||
from datetime import datetime
|
||||
from json import dumps
|
||||
from dataclasses import dataclass, field, asdict, fields
|
||||
from dataclasses import dataclass, field, asdict, fields, replace
|
||||
from typing import Literal, Any, Optional
|
||||
from os import sep
|
||||
|
||||
@@ -15,6 +15,10 @@ class MainModel:
|
||||
"""Return the object of a dataclass a json str."""
|
||||
return dumps(self.to_dict())
|
||||
|
||||
def copy(self):
|
||||
"""Return the object of a dataclass a json str."""
|
||||
return replace(self)
|
||||
|
||||
def get_attributes(self) -> list[str]:
|
||||
"""Return a list of attributes name"""
|
||||
return [f.name for f in fields(self)]
|
||||
@@ -31,6 +35,7 @@ class MClient(MainModel):
|
||||
umodes: str = None
|
||||
vhost: str = None
|
||||
fingerprint: str = None
|
||||
tls_cipher: str = None
|
||||
isWebirc: bool = False
|
||||
isWebsocket: bool = False
|
||||
remote_ip: str = None
|
||||
@@ -50,6 +55,7 @@ class MUser(MainModel):
|
||||
umodes: str = None
|
||||
vhost: str = None
|
||||
fingerprint: str = None
|
||||
tls_cipher: str = None
|
||||
isWebirc: bool = False
|
||||
isWebsocket: bool = False
|
||||
remote_ip: str = None
|
||||
@@ -70,12 +76,14 @@ class MAdmin(MainModel):
|
||||
umodes: str = None
|
||||
vhost: str = None
|
||||
fingerprint: str = None
|
||||
tls_cipher: str = None
|
||||
isWebirc: bool = False
|
||||
isWebsocket: bool = False
|
||||
remote_ip: str = None
|
||||
score_connexion: int = 0
|
||||
geoip: str = None
|
||||
connexion_datetime: datetime = field(default=datetime.now())
|
||||
language: str = "EN"
|
||||
level: int = 0
|
||||
|
||||
@dataclass
|
||||
@@ -89,6 +97,7 @@ class MReputation(MainModel):
|
||||
umodes: str = None
|
||||
vhost: str = None
|
||||
fingerprint: str = None
|
||||
tls_cipher: str = None
|
||||
isWebirc: bool = False
|
||||
isWebsocket: bool = False
|
||||
remote_ip: str = None
|
||||
@@ -191,12 +200,18 @@ class MConfig(MainModel):
|
||||
SERVICE_ID: str = field(init=False)
|
||||
"""The service unique ID"""
|
||||
|
||||
LANG: str = "EN"
|
||||
"""The default language of Defender. default: EN"""
|
||||
|
||||
OWNER: str = "admin"
|
||||
"""The nickname of the admin of the service"""
|
||||
|
||||
PASSWORD: str = "password"
|
||||
"""The password of the admin of the service"""
|
||||
|
||||
RPC_USERS: list[dict] = field(default_factory=list)
|
||||
"""The Defender rpc users"""
|
||||
|
||||
JSONRPC_URL: str = None
|
||||
"""The RPC url, if local https://127.0.0.1:PORT/api should be fine"""
|
||||
|
||||
@@ -341,6 +356,14 @@ class MModule(MainModel):
|
||||
class_name: str = None
|
||||
class_instance: Optional[Any] = None
|
||||
|
||||
@dataclass
|
||||
class DefenderModuleHeader(MainModel):
|
||||
name: str = ''
|
||||
version: str = ''
|
||||
description: str = ''
|
||||
author: str = ''
|
||||
core_version: str = ''
|
||||
|
||||
@dataclass
|
||||
class MSModule:
|
||||
"""Server Modules model"""
|
||||
@@ -359,5 +382,16 @@ class MSasl(MainModel):
|
||||
username: Optional[str] = None
|
||||
password: Optional[str] = None
|
||||
fingerprint: Optional[str] = None
|
||||
language: str = "EN"
|
||||
auth_success: bool = False
|
||||
level: int = 0
|
||||
|
||||
@dataclass
|
||||
class MRegister:
|
||||
command_name: str
|
||||
func: Any
|
||||
|
||||
@dataclass
|
||||
class MIrcdCommand:
|
||||
command_name: str
|
||||
func: Any
|
||||
150
core/install.py
Normal file
150
core/install.py
Normal file
@@ -0,0 +1,150 @@
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from subprocess import check_call, CalledProcessError, check_output
|
||||
from pathlib import Path
|
||||
from platform import python_version_tuple
|
||||
import traceback
|
||||
|
||||
parser = argparse.ArgumentParser(description="Python Installation Code")
|
||||
parser.add_argument('--check-version', action='store_true', help='Check if the python version is ok!')
|
||||
parser.add_argument('--install', action='store_true', help='Run the installation')
|
||||
parser.add_argument('--git-update', action='store_true', help='Update from git (main repository)')
|
||||
args = parser.parse_args()
|
||||
|
||||
PYTHON_REQUIRED_VERSION = (3, 10, 0)
|
||||
PYTHON_SYSTEM_VERSION = tuple(map(int, python_version_tuple()))
|
||||
ROOT_PATH = os.getcwd()
|
||||
PYENV = Path(ROOT_PATH).joinpath('.pyenv/bin/python') if os.name != 'nt' else Path(ROOT_PATH).joinpath('.pyenv/Scripts/python.exe')
|
||||
PIPENV = Path(f'{ROOT_PATH}/.pyenv/bin/pip') if os.name != 'nt' else Path(f'{ROOT_PATH}/.pyenv/Scripts/pip.exe')
|
||||
USER_HOME_DIRECTORY = Path.home()
|
||||
SYSTEMD_PATH = Path(USER_HOME_DIRECTORY).joinpath('.config', 'systemd', 'user')
|
||||
PY_EXEC = 'defender.py'
|
||||
SERVICE_FILE_NAME = 'defender.service'
|
||||
|
||||
@dataclass
|
||||
class Package:
|
||||
name: str = None
|
||||
version: str = None
|
||||
|
||||
def __load_required_package_versions() -> list[Package]:
|
||||
"""This will create Package model with package names and required version
|
||||
"""
|
||||
try:
|
||||
DB_PACKAGES: list[Package] = []
|
||||
version_filename = Path(ROOT_PATH).joinpath('version.json') # 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
|
||||
DB_PACKAGES.append(
|
||||
Package(name=name, version=version)
|
||||
)
|
||||
|
||||
return DB_PACKAGES
|
||||
|
||||
except FileNotFoundError as fe:
|
||||
print(f"File not found: {fe}")
|
||||
except Exception as err:
|
||||
print(f"General Error: {err}")
|
||||
|
||||
def update_packages() -> None:
|
||||
try:
|
||||
newVersion = False
|
||||
db_packages = __load_required_package_versions()
|
||||
print(ROOT_PATH)
|
||||
if sys.prefix not in PYENV.__str__():
|
||||
print(f"You are probably running a new installation or you are not using your virtual env {PYENV}")
|
||||
return newVersion
|
||||
|
||||
print(f"> Checking for dependencies versions ==> WAIT")
|
||||
for package in db_packages:
|
||||
newVersion = False
|
||||
_required_version = package.version
|
||||
_installed_version: str = None
|
||||
output = check_output([PIPENV, 'show', package.name])
|
||||
for line in output.decode().splitlines():
|
||||
if line.startswith('Version:'):
|
||||
_installed_version = line.split(':')[1].strip()
|
||||
break
|
||||
|
||||
required_version = tuple(map(int, _required_version.split('.')))
|
||||
installed_version = tuple(map(int, _installed_version.split('.')))
|
||||
|
||||
if required_version > installed_version:
|
||||
print(f'> New version of {package.name} is available {installed_version} ==> {required_version}')
|
||||
newVersion = True
|
||||
|
||||
if newVersion:
|
||||
check_call([PIPENV, '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"UpdatePackage Error: {err}")
|
||||
traceback.print_exc()
|
||||
|
||||
def run_git_update() -> None:
|
||||
check_call(['git', 'pull', 'origin', 'main'])
|
||||
|
||||
def check_python_requirement():
|
||||
if PYTHON_SYSTEM_VERSION < PYTHON_REQUIRED_VERSION:
|
||||
raise RuntimeError(f"Your Python Version is not meeting the requirement, System Version: {PYTHON_SYSTEM_VERSION} < Required Version {PYTHON_REQUIRED_VERSION}")
|
||||
|
||||
def create_service_file():
|
||||
|
||||
pyenv = PYENV
|
||||
systemd_path = SYSTEMD_PATH
|
||||
py_exec = PY_EXEC
|
||||
service_file_name = SERVICE_FILE_NAME
|
||||
|
||||
if not Path(systemd_path).exists():
|
||||
print("[!] Folder not available")
|
||||
sys.exit(1)
|
||||
|
||||
contain = f'''[Unit]
|
||||
Description=Defender IRC Service
|
||||
|
||||
[Service]
|
||||
ExecStart={pyenv} {py_exec}
|
||||
WorkingDirectory={ROOT_PATH}
|
||||
SyslogIdentifier=Defender
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
'''
|
||||
with open(Path(systemd_path).joinpath(service_file_name), "w") as file:
|
||||
file.write(contain)
|
||||
print('Service file generated with current configuration')
|
||||
print('Running IRC Service ...')
|
||||
|
||||
print(f"#"*24)
|
||||
print("Installation complete ...")
|
||||
print("If the configuration is correct, then you must see your service connected to your irc server")
|
||||
print(f"If any issue, you can see the log file for debug {ROOT_PATH}{os.sep}logs{os.sep}defender.log")
|
||||
print(f"#"*24)
|
||||
|
||||
def main():
|
||||
if args.check_version:
|
||||
check_python_requirement()
|
||||
sys.exit(0)
|
||||
|
||||
if args.install:
|
||||
create_service_file()
|
||||
sys.exit(0)
|
||||
|
||||
if args.git_update:
|
||||
run_git_update()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,331 +0,0 @@
|
||||
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:
|
||||
|
||||
@dataclass
|
||||
class CoreConfig:
|
||||
install_log_file: str
|
||||
unix_systemd_folder: str
|
||||
service_file_name: str
|
||||
service_cmd_executable: list
|
||||
service_cmd_daemon_reload: list
|
||||
defender_main_executable: str
|
||||
python_min_version: str
|
||||
python_current_version_tuple: tuple[str, str, str]
|
||||
python_current_version: str
|
||||
defender_install_folder: str
|
||||
venv_folder: str
|
||||
venv_cmd_installation: list
|
||||
venv_cmd_requirements: list
|
||||
venv_pip_executable: str
|
||||
venv_python_executable: str
|
||||
|
||||
@dataclass
|
||||
class Package:
|
||||
name: str = None
|
||||
version: str = None
|
||||
|
||||
DB_PACKAGES: list[Package] = []
|
||||
|
||||
def __init__(self) -> None:
|
||||
|
||||
self.set_configuration()
|
||||
|
||||
if self.skip_install:
|
||||
self.install_dependencies()
|
||||
self.check_packages_version()
|
||||
return None
|
||||
|
||||
self.check_packages_version()
|
||||
|
||||
# Sinon tester les dependances python et les installer avec pip
|
||||
if self.do_install():
|
||||
|
||||
self.install_dependencies()
|
||||
|
||||
self.create_service_file()
|
||||
|
||||
self.print_final_message()
|
||||
|
||||
return None
|
||||
|
||||
def set_configuration(self):
|
||||
|
||||
self.skip_install = False
|
||||
defender_install_folder = os.getcwd()
|
||||
venv_folder = '.pyenv'
|
||||
unix_user_home_directory = os.path.expanduser("~")
|
||||
unix_systemd_folder = os.path.join(unix_user_home_directory, '.config', 'systemd', 'user')
|
||||
defender_main_executable = os.path.join(defender_install_folder, 'defender.py')
|
||||
|
||||
self.config = self.CoreConfig(
|
||||
install_log_file='install.log',
|
||||
unix_systemd_folder=unix_systemd_folder,
|
||||
service_file_name='defender.service',
|
||||
service_cmd_executable=['systemctl', '--user', 'start', 'defender'],
|
||||
service_cmd_daemon_reload=['systemctl', '--user', 'daemon-reload'],
|
||||
defender_main_executable=defender_main_executable,
|
||||
python_min_version='3.10',
|
||||
python_current_version_tuple=python_version_tuple(),
|
||||
python_current_version=python_version(),
|
||||
defender_install_folder=defender_install_folder,
|
||||
venv_folder=venv_folder,
|
||||
venv_cmd_installation=['python3', '-m', 'venv', venv_folder],
|
||||
venv_cmd_requirements=['sqlalchemy','psutil','requests','faker','unrealircd_rpc_py'],
|
||||
venv_pip_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}pip',
|
||||
venv_python_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}python'
|
||||
)
|
||||
|
||||
if not self.check_python_version():
|
||||
# If the Python version is not good then Exit
|
||||
exit("/!\\ Python version error /!\\")
|
||||
|
||||
if not os.path.exists(os.path.join(self.config.defender_install_folder, 'config', 'configuration.json')):
|
||||
# If configuration file do not exist
|
||||
exit("/!\\ Configuration file (core/configuration.json) doesn't exist! please create it /!\\")
|
||||
|
||||
# Exclude Windows OS from the installation
|
||||
if os.name == 'nt':
|
||||
# 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:
|
||||
|
||||
full_service_file_path = os.path.join(self.config.unix_systemd_folder, self.config.service_file_name)
|
||||
|
||||
if not os.path.exists(full_service_file_path):
|
||||
print(f'/!\\ Service file does not exist /!\\')
|
||||
return True
|
||||
|
||||
# Check if virtual env exist
|
||||
if not os.path.exists(f'{os.path.join(self.config.defender_install_folder, self.config.venv_folder)}'):
|
||||
self.run_subprocess(self.config.venv_cmd_installation)
|
||||
print(f'/!\\ Virtual env does not exist run the install /!\\')
|
||||
return True
|
||||
|
||||
def run_subprocess(self, command:list) -> None:
|
||||
|
||||
print(f'> {command}')
|
||||
try:
|
||||
check_call(command)
|
||||
print("The command completed successfully.")
|
||||
except CalledProcessError as e:
|
||||
print(f"The command failed with the return code: {e.returncode}")
|
||||
print(f"Try to install dependencies ...")
|
||||
exit(5)
|
||||
|
||||
def get_packages_version_from_json(self) -> None:
|
||||
"""This will create Package model with package names and required version
|
||||
"""
|
||||
try:
|
||||
|
||||
version_filename = f'.{os.sep}version.json'
|
||||
with open(version_filename, 'r') as version_data:
|
||||
package_info:dict[str, str] = json.load(version_data)
|
||||
|
||||
for name, version in package_info.items():
|
||||
if name == 'version':
|
||||
continue
|
||||
|
||||
self.DB_PACKAGES.append(
|
||||
self.Package(name=name, version=version)
|
||||
)
|
||||
|
||||
return None
|
||||
except FileNotFoundError as fe:
|
||||
print(f"File not found: {fe}")
|
||||
except Exception as err:
|
||||
print(f"General Error: {err}")
|
||||
|
||||
def check_packages_version(self) -> bool:
|
||||
|
||||
try:
|
||||
newVersion = False
|
||||
self.get_packages_version_from_json()
|
||||
|
||||
if not self.config.venv_folder in prefix:
|
||||
print(f"You are probably running a new installation or you are not using your virtual env {self.config.venv_folder}")
|
||||
return newVersion
|
||||
|
||||
print(f"> Checking for dependencies versions ==> WAIT")
|
||||
for package in self.DB_PACKAGES:
|
||||
newVersion = False
|
||||
required_version = package.version
|
||||
installed_version = None
|
||||
|
||||
output = check_output([self.config.venv_pip_executable, 'show', package.name])
|
||||
for line in output.decode().splitlines():
|
||||
if line.startswith('Version:'):
|
||||
installed_version = line.split(':')[1].strip()
|
||||
break
|
||||
|
||||
required_major, required_minor, required_patch = required_version.split('.')
|
||||
installed_major, installed_minor, installed_patch = installed_version.split('.')
|
||||
|
||||
if required_major > installed_major:
|
||||
print(f'> New version of {package.name} is available {installed_version} ==> {required_version}')
|
||||
newVersion = True
|
||||
elif required_major == installed_major and required_minor > installed_minor:
|
||||
print(f'> New version of {package.name} is available {installed_version} ==> {required_version}')
|
||||
newVersion = True
|
||||
elif required_major == installed_major and required_minor == installed_minor and required_patch > installed_patch:
|
||||
print(f'> New version of {package.name} is available {installed_version} ==> {required_version}')
|
||||
newVersion = True
|
||||
|
||||
if newVersion:
|
||||
self.run_subprocess([self.config.venv_pip_executable, 'install', '--upgrade', package.name])
|
||||
|
||||
print(f"> Dependencies versions ==> OK")
|
||||
return newVersion
|
||||
|
||||
except CalledProcessError:
|
||||
print(f"/!\\ Package {package.name} not installed /!\\")
|
||||
except Exception as err:
|
||||
print(f"General Error: {err}")
|
||||
|
||||
def check_python_version(self) -> bool:
|
||||
"""Test si la version de python est autorisée ou non
|
||||
|
||||
Returns:
|
||||
bool: True si la version de python est autorisé sinon False
|
||||
"""
|
||||
# Current system version
|
||||
sys_major, sys_minor, sys_patch = self.config.python_current_version_tuple
|
||||
|
||||
# min python version required
|
||||
python_required_version = self.config.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.config.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.config.python_min_version} ##")
|
||||
return False
|
||||
|
||||
print(f"> Version of python : {self.config.python_current_version} ==> OK")
|
||||
|
||||
return True
|
||||
|
||||
def check_package(self, package_name) -> bool:
|
||||
|
||||
try:
|
||||
# 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)
|
||||
return True
|
||||
except CalledProcessError as cpe:
|
||||
print(cpe)
|
||||
return False
|
||||
|
||||
def install_dependencies(self) -> None:
|
||||
"""### Verifie les dépendances si elles sont installées
|
||||
- Test si les modules sont installés
|
||||
- Met a jour pip
|
||||
- Install les modules manquants
|
||||
"""
|
||||
do_install = False
|
||||
|
||||
# Check if virtual env exist
|
||||
if not os.path.exists(f'{os.path.join(self.config.defender_install_folder, self.config.venv_folder)}'):
|
||||
self.run_subprocess(self.config.venv_cmd_installation)
|
||||
do_install = True
|
||||
|
||||
for module in self.config.venv_cmd_requirements:
|
||||
if not self.check_package(module):
|
||||
do_install = True
|
||||
|
||||
if not do_install:
|
||||
return None
|
||||
|
||||
print("===> Clean pip cache")
|
||||
self.run_subprocess([self.config.venv_pip_executable, 'cache', 'purge'])
|
||||
|
||||
print("===> Check if pip is up to date")
|
||||
self.run_subprocess([self.config.venv_python_executable, '-m', 'pip', 'install', '--upgrade', 'pip'])
|
||||
|
||||
if not self.check_package('greenlet'):
|
||||
self.run_subprocess([self.config.venv_pip_executable, 'install', '--only-binary', ':all:', 'greenlet'])
|
||||
print('====> Greenlet installed')
|
||||
|
||||
for module in self.config.venv_cmd_requirements:
|
||||
if not self.check_package(module):
|
||||
print("### Trying to install missing python packages ###")
|
||||
self.run_subprocess([self.config.venv_pip_executable, 'install', module])
|
||||
print(f"====> Module {module} installed!")
|
||||
else:
|
||||
print(f"==> {module} already installed")
|
||||
|
||||
def create_service_file(self) -> None:
|
||||
|
||||
full_service_file_path = os.path.join(self.config.unix_systemd_folder, self.config.service_file_name)
|
||||
|
||||
if os.path.exists(full_service_file_path):
|
||||
print(f'/!\\ Service file already exist /!\\')
|
||||
self.run_subprocess(self.config.service_cmd_executable)
|
||||
return None
|
||||
|
||||
contain = f'''[Unit]
|
||||
Description=Defender IRC Service
|
||||
|
||||
[Service]
|
||||
ExecStart={self.config.venv_python_executable} {self.config.defender_main_executable}
|
||||
WorkingDirectory={self.config.defender_install_folder}
|
||||
SyslogIdentifier=Defender
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
'''
|
||||
# Check if user systemd is available (.config/systemd/user/)
|
||||
if not os.path.exists(self.config.unix_systemd_folder):
|
||||
self.run_subprocess(['mkdir', '-p', self.config.unix_systemd_folder])
|
||||
|
||||
with open(full_service_file_path, 'w+') as servicefile:
|
||||
servicefile.write(contain)
|
||||
servicefile.close()
|
||||
print('Service file generated with current configuration')
|
||||
print('Running 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('Service file generated with current configuration')
|
||||
print('Running IRC Service ...')
|
||||
self.run_subprocess(self.config.service_cmd_daemon_reload)
|
||||
self.run_subprocess(self.config.service_cmd_executable)
|
||||
|
||||
def print_final_message(self) -> None:
|
||||
|
||||
print(f"#"*24)
|
||||
print("Installation complete ...")
|
||||
print("If the configuration is correct, then you must see your service connected to your irc server")
|
||||
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)
|
||||
exit(1)
|
||||
514
core/irc.py
514
core/irc.py
@@ -6,13 +6,13 @@ import time
|
||||
from ssl import SSLSocket
|
||||
from datetime import datetime, timedelta
|
||||
from typing import TYPE_CHECKING, Any, Optional, Union
|
||||
from core.classes import rehash
|
||||
from core.loader import Loader
|
||||
from core.classes.protocol import Protocol
|
||||
from core.classes.commands import Command
|
||||
from core.classes.modules import rehash
|
||||
from core.classes.interfaces.iprotocol import IProtocol
|
||||
from core.utils import tr
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.definition import MSasl
|
||||
from core.loader import Loader
|
||||
|
||||
class Irc:
|
||||
_instance = None
|
||||
@@ -24,7 +24,7 @@ class Irc:
|
||||
|
||||
return cls._instance
|
||||
|
||||
def __init__(self, loader: Loader) -> 'Irc':
|
||||
def __init__(self, loader: 'Loader'):
|
||||
|
||||
# Loader class
|
||||
self.Loader = loader
|
||||
@@ -125,17 +125,19 @@ class Irc:
|
||||
self.build_command(3, 'core', 'cert', 'Append your new fingerprint to your account!')
|
||||
self.build_command(4, 'core', 'rehash', 'Reload the configuration file without restarting')
|
||||
self.build_command(4, 'core', 'raw', 'Send a raw command directly to the IRC server')
|
||||
self.build_command(4, 'core', 'print_vars', 'Print users in a file.')
|
||||
self.build_command(4, 'core', 'start_rpc', 'Start defender jsonrpc server')
|
||||
self.build_command(4, 'core', 'stop_rpc', 'Stop defender jsonrpc server')
|
||||
|
||||
# Define the IrcSocket object
|
||||
self.IrcSocket: Optional[Union[socket.socket, SSLSocket]] = None
|
||||
|
||||
self.__create_table()
|
||||
self.Base.create_thread(func=self.heartbeat, func_args=(self.beat, ))
|
||||
|
||||
##############################################
|
||||
# CONNEXION IRC #
|
||||
##############################################
|
||||
def init_irc(self, ircInstance: 'Irc') -> None:
|
||||
def init_irc(self) -> None:
|
||||
"""Create a socket and connect to irc server
|
||||
|
||||
Args:
|
||||
@@ -143,8 +145,8 @@ class Irc:
|
||||
"""
|
||||
try:
|
||||
self.init_service_user()
|
||||
self.Utils.create_socket(ircInstance)
|
||||
self.__connect_to_irc(ircInstance)
|
||||
self.Utils.create_socket(self)
|
||||
self.__connect_to_irc()
|
||||
|
||||
except AssertionError as ae:
|
||||
self.Logs.critical(f'Assertion error: {ae}')
|
||||
@@ -161,35 +163,39 @@ class Irc:
|
||||
))
|
||||
return None
|
||||
|
||||
def __connect_to_irc(self, ircInstance: 'Irc') -> None:
|
||||
def __connect_to_irc(self) -> None:
|
||||
try:
|
||||
self.init_service_user()
|
||||
self.ircObject = ircInstance # créer une copie de l'instance Irc
|
||||
self.Protocol = Protocol(
|
||||
protocol=self.Config.SERVEUR_PROTOCOL,
|
||||
ircInstance=self.ircObject
|
||||
).Protocol
|
||||
self.Protocol: 'IProtocol' = self.Loader.PFactory.get()
|
||||
self.Protocol.register_command()
|
||||
self.Protocol.send_link() # Etablir le link en fonction du protocol choisi
|
||||
self.signal = True # Une variable pour initier la boucle infinie
|
||||
self.join_saved_channels() # Join existing channels
|
||||
self.ModuleUtils.db_load_all_existing_modules(self)
|
||||
# self.join_saved_channels() # Join existing channels
|
||||
# self.ModuleUtils.db_load_all_existing_modules(self)
|
||||
|
||||
while self.signal:
|
||||
try:
|
||||
if self.Config.DEFENDER_RESTART == 1:
|
||||
rehash.restart_service(self.ircObject)
|
||||
rehash.restart_service(self)
|
||||
|
||||
# 4072 max what the socket can grab
|
||||
buffer_size = self.IrcSocket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)
|
||||
data_in_bytes = self.IrcSocket.recv(buffer_size)
|
||||
data = data_in_bytes.splitlines(True)
|
||||
count_bytes = len(data_in_bytes)
|
||||
eol = True
|
||||
if data_in_bytes[-2:] != b"\r\n":
|
||||
eol = False
|
||||
|
||||
while count_bytes > 4070:
|
||||
# If the received message is > 4070 then loop and add the value to the variable
|
||||
while not eol:
|
||||
new_data = self.IrcSocket.recv(buffer_size)
|
||||
data_in_bytes += new_data
|
||||
count_bytes = len(new_data)
|
||||
if data_in_bytes[-2:] == eol:
|
||||
eol = False
|
||||
|
||||
# while count_bytes > 4070:
|
||||
# # If the received message is > 4070 then loop and add the value to the variable
|
||||
# new_data = self.IrcSocket.recv(buffer_size)
|
||||
# data_in_bytes += new_data
|
||||
# count_bytes = len(new_data)
|
||||
|
||||
data = data_in_bytes.splitlines(True)
|
||||
|
||||
@@ -202,9 +208,11 @@ class Irc:
|
||||
self.Logs.error(f"SSLEOFError __connect_to_irc: {soe} - {data}")
|
||||
except ssl.SSLError as se:
|
||||
self.Logs.error(f"SSLError __connect_to_irc: {se} - {data}")
|
||||
sys.exit(1)
|
||||
sys.exit(-1)
|
||||
except OSError as oe:
|
||||
self.Logs.error(f"SSLError __connect_to_irc: {oe} - {data}")
|
||||
self.Logs.error(f"SSLError __connect_to_irc: {oe} {oe.errno}")
|
||||
if oe.errno == 10053:
|
||||
sys.exit(-1)
|
||||
except (socket.error, ConnectionResetError):
|
||||
self.Logs.debug("Connexion reset")
|
||||
|
||||
@@ -220,7 +228,7 @@ class Irc:
|
||||
except ssl.SSLEOFError as soe:
|
||||
self.Logs.error(f"SSLEOFError: {soe}")
|
||||
except AttributeError as atte:
|
||||
self.Logs.critical(f"AttributeError: {atte}")
|
||||
self.Logs.critical(f"AttributeError: {atte}", exc_info=True)
|
||||
except Exception as e:
|
||||
self.Logs.critical(f"General Error: {e}", exc_info=True)
|
||||
|
||||
@@ -261,9 +269,9 @@ class Irc:
|
||||
# This is only to reference the method
|
||||
return None
|
||||
|
||||
##############################################
|
||||
# --------------------------------------------
|
||||
# FIN CONNEXION IRC #
|
||||
##############################################
|
||||
# --------------------------------------------
|
||||
|
||||
def build_command(self, level: int, module_name: str, command_name: str, command_description: str) -> None:
|
||||
"""This method build the commands variable
|
||||
@@ -313,15 +321,15 @@ class Irc:
|
||||
def db_get_admin_info(*, username: Optional[str] = None, password: Optional[str] = None, fingerprint: Optional[str] = None) -> Optional[dict[str, Any]]:
|
||||
if fingerprint:
|
||||
mes_donnees = {'fingerprint': fingerprint}
|
||||
query = f"SELECT user, level FROM {self.Config.TABLE_ADMIN} WHERE fingerprint = :fingerprint"
|
||||
query = f"SELECT user, level, language FROM {self.Config.TABLE_ADMIN} WHERE fingerprint = :fingerprint"
|
||||
else:
|
||||
mes_donnees = {'user': username, 'password': self.Utils.hash_password(password)}
|
||||
query = f"SELECT user, level FROM {self.Config.TABLE_ADMIN} WHERE user = :user AND password = :password"
|
||||
query = f"SELECT user, level, language FROM {self.Config.TABLE_ADMIN} WHERE user = :user AND password = :password"
|
||||
|
||||
result = self.Base.db_execute_query(query, mes_donnees)
|
||||
user_from_db = result.fetchone()
|
||||
if user_from_db:
|
||||
return {'user': user_from_db[0], 'level': user_from_db[1]}
|
||||
return {'user': user_from_db[0], 'level': user_from_db[1], 'language': user_from_db[2]}
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -331,6 +339,7 @@ class Irc:
|
||||
if admin_info is not None:
|
||||
s.auth_success = True
|
||||
s.level = admin_info.get('level', 0)
|
||||
s.language = admin_info.get('language', 'EN')
|
||||
self.Protocol.send2socket(f":{self.Config.SERVEUR_LINK} SASL {self.Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D S")
|
||||
self.Protocol.send2socket(f":{self.Config.SERVEUR_LINK} 903 {s.username} :SASL authentication successful")
|
||||
else:
|
||||
@@ -345,6 +354,7 @@ class Irc:
|
||||
s.auth_success = True
|
||||
s.level = admin_info.get('level', 0)
|
||||
s.username = admin_info.get('user', None)
|
||||
s.language = admin_info.get('language', 'EN')
|
||||
self.Protocol.send2socket(f":{self.Config.SERVEUR_LINK} SASL {self.Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D S")
|
||||
self.Protocol.send2socket(f":{self.Config.SERVEUR_LINK} 903 {s.username} :SASL authentication successful")
|
||||
else:
|
||||
@@ -352,11 +362,6 @@ class Irc:
|
||||
self.Protocol.send2socket(f":{self.Config.SERVEUR_LINK} SASL {self.Settings.MAIN_SERVER_HOSTNAME} {s.client_uid} D F")
|
||||
self.Protocol.send2socket(f":{self.Config.SERVEUR_LINK} 904 {s.username} :SASL authentication failed")
|
||||
|
||||
def __create_table(self) -> None:
|
||||
"""## Create core tables
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_defender_uptime(self) -> str:
|
||||
"""Savoir depuis quand Defender est connecté
|
||||
|
||||
@@ -380,14 +385,16 @@ class Irc:
|
||||
time.sleep(beat)
|
||||
self.Base.execute_periodic_action()
|
||||
|
||||
def insert_db_admin(self, uid: str, account: str, level: int) -> None:
|
||||
def insert_db_admin(self, uid: str, account: str, level: int, language: str) -> None:
|
||||
user_obj = self.User.get_user(uid)
|
||||
|
||||
if user_obj is None:
|
||||
return None
|
||||
|
||||
self.Admin.insert(
|
||||
self.Loader.Definition.MAdmin(
|
||||
**user_obj.to_dict(),
|
||||
language=language,
|
||||
account=account,
|
||||
level=int(level)
|
||||
)
|
||||
@@ -405,57 +412,58 @@ class Irc:
|
||||
|
||||
return None
|
||||
|
||||
def create_defender_user(self, nickname: str, level: int, password: str) -> str:
|
||||
def create_defender_user(self, sender: str, new_admin: str, new_level: int, new_password: str) -> bool:
|
||||
"""Create a new admin user for defender
|
||||
|
||||
Args:
|
||||
sender (str): The current admin sending the request
|
||||
new_admin (str): The new admin to create
|
||||
new_level (int): The level of the admin
|
||||
new_password (str): The clear password
|
||||
|
||||
Returns:
|
||||
bool: True if created.
|
||||
"""
|
||||
|
||||
# > addaccess [nickname] [level] [password]
|
||||
dnick = self.Config.SERVICE_NICKNAME
|
||||
p = self.Protocol
|
||||
|
||||
get_user = self.User.get_user(nickname)
|
||||
level = self.Base.convert_to_int(level)
|
||||
password = password
|
||||
get_user = self.User.get_user(new_admin)
|
||||
level = self.Base.convert_to_int(new_level)
|
||||
password = new_password
|
||||
|
||||
if get_user is None:
|
||||
response = f'This nickname {nickname} does not exist, it is not possible to create this user'
|
||||
self.Logs.warning(response)
|
||||
return response
|
||||
response = tr("The nickname (%s) is not currently connected! please create a new admin when the nickname is connected to the network!", new_admin)
|
||||
p.send_notice(dnick, sender, response)
|
||||
self.Logs.debug(f"New admin {new_admin} sent by {sender} is not connected")
|
||||
return False
|
||||
|
||||
if level is None:
|
||||
response = f'The level [{level}] must be a number from 1 to 4'
|
||||
self.Logs.warning(response)
|
||||
return response
|
||||
|
||||
if level > 4:
|
||||
response = "Impossible d'ajouter un niveau > 4"
|
||||
self.Logs.warning(response)
|
||||
return response
|
||||
if level is None or level > 4 or level == 0:
|
||||
p.send_notice(dnick, sender, tr("The level (%s) must be a number from 1 to 4", level))
|
||||
self.Logs.debug(f"Level must a number between 1 to 4 (sent by {sender})")
|
||||
return False
|
||||
|
||||
nickname = get_user.nickname
|
||||
response = ''
|
||||
|
||||
hostname = get_user.hostname
|
||||
vhost = get_user.vhost
|
||||
spassword = self.Loader.Utils.hash_password(password)
|
||||
|
||||
mes_donnees = {'admin': nickname}
|
||||
query_search_user = f"SELECT id FROM {self.Config.TABLE_ADMIN} WHERE user=:admin"
|
||||
r = self.Base.db_execute_query(query_search_user, mes_donnees)
|
||||
exist_user = r.fetchone()
|
||||
|
||||
# On verifie si le user exist dans la base
|
||||
if not exist_user:
|
||||
mes_donnees = {'datetime': self.Utils.get_sdatetime(), 'user': nickname, 'password': spassword, 'hostname': hostname, 'vhost': vhost, 'level': level}
|
||||
# Check if the user already exist
|
||||
if not self.Admin.db_is_admin_exist(nickname):
|
||||
mes_donnees = {'datetime': self.Utils.get_sdatetime(), 'user': nickname, 'password': spassword, 'hostname': hostname, 'vhost': vhost, 'level': level, 'language': self.Config.LANG}
|
||||
self.Base.db_execute_query(f'''INSERT INTO {self.Config.TABLE_ADMIN}
|
||||
(createdOn, user, password, hostname, vhost, level) VALUES
|
||||
(:datetime, :user, :password, :hostname, :vhost, :level)
|
||||
(createdOn, user, password, hostname, vhost, level, language) VALUES
|
||||
(:datetime, :user, :password, :hostname, :vhost, :level, :language)
|
||||
''', mes_donnees)
|
||||
response = f"{nickname} ajouté en tant qu'administrateur de niveau {level}"
|
||||
self.Protocol.send_notice(nick_from=self.Config.SERVICE_NICKNAME, nick_to=nickname, msg=response)
|
||||
self.Logs.info(response)
|
||||
return response
|
||||
|
||||
p.send_notice(dnick, sender, tr("New admin (%s) has been added with level %s", nickname, level))
|
||||
self.Logs.info(f"A new admin ({nickname}) has been created by {sender}!")
|
||||
return True
|
||||
else:
|
||||
response = f'{nickname} Existe déjà dans les users enregistrés'
|
||||
self.Protocol.send_notice(nick_from=self.Config.SERVICE_NICKNAME, nick_to=nickname, msg=response)
|
||||
self.Logs.info(response)
|
||||
return response
|
||||
p.send_notice(dnick, sender, tr("The nickname (%s) Already exist!", nickname))
|
||||
self.Logs.info(f"The nickname {nickname} already exist! (sent by {sender})")
|
||||
return False
|
||||
|
||||
def thread_check_for_new_version(self, fromuser: str) -> None:
|
||||
dnickname = self.Config.SERVICE_NICKNAME
|
||||
@@ -476,129 +484,25 @@ class Irc:
|
||||
"""
|
||||
try:
|
||||
original_response: list[str] = data.copy()
|
||||
|
||||
if len(original_response) < 2:
|
||||
self.Logs.warning(f'Size ({str(len(original_response))}) - {original_response}')
|
||||
return None
|
||||
|
||||
self.Logs.debug(f">> {self.Utils.hide_sensitive_data(original_response)}")
|
||||
parsed_protocol = self.Protocol.parse_server_msg(original_response.copy())
|
||||
match parsed_protocol:
|
||||
pos, parsed_protocol = self.Protocol.get_ircd_protocol_poisition(cmd=original_response, log=True)
|
||||
modules = self.ModuleUtils.model_get_loaded_modules().copy()
|
||||
|
||||
case 'PING':
|
||||
self.Protocol.on_server_ping(serverMsg=original_response)
|
||||
|
||||
case 'SERVER':
|
||||
self.Protocol.on_server(serverMsg=original_response)
|
||||
|
||||
case 'SJOIN':
|
||||
self.Protocol.on_sjoin(serverMsg=original_response)
|
||||
|
||||
case 'EOS':
|
||||
self.Protocol.on_eos(serverMsg=original_response)
|
||||
|
||||
case 'UID':
|
||||
try:
|
||||
self.Protocol.on_uid(serverMsg=original_response)
|
||||
for module in self.ModuleUtils.model_get_loaded_modules().copy():
|
||||
for parsed in self.Protocol.Handler.get_ircd_commands():
|
||||
if parsed.command_name.upper() == parsed_protocol:
|
||||
parsed.func(original_response)
|
||||
for module in modules:
|
||||
module.class_instance.cmd(original_response)
|
||||
|
||||
# SASL authentication
|
||||
# ['@s2s-md/..', ':001', 'UID', 'adator__', '0', '1755987444', '...', 'desktop-h1qck20.mshome.net', '001XLTT0U', '0', '+iwxz', '*', 'Clk-EC2256B2.mshome.net', 'rBKAAQ==', ':...']
|
||||
dnickname = self.Config.SERVICE_NICKNAME
|
||||
dchanlog = self.Config.SERVICE_CHANLOG
|
||||
uid = original_response[8]
|
||||
nickname = original_response[3]
|
||||
sasl_obj = self.Sasl.get_sasl_obj(uid)
|
||||
if sasl_obj:
|
||||
if sasl_obj.auth_success:
|
||||
self.insert_db_admin(sasl_obj.client_uid, sasl_obj.username, sasl_obj.level)
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"[ {self.Config.COLORS.green}SASL AUTH{self.Config.COLORS.nogc} ] - {nickname} ({sasl_obj.username}) est désormais connecté a {dnickname}",
|
||||
channel=dchanlog)
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=nickname, msg=f"Connexion a {dnickname} réussie!")
|
||||
else:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"[ {self.Config.COLORS.red}SASL AUTH{self.Config.COLORS.nogc} ] - {nickname} a tapé un mauvais mot de pass pour le username ({sasl_obj.username})",
|
||||
channel=dchanlog)
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=nickname, msg=f"Mot de passe incorrecte")
|
||||
|
||||
# Delete sasl object!
|
||||
self.Sasl.delete_sasl_client(uid)
|
||||
|
||||
return None
|
||||
except Exception as err:
|
||||
self.Logs.error(f'General Error: {err}')
|
||||
|
||||
case 'QUIT':
|
||||
self.Protocol.on_quit(serverMsg=original_response)
|
||||
|
||||
case 'PROTOCTL':
|
||||
self.Protocol.on_protoctl(serverMsg=original_response)
|
||||
|
||||
case 'SVS2MODE':
|
||||
# >> [':00BAAAAAG', 'SVS2MODE', '001U01R03', '-r']
|
||||
self.Protocol.on_svs2mode(serverMsg=original_response)
|
||||
|
||||
case 'SQUIT':
|
||||
self.Protocol.on_squit(serverMsg=original_response)
|
||||
|
||||
case 'PART':
|
||||
self.Protocol.on_part(serverMsg=original_response)
|
||||
|
||||
case 'VERSION':
|
||||
self.Protocol.on_version_msg(serverMsg=original_response)
|
||||
|
||||
case 'UMODE2':
|
||||
# [':adator_', 'UMODE2', '-i']
|
||||
self.Protocol.on_umode2(serverMsg=original_response)
|
||||
|
||||
case 'NICK':
|
||||
self.Protocol.on_nick(serverMsg=original_response)
|
||||
|
||||
case 'REPUTATION':
|
||||
self.Protocol.on_reputation(serverMsg=original_response)
|
||||
|
||||
case 'SMOD':
|
||||
self.Protocol.on_smod(original_response)
|
||||
|
||||
case 'SASL':
|
||||
sasl_response = self.Protocol.on_sasl(original_response, self.Sasl)
|
||||
self.on_sasl_authentication_process(sasl_response)
|
||||
|
||||
case 'SLOG': # TODO
|
||||
self.Logs.debug(f"[!] TO HANDLE: {parsed_protocol}")
|
||||
|
||||
case 'MD': # TODO
|
||||
self.Logs.debug(f"[!] TO HANDLE: {parsed_protocol}")
|
||||
|
||||
case 'PRIVMSG':
|
||||
self.Protocol.on_privmsg(serverMsg=original_response)
|
||||
|
||||
case 'PONG': # TODO
|
||||
self.Logs.debug(f"[!] TO HANDLE: {parsed_protocol}")
|
||||
|
||||
case 'MODE': # TODO
|
||||
#['@msgid=d0ySx56Yd0nc35oHts2SkC-/J9mVUA1hfM6...', ':001', 'MODE', '#a', '+nt', '1723207536']
|
||||
#['@unrealircd.org/userhost=adator@localhost;...', ':001LQ0L0C', 'MODE', '#services', '-l']
|
||||
self.Logs.debug(f"[!] TO HANDLE: {parsed_protocol}")
|
||||
|
||||
case '320': # TODO
|
||||
#:irc.deb.biz.st 320 PyDefender IRCParis07 :is in security-groups: known-users,webirc-users,tls-and-known-users,tls-users
|
||||
self.Logs.debug(f"[!] TO HANDLE: {parsed_protocol}")
|
||||
|
||||
case '318': # TODO
|
||||
#:irc.deb.biz.st 318 PyDefender IRCParis93 :End of /WHOIS list.
|
||||
self.Logs.debug(f"[!] TO HANDLE: {parsed_protocol}")
|
||||
|
||||
case None:
|
||||
self.Logs.debug(f"[!] TO HANDLE: {original_response}")
|
||||
|
||||
if len(original_response) > 2:
|
||||
if original_response[2] != 'UID':
|
||||
# Envoyer la commande aux classes dynamiquement chargées
|
||||
for module in self.ModuleUtils.model_get_loaded_modules().copy():
|
||||
module.class_instance.cmd(original_response)
|
||||
# if len(original_response) > 2:
|
||||
# if original_response[2] != 'UID':
|
||||
# # Envoyer la commande aux classes dynamiquement chargées
|
||||
# for module in self.ModuleUtils.model_get_loaded_modules().copy():
|
||||
# module.class_instance.cmd(original_response)
|
||||
|
||||
except IndexError as ie:
|
||||
self.Logs.error(f"IndexError: {ie}")
|
||||
@@ -617,12 +521,21 @@ class Irc:
|
||||
Returns:
|
||||
None: Nothing to return
|
||||
"""
|
||||
u = self.User.get_user(user)
|
||||
"""The User Object"""
|
||||
if u is None:
|
||||
return None
|
||||
|
||||
fromuser = self.User.get_nickname(user) # Nickname qui a lancé la commande
|
||||
uid = self.User.get_uid(fromuser) # Récuperer le uid de l'utilisateur
|
||||
c = self.Client.get_Client(u.uid)
|
||||
"""The Client Object"""
|
||||
|
||||
fromuser = u.nickname
|
||||
uid = u.uid
|
||||
self.Settings.current_admin = self.Admin.get_admin(user) # set Current admin if any.
|
||||
|
||||
RED = self.Config.COLORS.red
|
||||
GREEN = self.Config.COLORS.green
|
||||
BLACK = self.Config.COLORS.black
|
||||
NOGC = self.Config.COLORS.nogc
|
||||
|
||||
# Defender information
|
||||
@@ -646,9 +559,9 @@ class Irc:
|
||||
|
||||
case 'notallowed':
|
||||
try:
|
||||
current_command = cmd[0]
|
||||
current_command = str(cmd[0])
|
||||
self.Protocol.send_priv_msg(
|
||||
msg=f'[ {RED}{current_command}{NOGC} ] - Accès Refusé à {self.User.get_nickname(fromuser)}',
|
||||
msg=tr('[ %s%s%s ] - Access denied to %s', RED, current_command.upper(), NOGC, fromuser),
|
||||
nick_from=dnickname,
|
||||
channel=dchanlog
|
||||
)
|
||||
@@ -656,7 +569,7 @@ class Irc:
|
||||
self.Protocol.send_notice(
|
||||
nick_from=dnickname,
|
||||
nick_to=fromuser,
|
||||
msg=f'Accès Refusé'
|
||||
msg=tr('Access denied!')
|
||||
)
|
||||
|
||||
except IndexError as ie:
|
||||
@@ -664,22 +577,34 @@ class Irc:
|
||||
|
||||
case 'deauth':
|
||||
|
||||
current_command = cmd[0]
|
||||
uid_to_deauth = self.User.get_uid(fromuser)
|
||||
current_command = str(cmd[0]).upper()
|
||||
uid_to_deauth = uid
|
||||
self.delete_db_admin(uid_to_deauth)
|
||||
|
||||
self.Protocol.send_priv_msg(
|
||||
msg=f"[ {RED}{str(current_command).upper()}{NOGC} ] - {self.User.get_nickname(fromuser)} est désormais déconnecter de {dnickname}",
|
||||
msg=tr("[ %s%s%s ] - %s has been disconnected from %s", RED, current_command, NOGC, fromuser, dnickname),
|
||||
nick_from=dnickname,
|
||||
channel=dchanlog
|
||||
)
|
||||
|
||||
self.Protocol.send_notice(dnickname, fromuser, tr("You have been successfully disconnected from %s", dnickname))
|
||||
return None
|
||||
|
||||
case 'firstauth':
|
||||
# firstauth OWNER_NICKNAME OWNER_PASSWORD
|
||||
current_nickname = self.User.get_nickname(fromuser)
|
||||
current_uid = self.User.get_uid(fromuser)
|
||||
# Syntax. /msg defender firstauth OWNER_NICKNAME OWNER_PASSWORD
|
||||
# Check command
|
||||
current_nickname = fromuser
|
||||
current_uid = uid
|
||||
current_command = str(cmd[0])
|
||||
|
||||
if current_nickname is None:
|
||||
self.Logs.critical(f"This nickname [{fromuser}] don't exist")
|
||||
return None
|
||||
|
||||
if len(cmd) < 3:
|
||||
self.Protocol.send_notice(dnickname,fromuser, tr("Syntax. /msg %s %s [OWNER_NICKNAME] [OWNER_PASSWORD]", self.Config.SERVICE_NICKNAME, current_command))
|
||||
return None
|
||||
|
||||
query = f"SELECT count(id) as c FROM {self.Config.TABLE_ADMIN}"
|
||||
result = self.Base.db_execute_query(query)
|
||||
result_db = result.fetchone()
|
||||
@@ -688,13 +613,9 @@ class Irc:
|
||||
self.Protocol.send_notice(
|
||||
nick_from=dnickname,
|
||||
nick_to=fromuser,
|
||||
msg=f"You can't use this command anymore ! Please use [{self.Config.SERVICE_PREFIX}auth] instead"
|
||||
msg=tr("You can't use this command anymore ! Please use [%sauth] instead", self.Config.SERVICE_PREFIX)
|
||||
)
|
||||
return False
|
||||
|
||||
if current_nickname is None:
|
||||
self.Logs.critical(f"This nickname [{fromuser}] don't exist")
|
||||
return False
|
||||
return None
|
||||
|
||||
# Credentials sent from the user
|
||||
cmd_owner = str(cmd[1])
|
||||
@@ -704,59 +625,33 @@ class Irc:
|
||||
config_owner = self.Config.OWNER
|
||||
config_password = self.Config.PASSWORD
|
||||
|
||||
if current_nickname != cmd_owner:
|
||||
self.Logs.critical(f"The current nickname [{fromuser}] is different than the nickname sent [{cmd_owner}] !")
|
||||
self.Protocol.send_notice(
|
||||
nick_from=dnickname,
|
||||
nick_to=fromuser,
|
||||
msg=f"The current nickname [{fromuser}] is different than the nickname sent [{cmd_owner}] !"
|
||||
)
|
||||
return False
|
||||
|
||||
if current_nickname != config_owner:
|
||||
self.Logs.critical(f"The current nickname [{current_nickname}] is different than the configuration owner [{config_owner}] !")
|
||||
self.Protocol.send_notice(
|
||||
nick_from=dnickname,
|
||||
nick_to=fromuser,
|
||||
msg=f"The current nickname [{current_nickname}] is different than the configuration owner [{config_owner}] !"
|
||||
)
|
||||
return False
|
||||
|
||||
if cmd_owner != config_owner:
|
||||
self.Logs.critical(f"The nickname sent [{cmd_owner}] is different than the configuration owner [{config_owner}] !")
|
||||
self.Protocol.send_notice(
|
||||
nick_from=dnickname,
|
||||
nick_to=fromuser,
|
||||
msg=f"The nickname sent [{cmd_owner}] is different than the configuration owner [{config_owner}] !"
|
||||
msg=tr("The nickname sent [%s] is different than the one set in the configuration file !", cmd_owner)
|
||||
)
|
||||
return False
|
||||
return None
|
||||
|
||||
if cmd_owner == config_owner and cmd_password == config_password:
|
||||
self.Base.db_create_first_admin()
|
||||
self.insert_db_admin(current_uid, cmd_owner, 5)
|
||||
self.insert_db_admin(current_uid, cmd_owner, 5, self.Config.LANG)
|
||||
self.Protocol.send_priv_msg(
|
||||
msg=f"[ {self.Config.COLORS.green}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.get_nickname(fromuser)} est désormais connecté a {dnickname}",
|
||||
msg=tr("[%s %s %s] - %s is now connected to %s", GREEN, current_command.upper(), NOGC, fromuser, dnickname),
|
||||
nick_from=dnickname,
|
||||
channel=dchanlog
|
||||
)
|
||||
|
||||
self.Protocol.send_notice(
|
||||
nick_from=dnickname,
|
||||
nick_to=fromuser,
|
||||
msg=f"Connexion a {dnickname} réussie!"
|
||||
)
|
||||
self.Protocol.send_notice(dnickname, fromuser, tr("Successfuly connected to %s", dnickname))
|
||||
else:
|
||||
self.Protocol.send_priv_msg(
|
||||
msg=f"[ {self.Config.COLORS.red}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.get_nickname(fromuser)} a tapé un mauvais mot de pass",
|
||||
msg=tr("[ %s %s %s ] - %s provided a wrong password!", RED, current_command.upper(), NOGC, current_nickname),
|
||||
nick_from=dnickname,
|
||||
channel=dchanlog
|
||||
)
|
||||
|
||||
self.Protocol.send_notice(
|
||||
nick_from=dnickname,
|
||||
nick_to=fromuser,
|
||||
msg=f"Mot de passe incorrecte"
|
||||
)
|
||||
self.Protocol.send_notice(dnickname, fromuser, tr("Wrong password!"))
|
||||
|
||||
case 'auth':
|
||||
# Syntax. !auth nickname password
|
||||
@@ -764,44 +659,45 @@ class Irc:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} [nickname] [password]")
|
||||
return None
|
||||
|
||||
current_command = cmd[0]
|
||||
user_to_log = cmd[1]
|
||||
password = cmd[2]
|
||||
current_client = self.User.get_user(fromuser)
|
||||
current_client = u
|
||||
admin_obj = self.Admin.get_admin(fromuser)
|
||||
|
||||
if current_client is None:
|
||||
# This case should never happen
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"[ {GREEN}{str(current_command).upper()}{NOGC} ] - Nickname {fromuser} is trying to connect to defender wrongly",
|
||||
msg=f"[ {RED}{str(command).upper()} FAIL{NOGC} ] - Nickname {fromuser} is trying to connect to defender wrongly",
|
||||
channel=dchanlog)
|
||||
return None
|
||||
|
||||
if admin_obj:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"[ {GREEN}{str(current_command).upper()}{NOGC} ] - You are already connected to {dnickname}",
|
||||
msg=f"[ {GREEN}{str(command).upper()}{NOGC} ] - {fromuser} is already connected to {dnickname}",
|
||||
channel=dchanlog)
|
||||
self.Protocol.send_notice(dnickname, fromuser, tr("You are already connected to %s", dnickname))
|
||||
return None
|
||||
|
||||
mes_donnees = {'user': user_to_log, 'password': self.Loader.Utils.hash_password(password)}
|
||||
query = f"SELECT id, user, level FROM {self.Config.TABLE_ADMIN} WHERE user = :user AND password = :password"
|
||||
query = f"SELECT id, user, level, language FROM {self.Config.TABLE_ADMIN} WHERE user = :user AND password = :password"
|
||||
result = self.Base.db_execute_query(query, mes_donnees)
|
||||
user_from_db = result.fetchone()
|
||||
|
||||
if user_from_db:
|
||||
account = user_from_db[1]
|
||||
level = user_from_db[2]
|
||||
self.insert_db_admin(current_client.uid, account, level)
|
||||
account = str(user_from_db[1])
|
||||
level = int(user_from_db[2])
|
||||
language = str(user_from_db[3])
|
||||
self.insert_db_admin(current_client.uid, account, level, language)
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"[ {GREEN}{str(current_command).upper()}{NOGC} ] - {current_client.nickname} ({account}) est désormais connecté a {dnickname}",
|
||||
msg=f"[ {GREEN}{str(command).upper()} SUCCESS{NOGC} ] - {current_client.nickname} ({account}) est désormais connecté a {dnickname}",
|
||||
channel=dchanlog)
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Connexion a {dnickname} réussie!")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=tr("Successfuly connected to %s", dnickname))
|
||||
return None
|
||||
else:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"[ {RED}{str(current_command).upper()}{NOGC} ] - {current_client.nickname} a tapé un mauvais mot de pass",
|
||||
msg=f"[ {RED}{str(command).upper()} FAIL{NOGC} ] - {current_client.nickname} a tapé un mauvais mot de pass",
|
||||
channel=dchanlog)
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Mot de passe incorrecte")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=tr("Wrong password!"))
|
||||
return None
|
||||
|
||||
case 'addaccess':
|
||||
@@ -810,15 +706,14 @@ class Irc:
|
||||
if len(cmd) < 4:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Right command : /msg {dnickname} addaccess [nickname] [level] [password]")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"level: from 1 to 4")
|
||||
return None
|
||||
|
||||
newnickname = cmd[1]
|
||||
newlevel = self.Base.int_if_possible(cmd[2])
|
||||
password = cmd[3]
|
||||
new_admin = str(cmd[1])
|
||||
level = self.Base.int_if_possible(cmd[2])
|
||||
password = str(cmd[3])
|
||||
|
||||
response = self.create_defender_user(newnickname, newlevel, password)
|
||||
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{response}")
|
||||
self.Logs.info(response)
|
||||
self.create_defender_user(fromuser, new_admin, level, password)
|
||||
return None
|
||||
|
||||
except IndexError as ie:
|
||||
self.Logs.error(f'_hcmd addaccess: {ie}')
|
||||
@@ -828,7 +723,7 @@ class Irc:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} addaccess [nickname] [level] [password]")
|
||||
|
||||
case 'editaccess':
|
||||
# .editaccess [USER] [PASSWORD] [LEVEL]
|
||||
# .editaccess [USER] [NEW_PASSWORD] [LEVEL]
|
||||
try:
|
||||
if len(cmd) < 3:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Right command : /msg {dnickname} editaccess [nickname] [NEWPASSWORD] [NEWLEVEL]")
|
||||
@@ -843,8 +738,8 @@ class Irc:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"This user {fromuser} has no Admin access")
|
||||
return None
|
||||
|
||||
current_user = self.User.get_nickname(fromuser)
|
||||
current_uid = self.User.get_uid(fromuser)
|
||||
current_user = fromuser
|
||||
current_uid = uid
|
||||
current_user_level = get_admin.level
|
||||
|
||||
user_new_level = int(cmd[3]) if len(cmd) == 4 else get_admin.level
|
||||
@@ -908,8 +803,8 @@ class Irc:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"This user {fromuser} has no admin access")
|
||||
return None
|
||||
|
||||
current_user = self.User.get_nickname(fromuser)
|
||||
current_uid = self.User.get_uid(fromuser)
|
||||
current_user = fromuser
|
||||
current_uid = uid
|
||||
current_user_level = get_admin.level
|
||||
|
||||
# Rechercher le user dans la base de données.
|
||||
@@ -938,14 +833,37 @@ class Irc:
|
||||
case 'cert':
|
||||
# Syntax !cert
|
||||
try:
|
||||
if len(cmd) < 2:
|
||||
self.Protocol.send_notice(dnickname, fromuser, f"Right command : /msg {dnickname} cert add")
|
||||
self.Protocol.send_notice(dnickname, fromuser, f"Right command : /msg {dnickname} cert del")
|
||||
return None
|
||||
|
||||
admin_obj = self.Admin.get_admin(fromuser)
|
||||
param = cmd[1] # add or del
|
||||
match param:
|
||||
case 'add':
|
||||
if admin_obj:
|
||||
if admin_obj.fingerprint is not None:
|
||||
query = f'UPDATE {self.Config.TABLE_ADMIN} SET fingerprint = :fingerprint WHERE user = :user'
|
||||
r = self.Base.db_execute_query(query, {'fingerprint': admin_obj.fingerprint, 'user': admin_obj.account})
|
||||
if r.rowcount > 0:
|
||||
self.Protocol.send_notice(dnickname, fromuser, f'[ {GREEN}CERT{NOGC} ] Your new fingerprint has been attached to your account. {admin_obj.fingerprint}')
|
||||
else:
|
||||
self.Protocol.send_notice(dnickname, fromuser, f'[ {RED}CERT{NOGC} ] Impossible to add your fingerprint.{admin_obj.fingerprint}')
|
||||
else:
|
||||
self.Protocol.send_notice(dnickname, fromuser, f'[ {RED}CERT{NOGC} ] There is no fingerprint to add.')
|
||||
case 'del':
|
||||
if admin_obj:
|
||||
query = f"UPDATE {self.Config.TABLE_ADMIN} SET fingerprint = :fingerprint WHERE user =:user"
|
||||
r = self.Base.db_execute_query(query, {'fingerprint': None, 'user': admin_obj.account})
|
||||
if r.rowcount > 0:
|
||||
self.Protocol.send_notice(dnickname, fromuser, f'[ {GREEN}CERT{NOGC} ] Your fingerprint has been removed from your account. {admin_obj.fingerprint}')
|
||||
else:
|
||||
self.Protocol.send_notice(dnickname, fromuser, f'[ {RED}CERT{NOGC} ] Impossible to remove your fingerprint.{admin_obj.fingerprint}')
|
||||
case _:
|
||||
self.Protocol.send_notice(dnickname, fromuser, f"Right command : /msg {dnickname} cert add")
|
||||
self.Protocol.send_notice(dnickname, fromuser, f"Right command : /msg {dnickname} cert del")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
self.Logs.error(e)
|
||||
@@ -973,7 +891,7 @@ class Irc:
|
||||
)
|
||||
return None
|
||||
|
||||
user_obj = self.User.get_user(fromuser)
|
||||
user_obj = u
|
||||
|
||||
if user_obj is None:
|
||||
self.Logs.error(f"Nickname ({fromuser}) doesn't exist, it is impossible to register this nickname")
|
||||
@@ -1028,8 +946,8 @@ class Irc:
|
||||
|
||||
account = str(cmd[1]) # account
|
||||
encrypted_password = self.Loader.Utils.hash_password(cmd[2])
|
||||
user_obj = self.User.get_user(fromuser)
|
||||
client_obj = self.Client.get_Client(user_obj.uid)
|
||||
user_obj = u
|
||||
client_obj = c
|
||||
|
||||
if client_obj is not None:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You are already logged in")
|
||||
@@ -1069,19 +987,18 @@ class Irc:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} <account>")
|
||||
return None
|
||||
|
||||
user_obj = self.User.get_user(fromuser)
|
||||
user_obj = u
|
||||
if user_obj is None:
|
||||
self.Logs.error(f"The User [{fromuser}] is not available in the database")
|
||||
return None
|
||||
|
||||
client_obj = self.Client.get_Client(user_obj.uid)
|
||||
client_obj = c
|
||||
|
||||
if client_obj is None:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="Nothing to logout. please login first")
|
||||
return None
|
||||
|
||||
self.Protocol.send_svslogout(client_obj)
|
||||
# self.Protocol.send_svsmode(nickname=fromuser, user_mode='-r')
|
||||
self.Client.delete(user_obj.uid)
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You have been logged out successfully")
|
||||
|
||||
@@ -1101,18 +1018,25 @@ class Irc:
|
||||
case 'load':
|
||||
try:
|
||||
# Load a module ex: .load mod_defender
|
||||
if len(cmd) < 2:
|
||||
self.Protocol.send_notice(dnickname, fromuser, tr("Syntax. /msg %s %s MODULE_NAME", dnickname, command.upper()))
|
||||
return None
|
||||
|
||||
mod_name = str(cmd[1])
|
||||
self.ModuleUtils.load_one_module(self, mod_name, fromuser)
|
||||
return None
|
||||
except KeyError as ke:
|
||||
self.Logs.error(f"Key Error: {ke} - list recieved: {cmd}")
|
||||
except Exception as err:
|
||||
self.Logs.error(f"General Error: {ke} - list recieved: {cmd}")
|
||||
self.Logs.error(f"General Error: {err} - list recieved: {cmd}", exc_info=True)
|
||||
|
||||
case 'unload':
|
||||
# unload mod_defender
|
||||
try:
|
||||
# The module name. exemple: mod_defender
|
||||
if len(cmd) < 2:
|
||||
self.Protocol.send_notice(dnickname, fromuser, tr("Syntax. /msg %s %s MODULE_NAME", dnickname, command.upper()))
|
||||
return None
|
||||
module_name = str(cmd[1]).lower()
|
||||
self.ModuleUtils.unload_one_module(self, module_name, False)
|
||||
return None
|
||||
@@ -1123,6 +1047,10 @@ class Irc:
|
||||
# reload mod_defender
|
||||
try:
|
||||
# ==> mod_defender
|
||||
if len(cmd) < 2:
|
||||
self.Protocol.send_notice(dnickname, fromuser, tr("Syntax. /msg %s %s MODULE_NAME", dnickname, command.upper()))
|
||||
return None
|
||||
|
||||
module_name = str(cmd[1]).lower()
|
||||
self.ModuleUtils.reload_one_module(self, module_name, fromuser)
|
||||
return None
|
||||
@@ -1143,7 +1071,8 @@ class Irc:
|
||||
self.Base.execute_periodic_action()
|
||||
|
||||
for chan_name in self.Channel.UID_CHANNEL_DB:
|
||||
self.Protocol.send_mode_chan(chan_name.name, '-l')
|
||||
# self.Protocol.send_mode_chan(chan_name.name, '-l')
|
||||
self.Protocol.send_set_mode('-l', channel_name=chan_name.name)
|
||||
|
||||
for client in self.Client.CLIENT_DB:
|
||||
self.Protocol.send_svslogout(client)
|
||||
@@ -1164,11 +1093,15 @@ class Irc:
|
||||
case 'restart':
|
||||
final_reason = ' '.join(cmd[1:])
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{dnickname.capitalize()} is going to restart!")
|
||||
self.Config.DEFENDER_RESTART = 1 # Set restart status to 1 saying that the service will restart
|
||||
self.Config.DEFENDER_INIT = 1 # set init to 1 saying that the service will be re initiated
|
||||
|
||||
# Set restart status to 1 saying that the service will restart
|
||||
self.Config.DEFENDER_RESTART = 1
|
||||
|
||||
# set init to 1 saying that the service will be re initiated
|
||||
self.Config.DEFENDER_INIT = 1
|
||||
|
||||
case 'rehash':
|
||||
rehash.rehash_service(self.ircObject, fromuser)
|
||||
rehash.rehash_service(self, fromuser)
|
||||
return None
|
||||
|
||||
case 'show_modules':
|
||||
@@ -1189,14 +1122,14 @@ class Irc:
|
||||
self.Protocol.send_notice(
|
||||
nick_from=dnickname,
|
||||
nick_to=fromuser,
|
||||
msg=f"{module} - {GREEN}Loaded{NOGC} by {loaded_user} on {loaded_datetime}"
|
||||
msg=tr('%s - %sLoaded%s by %s on %s', module, GREEN, NOGC, loaded_user, loaded_datetime)
|
||||
)
|
||||
loaded = False
|
||||
else:
|
||||
self.Protocol.send_notice(
|
||||
nick_from=dnickname,
|
||||
nick_to=fromuser,
|
||||
msg=f"{module} - {RED}Not Loaded{NOGC}"
|
||||
msg=tr('%s - %sNot Loaded%s', module, RED, NOGC)
|
||||
)
|
||||
|
||||
case 'show_timers':
|
||||
@@ -1266,7 +1199,7 @@ class Irc:
|
||||
self.Protocol.send_notice(
|
||||
nick_from=dnickname,
|
||||
nick_to=fromuser,
|
||||
msg=f"UID : {db_admin.uid} - Nickname: {db_admin.nickname} - Account: {db_admin.account} - Level: {db_admin.level} - Connection: {db_admin.connexion_datetime}"
|
||||
msg=f"UID : {db_admin.uid} - Nickname: {db_admin.nickname} - Account: {db_admin.account} - Level: {db_admin.level} - Language: {db_admin.language} - Connection: {db_admin.connexion_datetime}"
|
||||
)
|
||||
return None
|
||||
|
||||
@@ -1301,7 +1234,7 @@ class Irc:
|
||||
self.Protocol.send_notice(
|
||||
nick_from=dnickname,
|
||||
nick_to=fromuser,
|
||||
msg=f"{uptime}"
|
||||
msg=uptime
|
||||
)
|
||||
return None
|
||||
|
||||
@@ -1309,7 +1242,7 @@ class Irc:
|
||||
self.Protocol.send_notice(
|
||||
nick_from=dnickname,
|
||||
nick_to=fromuser,
|
||||
msg=f">> Defender V.{self.Config.CURRENT_VERSION} Developped by adator®."
|
||||
msg=f">> Defender V{self.Config.CURRENT_VERSION} Developped by adator®."
|
||||
)
|
||||
return None
|
||||
|
||||
@@ -1322,5 +1255,30 @@ class Irc:
|
||||
self.Protocol.send_raw(raw_command)
|
||||
return None
|
||||
|
||||
case 'print_vars':
|
||||
with open('users.txt', 'w') as fw:
|
||||
i = 1
|
||||
for u in self.User.UID_DB:
|
||||
w = fw.write(u.to_dict().__str__() + "\n")
|
||||
self.Logs.debug(f" {i} - chars written {w}")
|
||||
i += 1
|
||||
self.Protocol.send_priv_msg(dnickname, "Data written in users.txt file", dchanlog)
|
||||
|
||||
with open('modules.txt', 'w') as fw:
|
||||
i = 1
|
||||
for u in self.ModuleUtils.DB_MODULE_HEADERS:
|
||||
w = fw.write(u.to_dict().__str__() + "\n")
|
||||
self.Logs.debug(f" {i} - chars written {w}")
|
||||
i += 1
|
||||
self.Protocol.send_priv_msg(dnickname, "Data written in modules.txt file", dchanlog)
|
||||
|
||||
return None
|
||||
|
||||
case 'start_rpc':
|
||||
self.Loader.RpcServer.start_server()
|
||||
|
||||
case 'stop_rpc':
|
||||
self.Loader.RpcServer.stop_server()
|
||||
|
||||
case _:
|
||||
pass
|
||||
|
||||
21
core/language/fr/core-fr.yaml
Normal file
21
core/language/fr/core-fr.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
traduction:
|
||||
# Message help
|
||||
- orig: "Access denied!"
|
||||
trad: "Accès refusé."
|
||||
- orig: "Wrong password!"
|
||||
trad: "Mot de passe incorrect!"
|
||||
- orig: "%s - %sLoaded%s by %s on %s"
|
||||
trad: "%s - %sChargé%s par %s le %s"
|
||||
- orig: "Module %s loaded!"
|
||||
trad: "Module %s chargé!"
|
||||
- orig: "cmd method is not available in the module (%s)"
|
||||
trad: "La méthode cmd n'est pas disponible dans le module (%s)"
|
||||
- orig: "[%sMODULE ERROR%s] Module %s is facing issues! %s"
|
||||
trad: "[%sMODULE ERREUR%s] Le module %s a rencontré une erreur! %s"
|
||||
- orig: "%s - %sNot Loaded%s"
|
||||
trad: "%s - %sNon chargé%s"
|
||||
- orig: "Successfuly connected to %s"
|
||||
trad: "Connecter a %s avec succés"
|
||||
- orig: "[ %sINFORMATION%s ] >> %s is ready!"
|
||||
trad: "[ %sINFORMATION%s ] >> %s est prêt!"
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
from logging import Logger
|
||||
from core.classes import user, admin, client, channel, reputation, settings, sasl
|
||||
from core.classes.modules.settings import global_settings
|
||||
from core.classes.modules import translation, user, admin, client, channel, reputation, settings, sasl
|
||||
import core.logs as logs
|
||||
import core.definition as df
|
||||
import core.utils as utils
|
||||
import core.base as base_mod
|
||||
import core.module as module_mod
|
||||
import core.classes.commands as commands_mod
|
||||
import core.classes.config as conf_mod
|
||||
import core.classes.modules.commands as commands_mod
|
||||
import core.classes.modules.config as conf_mod
|
||||
import core.classes.modules.rpc.rpc as rpc_mod
|
||||
import core.irc as irc
|
||||
import core.classes.protocols.factory as factory
|
||||
|
||||
class Loader:
|
||||
|
||||
@@ -23,21 +27,33 @@ class Loader:
|
||||
|
||||
self.LoggingModule: logs = logs
|
||||
|
||||
self.RpcServerModule: rpc_mod = rpc_mod
|
||||
|
||||
self.Utils: utils = utils
|
||||
|
||||
# Load Classes
|
||||
self.Settings: settings.Settings = global_settings
|
||||
|
||||
self.ServiceLogging: logs.ServiceLogging = self.LoggingModule.ServiceLogging()
|
||||
|
||||
self.Logs: Logger = self.ServiceLogging.get_logger()
|
||||
|
||||
self.Settings: settings.Settings = settings.Settings()
|
||||
self.Config: df.MConfig = self.ConfModule.Configuration(self).configuration_model
|
||||
|
||||
self.Config: df.MConfig = self.ConfModule.Configuration(self).get_config_model()
|
||||
self.Settings.global_lang = self.Config.LANG if self.Config.LANG else "EN"
|
||||
|
||||
self.Settings.global_logger = self.Logs
|
||||
|
||||
self.Translation: translation.Translation = translation.Translation(self)
|
||||
|
||||
self.Settings.global_translation = self.Translation.get_translation()
|
||||
|
||||
self.Base: base_mod.Base = self.BaseModule.Base(self)
|
||||
|
||||
self.User: user.User = user.User(self)
|
||||
|
||||
self.Settings.global_user = self.User
|
||||
|
||||
self.Client: client.Client = client.Client(self)
|
||||
|
||||
self.Admin: admin.Admin = admin.Admin(self)
|
||||
@@ -52,4 +68,12 @@ class Loader:
|
||||
|
||||
self.Sasl: sasl.Sasl = sasl.Sasl(self)
|
||||
|
||||
self.Logs.debug("LOADER Success!")
|
||||
self.Irc: irc.Irc = irc.Irc(self)
|
||||
|
||||
self.PFactory: factory.ProtocolFactorty = factory.ProtocolFactorty(self.Irc)
|
||||
|
||||
self.RpcServer: rpc_mod.JSONRPCServer = rpc_mod.JSONRPCServer(self)
|
||||
|
||||
self.Base.init()
|
||||
|
||||
self.Logs.debug(self.Utils.tr("Loader %s success", __name__))
|
||||
|
||||
@@ -15,7 +15,7 @@ class ServiceLogging:
|
||||
self.SERVER_PREFIX = None
|
||||
self.LOGGING_CONSOLE = True
|
||||
|
||||
self.LOG_FILTERS: list[str] = ['PING', f":{self.SERVER_PREFIX}auth", "['PASS'"]
|
||||
self.LOG_FILTERS: list[str] = ["PING", f":{self.SERVER_PREFIX}auth", "['PASS'"]
|
||||
|
||||
self.file_handler = None
|
||||
self.stdout_handler = None
|
||||
|
||||
@@ -6,7 +6,8 @@ import sys
|
||||
import importlib
|
||||
from types import ModuleType
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
from core.definition import MModule
|
||||
from core.definition import DefenderModuleHeader, MModule
|
||||
from core.utils import tr
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.loader import Loader
|
||||
@@ -15,6 +16,7 @@ if TYPE_CHECKING:
|
||||
class Module:
|
||||
|
||||
DB_MODULES: list[MModule] = []
|
||||
DB_MODULE_HEADERS: list[DefenderModuleHeader] = []
|
||||
|
||||
def __init__(self, loader: 'Loader') -> None:
|
||||
self.__Loader = loader
|
||||
@@ -31,18 +33,57 @@ class Module:
|
||||
list[str]: List of all module names.
|
||||
"""
|
||||
base_path = Path('mods')
|
||||
return [file.name.replace('.py', '') for file in base_path.rglob('mod_*.py')]
|
||||
modules_available = [file.name.replace('.py', '') for file in base_path.rglob('mod_*.py')]
|
||||
self.__Logs.debug(f"Modules available: {modules_available}")
|
||||
return modules_available
|
||||
|
||||
def get_module_information(self, module_name: str) -> tuple[str, str, str]:
|
||||
def get_module_information(self, module_name: str) -> tuple[Optional[str], Optional[str], Optional[str]]:
|
||||
# module_name : mod_defender
|
||||
if not module_name.lower().startswith('mod_'):
|
||||
return None, None, None
|
||||
|
||||
module_name = module_name.lower()
|
||||
module_name = module_name.lower() # --> mod_defender
|
||||
module_folder = module_name.split('_')[1].lower() # --> defender
|
||||
class_name = module_name.split('_')[1].capitalize() # --> Defender
|
||||
self.__Logs.debug(f"Module information Folder: {module_folder}, Name: {module_name}, Class: {class_name}")
|
||||
return module_folder, module_name, class_name
|
||||
|
||||
def get_module_header(self, module_name: str) -> Optional[DefenderModuleHeader]:
|
||||
|
||||
for mod_h in self.DB_MODULE_HEADERS:
|
||||
if module_name.lower() == mod_h.name.lower():
|
||||
self.__Logs.debug(f"Module Header found: {mod_h}")
|
||||
return mod_h
|
||||
|
||||
return None
|
||||
|
||||
def create_module_header(self, module_header: dict[str, str]) -> bool:
|
||||
"""Create a new module header into DB_MODULE_HEADERS
|
||||
|
||||
Args:
|
||||
module_header (dict[str, str]): The module header
|
||||
|
||||
Returns:
|
||||
bool: True if the module header has been created.
|
||||
"""
|
||||
mod_header = DefenderModuleHeader(**module_header)
|
||||
if self.get_module_header(mod_header.name) is None:
|
||||
self.__Logs.debug(f"[MOD_HEADER] The module header has been created! ({mod_header.name} v{mod_header.version})")
|
||||
self.DB_MODULE_HEADERS.append(mod_header)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def delete_module_header(self, module_name: str) -> bool:
|
||||
mod_header = self.get_module_header(module_name)
|
||||
if mod_header is not None:
|
||||
self.__Logs.debug(f"[MOD_HEADER] The module header has been deleted ({mod_header.name} v{mod_header.version})")
|
||||
self.DB_MODULE_HEADERS.remove(mod_header)
|
||||
return True
|
||||
|
||||
self.__Logs.debug(f"[MOD_HEADER ERROR] Impossible to remove the module header ({module_name})")
|
||||
return False
|
||||
|
||||
def load_one_module(self, uplink: 'Irc', module_name: str, nickname: str, is_default: bool = False) -> bool:
|
||||
|
||||
module_folder, module_name, class_name = self.get_module_information(module_name)
|
||||
@@ -66,14 +107,26 @@ class Module:
|
||||
return self.reload_one_module(uplink, module_name, nickname)
|
||||
|
||||
# Charger le module
|
||||
try:
|
||||
loaded_module = importlib.import_module(f'mods.{module_folder}.{module_name}')
|
||||
my_class = getattr(loaded_module, class_name, None) # Récuperer le nom de classe
|
||||
create_instance_of_the_class = my_class(uplink) # Créer une nouvelle instance de la classe
|
||||
self.create_module_header(create_instance_of_the_class.MOD_HEADER)
|
||||
except AttributeError as attr:
|
||||
red = uplink.Config.COLORS.red
|
||||
nogc = uplink.Config.COLORS.nogc
|
||||
uplink.Protocol.send_priv_msg(
|
||||
nick_from=self.__Config.SERVICE_NICKNAME,
|
||||
msg=tr("[%sMODULE ERROR%s] Module %s is facing issues! %s", red, nogc, module_name, attr),
|
||||
channel=self.__Config.SERVICE_CHANLOG
|
||||
)
|
||||
self.__Logs.error(msg=attr, exc_info=True)
|
||||
return False
|
||||
|
||||
if not hasattr(create_instance_of_the_class, 'cmd'):
|
||||
uplink.Protocol.send_priv_msg(
|
||||
nick_from=self.__Config.SERVICE_NICKNAME,
|
||||
msg=f"Module {module_name} ne contient pas de méthode cmd",
|
||||
msg=tr("cmd method is not available in the module (%s)", module_name),
|
||||
channel=self.__Config.SERVICE_CHANLOG
|
||||
)
|
||||
self.__Logs.critical(f"The Module {module_name} has not been loaded because cmd method is not available")
|
||||
@@ -86,11 +139,14 @@ class Module:
|
||||
self.db_register_module(module_name, nickname, is_default)
|
||||
uplink.Protocol.send_priv_msg(
|
||||
nick_from=self.__Config.SERVICE_NICKNAME,
|
||||
msg=f"Module {module_name} chargé",
|
||||
msg=tr("Module %s loaded!", module_name),
|
||||
channel=self.__Config.SERVICE_CHANLOG
|
||||
)
|
||||
|
||||
self.__Logs.debug(f"Module {class_name} has been loaded")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def load_all_modules(self) -> bool:
|
||||
...
|
||||
@@ -113,6 +169,7 @@ class Module:
|
||||
if self.is_module_exist_in_sys_module(module_name):
|
||||
module_model = self.model_get_module(module_name)
|
||||
if module_model:
|
||||
self.delete_module_header(module_model.class_instance.MOD_HEADER['name'])
|
||||
module_model.class_instance.unload()
|
||||
else:
|
||||
uplink.Protocol.send_priv_msg(
|
||||
@@ -130,6 +187,7 @@ class Module:
|
||||
importlib.reload(the_module)
|
||||
my_class = getattr(the_module, class_name, None)
|
||||
new_instance = my_class(uplink)
|
||||
self.create_module_header(new_instance.MOD_HEADER)
|
||||
module_model.class_instance = new_instance
|
||||
|
||||
# Créer le module dans la base de données
|
||||
@@ -152,7 +210,7 @@ class Module:
|
||||
return False
|
||||
|
||||
except (TypeError, AttributeError, KeyError, Exception) as err:
|
||||
self.__Logs.error(f"[RELOAD MODULE ERROR]: {err}")
|
||||
self.__Logs.error(f"[RELOAD MODULE ERROR]: {err}", exc_info=True)
|
||||
uplink.Protocol.send_priv_msg(
|
||||
nick_from=self.__Config.SERVICE_NICKNAME,
|
||||
msg=f"[RELOAD MODULE ERROR]: {err}",
|
||||
@@ -193,7 +251,9 @@ class Module:
|
||||
"""Unload a module
|
||||
|
||||
Args:
|
||||
mod_name (str): Module name ex mod_defender
|
||||
uplink (Irc): The Irc instance
|
||||
module_name (str): Module name ex mod_defender
|
||||
keep_in_db (bool): Keep in database
|
||||
|
||||
Returns:
|
||||
bool: True if success
|
||||
@@ -206,6 +266,7 @@ class Module:
|
||||
module = self.model_get_module(module_name)
|
||||
if module is None:
|
||||
self.__Logs.debug(f"[ UNLOAD MODULE ERROR ] This module {module_name} is not loaded!")
|
||||
self.db_delete_module(module_name)
|
||||
uplink.Protocol.send_priv_msg(
|
||||
nick_from=self.__Config.SERVICE_NICKNAME,
|
||||
msg=f"[ {red}UNLOAD MODULE ERROR{nogc} ] This module {module_name} is not loaded!",
|
||||
@@ -214,6 +275,7 @@ class Module:
|
||||
return False
|
||||
|
||||
if module:
|
||||
self.delete_module_header(module.class_instance.MOD_HEADER['name'])
|
||||
module.class_instance.unload()
|
||||
self.DB_MODULES.remove(module)
|
||||
|
||||
@@ -240,7 +302,7 @@ class Module:
|
||||
return False
|
||||
|
||||
except Exception as err:
|
||||
self.__Logs.error(f"General Error: {err}")
|
||||
self.__Logs.error(f"General Error: {err}", exc_info=True)
|
||||
return False
|
||||
|
||||
def unload_all_modules(self) -> bool:
|
||||
@@ -257,7 +319,9 @@ class Module:
|
||||
"""
|
||||
module_folder, module_name, class_name = self.get_module_information(module_name)
|
||||
if "mods." + module_folder + "." + module_name in sys.modules:
|
||||
self.__Logs.debug(f"[SYS MODULE] (mods.{module_folder}.{module_name}) found in sys.modules")
|
||||
return True
|
||||
self.__Logs.debug(f"[SYS MODULE] (mods.{module_folder}.{module_name}) not found in sys.modules")
|
||||
return False
|
||||
|
||||
'''
|
||||
|
||||
@@ -1,22 +1,67 @@
|
||||
'''
|
||||
"""
|
||||
Main utils library.
|
||||
'''
|
||||
"""
|
||||
import gc
|
||||
import ssl
|
||||
import socket
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from re import match, sub
|
||||
from base64 import b64decode
|
||||
from typing import Literal, Optional, Any, TYPE_CHECKING
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from datetime import datetime
|
||||
from time import time
|
||||
from random import choice
|
||||
from hashlib import md5, sha3_512
|
||||
from core.classes.modules.settings import global_settings
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
|
||||
def tr(message: str, *args) -> str:
|
||||
"""Translation Engine system
|
||||
```python
|
||||
example:
|
||||
_('Hello my firstname is %s and my lastname is %s', firstname, lastname)
|
||||
```
|
||||
Args:
|
||||
message (str): The message to translate
|
||||
*args (any) : Whatever the variable you want to pass
|
||||
|
||||
Returns:
|
||||
str: The translated message
|
||||
"""
|
||||
count_args = len(args) # Count number of args sent
|
||||
count_placeholder = message.count('%s') # Count number of placeholder in the message
|
||||
is_args_available = True if args else False
|
||||
g = global_settings
|
||||
try:
|
||||
# Access to admin object
|
||||
client_language = g.current_admin.language if g.current_admin else g.global_lang
|
||||
|
||||
if count_args != count_placeholder:
|
||||
g.global_logger.error(f"Translation: Original message: {message} | Args: {count_args} - Placeholder: {count_placeholder}")
|
||||
return message
|
||||
|
||||
if g.global_lang is None:
|
||||
return message % args if is_args_available else message
|
||||
|
||||
if client_language.lower() == 'en':
|
||||
return message % args if is_args_available else message
|
||||
|
||||
for trads in g.global_translation[client_language.lower()]:
|
||||
if sub(r"\s+", "", message) == sub(r"\s+", "", trads[0]):
|
||||
return trads[1] % args if is_args_available else trads[1]
|
||||
|
||||
return message % args if is_args_available else message
|
||||
|
||||
except KeyError as ke:
|
||||
g.global_logger.error(f"KeyError: {ke}")
|
||||
return message % args if is_args_available else message
|
||||
|
||||
except Exception as err:
|
||||
global_settings.global_logger.error(f"General Error: {err} / {message}")
|
||||
return message
|
||||
|
||||
def convert_to_int(value: Any) -> Optional[int]:
|
||||
"""Convert a value to int
|
||||
|
||||
@@ -38,9 +83,9 @@ def get_unixtime() -> int:
|
||||
Returns:
|
||||
int: Current time in seconds since the Epoch (int)
|
||||
"""
|
||||
cet_offset = timezone(timedelta(hours=2))
|
||||
now_cet = datetime.now(cet_offset)
|
||||
unixtime_cet = int(now_cet.timestamp())
|
||||
# cet_offset = timezone(timedelta(hours=2))
|
||||
# now_cet = datetime.now(cet_offset)
|
||||
# unixtime_cet = int(now_cet.timestamp())
|
||||
return int(time())
|
||||
|
||||
def get_sdatetime() -> str:
|
||||
@@ -96,7 +141,9 @@ def create_socket(uplink: 'Irc') -> None:
|
||||
except OSError as oe:
|
||||
uplink.Logs.critical(f"[OS Error]: {oe}")
|
||||
if 'connection refused' in str(oe).lower():
|
||||
sys.exit(oe)
|
||||
sys.exit(oe.__str__())
|
||||
if oe.errno == 10053:
|
||||
sys.exit(oe.__str__())
|
||||
except AttributeError as ae:
|
||||
uplink.Logs.critical(f"AttributeError: {ae}")
|
||||
|
||||
@@ -130,7 +177,7 @@ def generate_random_string(lenght: int) -> str:
|
||||
|
||||
return randomize
|
||||
|
||||
def hash_password(password: str, algorithm: Literal["md5, sha3_512"] = 'md5') -> str:
|
||||
def hash_password(password: str, algorithm: Literal["md5", "sha3_512"] = 'md5') -> str:
|
||||
"""Return the crypted password following the selected algorithm
|
||||
|
||||
Args:
|
||||
@@ -143,16 +190,16 @@ def hash_password(password: str, algorithm: Literal["md5, sha3_512"] = 'md5') ->
|
||||
|
||||
match algorithm:
|
||||
case 'md5':
|
||||
password = md5(password.encode()).hexdigest()
|
||||
return password
|
||||
hashed_password = md5(password.encode()).hexdigest()
|
||||
return hashed_password
|
||||
|
||||
case 'sha3_512':
|
||||
password = sha3_512(password.encode()).hexdigest()
|
||||
return password
|
||||
hashed_password = sha3_512(password.encode()).hexdigest()
|
||||
return hashed_password
|
||||
|
||||
case _:
|
||||
password = md5(password.encode()).hexdigest()
|
||||
return password
|
||||
hashed_password = md5(password.encode()).hexdigest()
|
||||
return hashed_password
|
||||
|
||||
def get_all_modules() -> list[str]:
|
||||
"""Get list of all main modules
|
||||
@@ -177,9 +224,9 @@ def clean_uid(uid: str) -> Optional[str]:
|
||||
return None
|
||||
|
||||
pattern = fr'[:|@|%|\+|~|\*]*'
|
||||
parsed_UID = sub(pattern, '', uid)
|
||||
parsed_uid = sub(pattern, '', uid)
|
||||
|
||||
return parsed_UID
|
||||
return parsed_uid
|
||||
|
||||
def hide_sensitive_data(srvmsg: list[str]) -> list[str]:
|
||||
try:
|
||||
|
||||
18
defender.py
18
defender.py
@@ -1,24 +1,22 @@
|
||||
from core import installation
|
||||
from core import install
|
||||
|
||||
#############################################
|
||||
# @Version : 6.2 #
|
||||
# @Version : 6.3 #
|
||||
# Requierements : #
|
||||
# Python3.10 or higher #
|
||||
# SQLAlchemy, requests, psutil #
|
||||
# unrealircd-rpc-py #
|
||||
# unrealircd-rpc-py, pyyaml #
|
||||
# UnrealIRCD 6.2.2 or higher #
|
||||
#############################################
|
||||
|
||||
try:
|
||||
|
||||
installation.Install()
|
||||
|
||||
# install.update_packages()
|
||||
from core.loader import Loader
|
||||
from core.irc import Irc
|
||||
ircInstance = Irc(Loader())
|
||||
ircInstance.init_irc(ircInstance)
|
||||
loader = Loader()
|
||||
loader.Irc.init_irc()
|
||||
|
||||
except AssertionError as ae:
|
||||
print(f'Assertion Error -> {ae}')
|
||||
except KeyboardInterrupt as k:
|
||||
ircInstance.Base.execute_periodic_action()
|
||||
# ircInstance.Base.execute_periodic_action()
|
||||
...
|
||||
4
mods/clone/language/es/clone-es_1.yaml
Normal file
4
mods/clone/language/es/clone-es_1.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
traduction:
|
||||
# Message help
|
||||
- orig: "Hi my name is clone-es"
|
||||
trad: "Hola mi name is clone-es"
|
||||
6
mods/clone/language/fr/clone-fr_1.yaml
Normal file
6
mods/clone/language/fr/clone-fr_1.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
traduction:
|
||||
# Message help
|
||||
- orig: "You are now logged in"
|
||||
trad: "Vous étes désomais identifier"
|
||||
- orig: "NSUser ==> nsuid: %s | cuid: %s | Account: %s | Nickname: %s | email: %s"
|
||||
trad: "NSUser ==> nsuid: %s | cuid: %s | Compte: %s | Pseudo: %s | email: %s"
|
||||
0
mods/clone/language/fr/clone-fr_2.yaml
Normal file
0
mods/clone/language/fr/clone-fr_2.yaml
Normal file
@@ -1,90 +1,29 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, Optional, Any
|
||||
from core.classes.interfaces.imodule import IModule
|
||||
import mods.clone.utils as utils
|
||||
import mods.clone.threads as thds
|
||||
import mods.clone.schemas as schemas
|
||||
from mods.clone.clone_manager import CloneManager
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
from faker import Faker
|
||||
|
||||
class Clone:
|
||||
class Clone(IModule):
|
||||
|
||||
def __init__(self, irc_instance: 'Irc') -> None:
|
||||
@dataclass
|
||||
class ModConfModel(schemas.ModConfModel):
|
||||
...
|
||||
|
||||
# Module name (Mandatory)
|
||||
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
|
||||
MOD_HEADER: dict[str, str] = {
|
||||
'name':'Clone',
|
||||
'version':'1.0.0',
|
||||
'description':'Connect thousands of clones to your IRCD, by group. You can use them as security moderation.',
|
||||
'author':'Defender Team',
|
||||
'core_version':'Defender-6'
|
||||
}
|
||||
|
||||
# Add Irc Object to the module (Mandatory)
|
||||
self.Irc = irc_instance
|
||||
|
||||
# Add Irc Protocol Object to the module (Mandatory)
|
||||
self.Protocol = irc_instance.Protocol
|
||||
|
||||
# Add Global Configuration to the module (Mandatory)
|
||||
self.Config = irc_instance.Config
|
||||
|
||||
# Add Base object to the module (Mandatory)
|
||||
self.Base = irc_instance.Base
|
||||
|
||||
# Add logs object to the module (Mandatory)
|
||||
self.Logs = irc_instance.Loader.Logs
|
||||
|
||||
# Add User object to the module (Mandatory)
|
||||
self.User = irc_instance.User
|
||||
|
||||
# Add Channel object to the module (Mandatory)
|
||||
self.Channel = irc_instance.Channel
|
||||
|
||||
# Add global definitions
|
||||
self.Definition = irc_instance.Loader.Definition
|
||||
|
||||
# The Global Settings
|
||||
self.Settings = irc_instance.Loader.Settings
|
||||
|
||||
self.Schemas = schemas
|
||||
|
||||
self.Utils = utils
|
||||
|
||||
self.Threads = thds
|
||||
|
||||
self.Faker: Optional['Faker'] = self.Utils.create_faker_object('en_GB')
|
||||
|
||||
self.Clone = CloneManager(self)
|
||||
|
||||
metadata = self.Settings.get_cache('UID_CLONE_DB')
|
||||
|
||||
if metadata is not None:
|
||||
self.Clone.UID_CLONE_DB = metadata
|
||||
self.Logs.debug(f"Cache Size = {self.Settings.get_cache_size()}")
|
||||
|
||||
# Créer les nouvelles commandes du module
|
||||
self.Irc.build_command(1, self.module_name, 'clone', 'Connect, join, part, kill and say clones')
|
||||
|
||||
# Init the module (Mandatory)
|
||||
self.__init_module()
|
||||
|
||||
# Log the module
|
||||
self.Logs.debug(f'Module {self.module_name} loaded ...')
|
||||
|
||||
def __init_module(self) -> None:
|
||||
|
||||
# Créer les tables necessaire a votre module (ce n'es pas obligatoire)
|
||||
self.__create_tables()
|
||||
|
||||
self.stop = False
|
||||
|
||||
# Load module configuration (Mandatory)
|
||||
self.__load_module_configuration()
|
||||
|
||||
self.Channel.db_query_channel(action='add', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
|
||||
self.Protocol.send_join_chan(self.Config.SERVICE_NICKNAME, self.Config.CLONE_CHANNEL)
|
||||
|
||||
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} SAMODE {self.Config.CLONE_CHANNEL} +o {self.Config.SERVICE_NICKNAME}")
|
||||
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +nts")
|
||||
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +k {self.Config.CLONE_CHANNEL_PASSWORD}")
|
||||
|
||||
def __create_tables(self) -> 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
|
||||
|
||||
@@ -104,20 +43,28 @@ class Clone:
|
||||
|
||||
return None
|
||||
|
||||
def __load_module_configuration(self) -> None:
|
||||
"""### Load Module Configuration
|
||||
"""
|
||||
try:
|
||||
# Variable qui va contenir les options de configuration du module Defender
|
||||
self.ModConfig = self.Schemas.ModConfModel()
|
||||
def load(self) -> None:
|
||||
self.ModConfig = self.ModConfModel()
|
||||
self.stop = False
|
||||
self.Schemas = schemas
|
||||
self.Utils = utils
|
||||
self.Threads = thds
|
||||
self.Faker: Optional['Faker'] = self.Utils.create_faker_object('en_GB')
|
||||
self.Clone = CloneManager(self)
|
||||
metadata = self.Settings.get_cache('UID_CLONE_DB')
|
||||
|
||||
# Sync the configuration with core configuration (Mandatory)
|
||||
# self.Base.db_sync_core_config(self.module_name, self.ModConfig)
|
||||
if metadata is not None:
|
||||
self.Clone.UID_CLONE_DB = metadata
|
||||
self.Logs.debug(f"Cache Size = {self.Settings.get_cache_size()}")
|
||||
|
||||
return None
|
||||
# Créer les nouvelles commandes du module
|
||||
self.Irc.build_command(1, self.module_name, 'clone', 'Connect, join, part, kill and say clones')
|
||||
|
||||
except TypeError as te:
|
||||
self.Logs.critical(te)
|
||||
self.Channel.db_query_channel(action='add', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
|
||||
self.Protocol.send_sjoin(self.Config.CLONE_CHANNEL)
|
||||
self.Protocol.send_set_mode('+o', nickname=self.Config.SERVICE_NICKNAME, channel_name=self.Config.CLONE_CHANNEL)
|
||||
self.Protocol.send_set_mode('+nts', channel_name=self.Config.CLONE_CHANNEL)
|
||||
self.Protocol.send_set_mode('+k', channel_name=self.Config.CLONE_CHANNEL, params=self.Config.CLONE_CHANNEL_PASSWORD)
|
||||
|
||||
def unload(self) -> None:
|
||||
"""Cette methode sera executée a chaque désactivation ou
|
||||
@@ -127,10 +74,12 @@ class Clone:
|
||||
self.Settings.set_cache('UID_CLONE_DB', self.Clone.UID_CLONE_DB)
|
||||
|
||||
self.Channel.db_query_channel(action='del', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
|
||||
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} -nts")
|
||||
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} -k {self.Config.CLONE_CHANNEL_PASSWORD}")
|
||||
self.Protocol.send_set_mode('-nts', channel_name=self.Config.CLONE_CHANNEL)
|
||||
self.Protocol.send_set_mode('-k', channel_name=self.Config.CLONE_CHANNEL)
|
||||
self.Protocol.send_part_chan(self.Config.SERVICE_NICKNAME, self.Config.CLONE_CHANNEL)
|
||||
|
||||
self.Irc.Commands.drop_command_by_module(self.module_name)
|
||||
|
||||
return None
|
||||
|
||||
def cmd(self, data:list) -> None:
|
||||
@@ -146,7 +95,8 @@ class Clone:
|
||||
match command:
|
||||
|
||||
case 'PRIVMSG':
|
||||
return self.Utils.handle_on_privmsg(self, cmd)
|
||||
self.Utils.handle_on_privmsg(self, cmd)
|
||||
return None
|
||||
|
||||
case 'QUIT':
|
||||
return None
|
||||
@@ -200,7 +150,7 @@ class Clone:
|
||||
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone connect [number of clone you want to connect] [Group]")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone connect [number of clone you want to connect] [Group] [freq]")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Exemple /msg {dnickname} clone connect 6 Ambiance")
|
||||
|
||||
case 'kill':
|
||||
@@ -230,8 +180,7 @@ class Clone:
|
||||
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill all")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill clone_nickname")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill [all | group name | nickname]")
|
||||
|
||||
case 'join':
|
||||
try:
|
||||
@@ -260,8 +209,7 @@ class Clone:
|
||||
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join all #channel")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join clone_nickname #channel")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join [all | group name | nickname] #channel")
|
||||
|
||||
case 'part':
|
||||
try:
|
||||
@@ -291,8 +239,7 @@ class Clone:
|
||||
|
||||
except Exception as err:
|
||||
self.Logs.error(f'{err}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part all #channel")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part clone_nickname #channel")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part [all | group name | nickname] #channel")
|
||||
|
||||
case 'list':
|
||||
try:
|
||||
|
||||
@@ -174,17 +174,15 @@ def create_new_clone(uplink: 'Clone', faker_instance: 'Faker', group: str = 'Def
|
||||
|
||||
return True
|
||||
|
||||
def handle_on_privmsg(uplink: 'Clone', srvmsg: list[str]):
|
||||
def handle_on_privmsg(uplink: 'Clone', srvmsg: list[str]) -> None:
|
||||
|
||||
uid_sender = uplink.Irc.Utils.clean_uid(srvmsg[1])
|
||||
senderObj = uplink.User.get_user(uid_sender)
|
||||
senderObj, recieverObj, channel, message = uplink.Protocol.parse_privmsg(srvmsg)
|
||||
|
||||
if senderObj is not None:
|
||||
if senderObj.hostname in uplink.Config.CLONE_LOG_HOST_EXEMPT:
|
||||
return
|
||||
|
||||
if not senderObj is None:
|
||||
senderMsg = ' '.join(srvmsg[4:])
|
||||
clone_obj = uplink.Clone.get_clone(srvmsg[3])
|
||||
senderMsg = message
|
||||
clone_obj = recieverObj
|
||||
|
||||
if clone_obj is None:
|
||||
return
|
||||
@@ -196,3 +194,5 @@ def handle_on_privmsg(uplink: 'Clone', srvmsg: list[str]):
|
||||
msg=final_message,
|
||||
channel=uplink.Config.CLONE_CHANNEL
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
from dataclasses import dataclass
|
||||
from core.classes.interfaces.imodule import IModule
|
||||
import mods.command.utils as utils
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
from core.definition import MUser
|
||||
from sqlalchemy import CursorResult, Row, Sequence
|
||||
|
||||
class Command:
|
||||
class Command(IModule):
|
||||
|
||||
@dataclass
|
||||
class ModConfModel:
|
||||
@@ -15,54 +14,62 @@ class Command:
|
||||
"""
|
||||
pass
|
||||
|
||||
def __init__(self, ircInstance: 'Irc') -> None:
|
||||
MOD_HEADER: dict[str, str] = {
|
||||
'name':'Command',
|
||||
'version':'1.0.0',
|
||||
'description':'Module contains all IRC commands',
|
||||
'author':'Defender Team',
|
||||
'core_version':'Defender-6'
|
||||
}
|
||||
|
||||
# Module name (Mandatory)
|
||||
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
|
||||
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 )
|
||||
|
||||
# Add Irc Object to the module (Mandatory)
|
||||
self.Irc = ircInstance
|
||||
Returns:
|
||||
None: Aucun retour n'es attendu
|
||||
"""
|
||||
|
||||
# Add Loader Object to the module (Mandatory)
|
||||
self.Loader = ircInstance.Loader
|
||||
table_automode = '''CREATE TABLE IF NOT EXISTS command_automode (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
created_on TEXT,
|
||||
updated_on TEXT,
|
||||
nickname TEXT,
|
||||
channel TEXT,
|
||||
mode TEXT
|
||||
)
|
||||
'''
|
||||
|
||||
# Add Protocol object to the module (Mandatory)
|
||||
self.Protocol = ircInstance.Protocol
|
||||
|
||||
# Add Global Configuration to the module (Mandatory)
|
||||
self.Config = ircInstance.Config
|
||||
|
||||
# Add Base object to the module (Mandatory)
|
||||
self.Base = ircInstance.Base
|
||||
|
||||
# Add main Utils to the module
|
||||
self.MainUtils = ircInstance.Utils
|
||||
|
||||
# Add logs object to the module (Mandatory)
|
||||
self.Logs = ircInstance.Loader.Logs
|
||||
|
||||
# Add User object to the module (Mandatory)
|
||||
self.User = ircInstance.User
|
||||
|
||||
# Add Client object to the module (Mandatory)
|
||||
self.Client = ircInstance.Client
|
||||
|
||||
# Add Channel object to the module (Mandatory)
|
||||
self.Channel = ircInstance.Channel
|
||||
self.Base.db_execute_query(table_automode)
|
||||
return None
|
||||
|
||||
def load(self) -> None:
|
||||
# Module Utils
|
||||
self.mod_utils = utils
|
||||
|
||||
self.Irc.build_command(1, self.module_name, 'join', 'Join a channel')
|
||||
self.Irc.build_command(1, self.module_name, 'assign', 'Assign a user to a role or task')
|
||||
self.Irc.build_command(1, self.module_name, 'part', 'Leave a channel')
|
||||
self.Irc.build_command(1, self.module_name, 'unassign', 'Remove a user from a role or task')
|
||||
self.Irc.build_command(1, self.module_name, 'owner', 'Give channel ownership to a user')
|
||||
self.Irc.build_command(1, self.module_name, 'deowner', 'Remove channel ownership from a user')
|
||||
self.Irc.build_command(1, self.module_name, 'protect', 'Protect a user from being kicked')
|
||||
self.Irc.build_command(1, self.module_name, 'deprotect', 'Remove protection from a user')
|
||||
self.Irc.build_command(1, self.module_name, 'op', 'Grant operator privileges to a user')
|
||||
self.Irc.build_command(1, self.module_name, 'deop', 'Remove operator privileges from a user')
|
||||
# Build the default configuration model (Mandatory)
|
||||
self.ModConfig = self.ModConfModel()
|
||||
|
||||
self.user_to_notice: str = ''
|
||||
self.show_219: bool = True
|
||||
|
||||
# Register new commands into the protocol
|
||||
new_cmds = {'403', '401', '006', '018', '219', '223'}
|
||||
for c in new_cmds:
|
||||
self.Irc.Protocol.known_protocol.add(c)
|
||||
|
||||
self.Irc.build_command(2, self.module_name, 'join', 'Join a channel')
|
||||
self.Irc.build_command(2, self.module_name, 'assign', 'Assign a user to a role or task')
|
||||
self.Irc.build_command(2, self.module_name, 'part', 'Leave a channel')
|
||||
self.Irc.build_command(2, self.module_name, 'unassign', 'Remove a user from a role or task')
|
||||
self.Irc.build_command(2, self.module_name, 'owner', 'Give channel ownership to a user')
|
||||
self.Irc.build_command(2, self.module_name, 'deowner', 'Remove channel ownership from a user')
|
||||
self.Irc.build_command(2, self.module_name, 'protect', 'Protect a user from being kicked')
|
||||
self.Irc.build_command(2, self.module_name, 'deprotect', 'Remove protection from a user')
|
||||
self.Irc.build_command(2, self.module_name, 'op', 'Grant operator privileges to a user')
|
||||
self.Irc.build_command(2, self.module_name, 'deop', 'Remove operator privileges from a user')
|
||||
self.Irc.build_command(1, self.module_name, 'halfop', 'Grant half-operator privileges to a user')
|
||||
self.Irc.build_command(1, self.module_name, 'dehalfop', 'Remove half-operator privileges from a user')
|
||||
self.Irc.build_command(1, self.module_name, 'voice', 'Grant voice privileges to a user')
|
||||
@@ -104,75 +111,8 @@ class Command:
|
||||
self.Irc.build_command(2, self.module_name, 'klinelist', 'List all K-line bans')
|
||||
self.Irc.build_command(3, self.module_name, 'map', 'Show the server network map')
|
||||
|
||||
# Init the module
|
||||
self.__init_module()
|
||||
|
||||
# Log the module
|
||||
self.Logs.debug(f'-- Module {self.module_name} loaded ...')
|
||||
|
||||
def __init_module(self) -> None:
|
||||
|
||||
# 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.user_to_notice: str = ''
|
||||
self.show_219: bool = True
|
||||
|
||||
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_automode = '''CREATE TABLE IF NOT EXISTS command_automode (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
created_on TEXT,
|
||||
updated_on TEXT,
|
||||
nickname TEXT,
|
||||
channel TEXT,
|
||||
mode TEXT
|
||||
)
|
||||
'''
|
||||
|
||||
self.Base.db_execute_query(table_automode)
|
||||
return None
|
||||
|
||||
def __load_module_configuration(self) -> None:
|
||||
"""### Load Module Configuration
|
||||
"""
|
||||
try:
|
||||
# Build the default configuration model (Mandatory)
|
||||
self.ModConfig = self.ModConfModel()
|
||||
|
||||
# Sync the configuration with core configuration (Mandatory)
|
||||
self.Base.db_sync_core_config(self.module_name, self.ModConfig)
|
||||
|
||||
return None
|
||||
|
||||
except TypeError as te:
|
||||
self.Logs.critical(te)
|
||||
|
||||
def __update_configuration(self, param_key: str, param_value: str):
|
||||
"""Update the local and core configuration
|
||||
|
||||
Args:
|
||||
param_key (str): The parameter key
|
||||
param_value (str): The parameter value
|
||||
"""
|
||||
self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
|
||||
|
||||
def unload(self) -> None:
|
||||
|
||||
self.Irc.Commands.drop_command_by_module(self.module_name)
|
||||
return None
|
||||
|
||||
def cmd(self, data: list[str]) -> None:
|
||||
@@ -186,10 +126,12 @@ class Command:
|
||||
nogc = self.Config.COLORS.nogc
|
||||
cmd = list(data).copy()
|
||||
|
||||
if len(cmd) < 2:
|
||||
pos, parsed_cmd = self.Irc.Protocol.get_ircd_protocol_poisition(cmd=cmd, log=True)
|
||||
|
||||
if pos == -1:
|
||||
return None
|
||||
|
||||
match cmd[1]:
|
||||
match parsed_cmd:
|
||||
# [':irc.deb.biz.st', '403', 'Dev-PyDefender', '#Z', ':No', 'such', 'channel']
|
||||
case '403' | '401':
|
||||
try:
|
||||
@@ -260,22 +202,10 @@ class Command:
|
||||
except Exception as err:
|
||||
self.Logs.warning(f'Unknown Error: {str(err)}')
|
||||
|
||||
case _:
|
||||
pass
|
||||
|
||||
if len(cmd) < 3:
|
||||
return None
|
||||
|
||||
match cmd[2]:
|
||||
|
||||
case 'SJOIN':
|
||||
# ['@msgid=yldTlbwAGbzCGUcCIHi3ku;time=2024-11-11T17:56:24.297Z', ':001', 'SJOIN', '1728815963', '#znc', ':001LQ0L0C']
|
||||
# Check if the user has an automode
|
||||
try:
|
||||
|
||||
if len(cmd) < 6:
|
||||
return None
|
||||
|
||||
user_uid = self.User.clean_uid(cmd[5])
|
||||
userObj: MUser = self.User.get_user(user_uid)
|
||||
channel_name = cmd[4] if self.Channel.is_valid_channel(cmd[4]) else None
|
||||
@@ -301,6 +231,9 @@ class Command:
|
||||
except KeyError as ke:
|
||||
self.Logs.error(f"Key Error: {err}")
|
||||
|
||||
case _:
|
||||
pass
|
||||
|
||||
except Exception as err:
|
||||
self.Logs.error(f"General Error: {err}")
|
||||
|
||||
|
||||
@@ -134,17 +134,20 @@ def set_operation(uplink: 'Command', cmd: list[str], channel_name: Optional[str]
|
||||
return False
|
||||
|
||||
if len(cmd) == 1:
|
||||
uplink.Protocol.send2socket(f":{dnickname} MODE {channel_name} {mode} {client}")
|
||||
# uplink.Protocol.send2socket(f":{service_id} MODE {channel_name} {mode} {client}")
|
||||
uplink.Protocol.send_set_mode(mode, nickname=client, channel_name=channel_name)
|
||||
return None
|
||||
|
||||
# deop nickname
|
||||
if len(cmd) == 2:
|
||||
nickname = cmd[1]
|
||||
uplink.Protocol.send2socket(f":{service_id} MODE {channel_name} {mode} {nickname}")
|
||||
# uplink.Protocol.send2socket(f":{service_id} MODE {channel_name} {mode} {nickname}")
|
||||
uplink.Protocol.send_set_mode(mode, nickname=nickname, channel_name=channel_name)
|
||||
return None
|
||||
|
||||
nickname = cmd[2]
|
||||
uplink.Protocol.send2socket(f":{service_id} MODE {channel_name} {mode} {nickname}")
|
||||
# uplink.Protocol.send2socket(f":{service_id} MODE {channel_name} {mode} {nickname}")
|
||||
uplink.Protocol.send_set_mode(mode, nickname=nickname, channel_name=channel_name)
|
||||
return None
|
||||
|
||||
def set_ban(uplink: 'Command', cmd: list[str], action: Literal['+', '-'], client: str) -> None:
|
||||
|
||||
@@ -1,129 +1,26 @@
|
||||
import traceback
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
from core.classes.interfaces.imodule import IModule
|
||||
import mods.defender.schemas as schemas
|
||||
import mods.defender.utils as utils
|
||||
import mods.defender.threads as thds
|
||||
from typing import TYPE_CHECKING
|
||||
from core.utils import tr
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
class Defender(IModule):
|
||||
|
||||
class Defender:
|
||||
@dataclass
|
||||
class ModConfModel(schemas.ModConfModel):
|
||||
...
|
||||
|
||||
def __init__(self, irc_instance: 'Irc') -> None:
|
||||
MOD_HEADER: dict[str, str] = {
|
||||
'name':'Defender',
|
||||
'version':'1.0.0',
|
||||
'description':'Defender main module that uses the reputation security.',
|
||||
'author':'Defender Team',
|
||||
'core_version':'Defender-6'
|
||||
}
|
||||
|
||||
# Module name (Mandatory)
|
||||
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
|
||||
|
||||
# Add Irc Object to the module (Mandatory)
|
||||
self.Irc = irc_instance
|
||||
|
||||
# Add Loader Object to the module (Mandatory)
|
||||
self.Loader = irc_instance.Loader
|
||||
|
||||
# Add server protocol Object to the module (Mandatory)
|
||||
self.Protocol = irc_instance.Protocol
|
||||
|
||||
# Add Global Configuration to the module (Mandatory)
|
||||
self.Config = irc_instance.Config
|
||||
|
||||
# Add Base object to the module (Mandatory)
|
||||
self.Base = irc_instance.Base
|
||||
|
||||
# Add logs object to the module (Mandatory)
|
||||
self.Logs = irc_instance.Loader.Logs
|
||||
|
||||
# Add User object to the module (Mandatory)
|
||||
self.User = irc_instance.User
|
||||
|
||||
# Add Channel object to the module (Mandatory)
|
||||
self.Channel = irc_instance.Channel
|
||||
|
||||
# Add Settings object to save objects when reloading modules (Mandatory)
|
||||
self.Settings = irc_instance.Settings
|
||||
|
||||
# Add Reputation object to the module (Optional)
|
||||
self.Reputation = irc_instance.Reputation
|
||||
|
||||
# Add module schemas
|
||||
self.Schemas = schemas
|
||||
|
||||
# Add utils functions
|
||||
self.Utils = utils
|
||||
|
||||
# Create module commands (Mandatory)
|
||||
self.Irc.build_command(0, self.module_name, 'code', 'Display the code or key for access')
|
||||
self.Irc.build_command(1, self.module_name, 'info', 'Provide information about the channel or server')
|
||||
self.Irc.build_command(1, self.module_name, 'autolimit', 'Automatically set channel user limits')
|
||||
self.Irc.build_command(3, self.module_name, 'reputation', 'Check or manage user reputation')
|
||||
self.Irc.build_command(3, self.module_name, 'proxy_scan', 'Scan users for proxy connections')
|
||||
self.Irc.build_command(3, self.module_name, 'flood', 'Handle flood detection and mitigation')
|
||||
self.Irc.build_command(3, self.module_name, 'status', 'Check the status of the server or bot')
|
||||
self.Irc.build_command(3, self.module_name, 'timer', 'Set or manage timers')
|
||||
self.Irc.build_command(3, self.module_name, 'show_reputation', 'Display reputation information')
|
||||
self.Irc.build_command(3, self.module_name, 'sentinel', 'Monitor and guard the channel or server')
|
||||
|
||||
# Init the module (Mandatory)
|
||||
self.__init_module()
|
||||
|
||||
# Log the module
|
||||
self.Logs.debug(f'-- Module {self.module_name} V2 loaded ...')
|
||||
|
||||
def __init_module(self) -> None:
|
||||
|
||||
# Create you own tables if needed (Mandatory)
|
||||
self.__create_tables()
|
||||
|
||||
# Load module configuration (Mandatory)
|
||||
self.__load_module_configuration()
|
||||
# End of mandatory methods you can start your customization #
|
||||
|
||||
self.timeout = self.Config.API_TIMEOUT
|
||||
|
||||
# Listes qui vont contenir les ip a scanner avec les différentes API
|
||||
self.Schemas.DB_ABUSEIPDB_USERS = []
|
||||
self.Schemas.DB_FREEIPAPI_USERS = []
|
||||
self.Schemas.DB_CLOUDFILT_USERS = []
|
||||
self.Schemas.DB_PSUTIL_USERS = []
|
||||
self.Schemas.DB_LOCALSCAN_USERS = []
|
||||
|
||||
# Variables qui indique que les threads sont en cours d'éxecutions
|
||||
self.abuseipdb_isRunning:bool = True
|
||||
self.freeipapi_isRunning:bool = True
|
||||
self.cloudfilt_isRunning:bool = True
|
||||
self.psutil_isRunning:bool = True
|
||||
self.localscan_isRunning:bool = True
|
||||
self.reputationTimer_isRunning:bool = True
|
||||
self.autolimit_isRunning: bool = True
|
||||
|
||||
# Variable qui va contenir les users
|
||||
self.flood_system = {}
|
||||
|
||||
# Contient les premieres informations de connexion
|
||||
self.reputation_first_connexion = {'ip': '', 'score': -1}
|
||||
|
||||
# Laisser vide si aucune clé
|
||||
self.abuseipdb_key = '13c34603fee4d2941a2c443cc5c77fd750757ca2a2c1b304bd0f418aff80c24be12651d1a3cfe674'
|
||||
self.cloudfilt_key = 'r1gEtjtfgRQjtNBDMxsg'
|
||||
|
||||
# Démarrer les threads pour démarrer les api
|
||||
self.Base.create_thread(func=thds.thread_freeipapi_scan, func_args=(self, ))
|
||||
self.Base.create_thread(func=thds.thread_cloudfilt_scan, func_args=(self, ))
|
||||
self.Base.create_thread(func=thds.thread_abuseipdb_scan, func_args=(self, ))
|
||||
self.Base.create_thread(func=thds.thread_local_scan, func_args=(self, ))
|
||||
self.Base.create_thread(func=thds.thread_psutil_scan, func_args=(self, ))
|
||||
|
||||
self.Base.create_thread(func=thds.thread_apply_reputation_sanctions, func_args=(self, ))
|
||||
|
||||
if self.ModConfig.autolimit == 1:
|
||||
self.Base.create_thread(func=thds.thread_autolimit, func_args=(self, ))
|
||||
|
||||
if self.ModConfig.reputation == 1:
|
||||
self.Protocol.send_sjoin(self.Config.SALON_JAIL)
|
||||
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} SAMODE {self.Config.SALON_JAIL} +o {self.Config.SERVICE_NICKNAME}")
|
||||
|
||||
return None
|
||||
|
||||
def __create_tables(self) -> None:
|
||||
def create_tables(self) -> None:
|
||||
"""Methode qui va créer la base de donnée si elle n'existe pas.
|
||||
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
|
||||
Args:
|
||||
@@ -146,20 +43,63 @@ class Defender:
|
||||
# self.Base.db_execute_query(table_trusted)
|
||||
return None
|
||||
|
||||
def __load_module_configuration(self) -> None:
|
||||
"""### Load Module Configuration
|
||||
"""
|
||||
def load(self):
|
||||
|
||||
# Add module schemas
|
||||
self.Schemas = schemas
|
||||
|
||||
# Add utils functions
|
||||
self.Utils = utils
|
||||
|
||||
# Variable qui va contenir les options de configuration du module Defender
|
||||
self.ModConfig = self.Schemas.ModConfModel()
|
||||
self.ModConfig: schemas.ModConfModel = self.ModConfModel()
|
||||
|
||||
# Sync the configuration with core configuration (Mandatory)
|
||||
self.Base.db_sync_core_config(self.module_name, self.ModConfig)
|
||||
# Create module commands (Mandatory)
|
||||
self.Irc.build_command(0, self.module_name, 'code', 'Display the code or key for access')
|
||||
self.Irc.build_command(1, self.module_name, 'info', 'Provide information about the channel or server')
|
||||
self.Irc.build_command(1, self.module_name, 'autolimit', 'Automatically set channel user limits')
|
||||
self.Irc.build_command(3, self.module_name, 'reputation', 'Check or manage user reputation')
|
||||
self.Irc.build_command(3, self.module_name, 'proxy_scan', 'Scan users for proxy connections')
|
||||
self.Irc.build_command(3, self.module_name, 'flood', 'Handle flood detection and mitigation')
|
||||
self.Irc.build_command(3, self.module_name, 'status', 'Check the status of the server or bot')
|
||||
self.Irc.build_command(3, self.module_name, 'show_reputation', 'Display reputation information')
|
||||
self.Irc.build_command(3, self.module_name, 'sentinel', 'Monitor and guard the channel or server')
|
||||
|
||||
return None
|
||||
self.timeout = self.Config.API_TIMEOUT
|
||||
|
||||
def __update_configuration(self, param_key: str, param_value: str):
|
||||
# Listes qui vont contenir les ip a scanner avec les différentes API
|
||||
self.Schemas.DB_ABUSEIPDB_USERS = self.Schemas.DB_FREEIPAPI_USERS = self.Schemas.DB_CLOUDFILT_USERS = []
|
||||
self.Schemas.DB_PSUTIL_USERS = self.Schemas.DB_LOCALSCAN_USERS = []
|
||||
|
||||
self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
|
||||
# Variables qui indique que les threads sont en cours d'éxecutions
|
||||
self.abuseipdb_isRunning = self.freeipapi_isRunning = self.cloudfilt_isRunning = True
|
||||
self.psutil_isRunning = self.localscan_isRunning = self.reputationTimer_isRunning = True
|
||||
self.autolimit_isRunning = True
|
||||
|
||||
# Variable qui va contenir les users
|
||||
self.flood_system = {}
|
||||
|
||||
# Contient les premieres informations de connexion
|
||||
self.reputation_first_connexion = {'ip': '', 'score': -1}
|
||||
|
||||
# Laisser vide si aucune clé
|
||||
self.abuseipdb_key = '13c34603fee4d2941a2c443cc5c77fd750757ca2a2c1b304bd0f418aff80c24be12651d1a3cfe674'
|
||||
self.cloudfilt_key = 'r1gEtjtfgRQjtNBDMxsg'
|
||||
|
||||
# Démarrer les threads pour démarrer les api
|
||||
self.Base.create_thread(func=thds.thread_freeipapi_scan, func_args=(self, ))
|
||||
self.Base.create_thread(func=thds.thread_cloudfilt_scan, func_args=(self, ))
|
||||
self.Base.create_thread(func=thds.thread_abuseipdb_scan, func_args=(self, ))
|
||||
self.Base.create_thread(func=thds.thread_local_scan, func_args=(self, ))
|
||||
self.Base.create_thread(func=thds.thread_psutil_scan, func_args=(self, ))
|
||||
self.Base.create_thread(func=thds.thread_apply_reputation_sanctions, func_args=(self, ))
|
||||
|
||||
if self.ModConfig.autolimit == 1:
|
||||
self.Base.create_thread(func=thds.thread_autolimit, func_args=(self, ))
|
||||
|
||||
if self.ModConfig.reputation == 1:
|
||||
self.Protocol.send_sjoin(self.Config.SALON_JAIL)
|
||||
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} SAMODE {self.Config.SALON_JAIL} +o {self.Config.SERVICE_NICKNAME}")
|
||||
|
||||
def __onload(self):
|
||||
|
||||
@@ -202,12 +142,17 @@ class Defender:
|
||||
self.reputationTimer_isRunning:bool = False
|
||||
self.autolimit_isRunning: bool = False
|
||||
|
||||
self.Irc.Commands.drop_command_by_module(self.module_name)
|
||||
|
||||
return None
|
||||
|
||||
def insert_db_trusted(self, uid: str, nickname:str) -> None:
|
||||
u = self.User.get_user(uid)
|
||||
if u is None:
|
||||
return None
|
||||
|
||||
uid = self.User.get_uid(uid)
|
||||
nickname = self.User.get_nickname(nickname)
|
||||
uid = u.uid
|
||||
nickname = u.nickname
|
||||
|
||||
query = "SELECT id FROM def_trusted WHERE user = ?"
|
||||
exec_query = self.Base.db_execute_query(query, {"user": nickname})
|
||||
@@ -215,7 +160,7 @@ class Defender:
|
||||
|
||||
if response is not None:
|
||||
q_insert = "INSERT INTO def_trusted (datetime, user, host, vhost) VALUES (?, ?, ?, ?)"
|
||||
mes_donnees = {'datetime': self.Base.get_datetime(), 'user': nickname, 'host': '*', 'vhost': '*'}
|
||||
mes_donnees = {'datetime': self.Loader.Utils.get_datetime(), 'user': nickname, 'host': '*', 'vhost': '*'}
|
||||
exec_query = self.Base.db_execute_query(q_insert, mes_donnees)
|
||||
pass
|
||||
|
||||
@@ -243,29 +188,6 @@ class Defender:
|
||||
except Exception as err:
|
||||
self.Logs.error(f"General Error: {err}")
|
||||
|
||||
def run_db_action_timer(self, wait_for: float = 0) -> None:
|
||||
|
||||
query = f"SELECT param_key FROM {self.Config.TABLE_CONFIG}"
|
||||
res = self.Base.db_execute_query(query)
|
||||
service_id = self.Config.SERVICE_ID
|
||||
dchanlog = self.Config.SERVICE_CHANLOG
|
||||
|
||||
for param in res.fetchall():
|
||||
if param[0] == 'reputation':
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=service_id,
|
||||
msg=f" ===> {param[0]}",
|
||||
channel=dchanlog
|
||||
)
|
||||
else:
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=service_id,
|
||||
msg=f"{param[0]}",
|
||||
channel=dchanlog
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
def cmd(self, data: list[str]) -> None:
|
||||
|
||||
if not data or len(data) < 2:
|
||||
@@ -296,7 +218,6 @@ class Defender:
|
||||
return None
|
||||
|
||||
case 'SJOIN':
|
||||
|
||||
self.Utils.handle_on_sjoin(self, cmd)
|
||||
return None
|
||||
|
||||
@@ -320,13 +241,15 @@ class Defender:
|
||||
except IndexError as ie:
|
||||
self.Logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
|
||||
except Exception as err:
|
||||
self.Logs.error(f"General Error: {err}")
|
||||
traceback.print_exc()
|
||||
self.Logs.error(f"General Error: {err}", exc_info=True)
|
||||
|
||||
def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
|
||||
def hcmds(self, user: str, channel: Any, cmd: list, fullcmd: list = []) -> None:
|
||||
u = self.User.get_user(user)
|
||||
if u is None:
|
||||
return None
|
||||
|
||||
command = str(cmd[0]).lower()
|
||||
fromuser = user
|
||||
fromuser = u.nickname
|
||||
channel = fromchannel = channel if self.Channel.is_valid_channel(channel) else None
|
||||
|
||||
dnickname = self.Config.SERVICE_NICKNAME # Defender nickname
|
||||
@@ -338,17 +261,6 @@ class Defender:
|
||||
|
||||
match command:
|
||||
|
||||
case 'timer':
|
||||
try:
|
||||
timer_sent = self.Base.int_if_possible(cmd[1])
|
||||
timer_sent = int(timer_sent)
|
||||
self.Base.create_timer(timer_sent, self.run_db_action_timer)
|
||||
|
||||
except TypeError as te:
|
||||
self.Logs.error(f"Type Error -> {te}")
|
||||
except ValueError as ve:
|
||||
self.Logs.error(f"Value Error -> {ve}")
|
||||
|
||||
case 'show_reputation':
|
||||
|
||||
if not self.Reputation.UID_REPUTATION_DB:
|
||||
@@ -362,9 +274,9 @@ class Defender:
|
||||
case 'code':
|
||||
try:
|
||||
release_code = cmd[1]
|
||||
jailed_nickname = self.User.get_nickname(fromuser)
|
||||
jailed_UID = self.User.get_uid(fromuser)
|
||||
get_reputation = self.Reputation.get_Reputation(jailed_UID)
|
||||
jailed_nickname = u.nickname
|
||||
jailed_UID = u.uid
|
||||
get_reputation = self.Reputation.get_reputation(jailed_UID)
|
||||
|
||||
if get_reputation is None:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=" No code is requested ...")
|
||||
@@ -393,7 +305,7 @@ class Defender:
|
||||
self.Protocol.send_sapart(nick_to_sapart=jailed_nickname, channel_name=jailed_salon)
|
||||
self.Protocol.send_sajoin(nick_to_sajoin=jailed_nickname, channel_name=welcome_salon)
|
||||
self.Protocol.send2socket(f":{link} REPUTATION {jailed_IP} {self.ModConfig.reputation_score_after_release}")
|
||||
self.User.get_user(jailed_UID).score_connexion = reputation_seuil + 1
|
||||
u.score_connexion = reputation_seuil + 1
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"[{color_green} MOT DE PASS CORRECT {color_black}] : You have now the right to enjoy the network !",
|
||||
nick_to=jailed_nickname)
|
||||
@@ -430,7 +342,7 @@ class Defender:
|
||||
match arg:
|
||||
case 'on':
|
||||
if self.ModConfig.autolimit == 0:
|
||||
self.__update_configuration('autolimit', 1)
|
||||
self.update_configuration('autolimit', 1)
|
||||
self.autolimit_isRunning = True
|
||||
self.Base.create_thread(func=thds.thread_autolimit, func_args=(self, ))
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[{self.Config.COLORS.green}AUTOLIMIT{self.Config.COLORS.nogc}] Activated", channel=self.Config.SERVICE_CHANLOG)
|
||||
@@ -439,7 +351,7 @@ class Defender:
|
||||
|
||||
case 'off':
|
||||
if self.ModConfig.autolimit == 1:
|
||||
self.__update_configuration('autolimit', 0)
|
||||
self.update_configuration('autolimit', 0)
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[{self.Config.COLORS.green}AUTOLIMIT{self.Config.COLORS.nogc}] Deactivated", channel=self.Config.SERVICE_CHANLOG)
|
||||
else:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[{self.Config.COLORS.red}AUTOLIMIT{self.Config.COLORS.nogc}] Already Deactivated", channel=self.Config.SERVICE_CHANLOG)
|
||||
@@ -448,8 +360,8 @@ class Defender:
|
||||
amount = int(cmd[2])
|
||||
interval = int(cmd[3])
|
||||
|
||||
self.__update_configuration('autolimit_amount', amount)
|
||||
self.__update_configuration('autolimit_interval', interval)
|
||||
self.update_configuration('autolimit_amount', amount)
|
||||
self.update_configuration('autolimit_interval', interval)
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=dnickname,
|
||||
msg=f"[{self.Config.COLORS.green}AUTOLIMIT{self.Config.COLORS.nogc}] Amount set to ({amount}) | Interval set to ({interval})",
|
||||
@@ -488,7 +400,7 @@ class Defender:
|
||||
return False
|
||||
|
||||
# self.update_db_configuration('reputation', 1)
|
||||
self.__update_configuration(key, 1)
|
||||
self.update_configuration(key, 1)
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.Config.COLORS.green}REPUTATION{self.Config.COLORS.black} ] : Activated by {fromuser}", channel=dchanlog)
|
||||
|
||||
@@ -514,7 +426,7 @@ class Defender:
|
||||
)
|
||||
return False
|
||||
|
||||
self.__update_configuration(key, 0)
|
||||
self.update_configuration(key, 0)
|
||||
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=dnickname,
|
||||
@@ -550,7 +462,7 @@ class Defender:
|
||||
msg=f"This nickname ({str(cmd[2])}) is not connected to the network!")
|
||||
return None
|
||||
|
||||
client_to_release = self.Reputation.get_Reputation(client_obj.uid)
|
||||
client_to_release = self.Reputation.get_reputation(client_obj.uid)
|
||||
|
||||
if client_to_release is None:
|
||||
p.send_notice(nick_from=dnickname,
|
||||
@@ -603,7 +515,7 @@ class Defender:
|
||||
return False
|
||||
|
||||
# self.update_db_configuration(key, 1)
|
||||
self.__update_configuration(key, 1)
|
||||
self.update_configuration(key, 1)
|
||||
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=dnickname,
|
||||
@@ -621,7 +533,7 @@ class Defender:
|
||||
return False
|
||||
|
||||
# self.update_db_configuration(key, 0)
|
||||
self.__update_configuration(key, 0)
|
||||
self.update_configuration(key, 0)
|
||||
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=dnickname,
|
||||
@@ -634,7 +546,7 @@ class Defender:
|
||||
key = 'reputation_seuil'
|
||||
|
||||
# self.update_db_configuration(key, reputation_seuil)
|
||||
self.__update_configuration(key, reputation_seuil)
|
||||
self.update_configuration(key, reputation_seuil)
|
||||
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=dnickname,
|
||||
@@ -646,7 +558,7 @@ class Defender:
|
||||
case 'timer':
|
||||
reputation_timer = int(cmd[3])
|
||||
key = 'reputation_timer'
|
||||
self.__update_configuration(key, reputation_timer)
|
||||
self.update_configuration(key, reputation_timer)
|
||||
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=dnickname,
|
||||
@@ -658,7 +570,7 @@ class Defender:
|
||||
case 'score_after_release':
|
||||
reputation_score_after_release = int(cmd[3])
|
||||
key = 'reputation_score_after_release'
|
||||
self.__update_configuration(key, reputation_score_after_release)
|
||||
self.update_configuration(key, reputation_score_after_release)
|
||||
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=dnickname,
|
||||
@@ -670,7 +582,7 @@ class Defender:
|
||||
case 'security_group':
|
||||
reputation_sg = int(cmd[3])
|
||||
key = 'reputation_sg'
|
||||
self.__update_configuration(key, reputation_sg)
|
||||
self.update_configuration(key, reputation_sg)
|
||||
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=dnickname,
|
||||
@@ -732,7 +644,7 @@ class Defender:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog)
|
||||
return None
|
||||
|
||||
self.__update_configuration(option, 1)
|
||||
self.update_configuration(option, 1)
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog)
|
||||
elif action == 'off':
|
||||
@@ -740,7 +652,7 @@ class Defender:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated", channel=dchanlog)
|
||||
return None
|
||||
|
||||
self.__update_configuration(option, 0)
|
||||
self.update_configuration(option, 0)
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog)
|
||||
|
||||
@@ -750,7 +662,7 @@ class Defender:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog)
|
||||
return None
|
||||
|
||||
self.__update_configuration(option, 1)
|
||||
self.update_configuration(option, 1)
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog)
|
||||
elif action == 'off':
|
||||
@@ -758,7 +670,7 @@ class Defender:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated", channel=dchanlog)
|
||||
return None
|
||||
|
||||
self.__update_configuration(option, 0)
|
||||
self.update_configuration(option, 0)
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog)
|
||||
|
||||
@@ -768,7 +680,7 @@ class Defender:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog)
|
||||
return None
|
||||
|
||||
self.__update_configuration(option, 1)
|
||||
self.update_configuration(option, 1)
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog)
|
||||
elif action == 'off':
|
||||
@@ -776,7 +688,7 @@ class Defender:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated", channel=dchanlog)
|
||||
return None
|
||||
|
||||
self.__update_configuration(option, 0)
|
||||
self.update_configuration(option, 0)
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog)
|
||||
|
||||
@@ -786,7 +698,7 @@ class Defender:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog)
|
||||
return None
|
||||
|
||||
self.__update_configuration(option, 1)
|
||||
self.update_configuration(option, 1)
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog)
|
||||
elif action == 'off':
|
||||
@@ -794,7 +706,7 @@ class Defender:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated", channel=dchanlog)
|
||||
return None
|
||||
|
||||
self.__update_configuration(option, 0)
|
||||
self.update_configuration(option, 0)
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog)
|
||||
|
||||
@@ -804,7 +716,7 @@ class Defender:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog)
|
||||
return None
|
||||
|
||||
self.__update_configuration(option, 1)
|
||||
self.update_configuration(option, 1)
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog)
|
||||
elif action == 'off':
|
||||
@@ -812,7 +724,7 @@ class Defender:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated", channel=dchanlog)
|
||||
return None
|
||||
|
||||
self.__update_configuration(option, 0)
|
||||
self.update_configuration(option, 0)
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog)
|
||||
|
||||
@@ -845,7 +757,7 @@ class Defender:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Already activated", channel=dchanlog)
|
||||
return False
|
||||
|
||||
self.__update_configuration(key, 1)
|
||||
self.update_configuration(key, 1)
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Activated by {fromuser}", channel=dchanlog)
|
||||
|
||||
@@ -854,7 +766,7 @@ class Defender:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.Config.COLORS.red}FLOOD{self.Config.COLORS.black} ] : Already Deactivated", channel=dchanlog)
|
||||
return False
|
||||
|
||||
self.__update_configuration(key, 0)
|
||||
self.update_configuration(key, 0)
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Deactivated by {fromuser}", channel=dchanlog)
|
||||
|
||||
@@ -866,7 +778,7 @@ class Defender:
|
||||
case 'flood_message':
|
||||
key = 'flood_message'
|
||||
set_value = int(cmd[3])
|
||||
self.__update_configuration(key, set_value)
|
||||
self.update_configuration(key, set_value)
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Flood message set to {set_value} by {fromuser}",
|
||||
@@ -875,7 +787,7 @@ class Defender:
|
||||
case 'flood_time':
|
||||
key = 'flood_time'
|
||||
set_value = int(cmd[3])
|
||||
self.__update_configuration(key, set_value)
|
||||
self.update_configuration(key, set_value)
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Flood time set to {set_value} by {fromuser}",
|
||||
@@ -884,7 +796,7 @@ class Defender:
|
||||
case 'flood_timer':
|
||||
key = 'flood_timer'
|
||||
set_value = int(cmd[3])
|
||||
self.__update_configuration(key, set_value)
|
||||
self.update_configuration(key, set_value)
|
||||
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Flood timer set to {set_value} by {fromuser}",
|
||||
@@ -921,6 +833,7 @@ class Defender:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_message ==> {self.ModConfig.flood_message}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_time ==> {self.ModConfig.flood_time}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_timer ==> {self.ModConfig.flood_timer}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' [{color_green if self.ModConfig.flood == 1 else color_red}Sentinel{nogc}] ==> {self.ModConfig.sentinel}')
|
||||
except KeyError as ke:
|
||||
self.Logs.error(f"Key Error : {ke}")
|
||||
|
||||
@@ -948,7 +861,7 @@ class Defender:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' WebWebsocket : {UserObject.isWebsocket}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' REPUTATION : {UserObject.score_connexion}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' MODES : {UserObject.umodes}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' CHANNELS : {channels}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' CHANNELS : {", ".join(channels)}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' CONNECTION TIME : {UserObject.connexion_datetime}')
|
||||
else:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"This user {nickoruid} doesn't exist")
|
||||
@@ -962,14 +875,27 @@ class Defender:
|
||||
channel_to_dont_quit = [self.Config.SALON_JAIL, self.Config.SERVICE_CHANLOG]
|
||||
|
||||
if activation == 'on':
|
||||
result = self.Base.db_execute_query(f"SELECT distinct channel_name FROM {self.Config.TABLE_CHANNEL}")
|
||||
channels = result.fetchall()
|
||||
channel_in_db = [channel[0] for channel in channels]
|
||||
channel_to_dont_quit.extend(channel_in_db)
|
||||
|
||||
self.update_configuration('sentinel', 1)
|
||||
for chan in self.Channel.UID_CHANNEL_DB:
|
||||
if chan.name not in channel_to_dont_quit:
|
||||
self.Protocol.send_join_chan(uidornickname=dnickname, channel=chan.name)
|
||||
self.Protocol.send_priv_msg(dnickname, f"Sentinel mode activated on {channel}", channel=chan.name)
|
||||
return None
|
||||
|
||||
if activation == 'off':
|
||||
result = self.Base.db_execute_query(f"SELECT distinct channel_name FROM {self.Config.TABLE_CHANNEL}")
|
||||
channels = result.fetchall()
|
||||
channel_in_db = [channel[0] for channel in channels]
|
||||
channel_to_dont_quit.extend(channel_in_db)
|
||||
self.update_configuration('sentinel', 0)
|
||||
for chan in self.Channel.UID_CHANNEL_DB:
|
||||
if chan.name not in channel_to_dont_quit:
|
||||
self.Protocol.send_part_chan(uidornickname=dnickname, channel=chan.name)
|
||||
self.Protocol.send_priv_msg(dnickname, f"Sentinel mode deactivated on {channel}", channel=chan.name)
|
||||
self.join_saved_channels()
|
||||
return None
|
||||
|
||||
@@ -20,6 +20,7 @@ class ModConfModel(MainModel):
|
||||
autolimit: int = 0
|
||||
autolimit_amount: int = 3
|
||||
autolimit_interval: int = 3
|
||||
sentinel: int = 0
|
||||
|
||||
@dataclass
|
||||
class FloodUser(MainModel):
|
||||
|
||||
@@ -62,6 +62,11 @@ def handle_on_mode(uplink: 'Defender', srvmsg: list[str]):
|
||||
|
||||
def handle_on_privmsg(uplink: 'Defender', srvmsg: list[str]):
|
||||
# ['@mtag....',':python', 'PRIVMSG', '#defender', ':zefzefzregreg', 'regg', 'aerg']
|
||||
|
||||
sender, reciever, channel, message = uplink.Protocol.parse_privmsg(srvmsg)
|
||||
if uplink.ModConfig.sentinel == 1 and channel.name != uplink.Config.SERVICE_CHANLOG:
|
||||
uplink.Protocol.send_priv_msg(uplink.Config.SERVICE_NICKNAME, f"{sender.nickname} say on {channel.name}: {' '.join(message)}", uplink.Config.SERVICE_CHANLOG)
|
||||
|
||||
action_on_flood(uplink, srvmsg)
|
||||
return None
|
||||
|
||||
@@ -87,7 +92,7 @@ def handle_on_sjoin(uplink: 'Defender', srvmsg: list[str]):
|
||||
return
|
||||
|
||||
if confmodel.reputation == 1:
|
||||
get_reputation = irc.Reputation.get_Reputation(parsed_UID)
|
||||
get_reputation = irc.Reputation.get_reputation(parsed_UID)
|
||||
|
||||
if parsed_chan != gconfig.SALON_JAIL:
|
||||
p.send2socket(f":{gconfig.SERVICE_ID} MODE {parsed_chan} +b ~security-group:unknown-users")
|
||||
@@ -138,18 +143,25 @@ def handle_on_slog(uplink: 'Defender', srvmsg: list[str]):
|
||||
return None
|
||||
|
||||
def handle_on_nick(uplink: 'Defender', srvmsg: list[str]):
|
||||
"""_summary_
|
||||
"""Handle nickname changes.
|
||||
>>> srvmsg = ['@unrealircd.org...', ':001MZQ0RB', 'NICK', 'newnickname', '1754663712']
|
||||
>>> [':97KAAAAAC', 'NICK', 'testinspir', '1757360740']
|
||||
Args:
|
||||
irc_instance (Irc): The Irc instance
|
||||
srvmsg (list[str]): The Server MSG
|
||||
confmodel (ModConfModel): The Module Configuration
|
||||
"""
|
||||
uid = uplink.Loader.Utils.clean_uid(str(srvmsg[1]))
|
||||
p = uplink.Protocol
|
||||
u, new_nickname, timestamp = p.parse_nick(srvmsg)
|
||||
|
||||
if u is None:
|
||||
uplink.Logs.error(f"[USER OBJ ERROR {timestamp}] - {srvmsg}")
|
||||
return None
|
||||
|
||||
uid = u.uid
|
||||
confmodel = uplink.ModConfig
|
||||
|
||||
get_reputation = uplink.Reputation.get_Reputation(uid)
|
||||
get_reputation = uplink.Reputation.get_reputation(uid)
|
||||
jail_salon = uplink.Config.SALON_JAIL
|
||||
service_id = uplink.Config.SERVICE_ID
|
||||
|
||||
@@ -159,7 +171,7 @@ def handle_on_nick(uplink: 'Defender', srvmsg: list[str]):
|
||||
|
||||
# Update the new nickname
|
||||
oldnick = get_reputation.nickname
|
||||
newnickname = srvmsg[3]
|
||||
newnickname = new_nickname
|
||||
get_reputation.nickname = newnickname
|
||||
|
||||
# If ban in all channel is ON then unban old nickname an ban the new nickname
|
||||
@@ -170,20 +182,24 @@ def handle_on_nick(uplink: 'Defender', srvmsg: list[str]):
|
||||
p.send2socket(f":{service_id} MODE {chan.name} +b {newnickname}!*@*")
|
||||
|
||||
def handle_on_quit(uplink: 'Defender', srvmsg: list[str]):
|
||||
"""_summary_
|
||||
"""Handle on quit message
|
||||
>>> srvmsg = ['@unrealircd.org...', ':001MZQ0RB', 'QUIT', ':Quit:', 'quit message']
|
||||
Args:
|
||||
uplink (Irc): The Defender Module instance
|
||||
srvmsg (list[str]): The Server MSG
|
||||
"""
|
||||
p = uplink.Protocol
|
||||
userobj, reason = p.parse_quit(srvmsg)
|
||||
confmodel = uplink.ModConfig
|
||||
|
||||
if userobj is None:
|
||||
uplink.Logs.debug(f"This UID do not exist anymore: {srvmsg}")
|
||||
return None
|
||||
|
||||
ban_all_chan = uplink.Base.int_if_possible(confmodel.reputation_ban_all_chan)
|
||||
final_UID = uplink.Loader.Utils.clean_uid(str(srvmsg[1]))
|
||||
jail_salon = uplink.Config.SALON_JAIL
|
||||
service_id = uplink.Config.SERVICE_ID
|
||||
get_user_reputation = uplink.Reputation.get_Reputation(final_UID)
|
||||
get_user_reputation = uplink.Reputation.get_reputation(userobj.uid)
|
||||
|
||||
if get_user_reputation is not None:
|
||||
final_nickname = get_user_reputation.nickname
|
||||
@@ -192,7 +208,7 @@ def handle_on_quit(uplink: 'Defender', srvmsg: list[str]):
|
||||
p.send2socket(f":{service_id} MODE {chan.name} -b {final_nickname}!*@*")
|
||||
uplink.Logs.debug(f"Mode -b {final_nickname} on channel {chan.name}")
|
||||
|
||||
uplink.Reputation.delete(final_UID)
|
||||
uplink.Reputation.delete(userobj.uid)
|
||||
uplink.Logs.debug(f"Client {get_user_reputation.nickname} has been removed from Reputation local DB")
|
||||
|
||||
def handle_on_uid(uplink: 'Defender', srvmsg: list[str]):
|
||||
@@ -204,6 +220,7 @@ def handle_on_uid(uplink: 'Defender', srvmsg: list[str]):
|
||||
uplink (Defender): The Defender instance
|
||||
srvmsg (list[str]): The Server MSG
|
||||
"""
|
||||
_User = uplink.Protocol.parse_uid(srvmsg)
|
||||
gconfig = uplink.Config
|
||||
irc = uplink.Irc
|
||||
confmodel = uplink.ModConfig
|
||||
@@ -213,10 +230,8 @@ def handle_on_uid(uplink: 'Defender', srvmsg: list[str]):
|
||||
return None
|
||||
|
||||
# Get User information
|
||||
_User = irc.User.get_user(str(srvmsg[8]))
|
||||
|
||||
if _User is None:
|
||||
irc.Logs.warning(f'This UID: [{srvmsg[8]}] is not available please check why')
|
||||
irc.Logs.warning(f'Error when parsing UID', exc_info=True)
|
||||
return
|
||||
|
||||
# If user is not service or IrcOp then scan them
|
||||
@@ -249,7 +264,8 @@ def handle_on_uid(uplink: 'Defender', srvmsg: list[str]):
|
||||
####################
|
||||
# ACTION FUNCTIONS #
|
||||
####################
|
||||
|
||||
# [:<sid>] UID <uid> <ts> <nick> <real-host> <displayed-host> <real-user> <ip> <signon> <modes> [<mode-parameters>]+ :<real>
|
||||
# [:<sid>] UID nickname hopcount timestamp username hostname uid servicestamp umodes virthost cloakedhost ip :gecos
|
||||
def action_on_flood(uplink: 'Defender', srvmsg: list[str]):
|
||||
|
||||
confmodel = uplink.ModConfig
|
||||
@@ -318,7 +334,7 @@ def action_add_reputation_sanctions(uplink: 'Defender', jailed_uid: str ):
|
||||
p = uplink.Protocol
|
||||
confmodel = uplink.ModConfig
|
||||
|
||||
get_reputation = irc.Reputation.get_Reputation(jailed_uid)
|
||||
get_reputation = irc.Reputation.get_reputation(jailed_uid)
|
||||
|
||||
if get_reputation is None:
|
||||
irc.Logs.warning(f'UID {jailed_uid} has not been found')
|
||||
@@ -378,14 +394,11 @@ def action_apply_reputation_santions(uplink: 'Defender') -> None:
|
||||
color_red = gconfig.COLORS.red
|
||||
nogc = gconfig.COLORS.nogc
|
||||
salon_jail = gconfig.SALON_JAIL
|
||||
|
||||
if reputation_flag == 0:
|
||||
return None
|
||||
elif reputation_timer == 0:
|
||||
return None
|
||||
|
||||
uid_to_clean = []
|
||||
|
||||
if reputation_flag == 0 or reputation_timer == 0 or not irc.Reputation.UID_REPUTATION_DB:
|
||||
return None
|
||||
|
||||
for user in irc.Reputation.UID_REPUTATION_DB:
|
||||
if not user.isWebirc: # Si il ne vient pas de WebIRC
|
||||
if irc.User.get_user_uptime_in_minutes(user.uid) >= reputation_timer and int(user.score_connexion) <= int(reputation_seuil):
|
||||
@@ -404,7 +417,7 @@ def action_apply_reputation_santions(uplink: 'Defender') -> None:
|
||||
# Suppression des éléments dans {UID_DB} et {REPUTATION_DB}
|
||||
for chan in irc.Channel.UID_CHANNEL_DB:
|
||||
if chan.name != salon_jail and ban_all_chan == 1:
|
||||
get_user_reputation = irc.Reputation.get_Reputation(uid)
|
||||
get_user_reputation = irc.Reputation.get_reputation(uid)
|
||||
p.send2socket(f":{service_id} MODE {chan.name} -b {get_user_reputation.nickname}!*@*")
|
||||
|
||||
# Lorsqu'un utilisateur quitte, il doit être supprimé de {UID_DB}.
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
import logging
|
||||
import asyncio
|
||||
from unrealircd_rpc_py.objects.Definition import LiveRPCResult
|
||||
from core.classes.interfaces.imodule import IModule
|
||||
import mods.jsonrpc.utils as utils
|
||||
import mods.jsonrpc.threads as thds
|
||||
from time import sleep
|
||||
from types import SimpleNamespace
|
||||
from typing import TYPE_CHECKING
|
||||
from dataclasses import dataclass
|
||||
from unrealircd_rpc_py.Live import LiveWebsocket, LiveUnixSocket
|
||||
from unrealircd_rpc_py.Loader import Loader
|
||||
from unrealircd_rpc_py.ConnectionFactory import ConnectionFactory
|
||||
from unrealircd_rpc_py.LiveConnectionFactory import LiveConnectionFactory
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
|
||||
class Jsonrpc():
|
||||
class Jsonrpc(IModule):
|
||||
|
||||
@dataclass
|
||||
class ModConfModel:
|
||||
@@ -20,130 +15,15 @@ class Jsonrpc():
|
||||
"""
|
||||
jsonrpc: int = 0
|
||||
|
||||
def __init__(self, ircInstance: 'Irc') -> None:
|
||||
MOD_HEADER: dict[str, str] = {
|
||||
'name':'JsonRPC',
|
||||
'version':'1.0.0',
|
||||
'description':'Module using the unrealircd-rpc-py library',
|
||||
'author':'Defender Team',
|
||||
'core_version':'Defender-6'
|
||||
}
|
||||
|
||||
# Module name (Mandatory)
|
||||
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
|
||||
|
||||
# Add Irc Object to the module (Mandatory)
|
||||
self.Irc = ircInstance
|
||||
|
||||
# Add Protocol to the module (Mandatory)
|
||||
self.Protocol = ircInstance.Protocol
|
||||
|
||||
# Add Global Configuration to the module (Mandatory)
|
||||
self.Config = ircInstance.Config
|
||||
|
||||
# Add Base object to the module (Mandatory)
|
||||
self.Base = ircInstance.Base
|
||||
|
||||
# Add Main Utils (Mandatory)
|
||||
self.MainUtils = ircInstance.Utils
|
||||
|
||||
# Add logs object to the module (Mandatory)
|
||||
self.Logs = ircInstance.Loader.Logs
|
||||
|
||||
# Add User object to the module (Mandatory)
|
||||
self.User = ircInstance.User
|
||||
|
||||
# Add Channel object to the module (Mandatory)
|
||||
self.Channel = ircInstance.Channel
|
||||
|
||||
# Is RPC Active?
|
||||
self.is_streaming = False
|
||||
|
||||
# Module Utils
|
||||
self.Utils = utils
|
||||
|
||||
# Module threads
|
||||
self.Threads = thds
|
||||
|
||||
# Run Garbage collector.
|
||||
self.Base.create_timer(10, self.MainUtils.run_python_garbage_collector)
|
||||
|
||||
# Create module commands (Mandatory)
|
||||
self.Irc.build_command(1, self.module_name, 'jsonrpc', 'Activate the JSON RPC Live connection [ON|OFF]')
|
||||
self.Irc.build_command(1, self.module_name, 'jruser', 'Get Information about a user using JSON RPC')
|
||||
self.Irc.build_command(1, self.module_name, 'jrinstances', 'Get number of instances')
|
||||
|
||||
# Init the module
|
||||
self.__init_module()
|
||||
|
||||
# Log the module
|
||||
self.Logs.debug(f'Module {self.module_name} loaded ...')
|
||||
|
||||
def __init_module(self) -> None:
|
||||
|
||||
logging.getLogger('websockets').setLevel(logging.WARNING)
|
||||
logging.getLogger('unrealircd-rpc-py').setLevel(logging.CRITICAL)
|
||||
logging.getLogger('unrealircd-liverpc-py').setLevel(logging.CRITICAL)
|
||||
|
||||
# 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: LiveWebsocket = LiveWebsocket(
|
||||
url=self.Config.JSONRPC_URL,
|
||||
username=self.Config.JSONRPC_USER,
|
||||
password=self.Config.JSONRPC_PASSWORD,
|
||||
callback_object_instance=self,
|
||||
callback_method_or_function_name='callback_sent_to_irc'
|
||||
)
|
||||
|
||||
if self.UnrealIrcdRpcLive.get_error.code != 0:
|
||||
self.Logs.error(f"{self.UnrealIrcdRpcLive.get_error.message} ({self.UnrealIrcdRpcLive.get_error.code})")
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=self.Config.SERVICE_NICKNAME,
|
||||
msg=f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.UnrealIrcdRpcLive.get_error.message}",
|
||||
channel=self.Config.SERVICE_CHANLOG
|
||||
)
|
||||
raise Exception(f"[LIVE-JSONRPC ERROR] {self.UnrealIrcdRpcLive.get_error.message}")
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
if self.Rpc.get_error.code != 0:
|
||||
self.Logs.error(f"{self.Rpc.get_error.message} ({self.Rpc.get_error.code})")
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=self.Config.SERVICE_NICKNAME,
|
||||
msg=f"[{self.Config.COLORS.red}JSONRPC ERROR{self.Config.COLORS.nogc}] {self.Rpc.get_error.message}",
|
||||
channel=self.Config.SERVICE_CHANLOG
|
||||
)
|
||||
raise Exception(f"[JSONRPC ERROR] {self.Rpc.get_error.message}")
|
||||
|
||||
if self.ModConfig.jsonrpc == 1:
|
||||
self.Base.create_thread(func=self.Threads.thread_subscribe, func_args=(self, ), run_once=True)
|
||||
|
||||
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, response: SimpleNamespace) -> None:
|
||||
def callback_sent_to_irc(self, response: LiveRPCResult) -> None:
|
||||
|
||||
dnickname = self.Config.SERVICE_NICKNAME
|
||||
dchanlog = self.Config.SERVICE_CHANLOG
|
||||
@@ -152,22 +32,12 @@ class Jsonrpc():
|
||||
bold = self.Config.COLORS.bold
|
||||
red = self.Config.COLORS.red
|
||||
|
||||
if self.UnrealIrcdRpcLive.get_error.code != 0:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"[{bold}{red}JSONRPC ERROR{nogc}{bold}] {self.UnrealIrcdRpcLive.get_error.message}",
|
||||
channel=dchanlog)
|
||||
return None
|
||||
|
||||
if hasattr(response, 'error'):
|
||||
if response.error.code != 0:
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=self.Config.SERVICE_NICKNAME,
|
||||
msg=f"[{bold}{red}JSONRPC{nogc}{bold}] JSONRPC Event activated on {self.Config.JSONRPC_URL}",
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname,
|
||||
msg=f"[{bold}{red}JSONRPC ERROR{nogc}{bold}] {response.error.message} ({response.error.code})",
|
||||
channel=dchanlog)
|
||||
|
||||
return None
|
||||
|
||||
if hasattr(response, 'result'):
|
||||
if isinstance(response.result, bool):
|
||||
if response.result:
|
||||
self.Protocol.send_priv_msg(
|
||||
@@ -187,31 +57,74 @@ class Jsonrpc():
|
||||
|
||||
return None
|
||||
|
||||
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)
|
||||
|
||||
def create_tables(self) -> None:
|
||||
return None
|
||||
|
||||
except TypeError as te:
|
||||
self.Logs.critical(te)
|
||||
def load(self) -> None:
|
||||
|
||||
def update_configuration(self, param_key: str, param_value: str) -> None:
|
||||
"""Update the local and core configuration
|
||||
logging.getLogger('websockets').setLevel(logging.WARNING)
|
||||
logging.getLogger('unrealircd-rpc-py').setLevel(logging.CRITICAL)
|
||||
logging.getLogger('unrealircd-liverpc-py').setLevel(logging.CRITICAL)
|
||||
|
||||
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)
|
||||
self.ModConfig = self.ModConfModel(jsonrpc=0)
|
||||
|
||||
if self.Config.SERVEUR_PROTOCOL != 'unreal6':
|
||||
self.Loader.ModuleUtils.unload_one_module(self.Irc, self.module_name, False)
|
||||
return None
|
||||
|
||||
# Is RPC Active?
|
||||
self.is_streaming = False
|
||||
|
||||
# Module Utils
|
||||
self.Utils = utils
|
||||
|
||||
# Module threads
|
||||
self.Threads = thds
|
||||
|
||||
# Run Garbage collector.
|
||||
self.Base.create_timer(10, self.MainUtils.run_python_garbage_collector)
|
||||
|
||||
# Create module commands (Mandatory)
|
||||
self.Irc.build_command(1, self.module_name, 'jsonrpc', 'Activate the JSON RPC Live connection [ON|OFF]')
|
||||
self.Irc.build_command(1, self.module_name, 'jruser', 'Get Information about a user using JSON RPC')
|
||||
self.Irc.build_command(1, self.module_name, 'jrinstances', 'Get number of instances')
|
||||
|
||||
try:
|
||||
self.Rpc = ConnectionFactory(self.Config.DEBUG_LEVEL).get(self.Config.JSONRPC_METHOD)
|
||||
self.LiveRpc = LiveConnectionFactory(self.Config.DEBUG_LEVEL).get(self.Config.JSONRPC_METHOD)
|
||||
|
||||
sync_unixsocket = {'path_to_socket_file': self.Config.JSONRPC_PATH_TO_SOCKET_FILE}
|
||||
sync_http = {'url': self.Config.JSONRPC_URL, 'username': self.Config.JSONRPC_USER, 'password': self.Config.JSONRPC_PASSWORD}
|
||||
|
||||
live_unixsocket = {'path_to_socket_file': self.Config.JSONRPC_PATH_TO_SOCKET_FILE,
|
||||
'callback_object_instance' : self, 'callback_method_or_function_name': 'callback_sent_to_irc'}
|
||||
live_http = {'url': self.Config.JSONRPC_URL, 'username': self.Config.JSONRPC_USER, 'password': self.Config.JSONRPC_PASSWORD,
|
||||
'callback_object_instance' : self, 'callback_method_or_function_name': 'callback_sent_to_irc'}
|
||||
|
||||
sync_param = sync_unixsocket if self.Config.JSONRPC_METHOD == 'unixsocket' else sync_http
|
||||
live_param = live_unixsocket if self.Config.JSONRPC_METHOD == 'unixsocket' else live_http
|
||||
|
||||
self.Rpc.setup(sync_param)
|
||||
self.LiveRpc.setup(live_param)
|
||||
|
||||
if self.ModConfig.jsonrpc == 1:
|
||||
self.Base.create_thread(func=self.Threads.thread_subscribe, func_args=(self, ), run_once=True)
|
||||
|
||||
return None
|
||||
except Exception as err:
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=self.Config.SERVICE_NICKNAME,
|
||||
msg=f"[{self.Config.COLORS.red}JSONRPC ERROR{self.Config.COLORS.nogc}] {err.__str__()}",
|
||||
channel=self.Config.SERVICE_CHANLOG
|
||||
)
|
||||
self.Logs.error(f"JSONRPC ERROR: {err.__str__()}")
|
||||
|
||||
def unload(self) -> None:
|
||||
|
||||
if self.Config.SERVEUR_PROTOCOL != 'unreal6':
|
||||
self.Loader.ModuleUtils.unload_one_module(self.Irc, self.module_name, False)
|
||||
return None
|
||||
|
||||
if self.is_streaming:
|
||||
self.Protocol.send_priv_msg(
|
||||
nick_from=self.Config.SERVICE_NICKNAME,
|
||||
@@ -220,6 +133,7 @@ class Jsonrpc():
|
||||
)
|
||||
self.Base.create_thread(func=self.Threads.thread_unsubscribe, func_args=(self, ), run_once=True)
|
||||
self.update_configuration('jsonrpc', 0)
|
||||
self.Irc.Commands.drop_command_by_module(self.module_name)
|
||||
self.Logs.debug(f"Unloading {self.module_name}")
|
||||
return None
|
||||
|
||||
@@ -277,18 +191,13 @@ class Jsonrpc():
|
||||
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.get_error.code != 0:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'{rpc.get_error.message}')
|
||||
UserInfo = rpc.User.get(nickname)
|
||||
if UserInfo.error.code != 0:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'{UserInfo.error.message}')
|
||||
return None
|
||||
|
||||
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'UID : {UserInfo.id}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'NICKNAME : {UserInfo.name}')
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'USERNAME : {UserInfo.user.username}')
|
||||
@@ -320,9 +229,8 @@ class Jsonrpc():
|
||||
case 'jrinstances':
|
||||
try:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"GC Collect: {self.MainUtils.run_python_garbage_collector()}")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nombre d'instance LiveWebsock: {self.MainUtils.get_number_gc_objects(LiveWebsocket)}")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nombre d'instance LiveUnixSocket: {self.MainUtils.get_number_gc_objects(LiveUnixSocket)}")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nombre d'instance Loader: {self.MainUtils.get_number_gc_objects(Loader)}")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nombre d'instance LiveWebsock: {self.MainUtils.get_number_gc_objects(LiveConnectionFactory)}")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nombre d'instance ConnectionFactory: {self.MainUtils.get_number_gc_objects(ConnectionFactory)}")
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nombre de toute les instances: {self.MainUtils.get_number_gc_objects()}")
|
||||
except Exception as err:
|
||||
self.Logs.error(f"Unknown Error: {err}")
|
||||
@@ -5,24 +5,20 @@ if TYPE_CHECKING:
|
||||
from mods.jsonrpc.mod_jsonrpc import Jsonrpc
|
||||
|
||||
def thread_subscribe(uplink: 'Jsonrpc') -> None:
|
||||
response: dict[str, dict] = {}
|
||||
|
||||
snickname = uplink.Config.SERVICE_NICKNAME
|
||||
schannel = uplink.Config.SERVICE_CHANLOG
|
||||
|
||||
if uplink.UnrealIrcdRpcLive.get_error.code == 0:
|
||||
uplink.is_streaming = True
|
||||
response = asyncio.run(uplink.UnrealIrcdRpcLive.subscribe(["all"]))
|
||||
else:
|
||||
response = asyncio.run(uplink.LiveRpc.subscribe(["all"]))
|
||||
|
||||
if response.error.code != 0:
|
||||
uplink.Protocol.send_priv_msg(nick_from=snickname,
|
||||
msg=f"[{uplink.Config.COLORS.red}JSONRPC ERROR{uplink.Config.COLORS.nogc}] {uplink.UnrealIrcdRpcLive.get_error.message}",
|
||||
msg=f"[{uplink.Config.COLORS.red}JSONRPC ERROR{uplink.Config.COLORS.nogc}] {response.error.message}",
|
||||
channel=schannel
|
||||
)
|
||||
|
||||
if response is None:
|
||||
return
|
||||
|
||||
code = response.get('error', {}).get('code', 0)
|
||||
message = response.get('error', {}).get('message', None)
|
||||
code = response.error.code
|
||||
message = response.error.message
|
||||
|
||||
if code == 0:
|
||||
uplink.Protocol.send_priv_msg(
|
||||
@@ -39,18 +35,15 @@ def thread_subscribe(uplink: 'Jsonrpc') -> None:
|
||||
|
||||
def thread_unsubscribe(uplink: 'Jsonrpc') -> None:
|
||||
|
||||
response: dict[str, dict] = asyncio.run(uplink.UnrealIrcdRpcLive.unsubscribe())
|
||||
response = asyncio.run(uplink.LiveRpc.unsubscribe())
|
||||
uplink.Logs.debug("[JSONRPC UNLOAD] Unsubscribe from the stream!")
|
||||
uplink.is_streaming = False
|
||||
uplink.update_configuration('jsonrpc', 0)
|
||||
snickname = uplink.Config.SERVICE_NICKNAME
|
||||
schannel = uplink.Config.SERVICE_CHANLOG
|
||||
|
||||
if response is None:
|
||||
return None
|
||||
|
||||
code = response.get('error', {}).get('code', 0)
|
||||
message = response.get('error', {}).get('message', None)
|
||||
code = response.error.code
|
||||
message = response.error.message
|
||||
|
||||
if code != 0:
|
||||
uplink.Protocol.send_priv_msg(
|
||||
|
||||
@@ -1,75 +1,28 @@
|
||||
from typing import TYPE_CHECKING
|
||||
from dataclasses import dataclass, fields
|
||||
from typing import Any
|
||||
from core.classes.interfaces.imodule import IModule
|
||||
from dataclasses import dataclass
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
|
||||
class Test():
|
||||
class Test(IModule):
|
||||
|
||||
@dataclass
|
||||
class ModConfModel:
|
||||
"""The Model containing the module parameters
|
||||
"""The Model containing the module parameters (Mandatory)
|
||||
you can leave it without params.
|
||||
just use pass | if you leave it empty, in the load() method just init empty object ==> self.ModConfig = ModConfModel()
|
||||
"""
|
||||
param_exemple1: str
|
||||
param_exemple2: int
|
||||
|
||||
def __init__(self, ircInstance: 'Irc') -> None:
|
||||
MOD_HEADER: dict[str, str] = {
|
||||
'name':'Test',
|
||||
'version':'1.0.0',
|
||||
'description':'The test module',
|
||||
'author':'Defender Team',
|
||||
'core_version':'Defender-6'
|
||||
}
|
||||
"""Module Header (Mandatory)"""
|
||||
|
||||
# Module name (Mandatory)
|
||||
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
|
||||
|
||||
# Add Irc Object to the module (Mandatory)
|
||||
self.Irc = ircInstance
|
||||
|
||||
# Add Loader Object to the module (Mandatory)
|
||||
self.Loader = ircInstance.Loader
|
||||
|
||||
# Add server protocol Object to the module (Mandatory)
|
||||
self.Protocol = ircInstance.Protocol
|
||||
|
||||
# Add Global Configuration to the module (Mandatory)
|
||||
self.Config = ircInstance.Config
|
||||
|
||||
# Add Base object to the module (Mandatory)
|
||||
self.Base = ircInstance.Base
|
||||
|
||||
# Add logs object to the module (Mandatory)
|
||||
self.Logs = ircInstance.Loader.Logs
|
||||
|
||||
# Add User object to the module (Mandatory)
|
||||
self.User = ircInstance.User
|
||||
|
||||
# Add Channel object to the module (Mandatory)
|
||||
self.Channel = ircInstance.Channel
|
||||
|
||||
# Add Reputation object to the module (Optional)
|
||||
self.Reputation = ircInstance.Reputation
|
||||
|
||||
# Create module commands (Mandatory)
|
||||
self.Irc.build_command(0, self.module_name, 'test-command', 'Execute a test command')
|
||||
self.Irc.build_command(1, self.module_name, 'test_level_1', 'Execute a level 1 test command')
|
||||
self.Irc.build_command(2, self.module_name, 'test_level_2', 'Execute a level 2 test command')
|
||||
self.Irc.build_command(3, self.module_name, 'test_level_3', 'Execute a level 3 test command')
|
||||
|
||||
|
||||
# Init the module
|
||||
self.__init_module()
|
||||
|
||||
# Log the module
|
||||
self.Logs.debug(f'Module {self.module_name} loaded ...')
|
||||
|
||||
def __init_module(self) -> None:
|
||||
|
||||
# Create you own tables (Mandatory)
|
||||
self.__create_tables()
|
||||
|
||||
# Load module configuration and sync with core one (Mandatory)
|
||||
self.__load_module_configuration()
|
||||
# End of mandatory methods you can start your customization #
|
||||
|
||||
return None
|
||||
|
||||
def __create_tables(self) -> 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:
|
||||
@@ -86,69 +39,69 @@ class Test():
|
||||
)
|
||||
'''
|
||||
|
||||
self.Base.db_execute_query(table_logs)
|
||||
# self.Base.db_execute_query(table_logs)
|
||||
return None
|
||||
|
||||
def __load_module_configuration(self) -> None:
|
||||
"""### Load Module Configuration
|
||||
def load(self) -> None:
|
||||
"""### Load Module Configuration (Mandatory)
|
||||
"""
|
||||
try:
|
||||
|
||||
# Create module commands (Mandatory)
|
||||
self.Irc.build_command(0, self.module_name, 'test-command', 'Execute a test command')
|
||||
self.Irc.build_command(1, self.module_name, 'test_level_1', 'Execute a level 1 test command')
|
||||
self.Irc.build_command(2, self.module_name, 'test_level_2', 'Execute a level 2 test command')
|
||||
self.Irc.build_command(3, self.module_name, 'test_level_3', 'Execute a level 3 test command')
|
||||
|
||||
# Build the default configuration model (Mandatory)
|
||||
self.ModConfig = self.ModConfModel(param_exemple1='param value 1', param_exemple2=1)
|
||||
|
||||
# Sync the configuration with core configuration (Mandatory)
|
||||
self.Base.db_sync_core_config(self.module_name, self.ModConfig)
|
||||
|
||||
return None
|
||||
|
||||
except TypeError as te:
|
||||
self.Logs.critical(te)
|
||||
|
||||
def __update_configuration(self, param_key: str, param_value: str):
|
||||
"""Update the local and core configuration
|
||||
|
||||
Args:
|
||||
param_key (str): The parameter key
|
||||
param_value (str): The parameter value
|
||||
"""
|
||||
self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
|
||||
self.ModConfig = self.ModConfModel(param_exemple1='str', param_exemple2=1)
|
||||
|
||||
def unload(self) -> None:
|
||||
|
||||
"""### This method is called when you unload or you reload the module (Mandatory)"""
|
||||
self.Irc.Commands.drop_command_by_module(self.module_name)
|
||||
return None
|
||||
|
||||
def cmd(self, data:list) -> None:
|
||||
try:
|
||||
def cmd(self, data: list[str]) -> None:
|
||||
"""All messages coming from the IRCD server will be handled using this method (Mandatory)
|
||||
|
||||
Args:
|
||||
data (list): Messages coming from the IRCD server.
|
||||
"""
|
||||
cmd = list(data).copy()
|
||||
|
||||
try:
|
||||
return None
|
||||
except KeyError as ke:
|
||||
self.Logs.error(f"Key Error: {ke}")
|
||||
except IndexError as ie:
|
||||
self.Logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
|
||||
except Exception as err:
|
||||
self.Logs.error(f"General Error: {err}")
|
||||
|
||||
def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
|
||||
def hcmds(self, user: str, channel: Any, cmd: list, fullcmd: list = []) -> None:
|
||||
"""All messages coming from the user commands (Mandatory)
|
||||
|
||||
Args:
|
||||
user (str): The user who send the request.
|
||||
channel (Any): The channel from where is coming the message (could be None).
|
||||
cmd (list): The messages coming from the IRCD server.
|
||||
fullcmd (list, optional): The full messages coming from the IRCD server. Defaults to [].
|
||||
"""
|
||||
u = self.User.get_user(user)
|
||||
c = self.Channel.get_channel(channel) if self.Channel.is_valid_channel(channel) else None
|
||||
if u is None:
|
||||
return None
|
||||
|
||||
command = str(cmd[0]).lower()
|
||||
dnickname = self.Config.SERVICE_NICKNAME
|
||||
fromuser = user
|
||||
fromchannel = str(channel) if not channel is None else None
|
||||
|
||||
match command:
|
||||
|
||||
case 'test-command':
|
||||
try:
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=u.nickname, msg="This is a notice to the sender ...")
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"This is private message to the sender ...", nick_to=u.nickname)
|
||||
|
||||
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="This is a notice to the sender ...")
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"This is private message to the sender ...", nick_to=fromuser)
|
||||
|
||||
if not fromchannel is None:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"This is private message to the sender ...", channel=fromchannel)
|
||||
if c is not None:
|
||||
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"This is private message to the sender ...", channel=c.name)
|
||||
|
||||
# How to update your module configuration
|
||||
self.__update_configuration('param_exemple2', 7)
|
||||
self.update_configuration('param_exemple2', 7)
|
||||
self.update_configuration('param_exemple1', 'my_value')
|
||||
|
||||
# Log if you want the result
|
||||
self.Logs.debug(f"Test logs ready")
|
||||
|
||||
@@ -1,95 +1,37 @@
|
||||
"""
|
||||
File : mod_votekick.py
|
||||
Version : 1.0.0
|
||||
Version : 1.0.2
|
||||
Description : Manages votekick sessions for multiple channels.
|
||||
Handles activation, ongoing vote checks, and cleanup.
|
||||
Author : adator
|
||||
Created : 2025-08-16
|
||||
Last Updated: 2025-08-16
|
||||
Last Updated: 2025-11-01
|
||||
-----------------------------------------
|
||||
"""
|
||||
from dataclasses import dataclass
|
||||
import re
|
||||
from core.classes.interfaces.imodule import IModule
|
||||
import mods.votekick.schemas as schemas
|
||||
import mods.votekick.utils as utils
|
||||
from mods.votekick.votekick_manager import VotekickManager
|
||||
import mods.votekick.threads as thds
|
||||
from typing import TYPE_CHECKING, Any, Optional
|
||||
from typing import Any, Optional
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.irc import Irc
|
||||
class Votekick(IModule):
|
||||
|
||||
@dataclass
|
||||
class ModConfModel(schemas.VoteChannelModel):
|
||||
...
|
||||
|
||||
class Votekick:
|
||||
MOD_HEADER: dict[str, str] = {
|
||||
'name':'votekick',
|
||||
'version':'1.0.2',
|
||||
'description':'Channel Democraty',
|
||||
'author':'Defender Team',
|
||||
'core_version':'Defender-6'
|
||||
}
|
||||
|
||||
def __init__(self, uplink: 'Irc') -> None:
|
||||
|
||||
# Module name (Mandatory)
|
||||
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
|
||||
|
||||
# Add Irc Object to the module
|
||||
self.Irc = uplink
|
||||
|
||||
# Add Loader Object to the module (Mandatory)
|
||||
self.Loader = uplink.Loader
|
||||
|
||||
# Add server protocol Object to the module (Mandatory)
|
||||
self.Protocol = uplink.Protocol
|
||||
|
||||
# Add Global Configuration to the module
|
||||
self.Config = uplink.Config
|
||||
|
||||
# Add Base object to the module
|
||||
self.Base = uplink.Base
|
||||
|
||||
# Add logs object to the module
|
||||
self.Logs = uplink.Logs
|
||||
|
||||
# Add User object to the module
|
||||
self.User = uplink.User
|
||||
|
||||
# Add Channel object to the module
|
||||
self.Channel = uplink.Channel
|
||||
|
||||
# Add Utils.
|
||||
self.Utils = uplink.Utils
|
||||
|
||||
# Add Utils module
|
||||
self.ModUtils = utils
|
||||
|
||||
# Add Schemas module
|
||||
self.Schemas = schemas
|
||||
|
||||
# Add Threads module
|
||||
self.Threads = thds
|
||||
|
||||
# Add VoteKick Manager
|
||||
self.VoteKickManager = VotekickManager(self)
|
||||
|
||||
metadata = uplink.Loader.Settings.get_cache('VOTEKICK')
|
||||
|
||||
if metadata is not None:
|
||||
self.VoteKickManager.VOTE_CHANNEL_DB = metadata
|
||||
# self.VOTE_CHANNEL_DB = metadata
|
||||
|
||||
# Créer les nouvelles commandes du module
|
||||
self.Irc.build_command(1, self.module_name, 'vote', 'The kick vote module')
|
||||
|
||||
# Init the module
|
||||
self.__init_module()
|
||||
|
||||
# Log the module
|
||||
self.Logs.debug(f'-- Module {self.module_name} loaded ...')
|
||||
|
||||
def __init_module(self) -> None:
|
||||
|
||||
# Add admin object to retrieve admin users
|
||||
self.Admin = self.Irc.Admin
|
||||
self.__create_tables()
|
||||
self.ModUtils.join_saved_channels(self)
|
||||
|
||||
return None
|
||||
|
||||
def __create_tables(self) -> None:
|
||||
def create_tables(self) -> None:
|
||||
"""Methode qui va créer la base de donnée si elle n'existe pas.
|
||||
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
|
||||
|
||||
@@ -115,10 +57,37 @@ class Votekick:
|
||||
self.Base.db_execute_query(table_vote)
|
||||
return None
|
||||
|
||||
def load(self) -> None:
|
||||
|
||||
self.ModConfig = self.ModConfModel()
|
||||
|
||||
# Add VoteKick Manager
|
||||
self.VoteKickManager = VotekickManager(self)
|
||||
|
||||
# Add Utils module
|
||||
self.ModUtils = utils
|
||||
|
||||
# Add Schemas module
|
||||
self.Schemas = schemas
|
||||
|
||||
# Add Threads module
|
||||
self.Threads = thds
|
||||
|
||||
self.ModUtils.join_saved_channels(self)
|
||||
|
||||
metadata = self.Settings.get_cache('VOTEKICK')
|
||||
|
||||
if metadata is not None:
|
||||
self.VoteKickManager.VOTE_CHANNEL_DB = metadata
|
||||
|
||||
# Créer les nouvelles commandes du module
|
||||
self.Irc.build_command(1, self.module_name, 'vote', 'The kick vote module')
|
||||
|
||||
def unload(self) -> None:
|
||||
try:
|
||||
# Cache the local DB with current votes.
|
||||
self.Loader.Settings.set_cache('VOTEKICK', self.VoteKickManager.VOTE_CHANNEL_DB)
|
||||
if self.VoteKickManager.VOTE_CHANNEL_DB:
|
||||
self.Settings.set_cache('VOTEKICK', self.VoteKickManager.VOTE_CHANNEL_DB)
|
||||
|
||||
for chan in self.VoteKickManager.VOTE_CHANNEL_DB:
|
||||
self.Protocol.send_part_chan(uidornickname=self.Config.SERVICE_ID, channel=chan.channel_name)
|
||||
@@ -126,6 +95,8 @@ class Votekick:
|
||||
self.VoteKickManager.VOTE_CHANNEL_DB = []
|
||||
self.Logs.debug(f'Delete memory DB VOTE_CHANNEL_DB: {self.VoteKickManager.VOTE_CHANNEL_DB}')
|
||||
|
||||
self.Irc.Commands.drop_command_by_module(self.module_name)
|
||||
|
||||
return None
|
||||
except UnboundLocalError as ne:
|
||||
self.Logs.error(f'{ne}')
|
||||
|
||||
@@ -11,7 +11,7 @@ class VotekickManager:
|
||||
def __init__(self, uplink: 'Votekick'):
|
||||
self.uplink = uplink
|
||||
self.Logs = uplink.Logs
|
||||
self.Utils = uplink.Utils
|
||||
self.Utils = uplink.MainUtils
|
||||
|
||||
def activate_new_channel(self, channel_name: str) -> bool:
|
||||
"""Activate a new channel in the votekick systeme
|
||||
|
||||
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
Faker==37.12.0
|
||||
psutil==7.1.2
|
||||
PyYAML==6.0.3
|
||||
requests==2.32.5
|
||||
SQLAlchemy==2.0.44
|
||||
unrealircd_rpc_py==3.0.2
|
||||
13
version.json
13
version.json
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"version": "6.2.2",
|
||||
"version": "6.3.3",
|
||||
|
||||
"requests": "2.32.3",
|
||||
"psutil": "6.0.0",
|
||||
"unrealircd_rpc_py": "2.0.5",
|
||||
"sqlalchemy": "2.0.35",
|
||||
"faker": "30.1.0"
|
||||
"requests": "2.32.5",
|
||||
"psutil": "7.1.2",
|
||||
"unrealircd_rpc_py": "3.0.1",
|
||||
"sqlalchemy": "2.0.44",
|
||||
"faker": "37.12.0",
|
||||
"pyyaml": "6.0.3"
|
||||
}
|
||||
Reference in New Issue
Block a user