@xzq-xu/feishu
v0.3.1
Published
Production-grade Feishu/Lark channel plugin for OpenClaw
Maintainers
Readme
openclaw-plugin-feishu
Turn Feishu into your AI super-gateway. A production-grade Feishu/Lark channel plugin for OpenClaw — the brilliant AI agent framework.
Features
- Human-like Message Processing — Bot reads all accumulated messages before responding, just like a human catching up on a conversation
- Intelligent Batching — Groups messages by chat, flushes on trigger (like @mention) with full context
- Mention Preservation — Non-bot @mentions are preserved as
@[Name](open_id)so Agent can @ users back - Extensible Triggers —
@mentionis just one trigger type; architecture supports keywords, schedules, etc. - History Messages API — Fetch chat history with pagination for context gathering
- Flexible Access Control — DM policies (open/pairing/allowlist) and group policies (open/allowlist/disabled)
- Dual Domain Support — Works with both Feishu (China) and Lark (International)
- Interactive Card Tool — AI can send rich interactive cards with buttons, layouts, and formatted content
- Multi-Account Support — Configure multiple Feishu apps in a single OpenClaw instance
- Per-Sender Tool Policy — Fine-grained tool permissions based on message sender identity
Install
# npm
openclaw plugins install @xzq-xu/feishu
# GitHub (for testing)
openclaw plugins install github:xzq-xu/openclaw-plugin-feishuConfigure
Edit ~/.openclaw/openclaw.json:
{
"channels": {
"feishu": {
"enabled": true,
"appId": "cli_xxx",
"appSecret": "xxx",
"domain": "feishu",
"dmPolicy": "pairing",
"groupPolicy": "open"
}
}
}Or use environment variables (takes precedence if config values are empty):
export FEISHU_APP_ID="cli_xxx"
export FEISHU_APP_SECRET="xxx"Configuration Options
| Field | Type | Default | Description |
| ---------------- | ----------------------------------------- | ------------- | ------------------------------------------------------ |
| enabled | boolean | false | Enable/disable the channel |
| appId | string | - | Feishu App ID |
| appSecret | string | - | Feishu App Secret |
| domain | "feishu" | "lark" | "feishu" | API domain (China / International) |
| dmPolicy | "open" | "pairing" | "allowlist" | "pairing" | DM access policy |
| allowFrom | string[] | [] | User IDs allowed for DM (when dmPolicy: "allowlist") |
| groupPolicy | "open" | "allowlist" | "disabled" | "allowlist" | Group chat access policy |
| groupAllowFrom | string[] | [] | Group IDs allowed (when groupPolicy: "allowlist") |
| requireMention | boolean | true | Require @mention in groups |
Media Options
| Field | Type | Default | Description |
| ---------- | ------ | ---------------------------- | ------------------------------------------ |
| mediaDir | string | System temp dir (/tmp/...) | Directory to save downloaded media files |
| mediaMaxMb | number | - | Maximum media file size in MB |
The plugin downloads images, files, and audio from Feishu messages. By default, files are saved to the system temp directory (e.g., /tmp/openclaw-feishu-media/). You can customize this:
{
"channels": {
"feishu": {
"mediaDir": "~/.openclaw/media/feishu"
}
}
}Supported media types:
- Images: PNG, JPEG, GIF, WebP
- Files: PDF, DOC, TXT, etc.
- Audio: Opus/Ogg (Feishu voice messages)
Auto-Reply Options (Autonomous Mode)
Enable the bot to autonomously decide whether to respond in group chats, like a human observer.
| Field | Type | Default | Description |
| ---------------------- | ------- | ------- | ----------------------------------------------------- |
| autoReply.enabled | boolean | false | Enable autonomous reply mode |
| autoReply.minMessages| number | 5 | Minimum messages before considering auto-reply |
| autoReply.minTimeMs | number | 60000 | Minimum time window (ms) since first message |
| autoReply.debounceMs | number | 3000 | Wait time (ms) for no new messages before evaluating |
| autoReply.systemHint | string | - | Custom prompt for agent decision (optional) |
How it works:
消息进入
│
┌────────────┴────────────┐
│ │
@bot / 触发? 否
│ │
▼ ▼
┌──────────────┐ 累积到缓冲区
│ 立即触发 │ 新消息重置防抖
│ 必须回复 │ │
└──────────────┘ ▼
防抖到期(3s无新消息)
│
▼
双条件满足?
消息数 >= N 且 时间 >= T
│
┌────┴────┐
满足 不满足
│ │
▼ ▼
交给 Agent 继续等待
让它决定
是否回复- Trigger mode (default): Bot only responds when @mentioned - must reply
- Auto-reply mode: Bot observes group chat and decides whether to respond
Dual conditions (both must be met):
minMessages: Accumulated message count thresholdminTimeMs: Time elapsed since first message in buffer
Debounce: Every new message resets the debounce timer. Only when no new messages arrive for debounceMs, the conditions are checked.
Agent decision: If conditions are met, the agent receives all buffered messages with a system hint. The agent can:
- Reply normally → message sent to group
- Output
[NO_RESPONSE]→ silently dropped, no message sent
{
"channels": {
"feishu": {
"autoReply": {
"enabled": true,
"minMessages": 5,
"minTimeMs": 60000,
"debounceMs": 3000
}
}
}
}Note: @mentions always trigger a response regardless of auto-reply settings.
Streaming Message Options
| Field | Type | Default | Description |
| -------------------------------- | ------- | ------- | ------------------------------------------------ |
| blockStreamingCoalesce.enabled | boolean | false | Enable streaming message coalescing |
| blockStreamingCoalesce.minDelayMs | number | - | Minimum delay before sending coalesced message |
| blockStreamingCoalesce.maxDelayMs | number | - | Maximum delay before forcing message send |
| streamingCard.enabled | boolean | false | Enable streaming card (shows "typing" indicator) |
| streamingCard.title | string | - | Title shown on the streaming card |
| textChunkLimit | number | 4000 | Max characters per message chunk |
Example with streaming enabled:
{
"channels": {
"feishu": {
"enabled": true,
"appId": "cli_xxx",
"appSecret": "xxx",
"blockStreamingCoalesce": {
"enabled": true,
"minDelayMs": 500,
"maxDelayMs": 2000
},
"streamingCard": {
"enabled": true,
"title": "正在思考..."
}
}
}
}Multi-Account Support
Configure multiple Feishu apps in a single OpenClaw instance:
{
"channels": {
"feishu": {
"enabled": true,
"domain": "feishu",
"dmPolicy": "pairing",
"accounts": {
"main": {
"appId": "cli_main_xxx",
"appSecret": "secret1",
"botName": "Main Bot"
},
"support": {
"appId": "cli_support_xxx",
"appSecretFile": "~/.secrets/support-bot.txt",
"botName": "Support Bot",
"dmPolicy": "allowlist",
"allowFrom": ["ou_admin1", "ou_admin2"]
}
}
}
}
}Each account inherits from the base config and can override any setting. The appSecretFile option allows reading secrets from a file instead of storing them in config.
Per-Sender Tool Policy (toolsBySender)
Configure tool permissions based on message sender within groups:
{
"channels": {
"feishu": {
"groups": {
"oc_your_group_id": {
"requireMention": false,
"toolsBySender": {
"张三": { "deny": ["shell", "write_file"] },
"ou_admin_id": { "allow": ["*"] },
"*": { "allow": ["web_search", "feishu_card"] }
}
}
}
}
}
}Resolution priority: toolsBySender[senderId] > toolsBySender[senderName] > tools > toolsBySender["*"]
Agent Tools
The plugin registers the following tools that AI agents can use:
feishu_card
Send rich interactive cards with structured content:
{
"tool": "feishu_card",
"args": {
"title": "Status Report",
"titleTemplate": "green",
"elements": [
{ "tag": "markdown", "content": "**Task**: Completed\n**Duration**: 3.2s" },
{ "tag": "hr" },
{ "tag": "action", "actions": [
{ "tag": "button", "text": { "tag": "plain_text", "content": "View Details" }, "type": "primary", "url": "https://example.com" }
]}
]
}
}Supported elements:
div— Text block with plain_text or lark_mdhr— Horizontal dividermarkdown— Markdown content blocknote— Footnote with nested elementsaction— Button row (supportsurlfor links,type: default/primary/danger)column_set— Multi-column layoutimg— Image with img_key
Header colors: blue, wathet, turquoise, green, yellow, orange, red, carmine, violet, purple, indigo, grey
feishu_list_messages
Retrieve message history from a chat:
{
"tool": "feishu_list_messages",
"args": {
"chatId": "oc_xxx",
"pageSize": 20
}
}How It Works
Human-like Batch Processing
Unlike typical bots that respond to each message immediately, this plugin processes messages like a human would:
- Startup Window (10s): When the bot connects, it buffers all incoming messages
- Trigger Detection:
@mention(or other triggers) signals the bot should respond - Context Gathering: Bot reads ALL buffered messages, not just the trigger
- Single Response: Bot responds once with full conversation context
Example: Bot was offline, 5 messages arrive:
User A: "Let's discuss the project timeline"
User B: "@bot what do you think?" ← trigger
User A: "Budget is around $100k"
User C: "@bot please summarize" ← trigger
User A: "Deadline is next Monday"
OLD behavior: Bot responds twice (to each @mention), missing context
NEW behavior: Bot sees all 5 messages, understands full context, responds ONCEMention Handling
Non-bot mentions are preserved with their open_id, enabling the Agent to @ users in responses:
Inbound message: "Hi @张三 what do you think?"
Parsed content: "Hi @[张三](ou_xxx) what do you think?"
ParsedMessage.mentions: [{ name: "张三", openId: "ou_xxx" }]
Agent response: "I agree with @[张三](ou_xxx)'s point..."
Sent to Feishu: "I agree with <at user_id="ou_xxx">张三</at>'s point..."Bot mentions (@bot) are stripped completely to reduce noise.
Extensible Trigger System
The @mention is just the default trigger. The architecture supports:
- Keyword triggers: Respond when specific words appear
- Scheduled triggers: Periodic check-ins
- Custom triggers: Implement the
Triggerinterface
// src/core/triggers/index.ts
export interface Trigger {
name: string;
check(ctx: TriggerContext): boolean;
}Feishu App Setup
- Go to Feishu Open Platform
- Create a self-built app
- Enable permissions:
im:message,im:chat,contact:user.base:readonly - Events → Use Long Connection mode
- Subscribe to event:
im.message.receive_v1 - Get App ID and App Secret from Credentials page
- Publish the app
Changelog
See CHANGELOG.md for version history.
License
Acknowledgments
Inspired by samzong/moltbot-channel-feishu.
