mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
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,16 @@
|
||||
import { sortBy } from 'lodash';
|
||||
import {
|
||||
EntitySubscriberInterface,
|
||||
EventSubscriber,
|
||||
InsertEvent,
|
||||
UpdateEvent,
|
||||
} from 'typeorm';
|
||||
import TheMovieDb from '../api/themoviedb';
|
||||
import { IssueStatus, IssueType, IssueTypeName } from '../constants/issue';
|
||||
import { MediaType } from '../constants/media';
|
||||
import Issue from '../entity/Issue';
|
||||
import notificationManager, { Notification } from '../lib/notifications';
|
||||
import { Permission } from '../lib/permissions';
|
||||
|
||||
@EventSubscriber()
|
||||
export class IssueSubscriber implements EntitySubscriberInterface<Issue> {
|
||||
@@ -14,29 +18,75 @@ export class IssueSubscriber implements EntitySubscriberInterface<Issue> {
|
||||
return Issue;
|
||||
}
|
||||
|
||||
private async sendIssueCreatedNotification(entity: Issue) {
|
||||
private async sendIssueNotification(entity: Issue, type: Notification) {
|
||||
let title: string;
|
||||
let image: string;
|
||||
const tmdb = new TheMovieDb();
|
||||
if (entity.media.mediaType === MediaType.MOVIE) {
|
||||
const movie = await tmdb.getMovie({ movieId: entity.media.tmdbId });
|
||||
|
||||
title = movie.title;
|
||||
title = `${movie.title}${
|
||||
movie.release_date ? ` (${movie.release_date.slice(0, 4)})` : ''
|
||||
}`;
|
||||
image = `https://image.tmdb.org/t/p/w600_and_h900_bestv2${movie.poster_path}`;
|
||||
} else {
|
||||
const tvshow = await tmdb.getTvShow({ tvId: entity.media.tmdbId });
|
||||
|
||||
title = tvshow.name;
|
||||
title = `${tvshow.name}${
|
||||
tvshow.first_air_date ? ` (${tvshow.first_air_date.slice(0, 4)})` : ''
|
||||
}`;
|
||||
image = `https://image.tmdb.org/t/p/w600_and_h900_bestv2${tvshow.poster_path}`;
|
||||
}
|
||||
|
||||
const [firstComment] = entity.comments;
|
||||
const [firstComment] = sortBy(entity.comments, 'id');
|
||||
const extra: { name: string; value: string }[] = [];
|
||||
|
||||
notificationManager.sendNotification(Notification.ISSUE_CREATED, {
|
||||
if (entity.media.mediaType === MediaType.TV && entity.problemSeason > 0) {
|
||||
extra.push({
|
||||
name: 'Affected Season',
|
||||
value: entity.problemSeason.toString(),
|
||||
});
|
||||
|
||||
if (entity.problemEpisode > 0) {
|
||||
extra.push({
|
||||
name: 'Affected Episode',
|
||||
value: entity.problemEpisode.toString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
notificationManager.sendNotification(type, {
|
||||
event:
|
||||
type === Notification.ISSUE_CREATED
|
||||
? `New ${
|
||||
entity.issueType !== IssueType.OTHER
|
||||
? `${IssueTypeName[entity.issueType]} `
|
||||
: ''
|
||||
}Issue Reported`
|
||||
: type === Notification.ISSUE_RESOLVED
|
||||
? `${
|
||||
entity.issueType !== IssueType.OTHER
|
||||
? `${IssueTypeName[entity.issueType]} `
|
||||
: ''
|
||||
}Issue Resolved`
|
||||
: `${
|
||||
entity.issueType !== IssueType.OTHER
|
||||
? `${IssueTypeName[entity.issueType]} `
|
||||
: ''
|
||||
}Issue Reopened`,
|
||||
subject: title,
|
||||
message: firstComment.message,
|
||||
issue: entity,
|
||||
media: entity.media,
|
||||
image,
|
||||
extra,
|
||||
notifyAdmin: true,
|
||||
notifyUser:
|
||||
!entity.createdBy.hasPermission(Permission.MANAGE_ISSUES) &&
|
||||
(type === Notification.ISSUE_RESOLVED ||
|
||||
type === Notification.ISSUE_REOPENED)
|
||||
? entity.createdBy
|
||||
: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -45,6 +95,30 @@ export class IssueSubscriber implements EntitySubscriberInterface<Issue> {
|
||||
return;
|
||||
}
|
||||
|
||||
this.sendIssueCreatedNotification(event.entity);
|
||||
this.sendIssueNotification(event.entity, Notification.ISSUE_CREATED);
|
||||
}
|
||||
|
||||
public beforeUpdate(event: UpdateEvent<Issue>): void {
|
||||
if (!event.entity) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
event.entity.status === IssueStatus.RESOLVED &&
|
||||
event.databaseEntity.status !== IssueStatus.RESOLVED
|
||||
) {
|
||||
this.sendIssueNotification(
|
||||
event.entity as Issue,
|
||||
Notification.ISSUE_RESOLVED
|
||||
);
|
||||
} else if (
|
||||
event.entity.status === IssueStatus.OPEN &&
|
||||
event.databaseEntity.status !== IssueStatus.OPEN
|
||||
) {
|
||||
this.sendIssueNotification(
|
||||
event.entity as Issue,
|
||||
Notification.ISSUE_REOPENED
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user