diff --git a/drb-c2-core/app/routers/discord_web.py b/drb-c2-core/app/routers/discord_web.py index 3b6bbcb..936e9ba 100644 --- a/drb-c2-core/app/routers/discord_web.py +++ b/drb-c2-core/app/routers/discord_web.py @@ -83,7 +83,8 @@ async def put_guild_directory(guild_id: str, body: GuildDirectory, _: dict = Dep if body.present: data["voice_channels"] = [c.model_dump() for c in body.voice_channels] if body.name: - data["name"] = body.name + # Same field the /drb link flow writes (feat/213-guild-link, PR #218). + data["guild_name"] = body.name await fstore.doc_set(streams.GUILDS, guild_id, data) return {"ok": True, "guild_id": guild_id, "voice_channels": len(body.voice_channels)} @@ -109,23 +110,30 @@ async def _node_for_caller(node_id: str, decoded: dict) -> tuple[dict, bool]: return node, can +def _guild_name(g: dict) -> Optional[str]: + return g.get("guild_name") or g.get("name") + + def _require_control(can: bool) -> None: if not can: raise HTTPException(403, "Only the organization owner, an admin, or this node's operator can do that.") async def _org_guilds(org_id: str) -> list[dict]: - """``discord_guilds`` docs this org may stream into, bot still present.""" + """``discord_guilds`` docs this org may stream into, bot still present. + Mirrors streams.resolve_guild_org: explicit link, else the unlinked-server + fallback org — except a server an org explicitly unlinked (``unlinked_at``, + PR #218), which gets no fallback.""" fallback = settings.discord_unlinked_guild_org_id or None out = [] for g in await fstore.collection_list(streams.GUILDS): gid = str(g.get("guild_id") or "") if not gid or g.get("bot_present") is False: continue - owner = g.get("org_id") or fallback + owner = g.get("org_id") or (None if g.get("unlinked_at") else fallback) if owner and owner == org_id: out.append(g) - out.sort(key=lambda g: ((g.get("name") or "").lower(), g["guild_id"])) + out.sort(key=lambda g: ((_guild_name(g) or "").lower(), g["guild_id"])) return out @@ -183,7 +191,7 @@ async def join_options(node_id: str, decoded: dict = Depends(require_firebase_to }) out_guilds.append({ "guild_id": gid, - "name": g.get("name"), + "name": _guild_name(g), "linked": bool(g.get("org_id")), "synced": g.get("voice_channels") is not None, "synced_at": g.get("directory_at"), @@ -253,7 +261,7 @@ async def start_node_stream(node_id: str, body: NodeStreamStart, decoded: dict = doc = await streams.start_stream( org_id=org_id, guild_id=guild_id, channel_id=channel_id, system_id=system_id, node_id=node_id, mode=body.mode, - channel_name=channel.get("name"), guild_name=guild.get("name"), + channel_name=channel.get("name"), guild_name=_guild_name(guild), created_by=f"web:{decoded.get('uid') or 'unknown'}", max_streams=settings.discord_guild_max_streams, ) diff --git a/drb-c2-core/tests/test_discord_web_picker.py b/drb-c2-core/tests/test_discord_web_picker.py index 78cdcc6..52d6e8b 100644 --- a/drb-c2-core/tests/test_discord_web_picker.py +++ b/drb-c2-core/tests/test_discord_web_picker.py @@ -74,7 +74,7 @@ def test_directory_push_keeps_org_link(fs): run(web.put_guild_directory("100", body, {"service": True})) g = fs.get("discord_guilds", "100") assert g["org_id"] == ORG - assert g["name"] == "Fire Co. 7 (renamed)" + assert g["guild_name"] == "Fire Co. 7 (renamed)" assert [c["channel_id"] for c in g["voice_channels"]] == ["1003"] @@ -112,6 +112,10 @@ def test_options_unlinked_fallback_follows_bot_rule(fs, monkeypatch): got = {g["guild_id"]: g for g in run(web.join_options("n1", OWNER))["guilds"]} assert got["300"]["linked"] is False assert "200" not in got + # An org that explicitly unlinked its server (PR #218) never falls back. + fs.put("discord_guilds", "300", {"guild_id": "300", "org_id": None, "unlinked_at": "2026-10-04T00:00:00+00:00", + "guild_name": "Unlinked", "voice_channels": []}) + assert "300" not in [g["guild_id"] for g in run(web.join_options("n1", OWNER))["guilds"]] def test_options_other_org_node_is_404(fs): diff --git a/drb-frontend/components/NodeDiscordRadio.tsx b/drb-frontend/components/NodeDiscordRadio.tsx index 277572b..c516a82 100644 --- a/drb-frontend/components/NodeDiscordRadio.tsx +++ b/drb-frontend/components/NodeDiscordRadio.tsx @@ -134,8 +134,8 @@ function DiscordPicker({ Discord server {usable.length === 0 ? (

- No Discord server is linked to your organization yet. Add the DRB bot to your server; a platform - admin then links it to your organization. + No Discord server is linked to your organization yet. Add the DRB bot to your Discord server and + link it to your organization; it shows up here once the bot reports its channels.

) : (