From 7ee111ff9b055da88feccd4a285c66a269df0a5a Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Tue, 28 Jul 2026 09:25:06 -0700 Subject: [PATCH] fix(dingtalk): don't let richText re-derivation clobber VOICE classification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The msg_type_str == "richText" branch reset msg_type to PHOTO/TEXT after the rich-text item scan had already promoted it (e.g. a native voice item → VOICE), dropping voice notes from the auto-STT path. Only re-derive when the scan left the type at TEXT. Ports the root-cause analysis from PR #38276 (stale, targeted the deleted gateway/platforms/dingtalk.py) onto the live plugin adapter, with regression tests. Refs #38211 #38219 #38276 --- plugins/platforms/dingtalk/adapter.py | 14 +++++++---- tests/gateway/test_dingtalk.py | 34 +++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/plugins/platforms/dingtalk/adapter.py b/plugins/platforms/dingtalk/adapter.py index 2e11f9d85feb..0ad470d1c4c1 100644 --- a/plugins/platforms/dingtalk/adapter.py +++ b/plugins/platforms/dingtalk/adapter.py @@ -920,11 +920,15 @@ class DingTalkAdapter(BasePlatformAdapter): if msg_type_str == "picture" and not media_urls: msg_type = MessageType.PHOTO elif msg_type_str == "richText": - msg_type = ( - MessageType.PHOTO - if any("image" in t for t in media_types) - else MessageType.TEXT - ) + # Only re-derive the type when the rich-text scan above left it + # at TEXT. The scan may already have promoted it to VOICE/AUDIO/ + # VIDEO/DOCUMENT for embedded media items — resetting those here + # dropped native voice notes back to TEXT and skipped STT + # (#38211, #38219; analysis from #38276). + if msg_type == MessageType.TEXT and any( + "image" in t for t in media_types + ): + msg_type = MessageType.PHOTO elif msg_type_str == "audio": # Voice message — DingTalk already provides recognition text. # Do NOT add media_urls here: if audio_paths is non-empty, diff --git a/tests/gateway/test_dingtalk.py b/tests/gateway/test_dingtalk.py index c4ac504512b4..8c0ac5bc1b95 100644 --- a/tests/gateway/test_dingtalk.py +++ b/tests/gateway/test_dingtalk.py @@ -735,6 +735,40 @@ class TestExtractMedia: assert urls == ["dl_voice_abc"] assert mtypes == ["audio"] + def test_richtext_reset_does_not_clobber_voice(self): + """A richText envelope containing a native voice item must stay + VOICE — the ``msg_type_str == "richText"`` re-derivation used to + reset it to TEXT, dropping the voice note from the STT path + (#38211, #38219).""" + from plugins.platforms.dingtalk.adapter import DingTalkAdapter + from gateway.platforms.base import MessageType + + msg = self._msg_with_rich_text( + [{"type": "voice", "downloadCode": "dl_voice_rt"}] + ) + msg.message_type = "richText" + msg_type, urls, mtypes = DingTalkAdapter._extract_media( + DingTalkAdapter, msg + ) + assert msg_type == MessageType.VOICE + assert urls == ["dl_voice_rt"] + assert mtypes == ["audio"] + + def test_richtext_with_image_still_photo(self): + """richText with only an embedded image keeps the PHOTO promotion.""" + from plugins.platforms.dingtalk.adapter import DingTalkAdapter + from gateway.platforms.base import MessageType + + msg = self._msg_with_rich_text( + [{"type": "picture", "downloadCode": "dl_img_rt"}] + ) + msg.message_type = "richText" + msg_type, urls, mtypes = DingTalkAdapter._extract_media( + DingTalkAdapter, msg + ) + assert msg_type == MessageType.PHOTO + assert urls == ["dl_img_rt"] + def test_audio_rich_text_item_stays_audio(self): """Generic audio uploads (e.g. an mp3 the user attached) must NOT be auto-transcribed — they stay MessageType.AUDIO."""