feat(notifications): added telegram thread id's (#1145)

* feat(notifications): added telegram thread id's

* undid unwanted formatting

* chore: remove manual translations

* style: conformed formatting

* fix: add missing migration

* fix: corrected erroneous migration
This commit is contained in:
astro
2024-12-21 07:54:55 +01:00
committed by GitHub
parent 1da2f258a7
commit d76d794411
11 changed files with 146 additions and 0 deletions

View File

@@ -100,6 +100,7 @@
"options": {
"botAPI": "",
"chatId": "",
"messageThreadId": "",
"sendSilently": false
}
},

View File

@@ -1338,6 +1338,8 @@ components:
type: string
chatId:
type: string
messageThreadId:
type: string
sendSilently:
type: boolean
PushbulletSettings:
@@ -1821,6 +1823,9 @@ components:
telegramChatId:
type: string
nullable: true
telegramMessageThreadId:
type: string
nullable: true
telegramSendSilently:
type: boolean
nullable: true

View File

@@ -60,6 +60,9 @@ export class UserSettings {
@Column({ nullable: true })
public telegramChatId?: string;
@Column({ nullable: true })
public telegramMessageThreadId?: string;
@Column({ nullable: true })
public telegramSendSilently?: boolean;

View File

@@ -34,6 +34,7 @@ export interface UserSettingsNotificationsResponse {
telegramEnabled?: boolean;
telegramBotUsername?: string;
telegramChatId?: string;
telegramMessageThreadId?: string;
telegramSendSilently?: boolean;
webPushEnabled?: boolean;
notificationTypes: Partial<NotificationAgentTypes>;

View File

@@ -17,6 +17,7 @@ interface TelegramMessagePayload {
text: string;
parse_mode: string;
chat_id: string;
message_thread_id: string;
disable_notification: boolean;
}
@@ -25,6 +26,7 @@ interface TelegramPhotoPayload {
caption: string;
parse_mode: string;
chat_id: string;
message_thread_id: string;
disable_notification: boolean;
}
@@ -182,6 +184,7 @@ class TelegramAgent
body: JSON.stringify({
...notificationPayload,
chat_id: settings.options.chatId,
message_thread_id: settings.options.messageThreadId,
disable_notification: !!settings.options.sendSilently,
} as TelegramMessagePayload | TelegramPhotoPayload),
});
@@ -233,6 +236,8 @@ class TelegramAgent
body: JSON.stringify({
...notificationPayload,
chat_id: payload.notifyUser.settings.telegramChatId,
message_thread_id:
payload.notifyUser.settings.telegramMessageThreadId,
disable_notification:
!!payload.notifyUser.settings.telegramSendSilently,
} as TelegramMessagePayload | TelegramPhotoPayload),
@@ -296,6 +301,7 @@ class TelegramAgent
body: JSON.stringify({
...notificationPayload,
chat_id: user.settings.telegramChatId,
message_thread_id: user.settings.telegramMessageThreadId,
disable_notification: !!user.settings?.telegramSendSilently,
} as TelegramMessagePayload | TelegramPhotoPayload),
});

View File

@@ -213,6 +213,7 @@ export interface NotificationAgentTelegram extends NotificationAgentConfig {
botUsername?: string;
botAPI: string;
chatId: string;
messageThreadId: string;
sendSilently: boolean;
};
}
@@ -423,6 +424,7 @@ class Settings {
options: {
botAPI: '',
chatId: '',
messageThreadId: '',
sendSilently: false,
},
},

View File

@@ -0,0 +1,33 @@
import type { MigrationInterface, QueryRunner } from 'typeorm';
export class AddTelegramMessageThreadId1734287582736
implements MigrationInterface
{
name = 'AddTelegramMessageThreadId1734287582736';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "temporary_user_settings" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "locale" varchar NOT NULL DEFAULT (''), "discoverRegion" varchar, "streamingRegion" varchar, "originalLanguage" varchar, "pgpKey" varchar, "discordId" varchar, "pushbulletAccessToken" varchar, "pushoverApplicationToken" varchar, "pushoverUserKey" varchar, "pushoverSound" varchar, "telegramChatId" varchar, "telegramSendSilently" boolean, "watchlistSyncMovies" boolean, "watchlistSyncTv" boolean, "notificationTypes" text, "userId" integer, "telegramMessageThreadId" varchar, CONSTRAINT "REL_986a2b6d3c05eb4091bb8066f7" UNIQUE ("userId"), CONSTRAINT "FK_986a2b6d3c05eb4091bb8066f78" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
);
await queryRunner.query(
`INSERT INTO "temporary_user_settings"("id", "locale", "discoverRegion", "streamingRegion", "originalLanguage", "pgpKey", "discordId", "pushbulletAccessToken", "pushoverApplicationToken", "pushoverUserKey", "pushoverSound", "telegramChatId", "telegramSendSilently", "watchlistSyncMovies", "watchlistSyncTv", "notificationTypes", "userId") SELECT "id", "locale", "discoverRegion", "streamingRegion", "originalLanguage", "pgpKey", "discordId", "pushbulletAccessToken", "pushoverApplicationToken", "pushoverUserKey", "pushoverSound", "telegramChatId", "telegramSendSilently", "watchlistSyncMovies", "watchlistSyncTv", "notificationTypes", "userId" FROM "user_settings"`
);
await queryRunner.query(`DROP TABLE "user_settings"`);
await queryRunner.query(
`ALTER TABLE "temporary_user_settings" RENAME TO "user_settings"`
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "user_settings" RENAME TO "temporary_user_settings"`
);
await queryRunner.query(
`CREATE TABLE "user_settings" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "locale" varchar NOT NULL DEFAULT (''), "discoverRegion" varchar, "streamingRegion" varchar, "originalLanguage" varchar, "pgpKey" varchar, "discordId" varchar, "pushbulletAccessToken" varchar, "pushoverApplicationToken" varchar, "pushoverUserKey" varchar, "pushoverSound" varchar, "telegramChatId" varchar, "telegramSendSilently" boolean, "watchlistSyncMovies" boolean, "watchlistSyncTv" boolean, "notificationTypes" text, "userId" integer, CONSTRAINT "REL_986a2b6d3c05eb4091bb8066f7" UNIQUE ("userId"), CONSTRAINT "FK_986a2b6d3c05eb4091bb8066f78" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
);
await queryRunner.query(
`INSERT INTO "user_settings"("id", "locale", "discoverRegion", "streamingRegion", "originalLanguage", "pgpKey", "discordId", "pushbulletAccessToken", "pushoverApplicationToken", "pushoverUserKey", "pushoverSound", "telegramChatId", "telegramSendSilently", "watchlistSyncMovies", "watchlistSyncTv", "notificationTypes", "userId") SELECT "id", "locale", "discoverRegion", "streamingRegion", "originalLanguage", "pgpKey", "discordId", "pushbulletAccessToken", "pushoverApplicationToken", "pushoverUserKey", "pushoverSound", "telegramChatId", "telegramSendSilently", "watchlistSyncMovies", "watchlistSyncTv", "notificationTypes", "userId" FROM "temporary_user_settings"`
);
await queryRunner.query(`DROP TABLE "temporary_user_settings"`);
}
}

View File

@@ -323,6 +323,7 @@ userSettingsRoutes.get<{ id: string }, UserSettingsNotificationsResponse>(
telegramEnabled: settings.telegram.enabled,
telegramBotUsername: settings.telegram.options.botUsername,
telegramChatId: user.settings?.telegramChatId,
telegramMessageThreadId: user.settings?.telegramMessageThreadId,
telegramSendSilently: user.settings?.telegramSendSilently,
webPushEnabled: settings.webpush.enabled,
notificationTypes: user.settings?.notificationTypes ?? {},
@@ -365,6 +366,7 @@ userSettingsRoutes.post<{ id: string }, UserSettingsNotificationsResponse>(
pushoverApplicationToken: req.body.pushoverApplicationToken,
pushoverUserKey: req.body.pushoverUserKey,
telegramChatId: req.body.telegramChatId,
telegramMessageThreadId: req.body.telegramMessageThreadId,
telegramSendSilently: req.body.telegramSendSilently,
notificationTypes: req.body.notificationTypes,
});
@@ -377,6 +379,8 @@ userSettingsRoutes.post<{ id: string }, UserSettingsNotificationsResponse>(
user.settings.pushoverUserKey = req.body.pushoverUserKey;
user.settings.pushoverSound = req.body.pushoverSound;
user.settings.telegramChatId = req.body.telegramChatId;
user.settings.telegramMessageThreadId =
req.body.telegramMessageThreadId;
user.settings.telegramSendSilently = req.body.telegramSendSilently;
user.settings.notificationTypes = Object.assign(
{},
@@ -395,6 +399,7 @@ userSettingsRoutes.post<{ id: string }, UserSettingsNotificationsResponse>(
pushoverUserKey: user.settings.pushoverUserKey,
pushoverSound: user.settings.pushoverSound,
telegramChatId: user.settings.telegramChatId,
telegramMessageThreadId: user.settings.telegramMessageThreadId,
telegramSendSilently: user.settings.telegramSendSilently,
notificationTypes: user.settings.notificationTypes,
});

View File

@@ -23,8 +23,13 @@ const messages = defineMessages('components.Settings.Notifications', {
chatId: 'Chat ID',
chatIdTip:
'Start a chat with your bot, add <GetIdBotLink>@get_id_bot</GetIdBotLink>, and issue the <code>/my_id</code> command',
messageThreadId: 'Thread/Topic ID',
messageThreadIdTip:
"If your group-chat has topics enabled, you can specify a thread/topic's ID here",
validationBotAPIRequired: 'You must provide a bot authorization token',
validationChatIdRequired: 'You must provide a valid chat ID',
validationMessageThreadId:
'The thread/topic ID must be a positive whole number',
telegramsettingssaved: 'Telegram notification settings saved successfully!',
telegramsettingsfailed: 'Telegram notification settings failed to save.',
toastTelegramTestSending: 'Sending Telegram test notification…',
@@ -64,6 +69,15 @@ const NotificationsTelegram = () => {
/^-?\d+$/,
intl.formatMessage(messages.validationChatIdRequired)
),
messageThreadId: Yup.string()
.when(['types'], {
is: (enabled: boolean, types: number) => enabled && !!types,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationMessageThreadId)),
otherwise: Yup.string().nullable(),
})
.matches(/^\d+$/, intl.formatMessage(messages.validationMessageThreadId)),
});
if (!data && !error) {
@@ -78,6 +92,7 @@ const NotificationsTelegram = () => {
botUsername: data?.options.botUsername,
botAPI: data?.options.botAPI,
chatId: data?.options.chatId,
messageThreadId: data?.options.messageThreadId,
sendSilently: data?.options.sendSilently,
}}
validationSchema={NotificationsTelegramSchema}
@@ -94,6 +109,7 @@ const NotificationsTelegram = () => {
options: {
botAPI: values.botAPI,
chatId: values.chatId,
messageThreadId: values.messageThreadId,
sendSilently: values.sendSilently,
botUsername: values.botUsername,
},
@@ -151,6 +167,7 @@ const NotificationsTelegram = () => {
options: {
botAPI: values.botAPI,
chatId: values.chatId,
messageThreadId: values.messageThreadId,
sendSilently: values.sendSilently,
botUsername: values.botUsername,
},
@@ -286,6 +303,28 @@ const NotificationsTelegram = () => {
)}
</div>
</div>
<div className="form-row">
<label htmlFor="messageThreadId" className="text-label">
{intl.formatMessage(messages.messageThreadId)}
<span className="label-tip">
{intl.formatMessage(messages.messageThreadIdTip)}
</span>
</label>
<div className="form-input-area">
<div className="form-input-field">
<Field
id="messageThreadId"
name="messageThreadId"
type="text"
/>
</div>
{errors.messageThreadId &&
touched.messageThreadId &&
typeof errors.messageThreadId === 'string' && (
<div className="error">{errors.messageThreadId}</div>
)}
</div>
</div>
<div className="form-row">
<label htmlFor="sendSilently" className="checkbox-label">
<span>{intl.formatMessage(messages.sendSilently)}</span>

View File

@@ -21,9 +21,14 @@ const messages = defineMessages(
telegramChatId: 'Chat ID',
telegramChatIdTipLong:
'<TelegramBotLink>Start a chat</TelegramBotLink>, add <GetIdBotLink>@get_id_bot</GetIdBotLink>, and issue the <code>/my_id</code> command',
telegramMessageThreadId: 'Thread/Topic ID',
telegramMessageThreadIdTip:
"If your group-chat has topics enabled, you can specify a thread/topic's ID here",
sendSilently: 'Send Silently',
sendSilentlyDescription: 'Send notifications with no sound',
validationTelegramChatId: 'You must provide a valid chat ID',
validationTelegramMessageThreadId:
'The thread/topic ID must be a positive whole number',
}
);
@@ -53,6 +58,20 @@ const UserTelegramSettings = () => {
/^-?\d+$/,
intl.formatMessage(messages.validationTelegramChatId)
),
telegramMessageThreadId: Yup.string()
.when(['types'], {
is: (enabled: boolean, types: number) => enabled && !!types,
then: Yup.string()
.nullable()
.required(
intl.formatMessage(messages.validationTelegramMessageThreadId)
),
otherwise: Yup.string().nullable(),
})
.matches(
/^\d+$/,
intl.formatMessage(messages.validationTelegramMessageThreadId)
),
});
if (!data && !error) {
@@ -63,6 +82,7 @@ const UserTelegramSettings = () => {
<Formik
initialValues={{
telegramChatId: data?.telegramChatId,
telegramMessageThreadId: data?.telegramMessageThreadId,
telegramSendSilently: data?.telegramSendSilently,
types: data?.notificationTypes.telegram ?? 0,
}}
@@ -84,6 +104,7 @@ const UserTelegramSettings = () => {
pushoverApplicationToken: data?.pushoverApplicationToken,
pushoverUserKey: data?.pushoverUserKey,
telegramChatId: values.telegramChatId,
telegramMessageThreadId: values.telegramMessageThreadId,
telegramSendSilently: values.telegramSendSilently,
notificationTypes: {
telegram: values.types,
@@ -162,6 +183,30 @@ const UserTelegramSettings = () => {
)}
</div>
</div>
<div className="form-row">
<label htmlFor="telegramMessageThreadId" className="text-label">
{intl.formatMessage(messages.telegramMessageThreadId)}
<span className="label-tip">
{intl.formatMessage(messages.telegramMessageThreadIdTip)}
</span>
</label>
<div className="form-input-area">
<div className="form-input-field">
<Field
id="telegramMessageThreadId"
name="telegramMessageThreadId"
type="text"
/>
</div>
{errors.telegramMessageThreadId &&
touched.telegramMessageThreadId &&
typeof errors.telegramMessageThreadId === 'string' && (
<div className="error">
{errors.telegramMessageThreadId}
</div>
)}
</div>
</div>
<div className="form-row">
<label htmlFor="telegramSendSilently" className="checkbox-label">
{intl.formatMessage(messages.sendSilently)}

View File

@@ -697,6 +697,8 @@
"components.Settings.Notifications.encryptionNone": "None",
"components.Settings.Notifications.encryptionOpportunisticTls": "Always use STARTTLS",
"components.Settings.Notifications.encryptionTip": "In most cases, Implicit TLS uses port 465 and STARTTLS uses port 587",
"components.Settings.Notifications.messageThreadId": "Thread/Topic ID",
"components.Settings.Notifications.messageThreadIdTip": "If your group-chat has topics enabled, you can specify a thread/topic's ID here",
"components.Settings.Notifications.pgpPassword": "PGP Password",
"components.Settings.Notifications.pgpPasswordTip": "Sign encrypted email messages using <OpenPgpLink>OpenPGP</OpenPgpLink>",
"components.Settings.Notifications.pgpPrivateKey": "PGP Private Key",
@@ -721,6 +723,7 @@
"components.Settings.Notifications.validationBotAPIRequired": "You must provide a bot authorization token",
"components.Settings.Notifications.validationChatIdRequired": "You must provide a valid chat ID",
"components.Settings.Notifications.validationEmail": "You must provide a valid email address",
"components.Settings.Notifications.validationMessageThreadId": "The thread/topic ID must be a positive whole number",
"components.Settings.Notifications.validationPgpPassword": "You must provide a PGP password",
"components.Settings.Notifications.validationPgpPrivateKey": "You must provide a valid PGP private key",
"components.Settings.Notifications.validationSmtpHostRequired": "You must provide a valid hostname or IP address",
@@ -1288,6 +1291,8 @@
"components.UserProfile.UserSettings.UserNotificationSettings.sound": "Notification Sound",
"components.UserProfile.UserSettings.UserNotificationSettings.telegramChatId": "Chat ID",
"components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTipLong": "<TelegramBotLink>Start a chat</TelegramBotLink>, add <GetIdBotLink>@get_id_bot</GetIdBotLink>, and issue the <code>/my_id</code> command",
"components.UserProfile.UserSettings.UserNotificationSettings.telegramMessageThreadId": "Thread/Topic ID",
"components.UserProfile.UserSettings.UserNotificationSettings.telegramMessageThreadIdTip": "If your group-chat has topics enabled, you can specify a thread/topic's ID here",
"components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingsfailed": "Telegram notification settings failed to save.",
"components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingssaved": "Telegram notification settings saved successfully!",
"components.UserProfile.UserSettings.UserNotificationSettings.validationDiscordId": "You must provide a valid user ID",
@@ -1296,6 +1301,7 @@
"components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverApplicationToken": "You must provide a valid application token",
"components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverUserKey": "You must provide a valid user or group key",
"components.UserProfile.UserSettings.UserNotificationSettings.validationTelegramChatId": "You must provide a valid chat ID",
"components.UserProfile.UserSettings.UserNotificationSettings.validationTelegramMessageThreadId": "The thread/topic ID must be a positive whole number",
"components.UserProfile.UserSettings.UserNotificationSettings.webpush": "Web Push",
"components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingsfailed": "Web push notification settings failed to save.",
"components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingssaved": "Web push notification settings saved successfully!",