Replace video_warning with target_type + has_videos/has_photos
All checks were successful
Validate / Hassfest (push) Successful in 2s
All checks were successful
Validate / Hassfest (push) Successful in 2s
Major template system improvements:
- Remove video_warning field from TemplateConfig model
- Add target_type, has_videos, has_photos to template context
- Templates use {% if target_type == "telegram" and has_videos %}
for conditional Telegram warnings instead of a separate field
- date_format moved from "Telegram" to "Settings" group
- Add target type selector (Telegram/Webhook) in template editor
to preview how templates render for each target type
- All template slots now use JinjaEditor (not plain <input>)
- Preview endpoint accepts target_type parameter
- Clean up TemplateConfigCreate schema (remove stale fields)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -263,8 +263,8 @@
|
|||||||
"periodicAlbum": "Per-album item",
|
"periodicAlbum": "Per-album item",
|
||||||
"scheduledAssets": "Scheduled assets",
|
"scheduledAssets": "Scheduled assets",
|
||||||
"memoryMode": "Memory mode",
|
"memoryMode": "Memory mode",
|
||||||
"telegramSettings": "Telegram",
|
"settings": "Settings",
|
||||||
"videoWarning": "Video warning",
|
"previewAs": "Preview as",
|
||||||
"preview": "Preview",
|
"preview": "Preview",
|
||||||
"variables": "Variables",
|
"variables": "Variables",
|
||||||
"assetFields": "Asset fields (in {% for asset in added_assets %})",
|
"assetFields": "Asset fields (in {% for asset in added_assets %})",
|
||||||
@@ -289,7 +289,9 @@
|
|||||||
"added_assets": "List of asset dicts (use {% for asset in added_assets %})",
|
"added_assets": "List of asset dicts (use {% for asset in added_assets %})",
|
||||||
"removed_assets": "List of removed asset IDs (strings)",
|
"removed_assets": "List of removed asset IDs (strings)",
|
||||||
"shared": "Whether album is shared (boolean)",
|
"shared": "Whether album is shared (boolean)",
|
||||||
"video_warning": "Video size warning (from template config, only if videos present)",
|
"target_type": "Target type: 'telegram' or 'webhook'",
|
||||||
|
"has_videos": "Whether added assets contain videos (boolean)",
|
||||||
|
"has_photos": "Whether added assets contain photos (boolean)",
|
||||||
"old_name": "Previous album name (rename events)",
|
"old_name": "Previous album name (rename events)",
|
||||||
"new_name": "New album name (rename events)",
|
"new_name": "New album name (rename events)",
|
||||||
"old_shared": "Was album shared before rename (boolean)",
|
"old_shared": "Was album shared before rename (boolean)",
|
||||||
|
|||||||
@@ -263,8 +263,8 @@
|
|||||||
"periodicAlbum": "Элемент альбома",
|
"periodicAlbum": "Элемент альбома",
|
||||||
"scheduledAssets": "Запланированные фото",
|
"scheduledAssets": "Запланированные фото",
|
||||||
"memoryMode": "Воспоминания",
|
"memoryMode": "Воспоминания",
|
||||||
"telegramSettings": "Telegram",
|
"settings": "Настройки",
|
||||||
"videoWarning": "Предупреждение о видео",
|
"previewAs": "Предпросмотр как",
|
||||||
"preview": "Предпросмотр",
|
"preview": "Предпросмотр",
|
||||||
"variables": "Переменные",
|
"variables": "Переменные",
|
||||||
"assetFields": "Поля файла (в {% for asset in added_assets %})",
|
"assetFields": "Поля файла (в {% for asset in added_assets %})",
|
||||||
@@ -289,7 +289,9 @@
|
|||||||
"added_assets": "Список файлов ({% for asset in added_assets %})",
|
"added_assets": "Список файлов ({% for asset in added_assets %})",
|
||||||
"removed_assets": "Список ID удалённых файлов (строки)",
|
"removed_assets": "Список ID удалённых файлов (строки)",
|
||||||
"shared": "Общий альбом (boolean)",
|
"shared": "Общий альбом (boolean)",
|
||||||
"video_warning": "Предупреждение о видео (из конфига шаблона, если есть видео)",
|
"target_type": "Тип получателя: 'telegram' или 'webhook'",
|
||||||
|
"has_videos": "Содержат ли добавленные файлы видео (boolean)",
|
||||||
|
"has_photos": "Содержат ли добавленные файлы фото (boolean)",
|
||||||
"old_name": "Прежнее название альбома (при переименовании)",
|
"old_name": "Прежнее название альбома (при переименовании)",
|
||||||
"new_name": "Новое название альбома (при переименовании)",
|
"new_name": "Новое название альбома (при переименовании)",
|
||||||
"old_shared": "Был ли общим до переименования (boolean)",
|
"old_shared": "Был ли общим до переименования (boolean)",
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
// Debounce 800ms
|
// Debounce 800ms
|
||||||
validateTimers[slotKey] = setTimeout(async () => {
|
validateTimers[slotKey] = setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
const res = await api('/template-configs/preview-raw', { method: 'POST', body: JSON.stringify({ template }) });
|
const res = await api('/template-configs/preview-raw', { method: 'POST', body: JSON.stringify({ template, target_type: previewTargetType }) });
|
||||||
slotErrors = { ...slotErrors, [slotKey]: res.error || '' };
|
slotErrors = { ...slotErrors, [slotKey]: res.error || '' };
|
||||||
slotErrorLines = { ...slotErrorLines, [slotKey]: res.error_line || null };
|
slotErrorLines = { ...slotErrorLines, [slotKey]: res.error_line || null };
|
||||||
slotErrorTypes = { ...slotErrorTypes, [slotKey]: res.error_type || '' };
|
slotErrorTypes = { ...slotErrorTypes, [slotKey]: res.error_type || '' };
|
||||||
@@ -73,9 +73,9 @@
|
|||||||
scheduled_assets_message: '',
|
scheduled_assets_message: '',
|
||||||
memory_mode_message: '',
|
memory_mode_message: '',
|
||||||
date_format: '%d.%m.%Y, %H:%M UTC',
|
date_format: '%d.%m.%Y, %H:%M UTC',
|
||||||
video_warning: '\n\n⚠️ Note: Videos may not be sent due to Telegram\'s 50 MB file size limit.',
|
|
||||||
});
|
});
|
||||||
let form = $state(defaultForm());
|
let form = $state(defaultForm());
|
||||||
|
let previewTargetType = $state('telegram');
|
||||||
|
|
||||||
const templateSlots = [
|
const templateSlots = [
|
||||||
{ group: 'eventMessages', slots: [
|
{ group: 'eventMessages', slots: [
|
||||||
@@ -89,9 +89,8 @@
|
|||||||
{ key: 'scheduled_assets_message', label: 'scheduledAssets', rows: 6 },
|
{ key: 'scheduled_assets_message', label: 'scheduledAssets', rows: 6 },
|
||||||
{ key: 'memory_mode_message', label: 'memoryMode', rows: 6 },
|
{ key: 'memory_mode_message', label: 'memoryMode', rows: 6 },
|
||||||
]},
|
]},
|
||||||
{ group: 'telegramSettings', slots: [
|
{ group: 'settings', slots: [
|
||||||
{ key: 'date_format', label: 'dateFormat', rows: 1 },
|
{ key: 'date_format', label: 'dateFormat', rows: 1 },
|
||||||
{ key: 'video_warning', label: 'videoWarning', rows: 2 },
|
|
||||||
]},
|
]},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -124,7 +123,7 @@
|
|||||||
const template = (form as any)[slotKey] || '';
|
const template = (form as any)[slotKey] || '';
|
||||||
if (!template) { slotPreview = { ...slotPreview, [slotKey]: '(empty)' }; return; }
|
if (!template) { slotPreview = { ...slotPreview, [slotKey]: '(empty)' }; return; }
|
||||||
try {
|
try {
|
||||||
const res = await api('/template-configs/preview-raw', { method: 'POST', body: JSON.stringify({ template }) });
|
const res = await api('/template-configs/preview-raw', { method: 'POST', body: JSON.stringify({ template, target_type: previewTargetType }) });
|
||||||
slotPreview = { ...slotPreview, [slotKey]: res.error ? `Error: ${res.error}` : res.rendered };
|
slotPreview = { ...slotPreview, [slotKey]: res.error ? `Error: ${res.error}` : res.rendered };
|
||||||
} catch (err: any) { slotPreview = { ...slotPreview, [slotKey]: `Error: ${err.message}` }; }
|
} catch (err: any) { slotPreview = { ...slotPreview, [slotKey]: `Error: ${err.message}` }; }
|
||||||
}
|
}
|
||||||
@@ -135,7 +134,7 @@
|
|||||||
const template = config[slotKey] || '';
|
const template = config[slotKey] || '';
|
||||||
if (!template) return;
|
if (!template) return;
|
||||||
try {
|
try {
|
||||||
const res = await api('/template-configs/preview-raw', { method: 'POST', body: JSON.stringify({ template }) });
|
const res = await api('/template-configs/preview-raw', { method: 'POST', body: JSON.stringify({ template, target_type: previewTargetType }) });
|
||||||
slotPreview[slotKey + '_' + configId] = res.error ? `Error: ${res.error}` : res.rendered;
|
slotPreview[slotKey + '_' + configId] = res.error ? `Error: ${res.error}` : res.rendered;
|
||||||
} catch (err: any) { slotPreview[slotKey + '_' + configId] = `Error: ${err.message}`; }
|
} catch (err: any) { slotPreview[slotKey + '_' + configId] = `Error: ${err.message}`; }
|
||||||
}
|
}
|
||||||
@@ -180,6 +179,21 @@
|
|||||||
class="w-full px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
class="w-full px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Target type selector for preview -->
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<label class="text-sm font-medium">{t('templateConfig.previewAs')}:</label>
|
||||||
|
<div class="flex gap-1">
|
||||||
|
<button type="button" onclick={() => previewTargetType = 'telegram'}
|
||||||
|
class="px-3 py-1 text-xs rounded-md transition-colors {previewTargetType === 'telegram' ? 'bg-[var(--color-primary)] text-[var(--color-primary-foreground)]' : 'bg-[var(--color-muted)] text-[var(--color-muted-foreground)]'}">
|
||||||
|
Telegram
|
||||||
|
</button>
|
||||||
|
<button type="button" onclick={() => previewTargetType = 'webhook'}
|
||||||
|
class="px-3 py-1 text-xs rounded-md transition-colors {previewTargetType === 'webhook' ? 'bg-[var(--color-primary)] text-[var(--color-primary-foreground)]' : 'bg-[var(--color-muted)] text-[var(--color-muted-foreground)]'}">
|
||||||
|
Webhook
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{#each templateSlots as group}
|
{#each templateSlots as group}
|
||||||
<fieldset class="border border-[var(--color-border)] rounded-md p-3">
|
<fieldset class="border border-[var(--color-border)] rounded-md p-3">
|
||||||
<legend class="text-sm font-medium px-1">{t(`templateConfig.${group.group}`)}{#if group.group === 'eventMessages'}<Hint text={t('hints.eventMessages')} />{:else if group.group === 'assetFormatting'}<Hint text={t('hints.assetFormatting')} />{:else if group.group === 'dateLocation'}<Hint text={t('hints.dateLocation')} />{:else if group.group === 'scheduledMessages'}<Hint text={t('hints.scheduledMessages')} />{/if}</legend>
|
<legend class="text-sm font-medium px-1">{t(`templateConfig.${group.group}`)}{#if group.group === 'eventMessages'}<Hint text={t('hints.eventMessages')} />{:else if group.group === 'assetFormatting'}<Hint text={t('hints.assetFormatting')} />{:else if group.group === 'dateLocation'}<Hint text={t('hints.dateLocation')} />{:else if group.group === 'scheduledMessages'}<Hint text={t('hints.scheduledMessages')} />{/if}</legend>
|
||||||
@@ -197,8 +211,11 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if (slot.rows || 2) > 2}
|
{#if slot.key === 'date_format'}
|
||||||
<JinjaEditor value={(form as any)[slot.key] || ''} onchange={(v) => { (form as any)[slot.key] = v; validateSlot(slot.key, v); }} rows={slot.rows || 6} errorLine={slotErrorLines[slot.key] || null} />
|
<input bind:value={(form as any)[slot.key]}
|
||||||
|
class="w-full px-2 py-1 border border-[var(--color-border)] rounded text-sm bg-[var(--color-background)] font-mono" />
|
||||||
|
{:else}
|
||||||
|
<JinjaEditor value={(form as any)[slot.key] || ''} onchange={(v) => { (form as any)[slot.key] = v; validateSlot(slot.key, v); }} rows={slot.rows || 3} errorLine={slotErrorLines[slot.key] || null} />
|
||||||
{#if slotErrors[slot.key]}
|
{#if slotErrors[slot.key]}
|
||||||
{#if slotErrorTypes[slot.key] === 'undefined'}
|
{#if slotErrorTypes[slot.key] === 'undefined'}
|
||||||
<p class="mt-1 text-xs" style="color: #d97706;">⚠ {t('common.undefinedVar')}: {slotErrors[slot.key]}</p>
|
<p class="mt-1 text-xs" style="color: #d97706;">⚠ {t('common.undefinedVar')}: {slotErrors[slot.key]}</p>
|
||||||
@@ -211,9 +228,6 @@
|
|||||||
<pre class="whitespace-pre-wrap">{slotPreview[slot.key]}</pre>
|
<pre class="whitespace-pre-wrap">{slotPreview[slot.key]}</pre>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
|
||||||
<input bind:value={(form as any)[slot.key]}
|
|
||||||
class="w-full px-2 py-1 border border-[var(--color-border)] rounded text-sm bg-[var(--color-background)] font-mono" />
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
@@ -67,7 +67,9 @@ _SAMPLE_CONTEXT = {
|
|||||||
"removed_assets": ["asset-id-1", "asset-id-2"],
|
"removed_assets": ["asset-id-1", "asset-id-2"],
|
||||||
"people": ["Alice", "Bob"],
|
"people": ["Alice", "Bob"],
|
||||||
"shared": True,
|
"shared": True,
|
||||||
"video_warning": "⚠️ Note: Videos may not be sent due to Telegram's 50 MB file size limit.",
|
"target_type": "telegram",
|
||||||
|
"has_videos": True,
|
||||||
|
"has_photos": True,
|
||||||
# Rename fields (always present, empty for non-rename events)
|
# Rename fields (always present, empty for non-rename events)
|
||||||
"old_name": "Old Album",
|
"old_name": "Old Album",
|
||||||
"new_name": "New Album",
|
"new_name": "New Album",
|
||||||
@@ -82,27 +84,16 @@ _SAMPLE_CONTEXT = {
|
|||||||
|
|
||||||
class TemplateConfigCreate(BaseModel):
|
class TemplateConfigCreate(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
|
description: str | None = None
|
||||||
|
icon: str | None = None
|
||||||
message_assets_added: str | None = None
|
message_assets_added: str | None = None
|
||||||
message_assets_removed: str | None = None
|
message_assets_removed: str | None = None
|
||||||
message_album_renamed: str | None = None
|
message_album_renamed: str | None = None
|
||||||
message_album_deleted: str | None = None
|
message_album_deleted: str | None = None
|
||||||
message_asset_image: str | None = None
|
|
||||||
message_asset_video: str | None = None
|
|
||||||
message_assets_format: str | None = None
|
|
||||||
message_assets_more: str | None = None
|
|
||||||
message_people_format: str | None = None
|
|
||||||
date_format: str | None = None
|
|
||||||
common_date_template: str | None = None
|
|
||||||
date_if_unique_template: str | None = None
|
|
||||||
location_format: str | None = None
|
|
||||||
common_location_template: str | None = None
|
|
||||||
location_if_unique_template: str | None = None
|
|
||||||
favorite_indicator: str | None = None
|
|
||||||
periodic_summary_message: str | None = None
|
periodic_summary_message: str | None = None
|
||||||
periodic_album_template: str | None = None
|
|
||||||
scheduled_assets_message: str | None = None
|
scheduled_assets_message: str | None = None
|
||||||
memory_mode_message: str | None = None
|
memory_mode_message: str | None = None
|
||||||
video_warning: str | None = None
|
date_format: str | None = None
|
||||||
|
|
||||||
|
|
||||||
TemplateConfigUpdate = TemplateConfigCreate # Same shape, all optional
|
TemplateConfigUpdate = TemplateConfigCreate # Same shape, all optional
|
||||||
@@ -203,6 +194,7 @@ async def preview_config(
|
|||||||
|
|
||||||
class PreviewRequest(BaseModel):
|
class PreviewRequest(BaseModel):
|
||||||
template: str
|
template: str
|
||||||
|
target_type: str = "telegram" # "telegram" or "webhook"
|
||||||
|
|
||||||
|
|
||||||
@router.post("/preview-raw")
|
@router.post("/preview-raw")
|
||||||
@@ -229,9 +221,10 @@ async def preview_raw(
|
|||||||
|
|
||||||
# Pass 2: render with strict undefined to catch unknown variables
|
# Pass 2: render with strict undefined to catch unknown variables
|
||||||
try:
|
try:
|
||||||
|
ctx = {**_SAMPLE_CONTEXT, "target_type": body.target_type}
|
||||||
strict_env = SandboxedEnvironment(autoescape=False, undefined=StrictUndefined)
|
strict_env = SandboxedEnvironment(autoescape=False, undefined=StrictUndefined)
|
||||||
tmpl = strict_env.from_string(body.template)
|
tmpl = strict_env.from_string(body.template)
|
||||||
rendered = tmpl.render(**_SAMPLE_CONTEXT)
|
rendered = tmpl.render(**ctx)
|
||||||
return {"rendered": rendered}
|
return {"rendered": rendered}
|
||||||
except UndefinedError as e:
|
except UndefinedError as e:
|
||||||
# Still a valid template syntactically, but references unknown variable
|
# Still a valid template syntactically, but references unknown variable
|
||||||
|
|||||||
@@ -47,7 +47,9 @@ TEMPLATE_VARIABLES: dict[str, dict] = {
|
|||||||
"removed_assets": "Always empty list",
|
"removed_assets": "Always empty list",
|
||||||
"people": "Detected people across all added assets (list of strings)",
|
"people": "Detected people across all added assets (list of strings)",
|
||||||
"shared": "Whether album is shared (boolean)",
|
"shared": "Whether album is shared (boolean)",
|
||||||
"video_warning": "Video size warning text (set from template config if videos present)",
|
"target_type": "Target type: 'telegram' or 'webhook'",
|
||||||
|
"has_videos": "Whether added assets contain videos (boolean)",
|
||||||
|
"has_photos": "Whether added assets contain photos (boolean)",
|
||||||
"old_name": "Always empty (for rename events)",
|
"old_name": "Always empty (for rename events)",
|
||||||
"new_name": "Always empty (for rename events)",
|
"new_name": "Always empty (for rename events)",
|
||||||
},
|
},
|
||||||
@@ -66,7 +68,7 @@ TEMPLATE_VARIABLES: dict[str, dict] = {
|
|||||||
"removed_assets": "List of removed asset IDs (strings)",
|
"removed_assets": "List of removed asset IDs (strings)",
|
||||||
"people": "People in the album (list of strings)",
|
"people": "People in the album (list of strings)",
|
||||||
"shared": "Whether album is shared (boolean)",
|
"shared": "Whether album is shared (boolean)",
|
||||||
"video_warning": "Always empty",
|
"target_type": "Target type: 'telegram' or 'webhook'",
|
||||||
"old_name": "Always empty",
|
"old_name": "Always empty",
|
||||||
"new_name": "Always empty",
|
"new_name": "Always empty",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -135,9 +135,6 @@ class TemplateConfig(SQLModel, table=True):
|
|||||||
|
|
||||||
# Settings
|
# Settings
|
||||||
date_format: str = Field(default="%d.%m.%Y, %H:%M UTC")
|
date_format: str = Field(default="%d.%m.%Y, %H:%M UTC")
|
||||||
video_warning: str = Field(
|
|
||||||
default="⚠️ Note: Videos may not be sent due to Telegram's 50 MB file size limit."
|
|
||||||
)
|
|
||||||
|
|
||||||
created_at: datetime = Field(default_factory=_utcnow)
|
created_at: datetime = Field(default_factory=_utcnow)
|
||||||
|
|
||||||
@@ -204,7 +201,9 @@ _INLINE_TEMPLATES_REMOVED = {
|
|||||||
'{%- if asset.is_favorite %} ❤️{% endif %}\n'
|
'{%- if asset.is_favorite %} ❤️{% endif %}\n'
|
||||||
'{%- endfor %}'
|
'{%- endfor %}'
|
||||||
'{%- endif %}'
|
'{%- endif %}'
|
||||||
'{%- if video_warning %}\n\n{{ video_warning }}{%- endif %}'
|
'{%- if target_type == "telegram" and has_videos %}\n\n'
|
||||||
|
'⚠️ Videos may not be sent due to Telegram\'s 50 MB file size limit.'
|
||||||
|
'{%- endif %}'
|
||||||
),
|
),
|
||||||
|
|
||||||
"message_assets_removed": '🗑️ {{ removed_count }} photo(s) removed from album "{{ album_name }}".',
|
"message_assets_removed": '🗑️ {{ removed_count }} photo(s) removed from album "{{ album_name }}".',
|
||||||
@@ -254,7 +253,9 @@ DEFAULT_TEMPLATE_RU = {
|
|||||||
'{%- if asset.is_favorite %} ❤️{% endif %}\n'
|
'{%- if asset.is_favorite %} ❤️{% endif %}\n'
|
||||||
'{%- endfor %}'
|
'{%- endfor %}'
|
||||||
'{%- endif %}'
|
'{%- endif %}'
|
||||||
'{%- if video_warning %}\n\n{{ video_warning }}{%- endif %}'
|
'{%- if target_type == "telegram" and has_videos %}\n\n'
|
||||||
|
'⚠️ Видео может не отправиться из-за ограничения Telegram в 50 МБ.'
|
||||||
|
'{%- endif %}'
|
||||||
),
|
),
|
||||||
|
|
||||||
"message_assets_removed": '🗑️ {{ removed_count }} фото удалено из альбома "{{ album_name }}".',
|
"message_assets_removed": '🗑️ {{ removed_count }} фото удалено из альбома "{{ album_name }}".',
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ def _render(template_str: str, ctx: dict[str, Any]) -> str:
|
|||||||
|
|
||||||
def build_full_context(
|
def build_full_context(
|
||||||
event_data: dict[str, Any],
|
event_data: dict[str, Any],
|
||||||
template_config: TemplateConfig | None = None,
|
target_type: str = "webhook",
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Build template context by passing raw data directly to Jinja2.
|
"""Build template context by passing raw data directly to Jinja2.
|
||||||
|
|
||||||
@@ -50,10 +50,13 @@ def build_full_context(
|
|||||||
if isinstance(ctx.get("people"), str):
|
if isinstance(ctx.get("people"), str):
|
||||||
ctx["people"] = [ctx["people"]] if ctx["people"] else []
|
ctx["people"] = [ctx["people"]] if ctx["people"] else []
|
||||||
|
|
||||||
# Video warning
|
# Asset type flags
|
||||||
added_assets = ctx.get("added_assets", [])
|
added_assets = ctx.get("added_assets", [])
|
||||||
has_videos = any(a.get("type") == "VIDEO" for a in added_assets) if added_assets else False
|
ctx["has_videos"] = any(a.get("type") == "VIDEO" for a in added_assets) if added_assets else False
|
||||||
ctx["video_warning"] = (template_config.video_warning if template_config and has_videos else "")
|
ctx["has_photos"] = any(a.get("type") == "IMAGE" for a in added_assets) if added_assets else False
|
||||||
|
|
||||||
|
# Target type for conditional formatting (e.g. Telegram video size warning)
|
||||||
|
ctx["target_type"] = target_type
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
@@ -75,7 +78,7 @@ async def send_notification(
|
|||||||
|
|
||||||
# Render with template engine
|
# Render with template engine
|
||||||
if message is None:
|
if message is None:
|
||||||
ctx = build_full_context(event_data, template_config)
|
ctx = build_full_context(event_data, target_type=target.type)
|
||||||
|
|
||||||
template_body = DEFAULT_TEMPLATE
|
template_body = DEFAULT_TEMPLATE
|
||||||
if template_config:
|
if template_config:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
{%- if asset.is_favorite %} ❤️{% endif %}
|
{%- if asset.is_favorite %} ❤️{% endif %}
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- if video_warning %}
|
{%- if target_type == "telegram" and has_videos %}
|
||||||
|
|
||||||
{{ video_warning }}
|
⚠️ Videos may not be sent due to Telegram's 50 MB file size limit.
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
{%- if asset.is_favorite %} ❤️{% endif %}
|
{%- if asset.is_favorite %} ❤️{% endif %}
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- if video_warning %}
|
{%- if target_type == "telegram" and has_videos %}
|
||||||
|
|
||||||
{{ video_warning }}
|
⚠️ Видео может не отправиться из-за ограничения Telegram в 50 МБ.
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|||||||
Reference in New Issue
Block a user