feat(bot): support concurrent drafts and reply-based edits with error resilience
This commit is contained in:
@@ -29,15 +29,16 @@ KIND_MATERIAL = "material"
|
||||
# Store
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def create(chat_id: int, kind: str, payload: dict, source: str | None = None) -> BotDraft:
|
||||
async def create(chat_id: int, kind: str, payload: dict, source: str | None = None,
|
||||
source_message_id: int | None = None) -> BotDraft:
|
||||
async with SessionLocal() as session:
|
||||
# one active draft per (chat, kind)
|
||||
await session.execute(
|
||||
update(BotDraft)
|
||||
.where(BotDraft.chat_id == chat_id, BotDraft.kind == kind, BotDraft.status == "active")
|
||||
.values(status="superseded")
|
||||
draft = BotDraft(
|
||||
chat_id=chat_id,
|
||||
kind=kind,
|
||||
payload=payload,
|
||||
source=source,
|
||||
source_message_id=source_message_id,
|
||||
)
|
||||
draft = BotDraft(chat_id=chat_id, kind=kind, payload=payload, source=source)
|
||||
session.add(draft)
|
||||
await session.commit()
|
||||
await session.refresh(draft)
|
||||
@@ -53,6 +54,22 @@ async def get_active(chat_id: int, kind: str | None = None) -> BotDraft | None:
|
||||
return (await session.execute(stmt)).scalar_one_or_none()
|
||||
|
||||
|
||||
async def get_by_message_id(chat_id: int, message_id: int) -> BotDraft | None:
|
||||
"""Find the active draft associated with a given message_id (review card or user source message)."""
|
||||
async with SessionLocal() as session:
|
||||
stmt = (
|
||||
select(BotDraft)
|
||||
.where(
|
||||
BotDraft.chat_id == chat_id,
|
||||
BotDraft.status == "active",
|
||||
(BotDraft.review_message_id == message_id) | (BotDraft.source_message_id == message_id),
|
||||
)
|
||||
.order_by(BotDraft.updated_at.desc())
|
||||
.limit(1)
|
||||
)
|
||||
return (await session.execute(stmt)).scalar_one_or_none()
|
||||
|
||||
|
||||
async def get_by_prefix(draft_prefix: str, chat_id: int | None = None) -> BotDraft | None:
|
||||
"""Resolve the short id used in callback_data (first 8 hex of the UUID)."""
|
||||
async with SessionLocal() as session:
|
||||
@@ -63,12 +80,20 @@ async def get_by_prefix(draft_prefix: str, chat_id: int | None = None) -> BotDra
|
||||
|
||||
|
||||
async def save_payload(draft: BotDraft, missing: dict | None = None,
|
||||
review_message_id: int | None = None) -> None:
|
||||
review_message_id: int | None = None,
|
||||
source_message_id: int | None = None) -> None:
|
||||
async with SessionLocal() as session:
|
||||
values: dict = {
|
||||
"payload": draft.payload,
|
||||
"missing": missing,
|
||||
"review_message_id": review_message_id or draft.review_message_id,
|
||||
}
|
||||
if source_message_id:
|
||||
values["source_message_id"] = source_message_id
|
||||
await session.execute(
|
||||
update(BotDraft)
|
||||
.where(BotDraft.id == draft.id)
|
||||
.values(payload=draft.payload, missing=missing, review_message_id=review_message_id or draft.review_message_id)
|
||||
.values(**values)
|
||||
)
|
||||
await session.commit()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user