chorus/bot/chorus_bot/main.py
pyr0ball 73fb67b3bf fix: cog re-registration, thread NotFound, follow-up date, and error/empty states
I1: move cog registration + tree.sync into ChorusBot.setup_hook (called once
before gateway connect) instead of on_ready (fires on every reconnect),
preventing duplicate cogs/listeners/poll loops after a gateway RESUME.

I2 (email_ingest.py, addressed alongside I1 for IMAP stability): none here,
handled separately.

I3/I4: TriageList.vue now renders a follow_up_date column so it actually
surfaces back to Donna. App.vue adds a "Show completed" checkbox bound to
includeDone, matching what docs/smoke-test.md step 7 already describes.

I5: add frontend/tests/ItemModal.spec.js covering stagesForType() options
for donation/other/unset types and the type-change stage reset regression.

I6: App.vue wraps loadItems/openItem in try/catch with a calm (no-panic)
error message, and shows calm empty-state copy when there are zero items.

Also fixes thread_ingest.py: standalone threads not started from a message
raise discord.NotFound on fetch_message; now caught and returns early.
2026-07-13 15:01:32 -07:00

51 lines
1.8 KiB
Python

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())