feat: add /voice slash command to Discord + fix cross-platform send_voice
- Register /voice as Discord slash command with mode choices - Fix _send_voice_reply to handle adapters that don't accept metadata parameter (Discord) by inspecting the method signature at runtime
This commit is contained in:
@@ -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~")
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user