feat(notif): Notification improvements (#914)

pull/922/head
TheCatLady 3 years ago committed by GitHub
parent 7d3da1a1d2
commit 2768155bba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -45,8 +45,8 @@ These variables are only included in media related notifications, such as reques
- `{{media_tmdbid}}` Media's TMDb ID.
- `{{media_imdbid}}` Media's IMDb ID.
- `{{media_tvdbid}}` Media's TVDB ID.
- `{{media_status}}` Media's availability status. (Ex. `AVAILABLE` or `PENDING`)
- `{{media_status4k}}` Media's 4K availability status. (Ex. `AVAILABLE` or `PENDING`)
- `{{media_status}}` Media's availability status (e.g., `AVAILABLE` or `PENDING`).
- `{{media_status4k}}` Media's 4K availability status (e.g., `AVAILABLE` or `PENDING`).
### Special Key Variables
@ -54,3 +54,4 @@ These variables must be used as a key in the JSON Payload. (Ex, `"{{extra}}": []
- `{{extra}}` This will override the value of the property to be the pre-formatted "extra" array that can come along with certain notifications. Using this variable is _not required_.
- `{{media}}` This will override the value of the property to `null` if there is no media object passed along with the notification.
- `{{request}}` This will override the value of the property to `null` if there is no request object passed along with the notification.

@ -111,6 +111,7 @@ export class MediaRequest {
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${movie.poster_path}`,
notifyUser: this.requestedBy,
media,
request: this,
});
}
@ -130,6 +131,7 @@ export class MediaRequest {
.join(', '),
},
],
request: this,
});
}
}
@ -177,6 +179,7 @@ export class MediaRequest {
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${movie.poster_path}`,
notifyUser: this.requestedBy,
media,
request: this,
}
);
} else if (this.media.mediaType === MediaType.TV) {
@ -199,6 +202,7 @@ export class MediaRequest {
.join(', '),
},
],
request: this,
}
);
}
@ -454,6 +458,7 @@ export class MediaRequest {
notifyUser: admin,
media,
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${movie.poster_path}`,
request: this,
});
});
logger.info('Sent request to Radarr', { label: 'Media Request' });
@ -659,6 +664,7 @@ export class MediaRequest {
.join(', '),
},
],
request: this,
});
});
logger.info('Sent request to Sonarr', { label: 'Media Request' });

@ -1,5 +1,6 @@
import { Notification } from '..';
import Media from '../../../entity/Media';
import { MediaRequest } from '../../../entity/MediaRequest';
import { User } from '../../../entity/User';
import { NotificationAgentConfig } from '../../settings';
@ -10,6 +11,7 @@ export interface NotificationPayload {
image?: string;
message?: string;
extra?: { name: string; value: string }[];
request?: MediaRequest;
}
export abstract class BaseAgent<T extends NotificationAgentConfig> {

@ -98,106 +98,64 @@ class DiscordAgent
const fields: Field[] = [];
if (payload.request) {
fields.push({
name: 'Requested By',
value: payload.notifyUser.displayName ?? '',
inline: true,
});
}
switch (type) {
case Notification.MEDIA_PENDING:
color = EmbedColors.ORANGE;
fields.push(
{
name: 'Requested By',
value: payload.notifyUser.displayName ?? '',
inline: true,
},
{
name: 'Status',
value: 'Pending Approval',
inline: true,
}
);
if (settings.main.applicationUrl) {
fields.push({
name: 'View Media',
value: `${settings.main.applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`,
});
}
fields.push({
name: 'Status',
value: 'Pending Approval',
inline: true,
});
break;
case Notification.MEDIA_APPROVED:
color = EmbedColors.PURPLE;
fields.push(
{
name: 'Requested By',
value: payload.notifyUser.displayName ?? '',
inline: true,
},
{
name: 'Status',
value: 'Processing Request',
inline: true,
}
);
if (settings.main.applicationUrl) {
fields.push({
name: 'View Media',
value: `${settings.main.applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`,
});
}
fields.push({
name: 'Status',
value: 'Processing',
inline: true,
});
break;
case Notification.MEDIA_AVAILABLE:
color = EmbedColors.GREEN;
fields.push(
{
name: 'Requested By',
value: payload.notifyUser.displayName ?? '',
inline: true,
},
{
name: 'Status',
value: 'Available',
inline: true,
}
);
if (settings.main.applicationUrl) {
fields.push({
name: 'View Media',
value: `${settings.main.applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`,
});
}
fields.push({
name: 'Status',
value: 'Available',
inline: true,
});
break;
case Notification.MEDIA_DECLINED:
color = EmbedColors.RED;
fields.push(
{
name: 'Requested By',
value: payload.notifyUser.displayName ?? '',
inline: true,
},
{
name: 'Status',
value: 'Declined',
inline: true,
}
);
if (settings.main.applicationUrl) {
fields.push({
name: 'View Media',
value: `${settings.main.applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`,
});
}
fields.push({
name: 'Status',
value: 'Declined',
inline: true,
});
break;
case Notification.MEDIA_FAILED:
color = EmbedColors.RED;
if (settings.main.applicationUrl) {
fields.push({
name: 'View Media',
value: `${settings.main.applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`,
});
}
fields.push({
name: 'Status',
value: 'Failed',
inline: true,
});
break;
}
if (settings.main.applicationUrl && payload.media) {
fields.push({
name: `Open in ${settings.main.applicationTitle}`,
value: `${settings.main.applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`,
});
}
return {
title: payload.subject,
description: payload.message,

@ -9,6 +9,9 @@ interface PushoverPayload {
user: string;
title: string;
message: string;
url: string;
url_title: string;
priority: number;
html: number;
}
@ -41,10 +44,19 @@ class PushoverAgent
private constructMessageDetails(
type: Notification,
payload: NotificationPayload
): { title: string; message: string } {
): {
title: string;
message: string;
url: string | undefined;
url_title: string | undefined;
priority: number;
} {
const settings = getSettings();
let messageTitle = '';
let message = '';
let url: string | undefined;
let url_title: string | undefined;
let priority = 0;
const title = payload.subject;
const plot = payload.message;
@ -53,45 +65,69 @@ class PushoverAgent
switch (type) {
case Notification.MEDIA_PENDING:
messageTitle = 'New Request';
message += `${title}\n\n`;
message += `${plot}\n\n`;
message += `<b>Requested By</b>\n${username}\n\n`;
message += `<b>Status</b>\nPending Approval\n`;
message += `<b>${title}</b>`;
if (plot) {
message += `\n${plot}`;
}
message += `\n\n<b>Requested By</b>\n${username}`;
message += `\n\n<b>Status</b>\nPending Approval`;
break;
case Notification.MEDIA_APPROVED:
messageTitle = 'Request Approved';
message += `${title}\n\n`;
message += `${plot}\n\n`;
message += `<b>Requested By</b>\n${username}\n\n`;
message += `<b>Status</b>\nProcessing Request\n`;
message += `<b>${title}</b>`;
if (plot) {
message += `\n${plot}`;
}
message += `\n\n<b>Requested By</b>\n${username}`;
message += `\n\n<b>Status</b>\nProcessing`;
break;
case Notification.MEDIA_AVAILABLE:
messageTitle = 'Now Available';
message += `${title}\n\n`;
message += `${plot}\n\n`;
message += `<b>Requested By</b>\n${username}\n\n`;
message += `<b>Status</b>\nAvailable\n`;
message += `<b>${title}</b>`;
if (plot) {
message += `\n${plot}`;
}
message += `\n\n<b>Requested By</b>\n${username}`;
message += `\n\n<b>Status</b>\nAvailable`;
break;
case Notification.MEDIA_DECLINED:
messageTitle = 'Request Declined';
message += `${title}\n\n`;
message += `${plot}\n\n`;
message += `<b>Requested By</b>\n${username}\n\n`;
message += `<b>Status</b>\nDeclined\n`;
message += `<b>${title}</b>`;
if (plot) {
message += `\n${plot}`;
}
message += `\n\n<b>Requested By</b>\n${username}`;
message += `\n\n<b>Status</b>\nDeclined`;
priority = 1;
break;
case Notification.MEDIA_FAILED:
messageTitle = 'Failed Request';
message += `<b>${title}</b>`;
if (plot) {
message += `\n${plot}`;
}
message += `\n\n<b>Requested By</b>\n${username}`;
message += `\n\n<b>Status</b>\nFailed`;
priority = 1;
break;
case Notification.TEST_NOTIFICATION:
messageTitle = 'Test Notification';
message += `${plot}\n\n`;
message += `<b>Requested By</b>\n${username}\n`;
message += `${plot}`;
break;
}
if (settings.main.applicationUrl && payload.media) {
const actionUrl = `${settings.main.applicationUrl}/${payload.media.mediaType}/${payload.media.tmdbId}`;
message += `<a href="${actionUrl}">Open in ${settings.main.applicationTitle}</a>`;
url = `${settings.main.applicationUrl}/${payload.media.mediaType}/${payload.media.tmdbId}`;
url_title = `Open in ${settings.main.applicationTitle}`;
}
return { title: messageTitle, message };
return {
title: messageTitle,
message,
url,
url_title,
priority,
};
}
public async send(
@ -104,13 +140,22 @@ class PushoverAgent
const { accessToken, userToken } = this.getSettings().options;
const { title, message } = this.constructMessageDetails(type, payload);
const {
title,
message,
url,
url_title,
priority,
} = this.constructMessageDetails(type, payload);
await axios.post(endpoint, {
token: accessToken,
user: userToken,
title: title,
message: message,
url: url,
url_title: url_title,
priority: priority,
html: 1,
} as PushoverPayload);

@ -58,79 +58,63 @@ class SlackAgent
payload: NotificationPayload
): SlackBlockEmbed {
const settings = getSettings();
let header = settings.main.applicationTitle;
let header = '';
let actionUrl: string | undefined;
const fields: EmbedField[] = [];
if (payload.request) {
fields.push({
type: 'mrkdwn',
text: `*Requested By*\n${payload.notifyUser.displayName ?? ''}`,
});
}
switch (type) {
case Notification.MEDIA_PENDING:
header = 'New Request';
fields.push(
{
type: 'mrkdwn',
text: `*Requested By*\n${payload.notifyUser.displayName ?? ''}`,
},
{
type: 'mrkdwn',
text: '*Status*\nPending Approval',
}
);
if (settings.main.applicationUrl) {
actionUrl = `${settings.main.applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`;
}
fields.push({
type: 'mrkdwn',
text: '*Status*\nPending Approval',
});
break;
case Notification.MEDIA_APPROVED:
header = 'Request Approved';
fields.push(
{
type: 'mrkdwn',
text: `*Requested By*\n${payload.notifyUser.displayName ?? ''}`,
},
{
type: 'mrkdwn',
text: '*Status*\nProcessing Request',
}
);
if (settings.main.applicationUrl) {
actionUrl = `${settings.main.applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`;
}
fields.push({
type: 'mrkdwn',
text: '*Status*\nProcessing',
});
break;
case Notification.MEDIA_AVAILABLE:
header = 'Now Available';
fields.push({
type: 'mrkdwn',
text: '*Status*\nAvailable',
});
break;
case Notification.MEDIA_DECLINED:
header = 'Request Declined';
fields.push(
{
type: 'mrkdwn',
text: `*Requested By*\n${payload.notifyUser.displayName ?? ''}`,
},
{
type: 'mrkdwn',
text: '*Status*\nDeclined',
}
);
if (settings.main.applicationUrl) {
actionUrl = `${settings.main.applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`;
}
fields.push({
type: 'mrkdwn',
text: '*Status*\nDeclined',
});
break;
case Notification.MEDIA_AVAILABLE:
header = 'Now available!';
fields.push(
{
type: 'mrkdwn',
text: `*Requested By*\n${payload.notifyUser.displayName ?? ''}`,
},
{
type: 'mrkdwn',
text: '*Status*\nAvailable',
}
);
if (settings.main.applicationUrl) {
actionUrl = `${settings.main.applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`;
}
case Notification.MEDIA_FAILED:
header = 'Failed Request';
fields.push({
type: 'mrkdwn',
text: '*Status*\nFailed',
});
break;
case Notification.TEST_NOTIFICATION:
header = 'Test Notification';
break;
}
if (settings.main.applicationUrl && payload.media) {
actionUrl = `${settings.main.applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`;
}
const blocks: EmbedBlock[] = [
{
type: 'header',
@ -139,14 +123,17 @@ class SlackAgent
text: header,
},
},
{
];
if (type !== Notification.TEST_NOTIFICATION) {
blocks.push({
type: 'section',
text: {
type: 'mrkdwn',
text: `*${payload.subject}*`,
},
},
];
});
}
if (payload.message) {
blocks.push({
@ -191,7 +178,7 @@ class SlackAgent
value: 'open_overseerr',
text: {
type: 'plain_text',
text: `Open ${settings.main.applicationTitle}`,
text: `Open in ${settings.main.applicationTitle}`,
},
},
],

@ -8,6 +8,7 @@ interface TelegramPayload {
text: string;
parse_mode: string;
chat_id: string;
disable_notification: boolean;
}
class TelegramAgent
@ -56,49 +57,59 @@ class TelegramAgent
/* eslint-disable no-useless-escape */
switch (type) {
case Notification.MEDIA_PENDING:
message += `\*New Request\*\n`;
message += `${title}\n\n`;
message += `${plot}\n\n`;
message += `\*Requested By\*\n${user}\n\n`;
message += `\*Status\*\nPending Approval\n`;
message += `\*New Request\*`;
message += `\n\n\*${title}\*`;
if (plot) {
message += `\n${plot}`;
}
message += `\n\n\*Requested By\*\n${user}`;
message += `\n\n\*Status\*\nPending Approval`;
break;
case Notification.MEDIA_APPROVED:
message += `\*Request Approved\*\n`;
message += `${title}\n\n`;
message += `${plot}\n\n`;
message += `\*Requested By\*\n${user}\n\n`;
message += `\*Status\*\nProcessing Request\n`;
message += `\*Request Approved\*`;
message += `\n\n\*${title}\*`;
if (plot) {
message += `\n${plot}`;
}
message += `\n\n\*Requested By\*\n${user}`;
message += `\n\n\*Status\*\nProcessing`;
break;
case Notification.MEDIA_AVAILABLE:
message += `\*Now Available\*`;
message += `\n\n\*${title}\*`;
if (plot) {
message += `\n${plot}\n\n`;
}
message += `\n\n\*Requested By\*\n${user}`;
message += `\n\n\*Status\*\nAvailable`;
break;
case Notification.MEDIA_DECLINED:
message += `\*Request Declined\*\n`;
message += `${title}\n\n`;
message += `${plot}\n\n`;
message += `\*Requested By\*\n${user}\n\n`;
message += `\*Status\*\nDeclined\n`;
message += `\*Request Declined\*`;
message += `\n\n\*${title}\*`;
if (plot) {
message += `\n${plot}`;
}
message += `\n\n\*Requested By\*\n${user}`;
message += `\n\n\*Status\*\nDeclined`;
break;
case Notification.MEDIA_AVAILABLE:
message += `\*Now available\\!\*\n`;
message += `${title}\n\n`;
message += `${plot}\n\n`;
message += `\*Requested By\*\n${user}\n\n`;
message += `\*Status\*\nAvailable\n`;
case Notification.MEDIA_FAILED:
message += `\*Failed Request\*`;
message += `\n\n\*${title}\*`;
if (plot) {
message += `\n${plot}`;
}
message += `\n\n\*Requested By\*\n${user}`;
message += `\n\n\*Status\*\nFailed`;
break;
case Notification.TEST_NOTIFICATION:
message += `\*Test Notification\*\n`;
message += `${title}\n\n`;
message += `${plot}\n\n`;
message += `\*Requested By\*\n${user}\n`;
message += `\*Test Notification\*`;
message += `\n\n${plot}`;
break;
}
if (settings.main.applicationUrl && payload.media) {
const actionUrl = `${settings.main.applicationUrl}/${payload.media.mediaType}/${payload.media.tmdbId}`;
message += `\[Open in ${settings.main.applicationTitle}\]\(${actionUrl}\)`;
message += `\n\n\[Open in ${settings.main.applicationTitle}\]\(${actionUrl}\)`;
}
/* eslint-enable */
@ -119,6 +130,7 @@ class TelegramAgent
text: this.buildMessage(type, payload),
parse_mode: 'MarkdownV2',
chat_id: `${this.getSettings().options.chatId}`,
disable_notification: this.getSettings().options.sendSilently,
} as TelegramPayload);
return true;

@ -27,6 +27,7 @@ const KeyMap: Record<string, string | KeyMapFunction> = {
payload.media?.status ? MediaStatus[payload.media?.status] : '',
media_status4k: (payload) =>
payload.media?.status ? MediaStatus[payload.media?.status4k] : '',
request_id: 'request.id',
};
class WebhookAgent
@ -60,6 +61,14 @@ class WebhookAgent
}
delete finalPayload[key];
key = 'media';
} else if (key === '{{request}}') {
if (payload.request) {
finalPayload.request = finalPayload[key];
} else {
finalPayload.request = null;
}
delete finalPayload[key];
key = 'request';
}
if (typeof finalPayload[key] === 'string') {

@ -107,6 +107,7 @@ export interface NotificationAgentTelegram extends NotificationAgentConfig {
options: {
botAPI: string;
chatId: string;
sendSilently: boolean;
};
}
@ -220,6 +221,7 @@ class Settings {
options: {
botAPI: '',
chatId: '',
sendSilently: false,
},
},
pushover: {

@ -35,6 +35,7 @@ export class MediaSubscriber implements EntitySubscriberInterface {
message: movie.overview,
media: entity,
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${movie.poster_path}`,
request: request,
});
});
}
@ -96,6 +97,7 @@ export class MediaSubscriber implements EntitySubscriberInterface {
.join(', '),
},
],
request: request,
});
}
}

@ -204,12 +204,10 @@ const NotificationsEmail: React.FC = () => {
</div>
<div className="form-row">
<label htmlFor="secure" className="checkbox-label">
<div className="flex flex-col">
<span>{intl.formatMessage(messages.enableSsl)}</span>
<span className="text-gray-500">
{intl.formatMessage(messages.ssldisabletip)}
</span>
</div>
<span>{intl.formatMessage(messages.enableSsl)}</span>
<span className="label-tip">
{intl.formatMessage(messages.ssldisabletip)}
</span>
</label>
<div className="form-input">
<Field type="checkbox" id="secure" name="secure" />

@ -28,6 +28,8 @@ const messages = defineMessages({
Additionally, you need the chat ID for the chat you want the bot to send notifications to.\
You can do this by adding <GetIdBotLink>@get_id_bot</GetIdBotLink> to the chat or group chat.',
notificationtypes: 'Notification Types',
sendSilently: 'Send Silently',
sendSilentlyTip: 'Send notifications with no sound',
});
const NotificationsTelegram: React.FC = () => {
@ -57,6 +59,7 @@ const NotificationsTelegram: React.FC = () => {
types: data?.types,
botAPI: data?.options.botAPI,
chatId: data?.options.chatId,
sendSilently: data?.options.sendSilently,
}}
validationSchema={NotificationsTelegramSchema}
onSubmit={async (values) => {
@ -67,6 +70,7 @@ const NotificationsTelegram: React.FC = () => {
options: {
botAPI: values.botAPI,
chatId: values.chatId,
sendSilently: values.sendSilently,
},
});
addToast(intl.formatMessage(messages.telegramsettingssaved), {
@ -91,6 +95,7 @@ const NotificationsTelegram: React.FC = () => {
options: {
botAPI: values.botAPI,
chatId: values.chatId,
sendSilently: values.sendSilently,
},
});
@ -178,6 +183,21 @@ const NotificationsTelegram: React.FC = () => {
)}
</div>
</div>
<div className="form-row">
<label htmlFor="sendSilently" className="checkbox-label">
<span>{intl.formatMessage(messages.sendSilently)}</span>
<span className="label-tip">
{intl.formatMessage(messages.sendSilentlyTip)}
</span>
</label>
<div className="form-input">
<Field
type="checkbox"
id="sendSilently"
name="sendSilently"
/>
</div>
</div>
<div
role="group"
aria-labelledby="group-label"

@ -29,6 +29,9 @@ const defaultPayload = {
status4k: '{{media_status4k}}',
},
'{{extra}}': [],
'{{request}}': {
request_id: '{{request_id}}',
},
};
const messages = defineMessages({

@ -287,6 +287,8 @@
"components.Settings.Notifications.notificationtypes": "Notification Types",
"components.Settings.Notifications.save": "Save Changes",
"components.Settings.Notifications.saving": "Saving…",
"components.Settings.Notifications.sendSilently": "Send Silently",
"components.Settings.Notifications.sendSilentlyTip": "Send notifications with no sound",
"components.Settings.Notifications.senderName": "Sender Name",
"components.Settings.Notifications.settinguptelegram": "Setting Up Telegram Notifications",
"components.Settings.Notifications.settinguptelegramDescription": "To set up Telegram, you need to <CreateBotLink>create a bot</CreateBotLink> and get the bot API key. Additionally, you need the chat ID for the chat you want the bot to send notifications to. You can do this by adding <GetIdBotLink>@get_id_bot</GetIdBotLink> to the chat or group chat.",

Loading…
Cancel
Save