mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
feat(notif): include requested season numbers in notifications (#1211)
This commit is contained in:
@@ -179,7 +179,7 @@ export class MediaRequest {
|
||||
subject: movie.title,
|
||||
message: movie.overview,
|
||||
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${movie.poster_path}`,
|
||||
notifyUser: this.requestedBy,
|
||||
notifyUser: autoApproved ? undefined : this.requestedBy,
|
||||
media,
|
||||
request: this,
|
||||
}
|
||||
@@ -444,15 +444,10 @@ export class MediaRequest {
|
||||
label: 'Media Request',
|
||||
}
|
||||
);
|
||||
const userRepository = getRepository(User);
|
||||
const admin = await userRepository.findOneOrFail({
|
||||
select: ['id', 'plexToken'],
|
||||
order: { id: 'ASC' },
|
||||
});
|
||||
|
||||
notificationManager.sendNotification(Notification.MEDIA_FAILED, {
|
||||
subject: movie.title,
|
||||
message: 'Movie failed to add to Radarr',
|
||||
notifyUser: admin,
|
||||
message: movie.overview,
|
||||
media,
|
||||
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${movie.poster_path}`,
|
||||
request: this,
|
||||
@@ -641,14 +636,10 @@ export class MediaRequest {
|
||||
label: 'Media Request',
|
||||
}
|
||||
);
|
||||
const userRepository = getRepository(User);
|
||||
const admin = await userRepository.findOneOrFail({
|
||||
order: { id: 'ASC' },
|
||||
});
|
||||
|
||||
notificationManager.sendNotification(Notification.MEDIA_FAILED, {
|
||||
subject: series.name,
|
||||
message: 'Series failed to add to Sonarr',
|
||||
notifyUser: admin,
|
||||
message: series.overview,
|
||||
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${series.poster_path}`,
|
||||
media,
|
||||
extra: [
|
||||
|
@@ -6,7 +6,7 @@ import { NotificationAgentConfig } from '../../settings';
|
||||
|
||||
export interface NotificationPayload {
|
||||
subject: string;
|
||||
notifyUser: User;
|
||||
notifyUser?: User;
|
||||
media?: Media;
|
||||
image?: string;
|
||||
message?: string;
|
||||
@@ -21,15 +21,9 @@ export abstract class BaseAgent<T extends NotificationAgentConfig> {
|
||||
}
|
||||
|
||||
protected abstract getSettings(): T;
|
||||
|
||||
protected userNotificationTypes: Notification[] = [
|
||||
Notification.MEDIA_APPROVED,
|
||||
Notification.MEDIA_DECLINED,
|
||||
Notification.MEDIA_AVAILABLE,
|
||||
];
|
||||
}
|
||||
|
||||
export interface NotificationAgent {
|
||||
shouldSend(type: Notification, payload: NotificationPayload): boolean;
|
||||
shouldSend(type: Notification): boolean;
|
||||
send(type: Notification, payload: NotificationPayload): Promise<boolean>;
|
||||
}
|
||||
|
@@ -107,7 +107,7 @@ class DiscordAgent
|
||||
if (payload.request) {
|
||||
fields.push({
|
||||
name: 'Requested By',
|
||||
value: payload.notifyUser.displayName ?? '',
|
||||
value: payload.request?.requestedBy.displayName ?? '',
|
||||
inline: true,
|
||||
});
|
||||
}
|
||||
@@ -217,7 +217,7 @@ class DiscordAgent
|
||||
let content = undefined;
|
||||
|
||||
if (
|
||||
this.userNotificationTypes.includes(type) &&
|
||||
payload.notifyUser &&
|
||||
payload.notifyUser.settings?.enableNotifications &&
|
||||
payload.notifyUser.settings?.discordId
|
||||
) {
|
||||
|
@@ -22,13 +22,12 @@ class EmailAgent
|
||||
return settings.notifications.agents.email;
|
||||
}
|
||||
|
||||
public shouldSend(type: Notification, payload: NotificationPayload): boolean {
|
||||
public shouldSend(type: Notification): boolean {
|
||||
const settings = this.getSettings();
|
||||
|
||||
if (
|
||||
settings.enabled &&
|
||||
hasNotificationType(type, this.getSettings().types) &&
|
||||
(payload.notifyUser.settings?.enableNotifications ?? true)
|
||||
hasNotificationType(type, this.getSettings().types)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@@ -45,9 +44,13 @@ class EmailAgent
|
||||
|
||||
// Send to all users with the manage requests permission (or admins)
|
||||
users
|
||||
.filter((user) => user.hasPermission(Permission.MANAGE_REQUESTS))
|
||||
.filter(
|
||||
(user) =>
|
||||
user.hasPermission(Permission.MANAGE_REQUESTS) &&
|
||||
(user.settings?.enableNotifications ?? true)
|
||||
)
|
||||
.forEach((user) => {
|
||||
const email = new PreparedEmail(payload.notifyUser.settings?.pgpKey);
|
||||
const email = new PreparedEmail(user.settings?.pgpKey);
|
||||
|
||||
email.send({
|
||||
template: path.join(
|
||||
@@ -62,9 +65,11 @@ class EmailAgent
|
||||
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||
}!`,
|
||||
mediaName: payload.subject,
|
||||
mediaPlot: payload.message,
|
||||
mediaExtra: payload.extra ?? [],
|
||||
imageUrl: payload.image,
|
||||
timestamp: new Date().toTimeString(),
|
||||
requestedBy: payload.notifyUser.displayName,
|
||||
requestedBy: payload.request?.requestedBy.displayName,
|
||||
actionUrl: applicationUrl
|
||||
? `${applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`
|
||||
: undefined,
|
||||
@@ -95,9 +100,13 @@ class EmailAgent
|
||||
|
||||
// Send to all users with the manage requests permission (or admins)
|
||||
users
|
||||
.filter((user) => user.hasPermission(Permission.MANAGE_REQUESTS))
|
||||
.filter(
|
||||
(user) =>
|
||||
user.hasPermission(Permission.MANAGE_REQUESTS) &&
|
||||
(user.settings?.enableNotifications ?? true)
|
||||
)
|
||||
.forEach((user) => {
|
||||
const email = new PreparedEmail(payload.notifyUser.settings?.pgpKey);
|
||||
const email = new PreparedEmail(user.settings?.pgpKey);
|
||||
|
||||
email.send({
|
||||
template: path.join(
|
||||
@@ -112,11 +121,12 @@ class EmailAgent
|
||||
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||
} could not be added to ${
|
||||
payload.media?.mediaType === MediaType.TV ? 'Sonarr' : 'Radarr'
|
||||
}`,
|
||||
}:`,
|
||||
mediaName: payload.subject,
|
||||
mediaPlot: payload.message,
|
||||
imageUrl: payload.image,
|
||||
timestamp: new Date().toTimeString(),
|
||||
requestedBy: payload.notifyUser.displayName,
|
||||
requestedBy: payload.request?.requestedBy.displayName,
|
||||
actionUrl: applicationUrl
|
||||
? `${applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`
|
||||
: undefined,
|
||||
@@ -142,6 +152,10 @@ class EmailAgent
|
||||
// This is getting main settings for the whole app
|
||||
const { applicationUrl, applicationTitle } = getSettings().main;
|
||||
try {
|
||||
if (
|
||||
payload.notifyUser &&
|
||||
payload.notifyUser.settings?.enableNotifications
|
||||
) {
|
||||
const email = new PreparedEmail(payload.notifyUser.settings?.pgpKey);
|
||||
|
||||
await email.send({
|
||||
@@ -157,9 +171,10 @@ class EmailAgent
|
||||
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||
} has been approved:`,
|
||||
mediaName: payload.subject,
|
||||
mediaExtra: payload.extra ?? [],
|
||||
imageUrl: payload.image,
|
||||
timestamp: new Date().toTimeString(),
|
||||
requestedBy: payload.notifyUser.displayName,
|
||||
requestedBy: payload.request?.requestedBy.displayName,
|
||||
actionUrl: applicationUrl
|
||||
? `${applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`
|
||||
: undefined,
|
||||
@@ -170,6 +185,8 @@ class EmailAgent
|
||||
} Request Approved`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
logger.error('Email notification failed to send', {
|
||||
@@ -189,7 +206,11 @@ class EmailAgent
|
||||
|
||||
// Send to all users with the manage requests permission (or admins)
|
||||
users
|
||||
.filter((user) => user.hasPermission(Permission.MANAGE_REQUESTS))
|
||||
.filter(
|
||||
(user) =>
|
||||
user.hasPermission(Permission.MANAGE_REQUESTS) &&
|
||||
(user.settings?.enableNotifications ?? true)
|
||||
)
|
||||
.forEach((user) => {
|
||||
const email = new PreparedEmail();
|
||||
|
||||
@@ -206,9 +227,10 @@ class EmailAgent
|
||||
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||
} has been automatically approved:`,
|
||||
mediaName: payload.subject,
|
||||
mediaExtra: payload.extra ?? [],
|
||||
imageUrl: payload.image,
|
||||
timestamp: new Date().toTimeString(),
|
||||
requestedBy: payload.notifyUser.displayName,
|
||||
requestedBy: payload.request?.requestedBy.displayName,
|
||||
actionUrl: applicationUrl
|
||||
? `${applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`
|
||||
: undefined,
|
||||
@@ -234,6 +256,10 @@ class EmailAgent
|
||||
// This is getting main settings for the whole app
|
||||
const { applicationUrl, applicationTitle } = getSettings().main;
|
||||
try {
|
||||
if (
|
||||
payload.notifyUser &&
|
||||
payload.notifyUser.settings?.enableNotifications
|
||||
) {
|
||||
const email = new PreparedEmail(payload.notifyUser.settings?.pgpKey);
|
||||
|
||||
await email.send({
|
||||
@@ -249,9 +275,10 @@ class EmailAgent
|
||||
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||
} was declined:`,
|
||||
mediaName: payload.subject,
|
||||
mediaExtra: payload.extra ?? [],
|
||||
imageUrl: payload.image,
|
||||
timestamp: new Date().toTimeString(),
|
||||
requestedBy: payload.notifyUser.displayName,
|
||||
requestedBy: payload.request?.requestedBy.displayName,
|
||||
actionUrl: applicationUrl
|
||||
? `${applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`
|
||||
: undefined,
|
||||
@@ -262,6 +289,8 @@ class EmailAgent
|
||||
} Request Declined`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
logger.error('Email notification failed to send', {
|
||||
@@ -276,6 +305,10 @@ class EmailAgent
|
||||
// This is getting main settings for the whole app
|
||||
const { applicationUrl, applicationTitle } = getSettings().main;
|
||||
try {
|
||||
if (
|
||||
payload.notifyUser &&
|
||||
payload.notifyUser.settings?.enableNotifications
|
||||
) {
|
||||
const email = new PreparedEmail(payload.notifyUser.settings?.pgpKey);
|
||||
|
||||
await email.send({
|
||||
@@ -291,9 +324,10 @@ class EmailAgent
|
||||
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||
} you requested is now available!`,
|
||||
mediaName: payload.subject,
|
||||
mediaExtra: payload.extra ?? [],
|
||||
imageUrl: payload.image,
|
||||
timestamp: new Date().toTimeString(),
|
||||
requestedBy: payload.notifyUser.displayName,
|
||||
requestedBy: payload.request?.requestedBy.displayName,
|
||||
actionUrl: applicationUrl
|
||||
? `${applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`
|
||||
: undefined,
|
||||
@@ -304,6 +338,8 @@ class EmailAgent
|
||||
} Now Available`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
logger.error('Email notification failed to send', {
|
||||
@@ -318,6 +354,7 @@ class EmailAgent
|
||||
// This is getting main settings for the whole app
|
||||
const { applicationUrl, applicationTitle } = getSettings().main;
|
||||
try {
|
||||
if (payload.notifyUser) {
|
||||
const email = new PreparedEmail(payload.notifyUser.settings?.pgpKey);
|
||||
|
||||
await email.send({
|
||||
@@ -331,6 +368,8 @@ class EmailAgent
|
||||
applicationTitle,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
logger.error('Email notification failed to send', {
|
||||
|
@@ -47,7 +47,7 @@ class PushbulletAgent
|
||||
|
||||
const title = payload.subject;
|
||||
const plot = payload.message;
|
||||
const username = payload.notifyUser.displayName;
|
||||
const username = payload.request?.requestedBy.displayName;
|
||||
|
||||
switch (type) {
|
||||
case Notification.MEDIA_PENDING:
|
||||
@@ -122,6 +122,10 @@ class PushbulletAgent
|
||||
break;
|
||||
}
|
||||
|
||||
for (const extra of payload.extra ?? []) {
|
||||
message += `\n${extra.name}: ${extra.value}`;
|
||||
}
|
||||
|
||||
return {
|
||||
title: messageTitle,
|
||||
body: message,
|
||||
|
@@ -61,7 +61,7 @@ class PushoverAgent
|
||||
|
||||
const title = payload.subject;
|
||||
const plot = payload.message;
|
||||
const username = payload.notifyUser.displayName;
|
||||
const username = payload.request?.requestedBy.displayName;
|
||||
|
||||
switch (type) {
|
||||
case Notification.MEDIA_PENDING:
|
||||
@@ -70,10 +70,10 @@ class PushoverAgent
|
||||
} Request`;
|
||||
message += `<b>${title}</b>`;
|
||||
if (plot) {
|
||||
message += `\n${plot}`;
|
||||
message += `<small>\n${plot}</small>`;
|
||||
}
|
||||
message += `\n\n<b>Requested By</b>\n${username}`;
|
||||
message += `\n\n<b>Status</b>\nPending Approval`;
|
||||
message += `<small>\n\n<b>Requested By</b>\n${username}</small>`;
|
||||
message += `<small>\n\n<b>Status</b>\nPending Approval</small>`;
|
||||
break;
|
||||
case Notification.MEDIA_APPROVED:
|
||||
messageTitle = `${
|
||||
@@ -81,10 +81,10 @@ class PushoverAgent
|
||||
} Request Approved`;
|
||||
message += `<b>${title}</b>`;
|
||||
if (plot) {
|
||||
message += `\n${plot}`;
|
||||
message += `<small>\n${plot}</small>`;
|
||||
}
|
||||
message += `\n\n<b>Requested By</b>\n${username}`;
|
||||
message += `\n\n<b>Status</b>\nProcessing`;
|
||||
message += `<small>\n\n<b>Requested By</b>\n${username}</small>`;
|
||||
message += `<small>\n\n<b>Status</b>\nProcessing</small>`;
|
||||
break;
|
||||
case Notification.MEDIA_AUTO_APPROVED:
|
||||
messageTitle = `${
|
||||
@@ -92,10 +92,10 @@ class PushoverAgent
|
||||
} Request Automatically Approved`;
|
||||
message += `<b>${title}</b>`;
|
||||
if (plot) {
|
||||
message += `\n${plot}`;
|
||||
message += `<small>\n${plot}</small>`;
|
||||
}
|
||||
message += `\n\n<b>Requested By</b>\n${username}`;
|
||||
message += `\n\n<b>Status</b>\nProcessing`;
|
||||
message += `<small>\n\n<b>Requested By</b>\n${username}</small>`;
|
||||
message += `<small>\n\n<b>Status</b>\nProcessing</small>`;
|
||||
break;
|
||||
case Notification.MEDIA_AVAILABLE:
|
||||
messageTitle = `${
|
||||
@@ -103,10 +103,10 @@ class PushoverAgent
|
||||
} Now Available`;
|
||||
message += `<b>${title}</b>`;
|
||||
if (plot) {
|
||||
message += `\n${plot}`;
|
||||
message += `<small>\n${plot}</small>`;
|
||||
}
|
||||
message += `\n\n<b>Requested By</b>\n${username}`;
|
||||
message += `\n\n<b>Status</b>\nAvailable`;
|
||||
message += `<small>\n\n<b>Requested By</b>\n${username}</small>`;
|
||||
message += `<small>\n\n<b>Status</b>\nAvailable</small>`;
|
||||
break;
|
||||
case Notification.MEDIA_DECLINED:
|
||||
messageTitle = `${
|
||||
@@ -114,10 +114,10 @@ class PushoverAgent
|
||||
} Request Declined`;
|
||||
message += `<b>${title}</b>`;
|
||||
if (plot) {
|
||||
message += `\n${plot}`;
|
||||
message += `<small>\n${plot}</small>`;
|
||||
}
|
||||
message += `\n\n<b>Requested By</b>\n${username}`;
|
||||
message += `\n\n<b>Status</b>\nDeclined`;
|
||||
message += `<small>\n\n<b>Requested By</b>\n${username}</small>`;
|
||||
message += `<small>\n\n<b>Status</b>\nDeclined</small>`;
|
||||
priority = 1;
|
||||
break;
|
||||
case Notification.MEDIA_FAILED:
|
||||
@@ -126,18 +126,22 @@ class PushoverAgent
|
||||
} Request`;
|
||||
message += `<b>${title}</b>`;
|
||||
if (plot) {
|
||||
message += `\n${plot}`;
|
||||
message += `<small>\n${plot}</small>`;
|
||||
}
|
||||
message += `\n\n<b>Requested By</b>\n${username}`;
|
||||
message += `\n\n<b>Status</b>\nFailed`;
|
||||
message += `<small>\n\n<b>Requested By</b>\n${username}</small>`;
|
||||
message += `<small>\n\n<b>Status</b>\nFailed</small>`;
|
||||
priority = 1;
|
||||
break;
|
||||
case Notification.TEST_NOTIFICATION:
|
||||
messageTitle = 'Test Notification';
|
||||
message += `${plot}`;
|
||||
message += `<small>${plot}</small>`;
|
||||
break;
|
||||
}
|
||||
|
||||
for (const extra of payload.extra ?? []) {
|
||||
message += `<small>\n\n<b>${extra.name}</b>\n${extra.value}</small>`;
|
||||
}
|
||||
|
||||
if (settings.main.applicationUrl && payload.media) {
|
||||
url = `${settings.main.applicationUrl}/${payload.media.mediaType}/${payload.media.tmdbId}`;
|
||||
url_title = `Open in ${settings.main.applicationTitle}`;
|
||||
|
@@ -67,7 +67,9 @@ class SlackAgent
|
||||
if (payload.request) {
|
||||
fields.push({
|
||||
type: 'mrkdwn',
|
||||
text: `*Requested By*\n${payload.notifyUser.displayName ?? ''}`,
|
||||
text: `*Requested By*\n${
|
||||
payload.request?.requestedBy.displayName ?? ''
|
||||
}`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -131,6 +133,13 @@ class SlackAgent
|
||||
break;
|
||||
}
|
||||
|
||||
for (const extra of payload.extra ?? []) {
|
||||
fields.push({
|
||||
type: 'mrkdwn',
|
||||
text: `*${extra.name}*\n${extra.value}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (settings.main.applicationUrl && payload.media) {
|
||||
actionUrl = `${settings.main.applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`;
|
||||
}
|
||||
|
@@ -61,7 +61,7 @@ class TelegramAgent
|
||||
|
||||
const title = this.escapeText(payload.subject);
|
||||
const plot = this.escapeText(payload.message);
|
||||
const user = this.escapeText(payload.notifyUser.displayName);
|
||||
const user = this.escapeText(payload.request?.requestedBy.displayName);
|
||||
const applicationTitle = this.escapeText(settings.main.applicationTitle);
|
||||
|
||||
/* eslint-disable no-useless-escape */
|
||||
@@ -138,6 +138,10 @@ class TelegramAgent
|
||||
break;
|
||||
}
|
||||
|
||||
for (const extra of payload.extra ?? []) {
|
||||
message += `\n\n\*${extra.name}\*\n${extra.value}`;
|
||||
}
|
||||
|
||||
if (settings.main.applicationUrl && payload.media) {
|
||||
const actionUrl = `${settings.main.applicationUrl}/${payload.media.mediaType}/${payload.media.tmdbId}`;
|
||||
message += `\n\n\[Open in ${applicationTitle}\]\(${actionUrl}\)`;
|
||||
@@ -175,7 +179,7 @@ class TelegramAgent
|
||||
|
||||
// Send user notification
|
||||
if (
|
||||
this.userNotificationTypes.includes(type) &&
|
||||
payload.notifyUser &&
|
||||
payload.notifyUser.settings?.enableNotifications &&
|
||||
payload.notifyUser.settings?.telegramChatId &&
|
||||
payload.notifyUser.settings?.telegramChatId !==
|
||||
|
@@ -50,7 +50,7 @@ class NotificationManager {
|
||||
label: 'Notifications',
|
||||
});
|
||||
this.activeAgents.forEach((agent) => {
|
||||
if (settings.enabled && agent.shouldSend(type, payload)) {
|
||||
if (settings.enabled && agent.shouldSend(type)) {
|
||||
agent.send(type, payload);
|
||||
}
|
||||
});
|
||||
|
@@ -42,7 +42,6 @@ div(role='article' aria-roledescription='email' aria-label='' lang='en')
|
||||
table(style='width: 100%' width='100%' cellpadding='0' cellspacing='0' role='presentation')
|
||||
tr
|
||||
td(align='center' style='\
|
||||
font-size: 16px;\
|
||||
padding-top: 25px;\
|
||||
padding-bottom: 25px;\
|
||||
text-align: center;\
|
||||
@@ -50,7 +49,7 @@ div(role='article' aria-roledescription='email' aria-label='' lang='en')
|
||||
a(href=applicationUrl style='\
|
||||
text-shadow: 0 1px 0 #ffffff;\
|
||||
font-weight: 700;\
|
||||
font-size: 16px;\
|
||||
font-size: 24px;\
|
||||
color: #a8aaaf;\
|
||||
text-decoration: none;\
|
||||
')
|
||||
@@ -70,11 +69,15 @@ div(role='article' aria-roledescription='email' aria-label='' lang='en')
|
||||
br
|
||||
br
|
||||
p(style='margin-top: 4px; text-align: center')
|
||||
b
|
||||
| #{mediaName}
|
||||
table(cellpadding='0' cellspacing='0' role='presentation')
|
||||
each extra in mediaExtra
|
||||
br
|
||||
| #{extra.name}:
|
||||
| #{extra.value}
|
||||
table(align='center' cellpadding='0' cellspacing='0' role='presentation')
|
||||
tr
|
||||
td
|
||||
table(cellpadding='0' cellspacing='0' role='presentation')
|
||||
a(href=actionUrl style='color: #3869d4')
|
||||
img(src=imageUrl alt='')
|
||||
p(style='\
|
||||
|
Reference in New Issue
Block a user