feat: Receiver OOP hierarchy with per-receiver locale resolution

- Introduce Receiver base class + typed subclasses (TelegramReceiver,
  WebhookReceiver, EmailReceiver, etc.) in core/notifications/receiver.py
- Dispatcher uses typed Receiver objects instead of raw dicts, with
  per-receiver locale-aware template rendering
- load_link_data resolves locale from TelegramChat.language_override at
  load time: TargetReceiver.locale || chat.language_override || chat.language_code
- Add language_override field to TelegramChat (separate from auto-detected
  language_code), with per-chat commands toggle and command dispatch using
  override language
- Add locale field to TargetReceiver for explicit per-receiver overrides
This commit is contained in:
2026-03-23 21:20:31 +03:00
parent b3b6c31c4d
commit 1cfa72888c
16 changed files with 334 additions and 94 deletions
@@ -207,6 +207,21 @@ async def migrate_schema(engine: AsyncEngine) -> None:
)
logger.info("Added language_code column to telegram_chat table")
# Add language_override to telegram_chat if missing
if not await _has_column(conn, "telegram_chat", "language_override"):
await conn.execute(
text("ALTER TABLE telegram_chat ADD COLUMN language_override TEXT DEFAULT ''")
)
logger.info("Added language_override column to telegram_chat table")
# Add locale to target_receiver if missing
if await _has_table(conn, "target_receiver"):
if not await _has_column(conn, "target_receiver", "locale"):
await conn.execute(
text("ALTER TABLE target_receiver ADD COLUMN locale TEXT DEFAULT ''")
)
logger.info("Added locale column to target_receiver table")
# Add commands_enabled to telegram_chat if missing (default disabled)
if not await _has_column(conn, "telegram_chat", "commands_enabled"):
await conn.execute(