# Jarvis-dingtalk-bot **Repository Path**: kain/jarvis-dingtalk-bot ## Basic Information - **Project Name**: Jarvis-dingtalk-bot - **Description**: No description available - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-02-07 - **Last Updated**: 2026-02-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 钉钉机器人服务 (DingTalk Bot Service) 基于钉钉 Stream 模式的企业 AI 机器人后端服务,集成 OpenCode AI 实现智能对话与流式响应。 ## ✨ 特性 - 🔌 **Stream 模式连接** - 使用钉钉 Stream SDK 实现稳定的长连接消息接收 - 🤖 **OpenCode AI 集成** - 对接 OpenCode AI 服务,支持智能代码助手对话 - 🎴 **AI 卡片流式响应** - 使用钉钉 AI 卡片实现流式内容更新,实时展示 AI 思考与回复 - 👥 **多用户支持** - 基于 jobNo 实现用户隔离,每个用户独立的 OpenCode 实例 - 💾 **会话持久化** - 支持 Redis 持久化会话元数据,保证服务重启后会话恢复 - 📝 **智能内容分片** - 自动处理超长回复,分割为多张卡片确保内容完整 - 🔧 **TypeScript 开发** - 完整的类型定义,代码安全可靠 ## 🏗️ 架构设计 ``` ┌─────────────┐ Stream ┌─────────────────┐ HTTP/SSE ┌─────────────┐ │ 钉钉客户端 │ ◄──────────────► │ Bot Service │ ◄───────────────► │ OpenCode │ └─────────────┘ └─────────────────┘ └─────────────┘ │ ▼ ┌─────────────┐ │ Redis │ (可选) └─────────────┘ ``` ### 核心流程 1. 用户在钉钉发送消息 → Stream SDK 接收 2. 根据 `senderStaffId` 获取用户 jobNo 3. 创建/复用 AI 卡片实例 4. 连接对应 jobNo 的 OpenCode 服务 5. 订阅 OpenCode 事件流,实时更新 AI 卡片内容 6. 处理完成后标记卡片为完成状态 ## 🚀 快速开始 ### 环境要求 - Node.js >= 18.0.0 - npm 或 yarn - Redis (可选,用于会话持久化) ### 1. 安装依赖 ```bash npm install ``` ### 2. 配置环境变量 复制 `.env.example` 为 `.env`,并填写配置: ```bash cp .env.example .env ``` 编辑 `.env` 文件: ```env # 钉钉应用凭证(必填) DINGTALK_CLIENT_ID=your_client_id DINGTALK_CLIENT_SECRET=your_client_secret # AI 卡片模板 ID(必填) DINGTALK_CARD_TEMPLATE_ID=your_card_template_id # OpenCode 配置 LOCAL_MODEL=true # true: 使用本地 127.0.0.1:4096,false: 使用远程服务 # AI 卡片配置 CARD_UPDATE_INTERVAL=500 # 流式更新间隔(毫秒) CARD_TIMEOUT=600000 # 卡片超时时间(毫秒,默认 10 分钟) CARD_MAX_CONTENT_LENGTH=1000 # 单张卡片最大内容长度 # 日志配置 EVENT_LOG_ENABLED=false # 是否记录 OpenCode 事件日志 # Redis 配置(可选) REDIS_URL=redis://localhost:6379 # 或分别配置 REDIS_HOST=localhost REDIS_PORT=6379 REDIS_PASSWORD=your_password ``` ### 3. 运行服务 开发模式(热重载): ```bash npm run dev ``` 生产模式: ```bash npm run build npm start ``` ## 📁 项目结构 ``` . ├── src/ │ ├── index.ts # 服务入口,初始化 Stream 连接 │ ├── config.ts # 配置管理(环境变量加载) │ ├── bot/ │ │ ├── handler.ts # 消息处理器(AI 卡片逻辑) │ │ └── session.ts # 会话管理(内存 + Redis) │ ├── dingtalk/ │ │ └── dingtalkService.ts # 钉钉 AI 卡片 API 封装 │ ├── opencode/ │ │ ├── OpencodeClient.ts # OpenCode 单客户端封装 │ │ └── OpencodeManager.ts # OpenCode 多客户端管理器 │ ├── types/ │ │ └── index.ts # TypeScript 类型定义 │ └── test/ # 测试文件 ├── data/ │ ├── dingtalk_card_message.md # 钉钉卡片消息格式参考 │ └── opencode_event_data.md # OpenCode 事件数据样例 ├── logs/ # 运行时日志目录 ├── design.md # 设计文档 ├── package.json ├── tsconfig.json └── README.md ``` ## 🔧 核心模块说明 ### BotService (`src/index.ts`) 服务主入口,负责: - 初始化 Stream 客户端连接 - 注册消息回调处理器 - 处理卡片回调事件 ### MessageHandler (`src/bot/handler.ts`) 消息处理核心,功能包括: - 创建和管理 AI 卡片实例 - 连接 OpenCode 并订阅事件 - 处理流式响应内容更新 - 自动分片处理超长内容 - 超时保护与错误处理 ### SessionManager (`src/bot/session.ts`) 会话管理器,特性: - 基于 `jobNo + conversationId` 的会话隔离 - 内存缓存 + Redis 持久化双层存储 - 自动清理过期会话(30 分钟超时) - 保存 OpenCode sessionId 用于会话恢复 ### DingtalkCardService (`src/dingtalk/dingtalkService.ts`) 钉钉 AI 卡片 API 封装: - `createAndDeliverCard()` - 创建并投放卡片 - `streamingUpdate()` - 流式更新卡片内容 - `finalizeCard()` - 完成卡片更新 - `errorCard()` - 标记卡片错误状态 ### OpencodeManager (`src/opencode/OpencodeManager.ts`) OpenCode 客户端管理器: - 按 jobNo 管理多个独立客户端实例 - 支持本地/远程模式切换 - 自动创建和复用会话 ### OpencodeClient (`src/opencode/OpencodeClient.ts`) 单个 OpenCode 客户端封装: - 会话创建与管理 - 消息发送与事件订阅 - 事件日志记录(可配置) ## 📨 消息类型判断 钉钉机器人回调消息中可以通过以下字段判断消息来源: ### conversationType(会话类型) | 值 | 含义 | |---|---| | `'1'` | 单聊 — 消息是用户单独发给机器人的 | | `'2'` | 群聊 — 消息是在群聊中发送的 | ### atUsers(被@的用户) - 群聊中@机器人时,`atUsers` 字段会包含机器人的信息 - 单聊或未@机器人时,`atUsers` 为空数组或 `undefined` ### 判断逻辑示例 ```typescript function getMessageType(message: DingTalkCallbackMessage) { if (message.conversationType === '1') { return 'direct'; // 单聊:用户直接私信机器人 } // 群聊场景 const isBotMentioned = message.atUsers?.some( u => u.dingtalkId === message.chatbotUserId ); if (isBotMentioned || message.isAtAll) { return 'group-mention'; // 群聊中@了机器人 } return 'group-other'; // 群聊但未@机器人(通常不会触发回调) } ``` ## ⚙️ 钉钉应用配置 1. 登录 [钉钉开发者后台](https://open-dev.dingtalk.com/) 2. 创建企业内部应用 3. 获取 Client ID 和 Client Secret 4. 添加「机器人」应用能力,选择 **Stream 模式** 5. 创建 AI 卡片模板,获取模板 ID 6. 发布应用 ## 📦 依赖说明 | 依赖 | 版本 | 用途 | |------|------|------| | `dingtalk-stream` | ^2.1.4 | 钉钉 Stream SDK | | `@alicloud/dingtalk` | ^2.2.38 | 钉钉开放 API | | `@opencode-ai/sdk` | ^1.1.43 | OpenCode AI SDK | | `axios` | ^1.13.4 | HTTP 请求 | | `ioredis` | ^5.3.2 | Redis 客户端 | | `dotenv` | ^16.3.1 | 环境变量加载 | | `uuid` | ^13.0.0 | UUID 生成 | ## 🧪 测试 ```bash # 运行测试 npm test # 测试文件位于 src/test/ 目录 ``` ## 📚 参考文档 - [钉钉 Stream 模式文档](https://opensource.dingtalk.com/developerpedia/docs/learn/stream/overview) - [dingtalk-stream-sdk-nodejs](https://github.com/open-dingtalk/dingtalk-stream-sdk-nodejs) - [钉钉 AI 卡片文档](https://open.dingtalk.com/document/orgapp/ai-card-introduction) - [OpenCode AI SDK](https://github.com/opencode-ai/sdk) ## 📄 License MIT