|
@@ -31,6 +31,7 @@ import mimetypes
|
|
|
import os
|
|
import os
|
|
|
import re
|
|
import re
|
|
|
import tempfile
|
|
import tempfile
|
|
|
|
|
+import time
|
|
|
from collections import deque
|
|
from collections import deque
|
|
|
from collections.abc import AsyncIterator, Callable, Iterable
|
|
from collections.abc import AsyncIterator, Callable, Iterable
|
|
|
from contextlib import asynccontextmanager
|
|
from contextlib import asynccontextmanager
|
|
@@ -2763,6 +2764,85 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
"""
|
|
"""
|
|
|
return _normalise_outbound_text(content)
|
|
return _normalise_outbound_text(content)
|
|
|
|
|
|
|
|
|
|
+ async def send_clarify(
|
|
|
|
|
+ self,
|
|
|
|
|
+ chat_id: str,
|
|
|
|
|
+ question: str,
|
|
|
|
|
+ choices: list[str] | None,
|
|
|
|
|
+ clarify_id: str,
|
|
|
|
|
+ session_key: str,
|
|
|
|
|
+ metadata: dict[str, Any] | None = None,
|
|
|
|
|
+ ) -> SendResult:
|
|
|
|
|
+ """Post a reaction-driven clarify prompt.
|
|
|
|
|
+
|
|
|
|
|
+ When ``choices`` is empty/None or exceeds 9 entries (the number emojis
|
|
|
|
|
+ only cover 1-9), delegate to the base-class text fallback. Otherwise
|
|
|
|
|
+ post a numbered message, seed one reaction per option plus ``✏️``
|
|
|
|
|
+ (Other), register the prompt in both registries, and return.
|
|
|
|
|
+
|
|
|
|
|
+ BasePlatformAdapter override
|
|
|
|
|
+ """
|
|
|
|
|
+ if not choices or len(choices) > 9:
|
|
|
|
|
+ return await super().send_clarify(
|
|
|
|
|
+ chat_id, question, choices, clarify_id, session_key, metadata=metadata
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ client = await self._get_chatto_client()
|
|
|
|
|
+ if client is None:
|
|
|
|
|
+ return SendResult(
|
|
|
|
|
+ success=False, error="Chatto client not available", retryable=True
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ number_emojis = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣"]
|
|
|
|
|
+ emojis = [number_emojis[i] for i in range(len(choices))]
|
|
|
|
|
+ other_emoji = "✏️"
|
|
|
|
|
+
|
|
|
|
|
+ lines = [f"❓ {question}", ""]
|
|
|
|
|
+ for i, choice in enumerate(choices):
|
|
|
|
|
+ lines.append(f"{emojis[i]} {choice}")
|
|
|
|
|
+ lines.append(f"{other_emoji} Other (type your answer)")
|
|
|
|
|
+ content = "\n".join(lines)
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ msg = await client.post_message(
|
|
|
|
|
+ room_id=str(chat_id), body=content
|
|
|
|
|
+ )
|
|
|
|
|
+ except ChattoError as e:
|
|
|
|
|
+ logger.warning("Chatto: send_clarify post failed: %s", e)
|
|
|
|
|
+ return SendResult(success=False, error=str(e), retryable=True)
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ logger.warning("Chatto: send_clarify post error: %s", e)
|
|
|
|
|
+ return SendResult(success=False, error=str(e), retryable=False)
|
|
|
|
|
+
|
|
|
|
|
+ message_id = str(msg.id) if getattr(msg, "id", None) else ""
|
|
|
|
|
+ if message_id:
|
|
|
|
|
+ self._mark_seen(message_id)
|
|
|
|
|
+
|
|
|
|
|
+ for emoji in [*emojis, other_emoji]:
|
|
|
|
|
+ try:
|
|
|
|
|
+ await client.add_reaction(
|
|
|
|
|
+ room_id=str(chat_id),
|
|
|
|
|
+ message_event_id=message_id,
|
|
|
|
|
+ emoji=emoji,
|
|
|
|
|
+ )
|
|
|
|
|
+ except Exception:
|
|
|
|
|
+ logger.debug(
|
|
|
|
|
+ "Chatto: seed reaction %s failed", emoji, exc_info=True
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ prompt = _ClarifyPrompt(
|
|
|
|
|
+ clarify_id=clarify_id,
|
|
|
|
|
+ choices=list(choices),
|
|
|
|
|
+ session_key=session_key,
|
|
|
|
|
+ chat_id=str(chat_id),
|
|
|
|
|
+ message_id=message_id,
|
|
|
|
|
+ expires_at=time.monotonic() + INTERACTIVE_PROMPT_TIMEOUT_SECONDS,
|
|
|
|
|
+ )
|
|
|
|
|
+ self._clarify_prompts[message_id] = prompt
|
|
|
|
|
+ self._clarify_id_to_message[clarify_id] = message_id
|
|
|
|
|
+
|
|
|
|
|
+ return SendResult(success=True, message_id=message_id)
|
|
|
|
|
+
|
|
|
async def edit_message(
|
|
async def edit_message(
|
|
|
self,
|
|
self,
|
|
|
chat_id: str,
|
|
chat_id: str,
|