Merged
Conversation
实现 DingTalk Stream 模式(WebSocket 长连接),无需公网 IP,
服务端主动连接钉钉云,天然穿透内网/NAT 环境。
核心变更:
- channels/dingtalk_stream_worker.py (新建)
- DingTalkStreamWorker: 后台 asyncio 任务管理,断线由 SDK 自动重连
- _run_stream_client: 创建 SDK 适配层 _SdkChatbotHandler(继承 ChatbotHandler)
- _handle_message: 纯函数处理逻辑,解析消息→调用助手→sessionWebhook 回复
- _extract_text: 去除群消息中的 @机器人名 前缀
- _invoke_assistant: 为每条消息创建独立 DB 会话,调用 PersonalAssistant.reply()
- _send_reply: 通过临时 sessionWebhook 异步 POST 回复
- dingtalk_stream_worker: 模块单例,供 main.py lifespan 调用
- main.py: lifespan 中启动/停止 dingtalk_stream_worker
- channels/dingtalk.py: 重构 verify_signature(空 secret 快速通过;使用 hmac.HMAC)
- channels/channel.py: 修复 incoming.user_input 错误(应为 incoming.text),传 db 给 Assistant
- channels/__init__.py: 导出 DingTalkStreamWorker
- pyproject.toml: dingtalk-sdk → dingtalk-stream>=0.24.0
测试(52 个全部通过):
- test_dingtalk.py: 新增签名验证、Webhook URL 构建等用例
- test_dingtalk_stream_worker.py (新建): 覆盖 Worker 生命周期、消息处理、
@前缀去除、sessionWebhook 回复、异常容错等全路径
配置(需在 .env 或 ~/.nimo/config.yaml 中填写):
DINGTALK_ENABLED=true
DINGTALK_APP_KEY=<ClientID / AppKey>
DINGTALK_APP_SECRET=<ClientSecret / AppSecret>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
websockets 在系统存在 SOCKS 代理时需要 python-socks 才能建立 WebSocket 连接。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1. personal_assistant.reply(): toolkit._tools → _get_tool_names(toolkit) - Toolkit 类仅暴露 tools(公有),直接访问 _tools 抛 AttributeError - 与 reply_stream() 保持一致,统一使用已有的 _get_tool_names() 辅助函数 2. dingtalk_stream_worker: 添加 _ConnectLogSystemHandler - WebSocket 建立后 SDK 首条系统消息触发连接成功日志 ✅ - 仅记录一次,后续心跳不重复打印 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 移除 _ConnectLogSystemHandler:DingTalk 不发初始系统消息,该方案无效 - 新增 _watch_connection():每 200ms 轮询 client.websocket is not None 首次建连后打印 "WebSocket 连接成功 ✅" 日志,超时 30s 打 WARNING - 实测:endpoint 后约 400ms 出现连接成功日志 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 后端: DingTalk _stream_reply 中 upsert SessionRecord,确保钉钉会话出现在会话列表 - 前端: 新增 useAllSessions hook 合并 web + dingtalk 会话 - 前端: SessionPanel/SessionsPage 显示钉钉会话并带蓝色"钉钉"标签 - 前端: ChatPage 钉钉会话只读模式,隐藏输入框和配置面板 - 测试: 新增 _ensure_dingtalk_session 单元测试 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
_stream_reply中自动 upsertSessionRecord(channel=dingtalk),确保钉钉会话出现在前端会话列表useAllSessionshook 合并 web + dingtalk 会话,SessionPanel / SessionsPage 显示钉钉会话(带蓝色"钉钉"标签),ChatPage 钉钉会话进入只读模式(隐藏输入框、清空按钮、配置面板)_ensure_dingtalk_session单元测试,全部 45 个 channel 测试通过Test plan
🤖 Generated with Claude Code