feat: test menu dropdown, split text/media messages, target settings, provider URL links

- Replace 3 test buttons with unified dropdown menu (basic/periodic/scheduled/memory)
- Send text message first, then assets as reply (not combined caption+media)
- Pass all target config settings to Telegram client (disable_url_preview, max_media, chunk_delay, etc.)
- Real data test notifications for periodic/scheduled/memory (fetch from Immich)
- Provider card URL is now a clickable hyperlink
- Localized test type labels (EN/RU)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-20 16:34:25 +03:00
parent 03c5c66eed
commit 5015e378fe
9 changed files with 407 additions and 78 deletions
@@ -91,6 +91,14 @@ class NotificationDispatcher:
) -> dict[str, Any]:
bot_token = target.config.get("bot_token")
chat_id = target.config.get("chat_id")
disable_preview = target.config.get("disable_url_preview", False)
max_media = target.config.get("max_media_to_send", 50)
max_group = target.config.get("max_media_per_group", 10)
chunk_delay = target.config.get("media_delay", 500)
max_size = target.config.get("max_asset_size")
if max_size:
max_size = max_size * 1024 * 1024 # MB to bytes
send_large_as_docs = target.config.get("send_large_photos_as_documents", False)
if not bot_token or not chat_id:
return {"success": False, "error": "Missing bot_token or chat_id"}
@@ -98,15 +106,23 @@ class NotificationDispatcher:
async with aiohttp.ClientSession() as session:
client = TelegramClient(session, bot_token)
# Build asset list for media sending
# Attach API key header for URLs pointing to the provider (internal or external)
# Step 1: Send the text message first
text_result = await client.send_message(
chat_id=str(chat_id),
text=message,
disable_web_page_preview=disable_preview or None,
)
if not text_result.get("success"):
return text_result
# Step 2: Send assets as reply to the text message
provider_urls = []
if target.provider_internal_url:
provider_urls.append(target.provider_internal_url)
if target.provider_external_url:
provider_urls.append(target.provider_external_url)
assets = []
for asset in event.added_assets:
for asset in event.added_assets[:max_media]:
url = asset.full_url or asset.thumbnail_url
if url:
asset_type = "video" if asset.type.value == "video" else "photo"
@@ -115,11 +131,21 @@ class NotificationDispatcher:
asset_headers["x-api-key"] = target.provider_api_key
assets.append({"url": url, "type": asset_type, "headers": asset_headers})
return await client.send_notification(
chat_id=str(chat_id),
caption=message,
assets=assets if assets else None,
)
if assets:
reply_to = text_result.get("message_id")
media_result = await client.send_notification(
chat_id=str(chat_id),
assets=assets,
reply_to_message_id=reply_to,
max_group_size=max_group,
chunk_delay=chunk_delay,
max_asset_data_size=max_size,
send_large_photos_as_documents=send_large_as_docs,
)
if not media_result.get("success"):
_LOGGER.warning("Text sent OK but media failed: %s", media_result.get("error"))
return text_result
async def _send_webhook(
self, target: TargetConfig, message: str, event: ServiceEvent