Replace personal paths, user IDs, and names with generic placeholders. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
5.9 KiB
Channels
Qwen Code supports three messaging channels — Telegram, WeChat, and DingTalk. All adapters extend the shared channel architecture (ChannelBase, AcpBridge, SessionRouter) in packages/channels/base/src/. Each channel can be started individually or all together with node dist/cli.js channel start.
Telegram
Source: packages/channels/telegram/src/TelegramAdapter.ts, built on the Telegraf library.
The adapter supports plain text messaging, slash commands, a working indicator ("typing" chat action), DM pairing, and group chat (supergroups with @mention gating). Image receiving works via bot.on('photo') → getFileLink → download → base64, with captions passed as envelope text. File/document receiving saves downloaded files to /tmp/channel-files/ and includes the path in the envelope so the agent can read them via read-file (works with any model, no multimodal required). Referenced messages include the quoted text as context in the prompt. Output is formatted as Telegram HTML (converted from markdown). Authentication uses a static bot token. Session persistence and pairing state are stored under ~/.qwen/channels/.
// ~/.qwen/settings.json
{
"channels": {
"my-telegram": {
"type": "telegram",
"token": "$TELEGRAM_BOT_TOKEN",
"senderPolicy": "pairing",
"allowedUsers": [],
"sessionScope": "user",
"instructions": "Keep responses concise.",
},
},
}
source /path/to/telegram/.env
npm run bundle && node dist/cli.js channel start my-telegram
Future work: Streaming responses via in-place editMessageText (throttled at ~2s to respect rate limits, best-effort fallback to single message). Slash command polish — register with BotFather via setMyCommands(), fix /help timing, add /status command.
WeChat (Weixin)
Source: packages/channels/weixin/src/, ported from the cc-weixin project. Uses the iLink Bot API at ilinkai.weixin.qq.com.
The adapter supports plain text messaging via a custom long-poll loop (/ilink/bot/getupdates, cursor-based), with context_token caching per user for reply context. Authentication uses QR code login (qwen channel configure-weixin), producing a bearer token stored in ~/.qwen/channels/weixin/account.json. A typing indicator fires before each ACP prompt using the sendTyping API (ticket obtained from getConfig). Image and file/PDF receiving works through CDN download with AES-128-ECB decryption — images are forwarded as base64 content blocks, files are saved to /tmp/channel-files/ and referenced by path. Referenced messages (user replies) include quoted text as context in the prompt. Formatting is plain text only (all markdown is stripped). The adapter handles session expiry (errcode -14) with automatic reconnection, uses backoff after consecutive errors, and persists the polling cursor to ~/.qwen/channels/weixin/cursor.txt for crash recovery.
// ~/.qwen/settings.json
{
"channels": {
"my-weixin": {
"type": "weixin",
"senderPolicy": "pairing",
"allowedUsers": [],
"sessionScope": "user",
"instructions": "Keep responses concise, plain text only.",
"baseUrl": "https://ilinkai.weixin.qq.com", // optional override
},
},
}
Credentials are stored separately in ~/.qwen/channels/weixin/account.json, created by qwen channel configure-weixin.
# First time: login via QR code
node dist/cli.js channel configure-weixin
# Start
npm run bundle && node dist/cli.js channel start my-weixin
Future work: Media send (upload to WeChat CDN with AES encryption). Voice/video receive. Streaming responses via message_state: GENERATING → FINISH (pending client-side investigation). Multi-account support. Message chunking for long responses.
DingTalk (钉钉)
Source: packages/channels/dingtalk/src/, using Stream mode (WebSocket, no public IP required). Referenced from openclaw-channel-dingtalk.
The adapter connects via the dingtalk-stream SDK, which handles WebSocket connection, reconnection, heartbeats, and callback ACKs (DingTalk retries unACKed messages). Authentication reuses the SDK's built-in token (client.getConfig().access_token) from AppKey + AppSecret. Responses are sent back through a per-message sessionWebhook URL — a temporary, conversation-scoped endpoint that supports text, markdown, images, and files. Both DM and group chat are supported, with group messages gated by @mention detection (isInAtList). A 👀 emoji reaction serves as a working indicator while the agent processes (posted via the emotion API and recalled on completion). Output is formatted as DingTalk markdown, with tables converted to plain text, messages split at ~3800 characters, and code fences maintained across chunks. Image, file, audio, and video receiving works through a two-step download flow (downloadCode → downloadUrl → buffer); images are forwarded as base64, files saved to /tmp/channel-files/. Quoted message context is extracted from text.repliedMsg and quoteMessage, with bot-reply detection via chatbotUserId.
// ~/.qwen/settings.json
{
"channels": {
"my-dingtalk": {
"type": "dingtalk",
"clientId": "$DINGTALK_CLIENT_ID",
"clientSecret": "$DINGTALK_CLIENT_SECRET",
"senderPolicy": "open",
"sessionScope": "user",
"cwd": "/path/to/project",
"instructions": "Keep responses concise. Use DingTalk markdown.",
"groupPolicy": "open",
"groups": {
"*": { "requireMention": true },
},
},
},
}
export DINGTALK_CLIENT_ID=<your-app-key>
export DINGTALK_CLIENT_SECRET=<your-app-secret>
npm run bundle && node dist/cli.js channel start my-dingtalk
Future work: Quoted bot responses (persisting outbound messages keyed by processQueryKey for lookup on reply). AI Card streaming via /v1.0/card/instances and /v1.0/card/streaming with graceful markdown fallback.