feat: Refactored all cogs implementation
Some checks failed
CI / Run tests (pull_request) Has been cancelled
Some checks failed
CI / Run tests (pull_request) Has been cancelled
This commit is contained in:
32
setup/bot.py
32
setup/bot.py
@@ -5,7 +5,9 @@ from discord.ext import bridge
|
||||
from cogs.cs import CSCog
|
||||
from cogs.insult import InsultsCog
|
||||
from cogs.roulette import RouletteCog
|
||||
from cogs.roles import RolesCog
|
||||
from setup.logger import LOGGER
|
||||
from setup.config import Config
|
||||
|
||||
bot_intents = discord.Intents.default()
|
||||
bot_intents.message_content = True
|
||||
@@ -19,9 +21,26 @@ bot = bridge.Bot(command_prefix='$', intents=bot_intents)
|
||||
COG_REGISTRY = {
|
||||
'cs': CSCog,
|
||||
'insult': InsultsCog,
|
||||
'roulette': RouletteCog
|
||||
'roulette': RouletteCog,
|
||||
'roles': RolesCog,
|
||||
}
|
||||
|
||||
# Register a global admin slash command directly on the bot to avoid circular imports.
|
||||
from discord import option as discord_option
|
||||
|
||||
|
||||
@bot.slash_command(name="reload", description="Reload a feature (cog) by name, or 'all')")
|
||||
@discord_option(name="feature", description="Feature to reload (e.g. cs, insult, roles, roulette, all)", input_type=str, required=True)
|
||||
async def reload(ctx: discord.ApplicationContext, feature: str):
|
||||
# Only allow administrators
|
||||
if not ctx.author or not getattr(ctx.author, 'guild_permissions', None) or not ctx.author.guild_permissions.administrator:
|
||||
await ctx.respond("You must be a server administrator to use this command.", ephemeral=True)
|
||||
return
|
||||
|
||||
await ctx.defer()
|
||||
reload_feature(feature)
|
||||
await ctx.followup.send(f"Reload requested for feature: {feature}", ephemeral=True)
|
||||
|
||||
|
||||
def _normalize_cog_names(cogs=None):
|
||||
if cogs is None:
|
||||
@@ -46,6 +65,12 @@ def _normalize_cog_names(cogs=None):
|
||||
|
||||
|
||||
def register_cogs(cogs=None):
|
||||
# Ensure guild configs are loaded before instantiating cogs
|
||||
try:
|
||||
Config.load_guilds_configs()
|
||||
except Exception:
|
||||
LOGGER.debug('Could not reload guild configs during register_cogs.')
|
||||
|
||||
selected_cogs = _normalize_cog_names(cogs)
|
||||
|
||||
existing_cog_names = set(COG_REGISTRY.values())
|
||||
@@ -75,6 +100,11 @@ def reload_feature(feature: str):
|
||||
return
|
||||
|
||||
LOGGER.debug(f'Reloading {feature}...')
|
||||
# Refresh guild configs before reloading a single feature
|
||||
try:
|
||||
Config.load_guilds_configs()
|
||||
except Exception:
|
||||
LOGGER.debug('Could not reload guild configs during reload_feature.')
|
||||
target_cog_name = COG_REGISTRY[normalized_feature].__name__
|
||||
for existing_name in list(bot.cogs.keys()):
|
||||
if existing_name == target_cog_name:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
import os, pathlib, json
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from setup.logger import LOGGER
|
||||
|
||||
class Config:
|
||||
load_dotenv()
|
||||
@@ -9,6 +9,10 @@ class Config:
|
||||
CONFIG_PATH = str(os.getenv("CONFIG_PATH"))
|
||||
COGS = os.getenv("COGS", "")
|
||||
|
||||
GUILDS_IDS:list[int] = []
|
||||
|
||||
GUILD_CONFIGS:dict[int,dict] = {}
|
||||
|
||||
@classmethod
|
||||
def get_cogs(cls):
|
||||
raw_value = cls.COGS.strip()
|
||||
@@ -16,3 +20,53 @@ class Config:
|
||||
return None
|
||||
return [item.strip().lower() for item in raw_value.split(",") if item.strip()]
|
||||
|
||||
@classmethod
|
||||
def load_guilds_configs(cls):
|
||||
with os.scandir(cls.CONFIG_PATH) as d:
|
||||
guilds_config_files = [f.path for f in d if f.is_file() and f.name.endswith(".json")]
|
||||
for guild_config_file in guilds_config_files:
|
||||
with open(guild_config_file) as f:
|
||||
guild_config = json.load(f)
|
||||
guild_id = guild_config["guild_id"]
|
||||
cls.GUILDS_IDS.append(guild_id)
|
||||
cls.GUILD_CONFIGS[guild_id] = {k:v for k,v in guild_config.items() if k != "guild_id"}
|
||||
cls.GUILD_CONFIGS[guild_id]["config_path"] = guild_config_file
|
||||
|
||||
@classmethod
|
||||
def load_guild_config(cls,guild_id:int):
|
||||
config_path = cls.GUILD_CONFIGS[guild_id]["config_path"]
|
||||
with open(config_path) as f:
|
||||
guild_config = json.load(f)
|
||||
# Preserve config_path when re-loading
|
||||
cls.GUILD_CONFIGS[guild_id] = {k: v for k, v in guild_config.items() if k != "guild_id"}
|
||||
cls.GUILD_CONFIGS[guild_id]["config_path"] = config_path
|
||||
|
||||
@classmethod
|
||||
def save_guilds_configs(cls):
|
||||
for guild_id, guild_config in cls.GUILD_CONFIGS.items():
|
||||
config_path = guild_config.get("config_path")
|
||||
if not config_path:
|
||||
continue
|
||||
with open(config_path, 'w') as file:
|
||||
json_config = {k: v for k, v in guild_config.items() if k != "config_path"}
|
||||
json_config["guild_id"] = guild_id
|
||||
json.dump(json_config, file, ensure_ascii=False, indent=2)
|
||||
|
||||
@classmethod
|
||||
def save_guild_config(cls,guild_id:int):
|
||||
config = cls.GUILD_CONFIGS.get(guild_id)
|
||||
if not config:
|
||||
return
|
||||
config_path = config.get("config_path")
|
||||
if not config_path:
|
||||
return
|
||||
with open(config_path, 'w') as file:
|
||||
json_config = {k: v for k, v in config.items() if k != "config_path"}
|
||||
json_config["guild_id"] = guild_id
|
||||
json.dump(json_config, file, ensure_ascii=False, indent=2)
|
||||
|
||||
@classmethod
|
||||
def get_guild_config(cls,guild_id):
|
||||
return cls.GUILD_CONFIGS[guild_id]
|
||||
|
||||
Config.load_guilds_configs()
|
||||
@@ -1,8 +1,21 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format='[%(levelname)s] %(asctime)s: %(name)s: %(message)s')
|
||||
# Determine log level from environment variable `LOG_LEVEL` (e.g. DEBUG, INFO)
|
||||
raw_level = os.getenv("LOG_LEVEL", "INFO").upper()
|
||||
level = logging.INFO
|
||||
if raw_level.isdigit():
|
||||
try:
|
||||
level = int(raw_level)
|
||||
except Exception:
|
||||
level = logging.INFO
|
||||
else:
|
||||
level = getattr(logging, raw_level, logging.INFO)
|
||||
|
||||
logging.basicConfig(level=level, format='[%(levelname)s] %(asctime)s: %(name)s: %(message)s')
|
||||
LOGGER = logging.getLogger('botmafieux')
|
||||
LOGGER.setLevel(logging.DEBUG)
|
||||
LOGGER.setLevel(level)
|
||||
# File handler (append)
|
||||
handler = logging.FileHandler(filename='botmafieux.log', encoding='utf-8', mode='a')
|
||||
handler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s:%(name)s: %(message)s'))
|
||||
LOGGER.addHandler(handler)
|
||||
Reference in New Issue
Block a user