diff --git a/core/classes/protocols/unreal6.py b/core/classes/protocols/unreal6.py index e1561ff..28adbe0 100644 --- a/core/classes/protocols/unreal6.py +++ b/core/classes/protocols/unreal6.py @@ -1029,9 +1029,11 @@ class Unrealircd6(IProtocol): self._Irc.join_saved_channels() self._Irc.ModuleUtils.db_load_all_existing_modules(self._Irc) + self.send2socket(f":{self._Config.SERVEUR_ID} SMOD :L:Defender:1.0.0 :L:Command:1.0.0") + return None except IndexError as ie: - self._Logs.error(f"{__name__} - Key Error: {ie}") + self._Logs.error(f"{__name__} - Index Error: {ie}") except KeyError as ke: self._Logs.error(f"{__name__} - Key Error: {ke}") except Exception as err: diff --git a/core/definition.py b/core/definition.py index d8a6e05..d7016e3 100644 --- a/core/definition.py +++ b/core/definition.py @@ -349,6 +349,14 @@ class MModule(MainModel): class_name: str = None class_instance: Optional[Any] = None +@dataclass +class DefenderModuleHeader(MainModel): + name: str = '' + version: str = '' + description: str = '' + author: str = '' + core_version: str = '' + @dataclass class MSModule: """Server Modules model""" diff --git a/core/irc.py b/core/irc.py index a4aab8c..43aa828 100644 --- a/core/irc.py +++ b/core/irc.py @@ -125,7 +125,7 @@ class Irc: self.build_command(3, 'core', 'cert', 'Append your new fingerprint to your account!') self.build_command(4, 'core', 'rehash', 'Reload the configuration file without restarting') self.build_command(4, 'core', 'raw', 'Send a raw command directly to the IRC server') - self.build_command(4, 'core', 'print_users', 'Print users in a file.') + self.build_command(4, 'core', 'print_vars', 'Print users in a file.') # Define the IrcSocket object self.IrcSocket: Optional[Union[socket.socket, SSLSocket]] = None @@ -1253,7 +1253,7 @@ class Irc: self.Protocol.send_raw(raw_command) return None - case 'print_users': + case 'print_vars': with open('users.txt', 'w') as fw: i = 1 for u in self.User.UID_DB: @@ -1261,6 +1261,14 @@ class Irc: self.Logs.debug(f" {i} - chars written {w}") i += 1 self.Protocol.send_priv_msg(dnickname, "Data written in users.txt file", dchanlog) + + with open('modules.txt', 'w') as fw: + i = 1 + for u in self.ModuleUtils.DB_MODULE_HEADERS: + w = fw.write(u.to_dict().__str__() + "\n") + self.Logs.debug(f" {i} - chars written {w}") + i += 1 + self.Protocol.send_priv_msg(dnickname, "Data written in modules.txt file", dchanlog) return None diff --git a/core/module.py b/core/module.py index 94f4c72..b1e1596 100644 --- a/core/module.py +++ b/core/module.py @@ -1,12 +1,13 @@ ''' This is the main operational file to handle modules ''' +from optparse import Option from pathlib import Path import sys import importlib from types import ModuleType from typing import TYPE_CHECKING, Optional -from core.definition import MModule +from core.definition import DefenderModuleHeader, MModule if TYPE_CHECKING: from core.loader import Loader @@ -15,6 +16,7 @@ if TYPE_CHECKING: class Module: DB_MODULES: list[MModule] = [] + DB_MODULE_HEADERS: list[DefenderModuleHeader] = [] def __init__(self, loader: 'Loader') -> None: self.__Loader = loader @@ -38,11 +40,46 @@ class Module: if not module_name.lower().startswith('mod_'): return None, None, None - module_name = module_name.lower() + module_name = module_name.lower() # --> mod_defender module_folder = module_name.split('_')[1].lower() # --> defender class_name = module_name.split('_')[1].capitalize() # --> Defender 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(): + return mod_h + + return None + + def create_module_header(self, module_header: dict[str, str]) -> bool: + """Create a new module header into DB_MODULE_HEADERS + + Args: + module_header (dict[str, str]): The module header + + Returns: + bool: True if the module header has been created. + """ + mod_header = DefenderModuleHeader(**module_header) + if self.get_module_header(mod_header.name) is None: + self.__Logs.debug(f"[MOD_HEADER] The module header has been created! ({mod_header.name} v{mod_header.version})") + self.DB_MODULE_HEADERS.append(mod_header) + return True + + return False + + def delete_module_header(self, module_name: str) -> bool: + mod_header = self.get_module_header(module_name) + if mod_header is not None: + self.__Logs.debug(f"[MOD_HEADER] The module header has been deleted ({mod_header.name} v{mod_header.version})") + self.DB_MODULE_HEADERS.remove(mod_header) + return True + + self.__Logs.debug(f"[MOD_HEADER ERROR] Impossible to remove the module header ({module_name})") + return False + def load_one_module(self, uplink: 'Irc', module_name: str, nickname: str, is_default: bool = False) -> bool: module_folder, module_name, class_name = self.get_module_information(module_name) @@ -70,6 +107,7 @@ class Module: loaded_module = importlib.import_module(f'mods.{module_folder}.{module_name}') my_class = getattr(loaded_module, class_name, None) # Récuperer le nom de classe create_instance_of_the_class = my_class(uplink) # Créer une nouvelle instance de la classe + self.create_module_header(create_instance_of_the_class.MOD_HEADER) except AttributeError as attr: red = uplink.Config.COLORS.red nogc = uplink.Config.COLORS.nogc @@ -124,6 +162,7 @@ class Module: if self.is_module_exist_in_sys_module(module_name): module_model = self.model_get_module(module_name) if module_model: + self.delete_module_header(module_model.class_instance.MOD_HEADER['name']) module_model.class_instance.unload() else: uplink.Protocol.send_priv_msg( @@ -141,6 +180,7 @@ class Module: importlib.reload(the_module) my_class = getattr(the_module, class_name, None) new_instance = my_class(uplink) + self.create_module_header(new_instance.MOD_HEADER) module_model.class_instance = new_instance # Créer le module dans la base de données @@ -163,7 +203,7 @@ class Module: return False except (TypeError, AttributeError, KeyError, Exception) as err: - self.__Logs.error(f"[RELOAD MODULE ERROR]: {err}") + self.__Logs.error(f"[RELOAD MODULE ERROR]: {err}", exc_info=True) uplink.Protocol.send_priv_msg( nick_from=self.__Config.SERVICE_NICKNAME, msg=f"[RELOAD MODULE ERROR]: {err}", @@ -227,6 +267,7 @@ class Module: return False if module: + self.delete_module_header(module.class_instance.MOD_HEADER['name']) module.class_instance.unload() self.DB_MODULES.remove(module) @@ -253,7 +294,7 @@ class Module: return False except Exception as err: - self.__Logs.error(f"General Error: {err}") + self.__Logs.error(f"General Error: {err}", exc_info=True) return False def unload_all_modules(self) -> bool: diff --git a/mods/clone/mod_clone.py b/mods/clone/mod_clone.py index 4eb698f..999dd3b 100644 --- a/mods/clone/mod_clone.py +++ b/mods/clone/mod_clone.py @@ -15,12 +15,12 @@ class Clone(IModule): class ModConfModel(schemas.ModConfModel): ... - MOD_HEADER: set[str] = { - 'Clone', - '1.0.0', - 'Connect thousands of clones to your IRCD, by group. You can use them as security moderation.', - 'Defender Team', - 'Defender-6' + MOD_HEADER: dict[str, str] = { + 'name':'Clone', + 'version':'1.0.0', + 'description':'Connect thousands of clones to your IRCD, by group. You can use them as security moderation.', + 'author':'Defender Team', + 'core_version':'Defender-6' } def create_tables(self) -> None: diff --git a/mods/command/mod_command.py b/mods/command/mod_command.py index 0f74106..083b199 100644 --- a/mods/command/mod_command.py +++ b/mods/command/mod_command.py @@ -14,12 +14,12 @@ class Command(IModule): """ pass - MOD_HEADER: set[str] = { - 'Command', - '1.0.0', - 'Module contains all IRC commands', - 'Defender Team', - 'Defender-6' + MOD_HEADER: dict[str, str] = { + 'name':'Command', + 'version':'1.0.0', + 'description':'Module contains all IRC commands', + 'author':'Defender Team', + 'core_version':'Defender-6' } def create_tables(self) -> None: diff --git a/mods/defender/mod_defender.py b/mods/defender/mod_defender.py index f42da02..93cf86a 100644 --- a/mods/defender/mod_defender.py +++ b/mods/defender/mod_defender.py @@ -12,12 +12,12 @@ class Defender(IModule): class ModConfModel(schemas.ModConfModel): ... - MOD_HEADER: set[str] = { - 'Defender', - '1.0.0', - 'Defender main module that uses the reputation security.', - 'Defender Team', - 'Defender-6' + MOD_HEADER: dict[str, str] = { + 'name':'Defender', + 'version':'1.0.0', + 'description':'Defender main module that uses the reputation security.', + 'author':'Defender Team', + 'core_version':'Defender-6' } def create_tables(self) -> None: @@ -214,12 +214,10 @@ class Defender(IModule): return None case 'UID': - print(f"{self.module_name} - {cmd}") self.Utils.handle_on_uid(self, cmd) return None case 'SJOIN': - self.Utils.handle_on_sjoin(self, cmd) return None diff --git a/mods/jsonrpc/mod_jsonrpc.py b/mods/jsonrpc/mod_jsonrpc.py index 6086d94..bc8fd33 100644 --- a/mods/jsonrpc/mod_jsonrpc.py +++ b/mods/jsonrpc/mod_jsonrpc.py @@ -15,12 +15,12 @@ class Jsonrpc(IModule): """ jsonrpc: int = 0 - MOD_HEADER: set[str] = { - 'JsonRPC', - '1.0.0', - 'Module using the unrealircd-rpc-py library', - 'Defender Team', - 'Defender-6' + MOD_HEADER: dict[str, str] = { + 'name':'JsonRPC', + 'version':'1.0.0', + 'description':'Module using the unrealircd-rpc-py library', + 'author':'Defender Team', + 'core_version':'Defender-6' } def callback_sent_to_irc(self, response: LiveRPCResult) -> None: diff --git a/mods/test/mod_test.py b/mods/test/mod_test.py index 290cf1b..d3c7aec 100644 --- a/mods/test/mod_test.py +++ b/mods/test/mod_test.py @@ -12,12 +12,12 @@ class Test(IModule): param_exemple1: str param_exemple2: int - MOD_HEADER: set[str] = { - 'Test', - '1.0.0', - 'The test module', - 'Defender Team', - 'Defender-6' + MOD_HEADER: dict[str, str] = { + 'name':'Test', + 'version':'1.0.0', + 'description':'The test module', + 'author':'Defender Team', + 'core_version':'Defender-6' } def create_tables(self) -> None: diff --git a/mods/votekick/mod_votekick.py b/mods/votekick/mod_votekick.py index de65fe9..b6307e6 100644 --- a/mods/votekick/mod_votekick.py +++ b/mods/votekick/mod_votekick.py @@ -23,12 +23,12 @@ class Votekick(IModule): class ModConfModel(schemas.VoteChannelModel): ... - MOD_HEADER: set[str] = { - 'votekick', - '1.0.2', - 'Channel Democraty', - 'Defender Team', - 'Defender-6' + MOD_HEADER: dict[str, str] = { + 'name':'votekick', + 'version':'1.0.2', + 'description':'Channel Democraty', + 'author':'Defender Team', + 'core_version':'Defender-6' } def create_tables(self) -> None: @@ -79,7 +79,6 @@ class Votekick(IModule): if metadata is not None: self.VoteKickManager.VOTE_CHANNEL_DB = metadata - # self.VOTE_CHANNEL_DB = metadata # Créer les nouvelles commandes du module self.Irc.build_command(1, self.module_name, 'vote', 'The kick vote module') @@ -87,7 +86,8 @@ class Votekick(IModule): def unload(self) -> None: try: # Cache the local DB with current votes. - self.Settings.set_cache('VOTEKICK', self.VoteKickManager.VOTE_CHANNEL_DB) + if self.VoteKickManager.VOTE_CHANNEL_DB: + self.Settings.set_cache('VOTEKICK', self.VoteKickManager.VOTE_CHANNEL_DB) for chan in self.VoteKickManager.VOTE_CHANNEL_DB: self.Protocol.send_part_chan(uidornickname=self.Config.SERVICE_ID, channel=chan.channel_name)