Skip to main content

openclaw-dingtalk-channel

DingTalk channel plugin for OpenClaw that enables enterprise bot integration using Stream mode without public IP requirements

الانتقال إلى التثبيت

معلومات المصدر

المستودع
reason-machines/hermes-skills
آخر نشاط في المصدر
١٠ يونيو ٢٠٢٦ في ٠١:١٩
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٥
التفرعات
٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
openclaw-dingtalk-channel
description
DingTalk channel plugin for OpenClaw that enables enterprise bot integration using Stream mode without public IP requirements
triggers
["how do I set up the DingTalk channel for OpenClaw","configure OpenClaw with DingTalk enterprise bot","install the dingtalk plugin for openclaw","set up AI card streaming in DingTalk","configure multi-agent routing in DingTalk channel","troubleshoot OpenClaw DingTalk connection","implement btw bypass mode in DingTalk","handle DingTalk message types in OpenClaw"]
# OpenClaw DingTalk Channel Skill > Skill by [ara.so](https://ara.so) — Hermes Skills collection. This skill provides expertise in configuring and using the **@soimy/dingtalk** channel plugin for OpenClaw, which enables DingTalk enterprise bot integration with Stream mode (no public IP required). ## What It Does The DingTalk channel plugin connects OpenClaw to DingTalk enterprise bots, supporting: - **Stream mode**: No webhook or public IP needed - **Message types**: Text, images, voice, video, files, DingTalk docs/cards - **Reply modes**: Markdown and AI Card (v2 structured blocks with streaming) - **Multi-agent routing**: Bind multiple agents/bots, use `@agent-name` routing - **Bypass mode**: `/btw` for quick answers outside main session lock - **Real-time abort**: Stop AI generation mid-stream - **Device Flow auth**: Scan QR code to auto-register credentials ## Installation ### Prerequisites Requires OpenClaw >= 2026.3.24. ### Install from ClawHub ```bash openclaw plugins install @soimy/dingtalk ``` ### Install from Local Source ```bash git clone https://github.com/soimy/openclaw-channel-dingtalk.git cd openclaw-channel-dingtalk npm install openclaw plugins install -l . ``` ### Enable Plugin Add to OpenClaw configuration: ```json5 { "plugins": { "enabled": true, "allow": ["dingtalk"] } } ``` ## Configuration ### Interactive Setup (Recommended) ```bash openclaw onboard ``` Or configure channels section: ```bash openclaw configure --section channels ``` ### Minimal Manual Configuration ```json5 { "channels": { "dingtalk": { "enabled": true, "clientId": "dingxxxxxx", "clientSecret": "your-app-secret", "dmPolicy": "open", "groupPolicy": "open", "messageType": "markdown" } } } ``` ### Full Configuration Example ```json5 { "channels": { "dingtalk": { "enabled": true, "clientId": process.env.DINGTALK_CLIENT_ID, "clientSecret": process.env.DINGTALK_CLIENT_SECRET, // Access policies "dmPolicy": "open", // "open" | "whitelist" | "blacklist" | "closed" "groupPolicy": "open", // Same options "dmWhitelist": [], // User IDs if dmPolicy is "whitelist" "groupWhitelist": [], // Conversation IDs if groupPolicy is "whitelist" // Message type: "markdown" or "aiCard" "messageType": "aiCard", // AI Card streaming options "aiCardStreamingMode": true, "aiCardStreamingInterval": 800, // ms between chunks // Multi-agent bindings "agentBindings": [ { "agentName": "default", "robotCode": "dingxxxxxx", "isDefault": true }, { "agentName": "analyst", "robotCode": "dingyyyyyyy", "isDefault": false } ], // Routing "enableAtAgentRouting": true, "atAgentRoutingPrefix": "@", // Features "enableBtwBypass": true, "enableRealTimeAbort": true, "abortKeywords": ["停止", "stop", "/stop", "esc"], // Security "maxMessageLength": 10000, "rateLimitPerUser": 20, "rateLimitWindow": 60000 // 1 minute } } } ``` ### Environment Variables ```bash export DINGTALK_CLIENT_ID="dingxxxxxx" export DINGTALK_CLIENT_SECRET="your-app-secret" ``` ## DingTalk App Setup ### Required Permissions In DingTalk Developer Console, configure: 1. **Application Credentials**: Client ID and Client Secret 2. **Stream Mode**: Enable and configure 3. **API Permissions**: - `qyapi_robot_sendmsg` - Send messages - `qyapi_chat_manage` - Group chat management - `contact.User.Read` - Read user info - `im.Chat.Read` - Read conversation info 4. **Event Subscriptions**: - `chat_update_title` - Group name changes - `chat_update_owner` - Owner changes - `im_robot_at_message` - @ mentions - `im_robot_message` - Direct messages ## Key Features & Usage ### Reply Modes #### Markdown Mode Simple text responses with Markdown formatting: ```json5 { "messageType": "markdown" } ``` User sees plain Markdown-formatted text. #### AI Card Mode Rich interactive cards with streaming support: ```json5 { "messageType": "aiCard", "aiCardStreamingMode": true, "aiCardStreamingInterval": 800 } ``` Features: - Structured block rendering - Inline images - Task metadata display - Real-time streaming updates ### /btw Bypass Mode Quick answers outside the main session lock: **User**: `/btw What's the weather?` The message bypasses conversation locking and gets immediate response in a separate session. Prefix is configurable: ```json5 { "enableBtwBypass": true, "btwPrefix": "/btw" // Optional, defaults to "/btw" } ``` ### Multi-Agent & Bot Bindings Bind multiple OpenClaw agents to different DingTalk bots: ```json5 { "agentBindings": [ { "agentName": "default", "robotCode": "dingbot001", "isDefault": true }, { "agentName": "analyst", "robotCode": "dingbot002", "isDefault": false }, { "agentName": "writer", "robotCode": "dingbot003", "isDefault": false } ] } ``` ### @Agent Routing Route to specific agents by mentioning them: **User**: `@analyst analyze Q4 revenue` ```json5 { "enableAtAgentRouting": true, "atAgentRoutingPrefix": "@" } ``` The message routes to the "analyst" agent if bound. ### Real-Time Abort Stop AI generation mid-stream: **User**: `stop` or `停止` or `esc` ```json5 { "enableRealTimeAbort": true, "abortKeywords": ["停止", "stop", "/stop", "esc"] } ``` ### Message Type Handling Plugin automatically handles: - **Text**: Direct processing - **Images**: Extracted and passed to vision-capable models - **Voice**: Transcribed if supported - **Files**: Content extracted when possible - **DingTalk Docs**: Content fetched via API - **Reply/Quote**: Original message context included ## Code Examples ### TypeScript Plugin Development ```typescript import type { ChannelPlugin, InboundMessage, OutboundMessage } from '@openclaw/types'; export class DingTalkChannel implements ChannelPlugin { name = 'dingtalk'; async processInbound(rawMessage: any): Promise<InboundMessage> { const { msgtype, text, senderStaffId, conversationId } = rawMessage; return { channelId: 'dingtalk', userId: senderStaffId, conversationId, content: { type: 'text', text: text?.content || '' }, metadata: { msgId: rawMessage.msgId, timestamp: rawMessage.createAt } }; } async processOutbound(message: OutboundMessage): Promise<void> { const { conversationId, content, metadata } = message; if (this.config.messageType === 'aiCard') { await this.sendAICard(conversationId, content, metadata); } else { await this.sendMarkdown(conversationId, content); } } private async sendAICard( conversationId: string, content: any, metadata?: any ): Promise<void> { const cardData = { cardTemplateId: 'standard', outTrackId: metadata?.trackId, cardData: { cardParamMap: { content: content.text, blocks: this.formatBlocks(content) } } }; await this.client.sendInteractiveCard({ conversationId, ...cardData }); } } ``` ### Handling Streaming Updates ```typescript async streamAIResponse( conversationId: string, generator: AsyncGenerator<string> ): Promise<void> { let buffer = ''; let lastUpdate = 0; const interval = this.config.aiCardStreamingInterval || 800; for await (const chunk of generator) { buffer += chunk; const now = Date.now(); if (now - lastUpdate >= interval) { await this.updateAICard(conversationId, buffer); lastUpdate = now; } } // Final update await this.updateAICard(conversationId, buffer, { finished: true }); } ``` ### Custom Agent Routing Logic ```typescript function resolveAgent(message: InboundMessage): string { const { content, metadata } = message; // Check for @agent mention const atMatch = content.text.match(/@(\w+)\s/); if (atMatch) { const agentName = atMatch[1]; const binding = this.config.agentBindings.find( b => b.agentName === agentName ); if (binding) return agentName; } // Check robot code mapping const robotCode = metadata.robotCode; const binding = this.config.agentBindings.find( b => b.robotCode === robotCode ); return binding?.agentName || 'default'; } ``` ### Implementing Bypass Mode ```typescript function isBtwMessage(text: string): boolean { const prefix = this.config.btwPrefix || '/btw'; return text.trim().startsWith(prefix); } async processBtwMessage(message: InboundMessage): Promise<void> { const prefix = this.config.btwPrefix || '/btw'; const cleanText = message.content.text.replace(prefix, '').trim(); // Create isolated session const btwSession = await this.createBypassSession(message.userId); // Process independently const response = await this.agent.chat(cleanText, { sessionId: btwSession.id, bypass: true }); await this.send(message.conversationId, response); } ``` ## Common Patterns ### Policy-Based Access Control ```typescript function checkAccess( policy: 'open' | 'whitelist' | 'blacklist' | 'closed', identifier: string, whitelist: string[], blacklist: string[] ): boolean { switch (policy) { case 'closed': return false; case 'whitelist': return whitelist.includes(identifier); case 'blacklist': return !blacklist.includes(identifier); case 'open': default: return true; } } // Usage const canDM = checkAccess( config.dmPolicy, message.userId, config.dmWhitelist, config.dmBlacklist ); const canGroup = checkAccess( config.groupPolicy, message.conversationId, config.groupWhitelist, config.groupBlacklist ); ``` ### Rate Limiting ```typescript class RateLimiter { private counts = new Map<string, number[]>(); check(userId: string, limit: number, window: number): boolean { const now = Date.now(); const timestamps = this.counts.get(userId) || []; // Remove old timestamps const recent = timestamps.filter(t => now - t < window); if (recent.length >= limit) { return false; } recent.push(now); this.counts.set(userId, recent); return true; } } // Usage if (!this.rateLimiter.check( message.userId, config.rateLimitPerUser, config.rateLimitWindow )) { throw new Error('Rate limit exceeded'); } ``` ### Error Recovery ```typescript async sendWithRetry( conversationId: string, content: any, maxRetries = 3 ): Promise<void> { for (let i = 0; i < maxRetries; i++) { try { await this.send(conversationId, content); return; } catch (error) { if (i === maxRetries - 1) throw error; // Exponential backoff
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub