feat: cancel reply flow with cleanup & remove AI reply confirmation
- send_text now returns message_id for tracking - Reply prompt includes cancel hint; typing '取消'/'/cancel' clears it - Prompt message auto-deleted after reply sent or cancelled - AI reply suggestions always send immediately on tap (no confirm step) - Removed _confirm_keyboard, CALLBACK_CONFIRM_REPLY handler
This commit is contained in:
@@ -42,11 +42,22 @@ def send_summary(tg_cfg: TelegramConfig, chat_id: str, summary_text: str,
|
|||||||
|
|
||||||
|
|
||||||
@retry()
|
@retry()
|
||||||
def send_text(tg_cfg: TelegramConfig, chat_id: str, text: str):
|
def send_text(tg_cfg: TelegramConfig, chat_id: str, text: str) -> int:
|
||||||
_tg_req(tg_cfg, "sendMessage", {
|
result = _tg_req(tg_cfg, "sendMessage", {
|
||||||
"chat_id": chat_id,
|
"chat_id": chat_id,
|
||||||
"text": text,
|
"text": text,
|
||||||
})
|
})
|
||||||
|
return result["result"]["message_id"]
|
||||||
|
|
||||||
|
|
||||||
|
def delete_message(tg_cfg: TelegramConfig, chat_id: str, msg_id: int):
|
||||||
|
try:
|
||||||
|
_tg_req(tg_cfg, "deleteMessage", {
|
||||||
|
"chat_id": chat_id,
|
||||||
|
"message_id": msg_id,
|
||||||
|
})
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def poll_telegram(tg_cfg: TelegramConfig, cfg: Config, last_update_id: int) -> int:
|
def poll_telegram(tg_cfg: TelegramConfig, cfg: Config, last_update_id: int) -> int:
|
||||||
@@ -110,7 +121,6 @@ CALLBACK_VIEW_SUMM = "s"
|
|||||||
CALLBACK_REPLY = "r"
|
CALLBACK_REPLY = "r"
|
||||||
CALLBACK_AI_REPLY = "a"
|
CALLBACK_AI_REPLY = "a"
|
||||||
CALLBACK_SELECT_REPLY = "sr"
|
CALLBACK_SELECT_REPLY = "sr"
|
||||||
CALLBACK_CONFIRM_REPLY = "cr"
|
|
||||||
CALLBACK_CANCEL = "c"
|
CALLBACK_CANCEL = "c"
|
||||||
|
|
||||||
|
|
||||||
@@ -147,17 +157,6 @@ def _ai_reply_keyboard(msg_id: int, suggestions: list) -> dict:
|
|||||||
return {"inline_keyboard": kb}
|
return {"inline_keyboard": kb}
|
||||||
|
|
||||||
|
|
||||||
def _confirm_keyboard(msg_id: int, idx: int) -> dict:
|
|
||||||
return {
|
|
||||||
"inline_keyboard": [
|
|
||||||
[
|
|
||||||
{"text": "确认发送", "callback_data": f"{CALLBACK_CONFIRM_REPLY}|{msg_id}|{idx}"},
|
|
||||||
{"text": "取消", "callback_data": CALLBACK_CANCEL},
|
|
||||||
],
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# ── Telegram API ────────────────────────────────────────
|
# ── Telegram API ────────────────────────────────────────
|
||||||
|
|
||||||
def _tg_req(tg_cfg: TelegramConfig, method: str, payload: dict) -> dict:
|
def _tg_req(tg_cfg: TelegramConfig, method: str, payload: dict) -> dict:
|
||||||
@@ -213,8 +212,11 @@ def _handle_callback(tg_cfg: TelegramConfig, cfg: Config, cb: dict):
|
|||||||
})
|
})
|
||||||
|
|
||||||
elif action == CALLBACK_REPLY and ctx:
|
elif action == CALLBACK_REPLY and ctx:
|
||||||
_conversations[chat_id] = {"state": "awaiting_reply", "msg_id": msg_id}
|
prompt_msg_id = send_text(tg_cfg, str(chat_id), "请输入你的回复内容(或发送 \"取消\" 取消):")
|
||||||
send_text(tg_cfg, str(chat_id), "请输入你的回复内容:")
|
_conversations[chat_id] = {
|
||||||
|
"state": "awaiting_reply", "msg_id": msg_id,
|
||||||
|
"prompt_msg_id": prompt_msg_id,
|
||||||
|
}
|
||||||
|
|
||||||
elif action == CALLBACK_AI_REPLY and ctx:
|
elif action == CALLBACK_AI_REPLY and ctx:
|
||||||
suggestions = ctx["summary_data"].get("reply_suggestions", [])
|
suggestions = ctx["summary_data"].get("reply_suggestions", [])
|
||||||
@@ -230,8 +232,7 @@ def _handle_callback(tg_cfg: TelegramConfig, cfg: Config, cb: dict):
|
|||||||
|
|
||||||
text = "*AI 建议回复,请选择:*\n\n"
|
text = "*AI 建议回复,请选择:*\n\n"
|
||||||
for i, s in enumerate(suggestions, 1):
|
for i, s in enumerate(suggestions, 1):
|
||||||
tag = "✅ 无需确认" if s.get("is_simple") else "📝 需确认后发送"
|
text += f"{i}\\. {_escape(s['text'])}\n"
|
||||||
text += f"{i}\\. {_escape(s['text'])}\n *{tag}*\n"
|
|
||||||
_tg_req(tg_cfg, "editMessageText", {
|
_tg_req(tg_cfg, "editMessageText", {
|
||||||
"chat_id": chat_id, "message_id": msg_id,
|
"chat_id": chat_id, "message_id": msg_id,
|
||||||
"text": text, "parse_mode": "MarkdownV2",
|
"text": text, "parse_mode": "MarkdownV2",
|
||||||
@@ -247,32 +248,16 @@ def _handle_callback(tg_cfg: TelegramConfig, cfg: Config, cb: dict):
|
|||||||
if idx >= len(suggestions):
|
if idx >= len(suggestions):
|
||||||
return
|
return
|
||||||
sel = suggestions[idx]
|
sel = suggestions[idx]
|
||||||
|
|
||||||
if sel.get("is_simple"):
|
|
||||||
_do_send_reply(tg_cfg, cfg, chat_id, msg_id, ctx, sel["text"])
|
_do_send_reply(tg_cfg, cfg, chat_id, msg_id, ctx, sel["text"])
|
||||||
send_text(tg_cfg, str(chat_id), f"✅ 已发送: {sel['text']}")
|
send_text(tg_cfg, str(chat_id), f"✅ 已发送: {sel['text']}")
|
||||||
_conversations.pop(chat_id, None)
|
_conversations.pop(chat_id, None)
|
||||||
else:
|
can_reply = ctx.get("can_reply", True)
|
||||||
_conversations[chat_id]["selected_idx"] = idx
|
|
||||||
_tg_req(tg_cfg, "editMessageText", {
|
_tg_req(tg_cfg, "editMessageText", {
|
||||||
"chat_id": chat_id, "message_id": msg_id,
|
"chat_id": chat_id, "message_id": msg_id,
|
||||||
"text": f"确认发送此回复?\n\n{_escape(sel['text'])}",
|
"text": ctx["summary_text"], "parse_mode": "MarkdownV2",
|
||||||
"parse_mode": "MarkdownV2",
|
"reply_markup": _summary_keyboard(can_reply),
|
||||||
"reply_markup": _confirm_keyboard(msg_id, idx),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
elif action == CALLBACK_CONFIRM_REPLY and ctx:
|
|
||||||
idx = int(parts[2])
|
|
||||||
conv = _conversations.get(chat_id)
|
|
||||||
if not conv or conv.get("msg_id") != msg_id:
|
|
||||||
return
|
|
||||||
suggestions = conv.get("ai_suggestions", [])
|
|
||||||
if idx >= len(suggestions):
|
|
||||||
return
|
|
||||||
_do_send_reply(tg_cfg, cfg, chat_id, msg_id, ctx, suggestions[idx]["text"])
|
|
||||||
send_text(tg_cfg, str(chat_id), "✅ 回复已发送!")
|
|
||||||
_conversations.pop(chat_id, None)
|
|
||||||
|
|
||||||
elif action == CALLBACK_CANCEL:
|
elif action == CALLBACK_CANCEL:
|
||||||
_conversations.pop(chat_id, None)
|
_conversations.pop(chat_id, None)
|
||||||
if ctx:
|
if ctx:
|
||||||
@@ -292,13 +277,26 @@ def _handle_message(tg_cfg: TelegramConfig, cfg: Config, msg: dict):
|
|||||||
if not conv or conv.get("state") != "awaiting_reply":
|
if not conv or conv.get("state") != "awaiting_reply":
|
||||||
return
|
return
|
||||||
|
|
||||||
ctx = _email_contexts.get(conv["msg_id"])
|
prompt_msg_id = conv.get("prompt_msg_id")
|
||||||
if not ctx:
|
chat_id_str = str(chat_id)
|
||||||
|
|
||||||
|
if text in ("取消", "/cancel"):
|
||||||
|
if prompt_msg_id:
|
||||||
|
delete_message(tg_cfg, chat_id_str, prompt_msg_id)
|
||||||
_conversations.pop(chat_id, None)
|
_conversations.pop(chat_id, None)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
ctx = _email_contexts.get(conv["msg_id"])
|
||||||
|
if not ctx:
|
||||||
|
_conversations.pop(chat_id, None)
|
||||||
|
if prompt_msg_id:
|
||||||
|
delete_message(tg_cfg, chat_id_str, prompt_msg_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
if prompt_msg_id:
|
||||||
|
delete_message(tg_cfg, chat_id_str, prompt_msg_id)
|
||||||
_do_send_reply(tg_cfg, cfg, chat_id, conv["msg_id"], ctx, text)
|
_do_send_reply(tg_cfg, cfg, chat_id, conv["msg_id"], ctx, text)
|
||||||
send_text(tg_cfg, str(chat_id), "✅ 回复已发送!")
|
send_text(tg_cfg, chat_id_str, "✅ 回复已发送!")
|
||||||
_conversations.pop(chat_id, None)
|
_conversations.pop(chat_id, None)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user