Add document type and content_type support for send_telegram_notification
All checks were successful
Validate / Hassfest (push) Successful in 3s
All checks were successful
Validate / Hassfest (push) Successful in 3s
- Add type: document as default media type (instead of photo) - Add optional content_type field for explicit MIME type specification - Documents are sent separately (Telegram API limitation for media groups) - Default content types: image/jpeg (photo), video/mp4 (video), auto-detect (document) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
27
README.md
27
README.md
@@ -37,7 +37,7 @@ A Home Assistant custom integration that monitors [Immich](https://immich.app/)
|
||||
- **Services** - Custom service calls:
|
||||
- `immich_album_watcher.refresh` - Force immediate data refresh
|
||||
- `immich_album_watcher.get_assets` - Get assets from an album with filtering and ordering
|
||||
- `immich_album_watcher.send_telegram_notification` - Send text, photo, video, or media group to Telegram
|
||||
- `immich_album_watcher.send_telegram_notification` - Send text, photo, video, document, or media group to Telegram
|
||||
- **Share Link Management** - Button entities to create and delete share links:
|
||||
- Create/delete public (unprotected) share links
|
||||
- Create/delete password-protected share links
|
||||
@@ -335,11 +335,12 @@ data:
|
||||
Send notifications to Telegram. Supports multiple formats:
|
||||
|
||||
- **Text message** - When `urls` is empty or not provided
|
||||
- **Single photo** - When `urls` contains one photo
|
||||
- **Single video** - When `urls` contains one video
|
||||
- **Media group** - When `urls` contains multiple items
|
||||
- **Single document** - When `urls` contains one document (default type)
|
||||
- **Single photo** - When `urls` contains one photo (`type: photo`)
|
||||
- **Single video** - When `urls` contains one video (`type: video`)
|
||||
- **Media group** - When `urls` contains multiple photos/videos (documents are sent separately)
|
||||
|
||||
The service downloads media from Immich and uploads it to Telegram, bypassing any CORS restrictions. Large lists of media are automatically split into multiple media groups based on the `max_group_size` parameter (default: 10 items per group).
|
||||
The service downloads media from Immich and uploads it to Telegram, bypassing any CORS restrictions. Large lists of photos and videos are automatically split into multiple media groups based on the `max_group_size` parameter (default: 10 items per group). Documents cannot be grouped and are sent individually.
|
||||
|
||||
**File ID Caching:** When media is uploaded to Telegram, the service caches the returned `file_id`. Subsequent sends of the same media will use the cached `file_id` instead of re-uploading, significantly improving performance. The cache TTL is configurable in hub options (default: 48 hours, range: 1-168 hours). The cache is persistent across Home Assistant restarts and is stored per album.
|
||||
|
||||
@@ -357,6 +358,20 @@ data:
|
||||
disable_web_page_preview: true
|
||||
```
|
||||
|
||||
Single document (default):
|
||||
|
||||
```yaml
|
||||
service: immich_album_watcher.send_telegram_notification
|
||||
target:
|
||||
entity_id: sensor.album_name_asset_limit
|
||||
data:
|
||||
chat_id: "-1001234567890"
|
||||
urls:
|
||||
- url: "https://immich.example.com/api/assets/xxx/original?key=yyy"
|
||||
content_type: "image/heic" # Optional: explicit MIME type
|
||||
caption: "Original file"
|
||||
```
|
||||
|
||||
Single photo:
|
||||
|
||||
```yaml
|
||||
@@ -421,7 +436,7 @@ data:
|
||||
| Field | Description | Required |
|
||||
|-------|-------------|----------|
|
||||
| `chat_id` | Telegram chat ID to send to | Yes |
|
||||
| `urls` | List of media items with `url` and `type` (photo/video). Empty for text message. | No |
|
||||
| `urls` | List of media items with `url`, optional `type` (document/photo/video, default: document), and optional `content_type` (MIME type, e.g., `image/jpeg`). Empty for text message. Photos and videos can be grouped; documents are sent separately. | No |
|
||||
| `bot_token` | Telegram bot token (uses configured token if not provided) | No |
|
||||
| `caption` | For media: caption applied to first item. For text: the message text. Supports HTML formatting by default. | No |
|
||||
| `reply_to_message_id` | Message ID to reply to | No |
|
||||
|
||||
@@ -348,18 +348,39 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
|
||||
try:
|
||||
# Handle single photo
|
||||
if len(urls) == 1 and urls[0].get("type", "photo") == "photo":
|
||||
if len(urls) == 1 and urls[0].get("type") == "photo":
|
||||
return await self._send_telegram_photo(
|
||||
session, token, chat_id, urls[0].get("url"), caption, reply_to_message_id, parse_mode,
|
||||
max_asset_data_size, send_large_photos_as_documents
|
||||
max_asset_data_size, send_large_photos_as_documents, urls[0].get("content_type")
|
||||
)
|
||||
|
||||
# Handle single video
|
||||
if len(urls) == 1 and urls[0].get("type") == "video":
|
||||
return await self._send_telegram_video(
|
||||
session, token, chat_id, urls[0].get("url"), caption, reply_to_message_id, parse_mode, max_asset_data_size
|
||||
session, token, chat_id, urls[0].get("url"), caption, reply_to_message_id, parse_mode,
|
||||
max_asset_data_size, urls[0].get("content_type")
|
||||
)
|
||||
|
||||
# Handle single document (default type)
|
||||
if len(urls) == 1 and urls[0].get("type", "document") == "document":
|
||||
url = urls[0].get("url")
|
||||
item_content_type = urls[0].get("content_type")
|
||||
try:
|
||||
download_url = self.coordinator.get_internal_download_url(url)
|
||||
async with session.get(download_url) as resp:
|
||||
if resp.status != 200:
|
||||
return {"success": False, "error": f"Failed to download media: HTTP {resp.status}"}
|
||||
data = await resp.read()
|
||||
if max_asset_data_size is not None and len(data) > max_asset_data_size:
|
||||
return {"success": False, "error": f"Media size ({len(data)} bytes) exceeds max_asset_data_size limit ({max_asset_data_size} bytes)"}
|
||||
# Detect filename from URL or use generic name
|
||||
filename = url.split("/")[-1].split("?")[0] or "file"
|
||||
return await self._send_telegram_document(
|
||||
session, token, chat_id, data, filename, caption, reply_to_message_id, parse_mode, url, item_content_type
|
||||
)
|
||||
except aiohttp.ClientError as err:
|
||||
return {"success": False, "error": f"Failed to download media: {err}"}
|
||||
|
||||
# Handle multiple items - send as media group(s)
|
||||
return await self._send_telegram_media_group(
|
||||
session, token, chat_id, urls, caption, reply_to_message_id, max_group_size, chunk_delay, parse_mode,
|
||||
@@ -599,11 +620,16 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
parse_mode: str = "HTML",
|
||||
max_asset_data_size: int | None = None,
|
||||
send_large_photos_as_documents: bool = False,
|
||||
content_type: str | None = None,
|
||||
) -> ServiceResponse:
|
||||
"""Send a single photo to Telegram."""
|
||||
import aiohttp
|
||||
from aiohttp import FormData
|
||||
|
||||
# Use provided content type or default to image/jpeg
|
||||
if not content_type:
|
||||
content_type = "image/jpeg"
|
||||
|
||||
if not url:
|
||||
return {"success": False, "error": "Missing 'url' for photo"}
|
||||
|
||||
@@ -689,7 +715,7 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
# Build multipart form
|
||||
form = FormData()
|
||||
form.add_field("chat_id", chat_id)
|
||||
form.add_field("photo", data, filename="photo.jpg", content_type="image/jpeg")
|
||||
form.add_field("photo", data, filename="photo.jpg", content_type=content_type)
|
||||
form.add_field("parse_mode", parse_mode)
|
||||
|
||||
if caption:
|
||||
@@ -745,11 +771,16 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
reply_to_message_id: int | None = None,
|
||||
parse_mode: str = "HTML",
|
||||
max_asset_data_size: int | None = None,
|
||||
content_type: str | None = None,
|
||||
) -> ServiceResponse:
|
||||
"""Send a single video to Telegram."""
|
||||
import aiohttp
|
||||
from aiohttp import FormData
|
||||
|
||||
# Use provided content type or default to video/mp4
|
||||
if not content_type:
|
||||
content_type = "video/mp4"
|
||||
|
||||
if not url:
|
||||
return {"success": False, "error": "Missing 'url' for video"}
|
||||
|
||||
@@ -816,7 +847,7 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
# Build multipart form
|
||||
form = FormData()
|
||||
form.add_field("chat_id", chat_id)
|
||||
form.add_field("video", data, filename="video.mp4", content_type="video/mp4")
|
||||
form.add_field("video", data, filename="video.mp4", content_type=content_type)
|
||||
form.add_field("parse_mode", parse_mode)
|
||||
|
||||
if caption:
|
||||
@@ -867,16 +898,24 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
token: str,
|
||||
chat_id: str,
|
||||
data: bytes,
|
||||
filename: str = "photo.jpg",
|
||||
filename: str = "file",
|
||||
caption: str | None = None,
|
||||
reply_to_message_id: int | None = None,
|
||||
parse_mode: str = "HTML",
|
||||
source_url: str | None = None,
|
||||
content_type: str | None = None,
|
||||
) -> ServiceResponse:
|
||||
"""Send a photo as a document to Telegram (for oversized photos)."""
|
||||
"""Send a file as a document to Telegram."""
|
||||
import aiohttp
|
||||
import mimetypes
|
||||
from aiohttp import FormData
|
||||
|
||||
# Use provided content type or detect from filename
|
||||
if not content_type:
|
||||
content_type, _ = mimetypes.guess_type(filename)
|
||||
if not content_type:
|
||||
content_type = "application/octet-stream"
|
||||
|
||||
# Check cache for file_id if source_url is provided
|
||||
cache = self.coordinator.telegram_cache
|
||||
if source_url:
|
||||
@@ -915,7 +954,7 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
# Build multipart form
|
||||
form = FormData()
|
||||
form.add_field("chat_id", chat_id)
|
||||
form.add_field("document", data, filename=filename, content_type="image/jpeg")
|
||||
form.add_field("document", data, filename=filename, content_type=content_type)
|
||||
form.add_field("parse_mode", parse_mode)
|
||||
|
||||
if caption:
|
||||
@@ -927,7 +966,7 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
# Send to Telegram
|
||||
telegram_url = f"https://api.telegram.org/bot{token}/sendDocument"
|
||||
|
||||
_LOGGER.debug("Uploading oversized photo as document to Telegram (%d bytes)", len(data))
|
||||
_LOGGER.debug("Uploading document to Telegram (%d bytes, %s)", len(data), content_type)
|
||||
async with session.post(telegram_url, data=form) as response:
|
||||
result = await response.json()
|
||||
_LOGGER.debug("Telegram API response: status=%d, ok=%s", response.status, result.get("ok"))
|
||||
@@ -1003,8 +1042,9 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
# Optimize: Use single-item APIs for chunks with 1 item
|
||||
if len(chunk) == 1:
|
||||
item = chunk[0]
|
||||
media_type = item.get("type", "photo")
|
||||
media_type = item.get("type", "document")
|
||||
url = item.get("url")
|
||||
item_content_type = item.get("content_type")
|
||||
|
||||
# Only apply caption and reply_to to the first chunk
|
||||
chunk_caption = caption if chunk_idx == 0 else None
|
||||
@@ -1014,13 +1054,31 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
_LOGGER.debug("Sending chunk %d/%d as single photo", chunk_idx + 1, len(chunks))
|
||||
result = await self._send_telegram_photo(
|
||||
session, token, chat_id, url, chunk_caption, chunk_reply_to, parse_mode,
|
||||
max_asset_data_size, send_large_photos_as_documents
|
||||
max_asset_data_size, send_large_photos_as_documents, item_content_type
|
||||
)
|
||||
else: # video
|
||||
elif media_type == "video":
|
||||
_LOGGER.debug("Sending chunk %d/%d as single video", chunk_idx + 1, len(chunks))
|
||||
result = await self._send_telegram_video(
|
||||
session, token, chat_id, url, chunk_caption, chunk_reply_to, parse_mode, max_asset_data_size
|
||||
session, token, chat_id, url, chunk_caption, chunk_reply_to, parse_mode,
|
||||
max_asset_data_size, item_content_type
|
||||
)
|
||||
else: # document
|
||||
_LOGGER.debug("Sending chunk %d/%d as single document", chunk_idx + 1, len(chunks))
|
||||
try:
|
||||
download_url = self.coordinator.get_internal_download_url(url)
|
||||
async with session.get(download_url) as resp:
|
||||
if resp.status != 200:
|
||||
return {"success": False, "error": f"Failed to download media: HTTP {resp.status}", "failed_at_chunk": chunk_idx + 1}
|
||||
data = await resp.read()
|
||||
if max_asset_data_size is not None and len(data) > max_asset_data_size:
|
||||
_LOGGER.warning("Media size (%d bytes) exceeds max_asset_data_size limit (%d bytes), skipping", len(data), max_asset_data_size)
|
||||
continue
|
||||
filename = url.split("/")[-1].split("?")[0] or "file"
|
||||
result = await self._send_telegram_document(
|
||||
session, token, chat_id, data, filename, chunk_caption, chunk_reply_to, parse_mode, url, item_content_type
|
||||
)
|
||||
except aiohttp.ClientError as err:
|
||||
return {"success": False, "error": f"Failed to download media: {err}", "failed_at_chunk": chunk_idx + 1}
|
||||
|
||||
if not result.get("success"):
|
||||
result["failed_at_chunk"] = chunk_idx + 1
|
||||
@@ -1035,15 +1093,17 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
cache = self.coordinator.telegram_cache
|
||||
|
||||
# Collect media items - either from cache (file_id) or by downloading
|
||||
# Each item: (type, media_ref, filename, url, is_cached)
|
||||
# Each item: (type, media_ref, filename, url, is_cached, content_type)
|
||||
# media_ref is either file_id (str) or data (bytes)
|
||||
media_items: list[tuple[str, str | bytes, str, str, bool]] = []
|
||||
media_items: list[tuple[str, str | bytes, str, str, bool, str | None]] = []
|
||||
oversized_photos: list[tuple[bytes, str | None, str]] = [] # (data, caption, url)
|
||||
documents_to_send: list[tuple[bytes, str | None, str, str, str | None]] = [] # (data, caption, url, filename, content_type)
|
||||
skipped_count = 0
|
||||
|
||||
for i, item in enumerate(chunk):
|
||||
url = item.get("url")
|
||||
media_type = item.get("type", "photo")
|
||||
media_type = item.get("type", "document")
|
||||
item_content_type = item.get("content_type")
|
||||
|
||||
if not url:
|
||||
return {
|
||||
@@ -1051,19 +1111,48 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
"error": f"Missing 'url' in item {chunk_idx * max_group_size + i}",
|
||||
}
|
||||
|
||||
if media_type not in ("photo", "video"):
|
||||
if media_type not in ("photo", "video", "document"):
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"Invalid type '{media_type}' in item {chunk_idx * max_group_size + i}. Must be 'photo' or 'video'.",
|
||||
"error": f"Invalid type '{media_type}' in item {chunk_idx * max_group_size + i}. Must be 'photo', 'video', or 'document'.",
|
||||
}
|
||||
|
||||
# Check cache first
|
||||
# Documents can't be in media groups - collect them for separate sending
|
||||
if media_type == "document":
|
||||
try:
|
||||
download_url = self.coordinator.get_internal_download_url(url)
|
||||
async with session.get(download_url) as resp:
|
||||
if resp.status != 200:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"Failed to download media {chunk_idx * max_group_size + i}: HTTP {resp.status}",
|
||||
}
|
||||
data = await resp.read()
|
||||
if max_asset_data_size is not None and len(data) > max_asset_data_size:
|
||||
_LOGGER.warning(
|
||||
"Media %d size (%d bytes) exceeds max_asset_data_size limit (%d bytes), skipping",
|
||||
chunk_idx * max_group_size + i, len(data), max_asset_data_size
|
||||
)
|
||||
skipped_count += 1
|
||||
continue
|
||||
# Caption only on first item of first chunk if no media items yet
|
||||
doc_caption = caption if chunk_idx == 0 and i == 0 and len(media_items) == 0 and len(documents_to_send) == 0 else None
|
||||
filename = url.split("/")[-1].split("?")[0] or f"file_{i}"
|
||||
documents_to_send.append((data, doc_caption, url, filename, item_content_type))
|
||||
except aiohttp.ClientError as err:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"Failed to download media {chunk_idx * max_group_size + i}: {err}",
|
||||
}
|
||||
continue
|
||||
|
||||
# Check cache first for photos/videos
|
||||
cached = cache.get(url) if cache else None
|
||||
if cached and cached.get("file_id"):
|
||||
# Use cached file_id
|
||||
ext = "jpg" if media_type == "photo" else "mp4"
|
||||
filename = f"media_{chunk_idx * max_group_size + i}.{ext}"
|
||||
media_items.append((media_type, cached["file_id"], filename, url, True))
|
||||
media_items.append((media_type, cached["file_id"], filename, url, True, item_content_type))
|
||||
_LOGGER.debug("Using cached file_id for media %d", chunk_idx * max_group_size + i)
|
||||
continue
|
||||
|
||||
@@ -1108,7 +1197,7 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
|
||||
ext = "jpg" if media_type == "photo" else "mp4"
|
||||
filename = f"media_{chunk_idx * max_group_size + i}.{ext}"
|
||||
media_items.append((media_type, data, filename, url, False))
|
||||
media_items.append((media_type, data, filename, url, False, item_content_type))
|
||||
except aiohttp.ClientError as err:
|
||||
return {
|
||||
"success": False,
|
||||
@@ -1124,13 +1213,13 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
# Send media group if we have normal-sized files
|
||||
if media_items:
|
||||
# Check if all items are cached (can use simple JSON payload)
|
||||
all_cached = all(is_cached for _, _, _, _, is_cached in media_items)
|
||||
all_cached = all(is_cached for _, _, _, _, is_cached, _ in media_items)
|
||||
|
||||
if all_cached:
|
||||
# All items cached - use simple JSON payload with file_ids
|
||||
_LOGGER.debug("All %d items cached, using file_ids", len(media_items))
|
||||
media_json = []
|
||||
for i, (media_type, file_id, _, _, _) in enumerate(media_items):
|
||||
for i, (media_type, file_id, _, _, _, _) in enumerate(media_items):
|
||||
media_item_json: dict[str, Any] = {
|
||||
"type": media_type,
|
||||
"media": file_id,
|
||||
@@ -1178,7 +1267,7 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
upload_idx = 0
|
||||
urls_to_cache: list[tuple[str, int, str]] = [] # (url, result_idx, type)
|
||||
|
||||
for i, (media_type, media_ref, filename, url, is_cached) in enumerate(media_items):
|
||||
for i, (media_type, media_ref, filename, url, is_cached, item_content_type) in enumerate(media_items):
|
||||
if is_cached:
|
||||
# Use file_id directly
|
||||
media_item_json: dict[str, Any] = {
|
||||
@@ -1192,7 +1281,8 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
"type": media_type,
|
||||
"media": f"attach://{attach_name}",
|
||||
}
|
||||
content_type = "image/jpeg" if media_type == "photo" else "video/mp4"
|
||||
# Use provided content_type or default based on media type
|
||||
content_type = item_content_type or ("image/jpeg" if media_type == "photo" else "video/mp4")
|
||||
form.add_field(attach_name, media_ref, filename=filename, content_type=content_type)
|
||||
urls_to_cache.append((url, i, media_type))
|
||||
upload_idx += 1
|
||||
@@ -1236,7 +1326,7 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
else:
|
||||
# Log detailed error for media group with total size info
|
||||
uploaded_data = [m for m in media_items if not m[4]]
|
||||
total_size = sum(len(d) for _, d, _, _, _ in uploaded_data if isinstance(d, bytes))
|
||||
total_size = sum(len(d) for _, d, _, _, _, _ in uploaded_data if isinstance(d, bytes))
|
||||
_LOGGER.error(
|
||||
"Telegram API error for chunk %d/%d: %s | Media count: %d | Uploaded size: %d bytes (%.2f MB)",
|
||||
chunk_idx + 1, len(chunks),
|
||||
@@ -1246,7 +1336,7 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
total_size / (1024 * 1024) if total_size else 0
|
||||
)
|
||||
# Log detailed diagnostics for the first photo in the group
|
||||
for media_type, media_ref, _, _, is_cached in media_items:
|
||||
for media_type, media_ref, _, _, is_cached, _ in media_items:
|
||||
if media_type == "photo" and not is_cached and isinstance(media_ref, bytes):
|
||||
self._log_telegram_error(
|
||||
error_code=result.get("error_code"),
|
||||
@@ -1282,6 +1372,19 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
||||
_LOGGER.error("Failed to send oversized photo as document: %s", result.get("error"))
|
||||
# Continue with other photos even if one fails
|
||||
|
||||
# Send documents (can't be in media groups)
|
||||
for i, (data, doc_caption, doc_url, filename, doc_content_type) in enumerate(documents_to_send):
|
||||
_LOGGER.debug("Sending document %d/%d", i + 1, len(documents_to_send))
|
||||
result = await self._send_telegram_document(
|
||||
session, token, chat_id, data, filename,
|
||||
doc_caption, None, parse_mode, doc_url, doc_content_type
|
||||
)
|
||||
if result.get("success"):
|
||||
all_message_ids.append(result.get("message_id"))
|
||||
else:
|
||||
_LOGGER.error("Failed to send document: %s", result.get("error"))
|
||||
# Continue with other documents even if one fails
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message_ids": all_message_ids,
|
||||
|
||||
@@ -131,7 +131,7 @@ get_assets:
|
||||
|
||||
send_telegram_notification:
|
||||
name: Send Telegram Notification
|
||||
description: Send a notification to Telegram (text, photo, video, or media group).
|
||||
description: Send a notification to Telegram (text, photo, video, document, or media group).
|
||||
target:
|
||||
entity:
|
||||
integration: immich_album_watcher
|
||||
@@ -151,7 +151,7 @@ send_telegram_notification:
|
||||
text:
|
||||
urls:
|
||||
name: URLs
|
||||
description: List of media URLs to send. Each item should have 'url' and 'type' (photo/video). If empty, sends a text message. Large lists are automatically split into multiple media groups.
|
||||
description: "List of media URLs to send. Each item should have 'url', optional 'type' (document/photo/video, default: document), and optional 'content_type' (MIME type, e.g., 'image/jpeg'). If empty, sends a text message. Photos and videos can be grouped; documents are sent separately."
|
||||
required: false
|
||||
selector:
|
||||
object:
|
||||
|
||||
@@ -195,7 +195,7 @@
|
||||
},
|
||||
"send_telegram_notification": {
|
||||
"name": "Send Telegram Notification",
|
||||
"description": "Send a notification to Telegram (text, photo, video, or media group).",
|
||||
"description": "Send a notification to Telegram (text, photo, video, document, or media group).",
|
||||
"fields": {
|
||||
"bot_token": {
|
||||
"name": "Bot Token",
|
||||
@@ -207,7 +207,7 @@
|
||||
},
|
||||
"urls": {
|
||||
"name": "URLs",
|
||||
"description": "List of media URLs with type (photo/video). If empty, sends a text message. Large lists are automatically split into multiple media groups."
|
||||
"description": "List of media URLs with optional type (document/photo/video, default: document) and optional content_type (MIME type). If empty, sends a text message. Photos and videos can be grouped; documents are sent separately."
|
||||
},
|
||||
"caption": {
|
||||
"name": "Caption",
|
||||
|
||||
@@ -195,7 +195,7 @@
|
||||
},
|
||||
"send_telegram_notification": {
|
||||
"name": "Отправить уведомление в Telegram",
|
||||
"description": "Отправить уведомление в Telegram (текст, фото, видео или медиа-группу).",
|
||||
"description": "Отправить уведомление в Telegram (текст, фото, видео, документ или медиа-группу).",
|
||||
"fields": {
|
||||
"bot_token": {
|
||||
"name": "Токен бота",
|
||||
@@ -207,7 +207,7 @@
|
||||
},
|
||||
"urls": {
|
||||
"name": "URL-адреса",
|
||||
"description": "Список URL медиа-файлов с типом (photo/video). Если пусто, отправляет текстовое сообщение. Большие списки автоматически разделяются на несколько медиа-групп."
|
||||
"description": "Список URL медиа-файлов с типом (document/photo/video, по умолчанию document) и опциональным content_type (MIME-тип). Если пусто, отправляет текстовое сообщение. Фото и видео группируются; документы отправляются отдельно."
|
||||
},
|
||||
"caption": {
|
||||
"name": "Подпись",
|
||||
|
||||
Reference in New Issue
Block a user