Skip to content

feat(gateway/dingtalk): env-based config and richer msgtype handling#8960

Closed
sputnicyoji wants to merge 1 commit into
NousResearch:mainfrom
sputnicyoji:feat/dingtalk-env-config-msgtypes
Closed

feat(gateway/dingtalk): env-based config and richer msgtype handling#8960
sputnicyoji wants to merge 1 commit into
NousResearch:mainfrom
sputnicyoji:feat/dingtalk-env-config-msgtypes

Conversation

@sputnicyoji

Copy link
Copy Markdown

Summary

Aligns the DingTalk adapter with the env-var bootstrap pattern other platforms (Telegram/Discord/WhatsApp/BlueBubbles) use, and extends inbound handling beyond the text msgtype.

Environment variables (gateway/config.py)

  • DINGTALK_CLIENT_ID / DINGTALK_CLIENT_SECRET enable the platform
  • DINGTALK_ALLOWED_USERS (comma-separated staff ids) populates the allow-list under extra.allowed_users
  • DINGTALK_HOME_CHANNEL / DINGTALK_HOME_CHANNEL_NAME set the home conversation
  • get_connected_platforms now recognizes DingTalk via extra.client_id

Message-type dispatch (gateway/platforms/dingtalk.py)

DingTalk Stream delivers exactly three chatbot msgtypes: text, picture, richText. Previously anything other than text was silently dropped.

  • pictureMessageType.PHOTO with [图片] placeholder (the download API requires an OpenAPI scope this adapter doesn't call)
  • richText → render each segment: plain text, @user mentions, inline pictures as [图片]
  • unknown types → labelled placeholder [未支持的消息类型: <type>] rather than silent drop
  • _extract_text now takes an explicit msgtype to keep callers honest; falls back to message.message_type when omitted

Test plan

  • 24 tests pass including new dispatch coverage for picture / richText / unsupported / explicit-msgtype paths.
  • Verified locally: sending a picture to the bot no longer silently drops; richText messages render with mentions and image placeholders.

Depends conceptually on #8954 (SDK 0.24 compat) for the TextContent reading path in _read_plain_text, but the static methods are backwards-compatible with older SDKs via hasattr/isinstance fallbacks.

Aligns the DingTalk adapter with the env-var bootstrap pattern other
platforms use, and extends inbound handling beyond the `text` msgtype.

Environment variables (gateway/config.py):
- `DINGTALK_CLIENT_ID` / `DINGTALK_CLIENT_SECRET` enable the platform
- `DINGTALK_ALLOWED_USERS` (comma-separated staff ids) populates the
  allow-list under `extra.allowed_users`
- `DINGTALK_HOME_CHANNEL` / `DINGTALK_HOME_CHANNEL_NAME` set the home
  conversation
- `get_connected_platforms` now recognizes DingTalk via `extra.client_id`

Message-type dispatch (gateway/platforms/dingtalk.py):
- DingTalk Stream delivers exactly three chatbot msgtypes: `text`,
  `picture`, `richText`. Dispatch each explicitly and render unknown
  types as a labelled placeholder rather than silently dropping.
- Picture messages map to `MessageType.PHOTO` with `[图片]` placeholder
  (the download API requires an OpenAPI scope this adapter doesn't call).
- `richText` renders each segment: plain text, `@user` mentions, and
  inline pictures as `[图片]`.
- `_extract_text` now takes an explicit `msgtype` to keep callers
  honest; falls back to `message.message_type` when omitted.

Tests updated to cover each dispatch path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
sputnicyoji pushed a commit to sputnicyoji/hermes-agent that referenced this pull request Apr 13, 2026
Previously the adapter replaced any `picture` or inline `richText` image
with the literal string `[图片]`; vision tools therefore never saw the
actual bytes. This patch exchanges each `downloadCode` for the temporary
CDN URL via `/v1.0/robot/messageFiles/download`, fetches the bytes, and
caches them locally so `MessageEvent.media_urls` points at a real file.

Implementation notes:
- Piggyback on the dingtalk-stream SDK: `register_callback_handler`
  wires `handler.dingtalk_client = stream_client`, which gives us the
  SDK's access-token helper and `get_image_download_url`. Keep the
  handler reference on the adapter (`self._handler`).
- `get_image_download_url` is sync (requests-based) — offload via
  `asyncio.to_thread` to avoid blocking the event loop.
- Failures at any stage (missing code, empty URL, HTTP error) are
  logged but degrade gracefully: the event still dispatches with the
  `[图片]` text placeholder so the agent knows something arrived.
- `message.get_image_list()` handles both `picture` and `richText`
  shapes, so the same path supports inline images in rich messages.
- On successful download the placeholder text is replaced with
  `[图片 × N]` as a short caption alongside the attached media list.

Tests cover: missing handler, single-image success, empty downloadUrl,
partial HTTP failure (continue on remaining codes), and full end-to-end
picture dispatch through `_on_message`.

Depends on NousResearch#8954 (SDK 0.24 async process) and NousResearch#8960 (msgtype dispatch).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
sputnicyoji pushed a commit to sputnicyoji/hermes-agent that referenced this pull request Apr 13, 2026
Previously the adapter replaced any `picture` or inline `richText` image
with the literal string `[图片]`; vision tools therefore never saw the
actual bytes. This patch exchanges each `downloadCode` for the temporary
CDN URL via `/v1.0/robot/messageFiles/download`, fetches the bytes, and
caches them locally so `MessageEvent.media_urls` points at a real file.

Implementation notes:
- Piggyback on the dingtalk-stream SDK: `register_callback_handler`
  wires `handler.dingtalk_client = stream_client`, which gives us the
  SDK's access-token helper and `get_image_download_url`. Keep the
  handler reference on the adapter (`self._handler`).
- `get_image_download_url` is sync (requests-based) — offload via
  `asyncio.to_thread` to avoid blocking the event loop.
- Failures at any stage (missing code, empty URL, HTTP error) are
  logged but degrade gracefully: the event still dispatches with the
  `[图片]` text placeholder so the agent knows something arrived.
- `message.get_image_list()` handles both `picture` and `richText`
  shapes, so the same path supports inline images in rich messages.
- On successful download the placeholder text is replaced with
  `[图片 × N]` as a short caption alongside the attached media list.

Tests cover: missing handler, single-image success, empty downloadUrl,
partial HTTP failure (continue on remaining codes), and full end-to-end
picture dispatch through `_on_message`.

Depends on NousResearch#8954 (SDK 0.24 async process) and NousResearch#8960 (msgtype dispatch).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
sputnicyoji pushed a commit to sputnicyoji/hermes-agent that referenced this pull request Apr 15, 2026
Previously the adapter replaced any `picture` or inline `richText` image
with the literal string `[图片]`; vision tools therefore never saw the
actual bytes. This patch exchanges each `downloadCode` for the temporary
CDN URL via `/v1.0/robot/messageFiles/download`, fetches the bytes, and
caches them locally so `MessageEvent.media_urls` points at a real file.

Implementation notes:
- Piggyback on the dingtalk-stream SDK: `register_callback_handler`
  wires `handler.dingtalk_client = stream_client`, which gives us the
  SDK's access-token helper and `get_image_download_url`. Keep the
  handler reference on the adapter (`self._handler`).
- `get_image_download_url` is sync (requests-based) — offload via
  `asyncio.to_thread` to avoid blocking the event loop.
- Failures at any stage (missing code, empty URL, HTTP error) are
  logged but degrade gracefully: the event still dispatches with the
  `[图片]` text placeholder so the agent knows something arrived.
- `message.get_image_list()` handles both `picture` and `richText`
  shapes, so the same path supports inline images in rich messages.
- On successful download the placeholder text is replaced with
  `[图片 × N]` as a short caption alongside the attached media list.

Tests cover: missing handler, single-image success, empty downloadUrl,
partial HTTP failure (continue on remaining codes), and full end-to-end
picture dispatch through `_on_message`.

Depends on NousResearch#8954 (SDK 0.24 async process) and NousResearch#8960 (msgtype dispatch).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sputnicyoji

Copy link
Copy Markdown
Author

Closing — main already has the env-var config and msgtype handling from this PR (see gateway/config.py env bootstrap and _extract_media in gateway/platforms/dingtalk.py). Thanks!

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