feat(telegram,core,backends,storage): telegram frontend with inbox, outbox, drafts and question buttons

This commit is contained in:
hh committed 2026-08-28 17:57:09 +02:00
1 parent 7ae87aeeb8
commit 45a5eb4a94
15 files changed
+2244 -4

No files matched your search

+48
View File
@@ -184,6 +184,52 @@ class Schedule(SQLModel, table=True):
delivered_at: datetime | None = Field(default=None)
class TelegramUpdate(SQLModel, table=True):
"""Inbox of the Telegram frontend (§3.8).
The update is stored before the poll offset moves past it and handled
from here, so a restart neither loses nor duplicates a message.
"""
__tablename__ = "telegram_updates"
update_id: int = Field(primary_key=True, sa_column_kwargs={"autoincrement": False})
payload: dict[str, Any] = Field(
sa_column=Column(JSON().with_variant(JSONB(), "postgresql"), nullable=False)
)
received_at: datetime = Field(default_factory=_utcnow)
processed_at: datetime | None = Field(default=None, index=True)
error: str | None = Field(default=None)
class Delivery(SQLModel, table=True):
"""Outbox (§3.8): a reply is a row first and a Telegram message second.
``status`` walks ``queued -> sent`` with retries on the way, ``failed``
only when Telegram rejects the row for good (unknown thread, blocked
bot). ``dedupe_key`` keeps one row per turn chunk across restarts.
"""
__tablename__ = "deliveries"
id: int | None = Field(default=None, primary_key=True)
conversation_id: int | None = Field(default=None, index=True)
frontend: str = Field(default="telegram", index=True)
chat_id: int
thread_id: int | None = Field(default=None)
text: str
plain: bool = Field(default=False)
turn_id: str | None = Field(default=None)
dedupe_key: str | None = Field(default=None, unique=True)
status: str = Field(default="queued", index=True)
attempts: int = Field(default=0)
next_attempt_at: datetime = Field(default_factory=_utcnow, index=True)
last_error: str | None = Field(default=None)
message_id: int | None = Field(default=None)
created_at: datetime = Field(default_factory=_utcnow)
sent_at: datetime | None = Field(default=None)
class ConversationMessage(SQLModel, table=True):
"""One raw Anthropic-shape message in a conversation's transcript.
@@ -280,8 +326,10 @@ __all__ = [
"Conversation",
"ConversationBinding",
"ConversationMessage",
"Delivery",
"InjectQueueItem",
"Schedule",
"TelegramUpdate",
"Token",
"TranscriptEntry",
"Usage",