Skip to content

Commit

Permalink
feat: allow users to select notification types (#1512)
Browse files Browse the repository at this point in the history
* feat: allow users to select notification types

* fix(ui): display personal notification types before management types

* fix: update allRequestsAutoApproved check to account for new REQUEST_MOVIE & REQUEST_TV perms

* fix(ui): do not display Discord notif type selector if user not eligible for any types

* refactor(ui): remove unnecessary 'enabled' checkboxes from user notif settings

* fix(ui): correct checkbox behavior

* fix: add missing return type on hasNotificationType

* refactor: remove unused isValid prop in NotificationsWebPush

* fix(ui): use SensitiveInput for users' public PGP keys

* fix(ui): add missing tip/hint for email encryption setting

* refactor(svg): use the new Discord logo

* revert(api): undo breaking change removing discordEnabled from UserSettingsNotificationsResponse

* fix(lang): update notification type descriptions for clarity

* fix(telegram): do not send users notifications of their own auto-approved requests
  • Loading branch information
TheCatLady committed Jun 4, 2021
1 parent 6a3649f commit e605989
Show file tree
Hide file tree
Showing 35 changed files with 1,066 additions and 647 deletions.
5 changes: 3 additions & 2 deletions overseerr-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1213,8 +1213,6 @@ components:
type: string
userToken:
type: string
priority:
type: number
LunaSeaSettings:
type: object
properties:
Expand Down Expand Up @@ -1599,6 +1597,9 @@ components:
nullable: true
discordEnabled:
type: boolean
discordEnabledTypes:
type: number
nullable: true
discordId:
type: string
nullable: true
Expand Down
5 changes: 4 additions & 1 deletion server/entity/UserSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ export class UserSettings {
})
public notificationTypes: Partial<NotificationAgentTypes>;

public hasNotificationType(key: NotificationAgentKey, type: Notification) {
public hasNotificationType(
key: NotificationAgentKey,
type: Notification
): boolean {
return hasNotificationType(type, this.notificationTypes[key] ?? 0);
}
}
1 change: 1 addition & 0 deletions server/interfaces/api/userSettingsInterfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface UserSettingsNotificationsResponse {
emailEnabled?: boolean;
pgpKey?: string;
discordEnabled?: boolean;
discordEnabledTypes?: number;
discordId?: string;
telegramEnabled?: boolean;
telegramBotUsername?: string;
Expand Down
2 changes: 1 addition & 1 deletion server/lib/notifications/agents/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ export abstract class BaseAgent<T extends NotificationAgentConfig> {
}

export interface NotificationAgent {
shouldSend(type: Notification): boolean;
shouldSend(): boolean;
send(type: Notification, payload: NotificationPayload): Promise<boolean>;
}
29 changes: 13 additions & 16 deletions server/lib/notifications/agents/discord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,12 +193,10 @@ class DiscordAgent
};
}

public shouldSend(type: Notification): boolean {
if (
this.getSettings().enabled &&
this.getSettings().options.webhookUrl &&
hasNotificationType(type, this.getSettings().types)
) {
public shouldSend(): boolean {
const settings = this.getSettings();

if (settings.enabled && settings.options.webhookUrl) {
return true;
}

Expand All @@ -209,6 +207,12 @@ class DiscordAgent
type: Notification,
payload: NotificationPayload
): Promise<boolean> {
const settings = this.getSettings();

if (!hasNotificationType(type, settings.types ?? 0)) {
return true;
}

logger.debug('Sending Discord notification', {
label: 'Notifications',
type: Notification[type],
Expand All @@ -218,13 +222,6 @@ class DiscordAgent
let content = undefined;

try {
const { botUsername, botAvatarUrl, webhookUrl } =
this.getSettings().options;

if (!webhookUrl) {
return false;
}

if (payload.notifyUser) {
// Mention user who submitted the request
if (
Expand Down Expand Up @@ -258,9 +255,9 @@ class DiscordAgent
.join(' ');
}

await axios.post(webhookUrl, {
username: botUsername,
avatar_url: botAvatarUrl,
await axios.post(settings.options.webhookUrl, {
username: settings.options.botUsername,
avatar_url: settings.options.botAvatarUrl,
embeds: [this.buildEmbed(type, payload)],
content,
} as DiscordWebhookPayload);
Expand Down
8 changes: 5 additions & 3 deletions server/lib/notifications/agents/email.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { EmailOptions } from 'email-templates';
import path from 'path';
import { getRepository } from 'typeorm';
import { hasNotificationType, Notification } from '..';
import { Notification } from '..';
import { MediaType } from '../../../constants/media';
import { User } from '../../../entity/User';
import logger from '../../../logger';
Expand All @@ -28,12 +28,14 @@ class EmailAgent
return settings.notifications.agents.email;
}

public shouldSend(type: Notification): boolean {
public shouldSend(): boolean {
const settings = this.getSettings();

if (
settings.enabled &&
hasNotificationType(type, this.getSettings().types)
settings.options.emailFrom &&
settings.options.smtpHost &&
settings.options.smtpPort
) {
return true;
}
Expand Down
44 changes: 24 additions & 20 deletions server/lib/notifications/agents/lunasea.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,10 @@ class LunaSeaAgent
};
}

public shouldSend(type: Notification): boolean {
if (
this.getSettings().enabled &&
this.getSettings().options.webhookUrl &&
hasNotificationType(type, this.getSettings().types)
) {
public shouldSend(): boolean {
const settings = this.getSettings();

if (settings.enabled && settings.options.webhookUrl) {
return true;
}

Expand All @@ -66,26 +64,32 @@ class LunaSeaAgent
type: Notification,
payload: NotificationPayload
): Promise<boolean> {
const settings = this.getSettings();

if (!hasNotificationType(type, settings.types ?? 0)) {
return true;
}

logger.debug('Sending LunaSea notification', {
label: 'Notifications',
type: Notification[type],
subject: payload.subject,
});

try {
const { webhookUrl, profileName } = this.getSettings().options;

if (!webhookUrl) {
return false;
}

await axios.post(webhookUrl, this.buildPayload(type, payload), {
headers: {
Authorization: `Basic ${Buffer.from(`${profileName}:`).toString(
'base64'
)}`,
},
});
await axios.post(
settings.options.webhookUrl,
this.buildPayload(type, payload),
settings.options.profileName
? {
headers: {
Authorization: `Basic ${Buffer.from(
`${settings.options.profileName}:`
).toString('base64')}`,
},
}
: undefined
);

return true;
} catch (e) {
Expand All @@ -94,7 +98,7 @@ class LunaSeaAgent
type: Notification[type],
subject: payload.subject,
errorMessage: e.message,
response: e.response.data,
response: e.response?.data,
});

return false;
Expand Down
24 changes: 12 additions & 12 deletions server/lib/notifications/agents/pushbullet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,10 @@ class PushbulletAgent
return settings.notifications.agents.pushbullet;
}

public shouldSend(type: Notification): boolean {
if (
this.getSettings().enabled &&
this.getSettings().options.accessToken &&
hasNotificationType(type, this.getSettings().types)
) {
public shouldSend(): boolean {
const settings = this.getSettings();

if (settings.enabled && settings.options.accessToken) {
return true;
}

Expand Down Expand Up @@ -137,29 +135,31 @@ class PushbulletAgent
type: Notification,
payload: NotificationPayload
): Promise<boolean> {
const settings = this.getSettings();

if (!hasNotificationType(type, settings.types ?? 0)) {
return true;
}

logger.debug('Sending Pushbullet notification', {
label: 'Notifications',
type: Notification[type],
subject: payload.subject,
});

try {
const endpoint = 'https://api.pushbullet.com/v2/pushes';

const { accessToken } = this.getSettings().options;

const { title, body } = this.constructMessageDetails(type, payload);

await axios.post(
endpoint,
'https://api.pushbullet.com/v2/pushes',
{
type: 'note',
title: title,
body: body,
} as PushbulletPayload,
{
headers: {
'Access-Token': accessToken,
'Access-Token': settings.options.accessToken,
},
}
);
Expand Down
23 changes: 14 additions & 9 deletions server/lib/notifications/agents/pushover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@ class PushoverAgent
return settings.notifications.agents.pushover;
}

public shouldSend(type: Notification): boolean {
public shouldSend(): boolean {
const settings = this.getSettings();

if (
this.getSettings().enabled &&
this.getSettings().options.accessToken &&
this.getSettings().options.userToken &&
hasNotificationType(type, this.getSettings().types)
settings.enabled &&
settings.options.accessToken &&
settings.options.userToken
) {
return true;
}
Expand Down Expand Up @@ -161,6 +162,12 @@ class PushoverAgent
type: Notification,
payload: NotificationPayload
): Promise<boolean> {
const settings = this.getSettings();

if (!hasNotificationType(type, settings.types ?? 0)) {
return true;
}

logger.debug('Sending Pushover notification', {
label: 'Notifications',
type: Notification[type],
Expand All @@ -169,14 +176,12 @@ class PushoverAgent
try {
const endpoint = 'https://api.pushover.net/1/messages.json';

const { accessToken, userToken } = this.getSettings().options;

const { title, message, url, url_title, priority } =
this.constructMessageDetails(type, payload);

await axios.post(endpoint, {
token: accessToken,
user: userToken,
token: settings.options.accessToken,
user: settings.options.userToken,
title: title,
message: message,
url: url,
Expand Down
27 changes: 14 additions & 13 deletions server/lib/notifications/agents/slack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,12 +218,10 @@ class SlackAgent
};
}

public shouldSend(type: Notification): boolean {
if (
this.getSettings().enabled &&
this.getSettings().options.webhookUrl &&
hasNotificationType(type, this.getSettings().types)
) {
public shouldSend(): boolean {
const settings = this.getSettings();

if (settings.enabled && settings.options.webhookUrl) {
return true;
}

Expand All @@ -234,19 +232,22 @@ class SlackAgent
type: Notification,
payload: NotificationPayload
): Promise<boolean> {
const settings = this.getSettings();

if (!hasNotificationType(type, settings.types ?? 0)) {
return true;
}

logger.debug('Sending Slack notification', {
label: 'Notifications',
type: Notification[type],
subject: payload.subject,
});
try {
const webhookUrl = this.getSettings().options.webhookUrl;

if (!webhookUrl) {
return false;
}

await axios.post(webhookUrl, this.buildEmbed(type, payload));
await axios.post(
settings.options.webhookUrl,
this.buildEmbed(type, payload)
);

return true;
} catch (e) {
Expand Down

0 comments on commit e605989

Please sign in to comment.