mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00

* 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
119 lines
3.3 KiB
TypeScript
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;
|