feat: telegram commands, app settings, bot polling, webhook handling, UI improvements
Adds telegram bot command system with 13 commands (search, latest, random, etc.), webhook/polling handlers, rate limiting, app settings page, and various UI/UX improvements across all entity pages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -44,6 +44,30 @@ async def migrate_schema(engine: AsyncEngine) -> None:
|
||||
await conn.execute(text(sql))
|
||||
logger.info("Added %s column to event_log table", col)
|
||||
|
||||
# Add commands_config to telegram_bot if missing
|
||||
if not await _has_column("telegram_bot", "commands_config"):
|
||||
await conn.execute(
|
||||
text("ALTER TABLE telegram_bot ADD COLUMN commands_config TEXT DEFAULT '{}'")
|
||||
)
|
||||
logger.info("Added commands_config column to telegram_bot table")
|
||||
|
||||
# Add webhook_path_id to telegram_bot if missing
|
||||
if not await _has_column("telegram_bot", "webhook_path_id"):
|
||||
await conn.execute(
|
||||
text("ALTER TABLE telegram_bot ADD COLUMN webhook_path_id TEXT DEFAULT ''")
|
||||
)
|
||||
logger.info("Added webhook_path_id column to telegram_bot table")
|
||||
# Backfill existing bots with unique IDs
|
||||
import uuid
|
||||
bots = (await conn.execute(text("SELECT id FROM telegram_bot"))).fetchall()
|
||||
for bot in bots:
|
||||
await conn.execute(
|
||||
text("UPDATE telegram_bot SET webhook_path_id = :wid WHERE id = :bid"),
|
||||
{"wid": uuid.uuid4().hex, "bid": bot[0]},
|
||||
)
|
||||
if bots:
|
||||
logger.info("Backfilled webhook_path_id for %d existing bots", len(bots))
|
||||
|
||||
# Add date_only_format to template_config if missing
|
||||
if not await _has_column("template_config", "date_only_format"):
|
||||
await conn.execute(
|
||||
@@ -51,6 +75,20 @@ async def migrate_schema(engine: AsyncEngine) -> None:
|
||||
)
|
||||
logger.info("Added date_only_format column to template_config table")
|
||||
|
||||
# Add update_mode to telegram_bot if missing
|
||||
if not await _has_column("telegram_bot", "update_mode"):
|
||||
await conn.execute(
|
||||
text("ALTER TABLE telegram_bot ADD COLUMN update_mode TEXT DEFAULT 'polling'")
|
||||
)
|
||||
logger.info("Added update_mode column to telegram_bot table")
|
||||
|
||||
# Add memory_source to tracking_config if missing
|
||||
if not await _has_column("tracking_config", "memory_source"):
|
||||
await conn.execute(
|
||||
text("ALTER TABLE tracking_config ADD COLUMN memory_source TEXT DEFAULT 'albums'")
|
||||
)
|
||||
logger.info("Added memory_source column to tracking_config table")
|
||||
|
||||
# Add collection_name and shared to tracker_state if missing
|
||||
if not await _has_column("tracker_state", "collection_name"):
|
||||
await conn.execute(
|
||||
@@ -190,7 +228,10 @@ async def migrate_tracker_targets(engine: AsyncEngine) -> None:
|
||||
):
|
||||
target_bot_map[tgt[0]] = bot_id
|
||||
except Exception:
|
||||
pass
|
||||
logger.warning(
|
||||
"Failed to match bot token for target %s", tgt[0],
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
# Create TrackerTarget rows
|
||||
import json
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any
|
||||
from uuid import uuid4
|
||||
|
||||
from sqlmodel import JSON, Column, Field, SQLModel
|
||||
|
||||
@@ -44,6 +45,9 @@ class TelegramBot(SQLModel, table=True):
|
||||
icon: str = Field(default="")
|
||||
bot_username: str = Field(default="")
|
||||
bot_id: int = Field(default=0)
|
||||
webhook_path_id: str = Field(default_factory=lambda: uuid4().hex)
|
||||
update_mode: str = Field(default="polling") # "polling" or "webhook"
|
||||
commands_config: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
|
||||
created_at: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
|
||||
@@ -106,6 +110,7 @@ class TrackingConfig(SQLModel, table=True):
|
||||
|
||||
# Memory mode
|
||||
memory_enabled: bool = Field(default=False)
|
||||
memory_source: str = Field(default="albums") # "albums" or "native"
|
||||
memory_times: str = Field(default="09:00")
|
||||
memory_collection_mode: str = Field(default="combined")
|
||||
memory_limit: int = Field(default=10)
|
||||
@@ -221,13 +226,22 @@ class EventLog(SQLModel, table=True):
|
||||
__tablename__ = "event_log"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
tracker_id: int | None = Field(default=None, foreign_key="tracker.id")
|
||||
tracker_id: int | None = Field(default=None, foreign_key="tracker.id", index=True)
|
||||
tracker_name: str = Field(default="")
|
||||
provider_id: int | None = Field(default=None)
|
||||
provider_id: int | None = Field(default=None, index=True)
|
||||
provider_name: str = Field(default="")
|
||||
event_type: str
|
||||
event_type: str = Field(index=True)
|
||||
collection_id: str
|
||||
collection_name: str
|
||||
assets_count: int = Field(default=0)
|
||||
details: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
|
||||
created_at: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
|
||||
class AppSetting(SQLModel, table=True):
|
||||
"""Key-value app-level settings (admin-configurable)."""
|
||||
|
||||
__tablename__ = "app_setting"
|
||||
|
||||
key: str = Field(primary_key=True)
|
||||
value: str = Field(default="")
|
||||
|
||||
Reference in New Issue
Block a user