diff --git a/gateway/platforms/discord.py b/gateway/platforms/discord.py index 47760d23..d472aead 100644 --- a/gateway/platforms/discord.py +++ b/gateway/platforms/discord.py @@ -627,6 +627,23 @@ class DiscordAdapter(BasePlatformAdapter): async def slash_reload_mcp(interaction: discord.Interaction): await self._run_simple_slash(interaction, "/reload-mcp") + @tree.command(name="voice", description="Toggle voice reply mode") + @discord.app_commands.describe(mode="Voice mode: on, off, tts, or status") + @discord.app_commands.choices(mode=[ + discord.app_commands.Choice(name="on — voice reply to voice messages", value="on"), + discord.app_commands.Choice(name="tts — voice reply to all messages", value="tts"), + discord.app_commands.Choice(name="off — text only", value="off"), + discord.app_commands.Choice(name="status — show current mode", value="status"), + ]) + async def slash_voice(interaction: discord.Interaction, mode: str = ""): + await interaction.response.defer(ephemeral=True) + event = self._build_slash_event(interaction, f"/voice {mode}".strip()) + await self.handle_message(event) + try: + await interaction.followup.send("Done~", ephemeral=True) + except Exception as e: + logger.debug("Discord followup failed: %s", e) + @tree.command(name="update", description="Update Hermes Agent to the latest version") async def slash_update(interaction: discord.Interaction): await self._run_simple_slash(interaction, "/update", "Update initiated~") diff --git a/gateway/run.py b/gateway/run.py index 2a20c6fa..18757f93 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -2175,16 +2175,19 @@ class GatewayRunner: adapter = self.adapters.get(event.source.platform) if adapter and hasattr(adapter, "send_voice"): - _thread_md = ( - {"thread_id": event.source.thread_id} - if event.source.thread_id else None - ) - await adapter.send_voice( - event.source.chat_id, - audio_path=ogg_path, - reply_to=event.message_id, - metadata=_thread_md, - ) + send_kwargs: Dict[str, Any] = { + "chat_id": event.source.chat_id, + "audio_path": ogg_path, + "reply_to": event.message_id, + } + if event.source.thread_id: + send_kwargs["metadata"] = {"thread_id": event.source.thread_id} + # Only pass metadata if the adapter accepts it + import inspect + sig = inspect.signature(adapter.send_voice) + if "metadata" not in sig.parameters: + send_kwargs.pop("metadata", None) + await adapter.send_voice(**send_kwargs) try: os.unlink(ogg_path) except OSError: diff --git a/tests/gateway/test_voice_command.py b/tests/gateway/test_voice_command.py index 6825abcf..da84c68b 100644 --- a/tests/gateway/test_voice_command.py +++ b/tests/gateway/test_voice_command.py @@ -229,7 +229,7 @@ class TestSendVoiceReply: mock_adapter.send_voice.assert_called_once() call_args = mock_adapter.send_voice.call_args - assert call_args[0][0] == "123" # chat_id + assert call_args.kwargs.get("chat_id") == "123" @pytest.mark.asyncio async def test_empty_text_after_strip_skips(self, runner):