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:
Logan Cusano
2026-10-05 03:24:44 -04:00
co-authored by Claude Opus 5.5
parent f49dc86111
commit 9c124d4c8b
13 changed files with 652 additions and 60 deletions
+14 -3
View File
@@ -21,9 +21,20 @@ Where the directory comes from (server-26#225 S5 — the main bot is optional):
stored list is missing (inline, inside the permission-check time budget)
or older than ``discord_directory_refresh_seconds`` (in the background).
So C2 does call Discord when a signed-in user opens the picker — with a
radio bot's token, for a server already linked to the caller's org, through
the rate-limited, cached client. No token string ever reaches the browser.
This reverses the old rule "C2 never calls Discord on the browser's behalf"
on purpose (server-26#225 plan finding 5): C2 does call Discord when a
signed-in user opens the picker, presses Start or Re-check — with a radio
bot's token, only for a server already linked to the caller's org, through
discord_rest's rate-limited, cached, breaker-guarded client (refresh=1 at
most once per 10 s per user and server). No token string ever reaches the
browser, and REST answers never create or re-link a ``discord_guilds`` doc.
The DRB server bot is optional end to end (server-26#225 S8). One radio bot
in the server is enough to link it (per-bot OAuth, routers/discord_oauth.py),
list its voice channels (here), check permissions (routers/discord_checks.py)
and stream (streams.start_stream). The server bot adds its slash commands
(/join, /leave, /status, /drb link) and a gateway-fed channel list; every
path it uses keeps working.
Which servers an org sees (``_org_guilds``):