Channels
32 production-ready adapters, all normalising inbound messages into the same InboundMessage shape. Every adapter implements connect(), send(target, text), disconnect(), and ping().
InboundMessage contract
Every adapter, regardless of platform, produces an InboundMessage with these fields:
@dataclass
class InboundMessage:
channel: str # adapter name, e.g. "telegram"
sender_id: str # platform-specific user identifier
sender_name:str # display name (best-effort)
content: str # plain-text body
raw: dict # original platform payload
timestamp: float # Unix epoch seconds
reply_to: str | None # thread/reply context
attachments:list # [{type, url, name}]
All 32 adapters
| Adapter | Config key | Transport | Auth |
|---|---|---|---|
| Telegram | telegram | python-telegram-bot v21 | Bot token |
| Discord | discord | discord.py gateway WebSocket | Bot token |
| Slack | slack | slack-bolt Socket Mode | Bot + App token |
whatsapp | Meta Cloud API v19.0 | Phone number ID + access token | |
email | IMAP (aioimaplib) + SMTP (aiosmtplib) | IMAP/SMTP credentials | |
| SMS / Twilio | sms | TwiML webhook | Twilio account SID + auth token |
| Microsoft Teams | teams | Azure Bot Framework OAuth2 | Azure app ID + secret |
| Google Chat | google_chat | aiohttp webhook + service account JWT | GCP service account key |
| Matrix | matrix | matrix-nio sync_forever | Homeserver URL + access token |
| IRC | irc | Pure asyncio RFC1459/2812 over TLS 6697 | SASL PLAIN / NickServ |
| Signal | signal | signal-cli subprocess JSON-RPC | Registered phone number |
| Mattermost | mattermost | WebSocket events API + REST v4 | Access token |
| Mastodon | mastodon | Mastodon.py streaming | Instance URL + access token |
| Nextcloud Talk | nextcloud | OCS v2 REST long-poll | HTTP Basic Auth |
| Generic Webhook | webhook | aiohttp POST | Optional HMAC-SHA256 |
| WebSocket / REST | built-in | FastAPI built-in | Optional API key |
| LINE | line | aiohttp webhook | HMAC-SHA256 X-Line-Signature |
| Feishu / Lark | feishu | aiohttp webhook | App ID + App Secret (tenant access token) |
| iMessage (BlueBubbles) | imessage | REST polling | BlueBubbles server password |
| Synology Chat | synology | aiohttp webhook | Verification token |
| Nostr | nostr | aiohttp WebSocket relay | secp256k1 keypair (NIP-04) |
| Twilio Voice | twilio_voice | TwiML webhook + asyncio.Future | HMAC-SHA1 X-Twilio-Signature |
| Zalo OA | zalo | aiohttp webhook | HMAC-SHA256 X-ZAlo-Signature + OAuth2 |
| WeChat Work | wechat_work | aiohttp webhook (plain-text) | SHA1 URL verification + access token |
| QQ Bot | qq | aiohttp webhook | HMAC-SHA256 + op=13 challenge |
| Tlon / Urbit | tlon | Eyre HTTP API + SSE | Session cookie (POST /~/login) |
| Facebook Messenger | messenger | Meta Graph API v19.0 webhook | HMAC-SHA256 X-Hub-Signature-256 |
| Rocket.Chat | rocketchat | DDP WebSocket + REST v1 | SHA-256 password login |
| Twitch | twitch | IRC-over-WebSocket (IRCv3 tags) | OAuth2 bearer token |
| Bluesky | bluesky | AT Protocol XRPC polling | App password |
| Viber | viber | REST + webhook | HMAC-SHA256 X-Viber-Content-Signature |
| XMPP / Jabber | xmpp | slixmpp asyncio-native | SASL PLAIN / SCRAM-SHA-1 |
Telegram
Uses python-telegram-bot v21. Supports text, voice, photo, and document messages.
[channels.telegram]
bot_token = "ENV:TELEGRAM_BOT_TOKEN"
allowed_chat_ids = [] # empty = allow all
Discord
Uses discord.py v2 with gateway WebSocket. Requires message_content intent enabled in the Discord Developer Portal.
[channels.discord]
bot_token = "ENV:DISCORD_BOT_TOKEN"
allowed_guilds = [] # empty = all guilds
Slack
Uses slack-bolt in Socket Mode — no public URL required. Requires a Slack App with Socket Mode enabled and the chat:write + app_mentions:read scopes.
[channels.slack]
bot_token = "ENV:SLACK_BOT_TOKEN" # xoxb-...
app_token = "ENV:SLACK_APP_TOKEN" # xapp-... (Socket Mode)
Uses the Meta Cloud API v19.0 webhook. Supports text, image, audio, video, and interactive messages.
[channels.whatsapp]
phone_number_id = "ENV:WA_PHONE_NUMBER_ID"
access_token = "ENV:WA_ACCESS_TOKEN"
verify_token = "ENV:WA_VERIFY_TOKEN" # for webhook registration
Polls IMAP with aioimaplib; sends via aiosmtplib. STARTTLS supported. Threads replies to the same email thread.
[channels.email]
imap_host = "imap.gmail.com"
imap_port = 993
smtp_host = "smtp.gmail.com"
smtp_port = 587
username = "ENV:EMAIL_USERNAME"
password = "ENV:EMAIL_PASSWORD"
poll_interval_seconds = 30
SMS / Twilio
Receives inbound SMS via TwiML webhook; replies with Twilio Messaging API.
[channels.sms]
account_sid = "ENV:TWILIO_ACCOUNT_SID"
auth_token = "ENV:TWILIO_AUTH_TOKEN"
from_number = "+15551234567"
Microsoft Teams
Uses Azure Bot Framework with 24-hour OAuth2 token cache.
[channels.teams]
app_id = "ENV:TEAMS_APP_ID"
app_secret = "ENV:TEAMS_APP_SECRET"
Google Chat
HTTP endpoint bot. Service account OAuth2 — pure Python via cryptography + httpx, no google-auth dependency. Token cached with 60s pre-expiry buffer. Supports space targets (spaces/<ID>) and threaded replies (spaces/<ID>/threads/<THREAD_ID>).
[channels.google_chat]
service_account_json = "ENV:GOOGLE_SERVICE_ACCOUNT_JSON" # base64-encoded JSON
verification_token = "ENV:GOOGLE_CHAT_TOKEN"
Matrix
Uses matrix-nio with async sync_forever. Auto-joins rooms on invite.
[channels.matrix]
homeserver = "https://matrix.org"
access_token = "ENV:MATRIX_ACCESS_TOKEN"
IRC
Pure asyncio RFC1459/2812 implementation. TLS on port 6697. SASL PLAIN authentication. Auto-reconnect on disconnect.
[channels.irc]
host = "irc.libera.chat"
port = 6697
nick = "neuralcleave-bot"
password = "ENV:IRC_NICKSERV_PASSWORD"
channels = ["#general", "#dev"]
Signal
Delegates to a running signal-cli subprocess over JSON-RPC. Supports group messages.
[channels.signal]
phone_number = "+15551234567" # registered Signal number
signal_cli_path = "/usr/bin/signal-cli"
LINE
HMAC-SHA256 X-Line-Signature verification. Push-message API for outbound — no replyToken window restriction. Supports direct user, group, and room targets. bot_user_id echo-loop guard.
[channels.line]
channel_access_token = "ENV:LINE_CHANNEL_ACCESS_TOKEN"
channel_secret = "ENV:LINE_CHANNEL_SECRET"
bot_user_id = "ENV:LINE_BOT_USER_ID"
Feishu / Lark
Handles both v1 (legacy) and v2 (schema 2.0) event formats. URL verification challenge for webhook registration. Tenant access token cached with 60s pre-expiry buffer. Supports open_id, chat_id, user_id, and union_id targets.
[channels.feishu]
app_id = "ENV:FEISHU_APP_ID"
app_secret = "ENV:FEISHU_APP_SECRET"
bot_open_id = "ENV:FEISHU_BOT_OPEN_ID" # for echo-loop prevention
iMessage (BlueBubbles)
REST polling against a self-hosted BlueBubbles server on macOS. Polls /api/v1/message?after=<ms>. Outbound via POST /api/v1/message/text. Supports direct (iMessage;-;phone), Apple ID email, SMS fallback, and group chat targets.
[channels.imessage]
server_url = "http://your-mac-ip:1234"
password = "ENV:BLUEBUBBLES_PASSWORD"
bot_handle = "+15551234567" # your iMessage handle for echo-loop prevention
Synology Chat
Receives via aiohttp outgoing webhook. Sends via SYNO.Chat.External entry.cgi API. Supports user:{id}, channel:{id}, and bare integer string targets. SSL verify=False for self-signed NAS certificates.
[channels.synology]
server_url = "https://192.168.1.100:5001"
incoming_token = "ENV:SYNOLOGY_INCOMING_TOKEN"
bot_username = "neuralcleave"
Nostr
Connects to Nostr relays via aiohttp WebSocket. NIP-04 encrypted DMs (kind 4). Pure-Python secp256k1 + BIP-340 Schnorr — no extra pip dependencies. ECDH + AES-256-CBC encryption. Multi-relay broadcast with reconnect.
[channels.nostr]
private_key = "ENV:NOSTR_PRIVATE_KEY" # hex-encoded secp256k1 private key
relays = ["wss://relay.damus.io", "wss://nos.lol"]
Twilio Voice
Multi-turn voice conversations via asyncio.Future bridging. Answers calls with a greeting + <Gather input="speech">. On each transcription, dispatches an InboundMessage and awaits an AI response. The response is wrapped in <Say> TwiML and followed by a new <Gather> for the next turn. All TwiML output is HTML-escaped.
[channels.twilio_voice]
account_sid = "ENV:TWILIO_ACCOUNT_SID"
auth_token = "ENV:TWILIO_AUTH_TOKEN"
response_timeout = 10 # seconds to wait for AI response per turn
Zalo OA
HMAC-SHA256 X-ZAlo-Signature verification. Access token management via oauth.zaloapp.com/v4/oa/access_token with 60s pre-expiry buffer and in-memory refresh-token rotation. Outbound via Zalo OA v3 Customer Service Message API. Supports text, image, sticker, file, audio, video events.
[channels.zalo]
app_id = "ENV:ZALO_APP_ID"
app_secret = "ENV:ZALO_APP_SECRET"
refresh_token = "ENV:ZALO_REFRESH_TOKEN"
oa_id = "ENV:ZALO_OA_ID"
WeChat Work (WeCom)
Webhook in plain-text mode. GET URL challenge via SHA1(sort(token, timestamp, nonce)). POST XML inbound (text/image/voice/video/file/location/link). Access token cached for 7200s with 60s buffer. Targets: touser:, toparty:, totag:, @all, or bare string.
[channels.wechat_work]
corp_id = "ENV:WECHAT_CORP_ID"
agent_id = "ENV:WECHAT_AGENT_ID"
secret = "ENV:WECHAT_SECRET"
token = "ENV:WECHAT_TOKEN"
bot_userid = "neuralcleave_bot"
QQ Bot
HMAC-SHA256 X-Signature-Ed25519 + X-Signature-Timestamp verification. op=13 URL challenge. Dispatches AT_MESSAGE_CREATE, C2C_MESSAGE_CREATE, GROUP_AT_MESSAGE_CREATE, DIRECT_MESSAGE_CREATE events. Mention stripping. Access token cached 7200s with 60s buffer. Targets: channel:id, dm:guild_id, group:openid, c2c:openid.
[channels.qq]
app_id = "ENV:QQ_APP_ID"
client_secret = "ENV:QQ_CLIENT_SECRET"
bot_openid = "ENV:QQ_BOT_OPENID"
Tlon / Urbit
Connects to a Tlon app on an Urbit ship via the Eyre HTTP API. Auth via POST /~/login → session cookie. SSE channel at PUT /~/channel/{uid} with subscription to /updates. Parses both legacy {"text":"..."} and modern {"story":{"inline":[...]}} event formats. Send targets: ~ship (DM), ~host/channel (group), path:/raw/path.
[channels.tlon]
ship_url = "http://your-urbit-ship:8080"
password = "ENV:URBIT_PASSWORD"
bot_ship = "~sampel-palnet"
Facebook Messenger
Meta Graph API v19.0 webhook. HMAC-SHA256 X-Hub-Signature-256 verification. GET hub.challenge handshake. Supports text, image/audio/video attachments, and postback button events. page_id echo guard. Outbound via /me/messages with RESPONSE messaging type.
[channels.messenger]
page_id = "ENV:MESSENGER_PAGE_ID"
page_access_token = "ENV:MESSENGER_PAGE_ACCESS_TOKEN"
verify_token = "ENV:MESSENGER_VERIFY_TOKEN"
Rocket.Chat
DDP WebSocket real-time API + REST v1. SHA-256 password digest login. stream-room-messages subscription. Ping/pong keepalive. Bot echo guard via userId. Thread reply support via tmid. Auto-reconnect (5s delay).
[channels.rocketchat]
server_url = "https://your.rocketchat.server"
username = "ENV:RC_USERNAME"
password = "ENV:RC_PASSWORD"
Twitch
IRC-over-WebSocket (wss://irc-ws.chat.twitch.tv:443) with IRCv3 tags. Requests twitch.tv/tags + twitch.tv/commands capabilities for structured metadata (display-name, user-id, tmi-sent-ts, message ID). Auto-reconnect with configurable delay. ping() validates OAuth token via GET id.twitch.tv/oauth2/validate.
[channels.twitch]
oauth_token = "ENV:TWITCH_OAUTH_TOKEN" # oauth:... prefix stripped automatically
bot_nick = "neuralcleave_bot"
channels = ["#yourchannel"]
Bluesky (AT Protocol)
Polls app.bsky.notification.listNotifications every 30s (configurable). App-password auth with com.atproto.server.createSession. JWT auto-refresh on 401 via refreshSession. Posts via com.atproto.repo.createRecord. Threaded reply support. Echo guard via DID. Configurable notify_types filter.
[channels.bluesky]
handle = "yourbot.bsky.social"
app_password = "ENV:BLUESKY_APP_PASSWORD"
poll_interval_seconds = 30
Viber
REST + webhook. Registers bot via chatapi.viber.com/pa/set_webhook on connect(). HMAC-SHA256 X-Viber-Content-Signature. Supports text, picture, video, file, contact, url, sticker, subscribed, conversation_started events. 7,000-char send limit enforced.
[channels.viber]
auth_token = "ENV:VIBER_AUTH_TOKEN"
webhook_url = "https://your-public-url/viber/webhook"
bot_name = "NeuralCleave"
XMPP / Jabber
slixmpp asyncio-native. 1:1 chat + MUC rooms (XEP-0045). XEP-0030 service discovery. XEP-0199 ping keepalive. SASL PLAIN + SCRAM-SHA-1. Echo guard via bare JID in 1:1, room nick in MUC. 30s connect timeout via asyncio.wait_for.
[channels.xmpp]
jid = "bot@jabber.example.com"
password = "ENV:XMPP_PASSWORD"
rooms = ["general@conference.jabber.example.com"]
nick = "NeuralCleave"
Mattermost
[channels.mattermost]
server_url = "https://your.mattermost.com"
access_token = "ENV:MATTERMOST_TOKEN"
Mastodon
[channels.mastodon]
instance_url = "https://mastodon.social"
access_token = "ENV:MASTODON_ACCESS_TOKEN"
reply_visibility = "public" # public | unlisted | private
Nextcloud Talk
[channels.nextcloud]
server_url = "https://your.nextcloud.com"
username = "ENV:NC_USERNAME"
password = "ENV:NC_PASSWORD"
Generic Webhook
[channels.webhook]
secret = "ENV:WEBHOOK_SECRET" # optional HMAC-SHA256 verification
path = "/webhook/inbound"
Built-in WebSocket / REST
The gateway itself exposes a WebSocket endpoint at /ws/chat and a REST endpoint at POST /api/v1/chat. These are always active and need no additional configuration. See REST API for the protocol details.