feat(notif): issue notifications (#2242)
* feat(notif): issue notifications * refactor: dedupe test notification strings * fix: webhook key parsing * fix(notif): skip send for admin who requested on behalf of another user * fix(notif): send comment notifs to admins when other admins reply * fix(notif): also send resolved notifs to admins, and reopened notifs to issue creator * fix: don't send duplicate notifications * fix(lang): tweak notification description strings * fix(notif): tweak Slack notification styling * fix(notif): tweak Pushbullet & Telegram notification styling * docs: reformat webhooks page * fix(notif): add missing issue_type & issue_status variables to LunaSea notif payloads * fix: explicitly attach media & issue objects where applicable * fix(notif): correctly notify both notifyUser and managers where applicable * fix: update default webhook payload for new installs * fix(notif): add missing comment_message to LunaSea notif payload * refactor(sw): simplify notificationclick event listener logic * fix(notif): add missing event description for MEDIA_AVAILABLE notifications
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
import { EmailOptions } from 'email-templates';
|
||||
import path from 'path';
|
||||
import { getRepository } from 'typeorm';
|
||||
import { Notification } from '..';
|
||||
import { Notification, shouldSendAdminNotification } from '..';
|
||||
import { IssueType, IssueTypeName } from '../../../constants/issue';
|
||||
import { MediaType } from '../../../constants/media';
|
||||
import { User } from '../../../entity/User';
|
||||
import logger from '../../../logger';
|
||||
import PreparedEmail from '../../email';
|
||||
import { Permission } from '../../permissions';
|
||||
import {
|
||||
getSettings,
|
||||
NotificationAgentEmail,
|
||||
@@ -67,59 +67,34 @@ class EmailAgent
|
||||
};
|
||||
}
|
||||
|
||||
if (payload.media) {
|
||||
let requestType = '';
|
||||
const mediaType = payload.media
|
||||
? payload.media.mediaType === MediaType.MOVIE
|
||||
? 'movie'
|
||||
: 'series'
|
||||
: undefined;
|
||||
|
||||
if (payload.request) {
|
||||
let body = '';
|
||||
|
||||
switch (type) {
|
||||
case Notification.MEDIA_PENDING:
|
||||
requestType = `New ${
|
||||
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||
} Request`;
|
||||
body = `A user has requested a new ${
|
||||
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||
}!`;
|
||||
body = `A new request for the following ${mediaType} is pending approval:`;
|
||||
break;
|
||||
case Notification.MEDIA_APPROVED:
|
||||
requestType = `${
|
||||
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||
} Request Approved`;
|
||||
body = `Your request for the following ${
|
||||
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||
} has been approved:`;
|
||||
body = `Your request for the following ${mediaType} has been approved:`;
|
||||
break;
|
||||
case Notification.MEDIA_AUTO_APPROVED:
|
||||
requestType = `${
|
||||
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||
} Request Automatically Approved`;
|
||||
body = `A new request for the following ${
|
||||
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||
} has been automatically approved:`;
|
||||
body = `A new request for the following ${mediaType} has been automatically approved:`;
|
||||
break;
|
||||
case Notification.MEDIA_AVAILABLE:
|
||||
requestType = `${
|
||||
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||
} Now Available`;
|
||||
body = `The following ${
|
||||
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||
} you requested is now available!`;
|
||||
body = `Your request for the following ${mediaType} is now available:`;
|
||||
break;
|
||||
case Notification.MEDIA_DECLINED:
|
||||
requestType = `${
|
||||
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||
} Request Declined`;
|
||||
body = `Your request for the following ${
|
||||
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||
} was declined:`;
|
||||
body = `Your request for the following ${mediaType} was declined:`;
|
||||
break;
|
||||
case Notification.MEDIA_FAILED:
|
||||
requestType = `Failed ${
|
||||
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||
} Request`;
|
||||
body = `A new request for the following ${
|
||||
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||
} could not be added to ${
|
||||
payload.media?.mediaType === MediaType.TV ? 'Sonarr' : 'Radarr'
|
||||
body = `A request for the following ${mediaType} failed to be added to ${
|
||||
payload.media?.mediaType === MediaType.MOVIE ? 'Radarr' : 'Sonarr'
|
||||
}:`;
|
||||
break;
|
||||
}
|
||||
@@ -133,14 +108,13 @@ class EmailAgent
|
||||
to: recipientEmail,
|
||||
},
|
||||
locals: {
|
||||
requestType,
|
||||
event: payload.event,
|
||||
body,
|
||||
mediaName: payload.subject,
|
||||
mediaPlot: payload.message,
|
||||
mediaExtra: payload.extra ?? [],
|
||||
imageUrl: payload.image,
|
||||
timestamp: new Date().toTimeString(),
|
||||
requestedBy: payload.request?.requestedBy.displayName,
|
||||
requestedBy: payload.request.requestedBy.displayName,
|
||||
actionUrl: applicationUrl
|
||||
? `${applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`
|
||||
: undefined,
|
||||
@@ -150,6 +124,52 @@ class EmailAgent
|
||||
recipientEmail,
|
||||
},
|
||||
};
|
||||
} else if (payload.issue) {
|
||||
const issueType =
|
||||
payload.issue && payload.issue.issueType !== IssueType.OTHER
|
||||
? `${IssueTypeName[payload.issue.issueType].toLowerCase()} issue`
|
||||
: 'issue';
|
||||
|
||||
let body = '';
|
||||
|
||||
switch (type) {
|
||||
case Notification.ISSUE_CREATED:
|
||||
body = `A new ${issueType} has been reported by ${payload.issue.createdBy.displayName} for the ${mediaType} ${payload.subject}:`;
|
||||
break;
|
||||
case Notification.ISSUE_COMMENT:
|
||||
body = `${payload.comment?.user.displayName} commented on the ${issueType} for the ${mediaType} ${payload.subject}:`;
|
||||
break;
|
||||
case Notification.ISSUE_RESOLVED:
|
||||
body = `The ${issueType} for the ${mediaType} ${payload.subject} was marked as resolved by ${payload.issue.modifiedBy?.displayName}!`;
|
||||
break;
|
||||
case Notification.ISSUE_REOPENED:
|
||||
body = `The ${issueType} for the ${mediaType} ${payload.subject} was reopened by ${payload.issue.modifiedBy?.displayName}.`;
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
template: path.join(__dirname, '../../../templates/email/media-issue'),
|
||||
message: {
|
||||
to: recipientEmail,
|
||||
},
|
||||
locals: {
|
||||
event: payload.event,
|
||||
body,
|
||||
issueDescription: payload.message,
|
||||
issueComment: payload.comment?.message,
|
||||
mediaName: payload.subject,
|
||||
extra: payload.extra ?? [],
|
||||
imageUrl: payload.image,
|
||||
timestamp: new Date().toTimeString(),
|
||||
actionUrl: applicationUrl
|
||||
? `${applicationUrl}/issue/${payload.issue.id}`
|
||||
: undefined,
|
||||
applicationUrl,
|
||||
applicationTitle,
|
||||
recipientName,
|
||||
recipientEmail,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -160,7 +180,6 @@ class EmailAgent
|
||||
payload: NotificationPayload
|
||||
): Promise<boolean> {
|
||||
if (payload.notifyUser) {
|
||||
// Send notification to the user who submitted the request
|
||||
if (
|
||||
!payload.notifyUser.settings ||
|
||||
// Check if user has email notifications enabled and fallback to true if undefined
|
||||
@@ -203,8 +222,9 @@ class EmailAgent
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Send notifications to all users with the Manage Requests permission
|
||||
}
|
||||
|
||||
if (payload.notifyAdmin) {
|
||||
const userRepository = getRepository(User);
|
||||
const users = await userRepository.find();
|
||||
|
||||
@@ -212,7 +232,6 @@ class EmailAgent
|
||||
users
|
||||
.filter(
|
||||
(user) =>
|
||||
user.hasPermission(Permission.MANAGE_REQUESTS) &&
|
||||
(!user.settings ||
|
||||
// Check if user has email notifications enabled and fallback to true if undefined
|
||||
// since email should default to true
|
||||
@@ -221,9 +240,7 @@ class EmailAgent
|
||||
type
|
||||
) ??
|
||||
true)) &&
|
||||
// Check if it's the user's own auto-approved request
|
||||
(type !== Notification.MEDIA_AUTO_APPROVED ||
|
||||
user.id !== payload.request?.requestedBy.id)
|
||||
shouldSendAdminNotification(type, user, payload)
|
||||
)
|
||||
.map(async (user) => {
|
||||
logger.debug('Sending email notification', {
|
||||
|
||||
Reference in New Issue
Block a user