|
|
@@ -2814,7 +2814,7 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
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 ""
|
|
|
+ message_id = str(msg.id)
|
|
|
if message_id:
|
|
|
self._mark_seen(message_id)
|
|
|
|
|
|
@@ -2843,6 +2843,96 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
|
|
|
return SendResult(success=True, message_id=message_id)
|
|
|
|
|
|
+ async def _send_exec_approval_prompt(self, prompt) -> SendResult:
|
|
|
+ """Post a reaction-driven exec-approval prompt.
|
|
|
+
|
|
|
+ BasePlatformAdapter override
|
|
|
+ """
|
|
|
+ emoji_map = {
|
|
|
+ "once": "✅",
|
|
|
+ "session": "🌀",
|
|
|
+ "always": "♾️",
|
|
|
+ "deny": "❌",
|
|
|
+ }
|
|
|
+ number_emojis = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣"]
|
|
|
+
|
|
|
+ choices = list(prompt.choices)
|
|
|
+ choices_map: dict[str, str] = {}
|
|
|
+ emojis: list[str] = []
|
|
|
+ for i, choice in enumerate(choices):
|
|
|
+ emoji = emoji_map.get(choice)
|
|
|
+ if emoji is None:
|
|
|
+ emoji = number_emojis[i] if i < len(number_emojis) else "🔢"
|
|
|
+ emojis.append(emoji)
|
|
|
+ choices_map[emoji] = choice
|
|
|
+
|
|
|
+ client = await self._get_chatto_client()
|
|
|
+ if client is None:
|
|
|
+ return SendResult(
|
|
|
+ success=False, error="Chatto client not available", retryable=True
|
|
|
+ )
|
|
|
+
|
|
|
+ smart_denied = bool(getattr(prompt, "smart_denied", False))
|
|
|
+ tier = "once"
|
|
|
+ if not smart_denied:
|
|
|
+ if "always" in choices:
|
|
|
+ tier = "always"
|
|
|
+ elif "session" in choices:
|
|
|
+ tier = "session"
|
|
|
+ typed_hints = {
|
|
|
+ "once": "React with ✅ to approve once.",
|
|
|
+ "session": "React with 🌀 to approve for this session.",
|
|
|
+ "always": "React with ♾️ to approve always.",
|
|
|
+ }
|
|
|
+ typed_hint = typed_hints.get(tier, "")
|
|
|
+
|
|
|
+ lines = [str(prompt.text), ""]
|
|
|
+ if typed_hint:
|
|
|
+ lines.append(typed_hint)
|
|
|
+ lines.append("")
|
|
|
+ lines.append("Legend:")
|
|
|
+ for emoji, choice in choices_map.items():
|
|
|
+ lines.append(f"{emoji} {choice}")
|
|
|
+ content = "\n".join(lines)
|
|
|
+
|
|
|
+ try:
|
|
|
+ msg = await client.post_message(
|
|
|
+ room_id=str(prompt.chat_id), body=content
|
|
|
+ )
|
|
|
+ except ChattoError as e:
|
|
|
+ logger.warning("Chatto: send_exec_approval post failed: %s", e)
|
|
|
+ return SendResult(success=False, error=str(e), retryable=True)
|
|
|
+ except Exception as e:
|
|
|
+ logger.warning("Chatto: send_exec_approval post error: %s", e)
|
|
|
+ return SendResult(success=False, error=str(e), retryable=False)
|
|
|
+
|
|
|
+ message_id = str(msg.id)
|
|
|
+ if message_id:
|
|
|
+ self._mark_seen(message_id)
|
|
|
+
|
|
|
+ for emoji in emojis:
|
|
|
+ try:
|
|
|
+ await client.add_reaction(
|
|
|
+ room_id=str(prompt.chat_id),
|
|
|
+ message_event_id=message_id,
|
|
|
+ emoji=emoji,
|
|
|
+ )
|
|
|
+ except Exception:
|
|
|
+ logger.debug(
|
|
|
+ "Chatto: seed reaction %s failed", emoji, exc_info=True
|
|
|
+ )
|
|
|
+
|
|
|
+ approval_prompt = _ApprovalPrompt(
|
|
|
+ session_key=str(prompt.session_key),
|
|
|
+ chat_id=str(prompt.chat_id),
|
|
|
+ message_id=message_id,
|
|
|
+ choices_map=choices_map,
|
|
|
+ expires_at=time.monotonic() + INTERACTIVE_PROMPT_TIMEOUT_SECONDS,
|
|
|
+ )
|
|
|
+ self._approval_prompts[message_id] = approval_prompt
|
|
|
+
|
|
|
+ return SendResult(success=True, message_id=message_id)
|
|
|
+
|
|
|
async def edit_message(
|
|
|
self,
|
|
|
chat_id: str,
|