last changes for asyncio

This commit is contained in:
adator
2025-11-18 13:34:03 +01:00
parent 3926d7270d
commit af992f7721
10 changed files with 163 additions and 53 deletions

View File

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

View File

@@ -1,3 +1,4 @@
import asyncio
import os
import re
import json
@@ -8,7 +9,7 @@ import ipaddress
import ast
import requests
from dataclasses import fields
from typing import Any, Optional, TYPE_CHECKING
from typing import Any, Callable, Iterable, Optional, TYPE_CHECKING
from base64 import b64decode, b64encode
from sqlalchemy import create_engine, Engine, Connection, CursorResult
from sqlalchemy.sql import text
@@ -35,6 +36,9 @@ class Base:
# Liste des threads en cours
self.running_threads: list[threading.Thread] = self.Settings.RUNNING_THREADS
# List of all async tasks
self.running_asynctasks: list[asyncio.Task] = self.Settings.RUNNING_ASYNCTASKS
# Les sockets ouvert
self.running_sockets: list[socket.socket] = self.Settings.RUNNING_SOCKETS
@@ -358,6 +362,30 @@ class Base:
except Exception as err:
self.logs.error(err, exc_info=True)
def create_asynctask(self, func: Callable, *, async_name: str = None, run_once: bool = False) -> asyncio.Task:
"""Create a new asynchrone and store it into running_asynctasks variable
Args:
func (Callable): The function you want to call in asynchrone way
async_name (str, optional): The task name. Defaults to None.
run_once (bool, optional): If true the task will be run once. Defaults to False.
Returns:
asyncio.Task: The Task
"""
name = func.__name__ if async_name is None else async_name
if run_once:
for task in asyncio.all_tasks():
if task.get_name().lower() == async_name.lower():
return None
task = asyncio.create_task(func, name=name)
self.running_asynctasks.append(task)
self.logs.debug(f"++ New asynchrone task created as: {task.get_name()}")
return task
def is_thread_alive(self, thread_name: str) -> bool:
"""Check if the thread is still running! using the is_alive method of Threads.

View File

@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Optional
from dataclasses import dataclass
if TYPE_CHECKING:
from core.irc import Irc
from core.loader import Loader
class IModule(ABC):
@@ -13,19 +13,19 @@ class IModule(ABC):
"""The Model containing the module parameters
"""
def __init__(self, uplink: 'Irc') -> None:
def __init__(self, uplink: 'Loader') -> None:
# Module name (Mandatory)
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
# Add Irc Object to the module (Mandatory)
self.Irc = uplink
self.Irc = uplink.Irc
# Add Loader object to the module (Mandatory)
self.Loader = uplink.Loader
self.Loader = uplink
# Add Protocol to the module (Mandatory)
self.Protocol = uplink.Protocol
self.Protocol = uplink.Irc.Protocol
# Add Global Configuration to the module (Mandatory)
self.Config = uplink.Config
@@ -40,7 +40,7 @@ class IModule(ABC):
self.MainUtils = uplink.Utils
# Add logs object to the module (Mandatory)
self.Logs = uplink.Loader.Logs
self.Logs = uplink.Logs
# Add User object to the module (Mandatory)
self.User = uplink.User
@@ -57,19 +57,23 @@ class IModule(ABC):
# Add Reputation object to the module (Optional)
self.Reputation = uplink.Reputation
# Load the child classes
# Log the module
self.Logs.debug(f'Loading Module {self.module_name} ...')
def init(self) -> None:
self.load()
# Inspect child classes
self.inspect_class()
self.create_tables()
# Sync the configuration with core configuration (Mandatory)
uplink.Base.db_sync_core_config(self.module_name, self.ModConfig)
self.Base.db_sync_core_config(self.module_name, self.ModConfig)
return None
# Log the module
self.Logs.debug(f'Loading Module {self.module_name} ...')
def inspect_class(self):
if not hasattr(self, 'ModConfig'):
raise AttributeError("The Module must init ModConfig attribute in the load method!")
if not hasattr(self, 'MOD_HEADER'):
raise NotImplementedError(f"You must declare the header of the module in {self.__class__.__name__}!")
def update_configuration(self, param_key: str, param_value: str) -> None:
"""Update the local and core configuration
@@ -80,12 +84,6 @@ class IModule(ABC):
"""
self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
def inspect_class(self):
if not hasattr(self, 'ModConfig'):
raise AttributeError("The Module must init ModConfig attribute in the load method!")
if not hasattr(self, 'MOD_HEADER'):
raise NotImplementedError(f"You must declare the header of the module in {self.__class__.__name__}!")
@abstractmethod
def create_tables(self) -> None:
"""Method that will create the database if it does not exist.

View File

@@ -1,7 +1,9 @@
"""This class should never be reloaded.
"""
import asyncio
from logging import Logger
from threading import Timer, Thread, RLock
from asyncio.locks import Lock
from socket import socket
from typing import Any, Optional, TYPE_CHECKING
from core.definition import MSModule, MAdmin
@@ -17,9 +19,11 @@ class Settings:
RUNNING_TIMERS: list[Timer] = []
RUNNING_THREADS: list[Thread] = []
RUNNING_ASYNCTASKS: list[asyncio.Task] = []
RUNNING_SOCKETS: list[socket] = []
PERIODIC_FUNC: dict[str, Any] = {}
LOCK: RLock = RLock()
AILOCK: Lock = Lock()
CONSOLE: bool = False

69
core/context.py Normal file
View File

@@ -0,0 +1,69 @@
from logging import Logger
from core.classes.modules.settings import global_settings
from core.classes.modules import translation, user, admin, client, channel, reputation, settings, sasl
import core.logs as logs
import core.definition as df
import core.utils as utils
import core.base as base_mod
import core.module as module_mod
import core.classes.modules.commands as commands_mod
import core.classes.modules.config as conf_mod
import core.classes.modules.rpc.rpc as rpc_mod
import core.irc as irc
import core.classes.protocols.factory as factory
class IrcContext:
def ctx_modules(self) -> None:
self.Definition: df = df
self.ConfModule: conf_mod = conf_mod
self.BaseModule: base_mod = base_mod
self.CommandModule: commands_mod = commands_mod
self.LoggingModule: logs = logs
self.RpcServerModule: rpc_mod = rpc_mod
self.Utils: utils = utils
def ctx_system(self) -> None:
self.Settings: settings.Settings = global_settings
self.Settings.global_lang = self.Config.LANG if self.Config.LANG else "EN"
self.ServiceLogging: logs.ServiceLogging = self.LoggingModule.ServiceLogging()
self.Logs: Logger = self.ServiceLogging.get_logger()
self.Config: df.MConfig = self.ConfModule.Configuration(self.Logs, self.ServiceLogging).configuration_model
self.Settings.global_logger = self.Logs
self.Translation: translation.Translation = translation.Translation(self)
self.Settings.global_translation = self.Translation.get_translation()
self.Base: base_mod.Base = self.BaseModule.Base(self)
self.User: user.User = user.User(self)
self.Settings.global_user = self.User
self.Client: client.Client = client.Client(self)
self.Admin: admin.Admin = admin.Admin(self)
self.Channel: channel.Channel = channel.Channel(self)
self.Reputation: reputation.Reputation = reputation.Reputation(self)
self.Commands: commands_mod.Command = commands_mod.Command(self)
self.ModuleUtils: module_mod.Module = module_mod.Module(self)
self.Sasl: sasl.Sasl = sasl.Sasl(self)
self.Irc: irc.Irc = irc.Irc(self)
self.PFactory: factory.ProtocolFactorty = factory.ProtocolFactorty(self.Irc)
self.RpcServer: rpc_mod.JSONRPCServer = rpc_mod.JSONRPCServer(self)
self.Base.init()
self.Logs.debug(self.Utils.tr("Loader %s success", __name__))

View File

@@ -582,7 +582,7 @@ class Irc:
# Envoyer la commande aux classes dynamiquement chargées
if command != 'notallowed':
for module in self.ModuleUtils.DB_MODULES:
module.class_instance.hcmds(user, channel, cmd, fullcmd)
await module.class_instance.hcmds(user, channel, cmd, fullcmd)
match command:
@@ -1048,11 +1048,11 @@ class Irc:
try:
# Load a module ex: .load mod_defender
if len(cmd) < 2:
self.Protocol.send_notice(dnickname, fromuser, tr("Syntax. /msg %s %s MODULE_NAME", dnickname, command.upper()))
await self.Protocol.send_notice(dnickname, fromuser, tr("Syntax. /msg %s %s MODULE_NAME", dnickname, command.upper()))
return None
mod_name = str(cmd[1])
self.ModuleUtils.load_one_module(self, mod_name, fromuser)
await self.ModuleUtils.load_one_module(self, mod_name, fromuser)
return None
except KeyError as ke:
self.Logs.error(f"Key Error: {ke} - list recieved: {cmd}")
@@ -1077,15 +1077,15 @@ class Irc:
try:
# ==> mod_defender
if len(cmd) < 2:
self.Protocol.send_notice(dnickname, fromuser, tr("Syntax. /msg %s %s MODULE_NAME", dnickname, command.upper()))
await self.Protocol.send_notice(dnickname, fromuser, tr("Syntax. /msg %s %s MODULE_NAME", dnickname, command.upper()))
return None
module_name = str(cmd[1]).lower()
self.ModuleUtils.reload_one_module(self, module_name, fromuser)
await self.ModuleUtils.reload_one_module(self, module_name, fromuser)
return None
except Exception as e:
self.Logs.error(f"Something went wrong with a module you want to reload: {e}")
self.Protocol.send_priv_msg(
await self.Protocol.send_priv_msg(
nick_from=dnickname,
msg=f"Something went wrong with the module: {e}",
channel=dchanlog

View File

@@ -110,7 +110,7 @@ class 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) # Créer une nouvelle instance de la classe
create_instance_of_the_class = my_class(uplink.Loader) # 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
@@ -151,7 +151,7 @@ class Module:
def load_all_modules(self) -> bool:
...
def reload_one_module(self, uplink: 'Irc', module_name: str, nickname: str) -> bool:
async def reload_one_module(self, uplink: 'Irc', module_name: str, nickname: str) -> bool:
"""Reloading one module and insert it into the model as well as the database
Args:
@@ -172,7 +172,7 @@ class Module:
self.delete_module_header(module_model.class_instance.MOD_HEADER['name'])
module_model.class_instance.unload()
else:
uplink.Protocol.send_priv_msg(
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
@@ -186,13 +186,13 @@ 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)
new_instance = my_class(uplink.Loader)
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)
uplink.Protocol.send_priv_msg(
await uplink.Protocol.send_priv_msg(
nick_from=self.__Config.SERVICE_NICKNAME,
msg=f"Module [{module_folder}.{module_name}] has been reloaded!",
channel=self.__Config.SERVICE_CHANLOG
@@ -202,7 +202,7 @@ class Module:
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}")
uplink.Protocol.send_priv_msg(
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
@@ -211,7 +211,7 @@ class Module:
except (TypeError, AttributeError, KeyError, Exception) as err:
self.__Logs.error(f"[RELOAD MODULE ERROR]: {err}", exc_info=True)
uplink.Protocol.send_priv_msg(
await uplink.Protocol.send_priv_msg(
nick_from=self.__Config.SERVICE_NICKNAME,
msg=f"[RELOAD MODULE ERROR]: {err}",
channel=self.__Config.SERVICE_CHANLOG