mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-08 07:01:40 +02:00
feat(notifications): optional Telegram forum topic via message_thread_id (#1518)
Telegram groups with Topics enabled always received notifications in the General topic, since only Bot Token and Chat ID were configurable. Splitting notifications per printer meant running a separate chat for each one. The Telegram provider now takes an optional Forum Topic ID - the last number in a topic's link, t.me/c/1234567890/25 - and routes its messages there. Left empty, nothing changes. The value is coerced to an int once in _send_telegram and attached to both the sendMessage JSON body and the sendPhoto form data. That ordering matters: Telegram rejects a string message_thread_id in the JSON body while accepting one in the multipart call, so passing the raw form value through would have worked for thumbnail notifications and 400'd for plain-text ones. A non-numeric value is rejected in the form and again server-side before any request goes out. No migration - provider config is a JSON blob. Adds Forum Topic ID plus help text to the Telegram section of the provider dialog, translated in all 13 locales. Backend tests cover omitted / blank / int-typed / non-numeric values and both send paths; frontend tests cover the field being optional, absent for other providers, round-tripping on save, and blocking save on a bad value.
This commit is contained in:
@@ -160,6 +160,15 @@ export function AddNotificationModal({ provider, onClose }: AddNotificationModal
|
||||
}
|
||||
}
|
||||
|
||||
// Telegram forum topic must be a plain integer (#1518) — type="number"
|
||||
// still lets "1e5" and "-" through, and Telegram would 400 on those.
|
||||
if (providerType === 'telegram' && config.message_thread_id?.trim()) {
|
||||
if (!/^\d+$/.test(config.message_thread_id.trim())) {
|
||||
setError(t('notifications.telegramThreadIdInvalid'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const finalConfig: Record<string, unknown> =
|
||||
providerType === 'ntfy' && Object.keys(eventPriorities).length > 0
|
||||
? { ...config, event_priorities: eventPriorities }
|
||||
@@ -245,6 +254,16 @@ export function AddNotificationModal({ provider, onClose }: AddNotificationModal
|
||||
return [
|
||||
{ key: 'bot_token', label: 'Bot Token', placeholder: 'Bot token from @BotFather', type: 'password', required: true },
|
||||
{ key: 'chat_id', label: 'Chat ID', placeholder: 'Your chat or group ID', type: 'text', required: true },
|
||||
// Optional forum topic (#1518). Left empty, Telegram posts to the
|
||||
// group's General topic exactly as before.
|
||||
{
|
||||
key: 'message_thread_id',
|
||||
label: t('notifications.telegramThreadId'),
|
||||
placeholder: '123',
|
||||
type: 'number',
|
||||
required: false,
|
||||
help: t('notifications.telegramThreadIdHelp'),
|
||||
},
|
||||
];
|
||||
case 'email':
|
||||
return [
|
||||
@@ -423,6 +442,9 @@ export function AddNotificationModal({ provider, onClose }: AddNotificationModal
|
||||
className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none"
|
||||
/>
|
||||
)}
|
||||
{'help' in field && (field as { help?: string }).help && (
|
||||
<p className="text-xs text-bambu-gray mt-1">{(field as { help?: string }).help}</p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user