S8: main DRB bot optional end to end + owner-flow integration tests (server-26#225)
- Copy: picker, admin Servers/Streams/How-it-works, org Discord card, bot /help and the server-limit refusal no longer imply the DRB server bot is required; a radio bot alone is enough. - Admin overview: per-server main_bot_present, radio_bots, linked_via, directory_source; Servers tab gets a Bots column. - discord_web docstring: states the reversed "never calls Discord" rule (plan finding 5) and the optional server bot. - Server bot directory cog: re-push (which drops C2's cached permission answers) on overwrite changes, role permission changes and role deletes. - tests/test_discord_owner_flows.py: one-bot org (BYO bot, owner-set preferred, OAuth link, picker, start, reconciler) and full setup (server bot + 4 radio bots: placement, shared channel, every refusal explained), against a modelled Discord over httpx.MockTransport. - storage.download_audio removed: no caller on main since #233. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
f49dc86111
commit
9c124d4c8b
@@ -4,15 +4,22 @@ Server directory push (server-26#213 item 3).
|
||||
The web node page picks a Discord server + voice channel instead of taking raw
|
||||
IDs. It needs each server's name and voice channels, and this bot already has
|
||||
them in its gateway cache (default intents include ``guilds``), so it pushes
|
||||
them to C2 (PUT /discord/guilds/{id}/directory, service key). C2 never calls
|
||||
Discord on the browser's behalf and no token reaches the browser.
|
||||
them to C2 (PUT /discord/guilds/{id}/directory, service key).
|
||||
|
||||
This bot is optional (server-26#225): in a server it is not in, C2 reads the
|
||||
same list over Discord REST with a radio bot's token (routers/discord_web.py).
|
||||
Where it is present, its push wins while fresh and also tells C2 to drop its
|
||||
cached Discord answers for the server, so a permission fixed in Discord shows
|
||||
up on the next check instead of after the cache expires. No token ever
|
||||
reaches the browser either way.
|
||||
|
||||
Pushed: server name, and per voice channel its id, name, category name and
|
||||
order. Nothing about members, messages or roles.
|
||||
|
||||
When: every server on ready, on join/leave/rename, on any voice-channel or
|
||||
category create/delete/update (debounced per server), and a full re-push every
|
||||
15 minutes in case C2 missed one (it was down, or redeployed).
|
||||
category create/delete/update — including a permission-overwrite change — and
|
||||
on a role permission change or role delete (debounced per server), and a full
|
||||
re-push every 15 minutes in case C2 missed one (it was down, or redeployed).
|
||||
"""
|
||||
import asyncio
|
||||
from typing import Optional
|
||||
@@ -123,10 +130,22 @@ class DirectorySync(commands.Cog):
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_guild_channel_update(self, before: discord.abc.GuildChannel, after: discord.abc.GuildChannel):
|
||||
# Overwrites too: the push makes C2 drop its cached permission answers
|
||||
# for this server (server-26#225), so "Re-check" sees the fix at once.
|
||||
if _relevant(after) and (before.name != after.name or before.position != after.position
|
||||
or getattr(before, "category_id", None) != getattr(after, "category_id", None)):
|
||||
or getattr(before, "category_id", None) != getattr(after, "category_id", None)
|
||||
or before.overwrites != after.overwrites):
|
||||
self.schedule(after.guild)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_guild_role_update(self, before: discord.Role, after: discord.Role):
|
||||
if before.permissions != after.permissions:
|
||||
self.schedule(after.guild)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_guild_role_delete(self, role: discord.Role):
|
||||
self.schedule(role.guild)
|
||||
|
||||
|
||||
async def setup(bot: commands.Bot):
|
||||
await bot.add_cog(DirectorySync(bot))
|
||||
|
||||
Reference in New Issue
Block a user