Skip to content

feat: 钉钉历史会话前端展示(只读模式)#6

Merged
robscc merged 5 commits intomainfrom
feat/dingtalk-stream
Mar 13, 2026
Merged

feat: 钉钉历史会话前端展示(只读模式)#6
robscc merged 5 commits intomainfrom
feat/dingtalk-stream

Conversation

@robscc
Copy link
Owner

@robscc robscc commented Mar 13, 2026

Summary

  • 后端: DingTalk _stream_reply 中自动 upsert SessionRecord(channel=dingtalk),确保钉钉会话出现在前端会话列表
  • 前端: 新增 useAllSessions hook 合并 web + dingtalk 会话,SessionPanel / SessionsPage 显示钉钉会话(带蓝色"钉钉"标签),ChatPage 钉钉会话进入只读模式(隐藏输入框、清空按钮、配置面板)
  • 测试: 新增 _ensure_dingtalk_session 单元测试,全部 45 个 channel 测试通过

Test plan

  • 通过钉钉发消息后 DB 出现 channel=dingtalk 的 session 记录
  • 前端 /sessions 和侧边栏能看到钉钉会话(带钉钉标签)
  • 钉钉会话 ChatPage 为只读模式
  • Web 会话功能不受影响
  • pytest tests/unit/test_channels/ -v 全部通过

🤖 Generated with Claude Code

ChenchuanSong and others added 5 commits March 13, 2026 10:41
实现 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>
@robscc robscc merged commit ce8eaa5 into main Mar 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant