feat(channels): add DingTalk channel adapter

- Add @qwen-code/channel-dingtalk package with stream-based bot integration
- Support clientId/clientSecret authentication for DingTalk
- Add message deduplication and group chat mention handling
- Update ChannelConfig type to include dingtalk channel type

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
tanzhenxin 2026-03-26 08:03:43 +00:00
parent 1a272a12e9
commit 92c54ff309
9 changed files with 288 additions and 8 deletions

View file

@ -23,7 +23,7 @@ export function findCliEntryPath(): string {
throw new Error('Cannot determine CLI entry path');
}
const SUPPORTED_TYPES = ['telegram', 'weixin'];
const SUPPORTED_TYPES = ['telegram', 'weixin', 'dingtalk'];
export function parseChannelConfig(
name: string,
@ -41,16 +41,31 @@ export function parseChannelConfig(
}
let token = '';
if (channelType !== 'weixin') {
if (channelType !== 'weixin' && channelType !== 'dingtalk') {
if (!rawConfig['token']) {
throw new Error(`Channel "${name}" is missing required field "token".`);
}
token = resolveEnvVars(rawConfig['token'] as string);
}
// DingTalk uses clientId + clientSecret instead of token
let clientId: string | undefined;
let clientSecret: string | undefined;
if (channelType === 'dingtalk') {
if (!rawConfig['clientId'] || !rawConfig['clientSecret']) {
throw new Error(
`Channel "${name}" requires "clientId" and "clientSecret" for DingTalk.`,
);
}
clientId = resolveEnvVars(rawConfig['clientId'] as string);
clientSecret = resolveEnvVars(rawConfig['clientSecret'] as string);
}
return {
type: channelType as ChannelConfig['type'],
token,
clientId,
clientSecret,
senderPolicy:
(rawConfig['senderPolicy'] as ChannelConfig['senderPolicy']) ||
'allowlist',

View file

@ -7,6 +7,7 @@ import { AcpBridge, SessionRouter } from '@qwen-code/channel-base';
import type { ChannelBase, ToolCallEvent } from '@qwen-code/channel-base';
import { TelegramChannel } from '@qwen-code/channel-telegram';
import { WeixinChannel } from '@qwen-code/channel-weixin';
import { DingtalkChannel } from '@qwen-code/channel-dingtalk';
import { findCliEntryPath, parseChannelConfig } from './config-utils.js';
import {
readServiceInfo,
@ -38,6 +39,9 @@ function createChannel(
if (config.type === 'weixin') {
return new WeixinChannel(name, config, bridge, options);
}
if (config.type === 'dingtalk') {
return new DingtalkChannel(name, config, bridge, options);
}
return new TelegramChannel(name, config, bridge, options);
}