from datetime import datetime from utils.logger.custom_logging import Custom_Logger import logging from utils.adv_configparser import Advanced_ConfigParser from utils.datetime_tools import get_elapsed_time_smal, get_elapsed_time_big, get_elapsed_time_milliseconds import discord from discord.ext import commands from pathlib import Path import re import sys import traceback import asyncio from typing import Union from platforms.reddit import Reddit_Adapter from const import VERSION print(" ____ ____ ___________ __________") print(" / __ \/ __ \/ ___/_ __/ / _/_ __/") print(" / /_/ / / / /\__ \ / /_____ / / / / ") print(" / ____/ /_/ /___/ // /_____// / / / ") print(f" /_/ \____//____//_/ /___/ /_/ v{VERSION}") print(" Copyright (c) 2024-2025 Lars Winzer") print() print(" Source: https://github.com/official-Cromatin/Post-It") print(" Report an Issue: https://github.com/official-Cromatin/Post-It/issues/new?assignees=&labels=bug&projects=&template=issue_report.yml") print("\n") startup = datetime.now().timestamp() # Initialize the logger Custom_Logger.initialize() app_logger = logging.getLogger("app") startup_logger = logging.getLogger("app.startup") source_path = Path(__file__).resolve() base_path = source_path.parents[1] app_logger.info(f"Using the following path as entrypoint: '{base_path}'") intents = discord.Intents.default() intents.messages = True class MyBot(commands.Bot): def __init__(self): super().__init__(command_prefix=None, help_command=None, intents=intents) self.__first_on_ready = False self.VERSION = VERSION self.STARTUP_TIMESTAMP: float = None self.platforms_config: Advanced_ConfigParser = None self.bot_config: Advanced_ConfigParser = None self.reddit_adapter: Reddit_Adapter = None self.no_executed_commands:int = 0 self.no_succeeded_commands:int = 0 self.no_failed_commands:int = 0 async def setup_hook(self): # Register cogs to handle commands for cog_name in ["debug", "post"]: await self.load_extension(f"cogs.{cog_name}") await self.tree.sync() async def on_app_command_completion(self, interaction: discord.Interaction, command: Union[discord.app_commands.Command, discord.app_commands.ContextMenu]): """Called when a `app_commands.Command` or `app_commands.ContextMenu` has successfully completed without error""" self.no_succeeded_commands += 1 async def on_interaction(self, interaction: discord.Interaction): """Called when an interaction happened""" match interaction.type.name: case discord.InteractionType.application_command.name: self.no_executed_commands += 1 case discord.InteractionType.ping.name: print("App got pinged by discord") case discord.InteractionType.autocomplete.name: print("Interaction with autocomplete") case discord.InteractionType.modal_submit.name: print("Modal interaction submitted") case discord.InteractionType.component.name: print("Component interaction") async def on_connect(self): """A coroutine to be called to setup the bot, after the bot is logged in but before it has connected to the Websocket""" if not self.__first_on_ready: startup_logger.info("Beginning startup routine ...") routine_begin = datetime.now().timestamp() await self.change_presence(status = discord.Status.dnd, activity = discord.CustomActivity("Executing pre startup routine")) # Create the adapters for the platforms task_start = datetime.now().timestamp() startup_logger.debug("Loading platforms config ...") platforms_config = Advanced_ConfigParser(Path.joinpath(base_path, "config", "platforms.ini")) self.platforms_config = platforms_config startup_logger.info(f"Loaded platforms config after {get_elapsed_time_milliseconds(datetime.now().timestamp() - task_start)}") # Create platforms adapter task_start = datetime.now().timestamp() startup_logger.debug("Creating reddit adapter ...") self.reddit_adapter = Reddit_Adapter(platforms_config["REDDIT"]["CLIENT_ID"], platforms_config["REDDIT"]["CLIENT_SECRET"]) startup_logger.info(f"Created reddit adapter after {get_elapsed_time_milliseconds(datetime.now().timestamp() - task_start)}") await self.change_presence(status = discord.Status.online, activity = None) startup_logger.info(f"Startup routine finished after {get_elapsed_time_milliseconds(datetime.now().timestamp() - routine_begin)}") self.__first_on_ready = True else: startup_logger.info("Startup routine allready executed, omitting this execution") async def on_ready(self): app_logger.info(f"Successfully logged in (after {get_elapsed_time_smal(datetime.now().timestamp() - startup)}) as {self.user}") bot = MyBot() bot.STARTUP_TIMESTAMP = startup bot.bot_config = Advanced_ConfigParser(Path.joinpath(base_path, "config", "bot.ini")) if re.match(r'[A-Za-z\d]{24}\.[\w-]{6}\.[\w-]{27}', bot.bot_config["DISCORD"]["TOKEN"]): app_logger.critical("Bot (config/bot.ini) configuration invalid, please set a valid token") quit(1) elif bot.bot_config.compare_to_template() not in ("equal", "config_minus"): app_logger.critical("Bot (config/bot.ini) configuration is missing some parts. Make sure it at least has all the same keys as the template") quit(1) else: app_logger.info("Bot configuration valid, continuing with startup") # Setup handlers to handle states of command execution @bot.tree.error async def on_app_command_error(ctx:discord.Interaction, error): """Executed when exception during command execution occurs""" print('Ignoring exception in command {}:'.format(ctx.command), file=sys.stderr) traceback.print_exception(type(error), error, error.__traceback__) bot.no_failed_commands += 1 try: bot.run(bot.bot_config["DISCORD"]["TOKEN"], log_handler = None) except discord.errors.LoginFailure: app_logger.critical("Improper token has been passed. Aborting startup") quit(1) app_logger.info("Quitting application ...") asyncio.run(bot.close()) app_logger.info(f"Exiting. Application ran for {get_elapsed_time_big(datetime.now().timestamp() - startup)}")