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:
@@ -15,6 +15,7 @@ from ..database.models import (
|
||||
NotificationTracker,
|
||||
NotificationTrackerTarget,
|
||||
ServiceProvider,
|
||||
TargetReceiver,
|
||||
TemplateConfig,
|
||||
TemplateSlot,
|
||||
TrackingConfig,
|
||||
@@ -206,8 +207,39 @@ async def test_notification_tracker_target(
|
||||
if not target:
|
||||
raise HTTPException(status_code=404, detail="Target not found")
|
||||
|
||||
# Resolve effective locale from first receiver (explicit locale → TelegramChat lang)
|
||||
effective_locale = locale
|
||||
recv_result = await session.exec(
|
||||
select(TargetReceiver).where(
|
||||
TargetReceiver.target_id == target.id,
|
||||
TargetReceiver.enabled == True,
|
||||
)
|
||||
)
|
||||
first_recv = recv_result.first()
|
||||
if first_recv:
|
||||
recv_locale = getattr(first_recv, 'locale', '') or ''
|
||||
if not recv_locale and target.type == "telegram":
|
||||
# Resolve from TelegramChat language_override/language_code
|
||||
from ..database.models import TelegramBot, TelegramChat
|
||||
chat_id = str(first_recv.config.get("chat_id", ""))
|
||||
bot_id = target.config.get("bot_id")
|
||||
if chat_id and bot_id:
|
||||
chat_row = (await session.exec(
|
||||
select(TelegramChat).where(
|
||||
TelegramChat.bot_id == bot_id,
|
||||
TelegramChat.chat_id == chat_id,
|
||||
)
|
||||
)).first()
|
||||
if chat_row:
|
||||
recv_locale = (
|
||||
getattr(chat_row, 'language_override', '') or
|
||||
getattr(chat_row, 'language_code', '') or ''
|
||||
)
|
||||
if recv_locale:
|
||||
effective_locale = recv_locale[:2].lower()
|
||||
|
||||
if test_type == "basic":
|
||||
r = await send_test_notification(target, locale=locale)
|
||||
r = await send_test_notification(target, locale=effective_locale)
|
||||
return {"target": target.name, **r}
|
||||
|
||||
# For periodic/scheduled/memory — fetch real data from provider
|
||||
@@ -226,7 +258,7 @@ async def test_notification_tracker_target(
|
||||
select(TemplateSlot).where(
|
||||
TemplateSlot.config_id == template_config.id,
|
||||
TemplateSlot.slot_name == slot_name,
|
||||
TemplateSlot.locale == "en",
|
||||
TemplateSlot.locale == effective_locale,
|
||||
)
|
||||
)
|
||||
slot = slot_result.first()
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ async def list_targets(
|
||||
chat = chat_result.first()
|
||||
if chat:
|
||||
chat_names[f"{bot_id}_{chat_id}"] = chat.title or chat.username or ""
|
||||
lang = getattr(chat, 'language_code', '') or ''
|
||||
lang = getattr(chat, 'language_override', '') or getattr(chat, 'language_code', '') or ''
|
||||
if lang:
|
||||
chat_languages[f"{bot_id}_{chat_id}"] = lang
|
||||
|
||||
@@ -278,6 +278,7 @@ def _target_response(
|
||||
"name": r.name,
|
||||
"config": dict(r.config),
|
||||
"receiver_key": r.receiver_key,
|
||||
"locale": getattr(r, 'locale', '') or '',
|
||||
"enabled": r.enabled,
|
||||
}
|
||||
for r in recv_list
|
||||
|
||||
@@ -15,7 +15,7 @@ from ..auth.dependencies import get_current_user
|
||||
from ..commands.handler import register_commands_with_telegram
|
||||
from ..commands.webhook import register_webhook, unregister_webhook
|
||||
from ..database.engine import get_session
|
||||
from ..database.models import AppSetting, TelegramBot, TelegramChat, User
|
||||
from ..database.models import AppSetting, NotificationTarget, TargetReceiver, TelegramBot, TelegramChat, User
|
||||
from ..services.notifier import _get_test_message
|
||||
from ..services.telegram_poller import schedule_bot_polling, unschedule_bot_polling
|
||||
from .app_settings import get_setting
|
||||
@@ -297,7 +297,7 @@ async def test_chat(
|
||||
|
||||
|
||||
class ChatUpdate(BaseModel):
|
||||
language_code: str | None = None
|
||||
language_override: str | None = None
|
||||
title: str | None = None
|
||||
commands_enabled: bool | None = None
|
||||
|
||||
@@ -389,6 +389,7 @@ def _chat_response(c: TelegramChat) -> dict:
|
||||
"type": c.chat_type,
|
||||
"username": c.username,
|
||||
"language_code": getattr(c, 'language_code', '') or '',
|
||||
"language_override": getattr(c, 'language_override', '') or '',
|
||||
"commands_enabled": getattr(c, 'commands_enabled', False),
|
||||
"discovered_at": c.discovered_at.isoformat(),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user