import { z } from "zod"; // ======================= DingTalk Config Schema ======================= /** 群聊策略 */ export type DingTalkGroupPolicy = "open" | "allowlist" | "disabled"; /** 单个群组的独立配置 Schema */ export const DingTalkGroupConfigSchema = z.object({ /** 工具策略 */ tools: z.object({ allow: z.array(z.string()).optional(), deny: z.array(z.string()).optional(), }).optional(), /** 是否启用该群 */ enabled: z.boolean().optional(), /** 群内发送者白名单 */ allowFrom: z.array(z.union([z.string(), z.number()])).optional(), /** 群级系统提示词 */ systemPrompt: z.string().optional(), }).strict(); export type DingTalkGroupConfig = z.infer; /** * 单个钉钉账户配置 Schema * 每个账户对应一个钉钉企业机器人应用 */ export const DingTalkAccountConfigSchema = z.object({ /** 账户名称 */ name: z.string().optional(), /** 是否启用 */ enabled: z.boolean().optional(), /** 钉钉应用 AppKey */ clientId: z.string().optional(), /** 钉钉应用 AppSecret */ clientSecret: z.string().optional(), /** 允许的发送者白名单(单聊),默认 ["*"] 允许所有人 */ allowFrom: z.array(z.union([z.string(), z.number()])).optional(), /** 群聊策略:open=允许所有群, allowlist=白名单, disabled=禁止群聊 */ groupPolicy: z.enum(["open", "allowlist", "disabled"]).optional(), /** 群聊白名单(openConversationId 列表),groupPolicy=allowlist 时生效 */ groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(), /** 按群 ID 的独立配置 */ groups: z.record(z.string(), DingTalkGroupConfigSchema).optional(), }); export type DingTalkAccountConfig = z.infer; /** * 钉钉渠道配置 Schema * * 支持两种配置格式(兼容旧版单账户 + 新版多账户): * * 单账户(旧版兼容): * ```json * { "channels": { "ddingtalk": { "clientId": "xxx", "clientSecret": "yyy" } } } * ``` * * 多账户(新版): * ```json * { "channels": { "ddingtalk": { * "accounts": { * "bot1": { "clientId": "xxx", "clientSecret": "yyy" }, * "bot2": { "clientId": "aaa", "clientSecret": "bbb" } * }, * "defaultAccount": "bot1" * } } } * ``` * * 顶层字段作为所有账户的默认值,账户级字段可覆盖。 */ export const DingTalkConfigSchema = DingTalkAccountConfigSchema.extend({ /** 多账户配置字典,key 为 accountId */ accounts: z.record(z.string(), DingTalkAccountConfigSchema).optional(), /** 默认账户 ID(多账户时指定) */ defaultAccount: z.string().optional(), }); export type DingTalkConfig = z.infer; // ======================= Resolved Account Type ======================= /** * 解析后的钉钉账户配置 */ export interface ResolvedDingTalkAccount { /** 账户 ID */ accountId: string; /** 账户名称 */ name?: string; /** 是否启用 */ enabled: boolean; /** 钉钉应用 AppKey */ clientId: string; /** 钉钉应用 AppSecret */ clientSecret: string; /** Token 来源 */ tokenSource: "config" | "none"; /** 允许的发送者白名单(单聊),默认 ["*"] 允许所有人 */ allowFrom: Array; /** 群聊策略 */ groupPolicy: DingTalkGroupPolicy; /** 群聊白名单 */ groupAllowFrom: Array; /** 按群 ID 的独立配置 */ groups: Record; } // ======================= Message Types ======================= /** * 会话类型 */ export type ConversationType = "1" | "2"; // 1: 单聊, 2: 群聊 /** * 消息类型 */ export type MessageType = "text" | "picture" | "richText" | "markdown" | "file" | "audio" | "video"; // ======================= 消息内容类型 ======================= /** 图片消息内容 */ export interface PictureContent { downloadCode?: string; pictureDownloadCode?: string; height?: number; width?: number; extension?: string; } /** 音频消息内容 */ export interface AudioContent { downloadCode?: string; /** 语音时长(秒) */ duration?: number; /** 文件扩展名,如 amr */ extension?: string; mediaId?: string; /** 语音转文字结果 */ recognition?: string; } /** 视频消息内容 */ export interface VideoContent { downloadCode?: string; /** 视频时长(秒) */ duration?: number; /** 文件扩展名,如 mp4 */ extension?: string; mediaId?: string; videoType?: string; width?: number; height?: number; } /** 文件消息内容 */ export interface FileContent { downloadCode?: string; /** 文件名 */ fileName?: string; /** 文件大小(字节) */ fileSize?: number; /** 文件扩展名 */ extension?: string; spaceId?: string; mediaId?: string; } // ======================= 富文本消息类型 ======================= /** 富文本元素类型 */ export type RichTextElementType = "text" | "picture"; /** 富文本元素 - 文本 */ export interface RichTextTextElement { /** 文本元素可能没有 type 字段,或 type 为 "text" */ type?: "text"; /** 文本内容 */ text: string; } /** 富文本元素 - 图片 */ export interface RichTextPictureElement { type: "picture"; /** 下载码 */ downloadCode?: string; /** 备选下载码字段 */ pictureDownloadCode?: string; /** 图片宽度 */ width?: number; /** 图片高度 */ height?: number; /** 文件扩展名 */ extension?: string; } /** 富文本元素联合类型 */ export type RichTextElement = RichTextTextElement | RichTextPictureElement; /** 富文本消息内容 */ export interface RichTextContent { richText: RichTextElement[]; } /** 消息内容联合类型 */ export type MessageContent = PictureContent | AudioContent | VideoContent | FileContent | RichTextContent; /** * 钉钉机器人消息数据(来自 Stream 回调) */ export interface DingTalkMessageData { conversationId: string; conversationType: ConversationType; chatbotCorpId: string; chatbotUserId: string; msgId: string; msgtype: MessageType; createAt: string; senderNick: string; senderStaffId: string; senderCorpId: string; robotCode: string; isInAtList: boolean; sessionWebhook?: string; sessionWebhookExpiredTime?: string; text?: { content: string; }; /** 媒体消息内容(图片、语音、视频、文件) */ content?: MessageContent; atUsers?: Array<{ dingtalkId: string; staffId?: string; }>; // ---- 群聊特有字段 ---- /** 群名称(群聊时存在) */ conversationTitle?: string; /** 群会话 ID(群聊时存在,用于主动发消息) */ openConversationId?: string; /** 发送者是否群管理员 */ isAdmin?: boolean; } /** * Webhook 响应 */ export interface WebhookResponse { errcode: number; errmsg?: string; } // ======================= 回复消息体类型 ======================= /** @ 配置 */ export interface AtConfig { atUserIds?: string[]; atMobiles?: string[]; isAtAll?: boolean; } /** 回复消息体 - 文本 */ export interface TextReplyBody { msgtype: "text"; text: { content: string; }; at?: AtConfig; } /** 回复消息体 - Markdown */ export interface MarkdownReplyBody { msgtype: "markdown"; markdown: { title?: string; text: string; }; at?: AtConfig; } /** 回复消息体联合类型 */ export type ReplyBody = TextReplyBody | MarkdownReplyBody;