diff --git a/bot.py b/bot.py index 6481414..1605fac 100644 --- a/bot.py +++ b/bot.py @@ -15,7 +15,7 @@ from discord.ext.commands import Context from dotenv import load_dotenv from database import DatabaseManager -from utils.ascii_art import ascii +from utils import ascii, setup_logger, get_uptime, setup_signal_handlers load_dotenv() @@ -50,54 +50,7 @@ intents.messages = True -class LoggingFormatter(logging.Formatter): - # Colors - black = "\x1b[30m" - red = "\x1b[31m" - green = "\x1b[32m" - yellow = "\x1b[33m" - blue = "\x1b[34m" - gray = "\x1b[38m" - # Styles - reset = "\x1b[0m" - bold = "\x1b[1m" - - COLORS = { - logging.DEBUG: gray + bold, - logging.INFO: blue + bold, - logging.WARNING: yellow + bold, - logging.ERROR: red, - logging.CRITICAL: red + bold, - } - - def format(self, record): - log_color = self.COLORS[record.levelno] - format = "(black){asctime}(reset) (levelcolor){levelname:<8}(reset) (green){name}(reset) {message}" - format = format.replace("(black)", self.black + self.bold) - format = format.replace("(reset)", self.reset) - format = format.replace("(levelcolor)", log_color) - format = format.replace("(green)", self.green + self.bold) - formatter = logging.Formatter(format, "%Y-%m-%d %H:%M:%S", style="{") - return formatter.format(record) - - -logger = logging.getLogger("discord_bot") -logger.setLevel(logging.INFO) - -console_handler = logging.StreamHandler() -console_handler.setFormatter(LoggingFormatter()) -log_file_path = os.getenv("LOG_FILE", "logs/discord.log") -log_dir = os.path.dirname(log_file_path) -if log_dir: - os.makedirs(log_dir, exist_ok=True) -file_handler = logging.FileHandler(filename=log_file_path, encoding="utf-8", mode="w") -file_handler_formatter = logging.Formatter( - "[{asctime}] [{levelname:<8}] {name}: {message}", "%Y-%m-%d %H:%M:%S", style="{" -) -file_handler.setFormatter(file_handler_formatter) - -logger.addHandler(console_handler) -logger.addHandler(file_handler) +logger = setup_logger() class DiscordBot(commands.Bot): @@ -223,20 +176,7 @@ class DiscordBot(commands.Bot): ) def get_uptime(self) -> str: - uptime_seconds = int(time.time() - self.start_time) - days = uptime_seconds // 86400 - hours = (uptime_seconds % 86400) // 3600 - minutes = (uptime_seconds % 3600) // 60 - seconds = uptime_seconds % 60 - - if days > 0: - return f"{days}d {hours}h {minutes}m {seconds}s" - elif hours > 0: - return f"{hours}h {minutes}m {seconds}s" - elif minutes > 0: - return f"{minutes}m {seconds}s" - else: - return f"{seconds}s" + return get_uptime(self.start_time) async def close(self) -> None: if self._shutdown: @@ -374,13 +314,6 @@ class DiscordBot(commands.Bot): raise error -def signal_handler(signum, frame): - logger.info("Shutdown requested. Closing bot...") - if bot.loop and not bot.loop.is_closed(): - asyncio.create_task(bot.close()) - bot.loop.call_soon_threadsafe(bot.loop.stop) - - bot = DiscordBot() if __name__ == "__main__": @@ -388,8 +321,7 @@ if __name__ == "__main__": print(ascii) - signal.signal(signal.SIGINT, signal_handler) - signal.signal(signal.SIGTERM, signal_handler) + setup_signal_handlers(bot) try: bot.run(os.getenv("TOKEN")) diff --git a/utils/__init__.py b/utils/__init__.py index 9cffff4..74c5cac 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -1,3 +1,9 @@ from .ascii_art import ascii, ascii_plain, gradient_text, gradient_text_selective +from .logging import LoggingFormatter, setup_logger +from .time import get_uptime +from .signal import setup_signal_handlers -__all__ = ['ascii', 'ascii_plain', 'gradient_text', 'gradient_text_selective'] \ No newline at end of file +__all__ = [ + 'ascii', 'ascii_plain', 'gradient_text', 'gradient_text_selective', + 'LoggingFormatter', 'setup_logger', 'get_uptime', 'setup_signal_handlers' +] \ No newline at end of file diff --git a/utils/logging.py b/utils/logging.py new file mode 100644 index 0000000..c5c86ec --- /dev/null +++ b/utils/logging.py @@ -0,0 +1,54 @@ +import logging +import os + + +class LoggingFormatter(logging.Formatter): + black = "\x1b[30m" + red = "\x1b[31m" + green = "\x1b[32m" + yellow = "\x1b[33m" + blue = "\x1b[34m" + gray = "\x1b[38m" + reset = "\x1b[0m" + bold = "\x1b[1m" + + COLORS = { + logging.DEBUG: gray + bold, + logging.INFO: blue + bold, + logging.WARNING: yellow + bold, + logging.ERROR: red, + logging.CRITICAL: red + bold, + } + + def format(self, record): + log_color = self.COLORS[record.levelno] + format = "(black){asctime}(reset) (levelcolor){levelname:<8}(reset) (green){name}(reset) {message}" + format = format.replace("(black)", self.black + self.bold) + format = format.replace("(reset)", self.reset) + format = format.replace("(levelcolor)", log_color) + format = format.replace("(green)", self.green + self.bold) + formatter = logging.Formatter(format, "%Y-%m-%d %H:%M:%S", style="{") + return formatter.format(record) + + +def setup_logger(): + logger = logging.getLogger("discord_bot") + logger.setLevel(logging.INFO) + + console_handler = logging.StreamHandler() + console_handler.setFormatter(LoggingFormatter()) + + log_file_path = os.getenv("LOG_FILE", "logs/discord.log") + log_dir = os.path.dirname(log_file_path) + if log_dir: + os.makedirs(log_dir, exist_ok=True) + file_handler = logging.FileHandler(filename=log_file_path, encoding="utf-8", mode="w") + file_handler_formatter = logging.Formatter( + "[{asctime}] [{levelname:<8}] {name}: {message}", "%Y-%m-%d %H:%M:%S", style="{" + ) + file_handler.setFormatter(file_handler_formatter) + + logger.addHandler(console_handler) + logger.addHandler(file_handler) + + return logger diff --git a/utils/signal.py b/utils/signal.py new file mode 100644 index 0000000..678f1f8 --- /dev/null +++ b/utils/signal.py @@ -0,0 +1,13 @@ +import asyncio +import signal + + +def setup_signal_handlers(bot): + def signal_handler(signum, frame): + bot.logger.info("Shutdown requested. Closing bot...") + if bot.loop and not bot.loop.is_closed(): + asyncio.create_task(bot.close()) + bot.loop.call_soon_threadsafe(bot.loop.stop) + + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) diff --git a/utils/time.py b/utils/time.py new file mode 100644 index 0000000..51adcdc --- /dev/null +++ b/utils/time.py @@ -0,0 +1,18 @@ +import time + + +def get_uptime(start_time: float) -> str: + uptime_seconds = int(time.time() - start_time) + days = uptime_seconds // 86400 + hours = (uptime_seconds % 86400) // 3600 + minutes = (uptime_seconds % 3600) // 60 + seconds = uptime_seconds % 60 + + if days > 0: + return f"{days}d {hours}h {minutes}m {seconds}s" + elif hours > 0: + return f"{hours}h {minutes}m {seconds}s" + elif minutes > 0: + return f"{minutes}m {seconds}s" + else: + return f"{seconds}s"