mirror of
https://github.com/iio612/DEFENDER.git
synced 2026-02-13 19:24:23 +00:00
First Version 6
This commit is contained in:
252
core/base.py
252
core/base.py
@@ -13,27 +13,39 @@ import ast
|
||||
import requests
|
||||
|
||||
from dataclasses import fields
|
||||
from typing import Union, Literal
|
||||
from typing import Union, Literal, TYPE_CHECKING
|
||||
from base64 import b64decode, b64encode
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from sqlalchemy import create_engine, Engine, Connection, CursorResult
|
||||
from sqlalchemy.sql import text
|
||||
from core.loadConf import ConfigDataModel
|
||||
from core.definition import MConfig
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.classes.settings import Settings
|
||||
|
||||
class Base:
|
||||
|
||||
def __init__(self, Config: ConfigDataModel) -> None:
|
||||
def __init__(self, Config: MConfig, settings: 'Settings') -> None:
|
||||
|
||||
self.Config = Config # Assigner l'objet de configuration
|
||||
self.Settings: Settings = settings
|
||||
self.init_log_system() # Demarrer le systeme de log
|
||||
self.check_for_new_version(True) # Verifier si une nouvelle version est disponible
|
||||
|
||||
self.running_timers:list[threading.Timer] = [] # Liste des timers en cours
|
||||
self.running_threads:list[threading.Thread] = [] # Liste des threads en cours
|
||||
self.running_sockets: list[socket.socket] = [] # Les sockets ouvert
|
||||
self.periodic_func:dict[object] = {} # Liste des fonctions en attentes
|
||||
# Liste des timers en cours
|
||||
self.running_timers:list[threading.Timer] = self.Settings.RUNNING_TIMERS
|
||||
|
||||
self.lock = threading.RLock() # Création du lock
|
||||
# Liste des threads en cours
|
||||
self.running_threads:list[threading.Thread] = self.Settings.RUNNING_THREADS
|
||||
|
||||
# Les sockets ouvert
|
||||
self.running_sockets: list[socket.socket] = self.Settings.RUNNING_SOCKETS
|
||||
|
||||
# Liste des fonctions en attentes
|
||||
self.periodic_func:dict[object] = self.Settings.PERIODIC_FUNC
|
||||
|
||||
# Création du lock
|
||||
self.lock = self.Settings.LOCK
|
||||
|
||||
self.install: bool = False # Initialisation de la variable d'installation
|
||||
self.engine, self.cursor = self.db_init() # Initialisation de la connexion a la base de données
|
||||
@@ -48,16 +60,15 @@ class Base:
|
||||
with open(version_filename, 'r') as version_data:
|
||||
current_version:dict[str, str] = json.load(version_data)
|
||||
|
||||
self.Config.current_version = current_version['version']
|
||||
self.Config.CURRENT_VERSION = current_version['version']
|
||||
|
||||
return None
|
||||
|
||||
def __get_latest_defender_version(self) -> None:
|
||||
try:
|
||||
self.logs.debug(f'Looking for a new version available on Github')
|
||||
# print(f'===> Looking for a new version available on Github')
|
||||
token = ''
|
||||
json_url = f'https://raw.githubusercontent.com/adator85/IRC_DEFENDER_MODULES/main/version.json'
|
||||
json_url = f'https://raw.githubusercontent.com/adator85/DEFENDER/main/version.json'
|
||||
headers = {
|
||||
'Authorization': f'token {token}',
|
||||
'Accept': 'application/vnd.github.v3.raw' # Indique à GitHub que nous voulons le contenu brut du fichier
|
||||
@@ -71,7 +82,7 @@ class Base:
|
||||
response.raise_for_status() # Vérifie si la requête a réussi
|
||||
json_response:dict = response.json()
|
||||
# self.LATEST_DEFENDER_VERSION = json_response["version"]
|
||||
self.Config.latest_version = json_response['version']
|
||||
self.Config.LATEST_VERSION = json_response['version']
|
||||
|
||||
return None
|
||||
except requests.HTTPError as err:
|
||||
@@ -99,8 +110,8 @@ class Base:
|
||||
self.__get_latest_defender_version()
|
||||
|
||||
isNewVersion = False
|
||||
latest_version = self.Config.latest_version
|
||||
current_version = self.Config.current_version
|
||||
latest_version = self.Config.LATEST_VERSION
|
||||
current_version = self.Config.CURRENT_VERSION
|
||||
|
||||
curr_major , curr_minor, curr_patch = current_version.split('.')
|
||||
last_major, last_minor, last_patch = latest_version.split('.')
|
||||
@@ -120,6 +131,10 @@ class Base:
|
||||
return isNewVersion
|
||||
except ValueError as ve:
|
||||
self.logs.error(f'Impossible to convert in version number : {ve}')
|
||||
except AttributeError as atterr:
|
||||
self.logs.error(f'Attribute Error: {atterr}')
|
||||
except Exception as err:
|
||||
self.logs.error(f'General Error: {err}')
|
||||
|
||||
def get_unixtime(self) -> int:
|
||||
"""
|
||||
@@ -159,7 +174,7 @@ class Base:
|
||||
Returns:
|
||||
None: Aucun retour
|
||||
"""
|
||||
sql_insert = f"INSERT INTO {self.Config.table_log} (datetime, server_msg) VALUES (:datetime, :server_msg)"
|
||||
sql_insert = f"INSERT INTO {self.Config.TABLE_LOG} (datetime, server_msg) VALUES (:datetime, :server_msg)"
|
||||
mes_donnees = {'datetime': str(self.get_datetime()),'server_msg': f'{log_message}'}
|
||||
self.db_execute_query(sql_insert, mes_donnees)
|
||||
|
||||
@@ -167,21 +182,34 @@ class Base:
|
||||
|
||||
def init_log_system(self) -> None:
|
||||
# Create folder if not available
|
||||
logs_directory = f'logs{os.sep}'
|
||||
logs_directory = f'logs{self.Config.OS_SEP}'
|
||||
if not os.path.exists(f'{logs_directory}'):
|
||||
os.makedirs(logs_directory)
|
||||
|
||||
# Init logs object
|
||||
self.logs = logging
|
||||
self.logs.basicConfig(level=self.Config.DEBUG_LEVEL,
|
||||
filename='logs/defender.log',
|
||||
filename=f'logs{self.Config.OS_SEP}defender.log',
|
||||
encoding='UTF-8',
|
||||
format='%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s')
|
||||
|
||||
logger = logging.getLogger()
|
||||
logger.addFilter(self.replace_filter)
|
||||
self.logs.info('#################### STARTING DEFENDER ####################')
|
||||
|
||||
return None
|
||||
|
||||
def replace_filter(self, record: logging.LogRecord) -> bool:
|
||||
|
||||
response = True
|
||||
|
||||
# record.msg = record.getMessage().replace("PING", "[REDACTED]")
|
||||
|
||||
response = False if "PING" in record.getMessage() else True
|
||||
response = False if f":{self.Config.SERVICE_PREFIX}auth" in record.getMessage() else True
|
||||
|
||||
return response # Retourne True pour permettre l'affichage du message
|
||||
|
||||
def log_cmd(self, user_cmd:str, cmd:str) -> None:
|
||||
"""Enregistre les commandes envoyées par les utilisateurs
|
||||
|
||||
@@ -195,7 +223,7 @@ class Base:
|
||||
cmd_list[2] = '*******'
|
||||
cmd = ' '.join(cmd_list)
|
||||
|
||||
insert_cmd_query = f"INSERT INTO {self.Config.table_commande} (datetime, user, commande) VALUES (:datetime, :user, :commande)"
|
||||
insert_cmd_query = f"INSERT INTO {self.Config.TABLE_COMMAND} (datetime, user, commande) VALUES (:datetime, :user, :commande)"
|
||||
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'commande': cmd}
|
||||
self.db_execute_query(insert_cmd_query, mes_donnees)
|
||||
|
||||
@@ -210,7 +238,7 @@ class Base:
|
||||
Returns:
|
||||
bool: True si le module existe déja dans la base de données sinon False
|
||||
"""
|
||||
query = f"SELECT id FROM {self.Config.table_module} WHERE module_name = :module_name"
|
||||
query = f"SELECT id FROM {self.Config.TABLE_MODULE} WHERE module_name = :module_name"
|
||||
mes_donnes = {'module_name': module_name}
|
||||
results = self.db_execute_query(query, mes_donnes)
|
||||
|
||||
@@ -228,7 +256,7 @@ class Base:
|
||||
|
||||
if not self.db_isModuleExist(module_name):
|
||||
self.logs.debug(f"Le module {module_name} n'existe pas alors ont le créer")
|
||||
insert_cmd_query = f"INSERT INTO {self.Config.table_module} (datetime, user, module_name, isdefault) VALUES (:datetime, :user, :module_name, :isdefault)"
|
||||
insert_cmd_query = f"INSERT INTO {self.Config.TABLE_MODULE} (datetime, user, module_name, isdefault) VALUES (:datetime, :user, :module_name, :isdefault)"
|
||||
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'module_name': module_name, 'isdefault': isdefault}
|
||||
self.db_execute_query(insert_cmd_query, mes_donnees)
|
||||
else:
|
||||
@@ -243,7 +271,7 @@ class Base:
|
||||
user_cmd (str): le user qui a rechargé le module
|
||||
module_name (str): le module a rechargé
|
||||
"""
|
||||
update_cmd_query = f"UPDATE {self.Config.table_module} SET datetime = :datetime, user = :user WHERE module_name = :module_name"
|
||||
update_cmd_query = f"UPDATE {self.Config.TABLE_MODULE} SET datetime = :datetime, user = :user WHERE module_name = :module_name"
|
||||
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'module_name': module_name}
|
||||
self.db_execute_query(update_cmd_query, mes_donnees)
|
||||
|
||||
@@ -255,7 +283,7 @@ class Base:
|
||||
Args:
|
||||
cmd (str): le module a supprimer
|
||||
"""
|
||||
insert_cmd_query = f"DELETE FROM {self.Config.table_module} WHERE module_name = :module_name"
|
||||
insert_cmd_query = f"DELETE FROM {self.Config.TABLE_MODULE} WHERE module_name = :module_name"
|
||||
mes_donnees = {'module_name': module_name}
|
||||
self.db_execute_query(insert_cmd_query, mes_donnees)
|
||||
|
||||
@@ -278,7 +306,7 @@ class Base:
|
||||
try:
|
||||
response = True
|
||||
current_date = self.get_datetime()
|
||||
core_table = self.Config.table_config
|
||||
core_table = self.Config.TABLE_CONFIG
|
||||
|
||||
# Add local parameters to DB
|
||||
for field in fields(dataclassObj):
|
||||
@@ -342,7 +370,7 @@ class Base:
|
||||
|
||||
def db_update_core_config(self, module_name:str, dataclassObj: object, param_key:str, param_value: str) -> bool:
|
||||
|
||||
core_table = self.Config.table_config
|
||||
core_table = self.Config.TABLE_CONFIG
|
||||
# Check if the param exist
|
||||
if not hasattr(dataclassObj, param_key):
|
||||
self.logs.error(f"Le parametre {param_key} n'existe pas dans la variable global")
|
||||
@@ -374,62 +402,62 @@ class Base:
|
||||
|
||||
return True
|
||||
|
||||
def db_query_channel(self, action: Literal['add','del'], module_name: str, channel_name: str) -> bool:
|
||||
"""You can add a channel or delete a channel.
|
||||
# def db_query_channel(self, action: Literal['add','del'], module_name: str, channel_name: str) -> bool:
|
||||
# """You can add a channel or delete a channel.
|
||||
|
||||
Args:
|
||||
action (Literal['add','del']): Action on the database
|
||||
module_name (str): The module name (mod_test)
|
||||
channel_name (str): The channel name (With #)
|
||||
# Args:
|
||||
# action (Literal['add','del']): Action on the database
|
||||
# module_name (str): The module name (mod_test)
|
||||
# channel_name (str): The channel name (With #)
|
||||
|
||||
Returns:
|
||||
bool: True if action done
|
||||
"""
|
||||
try:
|
||||
channel_name = channel_name.lower() if self.Is_Channel(channel_name) else None
|
||||
core_table = 'core_channel'
|
||||
# Returns:
|
||||
# bool: True if action done
|
||||
# """
|
||||
# try:
|
||||
# channel_name = channel_name.lower() if self.Is_Channel(channel_name) else None
|
||||
# core_table = self.Config.TABLE_CHANNEL
|
||||
|
||||
if not channel_name:
|
||||
self.logs.warn(f'The channel [{channel_name}] is not correct')
|
||||
return False
|
||||
# if not channel_name:
|
||||
# self.logs.warning(f'The channel [{channel_name}] is not correct')
|
||||
# return False
|
||||
|
||||
match action:
|
||||
# match action:
|
||||
|
||||
case 'add':
|
||||
mes_donnees = {'module_name': module_name, 'channel_name': channel_name}
|
||||
response = self.db_execute_query(f"SELECT id FROM {core_table} WHERE module_name = :module_name AND channel_name = :channel_name", mes_donnees)
|
||||
isChannelExist = response.fetchone()
|
||||
# case 'add':
|
||||
# mes_donnees = {'module_name': module_name, 'channel_name': channel_name}
|
||||
# response = self.db_execute_query(f"SELECT id FROM {core_table} WHERE module_name = :module_name AND channel_name = :channel_name", mes_donnees)
|
||||
# isChannelExist = response.fetchone()
|
||||
|
||||
if isChannelExist is None:
|
||||
mes_donnees = {'datetime': self.get_datetime(), 'channel_name': channel_name, 'module_name': module_name}
|
||||
insert = self.db_execute_query(f"INSERT INTO {core_table} (datetime, channel_name, module_name) VALUES (:datetime, :channel_name, :module_name)", mes_donnees)
|
||||
if insert.rowcount:
|
||||
self.logs.debug(f'New channel added: channel={channel_name} / module_name={module_name}')
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
pass
|
||||
# if isChannelExist is None:
|
||||
# mes_donnees = {'datetime': self.get_datetime(), 'channel_name': channel_name, 'module_name': module_name}
|
||||
# insert = self.db_execute_query(f"INSERT INTO {core_table} (datetime, channel_name, module_name) VALUES (:datetime, :channel_name, :module_name)", mes_donnees)
|
||||
# if insert.rowcount:
|
||||
# self.logs.debug(f'New channel added: channel={channel_name} / module_name={module_name}')
|
||||
# return True
|
||||
# else:
|
||||
# return False
|
||||
# pass
|
||||
|
||||
case 'del':
|
||||
mes_donnes = {'channel_name': channel_name, 'module_name': module_name}
|
||||
response = self.db_execute_query(f"DELETE FROM {core_table} WHERE channel_name = :channel_name AND module_name = :module_name", mes_donnes)
|
||||
# case 'del':
|
||||
# mes_donnes = {'channel_name': channel_name, 'module_name': module_name}
|
||||
# response = self.db_execute_query(f"DELETE FROM {core_table} WHERE channel_name = :channel_name AND module_name = :module_name", mes_donnes)
|
||||
|
||||
if response.rowcount > 0:
|
||||
self.logs.debug(f'Channel deleted: channel={channel_name} / module: {module_name}')
|
||||
return True
|
||||
else:
|
||||
# if response.rowcount > 0:
|
||||
# self.logs.debug(f'Channel deleted: channel={channel_name} / module: {module_name}')
|
||||
# return True
|
||||
# else:
|
||||
|
||||
return False
|
||||
# return False
|
||||
|
||||
case _:
|
||||
return False
|
||||
# case _:
|
||||
# return False
|
||||
|
||||
except Exception as err:
|
||||
self.logs.error(err)
|
||||
# except Exception as err:
|
||||
# self.logs.error(err)
|
||||
|
||||
def db_create_first_admin(self) -> None:
|
||||
|
||||
user = self.db_execute_query(f"SELECT id FROM {self.Config.table_admin}")
|
||||
user = self.db_execute_query(f"SELECT id FROM {self.Config.TABLE_ADMIN}")
|
||||
if not user.fetchall():
|
||||
admin = self.Config.OWNER
|
||||
password = self.crypt_password(self.Config.PASSWORD)
|
||||
@@ -442,7 +470,7 @@ class Base:
|
||||
'level': 5
|
||||
}
|
||||
self.db_execute_query(f"""
|
||||
INSERT INTO {self.Config.table_admin}
|
||||
INSERT INTO {self.Config.TABLE_ADMIN}
|
||||
(createdOn, user, password, hostname, vhost, level)
|
||||
VALUES
|
||||
(:createdOn, :user, :password, :hostname, :vhost, :level)"""
|
||||
@@ -489,6 +517,25 @@ class Base:
|
||||
except AssertionError as ae:
|
||||
self.logs.error(f'{ae}')
|
||||
|
||||
def thread_count(self, thread_name: str) -> int:
|
||||
"""This method return the number of existing threads
|
||||
currently running or not running
|
||||
|
||||
Args:
|
||||
thread_name (str): The name of the thread
|
||||
|
||||
Returns:
|
||||
int: Number of threads
|
||||
"""
|
||||
with self.lock:
|
||||
count = 0
|
||||
|
||||
for thr in self.running_threads:
|
||||
if thread_name == thr.getName():
|
||||
count += 1
|
||||
|
||||
return count
|
||||
|
||||
def garbage_collector_timer(self) -> None:
|
||||
"""Methode qui supprime les timers qui ont finis leurs job
|
||||
"""
|
||||
@@ -511,6 +558,7 @@ class Base:
|
||||
try:
|
||||
for thread in self.running_threads:
|
||||
if thread.getName() != 'heartbeat':
|
||||
# print(thread.getName(), thread.is_alive(), sep=' / ')
|
||||
if not thread.is_alive():
|
||||
self.running_threads.remove(thread)
|
||||
self.logs.info(f"Thread {str(thread.getName())} {str(thread.native_id)} removed")
|
||||
@@ -564,8 +612,8 @@ class Base:
|
||||
|
||||
def db_init(self) -> tuple[Engine, Connection]:
|
||||
|
||||
db_directory = self.Config.db_path
|
||||
full_path_db = self.Config.db_path + self.Config.db_name
|
||||
db_directory = self.Config.DB_PATH
|
||||
full_path_db = self.Config.DB_PATH + self.Config.DB_NAME
|
||||
|
||||
if not os.path.exists(db_directory):
|
||||
self.install = True
|
||||
@@ -578,14 +626,14 @@ class Base:
|
||||
|
||||
def __create_db(self) -> None:
|
||||
|
||||
table_core_log = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_log} (
|
||||
table_core_log = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_LOG} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
datetime TEXT,
|
||||
server_msg TEXT
|
||||
)
|
||||
'''
|
||||
|
||||
table_core_config = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_config} (
|
||||
table_core_config = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_CONFIG} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
datetime TEXT,
|
||||
module_name TEXT,
|
||||
@@ -594,7 +642,7 @@ class Base:
|
||||
)
|
||||
'''
|
||||
|
||||
table_core_log_command = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_commande} (
|
||||
table_core_log_command = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_COMMAND} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
datetime TEXT,
|
||||
user TEXT,
|
||||
@@ -602,7 +650,7 @@ class Base:
|
||||
)
|
||||
'''
|
||||
|
||||
table_core_module = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_module} (
|
||||
table_core_module = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_MODULE} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
datetime TEXT,
|
||||
user TEXT,
|
||||
@@ -611,7 +659,7 @@ class Base:
|
||||
)
|
||||
'''
|
||||
|
||||
table_core_channel = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_channel} (
|
||||
table_core_channel = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_CHANNEL} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
datetime TEXT,
|
||||
module_name TEXT,
|
||||
@@ -619,7 +667,7 @@ class Base:
|
||||
)
|
||||
'''
|
||||
|
||||
table_core_admin = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_admin} (
|
||||
table_core_admin = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_ADMIN} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
createdOn TEXT,
|
||||
user TEXT,
|
||||
@@ -717,16 +765,19 @@ class Base:
|
||||
self.logs.critical(f'This remote ip is not valid : {ve}')
|
||||
return None
|
||||
|
||||
# def encode_ip(self, remote_ip_address: str) -> Union[str, None]:
|
||||
def encode_ip(self, remote_ip_address: str) -> Union[str, None]:
|
||||
|
||||
# binary_ip = b64encode()
|
||||
# try:
|
||||
# decoded_ip = ipaddress.ip_address(binary_ip)
|
||||
binary_ip = socket.inet_aton(remote_ip_address)
|
||||
try:
|
||||
encoded_ip = b64encode(binary_ip).decode()
|
||||
|
||||
# return decoded_ip.exploded
|
||||
# except ValueError as ve:
|
||||
# self.logs.critical(f'This remote ip is not valid : {ve}')
|
||||
# return None
|
||||
return encoded_ip
|
||||
except ValueError as ve:
|
||||
self.logs.critical(f'This remote ip is not valid : {ve}')
|
||||
return None
|
||||
except Exception as err:
|
||||
self.logs.critical(f'General Error: {err}')
|
||||
return None
|
||||
|
||||
def get_random(self, lenght:int) -> str:
|
||||
"""
|
||||
@@ -756,7 +807,7 @@ class Base:
|
||||
# Vider le dictionnaire de fonction
|
||||
self.periodic_func.clear()
|
||||
|
||||
def clean_uid(self, uid:str) -> str:
|
||||
def clean_uid(self, uid:str) -> Union[str, None]:
|
||||
"""Clean UID by removing @ / % / + / ~ / * / :
|
||||
|
||||
Args:
|
||||
@@ -765,34 +816,13 @@ class Base:
|
||||
Returns:
|
||||
str: Clean UID without any sign
|
||||
"""
|
||||
|
||||
pattern = fr'[:|@|%|\+|~|\*]*'
|
||||
parsed_UID = re.sub(pattern, '', uid)
|
||||
|
||||
return parsed_UID
|
||||
|
||||
def Is_Channel(self, channelToCheck: str) -> bool:
|
||||
"""Check if the string has the # caractere and return True if this is a channel
|
||||
|
||||
Args:
|
||||
channelToCheck (str): The string to test if it is a channel or not
|
||||
|
||||
Returns:
|
||||
bool: True if the string is a channel / False if this is not a channel
|
||||
"""
|
||||
try:
|
||||
|
||||
if channelToCheck is None:
|
||||
return False
|
||||
if uid is None:
|
||||
return None
|
||||
|
||||
pattern = fr'^#'
|
||||
isChannel = re.findall(pattern, channelToCheck)
|
||||
pattern = fr'[:|@|%|\+|~|\*]*'
|
||||
parsed_UID = re.sub(pattern, '', uid)
|
||||
|
||||
if not isChannel:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
return parsed_UID
|
||||
except TypeError as te:
|
||||
self.logs.error(f'TypeError: [{channelToCheck}] - {te}')
|
||||
except Exception as err:
|
||||
self.logs.error(f'Error Not defined: {err}')
|
||||
self.logs.error(f'Type Error: {te}')
|
||||
|
||||
Reference in New Issue
Block a user