feat(dingtalk): proactive messaging, media pipeline, card throttle + quoted-reply extraction#12769
Conversation
a0ccfd0 to
1dbfa9f
Compare
|
good change |
3b342e6 to
15806cd
Compare
…tool media - Add DingTalk access-token management (_dingtalk_fetch_access_token, _dingtalk_fetch_oapi_token) with process-wide cache, 5 min safety margin, and asyncio.Lock for concurrent safety. - Add _dingtalk_upload_media() — upload local files to /media/upload (legacy OAPI token) with mime auto-detection and 20 MB guard. - Add _dingtalk_classify_chat_id() — route on chat_id shape: 'cidXXX==' → group /groupMessages/send, plain staffId or 'user:<staffId>' → 1:1 /oToMessages/batchSend. - Add dingtalk_send_proactive() orchestrator — text + media delivery. - In send(), fall back to dingtalk_send_proactive() when no session webhook is cached (proactive/cron/cross-platform delivery). - Fix /sethome DM chat_id: persist 'user:<staffId>' instead of DM conversation_id so proactive DM delivery works (gateway/run.py). - Enhance send_message_tool.py: MEDIA:<path> tag extraction, native DingTalk/Feishu routing with media_files support. - Update test assertions for proactive-send error path.
15806cd to
8221966
Compare
|
Hi @alt-glitch, thanks so much for the quick labels and review across all 8 PRs — really appreciate the attention! 🙏 I wanted to provide some context on these PRs and how they relate to the earlier series (#14333–#14336). Who we areWe're from the DingTalk (钉钉) team at Alibaba — the same team that builds and maintains the DingTalk open platform, the dingtalk-stream SDK, and the AI Card APIs that these PRs integrate with. We've been running Hermes internally with DingTalk as the primary gateway, and these PRs represent battle-tested fixes and features from our production deployment. Why the re-submissionThe original 4 PRs (#14333–#14336 by @PeterGuy326) were stacked — each one included all changes from prior PRs, making them hard to review independently. With 4.6k+ open PRs in this repo, stacked PRs are easily buried or create merge conflicts. These 8 PRs are intentionally fine-grained replacements, each:
PR mapping
Regarding overlaps with other PRs
Next stepsOnce these 8 PRs are reviewed, we'll close the original stacked PRs (#14333–#14336). We're also happy to:
Looking forward to getting these merged — they'll significantly improve the DingTalk adapter's reliability and feature parity. Let us know if you'd prefer a different organization! 🚀 |
老哥 你们出个官方的hermes 分支吧 官方的钉钉插件不支持发送图片太难受了 |
|
Would be great if this could be merged soon — as a daily user, I really need this feature. |
Resolves pyproject.toml conflict in [project.optional-dependencies]:
- Align to upstream's exact-pin policy (2026-05-12 supply-chain tightening)
- Keep PR-added media-parsing deps with explicit pins:
python-docx==1.2.0, pdfplumber==0.11.9, openpyxl==3.1.5
- Adopt upstream pins for shared deps:
dingtalk-stream==0.24.3, alibabacloud-dingtalk==2.2.42,
qrcode==7.4.2, lark-oapi==1.5.3
Regenerated uv.lock; uv lock --check passes.
Tests: tests/gateway/test_dingtalk.py 76/76,
tests/hermes_cli/test_dingtalk_auth.py 15/15.
austinpickett
left a comment
There was a problem hiding this comment.
Please fix merge conflicts and use .github/PULL_REQUEST_TEMPLATE.md
Hi maintainers,
First off — huge thanks for building Hermes Agent. The design quality and the way platform adapters are factored out have made integration genuinely pleasant on our side.
This PR comes from the official DingTalk (钉钉) team at Alibaba. For context, DingTalk is one of the largest enterprise collaboration platforms in China, serving hundreds of millions of users across organizations of every size, and we've been integrating Hermes as a first-class option inside our workspace. Everything in this PR is upstream-worthy work we did during that integration — we'd love to give it back rather than carry a long-lived fork.
Summary
Major capability uplift for the DingTalk platform adapter — a 15-dimension enhancement covering proactive messaging, media pipeline, ACL, throttling, and inbound quoted-reply handling. This brings the DingTalk integration to production-grade for first-class workspace deployments. It also fixes a latent
websocketsmonkey-patch that brokedingtalk-streamreconnects when multiple adapters ran in the same gateway process.Nine commits, scoped:
HOME_CHANNELactually reachable at runtime.async defwrapper was globally monkey-patchingwebsockets.connect, returning a coroutine instead of theconnectobject. That brokeasync with websockets.connect(uri)inside thedingtalk-streamSDK. Fix: keep the wrapper sync-returning.repliedMsgrecursive parser, with placeholders for msgTypes DingTalk stream delivers metadata-only for (picture/file/audio/video/interactiveCard).repliedMsgfor interactiveCards with only 4 metadata fields (msgId/senderId/msgType/createdAt), no content. We are pushing the DingTalk IM team to populatecontent.text/content.markdown/content.title/content.summaryserver-side. Once shipped, Hermes picks it up with zero code change — the extractor already reads those keys in priority order.Test plan
pytest tests/gateway/test_dingtalk.py— 17/17 passing (13 original + 4 forward-compat)send_message_tool(markdown, text, card)[图片],[interactiveCard消息], etc.) match project copy conventionsNotes for reviewers
text → markdown → title → summary) is our guess. Happy to adjust based on what the IM team actually ships.