mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-08 13:12:08 +00:00
fix(discord): handle expired slash defer interactions
This commit is contained in:
parent
acfefa4fda
commit
b9d9b8aad6
2 changed files with 67 additions and 2 deletions
|
|
@ -1626,6 +1626,31 @@ class DiscordAdapter(BasePlatformAdapter):
|
|||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _is_discord_unknown_interaction(exc: BaseException) -> bool:
|
||||
"""True for Discord's expired interaction token error."""
|
||||
code = getattr(exc, "code", None)
|
||||
if code is None:
|
||||
data = getattr(exc, "data", None)
|
||||
if isinstance(data, dict):
|
||||
code = data.get("code")
|
||||
try:
|
||||
code = int(code)
|
||||
except (TypeError, ValueError):
|
||||
code = None
|
||||
|
||||
status = getattr(exc, "status", None)
|
||||
response = getattr(exc, "response", None)
|
||||
if status is None and response is not None:
|
||||
status = getattr(response, "status", None) or getattr(response, "status_code", None)
|
||||
try:
|
||||
status = int(status)
|
||||
except (TypeError, ValueError):
|
||||
status = None
|
||||
|
||||
message = str(exc).lower()
|
||||
return code == 10062 or (status == 404 and "unknown interaction" in message)
|
||||
|
||||
def _command_sync_mutation_interval_seconds(self) -> float:
|
||||
return _DISCORD_COMMAND_SYNC_MUTATION_INTERVAL_SECONDS
|
||||
|
||||
|
|
@ -3964,9 +3989,22 @@ class DiscordAdapter(BasePlatformAdapter):
|
|||
if not await self._check_slash_authorization(interaction, command_text):
|
||||
return
|
||||
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
deferred_response = False
|
||||
try:
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
deferred_response = True
|
||||
except Exception as e:
|
||||
if not self._is_discord_unknown_interaction(e):
|
||||
raise
|
||||
logger.warning(
|
||||
"[Discord] slash %s: interaction expired before defer. "
|
||||
"Executing command anyway, skipping interaction followup.",
|
||||
command_text,
|
||||
)
|
||||
event = self._build_slash_event(interaction, command_text)
|
||||
await self.handle_message(event)
|
||||
if not deferred_response:
|
||||
return
|
||||
try:
|
||||
if followup_msg:
|
||||
await interaction.edit_original_response(content=followup_msg)
|
||||
|
|
|
|||
|
|
@ -158,6 +158,34 @@ async def test_registers_native_restart_slash_command(adapter):
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_simple_slash_executes_when_defer_interaction_expired(adapter):
|
||||
class UnknownInteraction(Exception):
|
||||
status = 404
|
||||
code = 10062
|
||||
|
||||
interaction = SimpleNamespace(
|
||||
channel=_FakeTextChannel(channel_id=123, name="general"),
|
||||
channel_id=123,
|
||||
guild_id=456,
|
||||
user=SimpleNamespace(id=42, name="Jezza", display_name="Jezza"),
|
||||
response=SimpleNamespace(defer=AsyncMock(side_effect=UnknownInteraction("Unknown interaction"))),
|
||||
edit_original_response=AsyncMock(),
|
||||
delete_original_response=AsyncMock(),
|
||||
)
|
||||
adapter.handle_message = AsyncMock()
|
||||
|
||||
await adapter._run_simple_slash(interaction, "/reset", "Session reset~")
|
||||
|
||||
interaction.response.defer.assert_awaited_once_with(ephemeral=True)
|
||||
adapter.handle_message.assert_awaited_once()
|
||||
event = adapter.handle_message.await_args.args[0]
|
||||
assert event.text == "/reset"
|
||||
assert event.source.chat_id == "123"
|
||||
interaction.edit_original_response.assert_not_awaited()
|
||||
interaction.delete_original_response.assert_not_awaited()
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Auto-registration from COMMAND_REGISTRY
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -1117,4 +1145,3 @@ def test_register_skill_command_autocomplete_filters_by_name_and_description(ada
|
|||
# (covered in other tests). The autocomplete filter itself is exercised
|
||||
# via direct function call in the real-discord integration path.
|
||||
assert skill_cmd.callback is not None
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue