Files
sct-overseerr/server/lib/notifications/index.ts
TheCatLady c9ffac33f7 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
2021-12-04 21:24:26 +09:00

119 lines
3.3 KiB
TypeScript

import { User } from '../../entity/User';
import logger from '../../logger';
import { Permission } from '../permissions';
import type { NotificationAgent, NotificationPayload } from './agents/agent';
export enum Notification {
NONE = 0,
MEDIA_PENDING = 2,
MEDIA_APPROVED = 4,
MEDIA_AVAILABLE = 8,
MEDIA_FAILED = 16,
TEST_NOTIFICATION = 32,
MEDIA_DECLINED = 64,
MEDIA_AUTO_APPROVED = 128,
ISSUE_CREATED = 256,
ISSUE_COMMENT = 512,
ISSUE_RESOLVED = 1024,
ISSUE_REOPENED = 2048,
}
export const hasNotificationType = (
types: Notification | Notification[],
value: number
): boolean => {
let total = 0;
// If we are not checking any notifications, bail out and return true
if (types === 0) {
return true;
}
if (Array.isArray(types)) {
// Combine all notification values into one
total = types.reduce((a, v) => a + v, 0);
} else {
total = types;
}
// Test notifications don't need to be enabled
if (!(value & Notification.TEST_NOTIFICATION)) {
value += Notification.TEST_NOTIFICATION;
}
return !!(value & total);
};
export const getAdminPermission = (type: Notification): Permission => {
switch (type) {
case Notification.MEDIA_PENDING:
case Notification.MEDIA_APPROVED:
case Notification.MEDIA_AVAILABLE:
case Notification.MEDIA_FAILED:
case Notification.MEDIA_DECLINED:
case Notification.MEDIA_AUTO_APPROVED:
return Permission.MANAGE_REQUESTS;
case Notification.ISSUE_CREATED:
case Notification.ISSUE_COMMENT:
case Notification.ISSUE_RESOLVED:
case Notification.ISSUE_REOPENED:
return Permission.MANAGE_ISSUES;
default:
return Permission.ADMIN;
}
};
export const shouldSendAdminNotification = (
type: Notification,
user: User,
payload: NotificationPayload
): boolean => {
return (
user.id !== payload.notifyUser?.id &&
user.hasPermission(getAdminPermission(type)) &&
// Check if the user submitted this request (on behalf of themself OR another user)
(type !== Notification.MEDIA_AUTO_APPROVED ||
user.id !==
(payload.request?.modifiedBy ?? payload.request?.requestedBy)?.id) &&
// Check if the user created this issue
(type !== Notification.ISSUE_CREATED ||
user.id !== payload.issue?.createdBy.id) &&
// Check if the user submitted this issue comment
(type !== Notification.ISSUE_COMMENT ||
user.id !== payload.comment?.user.id) &&
// Check if the user resolved/reopened this issue
((type !== Notification.ISSUE_RESOLVED &&
type !== Notification.ISSUE_REOPENED) ||
user.id !== payload.issue?.modifiedBy?.id)
);
};
class NotificationManager {
private activeAgents: NotificationAgent[] = [];
public registerAgents = (agents: NotificationAgent[]): void => {
this.activeAgents = [...this.activeAgents, ...agents];
logger.info('Registered notification agents', { label: 'Notifications' });
};
public sendNotification(
type: Notification,
payload: NotificationPayload
): void {
logger.info(`Sending notification(s) for ${Notification[type]}`, {
label: 'Notifications',
subject: payload.subject,
});
this.activeAgents.forEach((agent) => {
if (agent.shouldSend()) {
agent.send(type, payload);
}
});
}
}
const notificationManager = new NotificationManager();
export default notificationManager;