feat(bot): support concurrent drafts and reply-based edits with error resilience

This commit is contained in:
2026-09-29 00:48:08 +03:30
parent 09245cdc2c
commit a2e7c27e85
10 changed files with 146 additions and 36 deletions
+34 -11
View File
@@ -75,18 +75,36 @@ async def handle_message(ctx: BotContext, chat_id: int, message: dict) -> None:
if user is None:
return # gate either started linking or is mid-linking
draft = await draft_store.get_active(chat_id)
draft_summary = _draft_summary(draft)
message_id = message.get("message_id")
reply_to = message.get("reply_to_message")
reply_to_msg_id = reply_to.get("message_id") if reply_to else None
target_draft = None
if reply_to_msg_id is not None:
target_draft = await draft_store.get_by_message_id(chat_id, reply_to_msg_id)
if target_draft is None:
await ctx.send(
chat_id,
"⚠️ پیش‌نویس فعال مربوط به این پیام یافت نشد (ممکن است ثبت نهایی، منقضی یا لغو شده باشد).\nبرای ثبت استعلام جدید، پیام خود را بدون ریپلای ارسال فرمایید.",
reply_to=message_id,
)
return
draft_summary = _draft_summary(target_draft) if target_draft else None
result = await understand(settings, text, draft_summary)
intent, patch = result.get("intent"), result.get("patch") or {}
logger.info("chat %s intent=%s conf=%s", chat_id, intent, result.get("confidence"))
logger.info("chat %s intent=%s conf=%s (is_edit=%s)", chat_id, intent, result.get("confidence"), target_draft is not None)
if intent in ("register_quote", "update_draft"):
await quote.handle_entities(ctx, chat_id, user, patch, source)
await quote.handle_entities(
ctx, chat_id, user, patch, source,
incoming_msg_id=message_id,
target_draft=target_draft,
)
elif intent == "commit_draft":
active = await draft_store.get_active(chat_id)
active = target_draft or await draft_store.get_active(chat_id)
if active is None:
await ctx.send(chat_id, NOTHING_TO_COMMIT)
await ctx.send(chat_id, NOTHING_TO_COMMIT, reply_to=message_id)
elif active.kind == draft_store.KIND_QUOTE:
await quote.commit(ctx, chat_id, user, active)
elif active.kind == draft_store.KIND_SUPPLIER:
@@ -94,15 +112,20 @@ async def handle_message(ctx: BotContext, chat_id: int, message: dict) -> None:
elif active.kind == draft_store.KIND_MATERIAL:
await material.commit(ctx, chat_id, user, active)
elif intent == "discard_draft":
active = await draft_store.get_active(chat_id)
active = target_draft or await draft_store.get_active(chat_id)
if active:
await draft_store.set_status(active.id, "cancelled")
ctx.pending.pop(chat_id)
await ctx.send(chat_id, CANCELLED)
if active.review_message_id:
try:
await ctx.delete(chat_id, active.review_message_id)
except Exception:
pass
ctx.pending.pop(chat_id, None)
await ctx.send(chat_id, CANCELLED, reply_to=message_id)
elif intent == "show_draft":
active = await draft_store.get_active(chat_id)
active = target_draft or await draft_store.get_active(chat_id)
if active is None:
await ctx.send(chat_id, NOTHING_TO_COMMIT)
await ctx.send(chat_id, NOTHING_TO_COMMIT, reply_to=message_id)
elif active.kind == draft_store.KIND_QUOTE:
await quote.render(ctx, chat_id, active, fresh=False)
elif active.kind == draft_store.KIND_SUPPLIER: