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
@@ -21,12 +21,14 @@ router = APIRouter(prefix="/api/targets/{target_id}/receivers", tags=["target-re
class ReceiverCreate(BaseModel):
name: str = ""
config: dict[str, Any] = {}
locale: str = ""
enabled: bool = True
class ReceiverUpdate(BaseModel):
name: str | None = None
config: dict[str, Any] | None = None
locale: str | None = None
enabled: bool | None = None
@@ -85,6 +87,7 @@ async def create_receiver(
name=body.name,
config=body.config,
receiver_key=key,
locale=body.locale,
enabled=body.enabled,
)
session.add(receiver)
@@ -133,7 +136,8 @@ async def test_receiver(
raise HTTPException(status_code=404, detail="Receiver not found")
from ..services.notifier import _get_test_message
message = _get_test_message(locale, target.type)
effective_locale = getattr(receiver, 'locale', '') or locale
message = _get_test_message(effective_locale, target.type)
return await send_to_receiver(target, dict(receiver.config), message)
@@ -159,6 +163,7 @@ def _response(r: TargetReceiver) -> dict:
"name": r.name,
"config": dict(r.config),
"receiver_key": r.receiver_key,
"locale": getattr(r, 'locale', '') or '',
"enabled": r.enabled,
"created_at": r.created_at.isoformat(),
}