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:
TheCatLady
2021-12-04 07:24:26 -05:00
committed by GitHub
parent 6245be1e10
commit c9ffac33f7
30 changed files with 1014 additions and 804 deletions

View File

@@ -1,6 +1,6 @@
import axios from 'axios';
import { hasNotificationType, Notification } from '..';
import { MediaType } from '../../../constants/media';
import { IssueStatus, IssueTypeName } from '../../../constants/issue';
import logger from '../../../logger';
import { getSettings, NotificationAgentSlack } from '../../settings';
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
@@ -19,9 +19,10 @@ interface TextItem {
interface Element {
type: 'button';
text?: TextItem;
value: string;
url: string;
action_id: 'button-action';
action_id: string;
url?: string;
value?: string;
style?: 'primary' | 'danger';
}
interface EmbedBlock {
@@ -34,7 +35,7 @@ interface EmbedBlock {
image_url: string;
alt_text: string;
};
elements?: Element[];
elements?: (Element | TextItem)[];
}
interface SlackBlockEmbed {
@@ -59,9 +60,7 @@ class SlackAgent
type: Notification,
payload: NotificationPayload
): SlackBlockEmbed {
const settings = getSettings();
let header = '';
let actionUrl: string | undefined;
const { applicationUrl, applicationTitle } = getSettings().main;
const fields: EmbedField[] = [];
@@ -70,66 +69,55 @@ class SlackAgent
type: 'mrkdwn',
text: `*Requested By*\n${payload.request.requestedBy.displayName}`,
});
}
switch (type) {
case Notification.MEDIA_PENDING:
header = `New ${
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
} Request`;
let status = '';
switch (type) {
case Notification.MEDIA_PENDING:
status = 'Pending Approval';
break;
case Notification.MEDIA_APPROVED:
case Notification.MEDIA_AUTO_APPROVED:
status = 'Processing';
break;
case Notification.MEDIA_AVAILABLE:
status = 'Available';
break;
case Notification.MEDIA_DECLINED:
status = 'Declined';
break;
case Notification.MEDIA_FAILED:
status = 'Failed';
break;
}
if (status) {
fields.push({
type: 'mrkdwn',
text: '*Status*\nPending Approval',
text: `*Request Status*\n${status}`,
});
break;
case Notification.MEDIA_APPROVED:
header = `${
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
} Request Approved`;
fields.push({
}
} else if (payload.comment) {
fields.push({
type: 'mrkdwn',
text: `*Comment from ${payload.comment.user.displayName}*\n${payload.comment.message}`,
});
} else if (payload.issue) {
fields.push(
{
type: 'mrkdwn',
text: '*Status*\nProcessing',
});
break;
case Notification.MEDIA_AUTO_APPROVED:
header = `${
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
} Request Automatically Approved`;
fields.push({
text: `*Reported By*\n${payload.issue.createdBy.displayName}`,
},
{
type: 'mrkdwn',
text: '*Status*\nProcessing',
});
break;
case Notification.MEDIA_AVAILABLE:
header = `${
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
} Now Available`;
fields.push({
text: `*Issue Type*\n${IssueTypeName[payload.issue.issueType]}`,
},
{
type: 'mrkdwn',
text: '*Status*\nAvailable',
});
break;
case Notification.MEDIA_DECLINED:
header = `${
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
} Request Declined`;
fields.push({
type: 'mrkdwn',
text: '*Status*\nDeclined',
});
break;
case Notification.MEDIA_FAILED:
header = `Failed ${
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
} Request`;
fields.push({
type: 'mrkdwn',
text: '*Status*\nFailed',
});
break;
case Notification.TEST_NOTIFICATION:
header = 'Test Notification';
break;
text: `*Issue Status*\n${
payload.issue.status === IssueStatus.OPEN ? 'Open' : 'Resolved'
}`,
}
);
}
for (const extra of payload.extra ?? []) {
@@ -139,30 +127,28 @@ class SlackAgent
});
}
if (settings.main.applicationUrl && payload.media) {
actionUrl = `${settings.main.applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`;
}
const blocks: EmbedBlock[] = [];
const blocks: EmbedBlock[] = [
{
type: 'header',
text: {
type: 'plain_text',
text: header,
},
},
];
if (type !== Notification.TEST_NOTIFICATION) {
if (payload.event) {
blocks.push({
type: 'section',
text: {
type: 'mrkdwn',
text: `*${payload.subject}*`,
},
type: 'context',
elements: [
{
type: 'mrkdwn',
text: `*${payload.event}*`,
},
],
});
}
blocks.push({
type: 'header',
text: {
type: 'plain_text',
text: payload.subject,
},
});
if (payload.message) {
blocks.push({
type: 'section',
@@ -183,30 +169,31 @@ class SlackAgent
if (fields.length > 0) {
blocks.push({
type: 'section',
fields: [
...fields,
...(payload.extra ?? []).map(
(extra): EmbedField => ({
type: 'mrkdwn',
text: `*${extra.name}*\n${extra.value}`,
})
),
],
fields,
});
}
if (actionUrl) {
const url = applicationUrl
? payload.issue
? `${applicationUrl}/issue/${payload.issue.id}`
: payload.media
? `${applicationUrl}/${payload.media.mediaType}/${payload.media.tmdbId}`
: undefined
: undefined;
if (url) {
blocks.push({
type: 'actions',
elements: [
{
action_id: 'button-action',
action_id: 'open-in-overseerr',
type: 'button',
url: actionUrl,
value: 'open_overseerr',
url,
text: {
type: 'plain_text',
text: `Open in ${settings.main.applicationTitle}`,
text: `View ${
payload.issue ? 'Issue' : 'Media'
} in ${applicationTitle}`,
},
},
],