fix(voice): make play_tts play in VC instead of no-op
play_tts was returning success without playing anything when bot was in a voice channel. Now it calls play_in_voice_channel directly. Simplified skip_double dedup: base adapter handles voice input TTS via play_tts (which now works for VC), runner skips to avoid double.
This commit is contained in:
@@ -695,13 +695,14 @@ class DiscordAdapter(BasePlatformAdapter):
|
||||
) -> SendResult:
|
||||
"""Play auto-TTS audio.
|
||||
|
||||
When the bot is in a voice channel for this chat's guild, skip the
|
||||
file attachment — the gateway runner plays audio in the VC instead.
|
||||
When the bot is in a voice channel for this chat's guild, play
|
||||
directly in the VC instead of sending as a file attachment.
|
||||
"""
|
||||
for gid, text_ch_id in self._voice_text_channels.items():
|
||||
if str(text_ch_id) == str(chat_id) and self.is_in_voice_channel(gid):
|
||||
logger.debug("[%s] Skipping play_tts for %s — VC playback handled by runner", self.name, chat_id)
|
||||
return SendResult(success=True)
|
||||
logger.info("[%s] Playing TTS in voice channel (guild=%d)", self.name, gid)
|
||||
success = await self.play_in_voice_channel(gid, audio_path)
|
||||
return SendResult(success=success)
|
||||
return await self.send_voice(chat_id=chat_id, audio_path=audio_path, **kwargs)
|
||||
|
||||
async def send_voice(
|
||||
|
||||
@@ -2575,18 +2575,9 @@ class GatewayRunner:
|
||||
if has_agent_tts:
|
||||
return False
|
||||
|
||||
# Dedup: base adapter auto-TTS already handles voice input.
|
||||
# Exception: Discord voice channel — play_tts override is a no-op,
|
||||
# so the runner must handle VC playback.
|
||||
skip_double = is_voice_input
|
||||
if skip_double:
|
||||
adapter = self.adapters.get(event.source.platform)
|
||||
guild_id = self._get_guild_id(event)
|
||||
if (guild_id and adapter
|
||||
and hasattr(adapter, "is_in_voice_channel")
|
||||
and adapter.is_in_voice_channel(guild_id)):
|
||||
skip_double = False
|
||||
if skip_double:
|
||||
# Dedup: base adapter auto-TTS already handles voice input
|
||||
# (play_tts plays in VC when connected, so runner can skip).
|
||||
if is_voice_input:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@@ -292,14 +292,14 @@ class TestAutoVoiceReply:
|
||||
|
||||
# -- Discord VC exception: runner must handle --------------------------
|
||||
|
||||
def test_discord_vc_voice_input_runner_fires(self, runner):
|
||||
"""Discord VC + voice input: base play_tts skips (VC override),
|
||||
so runner must handle via play_in_voice_channel."""
|
||||
assert self._call(runner, "all", MessageType.VOICE, in_voice_channel=True) is True
|
||||
def test_discord_vc_voice_input_base_handles(self, runner):
|
||||
"""Discord VC + voice input: base adapter play_tts plays in VC,
|
||||
so runner skips to avoid double playback."""
|
||||
assert self._call(runner, "all", MessageType.VOICE, in_voice_channel=True) is False
|
||||
|
||||
def test_discord_vc_voice_only_runner_fires(self, runner):
|
||||
"""Discord VC + voice_only + voice: runner must handle."""
|
||||
assert self._call(runner, "voice_only", MessageType.VOICE, in_voice_channel=True) is True
|
||||
def test_discord_vc_voice_only_base_handles(self, runner):
|
||||
"""Discord VC + voice_only + voice: base adapter handles."""
|
||||
assert self._call(runner, "voice_only", MessageType.VOICE, in_voice_channel=True) is False
|
||||
|
||||
# -- Edge cases --------------------------------------------------------
|
||||
|
||||
@@ -422,17 +422,23 @@ class TestDiscordPlayTtsSkip:
|
||||
return adapter
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_play_tts_skipped_when_in_vc(self):
|
||||
async def test_play_tts_plays_in_vc_when_connected(self):
|
||||
adapter = self._make_discord_adapter()
|
||||
# Simulate bot in voice channel for guild 111, text channel 123
|
||||
mock_vc = MagicMock()
|
||||
mock_vc.is_connected.return_value = True
|
||||
mock_vc.is_playing.return_value = False
|
||||
adapter._voice_clients[111] = mock_vc
|
||||
adapter._voice_text_channels[111] = 123
|
||||
|
||||
# Mock play_in_voice_channel to avoid actual ffmpeg call
|
||||
async def fake_play(gid, path):
|
||||
return True
|
||||
adapter.play_in_voice_channel = fake_play
|
||||
|
||||
result = await adapter.play_tts(chat_id="123", audio_path="/tmp/test.ogg")
|
||||
# play_tts now plays in VC instead of being a no-op
|
||||
assert result.success is True
|
||||
# send_voice should NOT have been called (no client, would fail)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_play_tts_not_skipped_when_not_in_vc(self):
|
||||
|
||||
Reference in New Issue
Block a user