Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions gateway/channel_directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ async def build_channel_directory(adapters: Dict[Any, Any]) -> Dict[str, Any]:

for platform, adapter in adapters.items():
try:
list_channels = getattr(adapter, "list_channels", None)
if callable(list_channels):
platform_channels = await list_channels()
if platform_channels is not None:
platforms[platform.value] = _normalize_adapter_channels(platform_channels)
continue
if platform == Platform.DISCORD:
platforms["discord"] = _build_discord(adapter)
elif platform == Platform.SLACK:
Expand Down Expand Up @@ -146,6 +152,32 @@ def _build_discord(adapter) -> List[Dict[str, str]]:
return channels


def _normalize_adapter_channels(raw_channels: Any) -> List[Dict[str, Any]]:
channels: List[Dict[str, Any]] = []
seen_ids = set()
if not isinstance(raw_channels, list):
return channels
for raw in raw_channels:
if not isinstance(raw, dict):
continue
channel_id = str(raw.get("id") or "").strip()
name = str(raw.get("name") or channel_id).strip()
if not channel_id or not name or channel_id in seen_ids:
continue
entry: Dict[str, Any] = {
"id": channel_id,
"name": name,
"type": str(raw.get("type") or "dm"),
}
if raw.get("thread_id"):
entry["thread_id"] = str(raw.get("thread_id"))
if raw.get("guild"):
entry["guild"] = str(raw.get("guild"))
channels.append(entry)
seen_ids.add(channel_id)
return channels


async def _build_slack(adapter) -> List[Dict[str, Any]]:
"""List Slack channels the bot has joined across all workspaces.

Expand Down
20 changes: 20 additions & 0 deletions tests/gateway/test_channel_directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock, patch

from gateway.config import Platform
from gateway.channel_directory import (
build_channel_directory,
lookup_channel_type,
Expand Down Expand Up @@ -68,6 +69,25 @@ def broken_dump(data, fp, *args, **kwargs):

assert result == previous

def test_uses_adapter_list_channels_when_available(self, tmp_path):
class AdapterWithChannels:
async def list_channels(self):
return [
{"id": "default", "name": "主对话", "type": "dm"},
{"id": "family_1", "name": "达拉崩吧", "type": "group"},
{"id": "", "name": "ignored", "type": "dm"},
{"id": "family_1", "name": "duplicate", "type": "group"},
]

cache_file = tmp_path / "channel_directory.json"
with patch("gateway.channel_directory.DIRECTORY_PATH", cache_file):
directory = asyncio.run(build_channel_directory({Platform.TELEGRAM: AdapterWithChannels()}))

assert directory["platforms"]["telegram"] == [
{"id": "default", "name": "主对话", "type": "dm"},
{"id": "family_1", "name": "达拉崩吧", "type": "group"},
]


class TestResolveChannelName:
def _setup(self, tmp_path, platforms):
Expand Down