diff --git a/app/client/api-client/types.gen.ts b/app/client/api-client/types.gen.ts
index 058f023..7fb6d77 100644
--- a/app/client/api-client/types.gen.ts
+++ b/app/client/api-client/types.gen.ts
@@ -1990,6 +1990,10 @@ export type GetScheduleNotificationsResponses = {
} | {
shoutrrrUrl: string;
type: 'custom';
+ } | {
+ type: 'telegram';
+ botToken: string;
+ chatId: string;
} | {
type: 'discord';
webhookUrl: string;
@@ -2007,7 +2011,7 @@ export type GetScheduleNotificationsResponses = {
enabled: boolean;
id: number;
name: string;
- type: 'custom' | 'discord' | 'email' | 'gotify' | 'ntfy' | 'pushover' | 'slack';
+ type: 'custom' | 'discord' | 'email' | 'gotify' | 'ntfy' | 'pushover' | 'slack' | 'telegram';
updatedAt: number;
};
destinationId: number;
@@ -2074,6 +2078,10 @@ export type UpdateScheduleNotificationsResponses = {
} | {
shoutrrrUrl: string;
type: 'custom';
+ } | {
+ type: 'telegram';
+ botToken: string;
+ chatId: string;
} | {
type: 'discord';
webhookUrl: string;
@@ -2091,7 +2099,7 @@ export type UpdateScheduleNotificationsResponses = {
enabled: boolean;
id: number;
name: string;
- type: 'custom' | 'discord' | 'email' | 'gotify' | 'ntfy' | 'pushover' | 'slack';
+ type: 'custom' | 'discord' | 'email' | 'gotify' | 'ntfy' | 'pushover' | 'slack' | 'telegram';
updatedAt: number;
};
destinationId: number;
@@ -2147,6 +2155,10 @@ export type ListNotificationDestinationsResponses = {
} | {
shoutrrrUrl: string;
type: 'custom';
+ } | {
+ type: 'telegram';
+ botToken: string;
+ chatId: string;
} | {
type: 'discord';
webhookUrl: string;
@@ -2164,7 +2176,7 @@ export type ListNotificationDestinationsResponses = {
enabled: boolean;
id: number;
name: string;
- type: 'custom' | 'discord' | 'email' | 'gotify' | 'ntfy' | 'pushover' | 'slack';
+ type: 'custom' | 'discord' | 'email' | 'gotify' | 'ntfy' | 'pushover' | 'slack' | 'telegram';
updatedAt: number;
}>;
};
@@ -2204,6 +2216,10 @@ export type CreateNotificationDestinationData = {
} | {
shoutrrrUrl: string;
type: 'custom';
+ } | {
+ type: 'telegram';
+ botToken: string;
+ chatId: string;
} | {
type: 'discord';
webhookUrl: string;
@@ -2260,6 +2276,10 @@ export type CreateNotificationDestinationResponses = {
} | {
shoutrrrUrl: string;
type: 'custom';
+ } | {
+ type: 'telegram';
+ botToken: string;
+ chatId: string;
} | {
type: 'discord';
webhookUrl: string;
@@ -2277,7 +2297,7 @@ export type CreateNotificationDestinationResponses = {
enabled: boolean;
id: number;
name: string;
- type: 'custom' | 'discord' | 'email' | 'gotify' | 'ntfy' | 'pushover' | 'slack';
+ type: 'custom' | 'discord' | 'email' | 'gotify' | 'ntfy' | 'pushover' | 'slack' | 'telegram';
updatedAt: number;
};
};
@@ -2363,6 +2383,10 @@ export type GetNotificationDestinationResponses = {
} | {
shoutrrrUrl: string;
type: 'custom';
+ } | {
+ type: 'telegram';
+ botToken: string;
+ chatId: string;
} | {
type: 'discord';
webhookUrl: string;
@@ -2380,7 +2404,7 @@ export type GetNotificationDestinationResponses = {
enabled: boolean;
id: number;
name: string;
- type: 'custom' | 'discord' | 'email' | 'gotify' | 'ntfy' | 'pushover' | 'slack';
+ type: 'custom' | 'discord' | 'email' | 'gotify' | 'ntfy' | 'pushover' | 'slack' | 'telegram';
updatedAt: number;
};
};
@@ -2420,6 +2444,10 @@ export type UpdateNotificationDestinationData = {
} | {
shoutrrrUrl: string;
type: 'custom';
+ } | {
+ type: 'telegram';
+ botToken: string;
+ chatId: string;
} | {
type: 'discord';
webhookUrl: string;
@@ -2486,6 +2514,10 @@ export type UpdateNotificationDestinationResponses = {
} | {
shoutrrrUrl: string;
type: 'custom';
+ } | {
+ type: 'telegram';
+ botToken: string;
+ chatId: string;
} | {
type: 'discord';
webhookUrl: string;
@@ -2503,7 +2535,7 @@ export type UpdateNotificationDestinationResponses = {
enabled: boolean;
id: number;
name: string;
- type: 'custom' | 'discord' | 'email' | 'gotify' | 'ntfy' | 'pushover' | 'slack';
+ type: 'custom' | 'discord' | 'email' | 'gotify' | 'ntfy' | 'pushover' | 'slack' | 'telegram';
updatedAt: number;
};
};
diff --git a/app/client/modules/notifications/components/create-notification-form.tsx b/app/client/modules/notifications/components/create-notification-form.tsx
index ee05948..c92321e 100644
--- a/app/client/modules/notifications/components/create-notification-form.tsx
+++ b/app/client/modules/notifications/components/create-notification-form.tsx
@@ -70,6 +70,11 @@ const defaultValuesForType = {
apiToken: "",
priority: 0 as const,
},
+ telegram: {
+ type: "telegram" as const,
+ botToken: "",
+ chatId: ""
+ },
custom: {
type: "custom" as const,
shoutrrrUrl: "",
@@ -146,6 +151,7 @@ export const CreateNotificationForm = ({ onSubmit, mode = "create", initialValue
Gotify
Ntfy
Pushover
+ Telegram
Custom (Shoutrrr URL)
@@ -613,6 +619,43 @@ export const CreateNotificationForm = ({ onSubmit, mode = "create", initialValue
>
)}
+ {watchedType === "telegram" && (
+ <>
+ (
+
+ Bot Token
+
+
+
+
+ Telegram bot token. Get this from BotFather when you create your bot.
+
+
+
+ )}
+ />
+ (
+
+ Chat ID
+
+
+
+
+ Telegram chat ID to send notifications to.
+
+
+
+ )}
+ />
+ >
+ )}
+
{watchedType === "custom" && (
Gotify
Ntfy
Pushover
+ Telegram
Custom
diff --git a/app/schemas/notifications.ts b/app/schemas/notifications.ts
index 33bafcc..28ff545 100644
--- a/app/schemas/notifications.ts
+++ b/app/schemas/notifications.ts
@@ -7,6 +7,7 @@ export const NOTIFICATION_TYPES = {
gotify: "gotify",
ntfy: "ntfy",
pushover: "pushover",
+ telegram: "telegram",
custom: "custom",
} as const;
@@ -64,6 +65,12 @@ export const pushoverNotificationConfigSchema = type({
priority: "-1 | 0 | 1",
});
+export const telegramNotificationConfigSchema = type({
+ type: "'telegram'",
+ botToken: "string",
+ chatId: "string",
+});
+
export const customNotificationConfigSchema = type({
type: "'custom'",
shoutrrrUrl: "string",
@@ -75,6 +82,7 @@ export const notificationConfigSchema = emailNotificationConfigSchema
.or(gotifyNotificationConfigSchema)
.or(ntfyNotificationConfigSchema)
.or(pushoverNotificationConfigSchema)
+ .or(telegramNotificationConfigSchema)
.or(customNotificationConfigSchema);
export type NotificationConfig = typeof notificationConfigSchema.infer;
diff --git a/app/server/modules/notifications/builders/index.ts b/app/server/modules/notifications/builders/index.ts
index a6e125c..fae5f3b 100644
--- a/app/server/modules/notifications/builders/index.ts
+++ b/app/server/modules/notifications/builders/index.ts
@@ -5,6 +5,7 @@ import { buildDiscordShoutrrrUrl } from "./discord";
import { buildGotifyShoutrrrUrl } from "./gotify";
import { buildNtfyShoutrrrUrl } from "./ntfy";
import { buildPushoverShoutrrrUrl } from "./pushover";
+import { buildTelegramShoutrrrUrl } from "./telegram";
import { buildCustomShoutrrrUrl } from "./custom";
export function buildShoutrrrUrl(config: NotificationConfig): string {
@@ -21,6 +22,8 @@ export function buildShoutrrrUrl(config: NotificationConfig): string {
return buildNtfyShoutrrrUrl(config);
case "pushover":
return buildPushoverShoutrrrUrl(config);
+ case "telegram":
+ return buildTelegramShoutrrrUrl(config);
case "custom":
return buildCustomShoutrrrUrl(config);
default: {
diff --git a/app/server/modules/notifications/builders/telegram.ts b/app/server/modules/notifications/builders/telegram.ts
new file mode 100644
index 0000000..3af34ad
--- /dev/null
+++ b/app/server/modules/notifications/builders/telegram.ts
@@ -0,0 +1,8 @@
+import type { NotificationConfig } from "~/schemas/notifications";
+
+export function buildTelegramShoutrrrUrl(config: Extract): string {
+ // Shoutrrr format: telegram://bottoken@telegram?channels=chatid1,chatid2
+ // config: { type: 'telegram', botToken: string, chatId: string }
+ // Do NOT encode botToken or chatId; Shoutrrr expects raw values (colon, dash, etc.)
+ return `telegram://${config.botToken}@telegram?channels=${config.chatId}`;
+}
diff --git a/app/server/modules/notifications/notifications.service.ts b/app/server/modules/notifications/notifications.service.ts
index 678dfc0..4099d8b 100644
--- a/app/server/modules/notifications/notifications.service.ts
+++ b/app/server/modules/notifications/notifications.service.ts
@@ -65,6 +65,11 @@ async function encryptSensitiveFields(config: NotificationConfig): Promise