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.
51 lines
1.8 KiB
Python
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())
|