# This Python file uses the following encoding: utf-8 # trunk-ignore-all(bandit/B311) # pylint: disable=line-too-long # pylint: disable=too-many-lines """ Module of a python bot named Conjurer - used to work on BDSM discord servers. """ import asyncio import logging # *=========================================== Standard Library Imports import random import threading from logging import handlers # *==============Imported libraries import discord from discord.ext import commands from communication_subroutine import comm_subroutine from constants import ENCODING, LOGFILE, TOKEN logger = logging.getLogger("discord") logger.setLevel(logging.INFO) handler = handlers.RotatingFileHandler( filename=LOGFILE, encoding=ENCODING, mode="a", maxBytes=6 * 1024 * 1024, backupCount=6, ) formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") handler.setFormatter(formatter) logger.addHandler(handler) # *=========================================== Initializations intents = discord.Intents.default() intents.message_content = True intents.typing = True intents.presences = True intents.members = True intents.messages = True intents.voice_states = True intents.moderation = True # on_member_ban - wyswietl na glownym kanale pieczatke "Niech spierdala" # on_member_unban - "mam wyjebane" random.seed() client = commands.Bot(intents=intents, command_prefix="$") # *=========================================== Define Events @client.event async def on_ready(): """Metoda wywoływana przy połączeniu do serwera.""" logger = logging.getLogger("discord") logger.debug("SAMPLE DEBUG LOG") logger.info("%s has connected to Discord!", client.user) # TODO: load vs reload logger.info("Reactor: online") await client.load_extension("administration_commands") await client.load_extension("librarian_commands") await client.load_extension("music_commands") await client.load_extension("radio_commands") await client.load_extension("ai_commands") await client.load_extension("other_commands") await client.load_extension("voice_recognition_commands") await client.load_extension("file_search_commands") await client.load_extension("latex_commands") await client.load_extension("conanjurer_commands") logger.info("Sensors: online") logger.info(client.cogs) await client.tree.sync() for com in client.commands: logger.info("Command %s is awejleble", com.qualified_name) logger.info("Logged in as ----> %s", client.user) logger.info("ID:%s ", client.user.id) logger.info("All systems: operational") # *=========================================== Runtime orchestration # The legacy bootstrap used two bare threads (client.run + comm_subroutine) and # joined them, which made a clean shutdown impossible. We now drive everything # from a single asyncio loop: the Flask comm layer still runs in its own # threads (via asyncio.to_thread) but is steered through a shared stop_event so # the bot can stop both halves cooperatively. async def _run_comm_subroutine(stop_event: threading.Event) -> None: """Run the blocking comm subroutine in a worker thread.""" await asyncio.to_thread(comm_subroutine, stop_event) async def _run_bot(token: str, shutdown_event: asyncio.Event) -> None: """Start the Discord client and flag shutdown when it returns.""" try: await client.start(token, log_handler=None) finally: shutdown_event.set() async def main() -> None: if not TOKEN: logger.error("Discord token missing - set DISCORD_TOKEN or configure netrc") return logger.info("Starting discord bot") shutdown_event = asyncio.Event() comm_stop_event = threading.Event() comm_task = asyncio.create_task(_run_comm_subroutine(comm_stop_event)) bot_task = asyncio.create_task(_run_bot(TOKEN, shutdown_event)) try: await shutdown_event.wait() except (KeyboardInterrupt, asyncio.CancelledError): logger.info("Shutdown signal received") comm_stop_event.set() await client.close() finally: comm_stop_event.set() if not client.is_closed(): await client.close() await asyncio.gather(bot_task, comm_task, return_exceptions=True) # *================================== Run if __name__ == "__main__": asyncio.run(main())