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
@@ -95,7 +95,8 @@ class TelegramChat(SQLModel, table=True):
title: str = Field(default="")
chat_type: str = Field(default="private")
username: str = Field(default="")
language_code: str = Field(default="")
language_code: str = Field(default="") # auto-detected from Telegram
language_override: str = Field(default="") # manual override set by user
commands_enabled: bool = Field(default=False)
discovered_at: datetime = Field(default_factory=_utcnow)
@@ -279,6 +280,7 @@ class TargetReceiver(SQLModel, table=True):
name: str = Field(default="")
config: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
receiver_key: str = Field(default="") # dedup key (e.g. chat_id, url, email)
locale: str = Field(default="") # e.g. "en", "ru"; empty = use target default
enabled: bool = Field(default=True)
created_at: datetime = Field(default_factory=_utcnow)