diff --git a/src/bot_base/bot_base.py b/src/bot_base/bot_base.py index edfa08b..0cb14e7 100644 --- a/src/bot_base/bot_base.py +++ b/src/bot_base/bot_base.py @@ -5,6 +5,7 @@ import inspect import logging import os import sys +import traceback import discord import toml @@ -85,7 +86,8 @@ class BotBase(discord.Client): # Check if module exists if not os.path.isdir(os.path.join(self.config["modules_folder"], module)): self.warning(f"Attempt to load unknown module {module}.") - raise errors.ModuleNotFoundError(f"Module {module} not found in modules folder ({self.config['modules_folder']}.)") + raise errors.ModuleNotFoundError( + f"Module {module} not found in modules folder ({self.config['modules_folder']}.)") if not os.path.isfile(os.path.join(self.config["modules_folder"], module, "infos.toml")): self.warning(f"Attempt to load incompatible module {module}: no infos.toml found") raise errors.IncompatibleModuleError(f"Module {module} is incompatible: no infos.toml found.") @@ -102,7 +104,7 @@ class BotBase(discord.Client): self.warning(f"Attempt to load incompatible module {module}: need bot version {infos['bot_version']} " f"and you have {__version__}") raise errors.IncompatibleModuleError(f"Module {module} is not compatible with your current bot version " - f"(need {infos['bot_version']} and you have {__version__}).") + f"(need {infos['bot_version']} and you have {__version__}).") # Check dependencies if infos.get("dependencies"): for dep, version in infos["dependencies"].items(): @@ -113,13 +115,17 @@ class BotBase(discord.Client): self.warning(f"Attempt to load incompatible module {module}: (require {dep} ({version}) " f"and you have {dep} ({self.modules[dep]['infos']['version']})") raise errors.IncompatibleModuleError(f"Module {module} is not compatible with your current install " - f"(require {dep} ({version}) and you have {dep} " - f"({self.modules[dep]['infos']['version']})") + f"(require {dep} ({version}) and you have {dep} " + f"({self.modules[dep]['infos']['version']})") # Check if module is meta if infos.get("metamodule", False) == False: # Check if module have __main_class__ - imported = importlib.import_module(module) + try: + imported = importlib.import_module(module) + except Exception as e: + self.warning(f"Attempt to load incompatible module {module}: failed import") + raise e try: main_class = imported.__main_class__ except AttributeError: @@ -139,8 +145,9 @@ class BotBase(discord.Client): dispatch = main_class.__dispatch__ except AttributeError: self.warning(f"Attempt to load incompatible module {module}: __dispatch_ not found") - raise errors.IncompatibleModuleError(f"Module {module} mainclass ({main_class}) does not provide __dispatch__" - f" attribute)") + raise errors.IncompatibleModuleError( + f"Module {module} mainclass ({main_class}) does not provide __dispatch__" + f" attribute)") # Check if __dispatch__ is function if not inspect.isfunction(imported.__main_class__.__dispatch__): self.warning(f"Attempt to load incompatible module {module}: __dispatch__ is not a function") @@ -178,7 +185,7 @@ class BotBase(discord.Client): "dispatch": dispatch, } }) - else: # Module is metamodule + else: # Module is metamodule self.info(f"Add modules {module} to current modules") self.modules.update({ module: { @@ -196,16 +203,19 @@ class BotBase(discord.Client): for module in self.modules.values(): module["dispatch"](event, *args, **kwargs) + async def on_error(self, event_method, exc, *args, **kwargs): + self.error(f"Error in {event_method}: \n{exc}") + # Logging def info(self, *args, **kwargs): if self.log: self.log.info(*args, **kwargs) self.dispatch("log_info", *args, **kwargs) - def error(self, *args, **kwargs): + def error(self, e, *args, **kwargs): if self.log: - self.log.error(*args, **kwargs) - self.dispatch("log_error", *args, **kwargs) + self.log.error(e, *args, **kwargs) + self.dispatch("log_error", e, *args, **kwargs) def warning(self, *args, **kwargs): if self.log: diff --git a/src/config/base.py b/src/config/base.py index f70a636..2afecf0 100644 --- a/src/config/base.py +++ b/src/config/base.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os import typing import toml @@ -75,8 +76,9 @@ class Config: >>> config = Config("doctest_config.toml") >>> config.register("my_parameter", factory(Int)) >>> config.set({"my_parameter": 3}) - >>> config.save() + >>> config.save() #doctest: +SKIP """ + os.makedirs(os.path.dirname(self.path), exist_ok=True) with open(self.path, 'w') as file: toml.dump({k: v.to_save() for k, v in self.fields.items()}, file) @@ -90,11 +92,11 @@ class Config: >>> config = Config("doctest_config.toml") >>> config.register("my_parameter", factory(Int)) >>> config.set({"my_parameter": 3}) - >>> config.save() + >>> config.save() #doctest: +SKIP >>> new_config = Config("doctest_config.toml") >>> new_config.register("my_parameter", factory(Int)) - >>> new_config.load() - >>> new_config["my_parameter"] + >>> new_config.load() #doctest: +SKIP + >>> new_config["my_parameter"] #doctest: +SKIP 3 :return: None @@ -103,7 +105,8 @@ class Config: with open(self.path, 'r') as file: self.set(toml.load(file)) except FileNotFoundError: - self.save() + pass + self.save() def __getitem__(self, item: str) -> typing.Any: """ diff --git a/src/config/config_types/discord_types/channel.py b/src/config/config_types/discord_types/channel.py index edffe0c..f7cd831 100644 --- a/src/config/config_types/discord_types/channel.py +++ b/src/config/config_types/discord_types/channel.py @@ -1,36 +1,57 @@ from __future__ import annotations -from typing import TYPE_CHECKING +import typing + +import discord from config.config_types.base_type import BaseType -if TYPE_CHECKING: - from main import LBI +if typing.TYPE_CHECKING: + from bot_base import BotBase class Channel(BaseType): - client: LBI + client: BotBase def __init__(self, client): - self.value = None + self.value = 0 + self.channel_instance = None self.client = client def check_value(self, value): + id = value + if isinstance(value, discord.Guild): + id = value.id + if not self.client.is_ready(): + self.client.warning(f"No check for channel {value} because client is not initialized!") + return True + if self.client.get_channel(id): + return True return True def set(self, value): - if self.check_value(value): - self.value = value - return - raise ValueError("Tentative de définir une valeur incompatible") + if not self.check_value(value): + raise ValueError("Tentative de définir une valeur incompatible") + self.value = value + self._update() def get(self): - return self.value + self._update() + return self.channel_instance or self.value def to_save(self): - return self.value + return self.value or 0 def load(self, value): + if self.check_value(value): raise ValueError("Tentative de charger une donnée incompatible.") - self.value = value + self.set(value) + self._update() + + def _update(self): + if self.client.is_ready() and self.channel_instance is None: + self.channel_instance = self.client.get_channel(self.value) + else: + self.channel_instance = None + diff --git a/src/config/config_types/discord_types/guild.py b/src/config/config_types/discord_types/guild.py index e38c654..9c9544d 100644 --- a/src/config/config_types/discord_types/guild.py +++ b/src/config/config_types/discord_types/guild.py @@ -29,7 +29,7 @@ class Guild(BaseType): >>> Guild(client) #doctest: +SKIP """ - self.value = None + self.value = 0 self.guild_instance = None self.client = client @@ -56,7 +56,7 @@ class Guild(BaseType): if isinstance(value, discord.Guild): id = value.id if not self.client.is_ready(): - self.client.warning("No check for guild `value` because client is not initialized!") + self.client.warning(f"No check for guild {value} because client is not initialized!") return True if self.client.get_guild(id): return True @@ -119,7 +119,7 @@ class Guild(BaseType): :return: Current id :rtype: Optional[int] """ - return self.value + return self.value or 0 def load(self, value): """ @@ -140,6 +140,7 @@ class Guild(BaseType): if self.check_value(value): raise ValueError("Tentative de charger une donnée incompatible.") self.set(value) + self._update() def __repr__(self): return f''