import asyncio import discord from discord.ext import commands from chorus_bot.config import load_config from chorus_bot.backend_client import BackendClient from chorus_bot.cogs.manual_share import setup as setup_manual_share from chorus_bot.cogs.email_ingest import setup as setup_email_ingest from chorus_bot.cogs.thread_ingest import setup as setup_thread_ingest from chorus_bot.cogs.subscribe import setup as setup_subscribe class ChorusBot(commands.Bot): """Bot subclass that registers cogs + syncs the command tree exactly once, in setup_hook (called before the gateway connects), instead of on_ready (which fires on every RESUME/reconnect and would re-register cogs, raising or duplicating listeners/loops each time).""" def __init__(self, config, backend_client: BackendClient, **kwargs): super().__init__(**kwargs) self.config = config self.backend_client = backend_client async def setup_hook(self): await setup_manual_share(self, self.config) await setup_email_ingest(self, self.config) await setup_thread_ingest(self, self.config, self.backend_client) await setup_subscribe(self, self.config) await self.tree.sync(guild=discord.Object(id=self.config.guild_id)) def build_bot(config, backend_client: BackendClient) -> ChorusBot: intents = discord.Intents.default() intents.message_content = True intents.members = True return ChorusBot(config, backend_client, command_prefix="!", intents=intents) async def main(): config = load_config() backend_client = BackendClient(config.backend_base_url) bot = build_bot(config, backend_client) @bot.event async def on_ready(): print(f"Chorus bot ready as {bot.user}") await bot.start(config.discord_token) if __name__ == "__main__": asyncio.run(main())