Introduce MOD_HEADER in all modules. impact modules.py, definition.py, unreal6 protocol.

This commit is contained in:
adator
2025-11-10 23:09:13 +01:00
parent 371c8fb5f1
commit 511e0c0715
10 changed files with 104 additions and 47 deletions

View File

@@ -1029,9 +1029,11 @@ class Unrealircd6(IProtocol):
self._Irc.join_saved_channels() self._Irc.join_saved_channels()
self._Irc.ModuleUtils.db_load_all_existing_modules(self._Irc) 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 return None
except IndexError as ie: except IndexError as ie:
self._Logs.error(f"{__name__} - Key Error: {ie}") self._Logs.error(f"{__name__} - Index Error: {ie}")
except KeyError as ke: except KeyError as ke:
self._Logs.error(f"{__name__} - Key Error: {ke}") self._Logs.error(f"{__name__} - Key Error: {ke}")
except Exception as err: except Exception as err:

View File

@@ -349,6 +349,14 @@ class MModule(MainModel):
class_name: str = None class_name: str = None
class_instance: Optional[Any] = None class_instance: Optional[Any] = None
@dataclass
class DefenderModuleHeader(MainModel):
name: str = ''
version: str = ''
description: str = ''
author: str = ''
core_version: str = ''
@dataclass @dataclass
class MSModule: class MSModule:
"""Server Modules model""" """Server Modules model"""

View File

@@ -125,7 +125,7 @@ class Irc:
self.build_command(3, 'core', 'cert', 'Append your new fingerprint to your account!') 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', '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', '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 # Define the IrcSocket object
self.IrcSocket: Optional[Union[socket.socket, SSLSocket]] = None self.IrcSocket: Optional[Union[socket.socket, SSLSocket]] = None
@@ -1253,7 +1253,7 @@ class Irc:
self.Protocol.send_raw(raw_command) self.Protocol.send_raw(raw_command)
return None return None
case 'print_users': case 'print_vars':
with open('users.txt', 'w') as fw: with open('users.txt', 'w') as fw:
i = 1 i = 1
for u in self.User.UID_DB: for u in self.User.UID_DB:
@@ -1261,6 +1261,14 @@ class Irc:
self.Logs.debug(f" {i} - chars written {w}") self.Logs.debug(f" {i} - chars written {w}")
i += 1 i += 1
self.Protocol.send_priv_msg(dnickname, "Data written in users.txt file", dchanlog) 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 return None

View File

@@ -1,12 +1,13 @@
''' '''
This is the main operational file to handle modules This is the main operational file to handle modules
''' '''
from optparse import Option
from pathlib import Path from pathlib import Path
import sys import sys
import importlib import importlib
from types import ModuleType from types import ModuleType
from typing import TYPE_CHECKING, Optional from typing import TYPE_CHECKING, Optional
from core.definition import MModule from core.definition import DefenderModuleHeader, MModule
if TYPE_CHECKING: if TYPE_CHECKING:
from core.loader import Loader from core.loader import Loader
@@ -15,6 +16,7 @@ if TYPE_CHECKING:
class Module: class Module:
DB_MODULES: list[MModule] = [] DB_MODULES: list[MModule] = []
DB_MODULE_HEADERS: list[DefenderModuleHeader] = []
def __init__(self, loader: 'Loader') -> None: def __init__(self, loader: 'Loader') -> None:
self.__Loader = loader self.__Loader = loader
@@ -38,11 +40,46 @@ class Module:
if not module_name.lower().startswith('mod_'): if not module_name.lower().startswith('mod_'):
return None, None, None return None, None, None
module_name = module_name.lower() module_name = module_name.lower() # --> mod_defender
module_folder = module_name.split('_')[1].lower() # --> defender module_folder = module_name.split('_')[1].lower() # --> defender
class_name = module_name.split('_')[1].capitalize() # --> Defender class_name = module_name.split('_')[1].capitalize() # --> Defender
return module_folder, module_name, 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():
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: 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) 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}') 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 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 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: except AttributeError as attr:
red = uplink.Config.COLORS.red red = uplink.Config.COLORS.red
nogc = uplink.Config.COLORS.nogc nogc = uplink.Config.COLORS.nogc
@@ -124,6 +162,7 @@ class Module:
if self.is_module_exist_in_sys_module(module_name): if self.is_module_exist_in_sys_module(module_name):
module_model = self.model_get_module(module_name) module_model = self.model_get_module(module_name)
if module_model: if module_model:
self.delete_module_header(module_model.class_instance.MOD_HEADER['name'])
module_model.class_instance.unload() module_model.class_instance.unload()
else: else:
uplink.Protocol.send_priv_msg( uplink.Protocol.send_priv_msg(
@@ -141,6 +180,7 @@ class Module:
importlib.reload(the_module) importlib.reload(the_module)
my_class = getattr(the_module, class_name, None) my_class = getattr(the_module, class_name, None)
new_instance = my_class(uplink) new_instance = my_class(uplink)
self.create_module_header(new_instance.MOD_HEADER)
module_model.class_instance = new_instance module_model.class_instance = new_instance
# Créer le module dans la base de données # Créer le module dans la base de données
@@ -163,7 +203,7 @@ class Module:
return False return False
except (TypeError, AttributeError, KeyError, Exception) as err: 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( uplink.Protocol.send_priv_msg(
nick_from=self.__Config.SERVICE_NICKNAME, nick_from=self.__Config.SERVICE_NICKNAME,
msg=f"[RELOAD MODULE ERROR]: {err}", msg=f"[RELOAD MODULE ERROR]: {err}",
@@ -227,6 +267,7 @@ class Module:
return False return False
if module: if module:
self.delete_module_header(module.class_instance.MOD_HEADER['name'])
module.class_instance.unload() module.class_instance.unload()
self.DB_MODULES.remove(module) self.DB_MODULES.remove(module)
@@ -253,7 +294,7 @@ class Module:
return False return False
except Exception as err: except Exception as err:
self.__Logs.error(f"General Error: {err}") self.__Logs.error(f"General Error: {err}", exc_info=True)
return False return False
def unload_all_modules(self) -> bool: def unload_all_modules(self) -> bool:

View File

@@ -15,12 +15,12 @@ class Clone(IModule):
class ModConfModel(schemas.ModConfModel): class ModConfModel(schemas.ModConfModel):
... ...
MOD_HEADER: set[str] = { MOD_HEADER: dict[str, str] = {
'Clone', 'name':'Clone',
'1.0.0', 'version':'1.0.0',
'Connect thousands of clones to your IRCD, by group. You can use them as security moderation.', 'description':'Connect thousands of clones to your IRCD, by group. You can use them as security moderation.',
'Defender Team', 'author':'Defender Team',
'Defender-6' 'core_version':'Defender-6'
} }
def create_tables(self) -> None: def create_tables(self) -> None:

View File

@@ -14,12 +14,12 @@ class Command(IModule):
""" """
pass pass
MOD_HEADER: set[str] = { MOD_HEADER: dict[str, str] = {
'Command', 'name':'Command',
'1.0.0', 'version':'1.0.0',
'Module contains all IRC commands', 'description':'Module contains all IRC commands',
'Defender Team', 'author':'Defender Team',
'Defender-6' 'core_version':'Defender-6'
} }
def create_tables(self) -> None: def create_tables(self) -> None:

View File

@@ -12,12 +12,12 @@ class Defender(IModule):
class ModConfModel(schemas.ModConfModel): class ModConfModel(schemas.ModConfModel):
... ...
MOD_HEADER: set[str] = { MOD_HEADER: dict[str, str] = {
'Defender', 'name':'Defender',
'1.0.0', 'version':'1.0.0',
'Defender main module that uses the reputation security.', 'description':'Defender main module that uses the reputation security.',
'Defender Team', 'author':'Defender Team',
'Defender-6' 'core_version':'Defender-6'
} }
def create_tables(self) -> None: def create_tables(self) -> None:
@@ -214,12 +214,10 @@ class Defender(IModule):
return None return None
case 'UID': case 'UID':
print(f"{self.module_name} - {cmd}")
self.Utils.handle_on_uid(self, cmd) self.Utils.handle_on_uid(self, cmd)
return None return None
case 'SJOIN': case 'SJOIN':
self.Utils.handle_on_sjoin(self, cmd) self.Utils.handle_on_sjoin(self, cmd)
return None return None

View File

@@ -15,12 +15,12 @@ class Jsonrpc(IModule):
""" """
jsonrpc: int = 0 jsonrpc: int = 0
MOD_HEADER: set[str] = { MOD_HEADER: dict[str, str] = {
'JsonRPC', 'name':'JsonRPC',
'1.0.0', 'version':'1.0.0',
'Module using the unrealircd-rpc-py library', 'description':'Module using the unrealircd-rpc-py library',
'Defender Team', 'author':'Defender Team',
'Defender-6' 'core_version':'Defender-6'
} }
def callback_sent_to_irc(self, response: LiveRPCResult) -> None: def callback_sent_to_irc(self, response: LiveRPCResult) -> None:

View File

@@ -12,12 +12,12 @@ class Test(IModule):
param_exemple1: str param_exemple1: str
param_exemple2: int param_exemple2: int
MOD_HEADER: set[str] = { MOD_HEADER: dict[str, str] = {
'Test', 'name':'Test',
'1.0.0', 'version':'1.0.0',
'The test module', 'description':'The test module',
'Defender Team', 'author':'Defender Team',
'Defender-6' 'core_version':'Defender-6'
} }
def create_tables(self) -> None: def create_tables(self) -> None:

View File

@@ -23,12 +23,12 @@ class Votekick(IModule):
class ModConfModel(schemas.VoteChannelModel): class ModConfModel(schemas.VoteChannelModel):
... ...
MOD_HEADER: set[str] = { MOD_HEADER: dict[str, str] = {
'votekick', 'name':'votekick',
'1.0.2', 'version':'1.0.2',
'Channel Democraty', 'description':'Channel Democraty',
'Defender Team', 'author':'Defender Team',
'Defender-6' 'core_version':'Defender-6'
} }
def create_tables(self) -> None: def create_tables(self) -> None:
@@ -79,7 +79,6 @@ class Votekick(IModule):
if metadata is not None: if metadata is not None:
self.VoteKickManager.VOTE_CHANNEL_DB = metadata self.VoteKickManager.VOTE_CHANNEL_DB = metadata
# self.VOTE_CHANNEL_DB = metadata
# Créer les nouvelles commandes du module # Créer les nouvelles commandes du module
self.Irc.build_command(1, self.module_name, 'vote', 'The kick vote 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: def unload(self) -> None:
try: try:
# Cache the local DB with current votes. # 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: for chan in self.VoteKickManager.VOTE_CHANNEL_DB:
self.Protocol.send_part_chan(uidornickname=self.Config.SERVICE_ID, channel=chan.channel_name) self.Protocol.send_part_chan(uidornickname=self.Config.SERVICE_ID, channel=chan.channel_name)