Node page Discord picker: org's servers + voice channels, not raw IDs (#213 item 3) #220
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -134,8 +134,8 @@ function DiscordPicker({
|
||||
<legend className="text-xs text-ink-muted mb-2">Discord server</legend>
|
||||
{usable.length === 0 ? (
|
||||
<p className="text-xs text-ink-2 bg-raised border border-line rounded-lg px-3 py-2">
|
||||
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.
|
||||
</p>
|
||||
) : (
|
||||
<div className="space-y-1.5">
|
||||
|
||||
Reference in New Issue
Block a user