fix(gateway): gate [[audio_as_voice]] to audio files so images aren't sent as documents

[[audio_as_voice]] is message-global but was applied to every media file in a
message. A non-audio file flagged is_voice is excluded from the embedded-photo
batch and falls through to send_document, so an image in a message that also
carries a voice note arrives as a file attachment instead of an inline photo.
Gate the voice flag on the file extension so one message can carry an inline
image AND a voice bubble. Also wrap the path append in try/except so a crafted
~\x00 path is skipped rather than aborting extraction of all attachments.
This commit is contained in:
Lumina 2026-06-12 06:10:53 -04:00 committed by Teknium
parent 0fd0161dfd
commit 6710ce97c4

View file

@ -4197,6 +4197,15 @@ class BasePlatformAdapter(ABC):
for match in media_pattern.finditer(scan_content):
path = _normalize_media_tag_path(match.group("path"))
if path:
# ``[[audio_as_voice]]`` is message-global, but it must only
# affect audio files. Tagging a non-audio file (image, video,
# document) as is_voice taints it: an image flagged is_voice is
# excluded from the embedded-photo batch and falls through to
# send_document, arriving as a file attachment instead of an
# inline photo. Gating on the extension lets one message carry
# an embedded image AND a voice bubble together.
ext = os.path.splitext(path)[1].lower()
is_voice = has_voice_tag and ext in _AUDIO_EXTS
try:
expanded = os.path.expanduser(path)
except (OSError, RuntimeError, ValueError):
@ -4205,7 +4214,7 @@ class BasePlatformAdapter(ABC):
continue
if expanded not in seen_paths:
seen_paths.add(expanded)
media.append((expanded, has_voice_tag))
media.append((expanded, is_voice))
for match in MEDIA_EXTENSIONLESS_TAG_RE.finditer(scan_content):
path = _normalize_media_tag_path(match.group("path"))
@ -4216,7 +4225,8 @@ class BasePlatformAdapter(ABC):
continue
safe = resolved[0]
if safe not in seen_paths:
media.append((safe, has_voice_tag))
_safe_ext = os.path.splitext(safe)[1].lower()
media.append((safe, has_voice_tag and _safe_ext in _AUDIO_EXTS))
seen_paths.add(safe)
# Remove the delivered MEDIA tags from the user-visible text. Mask a