Gateway Channel Directory

Let your bot enumerate every channel it can see — before anyone messages it

Discover every Discord and Slack channel your bot can address, without waiting for someone to say @bot first.

Diagram

Channel Directory

🤖 Bot Gateway

🔄 refresh_from_adapters

💬 Discord.list_channels

💼 Slack.list_channels

✈️ Telegram: observed-only

📇 ChannelDirectory

📣 Broadcast • DM warm-up • Routing

Quick Start

  1. Run an agent on the gateway

    Nothing to enable — if Discord or Slack is configured, its channels are auto-listed on every refresh. Your agent can address every visible channel without a prior inbound message:

    python
    from praisonaiagents import Agent
    
    agent = Agent(
        name="Announcer",
        instructions="Post the daily standup summary to every team channel.",
        model="gpt-4o-mini",
    )
    
    agent.start("Post 'Standup at 10:00 GMT' to every channel in the directory.")
  2. Configure Discord or Slack

    Enumeration works out of the box — no new keys are required:

    yaml
    # gateway.yaml
    channels:
      discord:
        platform: discord
        token: "${DISCORD_BOT_TOKEN}"
        # Guilds intent is already enabled by the adapter.
    
      slack:
        platform: slack
        bot_token: "${SLACK_BOT_TOKEN}"
        # Paginates public + private channels your bot user can access.

How It Works

ChannelDirectory.refresh_from_adapters asks each adapter for its channels, then merges them into the observed set.

Diagram
Slack AdapterDiscord AdapterChannelDirectoryBot GatewaySlack AdapterDiscord AdapterChannelDirectoryBot GatewayAgent tools can now iterate every visible channel.refresh_from_adapters(adapters)list_channels()[ChannelRef(id, name, type), …] (guild cache walk)list_channels()paginate conversations_list (cap 50 pages)[ChannelRef, …] or [] on timeout / errordirectory populated

Each adapter returns a list of ChannelRef descriptors. Only .id is consumed by the directory; name and type are for logging and UI.

FieldTypeDefaultDescription
idstr—Platform-native channel identifier (required)
namestr""Human-readable name, "" when unknown
typestr""Platform-native type tag ("text", "channel", "im")

Adapters implement list_channels() optionally. refresh_from_adapters looks it up via getattr(adapter, "list_channels", None) and skips any adapter that doesn’t provide it — Telegram, custom webhook, and IRC descriptor-only adapters stay observed-only, so nothing regresses.


Behavior Details

The directory degrades gracefully — a slow or empty adapter yields fewer entries, never an exception.

SituationResult
Discord client not readyReturns [] while client.is_ready() is False
Slack timeout or SDK errorReturns []; the refresh loop continues
Adapter without list_channels()Skipped — stays observed-only
Workspace over 10 000 channelsOnly the first 10 000 land in the directory (surfaced in the operator log)

Slack pagination and timeout

Slack’s async client is driven from the synchronous refresh loop by a sync-safe wrapper — asyncio.run(...) when no loop is running, or a short-lived worker thread when a loop is already active. This works whether the gateway starts inside an existing asyncio.run or from a plain sync entrypoint.

ConstantWhereDefaultDescription
_MAX_LIST_CHANNELS_PAGESbots/slack.py50Pagination cap × 200 per page → 10 000-channel ceiling
_LIST_CHANNELS_TIMEOUTbots/slack.py30.0Seconds before the Slack coroutine driver degrades to []
Discord guilds intentbots/discord.pyalready onRequired for the guild → text-channels cache. No user action

Best Practices

Read the directory each turn, don't hand-cache ids

Channels come and go. Iterate the auto-populated directory on every turn instead of caching channel ids in your agent.

Filter large Slack workspaces at the agent level

On workspaces over 10 000 conversations, add a channel-name prefix or topic-tag filter in your agent — the directory alone stops at the pagination cap.

Tolerate an empty directory on startup

list_channels() may return [] transiently while a client warms up. Guard agent code so an empty first tick is harmless.

Implement list_channels() only when it's cheap

For a custom platform adapter, list_channels() is opt-in. Skip it if enumeration is prohibitively expensive on that platform — the adapter stays observed-only.


How has_channel gates agent sends

ChannelDirectory.has_channel(platform, channel_id) is also the authority behind send_message‘s destination attestation. A chat is “known” to the gateway if it is a home channel, an alias, or an observed channel in the directory. See Destination Attestation.


Related