src/process/channels/plugins/dingtalk/README.md
DingTalk bot integration using dingtalk-stream SDK for WebSocket Stream connection.
| File | Purpose |
|---|---|
DingTalkPlugin.ts | Core plugin: connection, AI Card streaming, message sending |
DingTalkAdapter.ts | Message format conversion (DingTalk ↔ Unified) |
DingTalkCards.ts | Static card builders (help, settings, action cards) |
Uses DingTalk's built-in AI Card template (382e4302-551d-4880-bf29-a30acfab2e71.schema) for real-time streaming responses.
PROCESSING (1) → INPUTING (2) → FINISHED (3)
FAILED (5)
1. POST /v1.0/card/instances # Create card
- cardTemplateId, outTrackId
- callbackType: 'STREAM' # Required for streaming
- imGroupOpenSpaceModel / imRobotOpenSpaceModel
2. POST /v1.0/card/instances/deliver # Deliver to chat
- openSpaceId: 'dtv1.card//IM_GROUP.{id}' or 'dtv1.card//IM_ROBOT.{id}'
- userIdType: 1 # Required
- spaceType: 'IM_ROBOT' (not 'ONE_BOX')
3. PUT /v1.0/card/instances # Set INPUTING state (once, before first stream)
- flowStatus: '2'
- sys_full_json_obj: '{"order":["msgContent"]}'
4. PUT /v1.0/card/streaming # Stream content (repeat)
- key: 'msgContent' # Not 'content'
- isFull: true # Full replacement (AionUI sends complete content each time)
- isFinalize: false # Not 'isFinish'
- isError: false
5. PUT /v1.0/card/streaming # Final stream
- Same as above, but isFinalize: true
6. PUT /v1.0/card/instances # Set FINISHED state
- flowStatus: '3'
- msgContent: finalContent
callbackType: 'STREAM' must be set on card creation, otherwise streaming API returns HTTP 500userIdType: 1 is required on delivery, otherwise the card won't appearspaceType must be 'IM_ROBOT' for 1:1 chats (not 'ONE_BOX')key in streaming must be 'msgContent' (not 'content')isFinalize is the correct field name (not 'isFinish')PUT /v1.0/card/instances before the first streaming writeisFull: true is always used because AionUI sends the complete message content on each edit (not incremental deltas)DingTalk User → Stream WebSocket → DingTalkPlugin.handleRobotMessage()
→ ActionExecutor.handleIncomingMessage()
→ ChannelMessageService.sendMessage()
→ Agent (Gemini/ACP/Codex)
→ ChannelEventBus 'finish' event → Promise resolves
→ ActionExecutor sends final editMessage with replyMarkup
→ DingTalkPlugin.editMessage(isFinal=true)
→ streamAICard(isFinalize=true) + finishAICard()
If AI Card creation/streaming fails, the plugin falls back to:
sessionWebhook (cached from incoming messages)/v1.0/robot/oToMessages/batchSend or /v1.0/robot/groupMessages/send)