#12700: fix(tts): deliver WhatsApp voice as opus bubble instead of MP3 (#12672)
size: XS
trusted-contributor
Cluster:
Voice Message Enhancements
## Summary
Fixes #12672
## Problem
`resolveOutputFormat()` in `src/tts/tts.ts` only checked for `"telegram"` when deciding to use opus format, so WhatsApp received MP3 files instead of opus voice bubbles. Similarly, the `shouldVoice` flag only checked for `"telegram"`, so WhatsApp audio was never sent as a voice message — it appeared as a downloadable file attachment.
### Root cause
Two conditions in `src/tts/tts.ts` were missing `"whatsapp"`:
1. **`resolveOutputFormat`** (line 477): only returned `TELEGRAM_OUTPUT` (opus) for `"telegram"`
2. **`shouldVoice`** (line 1546): only set `audioAsVoice=true` for `"telegram"`
## Fix
Added `|| channelId === "whatsapp"` to both conditions so WhatsApp gets the same opus voice treatment as Telegram.
### Before
```typescript
// resolveOutputFormat
if (channelId === "telegram") {
return TELEGRAM_OUTPUT;
}
// shouldVoice
const shouldVoice = channelId === "telegram" && result.voiceCompatible === true;
```
### After
```typescript
// resolveOutputFormat
if (channelId === "telegram" || channelId === "whatsapp") {
return TELEGRAM_OUTPUT;
}
// shouldVoice
const shouldVoice =
(channelId === "telegram" || channelId === "whatsapp") && result.voiceCompatible === true;
```
## Reproduction & Verification
### Before fix (main branch) — Bug reproduced:
```
=== TTS WhatsApp opus format check ===
1. Telegram output format: openai=opus, voiceCompatible=true
✅ PASS: Telegram gets opus + voiceCompatible
2. WhatsApp output format: openai=mp3, voiceCompatible=false
❌ FAIL — BUG CONFIRMED: WhatsApp should get opus + voiceCompatible but gets mp3
3. shouldVoice for telegram: true
✅ PASS: Telegram shouldVoice=true
shouldVoice for whatsapp: false
❌ FAIL — BUG CONFIRMED: WhatsApp shouldVoice is false (should be true)
=== Summary ===
❌ BUG CONFIRMED: WhatsApp TTS gets MP3 format and audioAsVoice=false.
Users receive a downloadable MP3 file instead of an inline voice bubble.
```
### After fix — All verified:
```
=== TTS WhatsApp opus format verification ===
✅ PASS: Telegram → opus
✅ PASS: Telegram → voiceCompatible
✅ PASS: Telegram → .opus extension
✅ PASS: WhatsApp → opus
✅ PASS: WhatsApp → voiceCompatible
✅ PASS: WhatsApp → .opus extension
✅ PASS: Discord → mp3
✅ PASS: Discord → not voiceCompatible
✅ PASS: Slack → mp3
✅ PASS: null → mp3
✅ PASS: undefined → mp3
✅ PASS: shouldVoice(telegram, true) = true
✅ PASS: shouldVoice(whatsapp, true) = true
✅ PASS: shouldVoice(discord, true) = false
✅ PASS: shouldVoice(whatsapp, false) = false
=== Summary: ✅ ALL PASSED ===
```
## Effect on User Experience
**Before fix:**
WhatsApp users receiving TTS audio get a downloadable MP3 file attachment that requires manual tap-to-download and playback. The audio does not appear as an inline voice bubble.
**After fix:**
WhatsApp users receive TTS audio as an inline opus voice bubble (same as Telegram), playable directly in the chat with a single tap.
## Testing
- ✅ 34 tests pass (including new WhatsApp opus regression test)
- ✅ Lint passes (oxlint: 0 warnings, 0 errors)
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This change updates the TTS channel handling so WhatsApp matches Telegram’s “voice note” behavior: `resolveOutputFormat()` now returns the Opus-oriented `TELEGRAM_OUTPUT` for `channelId === "whatsapp"`, and `maybeApplyTtsToPayload()` now sets `audioAsVoice` for WhatsApp when the generated audio is marked `voiceCompatible`. A targeted regression test was added to ensure WhatsApp resolves to Opus output, and the changelog includes an entry documenting the fix for #12672.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk.
- The change is narrowly scoped: it extends an existing Telegram Opus/voice-note path to WhatsApp, adds a regression test for the new behavior, and updates the changelog. No other channel behavior is altered, and the implementation reuses existing constants and code paths.
- No files require special attention
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#21193: fix(tts): send voice messages as Opus bubbles on Telegram
by aris-katkova · 2026-02-19
85.3%
#23572: feat(voice): enable voice note conversation loop for Telegram and W...
by davidrudduck · 2026-02-22
80.0%
#15394: telegram: treat mp3 as voice-compatible when asVoice=true
by SnugMorg · 2026-02-13
79.1%
#7395: fix(whatsapp): strip markdown bold/italic from URLs before sending
by lailoo · 2026-02-02
78.0%
#8052: fix(whatsapp): strip leading whitespace from outbound messages
by FelixFoster · 2026-02-03
77.0%
#4390: fix(whatsapp): allow media from allowlisted groups without groupAllow…
by Sarang19114 · 2026-01-30
76.4%
#9606: fix: pass fileName to WhatsApp document messages
by AytuncYildizli · 2026-02-05
75.9%
#7458: fix: pass filename through to WhatsApp document sends (#7446)
by gavinbmoore · 2026-02-02
75.8%
#9727: fix(whatsapp): retry reconnect loop on initial connection failure
by luizlf · 2026-02-05
75.8%
#13431: feat(whatsapp): add built-in Markdown to WhatsApp format transform
by asklee-klawd · 2026-02-10
75.7%