Introduce full asyncio version (still some module to migrate). Defender JSONRPC Server ready and using with uvcorn

This commit is contained in:
adator
2025-11-20 00:29:32 +01:00
parent 1b30b1ff4e
commit aa15aea749
34 changed files with 2533 additions and 2627 deletions

View File

@@ -45,9 +45,6 @@ class Base:
# Liste des fonctions en attentes
self.periodic_func: dict[object] = self.Settings.PERIODIC_FUNC
# Création du lock
self.lock = self.Settings.LOCK
# Init install variable
self.install: bool = False
@@ -57,8 +54,8 @@ class Base:
# Create the database
# self.__create_db()
def init(self) -> None:
self.__create_db()
async def init(self) -> None:
await self.__create_db()
def __set_current_defender_version(self) -> None:
"""This will put the current version of Defender
@@ -145,7 +142,7 @@ class Base:
except Exception as err:
self.logs.error(f'General Error: {err}')
def create_log(self, log_message: str) -> None:
async def create_log(self, log_message: str) -> None:
"""Enregiste les logs
Args:
@@ -156,11 +153,11 @@ class Base:
"""
sql_insert = f"INSERT INTO {self.Config.TABLE_LOG} (datetime, server_msg) VALUES (:datetime, :server_msg)"
mes_donnees = {'datetime': str(self.Utils.get_sdatetime()),'server_msg': f'{log_message}'}
self.db_execute_query(sql_insert, mes_donnees)
await self.db_execute_query(sql_insert, mes_donnees)
return None
def log_cmd(self, user_cmd: str, cmd: str) -> None:
async def log_cmd(self, user_cmd: str, cmd: str) -> None:
"""Enregistre les commandes envoyées par les utilisateurs
Args:
@@ -176,11 +173,11 @@ class Base:
insert_cmd_query = f"INSERT INTO {self.Config.TABLE_COMMAND} (datetime, user, commande) VALUES (:datetime, :user, :commande)"
mes_donnees = {'datetime': self.Utils.get_sdatetime(), 'user': user_cmd, 'commande': cmd}
self.db_execute_query(insert_cmd_query, mes_donnees)
await self.db_execute_query(insert_cmd_query, mes_donnees)
return None
def db_sync_core_config(self, module_name: str, dataclassObj: object) -> bool:
async def db_sync_core_config(self, module_name: str, dataclassObj: object) -> bool:
"""Sync module local parameters with the database
if new module then local param will be stored in the database
if old module then db param will be moved to the local dataclassObj
@@ -207,7 +204,7 @@ class Base:
param_to_search = {'module_name': module_name, 'param_key': param_key}
search_query = f'''SELECT id FROM {core_table} WHERE module_name = :module_name AND param_key = :param_key'''
excecute_search_query = self.db_execute_query(search_query, param_to_search)
excecute_search_query = await self.db_execute_query(search_query, param_to_search)
result_search_query = excecute_search_query.fetchone()
if result_search_query is None:
@@ -219,7 +216,7 @@ class Base:
insert_query = f'''INSERT INTO {core_table} (datetime, module_name, param_key, param_value)
VALUES (:datetime, :module_name, :param_key, :param_value)
'''
execution = self.db_execute_query(insert_query, param_to_insert)
execution = await self.db_execute_query(insert_query, param_to_insert)
if execution.rowcount > 0:
self.logs.debug(f'New parameter added to the database: {param_key} --> {param_value}')
@@ -227,14 +224,14 @@ class Base:
# Delete from DB unused parameter
query_select = f"SELECT module_name, param_key, param_value FROM {core_table} WHERE module_name = :module_name"
parameter = {'module_name': module_name}
execute_query_select = self.db_execute_query(query_select, parameter)
execute_query_select = await self.db_execute_query(query_select, parameter)
result_query_select = execute_query_select.fetchall()
for result in result_query_select:
db_mod_name, db_param_key, db_param_value = result
if not hasattr(dataclassObj, db_param_key):
mes_donnees = {'param_key': db_param_key, 'module_name': db_mod_name}
execute_delete = self.db_execute_query(f'DELETE FROM {core_table} WHERE module_name = :module_name and param_key = :param_key', mes_donnees)
execute_delete = await self.db_execute_query(f'DELETE FROM {core_table} WHERE module_name = :module_name and param_key = :param_key', mes_donnees)
row_affected = execute_delete.rowcount
if row_affected > 0:
self.logs.debug(f'A parameter has been deleted from the database: {db_param_key} --> {db_param_value} | Mod: {db_mod_name}')
@@ -242,7 +239,7 @@ class Base:
# Sync local variable with Database
query = f"SELECT param_key, param_value FROM {core_table} WHERE module_name = :module_name"
parameter = {'module_name': module_name}
response = self.db_execute_query(query, parameter)
response = await self.db_execute_query(query, parameter)
result = response.fetchall()
for param, value in result:
@@ -259,7 +256,7 @@ class Base:
self.logs.error(err)
return False
def db_update_core_config(self, module_name:str, dataclass_obj: object, param_key:str, param_value: str) -> bool:
async def db_update_core_config(self, module_name:str, dataclass_obj: object, param_key:str, param_value: str) -> bool:
core_table = self.Config.TABLE_CONFIG
# Check if the param exist
@@ -269,7 +266,7 @@ class Base:
mes_donnees = {'module_name': module_name, 'param_key': param_key, 'param_value': param_value}
search_param_query = f"SELECT id FROM {core_table} WHERE module_name = :module_name AND param_key = :param_key"
result = self.db_execute_query(search_param_query, mes_donnees)
result = await self.db_execute_query(search_param_query, mes_donnees)
is_param_exist = result.fetchone()
if not is_param_exist is None:
@@ -279,7 +276,7 @@ class Base:
'param_value': param_value
}
query = f'''UPDATE {core_table} SET datetime = :datetime, param_value = :param_value WHERE module_name = :module_name AND param_key = :param_key'''
update = self.db_execute_query(query, mes_donnees)
update = await self.db_execute_query(query, mes_donnees)
updated_rows = update.rowcount
if updated_rows > 0:
setattr(dataclass_obj, param_key, self.int_if_possible(param_value))
@@ -293,9 +290,9 @@ class Base:
return True
def db_create_first_admin(self) -> None:
async def db_create_first_admin(self) -> None:
user = self.db_execute_query(f"SELECT id FROM {self.Config.TABLE_ADMIN}")
user = await self.db_execute_query(f"SELECT id FROM {self.Config.TABLE_ADMIN}")
if not user.fetchall():
admin = self.Config.OWNER
password = self.Utils.hash_password(self.Config.PASSWORD)
@@ -308,7 +305,7 @@ class Base:
'language': 'EN',
'level': 5
}
self.db_execute_query(f"""
await self.db_execute_query(f"""
INSERT INTO {self.Config.TABLE_ADMIN}
(createdOn, user, password, hostname, vhost, language, level)
VALUES
@@ -381,11 +378,28 @@ class Base:
return None
task = asyncio.create_task(func, name=name)
task.add_done_callback(self.asynctask_done)
self.running_asynctasks.append(task)
self.logs.debug(f"++ New asynchrone task created as: {task.get_name()}")
return task
def asynctask_done(self, task: asyncio.Task):
"""Log task when done
Args:
task (asyncio.Task): The Asyncio Task callback
"""
try:
if task.exception():
self.logs.error(f"[ASYNCIO] Task {task.get_name()} failed with exception: {task.exception()}")
else:
self.logs.debug(f"[ASYNCIO] Task {task.get_name()} completed successfully.")
except asyncio.CancelledError as ce:
self.logs.debug(f"[ASYNCIO] Task {task.get_name()} terminated with cancelled error.")
except asyncio.InvalidStateError as ie:
self.logs.debug(f"[ASYNCIO] Task {task.get_name()} terminated with invalid state error.")
def is_thread_alive(self, thread_name: str) -> bool:
"""Check if the thread is still running! using the is_alive method of Threads.
@@ -429,7 +443,7 @@ class Base:
Returns:
int: Number of threads
"""
with self.lock:
with self.Settings.LOCK:
count = 0
for thr in self.running_threads:
@@ -478,7 +492,7 @@ class Base:
self.running_sockets.remove(soc)
self.logs.debug(f"-- Socket ==> closed {str(soc.fileno())}")
def shutdown(self) -> None:
async def shutdown(self) -> None:
"""Methode qui va préparer l'arrêt complêt du service
"""
# Nettoyage des timers
@@ -507,6 +521,9 @@ class Base:
self.running_sockets.remove(soc)
self.logs.debug(f"> Socket ==> closed {str(soc.fileno())}")
await self.Loader.RpcServer.stop_server()
self.db_close()
return None
@@ -524,7 +541,7 @@ class Base:
self.logs.info("-- Database connexion has been initiated")
return engine, cursor
def __create_db(self) -> None:
async def __create_db(self) -> None:
table_core_log = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_LOG} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -594,27 +611,27 @@ class Base:
)
'''
self.db_execute_query(table_core_log)
self.db_execute_query(table_core_log_command)
self.db_execute_query(table_core_module)
self.db_execute_query(table_core_admin)
self.db_execute_query(table_core_client)
self.db_execute_query(table_core_channel)
self.db_execute_query(table_core_config)
await self.db_execute_query(table_core_log)
await self.db_execute_query(table_core_log_command)
await self.db_execute_query(table_core_module)
await self.db_execute_query(table_core_admin)
await self.db_execute_query(table_core_client)
await self.db_execute_query(table_core_channel)
await self.db_execute_query(table_core_config)
# Patch database
self.db_patch(self.Config.TABLE_ADMIN, "language", "TEXT")
await 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)
await self.Loader.ModuleUtils.db_register_module('mod_command', 'sys', True)
await self.Loader.ModuleUtils.db_register_module('mod_defender', 'sys', True)
self.install = False
return None
def db_execute_query(self, query:str, params:dict = {}) -> CursorResult:
async def db_execute_query(self, query:str, params:dict = {}) -> CursorResult:
with self.lock:
async with self.Loader.Settings.AILOCK:
insert_query = text(query)
if not params:
response = self.cursor.execute(insert_query)
@@ -625,8 +642,8 @@ 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})")
async def db_is_column_exist(self, table_name: str, column_name: str) -> bool:
q = await 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:
@@ -634,12 +651,12 @@ class Base:
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):
async def db_patch(self, table_name: str, column_name: str, column_type: str) -> bool:
if not await 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)
await self.db_execute_query(patch)
await 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
@@ -647,9 +664,9 @@ class Base:
return False
def db_close(self) -> None:
try:
self.cursor.close()
self.logs.debug("Database engine closed!")
except AttributeError as ae:
self.logs.error(f"Attribute Error : {ae}")

View File

@@ -24,22 +24,19 @@ class IModule(ABC):
# Log the module
self.ctx.Logs.debug(f'Loading Module {self.module_name} ...')
def init(self) -> None:
self.load()
self.create_tables()
async def sync_db(self) -> None:
# Sync the configuration with core configuration (Mandatory)
self.ctx.Base.db_sync_core_config(self.module_name, self.mod_config)
await self.ctx.Base.db_sync_core_config(self.module_name, self.mod_config)
return None
def update_configuration(self, param_key: str, param_value: Union[str, int]) -> None:
async def update_configuration(self, param_key: str, param_value: Union[str, int]) -> None:
"""Update the local and core configuration
Args:
param_key (str): The parameter key
param_value (str): The parameter value
"""
self.ctx.Base.db_update_core_config(self.module_name, self.mod_config, param_key, param_value)
await self.ctx.Base.db_update_core_config(self.module_name, self.mod_config, param_key, param_value)
@property
@abstractmethod
@@ -58,17 +55,17 @@ class IModule(ABC):
"""
@abstractmethod
def load(self) -> None:
async def load(self) -> None:
"""This method is executed when the module is loaded or reloaded.
"""
@abstractmethod
def unload(self) -> None:
async def unload(self) -> None:
"""This method is executed when the module is unloaded or reloaded.
"""
@abstractmethod
def cmd(self, data: list) -> None:
async def cmd(self, data: list) -> None:
"""When recieving server messages.
Args:
@@ -76,7 +73,7 @@ class IModule(ABC):
"""
@abstractmethod
def hcmds(self, user: str, channel: Optional[str], cmd: list[str], fullcmd: Optional[list[str]] = None) -> None:
async 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:

View File

@@ -4,31 +4,20 @@ from core.classes.protocols.command_handler import CommandHandler
if TYPE_CHECKING:
from core.definition import MClient, MSasl, MUser, MChannel
from core.irc import Irc
from core.loader import Loader
class IProtocol(ABC):
Handler: Optional[CommandHandler] = None
def __init__(self, uplink: 'Irc'):
def __init__(self, context: 'Loader'):
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._ctx = context
self.Handler = CommandHandler(context)
self.init_protocol()
self._Logs.info(f"[PROTOCOL] Protocol [{self.__class__.__name__}] loaded!")
self._ctx.Logs.info(f"[PROTOCOL] Protocol [{self.__class__.__name__}] loaded!")
@abstractmethod
def init_protocol(self):
@@ -313,7 +302,7 @@ class IProtocol(ABC):
# ------------------------------------------------------------------------
@abstractmethod
async def parse_uid(self, server_msg: list[str]) -> Optional['MUser']:
def parse_uid(self, server_msg: list[str]) -> Optional['MUser']:
"""Parse UID and return dictionary.
Args:
@@ -324,7 +313,7 @@ class IProtocol(ABC):
"""
@abstractmethod
async def parse_quit(self, server_msg: list[str]) -> tuple[Optional['MUser'], str]:
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:
@@ -335,7 +324,7 @@ class IProtocol(ABC):
"""
@abstractmethod
async def parse_nick(self, server_msg: list[str]) -> tuple[Optional['MUser'], str, str]:
def parse_nick(self, server_msg: list[str]) -> tuple[Optional['MUser'], str, str]:
"""Parse nick changes and return dictionary.
>>> [':97KAAAAAC', 'NICK', 'testinspir', '1757360740']
@@ -349,7 +338,7 @@ class IProtocol(ABC):
"""
@abstractmethod
async def parse_privmsg(self, server_msg: list[str]) -> tuple[Optional['MUser'], Optional['MUser'], Optional['MChannel'], str]:
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']

View File

@@ -173,7 +173,7 @@ class Admin:
return admin.language
def db_auth_admin_via_fingerprint(self, fp: str, uidornickname: str) -> bool:
async def db_auth_admin_via_fingerprint(self, fp: str, uidornickname: str) -> bool:
"""Check the fingerprint
Args:
@@ -188,7 +188,7 @@ class Admin:
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)
exe = await self.Base.db_execute_query(query, data)
result = exe.fetchone()
if result:
account = result[0]
@@ -204,7 +204,7 @@ class Admin:
return False
def db_is_admin_exist(self, admin_nickname: str) -> bool:
async def db_is_admin_exist(self, admin_nickname: str) -> bool:
"""Verify if the admin exist in the database!
Args:
@@ -216,7 +216,7 @@ class Admin:
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)
r = await self.Base.db_execute_query(query_search_user, mes_donnees)
exist_user = r.fetchone()
if exist_user:
return True

View File

@@ -17,9 +17,7 @@ class Channel:
Args:
loader (Loader): The Loader Instance
"""
self.Logs = loader.Logs
self.Base = loader.Base
self.Utils = loader.Utils
self._ctx = loader
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)
@@ -34,14 +32,14 @@ class Channel:
exist = False
if not self.is_valid_channel(new_channel.name):
self.Logs.error(f"The channel {new_channel.name} is not valid, channel must start with #")
self._ctx.Logs.error(f"The channel {new_channel.name} is not valid, channel must start with #")
return False
for record in self.UID_CHANNEL_DB:
if record.name.lower() == new_channel.name.lower():
# If the channel exist, update the user list and do not go further
exist = True
# self.Logs.debug(f'{record.name} already exist')
# self._ctx.Logs.debug(f'{record.name} already exist')
for user in new_channel.uids:
record.uids.append(user)
@@ -49,7 +47,7 @@ class Channel:
# Supprimer les doublons
del_duplicates = list(set(record.uids))
record.uids = del_duplicates
# self.Logs.debug(f'Updating a new UID to the channel {record}')
# self._ctx.Logs.debug(f'Updating a new UID to the channel {record}')
return result
if not exist:
@@ -57,10 +55,10 @@ class Channel:
new_channel.name = new_channel.name.lower()
self.UID_CHANNEL_DB.append(new_channel)
result = True
# self.Logs.debug(f'New Channel Created: ({new_channel})')
# self._ctx.Logs.debug(f'New Channel Created: ({new_channel})')
if not result:
self.Logs.critical(f'The Channel Object was not inserted {new_channel}')
self._ctx.Logs.critical(f'The Channel Object was not inserted {new_channel}')
self.clean_channel()
@@ -103,7 +101,7 @@ class Channel:
return result
for userid in chan_obj.uids:
if self.Utils.clean_uid(userid) == self.Utils.clean_uid(uid):
if self._ctx.Utils.clean_uid(userid) == self._ctx.Utils.clean_uid(uid):
chan_obj.uids.remove(userid)
result = True
@@ -111,7 +109,7 @@ class Channel:
return result
except ValueError as ve:
self.Logs.error(f'{ve}')
self._ctx.Logs.error(f'{ve}')
return False
def delete_user_from_all_channel(self, uid:str) -> bool:
@@ -128,7 +126,7 @@ class Channel:
for record in self.UID_CHANNEL_DB:
for user_id in record.uids:
if self.Utils.clean_uid(user_id) == self.Utils.clean_uid(uid):
if self._ctx.Utils.clean_uid(user_id) == self._ctx.Utils.clean_uid(uid):
record.uids.remove(user_id)
result = True
@@ -136,7 +134,7 @@ class Channel:
return result
except ValueError as ve:
self.Logs.error(f'{ve}')
self._ctx.Logs.error(f'{ve}')
return False
def add_user_to_a_channel(self, channel_name: str, uid: str) -> bool:
@@ -154,7 +152,7 @@ class Channel:
if chan_obj is None:
# Create a new channel if the channel don't exist
self.Logs.debug(f"New channel will be created ({channel_name} - {uid})")
self._ctx.Logs.debug(f"New channel will be created ({channel_name} - {uid})")
return self.insert(MChannel(channel_name, uids=[uid]))
chan_obj.uids.append(uid)
@@ -163,7 +161,7 @@ class Channel:
return True
except Exception as err:
self.Logs.error(f'{err}')
self._ctx.Logs.error(f'{err}')
return False
def is_user_present_in_channel(self, channel_name: str, uid: str) -> bool:
@@ -180,9 +178,9 @@ class Channel:
if chan is None:
return False
clean_uid = self.Utils.clean_uid(uid=uid)
clean_uid = self._ctx.Utils.clean_uid(uid=uid)
for chan_uid in chan.uids:
if self.Utils.clean_uid(chan_uid) == clean_uid:
if self._ctx.Utils.clean_uid(chan_uid) == clean_uid:
return True
return False
@@ -197,7 +195,7 @@ class Channel:
return None
except Exception as err:
self.Logs.error(f'{err}')
self._ctx.Logs.error(f'{err}')
def get_channel(self, channel_name: str) -> Optional['MChannel']:
"""Get the channel object
@@ -237,13 +235,13 @@ class Channel:
else:
return True
except TypeError as te:
self.Logs.error(f'TypeError: [{channel_to_check}] - {te}')
self._ctx.Logs.error(f'TypeError: [{channel_to_check}] - {te}')
return False
except Exception as err:
self.Logs.error(f'Error Not defined: {err}')
self._ctx.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:
async 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:
@@ -256,39 +254,49 @@ class Channel:
"""
try:
channel_name = channel_name.lower() if self.is_valid_channel(channel_name) else None
core_table = self.Base.Config.TABLE_CHANNEL
core_table = self._ctx.Base.Config.TABLE_CHANNEL
if not channel_name:
self.Logs.warning(f'The channel [{channel_name}] is not correct')
self._ctx.Logs.warning(f'The channel [{channel_name}] is not correct')
return False
match action:
case 'add':
mes_donnees = {'module_name': module_name, 'channel_name': channel_name}
response = self.Base.db_execute_query(f"SELECT id FROM {core_table} WHERE module_name = :module_name AND channel_name = :channel_name", mes_donnees)
response = await self._ctx.Base.db_execute_query(f"SELECT id FROM {core_table} WHERE module_name = :module_name AND channel_name = :channel_name", mes_donnees)
is_channel_exist = response.fetchone()
if is_channel_exist is None:
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)
mes_donnees = {'datetime': self._ctx.Utils.get_sdatetime(), 'channel_name': channel_name, 'module_name': module_name}
insert = await self._ctx.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'Channel added to DB: channel={channel_name} / module_name={module_name}')
self._ctx.Logs.debug(f'Channel added to DB: channel={channel_name} / module_name={module_name}')
return True
else:
return False
case 'del':
mes_donnes = {'channel_name': channel_name, 'module_name': module_name}
response = self.Base.db_execute_query(f"DELETE FROM {core_table} WHERE channel_name = :channel_name AND module_name = :module_name", mes_donnes)
response = await self._ctx.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 from DB: channel={channel_name} / module: {module_name}')
self._ctx.Logs.debug(f'Channel deleted from DB: channel={channel_name} / module: {module_name}')
return True
else:
return False
except Exception as err:
self.Logs.error(err)
self._ctx.Logs.error(err)
return False
async def db_join_saved_channels(self) -> None:
"""## Joining saved channels"""
exec_query = await self._ctx.Base.db_execute_query(f'SELECT distinct channel_name FROM {self._ctx.Config.TABLE_CHANNEL}')
result_query = exec_query.fetchall()
if result_query:
for chan_name in result_query:
chan = chan_name[0]
await self._ctx.Irc.Protocol.send_sjoin(channel=chan)

View File

@@ -201,7 +201,7 @@ class Client:
return True
def db_is_account_exist(self, account: str) -> bool:
async def db_is_account_exist(self, account: str) -> bool:
"""Check if the account exist in the database
Args:
@@ -213,7 +213,7 @@ class Client:
table_client = self.Base.Config.TABLE_CLIENT
account_to_check = {'account': account.lower()}
account_to_check_query = self.Base.db_execute_query(f"""
account_to_check_query = await self.Base.db_execute_query(f"""
SELECT id FROM {table_client} WHERE LOWER(account) = :account
""", account_to_check)

View File

@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
import socket
if TYPE_CHECKING:
from core.irc import Irc
from core.loader import Loader
# Modules impacted by rehashing!
REHASH_MODULES = [
@@ -14,7 +14,10 @@ REHASH_MODULES = [
'core.classes.modules.config',
'core.base',
'core.classes.modules.commands',
'core.classes.modules.rpc',
'core.classes.modules.rpc.rpc_channel',
'core.classes.modules.rpc.rpc_command',
'core.classes.modules.rpc.rpc_user',
'core.classes.modules.rpc.rpc',
'core.classes.interfaces.iprotocol',
'core.classes.interfaces.imodule',
'core.classes.protocols.command_handler',
@@ -24,66 +27,62 @@ REHASH_MODULES = [
]
def restart_service(uplink: 'Irc', reason: str = "Restarting with no reason!") -> None:
async def restart_service(uplink: 'Loader', reason: str = "Restarting with no reason!") -> None:
"""
Args:
uplink (Irc): The Irc instance
reason (str): The reason of the restart.
"""
# reload modules.
# unload modules.
for module in uplink.ModuleUtils.model_get_loaded_modules().copy():
uplink.ModuleUtils.unload_one_module(uplink, module.module_name)
await uplink.ModuleUtils.unload_one_module(module.module_name)
uplink.Base.garbage_collector_thread()
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=reason)
await uplink.Irc.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()
while uplink.IrcSocket.fileno() != -1:
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()
# Reload configuration
uplink.Config = uplink.ConfModule.Configuration(uplink).configuration_model
uplink.Base = uplink.BaseModule.Base(uplink)
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.Irc.Protocol.Handler.DB_IRCDCOMMS.clear()
uplink.init_service_user()
uplink.Utils.create_socket(uplink)
uplink.Protocol.send_link()
# Reload Service modules
for module in uplink.ModuleUtils.model_get_loaded_modules().copy():
await uplink.ModuleUtils.reload_one_module(module.module_name, uplink.Settings.current_admin)
uplink.Irc.signal = True
await uplink.Irc.run()
uplink.Config.DEFENDER_RESTART = 0
def rehash_service(uplink: 'Irc', nickname: str) -> None:
async def rehash_service(uplink: 'Loader', nickname: str) -> None:
need_a_restart = ["SERVEUR_ID"]
uplink.Settings.set_cache('db_commands', uplink.Commands.DB_COMMANDS)
uplink.Loader.RpcServer.stop_server()
await uplink.RpcServer.stop_server()
restart_flag = False
config_model_bakcup = uplink.Config
mods = REHASH_MODULES
for mod in mods:
importlib.reload(sys.modules[mod])
uplink.Protocol.send_priv_msg(
await uplink.Irc.Protocol.send_priv_msg(
nick_from=uplink.Config.SERVICE_NICKNAME,
msg=f'[REHASH] Module [{mod}] reloaded',
channel=uplink.Config.SERVICE_CHANLOG
)
uplink.Utils = sys.modules['core.utils']
uplink.Config = uplink.Loader.Config = uplink.Loader.ConfModule.Configuration(uplink.Loader).configuration_model
uplink.Config = uplink.ConfModule.Configuration(uplink).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
@@ -96,7 +95,7 @@ def rehash_service(uplink: 'Irc', nickname: str) -> None:
for key, value in conf_bkp_dict.items():
if config_dict[key] != value and key != 'COLORS':
uplink.Protocol.send_priv_msg(
await uplink.Irc.Protocol.send_priv_msg(
nick_from=uplink.Config.SERVICE_NICKNAME,
msg=f'[{key}]: {value} ==> {config_dict[key]}',
channel=uplink.Config.SERVICE_CHANLOG
@@ -105,27 +104,28 @@ def rehash_service(uplink: 'Irc', nickname: str) -> None:
restart_flag = True
if config_model_bakcup.SERVICE_NICKNAME != uplink.Config.SERVICE_NICKNAME:
uplink.Protocol.send_set_nick(uplink.Config.SERVICE_NICKNAME)
await uplink.Irc.Protocol.send_set_nick(uplink.Config.SERVICE_NICKNAME)
if restart_flag:
uplink.Config.SERVEUR_ID = config_model_bakcup.SERVEUR_ID
uplink.Protocol.send_priv_msg(
await uplink.Irc.Protocol.send_priv_msg(
nick_from=uplink.Config.SERVICE_NICKNAME,
channel=uplink.Config.SERVICE_CHANLOG,
msg='You need to restart defender !')
# 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 = uplink.CommandModule.Command(uplink)
uplink.Commands.DB_COMMANDS = uplink.Settings.get_cache('db_commands')
uplink.Loader.Base = uplink.Loader.BaseModule.Base(uplink.Loader)
uplink.Protocol = uplink.Loader.PFactory.get()
uplink.Protocol.register_command()
uplink.Base = uplink.BaseModule.Base(uplink)
uplink.Irc.Protocol = uplink.PFactory.get()
uplink.Irc.Protocol.register_command()
uplink.RpcServer = uplink.RpcServerModule.JSonRpcServer(uplink)
uplink.Base.create_asynctask(uplink.RpcServer.start_server())
# Reload Service modules
for module in uplink.ModuleUtils.model_get_loaded_modules().copy():
uplink.ModuleUtils.reload_one_module(uplink, module.module_name, nickname)
await uplink.ModuleUtils.reload_one_module(module.module_name, nickname)
return None

View File

@@ -0,0 +1 @@
__version__ = '1.0.0'

View File

@@ -1,8 +1,11 @@
import base64
import json
import logging
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.requests import Request
from starlette.routing import Route
import uvicorn
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
@@ -11,120 +14,101 @@ from core.classes.modules.rpc.rpc_command import RPCCommand
if TYPE_CHECKING:
from core.loader import Loader
ProxyLoader: Optional['Loader'] = None
class JSonRpcServer:
class RPCRequestHandler(BaseHTTPRequestHandler):
def __init__(self, context: 'Loader', *, hostname: str = 'localhost', port: int = 5000):
self._ctx = context
self.live: bool = False
self.host = hostname
self.port = port
self.routes: list[Route] = []
self.server: Optional[uvicorn.Server] = None
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)
self.methods: dict = {
'user.list': RPCUser(context).user_list,
'user.get': RPCUser(context).user_get,
'channel.list': RPCChannel(context).channel_list,
'command.list': RPCCommand(context).command_list,
'command.get.by.name': RPCCommand(context).command_get_by_name,
'command.get.by.module': RPCCommand(context).command_get_by_module
}
method = request_data.get("method")
async def start_server(self):
if not self.live:
self.routes = [Route('/api', self.request_handler, methods=['POST'])]
self.app_jsonrpc = Starlette(debug=False, routes=self.routes)
config = uvicorn.Config(self.app_jsonrpc, host=self.host, port=self.port, log_level=self._ctx.Config.DEBUG_LEVEL)
self.server = uvicorn.Server(config)
self.live = True
await self._ctx.Irc.Protocol.send_priv_msg(
self._ctx.Config.SERVICE_NICKNAME,
"[DEFENDER JSONRPC SERVER] RPC Server started!",
self._ctx.Config.SERVICE_CHANLOG
)
await self.server.serve()
self._ctx.Logs.debug("Server is going to shutdown!")
else:
self._ctx.Logs.debug("Server already running")
async def stop_server(self):
if self.server:
self.server.should_exit = True
await self.server.shutdown()
self.live = False
self._ctx.Logs.debug("JSON-RPC Server off!")
await self._ctx.Irc.Protocol.send_priv_msg(
self._ctx.Config.SERVICE_NICKNAME,
"[DEFENDER JSONRPC SERVER] RPC Server Stopped!",
self._ctx.Config.SERVICE_CHANLOG
)
async def request_handler(self, request: Request) -> JSONResponse:
request_data: dict = await request.json()
method = request_data.get("method", None)
params: dict[str, Any] = request_data.get("params", {})
auth: JSONResponse = self.authenticate(request.headers, request_data)
if not json.loads(auth.body).get('result', False):
return auth
response_data = {
"jsonrpc": "2.0",
"id": request_data.get('id', 123)
}
response_data['method'] = method
rip = request.client.host
rport = request.client.port
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
if method in self.methods:
response_data['result'] = self.methods[method](**params)
return JSONResponse(response_data, http_code)
case 'channel.list':
channel = RPCChannel(ProxyLoader)
response_data['result'] = channel.channel_list()
logs.debug(f'[RPC] {method} recieved from {rip}:{rport}')
del channel
response_data['error'] = create_error_response(JSONRPCErrorCode.METHOD_NOT_FOUND)
self._ctx.Logs.debug(f'[RPC ERROR] {method} recieved from {rip}:{rport}')
http_code = 404
return JSONResponse(response_data, http_code)
case 'command.list':
command = RPCCommand(ProxyLoader)
response_data['result'] = command.command_list()
logs.debug(f'[RPC] {method} recieved from {rip}:{rport}')
del command
def authenticate(self, headers: dict, body: dict) -> JSONResponse:
ok_auth = {
'jsonrpc': '2.0',
'id': body.get('id', 123),
'result': True
}
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
logs = self._ctx.Logs
auth: str = headers.get('Authorization', '')
if not auth:
return self.send_auth_error(body)
# 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
return self.send_auth_error(body)
try:
# Decode the base64-encoded username:password
@@ -132,70 +116,25 @@ class RPCRequestHandler(BaseHTTPRequestHandler):
username, password = decoded_credentials.split(":", 1)
# Check the username and password.
for rpcuser in ProxyLoader.Irc.Config.RPC_USERS:
for rpcuser in self._ctx.Config.RPC_USERS:
if rpcuser.get('USERNAME', None) == username and rpcuser.get('PASSWORD', None) == password:
return True
return JSONResponse(ok_auth)
self.send_auth_error(request_data)
return False
return self.send_auth_error(body)
except Exception as e:
self.send_auth_error(request_data)
logs.error(e)
return False
return self.send_auth_error(body)
def send_auth_error(self, request_data: dict) -> None:
def send_auth_error(self, request_data: dict) -> JSONResponse:
response_data = {
'jsonrpc': '2.0',
'id': request_data.get('id', 123),
'error': create_error_response(JSONRPCErrorCode.AUTHENTICATION_ERROR)
}
return JSONResponse(response_data)
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)

View File

@@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from core.loader import Loader
@@ -8,5 +8,5 @@ class RPCChannel:
self._Loader = loader
self._Channel = loader.Channel
def channel_list(self) -> list[dict]:
def channel_list(self, **kwargs) -> list[dict]:
return [chan.to_dict() for chan in self._Channel.UID_CHANNEL_DB]

View File

@@ -8,14 +8,22 @@ class RPCCommand:
self._Loader = loader
self._Command = loader.Commands
def command_list(self) -> list[dict]:
def command_list(self, **kwargs) -> list[dict]:
return [command.to_dict() for command in self._Command.DB_COMMANDS]
def command_get_by_module(self, module_name: str) -> list[dict]:
def command_get_by_module(self, **kwargs) -> list[dict]:
module_name = kwargs.get('module_name', None)
if module_name is None:
return []
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:
def command_get_by_name(self, **kwargs) -> dict:
command_name: str = kwargs.get('command_name', '')
if not command_name:
return dict()
for command in self._Command.DB_COMMANDS:
if command.command_name.lower() == command_name.lower():
return command.to_dict()
return {}
return dict()

View File

@@ -6,11 +6,10 @@ if TYPE_CHECKING:
class RPCUser:
def __init__(self, loader: 'Loader'):
self._Loader = loader
self._User = loader.User
self._ctx = loader
def user_list(self) -> list[dict]:
users = self._User.UID_DB.copy()
def user_list(self, **kwargs) -> list[dict]:
users = self._ctx.User.UID_DB.copy()
copy_users: list['MUser'] = []
for user in users:
@@ -20,8 +19,9 @@ class RPCUser:
return [user.to_dict() for user in copy_users]
def user_get(self, uidornickname: str) -> Optional[dict]:
user = self._User.get_user(uidornickname)
def user_get(self, **kwargs) -> Optional[dict]:
uidornickname = kwargs.get('uid_or_nickname', None)
user = self._ctx.User.get_user(uidornickname)
if user:
user_copy = user.copy()
user_copy.connexion_datetime = user_copy.connexion_datetime.strftime('%d-%m-%Y')

View File

@@ -4,30 +4,29 @@ from .inspircd import Inspircd
from ..interfaces.iprotocol import IProtocol
if TYPE_CHECKING:
from core.irc import Irc
from core.loader import Loader
class ProtocolFactorty:
def __init__(self, uplink: 'Irc'):
def __init__(self, context: 'Loader'):
"""ProtocolFactory init.
Args:
uplink (Irc): The Irc object
context (Loader): The Context object
"""
self.__Config = uplink.Config
self.__uplink = uplink
self.__ctx = context
def get(self) -> Optional[IProtocol]:
protocol = self.__Config.SERVEUR_PROTOCOL
protocol = self.__ctx.Config.SERVEUR_PROTOCOL
match protocol:
case 'unreal6':
self.__uplink.Logs.debug(f"[PROTOCOL] {protocol} has been loaded")
return Unrealircd6(self.__uplink)
self.__ctx.Logs.debug(f"[PROTOCOL] {protocol} has been loaded")
return Unrealircd6(self.__ctx)
case 'inspircd':
self.__uplink.Logs.debug(f"[PROTOCOL] {protocol} has been loaded")
return Inspircd(self.__uplink)
self.__ctx.Logs.debug(f"[PROTOCOL] {protocol} has been loaded")
return Inspircd(self.__ctx)
case _:
self.__uplink.Logs.critical(f"[PROTOCOL ERROR] This protocol name ({protocol} is not valid!)")
self.__ctx.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

View File

@@ -3,6 +3,7 @@ from json import dumps
from dataclasses import dataclass, field, asdict, fields, replace
from typing import Literal, Any, Optional
from os import sep
from core.classes.interfaces.imodule import IModule
@dataclass
class MainModel:
@@ -354,7 +355,7 @@ class MCommand(MainModel):
class MModule(MainModel):
module_name: str = None
class_name: str = None
class_instance: Optional[Any] = None
class_instance: Optional[IModule] = None
@dataclass
class DefenderModuleHeader(MainModel):

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,15 @@ import core.classes.protocols.factory as factory
class Loader:
_instance = None
def __new__(cls, *agrs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
# Load Main Modules
@@ -70,10 +79,11 @@ class Loader:
self.Irc: irc.Irc = irc.Irc(self)
self.PFactory: factory.ProtocolFactorty = factory.ProtocolFactorty(self.Irc)
self.PFactory: factory.ProtocolFactorty = factory.ProtocolFactorty(self)
self.RpcServer: rpc_mod.JSONRPCServer = rpc_mod.JSONRPCServer(self)
self.Base.init()
self.RpcServer: rpc_mod.JSonRpcServer = rpc_mod.JSonRpcServer(self)
self.Logs.debug(self.Utils.tr("Loader %s success", __name__))
async def start(self):
await self.Base.init()

View File

@@ -15,7 +15,7 @@ class ServiceLogging:
self.SERVER_PREFIX = None
self.LOGGING_CONSOLE = True
self.LOG_FILTERS: list[str] = [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

View File

@@ -1,9 +1,9 @@
'''
This is the main operational file to handle modules
'''
from pathlib import Path
import sys
import importlib
from pathlib import Path
from types import ModuleType
from typing import TYPE_CHECKING, Optional
from core.definition import DefenderModuleHeader, MModule
@@ -12,6 +12,7 @@ from core.utils import tr
if TYPE_CHECKING:
from core.loader import Loader
from core.irc import Irc
from core.classes.interfaces.imodule import IModule
class Module:
@@ -19,11 +20,7 @@ class Module:
DB_MODULE_HEADERS: list[DefenderModuleHeader] = []
def __init__(self, loader: 'Loader') -> None:
self.__Loader = loader
self.__Base = loader.Base
self.__Logs = loader.Logs
self.__Utils = loader.Utils
self.__Config = loader.Config
self._ctx = loader
def get_all_available_modules(self) -> list[str]:
"""Get list of all main modules
@@ -34,7 +31,7 @@ class Module:
"""
base_path = Path('mods')
modules_available = [file.name.replace('.py', '') for file in base_path.rglob('mod_*.py')]
self.__Logs.debug(f"Modules available: {modules_available}")
self._ctx.Logs.debug(f"Modules available: {modules_available}")
return modules_available
def get_module_information(self, module_name: str) -> tuple[Optional[str], Optional[str], Optional[str]]:
@@ -45,14 +42,14 @@ class Module:
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}")
self._ctx.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}")
self._ctx.Logs.debug(f"Module Header found: {mod_h}")
return mod_h
return None
@@ -68,7 +65,7 @@ class Module:
"""
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._ctx.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
@@ -77,73 +74,74 @@ class Module:
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._ctx.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})")
self._ctx.Logs.debug(f"[MOD_HEADER ERROR] Impossible to remove the module header ({module_name})")
return False
async def load_one_module(self, uplink: 'Irc', module_name: str, nickname: str, is_default: bool = False) -> bool:
async def load_one_module(self, module_name: str, nickname: str, is_default: bool = False) -> bool:
module_folder, module_name, class_name = self.get_module_information(module_name)
if module_folder is None or module_name is None or class_name is None:
self.__Logs.error(f"There is an error with the module name! {module_folder}, {module_name}, {class_name}")
self._ctx.Logs.error(f"There is an error with the module name! {module_folder}, {module_name}, {class_name}")
return False
if self.is_module_exist_in_sys_module(module_name):
self.__Logs.debug(f"Module [{module_folder}.{module_name}] already loaded!")
self._ctx.Logs.debug(f"Module [{module_folder}.{module_name}] already loaded!")
if self.model_is_module_exist(module_name):
# Si le module existe dans la variable globale retourne False
self.__Logs.debug(f"Module [{module_folder}.{module_name}] exist in the local variable!")
await uplink.Protocol.send_priv_msg(
nick_from=self.__Config.SERVICE_NICKNAME,
msg=f"Le module {module_name} est déja chargé ! si vous souhaiter le recharge tapez {self.__Config.SERVICE_PREFIX}reload {module_name}",
channel=self.__Config.SERVICE_CHANLOG
self._ctx.Logs.debug(f"Module [{module_folder}.{module_name}] exist in the local variable!")
await self._ctx.Irc.Protocol.send_priv_msg(
nick_from=self._ctx.Config.SERVICE_NICKNAME,
msg=f"Le module {module_name} est déja chargé ! si vous souhaiter le recharge tapez {self._ctx.Config.SERVICE_PREFIX}reload {module_name}",
channel=self._ctx.Config.SERVICE_CHANLOG
)
return False
return self.reload_one_module(uplink, module_name, nickname)
return self.reload_one_module(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.Loader) # Créer une nouvelle instance de la classe
create_instance_of_the_class: 'IModule' = my_class(self._ctx) # Créer une nouvelle instance de la classe
await create_instance_of_the_class.load() if self._ctx.Utils.is_coroutinefunction(create_instance_of_the_class.load) else create_instance_of_the_class.load()
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
await uplink.Protocol.send_priv_msg(
nick_from=self.__Config.SERVICE_NICKNAME,
red = self._ctx.Config.COLORS.red
nogc = self._ctx.Config.COLORS.red
await self._ctx.Irc.Protocol.send_priv_msg(
nick_from=self._ctx.Config.SERVICE_NICKNAME,
msg=tr("[%sMODULE ERROR%s] Module %s is facing issues! %s", red, nogc, module_name, attr),
channel=self.__Config.SERVICE_CHANLOG
channel=self._ctx.Config.SERVICE_CHANLOG
)
self.__Logs.error(msg=attr, exc_info=True)
self._ctx.Logs.error(msg=attr, exc_info=True)
return False
if not hasattr(create_instance_of_the_class, 'cmd'):
await uplink.Protocol.send_priv_msg(
nick_from=self.__Config.SERVICE_NICKNAME,
await self._ctx.Irc.Protocol.send_priv_msg(
nick_from=self._ctx.Config.SERVICE_NICKNAME,
msg=tr("cmd method is not available in the module (%s)", module_name),
channel=self.__Config.SERVICE_CHANLOG
channel=self._ctx.Config.SERVICE_CHANLOG
)
self.__Logs.critical(f"The Module {module_name} has not been loaded because cmd method is not available")
self.db_delete_module(module_name)
self._ctx.Logs.critical(f"The Module {module_name} has not been loaded because cmd method is not available")
await self.db_delete_module(module_name)
return False
# Charger la nouvelle class dans la variable globale
if self.model_insert_module(MModule(module_name, class_name, create_instance_of_the_class)):
# Enregistrer le module dans la base de données
self.db_register_module(module_name, nickname, is_default)
await uplink.Protocol.send_priv_msg(
nick_from=self.__Config.SERVICE_NICKNAME,
await self.db_register_module(module_name, nickname, is_default)
await self._ctx.Irc.Protocol.send_priv_msg(
nick_from=self._ctx.Config.SERVICE_NICKNAME,
msg=tr("Module %s loaded!", module_name),
channel=self.__Config.SERVICE_CHANLOG
channel=self._ctx.Config.SERVICE_CHANLOG
)
self.__Logs.debug(f"Module {class_name} has been loaded")
self._ctx.Logs.debug(f"Module {class_name} has been loaded")
return True
return False
@@ -151,7 +149,7 @@ class Module:
def load_all_modules(self) -> bool:
...
async def reload_one_module(self, uplink: 'Irc', module_name: str, nickname: str) -> bool:
async def reload_one_module(self, module_name: str, nickname: str) -> bool:
"""Reloading one module and insert it into the model as well as the database
Args:
@@ -163,21 +161,21 @@ class Module:
bool: True if the module has been reloaded
"""
module_folder, module_name, class_name = self.get_module_information(module_name)
red = self.__Config.COLORS.red
nogc = self.__Config.COLORS.nogc
red = self._ctx.Config.COLORS.red
nogc = self._ctx.Config.COLORS.nogc
try:
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()
await module_model.class_instance.unload() if self._ctx.Utils.is_coroutinefunction(module_model.class_instance.unload) else module_model.class_instance.unload()
else:
await uplink.Protocol.send_priv_msg(
nick_from=self.__Config.SERVICE_NICKNAME,
msg=f"[ {red}RELOAD MODULE ERROR{nogc} ] Module [{module_folder}.{module_name}] hasn't been reloaded! You must use {self.__Config.SERVICE_PREFIX}load {module_name}",
channel=self.__Config.SERVICE_CHANLOG
await self._ctx.Irc.Protocol.send_priv_msg(
nick_from=self._ctx.Config.SERVICE_NICKNAME,
msg=f"[ {red}RELOAD MODULE ERROR{nogc} ] Module [{module_folder}.{module_name}] hasn't been reloaded! You must use {self._ctx.Config.SERVICE_PREFIX}load {module_name}",
channel=self._ctx.Config.SERVICE_CHANLOG
)
self.__Logs.debug(f"Module [{module_folder}.{module_name}] not found! Please use {self.__Config.SERVICE_PREFIX}load {module_name}")
self._ctx.Logs.debug(f"Module [{module_folder}.{module_name}] not found! Please use {self._ctx.Config.SERVICE_PREFIX}load {module_name}")
return False
# reload module dependencies
@@ -186,37 +184,38 @@ class Module:
the_module = sys.modules[f'mods.{module_folder}.{module_name}']
importlib.reload(the_module)
my_class = getattr(the_module, class_name, None)
new_instance = my_class(uplink.Loader)
new_instance: 'IModule' = my_class(self._ctx)
await new_instance.load() if self._ctx.Utils.is_coroutinefunction(new_instance.load) else new_instance.load()
self.create_module_header(new_instance.MOD_HEADER)
module_model.class_instance = new_instance
# Créer le module dans la base de données
self.db_register_module(module_name, nickname)
await uplink.Protocol.send_priv_msg(
nick_from=self.__Config.SERVICE_NICKNAME,
await self.db_register_module(module_name, nickname)
await self._ctx.Irc.Protocol.send_priv_msg(
nick_from=self._ctx.Config.SERVICE_NICKNAME,
msg=f"Module [{module_folder}.{module_name}] has been reloaded!",
channel=self.__Config.SERVICE_CHANLOG
channel=self._ctx.Config.SERVICE_CHANLOG
)
self.__Logs.debug(f"Module [{module_folder}.{module_name}] reloaded!")
self._ctx.Logs.debug(f"Module [{module_folder}.{module_name}] reloaded!")
return True
else:
# Module is not loaded! Nothing to reload
self.__Logs.debug(f"[RELOAD MODULE ERROR] [{module_folder}.{module_name}] is not loaded! You must use {self.__Config.SERVICE_PREFIX}load {module_name}")
await uplink.Protocol.send_priv_msg(
nick_from=self.__Config.SERVICE_NICKNAME,
msg=f"[ {red}RELOAD MODULE ERROR{nogc} ] Module [{module_folder}.{module_name}] is not loaded! You must use {self.__Config.SERVICE_PREFIX}load {module_name}",
channel=self.__Config.SERVICE_CHANLOG
self._ctx.Logs.debug(f"[RELOAD MODULE ERROR] [{module_folder}.{module_name}] is not loaded! You must use {self._ctx.Config.SERVICE_PREFIX}load {module_name}")
await self._ctx.Irc.Protocol.send_priv_msg(
nick_from=self._ctx.Config.SERVICE_NICKNAME,
msg=f"[ {red}RELOAD MODULE ERROR{nogc} ] Module [{module_folder}.{module_name}] is not loaded! You must use {self._ctx.Config.SERVICE_PREFIX}load {module_name}",
channel=self._ctx.Config.SERVICE_CHANLOG
)
return False
except (TypeError, AttributeError, KeyError, Exception) as err:
self.__Logs.error(f"[RELOAD MODULE ERROR]: {err}", exc_info=True)
await uplink.Protocol.send_priv_msg(
nick_from=self.__Config.SERVICE_NICKNAME,
self._ctx.Logs.error(f"[RELOAD MODULE ERROR]: {err}", exc_info=True)
await self._ctx.Irc.Protocol.send_priv_msg(
nick_from=self._ctx.Config.SERVICE_NICKNAME,
msg=f"[RELOAD MODULE ERROR]: {err}",
channel=self.__Config.SERVICE_CHANLOG
channel=self._ctx.Config.SERVICE_CHANLOG
)
self.db_delete_module(module_name)
await self.db_delete_module(module_name)
def reload_all_modules(self) -> bool:
...
@@ -242,12 +241,12 @@ class Module:
try:
if 'mod_' not in name and 'schemas' not in name:
importlib.reload(module)
self.__Logs.debug(f'[LOAD_MODULE] Module {module} success')
self._ctx.Logs.debug(f'[LOAD_MODULE] Module {module} success')
except Exception as err:
self.__Logs.error(f'[LOAD_MODULE] Module {module} failed [!] - {err}')
self._ctx.Logs.error(f'[LOAD_MODULE] Module {module} failed [!] - {err}')
def unload_one_module(self, uplink: 'Irc', module_name: str, keep_in_db: bool = True) -> bool:
async def unload_one_module(self, module_name: str, keep_in_db: bool = True) -> bool:
"""Unload a module
Args:
@@ -260,23 +259,23 @@ class Module:
"""
try:
# Le nom du module. exemple: mod_defender
red = self.__Config.COLORS.red
nogc = self.__Config.COLORS.nogc
red = self._ctx.Config.COLORS.red
nogc = self._ctx.Config.COLORS.nogc
module_folder, module_name, class_name = self.get_module_information(module_name)
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,
self._ctx.Logs.debug(f"[ UNLOAD MODULE ERROR ] This module {module_name} is not loaded!")
await self.db_delete_module(module_name)
await self._ctx.Irc.Protocol.send_priv_msg(
nick_from=self._ctx.Config.SERVICE_NICKNAME,
msg=f"[ {red}UNLOAD MODULE ERROR{nogc} ] This module {module_name} is not loaded!",
channel=self.__Config.SERVICE_CHANLOG
channel=self._ctx.Config.SERVICE_CHANLOG
)
return False
if module:
self.delete_module_header(module.class_instance.MOD_HEADER['name'])
module.class_instance.unload()
await module.class_instance.unload() if self._ctx.Utils.is_coroutinefunction(module.class_instance.unload) else module.class_instance.unload()
self.DB_MODULES.remove(module)
# Delete from the sys.modules.
@@ -284,25 +283,25 @@ class Module:
del sys.modules[f"mods.{module_folder}.{module_name}"]
if sys.modules.get(f'mods.{module_folder}.{module_name}'):
self.__Logs.debug(f"Module mods.{module_folder}.{module_name} still in the sys.modules")
self._ctx.Logs.debug(f"Module mods.{module_folder}.{module_name} still in the sys.modules")
# Supprimer le module de la base de données
if not keep_in_db:
self.db_delete_module(module_name)
await self.db_delete_module(module_name)
uplink.Protocol.send_priv_msg(
nick_from=self.__Config.SERVICE_NICKNAME,
await self._ctx.Irc.Protocol.send_priv_msg(
nick_from=self._ctx.Config.SERVICE_NICKNAME,
msg=f"[ UNLOAD MODULE INFO ] Module {module_name} has been unloaded!",
channel=self.__Config.SERVICE_CHANLOG
channel=self._ctx.Config.SERVICE_CHANLOG
)
self.__Logs.debug(f"[ UNLOAD MODULE ] {module_name} has been unloaded!")
self._ctx.Logs.debug(f"[ UNLOAD MODULE ] {module_name} has been unloaded!")
return True
self.__Logs.debug(f"[UNLOAD MODULE]: Module {module_name} not found in DB_MODULES variable!")
self._ctx.Logs.debug(f"[UNLOAD MODULE]: Module {module_name} not found in DB_MODULES variable!")
return False
except Exception as err:
self.__Logs.error(f"General Error: {err}", exc_info=True)
self._ctx.Logs.error(f"General Error: {err}", exc_info=True)
return False
def unload_all_modules(self) -> bool:
@@ -319,9 +318,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")
self._ctx.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")
self._ctx.Logs.debug(f"[SYS MODULE] (mods.{module_folder}.{module_name}) not found in sys.modules")
return False
'''
@@ -338,10 +337,10 @@ class Module:
"""
for module in self.DB_MODULES:
if module.module_name.lower() == module_name.lower():
self.__Logs.debug(f"[MODEL MODULE GET] The module {module_name} has been found in the model DB_MODULES")
self._ctx.Logs.debug(f"[MODEL MODULE GET] The module {module_name} has been found in the model DB_MODULES")
return module
self.__Logs.debug(f"[MODEL MODULE GET] The module {module_name} not found in the model DB_MODULES")
self._ctx.Logs.debug(f"[MODEL MODULE GET] The module {module_name} not found in the model DB_MODULES")
return None
def model_get_loaded_modules(self) -> list[MModule]:
@@ -351,7 +350,7 @@ class Module:
Returns:
list[MModule]: A list of module model object
"""
# self.__Logs.debug(f"[MODEL MODULE LOADED MODULES] {len(self.DB_MODULES)} modules found!")
# self._ctx.Logs.debug(f"[MODEL MODULE LOADED MODULES] {len(self.DB_MODULES)} modules found!")
return self.DB_MODULES
def model_insert_module(self, module_model: MModule) -> bool:
@@ -366,17 +365,17 @@ class Module:
module = self.model_get_module(module_model.module_name)
if module is None:
self.DB_MODULES.append(module_model)
self.__Logs.debug(f"[MODEL MODULE INSERT] The module {module_model.module_name} has been inserted in the local variable model DB_MODULES")
self._ctx.Logs.debug(f"[MODEL MODULE INSERT] The module {module_model.module_name} has been inserted in the local variable model DB_MODULES")
return True
self.__Logs.debug(f"[MODEL MODULE INSERT] The module {module_model.module_name} already exist in the local variable model DB_MODULES")
self._ctx.Logs.debug(f"[MODEL MODULE INSERT] The module {module_model.module_name} already exist in the local variable model DB_MODULES")
return False
def model_clear(self) -> None:
"""Clear DB_MODULES list!
"""
self.DB_MODULES.clear()
self.__Logs.debug("[MODEL MODULE CLEAR] The local variable model DB_MODULES has been cleared")
self._ctx.Logs.debug("[MODEL MODULE CLEAR] The local variable model DB_MODULES has been cleared")
return None
def model_is_module_exist(self, module_name: str) -> bool:
@@ -389,30 +388,30 @@ class Module:
bool: True if the module_name exist
"""
if self.model_get_module(module_name):
self.__Logs.debug(f"[MODEL MODULE EXIST] The module {module_name} exist in the local model DB_MODULES!")
self._ctx.Logs.debug(f"[MODEL MODULE EXIST] The module {module_name} exist in the local model DB_MODULES!")
return True
self.__Logs.debug(f"[MODEL MODULE EXIST] The module {module_name} is not available in the local model DB_MODULES!")
self._ctx.Logs.debug(f"[MODEL MODULE EXIST] The module {module_name} is not available in the local model DB_MODULES!")
return False
'''
OPERATION DEDICATED TO DATABASE MANAGEMENT
'''
async def db_load_all_existing_modules(self, uplink: 'Irc') -> bool:
async def db_load_all_existing_modules(self) -> bool:
"""Charge les modules qui existe déja dans la base de données
Returns:
None: Aucun retour requis, elle charge puis c'est tout
"""
self.__Logs.debug("[DB LOAD MODULE] Loading modules from the database!")
result = self.__Base.db_execute_query(f"SELECT module_name FROM {self.__Config.TABLE_MODULE}")
self._ctx.Logs.debug("[DB LOAD MODULE] Loading modules from the database!")
result = await self._ctx.Base.db_execute_query(f"SELECT module_name FROM {self._ctx.Config.TABLE_MODULE}")
for r in result.fetchall():
await self.load_one_module(uplink, r[0], 'sys', True)
await self.load_one_module(r[0], 'sys', True)
return True
def db_is_module_exist(self, module_name: str) -> bool:
async def db_is_module_exist(self, module_name: str) -> bool:
"""Check if the module exist in the database
Args:
@@ -421,18 +420,18 @@ class Module:
Returns:
bool: True if the module exist in the database
"""
query = f"SELECT id FROM {self.__Config.TABLE_MODULE} WHERE module_name = :module_name"
query = f"SELECT id FROM {self._ctx.Config.TABLE_MODULE} WHERE module_name = :module_name"
mes_donnes = {'module_name': module_name.lower()}
results = self.__Base.db_execute_query(query, mes_donnes)
results = await self._ctx.Base.db_execute_query(query, mes_donnes)
if results.fetchall():
self.__Logs.debug(f"[DB MODULE EXIST] The module {module_name} exist in the database!")
self._ctx.Logs.debug(f"[DB MODULE EXIST] The module {module_name} exist in the database!")
return True
else:
self.__Logs.debug(f"[DB MODULE EXIST] The module {module_name} is not available in the database!")
self._ctx.Logs.debug(f"[DB MODULE EXIST] The module {module_name} is not available in the database!")
return False
def db_register_module(self, module_name: str, nickname: str, is_default: bool = False) -> bool:
async def db_register_module(self, module_name: str, nickname: str, is_default: bool = False) -> bool:
"""Insert a new module in the database
Args:
@@ -440,49 +439,49 @@ class Module:
nickname (str): The user who loaded the module
isdefault (int): Is this a default module. Default 0
"""
if not self.db_is_module_exist(module_name):
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.__Utils.get_sdatetime(), 'user': nickname, 'module_name': module_name.lower(), 'isdefault': is_default}
insert = self.__Base.db_execute_query(insert_cmd_query, mes_donnees)
if not await self.db_is_module_exist(module_name):
insert_cmd_query = f"INSERT INTO {self._ctx.Config.TABLE_MODULE} (datetime, user, module_name, isdefault) VALUES (:datetime, :user, :module_name, :isdefault)"
mes_donnees = {'datetime': self._ctx.Utils.get_sdatetime(), 'user': nickname, 'module_name': module_name.lower(), 'isdefault': is_default}
insert = await self._ctx.Base.db_execute_query(insert_cmd_query, mes_donnees)
if insert.rowcount > 0:
self.__Logs.debug(f"[DB REGISTER MODULE] Module {module_name} has been inserted to the database!")
self._ctx.Logs.debug(f"[DB REGISTER MODULE] Module {module_name} has been inserted to the database!")
return True
else:
self.__Logs.debug(f"[DB REGISTER MODULE] Module {module_name} not inserted to the database!")
self._ctx.Logs.debug(f"[DB REGISTER MODULE] Module {module_name} not inserted to the database!")
return False
self.__Logs.debug(f"[DB REGISTER MODULE] Module {module_name} already exist in the database! Nothing to insert!")
self._ctx.Logs.debug(f"[DB REGISTER MODULE] Module {module_name} already exist in the database! Nothing to insert!")
return False
def db_update_module(self, module_name: str, nickname: str) -> None:
async def db_update_module(self, module_name: str, nickname: str) -> None:
"""Update the datetime and the user that updated the module
Args:
module_name (str): The module name to update
nickname (str): The nickname who updated the module
"""
update_cmd_query = f"UPDATE {self.__Config.TABLE_MODULE} SET datetime = :datetime, LOWER(user) = :user WHERE LOWER(module_name) = :module_name"
mes_donnees = {'datetime': self.__Utils.get_sdatetime(), 'user': nickname.lower(), 'module_name': module_name.lower()}
result = self.__Base.db_execute_query(update_cmd_query, mes_donnees)
update_cmd_query = f"UPDATE {self._ctx.Config.TABLE_MODULE} SET datetime = :datetime, LOWER(user) = :user WHERE LOWER(module_name) = :module_name"
mes_donnees = {'datetime': self._ctx.Utils.get_sdatetime(), 'user': nickname.lower(), 'module_name': module_name.lower()}
result = await self._ctx.Base.db_execute_query(update_cmd_query, mes_donnees)
if result.rowcount > 0:
self.__Logs.debug(f"[DB UPDATE MODULE] Module {module_name} has been updated!")
self._ctx.Logs.debug(f"[DB UPDATE MODULE] Module {module_name} has been updated!")
return True
else:
self.__Logs.debug(f"[DB UPDATE MODULE] Module {module_name} not found! Nothing to update!")
self._ctx.Logs.debug(f"[DB UPDATE MODULE] Module {module_name} not found! Nothing to update!")
return False
def db_delete_module(self, module_name:str) -> None:
async def db_delete_module(self, module_name:str) -> None:
"""Delete a module from the database
Args:
module_name (str): The module name you want to delete
"""
insert_cmd_query = f"DELETE FROM {self.__Config.TABLE_MODULE} WHERE LOWER(module_name) = :module_name"
insert_cmd_query = f"DELETE FROM {self._ctx.Config.TABLE_MODULE} WHERE LOWER(module_name) = :module_name"
mes_donnees = {'module_name': module_name.lower()}
delete = self.__Base.db_execute_query(insert_cmd_query, mes_donnees)
delete = await self._ctx.Base.db_execute_query(insert_cmd_query, mes_donnees)
if delete.rowcount > 0:
self.__Logs.debug(f"[DB MODULE DELETE] The module {module_name} has been deleted from the dabatase!")
self._ctx.Logs.debug(f"[DB MODULE DELETE] The module {module_name} has been deleted from the dabatase!")
return True
self.__Logs.debug(f"[DB MODULE DELETE] The module {module_name} is not available in the database! Nothing to delete!")
self._ctx.Logs.debug(f"[DB MODULE DELETE] The module {module_name} is not available in the database! Nothing to delete!")
return False

View File

@@ -13,6 +13,7 @@ from time import time
from random import choice
from hashlib import md5, sha3_512
from core.classes.modules.settings import global_settings
from asyncio import iscoroutinefunction
if TYPE_CHECKING:
from core.irc import Irc
@@ -243,3 +244,14 @@ def hide_sensitive_data(srvmsg: list[str]) -> list[str]:
except ValueError:
return srvmsg
def is_coroutinefunction(func: Any) -> bool:
"""Check if the function is a coroutine or not
Args:
func (Any): an callable object
Returns:
bool: True if the function is a coroutine
"""
return iscoroutinefunction(func)