32 Commits

Author SHA1 Message Date
adator
743069f8e0 convert version string to int 2024-08-03 20:24:58 +02:00
adator
c8c5f782d7 Move checkversion command to admin commands 2024-08-03 20:12:19 +02:00
adator
ff0f880fcd V4.0.0 2024-08-03 19:21:30 +02:00
adator
41b9582e43 Merge pull request #8 from adator85/main
align main to dev
2024-08-03 17:04:26 +02:00
adator
dba0301190 Delete version.json 2024-08-03 17:03:42 +02:00
adator
6e6d001605 New Version 2024-08-03 17:01:19 +02:00
adator
e27a027ae9 Create version.json 2024-08-03 15:29:19 +02:00
adator85
19b7f85ec7 . 2024-03-24 01:01:18 +01:00
adator85
7068d88168 . 2024-03-23 03:40:50 +01:00
adator85
884c5bf0cd . 2024-03-23 02:43:24 +01:00
adator85
7e24d7cf4c . 2024-03-23 01:50:55 +01:00
adator85
d16b73656f . 2024-03-21 21:31:24 +01:00
adator85
8acbb7187c . 2024-03-20 23:57:23 +01:00
adator85
51bea90e6f . 2024-03-20 23:53:46 +01:00
adator85
4cb54b5b2e . 2024-03-20 23:49:14 +01:00
adator85
310f732a5b . 2024-03-20 23:46:23 +01:00
adator85
018fd8d959 . 2024-03-20 23:45:44 +01:00
adator85
07fa518fcc . 2024-03-20 23:19:28 +01:00
adator85
37dcd23353 . 2024-03-20 21:39:42 +01:00
adator85
748e7bffc9 Correction du status 2024-03-20 21:16:10 +01:00
adator85
681e0da041 Mise en forme du code 2024-03-20 20:33:50 +01:00
adator85
667281ffb4 Ajout de quelques param de config 2024-03-20 20:12:51 +01:00
adator85
4b18675e8b . 2024-03-20 01:26:30 +01:00
adator85
f63aabfb7a . 2024-03-20 00:54:37 +01:00
adator85
b4c1df7f4a . 2024-03-19 23:37:58 +01:00
adator85
4c1a929867 Adding freeipapi scan 2024-03-19 23:34:21 +01:00
adator
0f1aa6f946 Merge pull request #7 from adator85/dev
Fix KeyError when a user change nickname
2024-02-20 01:12:32 +01:00
adator85
20684339d3 Fix KeyError when a user change nickname 2024-02-20 01:10:54 +01:00
adator
d53a3c58c9 Merge pull request #6 from adator85/dev
Corriger les commandes qui ce dupliquent lors du rechargement du mod
2024-02-02 23:24:13 +01:00
adator85
5c7f0e3ad0 Corriger les commandes qui ce dupliquent lors du rechargement du mod 2024-02-02 23:22:49 +01:00
adator
168f8db5ab Merge pull request #5 from adator85/dev
Bug - Correction de la commande .code
2024-02-02 22:54:28 +01:00
adator85
91a6218692 Bug - Correction de la commande .code 2024-02-02 22:52:06 +01:00
7 changed files with 966 additions and 356 deletions

View File

@@ -1,4 +1,4 @@
import time, threading, os, random, socket, hashlib
import time, threading, os, random, socket, hashlib, ipaddress, logging, requests, json, sys
from datetime import datetime
from sqlalchemy import create_engine, Engine, Connection, CursorResult
from sqlalchemy.sql import text
@@ -16,9 +16,16 @@ class Base:
'modules': 'sys_modules'
}
DEFENDER_VERSION = '' # MAJOR.MINOR.BATCH
LATEST_DEFENDER_VERSION = '' # Latest Version of Defender in git
DEFENDER_DB_PATH = 'db' + os.sep # Séparateur en fonction de l'OS
DEFENDER_DB_NAME = 'defender' # Le nom de la base de données principale
def __init__(self, Config: Config) -> None:
self.Config = Config # Assigner l'objet de configuration
self.init_log_system() # Demarrer le systeme de log
self.check_for_new_version() # Verifier si une nouvelle version est disponible
self.running_timers:list[threading.Timer] = [] # Liste des timers en cours
self.running_threads:list[threading.Thread] = [] # Liste des threads en cours
@@ -32,7 +39,74 @@ class Base:
self.db_create_first_admin() # Créer un nouvel admin si la base de données est vide
def get_unixtime(self)->int:
def __set_current_defender_version(self) -> None:
"""This will put the current version of Defender
located in version.json
"""
version_filename = f'.{os.sep}version.json'
with open(version_filename, 'r') as version_data:
current_version:dict[str, str] = json.load(version_data)
self.DEFENDER_VERSION = current_version["version"]
return None
def __get_latest_defender_version(self) -> None:
try:
token = ''
json_url = f'https://raw.githubusercontent.com/adator85/IRC_DEFENDER_MODULES/main/version.json'
headers = {
'Authorization': f'token {token}',
'Accept': 'application/vnd.github.v3.raw' # Indique à GitHub que nous voulons le contenu brut du fichier
}
if token == '':
response = requests.get(json_url)
else:
response = requests.get(json_url, headers=headers)
response.raise_for_status() # Vérifie si la requête a réussi
json_response:dict = response.json()
self.LATEST_DEFENDER_VERSION = json_response["version"]
return None
except requests.HTTPError as err:
self.logs.error(f'Github not available to fetch latest version: {err}')
except:
self.logs.warning(f'Github not available to fetch latest version')
def check_for_new_version(self) -> bool:
try:
# Assigner la version actuelle de Defender
self.__set_current_defender_version()
# Récuperer la dernier version disponible dans github
self.__get_latest_defender_version()
isNewVersion = False
latest_version = self.LATEST_DEFENDER_VERSION
current_version = self.DEFENDER_VERSION
curr_major , curr_minor, curr_patch = current_version.split('.')
last_major, last_minor, last_patch = latest_version.split('.')
if int(last_major) > int(curr_major):
self.logs.info(f'New version available: {current_version} >>> {latest_version}')
isNewVersion = True
elif int(last_major) == int(curr_major) and int(last_minor) > int(curr_minor):
self.logs.info(f'New version available: {current_version} >>> {latest_version}')
isNewVersion = True
elif int(last_major) == int(curr_major) and int(last_minor) == int(curr_minor) and int(last_patch) > int(curr_patch):
self.logs.info(f'New version available: {current_version} >>> {latest_version}')
isNewVersion = True
else:
isNewVersion = False
return isNewVersion
except ValueError as ve:
self.logs.error(f'Impossible to convert in version number : {ve}')
def get_unixtime(self) -> int:
"""
Cette fonction retourne un UNIXTIME de type 12365456
Return: Current time in seconds since the Epoch (int)
@@ -40,7 +114,7 @@ class Base:
unixtime = int( time.time() )
return unixtime
def get_datetime(self)->str:
def get_datetime(self) -> str:
"""
Retourne une date au format string (24-12-2023 20:50:59)
"""
@@ -62,12 +136,36 @@ class Base:
return None
def init_log_system(self) -> None:
# Create folder if not available
logs_directory = f'logs{os.sep}'
if not os.path.exists(f'{logs_directory}'):
os.makedirs(logs_directory)
# Init logs object
self.logs = logging
self.logs.basicConfig(level=self.Config.DEBUG_LEVEL,
filename='logs/defender.log',
encoding='UTF-8',
format='%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s')
self.logs.info('#################### STARTING INTERCEPTOR HQ ####################')
return None
def log_cmd(self, user_cmd:str, cmd:str) -> None:
"""Enregistre les commandes envoyées par les utilisateurs
Args:
cmd (str): la commande a enregistrer
"""
cmd_list = cmd.split()
if len(cmd_list) == 3:
if cmd_list[0].replace('.', '') == 'auth':
cmd_list[1] = '*******'
cmd_list[2] = '*******'
cmd = ' '.join(cmd_list)
insert_cmd_query = f"INSERT INTO {self.DB_SCHEMA['commandes']} (datetime, user, commande) VALUES (:datetime, :user, :commande)"
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'commande': cmd}
self.db_execute_query(insert_cmd_query, mes_donnees)
@@ -100,13 +198,13 @@ class Base:
"""
if not self.db_isModuleExist(module_name):
self.__debug(f"Le module {module_name} n'existe pas alors ont le créer")
self.logs.debug(f"Le module {module_name} n'existe pas alors ont le créer")
insert_cmd_query = f"INSERT INTO {self.DB_SCHEMA['modules']} (datetime, user, module) VALUES (:datetime, :user, :module)"
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'module': module_name}
self.db_execute_query(insert_cmd_query, mes_donnees)
# self.db_close_session(self.session)
else:
self.__debug(f"Le module {module_name} existe déja dans la base de données")
self.logs.debug(f"Le module {module_name} existe déja dans la base de données")
return False
@@ -137,7 +235,7 @@ class Base:
(:createdOn, :user, :password, :hostname, :vhost, :level)"""
, mes_donnees)
pass
return None
def create_timer(self, time_to_wait: float, func: object, func_args: tuple = ()) -> None:
@@ -148,14 +246,20 @@ class Base:
self.running_timers.append(t)
self.__debug(f"Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}")
self.logs.debug(f"Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}")
except AssertionError as ae:
self.__debug(f'Assertion Error -> {ae}')
self.logs.error(f'Assertion Error -> {ae}')
def create_thread(self, func:object, func_args: tuple = ()) -> None:
def create_thread(self, func:object, func_args: tuple = (), run_once:bool = False) -> None:
try:
func_name = func.__name__
if run_once:
for thread in self.running_threads:
if thread.getName() == func_name:
return None
# if func_name in self.running_threads:
# print(f"HeartBeat is running")
# return None
@@ -164,98 +268,95 @@ class Base:
th.start()
self.running_threads.append(th)
self.__debug(f"Thread ID : {str(th.ident)} | Thread name : {th.getName()} | Running Threads : {len(threading.enumerate())}")
self.logs.debug(f"Thread ID : {str(th.ident)} | Thread name : {th.getName()} | Running Threads : {len(threading.enumerate())}")
except AssertionError as ae:
self.__debug(f'Assertion Error -> {ae}')
self.logs.error(f'{ae}')
def garbage_collector_timer(self) -> None:
"""Methode qui supprime les timers qui ont finis leurs job
"""
try:
self.__debug(f"=======> Checking for Timers to stop")
# print(f"{self.running_timers}")
for timer in self.running_timers:
if not timer.is_alive():
timer.cancel()
self.running_timers.remove(timer)
self.__debug(f"Timer {str(timer)} removed")
self.logs.info(f"Timer {str(timer)} removed")
else:
self.__debug(f"===> Timer {str(timer)} Still running ...")
self.logs.debug(f"===> Timer {str(timer)} Still running ...")
except AssertionError as ae:
print(f'Assertion Error -> {ae}')
self.logs.error(f'Assertion Error -> {ae}')
def garbage_collector_thread(self) -> None:
"""Methode qui supprime les threads qui ont finis leurs job
"""
try:
self.__debug(f"=======> Checking for Threads to stop")
for thread in self.running_threads:
if thread.getName() != 'heartbeat':
if not thread.is_alive():
self.running_threads.remove(thread)
self.__debug(f"Thread {str(thread.getName())} {str(thread.native_id)} removed")
self.logs.debug(f"Thread {str(thread.getName())} {str(thread.native_id)} removed")
# print(threading.enumerate())
except AssertionError as ae:
self.__debug(f'Assertion Error -> {ae}')
self.logs.error(f'Assertion Error -> {ae}')
def garbage_collector_sockets(self) -> None:
self.__debug(f"=======> Checking for Sockets to stop")
for soc in self.running_sockets:
while soc.fileno() != -1:
self.__debug(soc.fileno())
self.logs.debug(soc.fileno())
soc.close()
soc.close()
self.running_sockets.remove(soc)
self.__debug(f"Socket ==> closed {str(soc.fileno())}")
self.logs.debug(f"Socket ==> closed {str(soc.fileno())}")
def shutdown(self) -> None:
"""Methode qui va préparer l'arrêt complêt du service
"""
# Nettoyage des timers
print(f"=======> Checking for Timers to stop")
self.logs.debug(f"=======> Checking for Timers to stop")
for timer in self.running_timers:
while timer.is_alive():
print(f"> waiting for {timer.getName()} to close")
self.logs.debug(f"> waiting for {timer.getName()} to close")
timer.cancel()
time.sleep(0.2)
self.running_timers.remove(timer)
print(f"> Cancelling {timer.getName()} {timer.native_id}")
self.logs.debug(f"> Cancelling {timer.getName()} {timer.native_id}")
print(f"=======> Checking for Threads to stop")
self.logs.debug(f"=======> Checking for Threads to stop")
for thread in self.running_threads:
if thread.getName() == 'heartbeat' and thread.is_alive():
self.execute_periodic_action()
print(f"> Running the last periodic action")
self.logs.debug(f"> Running the last periodic action")
self.running_threads.remove(thread)
print(f"> Cancelling {thread.getName()} {thread.native_id}")
self.logs.debug(f"> Cancelling {thread.getName()} {thread.native_id}")
print(f"=======> Checking for Sockets to stop")
self.logs.debug(f"=======> Checking for Sockets to stop")
for soc in self.running_sockets:
soc.close()
while soc.fileno() != -1:
soc.close()
self.running_sockets.remove(soc)
print(f"> Socket ==> closed {str(soc.fileno())}")
self.logs.debug(f"> Socket ==> closed {str(soc.fileno())}")
pass
return None
def db_init(self) -> tuple[Engine, Connection]:
db_directory = self.Config.DEFENDER_DB_PATH
full_path_db = self.Config.DEFENDER_DB_PATH + self.Config.DEFENDER_DB_NAME
db_directory = self.DEFENDER_DB_PATH
full_path_db = self.DEFENDER_DB_PATH + self.DEFENDER_DB_NAME
if not os.path.exists(db_directory):
os.makedirs(db_directory)
engine = create_engine(f'sqlite:///{full_path_db}.db', echo=False)
cursor = engine.connect()
self.logs.info("database connexion has been initiated")
return engine, cursor
def __create_db(self) -> None:
@@ -319,7 +420,7 @@ class Base:
try:
self.cursor.close()
except AttributeError as ae:
self.__debug(f"Attribute Error : {ae}")
self.logs.error(f"Attribute Error : {ae}")
def crypt_password(self, password:str) -> str:
"""Retourne un mot de passe chiffré en MD5
@@ -352,6 +453,17 @@ class Base:
except TypeError:
return value
def is_valid_ip(self, ip_to_control:str) -> bool:
try:
if ip_to_control in self.Config.WHITELISTED_IP:
return False
ipaddress.ip_address(ip_to_control)
return True
except ValueError:
return False
def get_random(self, lenght:int) -> str:
"""
Retourn une chaîne aléatoire en fonction de la longueur spécifiée.
@@ -367,7 +479,7 @@ class Base:
# Run Garbage Collector Timer
self.garbage_collector_timer()
self.garbage_collector_thread()
self.garbage_collector_sockets()
# self.garbage_collector_sockets()
return None
for key, value in self.periodic_func.items():
@@ -379,10 +491,3 @@ class Base:
# Vider le dictionnaire de fonction
self.periodic_func.clear()
def __debug(self, debug_msg:str) -> None:
if self.Config.DEBUG == 1:
print(f"[{self.get_datetime()}] - {debug_msg}")
return None

View File

@@ -1,4 +1,3 @@
import os
##########################################
# CONFIGURATION FILE : #
# Rename file to : configuration.py #
@@ -6,18 +5,15 @@ import os
class Config:
DEFENDER_VERSION = '1.1.0' # MAJOR.MINOR.BATCH
DEFENDER_DB_PATH = 'db' + os.sep # Séparateur en fonction de l'OS
DEFENDER_DB_NAME = 'defender' # Le nom de la base de données principale
SERVICE_NAME = 'defender' # Le nom du service
SERVEUR_IP = '8.8.8.8' # IP ou host du serveur à rejoindre
SERVEUR_IP = '0.0.0.0' # IP ou host du serveur à rejoindre
SERVEUR_HOSTNAME = 'your hostname' # Le hostname du serveur IRC
SERVEUR_LINK = 'your link' # Host attendu par votre IRCd (ex. dans votre link block pour Unrealircd)
SERVEUR_PORT = 6666 # Port du link
SERVEUR_PASSWORD = 'your link password' # Mot de passe du link (Privilégiez argon2 sur Unrealircd)
SERVEUR_ID = '002' # SID (identification) du bot en tant que Services
SERVEUR_SSL = True # Activer / Desactiver la connexion SSL
SERVICE_NAME = 'defender' # Le nom du service
SERVICE_NICKNAME = 'BotName' # Nick du bot sur IRC
SERVICE_REALNAME = 'BotRealname' # Realname du bot
SERVICE_USERNAME = 'BotIdent' # Ident du bot
@@ -37,9 +33,13 @@ class Config:
SALON_JAIL_MODES = 'sS' # Mode du salon pot de miel
SALON_LIBERER = '#welcome' # Le salon ou sera envoyé l'utilisateur clean
PORTS_TO_SCAN = [3028, 8080, 1080, 1085, 4145, 9050] # Liste des ports a scanné pour une detection de proxy
API_TIMEOUT = 2 # Timeout des api's
DEBUG = 0 # Afficher l'ensemble des messages du serveurs dans la console
PORTS_TO_SCAN = [3028, 8080, 1080, 1085, 4145, 9050] # Liste des ports a scanné pour une detection de proxy
WHITELISTED_IP = ['127.0.0.1'] # IP a ne pas scanner
GLINE_DURATION = '1d' # La durée du gline
DEBUG_LEVEL = 10 # Le niveau des logs DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50
CONFIG_COLOR = {
'blanche': '\x0300', # Couleur blanche

View File

@@ -37,7 +37,7 @@ class Install:
print(f"===> Version of python : {python_version()} ==> OK")
return True
def checkDependencies(self) -> None:
"""### Verifie les dépendances si elles sont installées
- Test si les modules sont installés

View File

@@ -1,6 +1,7 @@
import ssl, re, importlib, sys, time, threading, socket
from ssl import SSLSocket
from datetime import datetime, timedelta
from typing import Union
from core.configuration import Config
from core.base import Base
@@ -16,18 +17,20 @@ class Irc:
self.beat = 30 # Lancer toutes les 30 secondes des actions de nettoyages
self.hb_active = True # Heartbeat active
self.HSID = '' # ID du serveur qui accueil le service ( Host Serveur Id )
self.IrcSocket:Union[socket.socket, SSLSocket] = None
self.INIT = 1 # Variable d'intialisation | 1 -> indique si le programme est en cours d'initialisation
self.RESTART = 0 # Variable pour le redemarrage du bot | 0 -> indique que le programme n'es pas en cours de redemarrage
self.CHARSET = ['utf-8', 'iso-8859-1'] # Charset utiliser pour décoder/encoder les messages
self.CHARSET = ['utf-8', 'iso-8859-1'] # Charset utiliser pour décoder/encoder les messages
self.SSL_VERSION = None # Version SSL
self.Config = Config()
# Liste des commandes internes du bot
self.commands_level = {
0: ['help', 'auth', 'copyright'],
1: ['load','reload','unload', 'deauth', 'uptime'],
2: ['show_sessions','show_modules', 'show_timers', 'show_threads'],
1: ['load','reload','unload', 'deauth', 'uptime', 'checkversion'],
2: ['show_modules', 'show_timers', 'show_threads', 'sentinel'],
3: ['quit', 'restart','addaccess','editaccess', 'delaccess']
}
@@ -38,7 +41,7 @@ class Irc:
self.commands.append(command)
self.Base = Base(self.Config)
self.Base.create_thread(self.heartbeat, (self.beat, ))
self.Base.create_thread(func=self.heartbeat, func_args=(self.beat, ))
##############################################
# CONNEXION IRC #
@@ -48,26 +51,46 @@ class Irc:
self.__create_socket()
self.__connect_to_irc(ircInstance)
except AssertionError as ae:
self.debug(f'Assertion error : {ae}')
self.Base.logs.critical(f'Assertion error: {ae}')
def __create_socket(self) -> None:
self.IrcSocket: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connexion_information = (self.Config.SERVEUR_IP, self.Config.SERVEUR_PORT)
self.IrcSocket.connect(connexion_information)
try:
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK)
connexion_information = (self.Config.SERVEUR_IP, self.Config.SERVEUR_PORT)
# Créer un object ssl
ssl_context = self.__ssl_context()
ssl_connexion = ssl_context.wrap_socket(self.IrcSocket, server_hostname=self.Config.SERVEUR_HOSTNAME)
self.IrcSocket = ssl_connexion
if self.Config.SERVEUR_SSL:
# Créer un object ssl
ssl_context = self.__ssl_context()
ssl_connexion = ssl_context.wrap_socket(soc, server_hostname=self.Config.SERVEUR_HOSTNAME)
ssl_connexion.connect(connexion_information)
self.IrcSocket:SSLSocket = ssl_connexion
return None
self.Base.logs.info(f"Connexion en mode SSL : Version = {self.IrcSocket.version()}")
self.SSL_VERSION = self.IrcSocket.version()
else:
soc.connect(connexion_information)
self.IrcSocket:socket.socket = soc
self.Base.logs.info("Connexion en mode normal")
return None
except ssl.SSLEOFError as soe:
self.Base.logs.critical(f"SSLEOFError __create_socket: {soe} - {soc.fileno()}")
except ssl.SSLError as se:
self.Base.logs.critical(f"SSLError __create_socket: {se} - {soc.fileno()}")
except OSError as oe:
self.Base.logs.critical(f"OSError __create_socket: {oe} - {soc.fileno()}")
except AttributeError as ae:
self.Base.logs.critical(f"OSError __create_socket: {oe} - {soc.fileno()}")
def __ssl_context(self) -> ssl.SSLContext:
ctx = ssl.create_default_context()
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
self.Base.logs.debug(f'SSLContext initiated with verified mode {ctx.verify_mode}')
return ctx
def __connect_to_irc(self, ircInstance: 'Irc') -> None:
@@ -78,50 +101,61 @@ class Irc:
self.load_existing_modules() # Charger les modules existant dans la base de données
while self.signal:
if self.RESTART == 1:
self.IrcSocket.shutdown(socket.SHUT_RDWR)
self.IrcSocket.close()
try:
if self.RESTART == 1:
self.Base.logs.debug('Restarting Defender ...')
self.IrcSocket.shutdown(socket.SHUT_RDWR)
self.IrcSocket.close()
while self.IrcSocket.fileno() != -1:
time.sleep(0.5)
self.debug("--> En attente de la fermeture du socket ...")
while self.IrcSocket.fileno() != -1:
time.sleep(0.5)
self.Base.logs.warning('--> Waiting for socket to close ...')
self.__create_socket()
self.__link(self.IrcSocket)
self.load_existing_modules()
self.RESTART = 0
# 4072 max what the socket can grab
buffer_size = self.IrcSocket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)
# data = self.IrcSocket.recv(buffer_size).splitlines(True)
self.__create_socket()
self.__link(self.IrcSocket)
self.load_existing_modules()
self.RESTART = 0
data_in_bytes = self.IrcSocket.recv(buffer_size)
count_bytes = len(data_in_bytes)
# 4072 max what the socket can grab
buffer_size = self.IrcSocket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)
data_in_bytes = self.IrcSocket.recv(buffer_size)
data = data_in_bytes.splitlines(True)
count_bytes = len(data_in_bytes)
while count_bytes > 4070:
# If the received message is > 4070 then loop and add the value to the variable
new_data = self.IrcSocket.recv(buffer_size)
data_in_bytes += new_data
count_bytes = len(new_data)
# print("========================================================")
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()
data = data_in_bytes.splitlines(True)
# print(f"{str(buffer_size)} - {str(len(data_in_bytes))}")
if not data:
break
if not data:
break
self.send_response(data)
self.send_response(data)
except ssl.SSLEOFError as soe:
self.Base.logs.error(f"SSLEOFError __connect_to_irc: {soe} - {data}")
except ssl.SSLError as se:
self.Base.logs.error(f"SSLError __connect_to_irc: {se} - {data}")
except OSError as oe:
self.Base.logs.error(f"SSLError __connect_to_irc: {oe} - {data}")
self.IrcSocket.shutdown(socket.SHUT_RDWR)
self.IrcSocket.close()
self.Base.logs.info("--> Fermeture de Defender ...")
except AssertionError as ae:
self.debug(f'Assertion error : {ae}')
self.Base.logs.error(f'Assertion error : {ae}')
except ValueError as ve:
self.debug(f'Value Error : {ve}')
except OSError as oe:
self.debug(f"OS Error : {oe}")
self.Base.logs.error(f'Value Error : {ve}')
except ssl.SSLEOFError as soe:
self.Base.logs.error(f"OS Error __connect_to_irc: {soe}")
except AttributeError as atte:
self.Base.logs.critical(f"{atte}")
# except Exception as e:
# self.debug(f"Exception: {e}")
def __link(self, writer:socket.socket) -> None:
"""Créer le link et envoyer les informations nécessaires pour la
@@ -130,63 +164,70 @@ class Irc:
Args:
writer (StreamWriter): permet l'envoi des informations au serveur.
"""
try:
nickname = self.Config.SERVICE_NICKNAME
username = self.Config.SERVICE_USERNAME
realname = self.Config.SERVICE_REALNAME
chan = self.Config.SERVICE_CHANLOG
info = self.Config.SERVICE_INFO
smodes = self.Config.SERVICE_SMODES
cmodes = self.Config.SERVICE_CMODES
umodes = self.Config.SERVICE_UMODES
host = self.Config.SERVICE_HOST
service_name = self.Config.SERVICE_NAME
nickname = self.Config.SERVICE_NICKNAME
username = self.Config.SERVICE_USERNAME
realname = self.Config.SERVICE_REALNAME
chan = self.Config.SERVICE_CHANLOG
info = self.Config.SERVICE_INFO
smodes = self.Config.SERVICE_SMODES
cmodes = self.Config.SERVICE_CMODES
umodes = self.Config.SERVICE_UMODES
host = self.Config.SERVICE_HOST
service_name = self.Config.SERVICE_NAME
password = self.Config.SERVEUR_PASSWORD
link = self.Config.SERVEUR_LINK
sid = self.Config.SERVEUR_ID
service_id = self.Config.SERVICE_ID
password = self.Config.SERVEUR_PASSWORD
link = self.Config.SERVEUR_LINK
sid = self.Config.SERVEUR_ID
service_id = self.Config.SERVICE_ID
version = self.Base.DEFENDER_VERSION
unixtime = self.Base.get_unixtime()
version = self.Config.DEFENDER_VERSION
unixtime = self.Base.get_unixtime()
# Envoyer un message d'identification
writer.send(f":{sid} PASS :{password}\r\n".encode('utf-8'))
writer.send(f":{sid} PROTOCTL NICKv2 VHP UMODE2 NICKIP SJOIN SJOIN2 SJ3 NOQUIT TKLEXT MLOCK SID MTAGS\r\n".encode('utf-8'))
writer.send(f":{sid} PROTOCTL EAUTH={link},,,{service_name}-v{version}\r\n".encode('utf-8'))
writer.send(f":{sid} PROTOCTL SID={sid}\r\n".encode('utf-8'))
writer.send(f":{sid} SERVER {link} 1 :{info}\r\n".encode('utf-8'))
writer.send(f":{sid} {nickname} :Reserved for services\r\n".encode('utf-8'))
writer.send(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * * :{realname}\r\n".encode('utf-8'))
writer.send(f":{sid} SJOIN {unixtime} {chan} + :{service_id}\r\n".encode('utf-8'))
writer.send(f":{sid} MODE {chan} +{cmodes}\r\n".encode('utf-8'))
writer.send(f":{service_id} SAMODE {chan} +{umodes} {nickname}\r\n".encode('utf-8'))
# Envoyer un message d'identification
# strtobytes = bytes(":" + sid + " PASS :" + password + "\r\n", 'utf-8')
# self.IrcSocket.send(strtobytes)
writer.send(f":{sid} PASS :{password}\r\n".encode('utf-8'))
writer.send(f":{sid} PROTOCTL NICKv2 VHP UMODE2 NICKIP SJOIN SJOIN2 SJ3 NOQUIT TKLEXT MLOCK SID MTAGS\r\n".encode('utf-8'))
writer.send(f":{sid} PROTOCTL EAUTH={link},,,{service_name}-v{version}\r\n".encode('utf-8'))
writer.send(f":{sid} PROTOCTL SID={sid}\r\n".encode('utf-8'))
writer.send(f":{sid} SERVER {link} 1 :{info}\r\n".encode('utf-8'))
writer.send(f":{sid} {nickname} :Reserved for services\r\n".encode('utf-8'))
writer.send(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * * :{realname}\r\n".encode('utf-8'))
writer.send(f":{sid} SJOIN {unixtime} {chan} + :{service_id}\r\n".encode('utf-8'))
writer.send(f":{sid} MODE {chan} +{cmodes}\r\n".encode('utf-8'))
writer.send(f":{service_id} SAMODE {chan} +{umodes} {nickname}\r\n".encode('utf-8'))
# writer.write(f"USER {nickname} {username} {username} {nickname} {username} :{username}\r\n".encode('utf-8'))
# writer.write(f"USER {username} {username} {username} :{username}\r\n".encode('utf-8'))
# writer.write(f"NICK {nickname}\r\n".encode('utf-8'))
self.Base.logs.debug('Link information sent to the server')
def send2socket(self, send_message:str)->None:
return None
except AttributeError as ae:
self.Base.logs.critical(f'{ae}')
def send2socket(self, send_message:str) -> None:
"""Envoit les commandes à envoyer au serveur.
Args:
string (Str): contient la commande à envoyer au serveur.
"""
try:
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0]))
with self.Base.lock:
# print(f">{str(send_message)}")
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0]))
self.Base.logs.debug(f'{send_message}')
except UnicodeDecodeError:
self.debug('Write Decode impossible try iso-8859-1')
self.Base.logs.error(f'Decode Error try iso-8859-1 - message: {send_message}')
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0],'replace'))
except UnicodeEncodeError:
self.debug('Write Encode impossible ... try iso-8859-1')
self.Base.logs.error(f'Encode Error try iso-8859-1 - message: {send_message}')
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0],'replace'))
except AssertionError as ae:
self.debug(f"Assertion error : {ae}")
self.Base.logs.warning(f'Assertion Error {ae} - message: {send_message}')
except ssl.SSLEOFError as soe:
self.Base.logs.error(f"SSLEOFError: {soe} - {send_message}")
except ssl.SSLError as se:
self.Base.logs.error(f"SSLError: {se} - {send_message}")
except OSError as oe:
self.debug(f"OS Error : {oe}")
self.Base.logs.error(f"OSError: {oe} - {send_message}")
def send_response(self, responses:list[bytes]) -> None:
try:
@@ -194,6 +235,7 @@ class Irc:
for data in responses:
response = data.decode(self.CHARSET[0]).split()
self.cmd(response)
except UnicodeEncodeError:
for data in responses:
response = data.decode(self.CHARSET[1],'replace').split()
@@ -203,10 +245,12 @@ class Irc:
response = data.decode(self.CHARSET[1],'replace').split()
self.cmd(response)
except AssertionError as ae:
self.debug(f"Assertion error : {ae}")
self.Base.logs.error(f"Assertion error : {ae}")
##############################################
# FIN CONNEXION IRC #
##############################################
def load_existing_modules(self) -> None:
"""Charge les modules qui existe déja dans la base de données
@@ -219,7 +263,7 @@ class Irc:
return None
def get_defender_uptime(self)->str:
def get_defender_uptime(self) -> str:
"""Savoir depuis quand Defender est connecté
Returns:
@@ -228,7 +272,7 @@ class Irc:
current_datetime = datetime.now()
diff_date = current_datetime - self.defender_connexion_datetime
uptime = timedelta(days=diff_date.days, seconds=diff_date.seconds)
return uptime
def heartbeat(self, beat:float) -> None:
@@ -252,7 +296,7 @@ class Irc:
# 2. Executer la fonction
try:
if not class_name in self.loaded_classes:
self.debug(f"La class [{class_name} n'existe pas !!]")
self.Base.logs.error(f"La class [{class_name} n'existe pas !!]")
return False
class_instance = self.loaded_classes[class_name]
@@ -262,12 +306,12 @@ class Irc:
self.Base.running_timers.append(t)
self.debug(f"Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}")
self.Base.logs.debug(f"Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}")
except AssertionError as ae:
self.debug(f'Assertion Error -> {ae}')
self.Base.logs.error(f'Assertion Error -> {ae}')
except TypeError as te:
self.debug(f"Type error -> {te}")
self.Base.logs.error(f"Type error -> {te}")
def __create_tasks(self, obj: object, method_name: str, param:list) -> None:
"""#### Ajouter les méthodes a éxecuter dans un dictionnaire
@@ -286,7 +330,7 @@ class Irc:
'param': param
}
self.debug(f'Function to execute : {str(self.Base.periodic_func)}')
self.Base.logs.debug(f'Function to execute : {str(self.Base.periodic_func)}')
self.send_ping_to_sereur()
return None
@@ -300,7 +344,6 @@ class Irc:
return None
def load_module(self, fromuser:str, module_name:str, init:bool = False) -> bool:
try:
# module_name : mod_voice
module_name = module_name.lower()
@@ -310,8 +353,8 @@ class Irc:
# Si le module est déja chargé
if 'mods.' + module_name in sys.modules:
self.debug("Module déja chargé ...")
self.debug('module name = ' + module_name)
self.Base.logs.info("Module déja chargé ...")
self.Base.logs.info('module name = ' + module_name)
if class_name in self.loaded_classes:
# Si le module existe dans la variable globale retourne False
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Le module {module_name} est déja chargé ! si vous souhaiter le recharge tapez {self.Config.SERVICE_PREFIX}reload {module_name}")
@@ -342,12 +385,14 @@ class Irc:
self.Base.db_record_module(fromuser, module_name)
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} chargé")
self.debug(self.loaded_classes)
self.Base.logs.info(self.loaded_classes)
return True
except ModuleNotFoundError as moduleNotFound:
self.debug(f"MODULE_NOT_FOUND: {moduleNotFound}")
self.Base.logs.error(f"MODULE_NOT_FOUND: {moduleNotFound}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}MODULE_NOT_FOUND{self.Config.CONFIG_COLOR['noire']} ]: {moduleNotFound}")
except Exception as e:
self.Base.logs.error(f"Something went wrong with a module you want to load : {e}")
def insert_db_uid(self, uid:str, nickname:str, username:str, hostname:str, umodes:str, vhost:str, isWebirc: bool) -> None:
@@ -377,7 +422,7 @@ class Irc:
return None
def update_db_uid(self, uid:str, newnickname:str) -> None:
# Récupérer l'ancien nickname
oldnickname = self.db_uid[uid]['nickname']
@@ -389,7 +434,7 @@ class Irc:
'umodes': self.db_uid[uid]['umodes'],
'vhost': self.db_uid[uid]['vhost']
}
# Modification du nickname dans la ligne UID
self.db_uid[uid]['nickname'] = newnickname
@@ -397,11 +442,11 @@ class Irc:
if oldnickname in self.db_uid:
del self.db_uid[oldnickname]
else:
self.debug(f"L'ancien nickname {oldnickname} n'existe pas dans UID_DB")
self.Base.logs.debug(f"L'ancien nickname {oldnickname} n'existe pas dans UID_DB")
response = False
self.debug(f"{oldnickname} changed to {newnickname}")
self.Base.logs.debug(f"{oldnickname} changed to {newnickname}")
return None
def delete_db_uid(self, uid:str) -> None:
@@ -428,8 +473,6 @@ class Irc:
umodes = self.db_uid[uid]['umodes']
vhost = self.db_uid[uid]['vhost']
level = int(level)
self.db_admin[uid] = {
'nickname': nickname,
@@ -479,7 +522,7 @@ class Irc:
"""
if channel in self.db_chan:
return False
response = True
# Ajouter un nouveau salon
self.db_chan.append(channel)
@@ -487,7 +530,7 @@ class Irc:
# Supprimer les doublons de la liste
self.db_chan = list(set(self.db_chan))
self.debug(f"Le salon {channel} a été ajouté à la liste CHAN_DB")
self.Base.logs.debug(f"Le salon {channel} a été ajouté à la liste CHAN_DB")
return response
@@ -498,13 +541,13 @@ class Irc:
if level > 4:
response = "Impossible d'ajouter un niveau > 4"
self.debug(response)
self.Base.logs.warning(response)
return response
# Verification si le user existe dans notre UID_DB
if not nickname in self.db_uid:
response = f"{nickname} n'est pas connecté, impossible de l'enregistrer pour le moment"
self.debug(response)
self.Base.logs.warning(response)
return response
hostname = self.db_uid[nickname]['hostname']
@@ -525,15 +568,15 @@ class Irc:
''', mes_donnees)
response = f"{nickname} ajouté en tant qu'administrateur de niveau {level}"
self.send2socket(f':{self.Config.SERVICE_NICKNAME} NOTICE {nickname} : {response}')
self.debug(response)
self.Base.logs.info(response)
return response
else:
response = f'{nickname} Existe déjà dans les users enregistrés'
self.send2socket(f':{self.Config.SERVICE_NICKNAME} NOTICE {nickname} : {response}')
self.debug(response)
self.Base.logs.info(response)
return response
def get_uid(self, uidornickname:str) -> str | None:
def get_uid(self, uidornickname:str) -> Union[str, None]:
uid_recherche = uidornickname
response = None
@@ -546,8 +589,8 @@ class Irc:
return response
def get_nickname(self, uidornickname:str) -> str | None:
def get_nickname(self, uidornickname:str) -> Union[str, None]:
nickname_recherche = uidornickname
response = None
@@ -561,11 +604,11 @@ class Irc:
return response
def is_cmd_allowed(self,nickname:str, cmd:str) -> bool:
# Vérifier si le user est identifié et si il a les droits
is_command_allowed = False
uid = self.get_uid(nickname)
if uid in self.db_admin:
admin_level = self.db_admin[uid]['level']
@@ -586,12 +629,13 @@ class Irc:
def debug(self, debug_msg:str) -> None:
if self.Config.DEBUG == 1:
if type(debug_msg) == list:
if debug_msg[0] != 'PING':
print(f"[{self.Base.get_datetime()}] - {debug_msg}")
else:
print(f"[{self.Base.get_datetime()}] - {debug_msg}")
# if self.Config.DEBUG == 1:
# if type(debug_msg) == list:
# if debug_msg[0] != 'PING':
# print(f"[{self.Base.get_datetime()}] - {debug_msg}")
# else:
#
print(f"[{self.Base.get_datetime()}] - {debug_msg}")
return None
@@ -604,13 +648,27 @@ class Irc:
def cmd(self, data:list) -> None:
try:
cmd_to_send:list[str] = data.copy()
cmd = data.copy()
cmd_to_debug = data.copy()
cmd_to_debug.pop(0)
if len(cmd) == 0 or len(cmd) == 1:
self.Base.logs.warning(f'Size ({str(len(cmd))}) - {cmd}')
return False
self.debug(cmd)
# self.debug(cmd_to_debug)
if len(data) == 7:
if data[2] == 'PRIVMSG' and data[4] == ':auth':
data_copy = data.copy()
data_copy[6] = '**********'
self.Base.logs.debug(data_copy)
else:
self.Base.logs.debug(data)
else:
self.Base.logs.debug(data)
match cmd[0]:
@@ -652,8 +710,8 @@ class Irc:
# self.Base.create_thread(self.abuseipdb_scan, (cmd[2], ))
pass
# Possibilité de déclancher les bans a ce niveau.
except IndexError:
self.debug(f'cmd reputation: index error')
except IndexError as ie:
self.Base.logs.error(f'{ie}')
case '320':
#:irc.deb.biz.st 320 PyDefender IRCParis07 :is in security-groups: known-users,webirc-users,tls-and-known-users,tls-users
@@ -672,17 +730,35 @@ class Irc:
hsid = str(cmd[0]).replace(':','')
if hsid == self.HSID:
if self.INIT == 1:
if self.Base.check_for_new_version():
version = f'{self.Base.DEFENDER_VERSION} >>> {self.Base.LATEST_DEFENDER_VERSION}'
else:
version = f'{self.Base.DEFENDER_VERSION}'
self.send2socket(f"MODE {self.Config.SERVICE_NICKNAME} +B")
self.send2socket(f"JOIN {self.Config.SERVICE_CHANLOG}")
print(f"################### DEFENDER ###################")
print(f"# SERVICE CONNECTE ")
print(f"# SERVEUR : {self.Config.SERVEUR_IP} ")
print(f"# PORT : {self.Config.SERVEUR_PORT} ")
print(f"# SSL : {self.Config.SERVEUR_SSL} ")
print(f"# SSL VER : {self.SSL_VERSION} ")
print(f"# NICKNAME : {self.Config.SERVICE_NICKNAME} ")
print(f"# CHANNEL : {self.Config.SERVICE_CHANLOG} ")
print(f"# VERSION : {self.Config.DEFENDER_VERSION} ")
print(f"# VERSION : {version} ")
print(f"################################################")
self.Base.logs.info(f"################### DEFENDER ###################")
self.Base.logs.info(f"# SERVICE CONNECTE ")
self.Base.logs.info(f"# SERVEUR : {self.Config.SERVEUR_IP} ")
self.Base.logs.info(f"# PORT : {self.Config.SERVEUR_PORT} ")
self.Base.logs.info(f"# SSL : {self.Config.SERVEUR_SSL} ")
self.Base.logs.info(f"# SSL VER : {self.SSL_VERSION} ")
self.Base.logs.info(f"# NICKNAME : {self.Config.SERVICE_NICKNAME} ")
self.Base.logs.info(f"# CHANNEL : {self.Config.SERVICE_CHANLOG} ")
self.Base.logs.info(f"# VERSION : {version} ")
self.Base.logs.info(f"################################################")
# Initialisation terminé aprés le premier PING
self.INIT = 0
# self.send2socket(f':{self.Config.SERVICE_ID} PING :{hsid}')
@@ -701,7 +777,7 @@ class Irc:
cmd.pop(0)
uid_who_quit = str(cmd[0]).replace(':', '')
self.delete_db_uid(uid_who_quit)
case 'PONG':
# ['@msgid=aTNJhp17kcPboF5diQqkUL;time=2023-12-28T20:35:58.411Z', ':irc.deb.biz.st', 'PONG', 'irc.deb.biz.st', ':Dev-PyDefender']
self.Base.execute_periodic_action()
@@ -739,12 +815,24 @@ class Irc:
self.insert_db_uid(uid, nickname, username, hostname, umodes, vhost, isWebirc)
for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(cmd_to_send)
case 'PRIVMSG':
try:
# Supprimer la premiere valeur
cmd.pop(0)
get_uid_or_nickname = str(cmd[0].replace(':',''))
if len(cmd) == 6:
if cmd[1] == 'PRIVMSG' and cmd[3] == ':auth':
cmd_copy = cmd.copy()
cmd_copy[5] = '**********'
self.Base.logs.debug(cmd_copy)
else:
self.Base.logs.info(cmd)
else:
self.Base.logs.info(f'{cmd}')
# user_trigger = get_user.split('!')[0]
user_trigger = self.get_nickname(get_uid_or_nickname)
dnickname = self.Config.SERVICE_NICKNAME
@@ -758,7 +846,7 @@ class Irc:
arg = convert_to_string.split()
arg.remove(f':{self.Config.SERVICE_PREFIX}')
if not arg[0].lower() in self.commands:
self.debug(f"This command {arg[0]} is not available")
self.Base.logs.debug(f"This command {arg[0]} is not available")
return False
cmd_to_send = convert_to_string.replace(':','')
@@ -778,15 +866,15 @@ class Irc:
# Réponse a un CTCP VERSION
if arg[0] == '\x01VERSION\x01':
self.send2socket(f':{dnickname} NOTICE {user_trigger} :\x01VERSION Service {self.Config.SERVICE_NICKNAME} V{self.Config.DEFENDER_VERSION}\x01')
self.send2socket(f':{dnickname} NOTICE {user_trigger} :\x01VERSION Service {self.Config.SERVICE_NICKNAME} V{self.Base.DEFENDER_VERSION}\x01')
return False
# Réponse a un TIME
if arg[0] == '\x01TIME\x01':
current_datetime = self.Base.get_datetime()
self.send2socket(f':{dnickname} NOTICE {user_trigger} :\x01TIME {current_datetime}\x01')
return False
# Réponse a un PING
if arg[0] == '\x01PING':
recieved_unixtime = int(arg[1].replace('\x01',''))
@@ -804,18 +892,19 @@ class Irc:
self._hcmds(user_trigger, arg)
except IndexError:
self.debug(f'cmd --> PRIVMSG --> List index out of range')
except IndexError as io:
self.Base.logs.error(f'{io}')
case _:
pass
# Envoyer la commande aux classes dynamiquement chargées
for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(cmd_to_send)
if cmd[2] != 'UID':
# Envoyer la commande aux classes dynamiquement chargées
for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(cmd_to_send)
except IndexError as ie:
self.debug(f"IRC CMD -> IndexError : {ie} - {cmd} - length {str(len(cmd))}")
self.Base.logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
def _hcmds(self, user: str, cmd:list) -> None:
@@ -847,8 +936,8 @@ class Irc:
current_command = cmd[0]
self.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["rouge"]}{current_command}{self.Config.CONFIG_COLOR["noire"]} ] - Accès Refusé à {self.get_nickname(fromuser)}')
self.send2socket(f':{dnickname} NOTICE {fromuser} : Accès Refusé')
except IndexError:
self.debug(f'_hcmd notallowed : Index Error')
except IndexError as ie:
self.Base.logs.error(f'{ie}')
case 'deauth':
@@ -868,11 +957,12 @@ class Irc:
query = f"SELECT id, level FROM {self.Base.DB_SCHEMA['admins']} WHERE user = :user AND password = :password"
result = self.Base.db_execute_query(query, mes_donnees)
user_from_db = result.fetchone()
if not user_from_db is None:
uid_user = self.get_uid(user_to_log)
self.insert_db_admin(uid_user, user_from_db[1])
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}{current_command}{self.Config.CONFIG_COLOR['noire']} ] - {self.get_nickname(fromuser)} est désormais connecté a {dnickname}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Connexion a {dnickname} réussie!")
else:
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}{current_command}{self.Config.CONFIG_COLOR['noire']} ] - {self.get_nickname(fromuser)} a tapé un mauvais mot de pass")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Mot de passe incorrecte")
@@ -889,13 +979,13 @@ class Irc:
response = self.create_defender_user(newnickname, newlevel, password)
self.send2socket(f':{dnickname} NOTICE {fromuser} : {response}')
self.debug(response)
self.Base.logs.info(response)
except IndexError as ie:
self.debug(f'_hcmd addaccess: {ie}')
self.Base.logs.error(f'_hcmd addaccess: {ie}')
self.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} addaccess [nickname] [level] [password]')
except TypeError as te:
self.debug(f'_hcmd addaccess: out of index : {te}')
self.Base.logs.error(f'_hcmd addaccess: out of index : {te}')
self.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} addaccess [nickname] [level] [password]')
case 'editaccess':
@@ -943,9 +1033,9 @@ class Irc:
self.send2socket(f":{dnickname} NOTICE {fromuser} : Impossible de modifier l'utilisateur {str(user_new_level)}")
except TypeError as te:
self.debug(f"Type error : {te}")
self.Base.logs.error(f"Type error : {te}")
except ValueError as ve:
self.debug(f"Value Error : {ve}")
self.Base.logs.error(f"Value Error : {ve}")
self.send2socket(f':{dnickname} NOTICE {fromuser} : .editaccess [USER] [NEWPASSWORD] [NEWLEVEL]')
case 'delaccess':
@@ -955,9 +1045,9 @@ class Irc:
if user_to_del != user_confirmation:
self.send2socket(f':{dnickname} NOTICE {fromuser} : Les user ne sont pas les mêmes, tu dois confirmer le user que tu veux supprimer')
self.Base.logs.warning(f':{dnickname} NOTICE {fromuser} : Les user ne sont pas les mêmes, tu dois confirmer le user que tu veux supprimer')
return None
print(len(cmd))
if len(cmd) < 3:
self.send2socket(f':{dnickname} NOTICE {fromuser} : .delaccess [USER] [CONFIRMUSER]')
return None
@@ -976,6 +1066,7 @@ class Irc:
level_user_to_del = info_user[1]
if current_user_level <= level_user_to_del:
self.send2socket(f':{dnickname} NOTICE {fromuser} : You are not allowed to delete this access')
self.Base.logs.warning(f':{dnickname} NOTICE {fromuser} : You are not allowed to delete this access')
return None
data_to_delete = {'user': user_to_del}
@@ -985,6 +1076,7 @@ class Irc:
self.send2socket(f':{dnickname} NOTICE {fromuser} : User {user_to_del} has been deleted !')
else:
self.send2socket(f":{dnickname} NOTICE {fromuser} : Impossible de supprimer l'utilisateur.")
self.Base.logs.warning(f":{dnickname} NOTICE {fromuser} : Impossible de supprimer l'utilisateur.")
case 'help':
@@ -1023,37 +1115,12 @@ class Irc:
case 'unload':
# unload mod_dktmb
module_name = str(cmd[1]).lower() # Le nom du module. exemple: mod_defender
class_name = module_name.split('_')[1].capitalize() # Nom de la class. exemple: Defender
try:
module_name = str(cmd[1]).lower() # Le nom du module. exemple: mod_defender
class_name = module_name.split('_')[1].capitalize() # Nom de la class. exemple: Defender
if class_name in self.loaded_classes:
for level, command in self.loaded_classes[class_name].commands_level.items():
# Supprimer la commande de la variable commands
for c in self.loaded_classes[class_name].commands_level[level]:
self.commands.remove(c)
self.commands_level[level].remove(c)
del self.loaded_classes[class_name]
# Supprimer le module de la base de données
self.Base.db_delete_module(module_name)
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} supprimé")
case 'reload':
# reload mod_dktmb
module_name = str(cmd[1]).lower() # ==> mod_defender
class_name = module_name.split('_')[1].capitalize() # ==> Defender
if 'mods.' + module_name in sys.modules:
self.debug('Module Already Loaded ... reload the module ...')
the_module = sys.modules['mods.' + module_name]
importlib.reload(the_module)
# Supprimer la class déja instancier
if class_name in self.loaded_classes:
# Supprimer les commandes déclarer dans la classe
self.loaded_classes[class_name].unload()
for level, command in self.loaded_classes[class_name].commands_level.items():
# Supprimer la commande de la variable commands
for c in self.loaded_classes[class_name].commands_level[level]:
@@ -1062,14 +1129,46 @@ class Irc:
del self.loaded_classes[class_name]
my_class = getattr(the_module, class_name, None)
new_instance = my_class(self.ircObject)
self.loaded_classes[class_name] = new_instance
# Supprimer le module de la base de données
self.Base.db_delete_module(module_name)
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} rechargé")
return False
else:
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} n'est pas chargé !")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} supprimé")
except:
self.Base.logs.error(f"Something went wrong with a module you want to load")
case 'reload':
# reload mod_dktmb
try:
module_name = str(cmd[1]).lower() # ==> mod_defender
class_name = module_name.split('_')[1].capitalize() # ==> Defender
if 'mods.' + module_name in sys.modules:
self.loaded_classes[class_name].unload()
self.Base.logs.info('Module Already Loaded ... reload the module ...')
the_module = sys.modules['mods.' + module_name]
importlib.reload(the_module)
# Supprimer la class déja instancier
if class_name in self.loaded_classes:
# Supprimer les commandes déclarer dans la classe
for level, command in self.loaded_classes[class_name].commands_level.items():
# Supprimer la commande de la variable commands
for c in self.loaded_classes[class_name].commands_level[level]:
self.commands.remove(c)
self.commands_level[level].remove(c)
del self.loaded_classes[class_name]
my_class = getattr(the_module, class_name, None)
new_instance = my_class(self.ircObject)
self.loaded_classes[class_name] = new_instance
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} rechargé")
return False
else:
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} n'est pas chargé !")
except:
self.Base.logs.error(f"Something went wrong with a module you want to reload")
case 'quit':
try:
@@ -1084,13 +1183,13 @@ class Irc:
self.send2socket(f':{dnickname} NOTICE {fromuser} : Arrêt du service {dnickname}')
self.send2socket(f':{self.Config.SERVEUR_LINK} SQUIT {self.Config.SERVEUR_LINK} :{final_reason}')
self.debug(f'Arrêt du server {dnickname}')
self.Base.logs.info(f'Arrêt du server {dnickname}')
self.RESTART = 0
self.signal = False
except IndexError:
self.debug('_hcmd die: out of index')
except IndexError as ie:
self.Base.logs.error(f'{ie}')
self.send2socket(f"QUIT Good bye")
case 'restart':
@@ -1102,16 +1201,19 @@ class Irc:
self.db_uid.clear() #Vider UID_DB
self.db_chan = [] #Vider les salons
for class_name in self.loaded_classes:
self.loaded_classes[class_name].unload()
self.send2socket(f':{dnickname} NOTICE {fromuser} : Redémarrage du service {dnickname}')
self.send2socket(f':{self.Config.SERVEUR_LINK} SQUIT {self.Config.SERVEUR_LINK} :{final_reason}')
self.debug(f'Redémarrage du server {dnickname}')
self.Base.logs.info(f'Redémarrage du server {dnickname}')
self.loaded_classes.clear()
self.RESTART = 1 # Set restart status to 1 saying that the service will restart
self.INIT = 1 # set init to 1 saying that the service will be re initiated
case 'show_modules':
self.debug(self.loaded_classes)
self.Base.logs.debug(self.loaded_classes)
results = self.Base.db_execute_query(f'SELECT module FROM {self.Base.DB_SCHEMA["modules"]}')
results = results.fetchall()
@@ -1122,25 +1224,52 @@ class Irc:
for r in results:
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :Le module {r[0]} chargé")
self.debug(r[0])
case 'show_timers':
if self.Base.running_timers:
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :{self.Base.running_timers}")
self.debug(self.Base.running_timers)
else:
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :Aucun timers en cours d'execution")
case 'show_threads':
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :{self.Base.running_threads}")
running_thread_name:list = []
for thread in self.Base.running_threads:
running_thread_name.append(f"{thread.getName()} ({thread.is_alive()})")
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :{str(running_thread_name)}")
case 'uptime':
uptime = self.get_defender_uptime()
self.send2socket(f':{dnickname} NOTICE {fromuser} : {uptime}')
case 'copyright':
self.send2socket(f':{dnickname} NOTICE {fromuser} : # Defender V.{self.Config.DEFENDER_VERSION} Developped by adator® and dktmb® #')
self.send2socket(f':{dnickname} NOTICE {fromuser} : # Defender V.{self.Base.DEFENDER_VERSION} Developped by adator® and dktmb® #')
case 'sentinel':
# .sentinel on
activation = str(cmd[1]).lower()
service_id = self.Config.SERVICE_ID
channel_to_dont_quit = [self.Config.SALON_JAIL, dchanlog]
if activation == 'on':
for chan in self.db_chan:
if not chan in channel_to_dont_quit:
self.send2socket(f":{service_id} JOIN {chan}")
if activation == 'off':
for chan in self.db_chan:
if not chan in channel_to_dont_quit:
self.send2socket(f":{service_id} PART {chan}")
case 'checkversion':
if self.Base.check_for_new_version():
self.send2socket(f':{dnickname} NOTICE {fromuser} : New Version available : {self.Base.DEFENDER_VERSION} >>> {self.Base.LATEST_DEFENDER_VERSION}')
else:
self.send2socket(f':{dnickname} NOTICE {fromuser} : You have the latest version of defender')
pass
case _:
pass

File diff suppressed because it is too large Load Diff

View File

@@ -66,6 +66,10 @@ class Test():
self.core.db_execute_query(self.session, table_logs)
return None
def unload(self) -> None:
return None
def _hcmds(self, user:str, cmd: list) -> None:
command = cmd[0].lower()

3
version.json Normal file
View File

@@ -0,0 +1,3 @@
{
"version": "4.0.2"
}