Skip to content

fix(dingtalk): support dingtalk-stream 0.24+ and oapi webhooks#11257

Closed
kevinskysunny wants to merge 1 commit into
NousResearch:mainfrom
kevinskysunny:fix/dingtalk-stream-024-compat
Closed

fix(dingtalk): support dingtalk-stream 0.24+ and oapi webhooks#11257
kevinskysunny wants to merge 1 commit into
NousResearch:mainfrom
kevinskysunny:fix/dingtalk-stream-024-compat

Conversation

@kevinskysunny

Copy link
Copy Markdown
Contributor

Summary

  • accept both api.dingtalk.com and oapi.dingtalk.com session webhook origins
  • await DingTalkStreamClient.start() directly for dingtalk-stream 0.24+
  • update the incoming handler to use async process() and convert CallbackMessage.data into ChatbotMessage

Root cause

Hermes' DingTalk adapter assumes an older dingtalk-stream API shape:

  • start() is treated as blocking and wrapped in asyncio.to_thread(...)
  • handler process() is synchronous and expects a ChatbotMessage
  • session webhook validation only allows https://api.dingtalk.com/...

With dingtalk-stream==0.24.3, this caused:

  • RuntimeWarning: coroutine 'DingTalkStreamClient.start' was never awaited
  • inbound handling mismatch for callback payloads
  • dropped session webhooks when DingTalk returned https://oapi.dingtalk.com/..., which then caused replies to fail with No session_webhook available

Validation

Reproduced on a live Hermes gateway using DingTalk and dingtalk-stream==0.24.3.
After this patch, DingTalk replies resumed working in the user's environment.

@kevinskysunny

Copy link
Copy Markdown
Contributor Author

Additional reproduction details from the live gateway where this was diagnosed:

Reproduction path

  1. Hermes gateway was upgraded/restarted from a live environment that already had DingTalk configured.
  2. dingtalk-stream==0.24.3 was present in the runtime venv.
  3. Gateway service came back up and reported DingTalk transport as connected.
  4. A fresh DingTalk DM (for example hi) reached Hermes, but Hermes did not reply.

Observed symptoms

  • The gateway could look healthy at a transport level (connected) while end-to-end DingTalk replies were still broken.
  • Logs showed:
    • RuntimeWarning: coroutine 'DingTalkStreamClient.start' was never awaited
    • Send failed: No session_webhook available. Reply must follow an incoming message.
    • Fallback send also failed: No session_webhook available. Reply must follow an incoming message.

Why the second error happened

The environment received inbound session webhooks under https://oapi.dingtalk.com/... rather than only https://api.dingtalk.com/....
Because the regex only accepted api.dingtalk.com, session_webhook was not persisted from the inbound message, so Hermes could receive the message but failed when trying to reply on that same conversation.

Validation after patch

After applying this patch on the same live environment and restarting hermes-gateway, a fresh DingTalk message immediately got a normal Hermes reply again.

This is why the patch includes both:

  • the async compatibility changes for dingtalk-stream 0.24+
  • the broader webhook host validation for both api and oapi

@teknium1

Copy link
Copy Markdown
Contributor

Merged via #11471 (#11471). Your commit was cherry-picked onto current main with your authorship preserved in git log (c3d2895). Thanks for the clean, minimal fix — this was exactly the right shape for the SDK change.

E2E testing against real dingtalk-stream==0.24.3 surfaced one follow-up: _extract_text() was still written for the pre-0.20 payload shape (message.text changed from dict → TextContent dataclass, and rich text moved to message.rich_text_content.rich_text_list). Added that fix on top so every received message doesn't come in as the literal string 'TextContent(content=...)'. Tests added for both the new and legacy shapes.

@teknium1 teknium1 closed this Apr 17, 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.

2 participants