OpenClaw Multi-Channel Setup 2026: Telegram, Discord, Feishu, QQ, WeChat
Deploy your OpenClaw AI assistant across all major messaging platforms. This comprehensive guide covers Telegram, Discord, Feishu (Lark), QQ, and WeChat integration with complete configuration examples.
Why Multi-Channel Deployment?
Meet Users Where They Are:
Global reach: Telegram (900M users), Discord (200M users)
China market: WeChat (1.3B users), QQ (600M users), Feishu (enterprise)
Unified AI: One OpenClaw instance, multiple channels
Cost efficiency: Share API costs across platformsUse Cases:
Customer support across regions
Internal team assistants
Community engagement bots
Multi-region businessesArchitecture Overview
```
┌─────────────────────────────────────────┐
│ OpenClaw Core Engine │
│ (Claude/GPT-4/Gemini + Memory + Tools) │
└──────────────┬──────────────────────────┘
│
┌───────┴───────┐
│ Adapters │
└───────┬───────┘
│
┌──────────┼──────────┬──────────┬──────────┐
│ │ │ │ │
┌───▼───┐ ┌──▼───┐ ┌──▼───┐ ┌──▼───┐ ┌──▼────┐
│Telegram│ │Discord│ │Feishu│ │ QQ │ │ WeChat │
└────────┘ └───────┘ └──────┘ └──────┘ └────────┘
```
Key Components:
Core Engine: Shared AI logic, memory, tools
Adapters: Platform-specific message handling
Webhook/Polling: Receive messages from platforms
Rate Limiting: Per-platform quotas
User Management: Unified user database1. Telegram Bot Setup
Step 1: Create Bot with BotFather
Open Telegram and search for `@BotFather`
Create new bot:
```
/newbot
Bot name: My OpenClaw Assistant
Username: myopenclaw_bot
```
Save your token:
```
Token: 123456789:ABCdefGHIjklMNOpqrsTUVwxyz
```
Step 2: Configure OpenClaw
Edit `.env`:
```env
Telegram Configuration
TELEGRAM_ENABLED=true
TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrsTUVwxyz
TELEGRAM_WEBHOOK_URL=https://your-domain.com/webhook/telegram
TELEGRAM_ALLOWED_USERS= # Leave empty for public, or comma-separated user IDs
```
Configure webhook (nded for production):
```bash
curl -X POST "https://api.telegram.org/bot/setWebhook" \
-H "Content-Type: application/json" \
-d '{"url": "https://your-domain.com/webhook/telegram"}'
```
Or use polling (easier for development):
```env
TELEGRAM_USE_POLLING=true
```
Step 3: Implement Telegram Adapter
`adapters/telegram.js`:
```javascript
const TelegramBot = require('node-telegram-bot-api');
const { processMessage } = require('../core/engine');
class TelegramAdapter {
constructor(config) {
this.bot = new TelegramBot(config.token polling: config.usePolling || false
});
this.setupHandlers();
}
setupHandlers() {
// Text messages
this.bot.on('message', async (msg) => {
const chatId = msg.chat.id;
const userId = `telegram_${msg.from.id}`;
const text = msg.text;
try {
// Show typing indicator
await this.bot.sendChatAction(chatId, 'typing');
// Process with OpenClaw core
const response = await processMessage({
userId,
message: text,
platform: 'telegram',
metadata: {
chatId,
username: msg.from.username,
firstName: msg.from.first_name
}
});
// Send response
await this.bot.sendMessage(chatId, response.text, {
parse_mode: 'Markdown',
reply_to_message_id: msg.message_id
});
} catch (error) {
console.error('Telegram error:', error);
await this.bot.sendMessage(chatId, 'Sorry, an error occurred.');
}
});
// Commands
this.bot.onText(/\/start/, (msg) => {
const chatId = msg.chat.id;
this.bot.sendMessage(chatId,
'Welcome to OpenClaw! I\'m your AI assistant. How can I help you today?'
);
});
this.bot.onText(/\/help/, (msg) => {
const chatId = msg.chat.id;
this.bot.sendMessage(chatId,
'Available commands:\n' +
'/start - Start conversation\n' +
'/help - Show this message\n' +
'/clear - Clear conversation history\n' +
'/model - Switch AI model'
);
});
}
// Webhook handler
async handleWebhook(req, res) {
try {
await this.bot.processUpdate(req.body);
res.sendStatus(200);
} catch (error) {
console.error('Webhook error:', error);
res.sendStatus(500);
}
}
}
module.exports = TelegramAdapter;
```
Step 4: Advanced Features
Inline Keyboards:
```javascript
await this.bot.sendMessage(chatId, 'Choose an option:', {
reply_markup: {
inline_keyboard: [
[
{ text: 'Option 1', callback_data: 'opt1' },
{ text: 'Option 2', callback_data: 'opt2' }
]
]
}
});
// Handle callbacks
this.bot.on('callback_query', async (query) => {
const data = query.data;
// Process callback
});
```
File Handling:
```javascript
this.bot.on('document', async (msg) => {
const fileId = msg.document.file_id;
const file = await this.bot.getFile(fileId);
const fileUrl = `https://api.telegram.org/file/bot${token}/${file.file_path}`;
// Download and process file
});
```
2. Discord Bot Setup
Step 1: Create Discord Application
Visit Discord Developer Portal
Click "New Application"
Name: "OpenClaw Assistant"
Go to "Bot" tab → "Add Bot"
Copy bot token
Enable "Message Content Intent"Step 2: Configure Permissions
Bot Permissions (OAuth2 → URL Generator):
✅ Read Messages/View Channels
✅ Send Messages
✅ Send Messages in Threads
✅ Embed Links
✅ Attach Files
✅ Read Message History
✅ Add ReactionsGenerated URL:
```
https://discord.com/api/oauth2/authorize?client_id=YOUR_CLIENT_ID&permissions=412317240384&scope=bot%20applications.commands
```
Step 3: Configure OpenClaw
`.env`:
```env
Discord Configuration
DISCORD_ENABLED=true
DISCORD_BOT_TOKEN=your-discord-bot-token
DISCORD_CLIENT_ID=your-client-id
DISCORD_GUILD_ID=your-server-id # Optional: restrict to specific server
```
Step 4: Implement Discord Adapter
`adapters/discord.js`:
```javascript
const { Client, GatewayIntentBits } = require('discord.js');
const { processMessage } = require('../core/engine');
class DiscordAdapter {
constructor(config) {
this.client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.DirectMessages
]
});
this.setupHandlers();
this.client.login(config.token);
}
setupHandlers() {
this.client.on('ready', () => {
console.log(`Discord bot logged in as ${this.client.user.tag}`);
});
this.client.on('messageCreate', async (message) => {
// Ignore bot messages
if (message.author.bot) return;
// Only respond to mentions or DMs
const isMentioned = message.mentions.has(this.client.user);
const isDM = message.channel.type === 'DM';
if (!isMentioned && !isDM) return;
try {
// Show typing indicator
await message.channel.sendTyping();
// Remove mention from message
let text = message.content;
if (isMentioned) {
text = text.replace(`<@${this.client.user.id}>`, '').trim();
}
// Process with OpenClaw core
const response = await processMessage({
userId: `discord_${message.author.id}`,
message: text,
platform: 'discord',
metadata: {
channelId: message.channel.id,
guildId: message.guild?.id,
username: message.author.username
}
});
// Send response (split if > 2000 chars)
await this.sendLongMessage(message.channel, response.text);
} catch (error) {
console.error('Discord error:', error);
await message.reply('Sorry, an error occurred.');
}
});
}
async sendLongMessage(channel, text) {
const chunks = text.match(/[\s\S]{1,2000}/g) || [];
for (const chunk of chunks) {
await channel.send(chunk);
}
}
}
module.exports = DiscordAdapter;
```
Step 5: Slash Commands
Register commands:
```javascript
const { REST, Routes, SlashCommandBuilder } = require('discord.js');
const commands = [
new SlashCommandBuilder()
.setName('ask')
.setDescription('Ask OpenClaw a question')
.addStringOption(option =>
option.setName('question')
.setDescription('Your question')
.setRequired(true)
),
new SlashCommandBuilder()
.setName('clear')
.setDescription('Clear conversation history')
].map(command => command.toJSON());
const rest = new REST({ version: '10' }).setToken(token);
await rest.put(
Routes.applicationCommands(clientId),
{ body: commands }
);
```
Handle slash commands:
```javascript
this.client.on('interactionCreate', async (interaction) => {
if (!interaction.isChatInputCommand()) return;
if (interaction.commandName === 'ask') {
const question = interaction.options.getString('question');
await interaction.deferReply();
const response = await processMessage({
userId: `discord_${interaction.user.id}`,
message: question,
platform: 'discord'
});
await interaction.editReply(response.text);
}
});
```
3. Feishu (Lark) Bot Setup
Step 1: Create Feishu App
Visit Feishu Open Platform
Create new app
Get App ID and App Secret
Enable bot capability
Subscribe to message eventsStep 2: Configure OpenClaw
`.env`:
```env
Feishu Configuration
FEISHU_ENABLED=true
FEISHU_APP_ID=cli_xxx
FEISHU_APP_SECRET=xxx
FEISHU_VERIFICATION_TOKEN=xxx
FEISHU_ENCRYPT_KEY=xxx # Optional
```
Step 3: Implement Feishu Adapter
`adapters/feishu.js`:
```javascript
const axios = require('axios');
const crypto = require('crypto');
class FeishuAdapter {
constructor(config) {
this.appId = config.appId;
this.appSecret = config.appSecret;
this.verificationToken = config.verificationToken;
this.accessToken = null;
this.tokenExpiry = 0;
}
async getAccessToken() {
if (this.accessToken && Date.now() < this.tokenExpiry) {
return this.accessToken;
}
const response = await axios.post(
'https://open.feishu.cn/open-api/auth/v3/tenant_access_token/internal',
{
app_id: this.appId,
app_secret: this.appSecret
}
);
this.accessToken = response.data.tenant_access_token;
this.tokenExpiry = Date.now() + (response.data.expire - 60) * 1000;
return this.accessToken;
}
async handleEvent(req, res) {
// Verify request
if (req.body.token !== this.verificationToken) {
return res.status(401).send('Invalid token');
}
// Handle URL verification
if (req.body.type === 'url_verification') {
return res.json({ challenge: req.body.challenge });
}
// Handle message event
if (req.body.event?.type === 'message') {
const event = req.body.event;
// Ignore bot messages
if (event.message.chat_type === 'bot') {
return res.sendStatus(200);
}
// Process message
const response = await processMessage({
userId: `feishu_${event.sender.sender_id.user_id}`,
message: JSON.parse(event.message.content).text,
platform: 'feishu',
metadata: {
chatId: event.message.chat_id,
messageId: event.message.message_id
}
});
// Send reply
await this.sendMessage(event.message.chat_id, response.text);
}
res.sendStatus(200);
}
async sendMessage(chatId, text) {
const token = await this.getAccessToken();
await axios.post(
'https://open.feishu.cn/open-api/im/v1/messages',
{
receive_id: chatId,
msg_type: 'text',
content: JSON.stringify({ text })
},
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
params: {
receive_id_type: 'chat_id'
}
}
);
}
// Send interactive card
async sendCard(chatId, card) {
const token = await this.getAccessToken();
await axios.post(
'https://open.feishu.cn/open-api/im/v1/messages',
{
receive_id: chatId,
msg_type: 'interactive',
content: JSON.stringify(card)
},
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
params: {
receive_id_type: 'chat_id'
}
}
);
}
}
module.exports = FeishuAdapter;
```
Step 4: Interactive Cards
Send rich card:
```javascript
const card = {
config: {
wide_screen_mode: true
},
header: {
title: {
tag: 'plain_text',
content: 'OpenClaw Response'
}
},
elements: [
{
tag: 'div',
text: {
tag: 'lark_md',
content: response.text
}
},
{
tag: 'action',
actions: [
{
tag: 'button',
text: {
tag: 'plain_text',
content: 'Continue'
},
type: 'primary',
value: { action: 'continue' }
}
]
}
]
};
await this.sendCard(chatId, card);
```
4. QQ Bot Setup
Step 1: Register QQ Bot
Visit QQ Open Platform
Create bot application
Get App ID and Token
Or use go-cqhttp (community solution)Step 2: Using go-cqhttp (Recommended)
Install go-cqhttp:
```bash
Download from https://github.com/Mrs4s/go-cqhttp/releases
wget https://github.com/Mrs4s/go-cqhttp/releases/download/v1.0.0/go-cqhttp_linux_amd64.tar.gz
tar -xzf go-cqhttp_linux_amd64.tar.gz
./go-cqhttp
```
Configure `config.yml`:
```yaml
account:
uin: 123456789 # QQ number
password: '' # Leave empty for QR code login
message:
post-format: array
servers:
- http:
host: 127.0.0.1
port: 5700
timeout: 5
post:
- url: 'http://localhost:3000/webhook/qq'
secret: 'your-secret'
```
Step 3: Configure OpenClaw
`.env`:
```env
QQ Configuration
QQ_ENABLED=true
QQ_WEBHOOK_SECRET=your-secret
QQ_API_URL=http://localhost:5700
```
Step 4: Implement QQ Adapter
`adapters/qq.js`:
```javascript
const axios = require('axios');
const crypto = require('crypto');
class QQAdapter {
constructor(config) {
this.apiUrl = config.apiUrl;
this.secret = config.secret;
}
async handleWebhook(req, res) {
// Verify signature
const signature = req.headers['x-signature'];
if (signature) {
const hash = crypto
.createHmac('sha1', this.secret)
.update(JSON.stringify(req.body))
.digest('hex');
if (`sha1=${hash}` !== signature) {
return res.status(401).send('Invalid signature');
}
}
const event = req.body;
// Handle message
if (event.post_type === 'message') {
const userId = `qq_${event.user_id}`;
const message = event.message;
// Process with OpenClaw
const response = await processMessage({
userId,
message: this.parseMessage(message),
platform: 'qq',
metadata: {
groupId: event.group_id,
messageId: event.message_id
}
});
// Send reply
await this.sendMessage(event, response.text);
}
res.sendStatus(200);
}
parseMessage(message) {
if (typeof message === 'string') return message;
// Parse CQ code format
return message
.map(seg => seg.type === 'text' ? seg.data.text : '')
.join('');
}
async sendMessage(event, text) {
const endpoint = event.message_type === 'group'
? '/send_group_msg'
: '/send_private_msg';
await axios.post(`${this.apiUrl}${endpoint}`, {
[event.message_type === 'group' ? 'group_id' : 'user_id']:
event.message_type === 'group' ? event.group_id : event.user_id,
message: text
});
}
}
module.exports = QQAdapter;
```
5. WeChat Bot Setup
Option 1: Enterprise WeChat (Official)
Step 1: Create Enterprise WeChat App
Register at Enterprise WeChat
Create internal app
Get Corp ID, Agent ID, SecretStep 2: Configure OpenClaw
`.env`:
```env
WeChat Work Configuration
WECHAT_WORK_ENABLED=true
WECHAT_WORK_CORP_ID=wwxxx
WECHAT_WORK_AGENT_ID=1000002
WECHAT_WORK_SECRET=xxx
WECHAT_WORK_TOKEN=xxx
WECHAT_WORK_ENCODING_AES_KEY=xxx
```
Step 3: Implement Adapter
```javascript
const { WXWork } = require('wxwork-api');
class WeChatWorkAdapter {
constructor(config) {
this.api = new WXWork({
corpId: config.corpId,
corpSecret: config.secret
});
}
async handleMessage(req, res) {
const message = req.body;
if (message.MsgType === 'text') {
const response = await processMessage({
userId: `wechat_${message.FromUserName}`,
message: message.Content,
platform: 'wechat_work'
});
await this.sendMessage(message.FromUserName, response.text);
}
res.send('success');
}
async sendMessage(userId, text) {
await this.api.sendText(userId, text);
}
}
```
Option 2: Personal WeChat (Unofficial)
⚠️ Warning: Unofficial methods violate WeChat ToS and may result in account ban.
Using wechaty (at your own risk):
```javascript
const { Wechaty } = require('wechaty');
const bot = new Wechaty();
bot.on('message', async (message) => {
if (message.self()) return;
const text = message.text();
const response = await processMessage({
userId: `wechat_${message.talker().id}`,
message: text,
platform: 'wechat'
});
await message.say(response.text);
});
bot.start();
```
6. Unified Management
Multi-Channel Router
`core/router.js`:
```javascript
class MultiChannelRouter {
constructor() {
this.adapters = new Map();
}
registerAdapter(platform, adapter) {
this.adapters.set(platform, adapter);
}
async routeMessage(userId, message, platform) {
// Get user preferences
const user = await this.getUserProfile(userId);
// Apply platform-specific formatting
let formattedMessage = message;
switch (platform) {
case 'telegram':
formattedMessage = this.formatMarkdown(message);
break;
case 'discord':
formattedMessage = this.formatDiscordMarkdown(message);
break;
case 'feishu':
formattedMessage = this.formatFeishuMarkdown(message);
break;
default:
formattedMessage = this.stripFormatting(message);
}
return formattedMessage;
}
formatMarkdown(text) {
// Telegram markdown
return text
.replace(/\*\*(.*?)\*\*/g, '*$1*') // Bold
.replace(/__(.*?)__/g, '_$1_'); // Italic
}
formatDiscordMarkdown(text) {
// Discord markdown (similar to standard)
return text;
}
formatFeishuMarkdown(text) {
// Feishu uses different syntax
return text
.replace(/\*\*(.*?)\*\*/g, '$1')
.replace(/__(.*?)__/g, '$1');
}
stripFormatting(text) {
// Remove all markdown
return text.replace(/[*_`~]/g, '');
}
}
```
Rate Limiting
Per-platform limits:
```javascript
const rateLimits = {
telegram: { requests: 30, window: 1000 }, // 30/sec
discord: { requests: 50, window: 1000 }, // 50/sec
feishu: { requests: 100, window: 60000 }, // 100/min
qq: { requests: 20, window: 1000 }, // 20/sec
wechat: { requests: 10, window: 1000 } // 10/sec
};
class RateLimiter {
constructor(limits) {
this.limits = limits;
this.counters = new Map();
}
async checkLimit(platform) {
const limit = this.limits[platform];
const key = `${platform}_${Date.now() / limit.window | 0}`;
const count = this.counters.get(key) || 0;
if (count >= limit.requests) {
throw new Error('Rate limit exceeded');
}
this.counters.set(key, count + 1);
// Cleanup old counters
setTimeout(() => this.counters.delete(key), limit.window);
}
}
```
7. Monitoring and Analytics
Track Multi-Channel Metrics
```javascript
const metrics = {
messageCount: new Map(),
responseTime: new Map(),
errorRate: new Map()
};
async function trackMessage(platform, duration, error = null) {
// Increment message count
const count = metrics.messageCount.get(platform) || 0;
metrics.messageCount.set(platform, count + 1);
// Track response time
const times = metrics.responseTime.get(platform) || [];
times.push(duration);
metrics.responseTime.set(platform, times);
// Track errors
if (error) {
const errors = metrics.errorRate.get(platform) || 0;
metrics.errorRate.set(platform, errors + 1);
}
}
// Generate report
function generateReport() {
const report = {};
for (const [platform, count] of metrics.messageCount) {
const times = metrics.responseTime.get(platform) || [];
const errors = metrics.errorRate.get(platform) || 0;
report[platform] = {
messages: count,
avgResponseTime: times.reduce((a, b) => a + b, 0) / times.length,
errorRate: (errors / count * 100).toFixed(2) + '%'
};
}
return report;
}
```
8. Best Practices
Security
Validate webhooks: Always verify signatures
Rate limiting: Implement per-user and per-platform limits
Input sanitization: Clean user input before processing
Secret management: Use environment variables, never commit secretsPerformance
Async processing: Don't block webhook responses
Queue system: Use Redis/RabbitMQ for high load
Caching: Cache access tokens and frequent responses
Connection pooling: Reuse HTTP connectionsUser Experience
Typing indicators: Show bot is processing
Error messages: User-friendly error handling
Help commands: Clear documentation
Response formatting: Platform-appropriate markdownTroubleshooting
Telegram Issues
Problem: Webhook not receiving messages
```bash
Check webhook status
curl https://api.telegram.org/bot/getWebhookInfo
Delete webhook
curl https://api.telegram.org/bot/deleteWebhook
Set webhook again
curl -X POST https://api.telegram.org/bot/setWebhook \
-d "url=https://your-domain.com/webhook/telegram"
```
Discord Issues
Problem: Bot not responding to messages
Check "Message Content Intent" is enabled
Verify bot has "Read Messages" permission in channel
Check bot role is above other rolesFeishu Issues
Problem: Access token expired
```javascript
// Always refresh token before API calls
const token = await this.getAccessToken();
```
QQ Issues
Problem: go-cqhttp login failed
Use QR code login instead of password
Check if account requires device verification
Try different go-cqhttp versionConclusion
You now have OpenClaw running across 5 major messaging platforms! This multi-channel setup allows you to:
Reach users globally (Telegram, Discord)
Serve China market (WeChat, QQ, Feishu)
Provide unified AI experience
Scale efficiently with shared infrastructureNext Steps:
OpenClaw Model Configuration
OpenClaw Skills Guide
OpenClaw Performance OptimizationNeed Help?
Get professional assistance with your multi-channel OpenClaw deployment:
Request Free Consultation →
---
About the Author: The OpenClaw team maintains this open-source project and provides enterprise support for multi-channel deployments.
Last Updated: March 21, 2026