feat: UX & notification improvements — icons, events, chat names, link validation, templates
- Show entity icons on all cards with fallback defaults (providers, trackers, targets, bots)
- Enrich EventLog with provider_name, tracker_name, assets_count; add DB migration
- Dashboard events: filtering (type, provider, search), sorting, pagination, dynamic page size
- Friendly chat names on telegram target cards (resolve from TelegramChat table)
- Test message button on bot chat items with locale-aware messages
- Album public link validation on tracker save with auto-create dialog
- Support albums without public links: conditional <a href> in templates
- Fetch shared links during poll, enrich events with public_url/protected_url
- Per-asset public_url in template context ({share_url}/photos/{asset_id})
- Common date/location detection: common_date + common_location context vars
- Dual date formats: date_format (datetime) + date_only_format (date only)
- Template clone button, HTML link rendering in template preview
- Fix Telegram asset download 401: pass x-api-key headers through client
- Fix provider external_url matching for API key scoping
- Fix event timestamp timezone (append Z suffix for UTC)
- Localize event filter controls, test messages (EN/RU)
- Template variable UI helpers updated with all new fields
- CLAUDE.md: template system sync rules documentation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,21 +8,37 @@ from ..database.models import NotificationTarget
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
_TEST_MESSAGES: dict[str, dict[str, str]] = {
|
||||
"en": {
|
||||
"telegram": "\u2705 Test message from <b>Notify Bridge</b>",
|
||||
"webhook": "Test notification from Notify Bridge",
|
||||
},
|
||||
"ru": {
|
||||
"telegram": "\u2705 Тестовое сообщение от <b>Notify Bridge</b>",
|
||||
"webhook": "Тестовое уведомление от Notify Bridge",
|
||||
},
|
||||
}
|
||||
|
||||
async def send_test_notification(target: NotificationTarget) -> dict:
|
||||
|
||||
def _get_test_message(locale: str, target_type: str) -> str:
|
||||
msgs = _TEST_MESSAGES.get(locale, _TEST_MESSAGES["en"])
|
||||
return msgs.get(target_type, msgs.get("webhook", "Test"))
|
||||
|
||||
|
||||
async def send_test_notification(target: NotificationTarget, locale: str = "en") -> dict:
|
||||
"""Send a simple test message to a notification target."""
|
||||
try:
|
||||
if target.type == "telegram":
|
||||
return await _test_telegram(target)
|
||||
return await _test_telegram(target, locale)
|
||||
elif target.type == "webhook":
|
||||
return await _test_webhook(target)
|
||||
return await _test_webhook(target, locale)
|
||||
return {"success": False, "error": f"Unknown target type: {target.type}"}
|
||||
except Exception as e:
|
||||
_LOGGER.error("Test notification failed: %s", e)
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
|
||||
async def _test_telegram(target: NotificationTarget) -> dict:
|
||||
async def _test_telegram(target: NotificationTarget, locale: str = "en") -> dict:
|
||||
from notify_bridge_core.notifications.telegram.client import TelegramClient
|
||||
|
||||
bot_token = target.config.get("bot_token")
|
||||
@@ -34,7 +50,7 @@ async def _test_telegram(target: NotificationTarget) -> dict:
|
||||
client = TelegramClient(session, bot_token)
|
||||
return await client.send_notification(
|
||||
chat_id=str(chat_id),
|
||||
caption="Test notification from Notify Bridge",
|
||||
caption=_get_test_message(locale, "telegram"),
|
||||
)
|
||||
|
||||
|
||||
@@ -88,7 +104,7 @@ async def _test_webhook_with_message(target: NotificationTarget, message: str) -
|
||||
return await client.send({"message": message, "event_type": "test_template"})
|
||||
|
||||
|
||||
async def _test_webhook(target: NotificationTarget) -> dict:
|
||||
async def _test_webhook(target: NotificationTarget, locale: str = "en") -> dict:
|
||||
from notify_bridge_core.notifications.webhook.client import WebhookClient
|
||||
|
||||
url = target.config.get("url")
|
||||
@@ -99,6 +115,6 @@ async def _test_webhook(target: NotificationTarget) -> dict:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
client = WebhookClient(session, url, headers)
|
||||
return await client.send({
|
||||
"message": "Test notification from Notify Bridge",
|
||||
"message": _get_test_message(locale, "webhook"),
|
||||
"event_type": "test",
|
||||
})
|
||||
|
||||
@@ -174,11 +174,16 @@ async def check_tracker(tracker_id: int) -> dict[str, Any]:
|
||||
session.add(new_ts)
|
||||
|
||||
for event in events:
|
||||
assets_count = event.added_count or event.removed_count or 0
|
||||
log = EventLog(
|
||||
tracker_id=tracker_id,
|
||||
tracker_name=tracker.name,
|
||||
provider_id=provider.id,
|
||||
provider_name=provider_name,
|
||||
event_type=event.event_type.value,
|
||||
collection_id=event.collection_id,
|
||||
collection_name=event.collection_name,
|
||||
assets_count=assets_count,
|
||||
details={
|
||||
"added_count": event.added_count,
|
||||
"removed_count": event.removed_count,
|
||||
@@ -233,8 +238,11 @@ async def check_tracker(tracker_id: int) -> dict[str, Any]:
|
||||
type=ld["target_type"],
|
||||
config=ld["target_config"],
|
||||
template_slots=slots,
|
||||
date_format=tmpl.date_format if tmpl else "%d.%m.%Y, %H:%M UTC",
|
||||
date_only_format=tmpl.date_only_format if tmpl and hasattr(tmpl, "date_only_format") else "%d.%m.%Y",
|
||||
provider_api_key=provider_config.get("api_key"),
|
||||
provider_internal_url=provider_config.get("url", ""),
|
||||
provider_external_url=provider_config.get("external_domain", ""),
|
||||
))
|
||||
|
||||
if target_configs:
|
||||
|
||||
Reference in New Issue
Block a user