feat(notif): add Pushbullet and Pushover agents to user notification settings (#1740)

* feat(notif): add Pushbullet and Pushover agents to user notification settings

* docs(notif): add "hint" about user notifications to Pushbullet and Pushover pages

* fix: regenerate DB migration
This commit is contained in:
TheCatLady
2021-10-25 06:46:05 -04:00
committed by GitHub
parent ab20c21184
commit aeb7a48d72
25 changed files with 914 additions and 155 deletions

View File

@@ -1,11 +1,19 @@
import axios from 'axios';
import { getRepository } from 'typeorm';
import { hasNotificationType, Notification } from '..';
import { MediaType } from '../../../constants/media';
import { User } from '../../../entity/User';
import logger from '../../../logger';
import { getSettings, NotificationAgentPushbullet } from '../../settings';
import { Permission } from '../../permissions';
import {
getSettings,
NotificationAgentKey,
NotificationAgentPushbullet,
} from '../../settings';
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
interface PushbulletPayload {
type: string;
title: string;
body: string;
}
@@ -25,22 +33,13 @@ class PushbulletAgent
}
public shouldSend(): boolean {
const settings = this.getSettings();
if (settings.enabled && settings.options.accessToken) {
return true;
}
return false;
return true;
}
private constructMessageDetails(
private getNotificationPayload(
type: Notification,
payload: NotificationPayload
): {
title: string;
body: string;
} {
): PushbulletPayload {
let messageTitle = '';
let message = '';
@@ -126,6 +125,7 @@ class PushbulletAgent
}
return {
type: 'note',
title: messageTitle,
body: message,
};
@@ -136,46 +136,132 @@ class PushbulletAgent
payload: NotificationPayload
): Promise<boolean> {
const settings = this.getSettings();
const endpoint = 'https://api.pushbullet.com/v2/pushes';
const notificationPayload = this.getNotificationPayload(type, payload);
if (!hasNotificationType(type, settings.types ?? 0)) {
return true;
}
logger.debug('Sending Pushbullet notification', {
label: 'Notifications',
type: Notification[type],
subject: payload.subject,
});
try {
const { title, body } = this.constructMessageDetails(type, payload);
await axios.post(
'https://api.pushbullet.com/v2/pushes',
{
type: 'note',
title: title,
body: body,
} as PushbulletPayload,
{
headers: {
'Access-Token': settings.options.accessToken,
},
}
);
return true;
} catch (e) {
logger.error('Error sending Pushbullet notification', {
// Send system notification
if (
hasNotificationType(type, settings.types ?? 0) &&
settings.enabled &&
settings.options.accessToken
) {
logger.debug('Sending Pushbullet notification', {
label: 'Notifications',
type: Notification[type],
subject: payload.subject,
errorMessage: e.message,
response: e.response?.data,
});
return false;
try {
await axios.post(endpoint, notificationPayload, {
headers: {
'Access-Token': settings.options.accessToken,
},
});
} catch (e) {
logger.error('Error sending Pushbullet notification', {
label: 'Notifications',
type: Notification[type],
subject: payload.subject,
errorMessage: e.message,
response: e.response?.data,
});
return false;
}
}
if (payload.notifyUser) {
// Send notification to the user who submitted the request
if (
payload.notifyUser.settings?.hasNotificationType(
NotificationAgentKey.PUSHBULLET,
type
) &&
payload.notifyUser.settings?.pushbulletAccessToken &&
payload.notifyUser.settings.pushbulletAccessToken !==
settings.options.accessToken
) {
logger.debug('Sending Pushbullet notification', {
label: 'Notifications',
recipient: payload.notifyUser.displayName,
type: Notification[type],
subject: payload.subject,
});
try {
await axios.post(endpoint, notificationPayload, {
headers: {
'Access-Token': payload.notifyUser.settings.pushbulletAccessToken,
},
});
} catch (e) {
logger.error('Error sending Pushbullet notification', {
label: 'Notifications',
recipient: payload.notifyUser.displayName,
type: Notification[type],
subject: payload.subject,
errorMessage: e.message,
response: e.response?.data,
});
return false;
}
}
} else {
// Send notifications to all users with the Manage Requests permission
const userRepository = getRepository(User);
const users = await userRepository.find();
await Promise.all(
users
.filter(
(user) =>
user.hasPermission(Permission.MANAGE_REQUESTS) &&
user.settings?.hasNotificationType(
NotificationAgentKey.PUSHBULLET,
type
) &&
// Check if it's the user's own auto-approved request
(type !== Notification.MEDIA_AUTO_APPROVED ||
user.id !== payload.request?.requestedBy.id)
)
.map(async (user) => {
if (
user.settings?.pushbulletAccessToken &&
user.settings.pushbulletAccessToken !==
settings.options.accessToken
) {
logger.debug('Sending Pushbullet notification', {
label: 'Notifications',
recipient: user.displayName,
type: Notification[type],
subject: payload.subject,
});
try {
await axios.post(endpoint, notificationPayload, {
headers: {
'Access-Token': user.settings.pushbulletAccessToken,
},
});
} catch (e) {
logger.error('Error sending Pushbullet notification', {
label: 'Notifications',
recipient: user.displayName,
type: Notification[type],
subject: payload.subject,
errorMessage: e.message,
response: e.response?.data,
});
return false;
}
}
})
);
}
return true;
}
}

View File

@@ -1,8 +1,15 @@
import axios from 'axios';
import { getRepository } from 'typeorm';
import { hasNotificationType, Notification } from '..';
import { MediaType } from '../../../constants/media';
import { User } from '../../../entity/User';
import logger from '../../../logger';
import { getSettings, NotificationAgentPushover } from '../../settings';
import { Permission } from '../../permissions';
import {
getSettings,
NotificationAgentKey,
NotificationAgentPushover,
} from '../../settings';
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
interface PushoverPayload {
@@ -31,29 +38,13 @@ class PushoverAgent
}
public shouldSend(): boolean {
const settings = this.getSettings();
if (
settings.enabled &&
settings.options.accessToken &&
settings.options.userToken
) {
return true;
}
return false;
return true;
}
private constructMessageDetails(
private getNotificationPayload(
type: Notification,
payload: NotificationPayload
): {
title: string;
message: string;
url: string | undefined;
url_title: string | undefined;
priority: number;
} {
): Partial<PushoverPayload> {
const settings = getSettings();
let messageTitle = '';
let message = '';
@@ -155,6 +146,7 @@ class PushoverAgent
url,
url_title,
priority,
html: 1,
};
}
@@ -163,45 +155,138 @@ class PushoverAgent
payload: NotificationPayload
): Promise<boolean> {
const settings = this.getSettings();
const endpoint = 'https://api.pushover.net/1/messages.json';
const notificationPayload = this.getNotificationPayload(type, payload);
if (!hasNotificationType(type, settings.types ?? 0)) {
return true;
}
logger.debug('Sending Pushover notification', {
label: 'Notifications',
type: Notification[type],
subject: payload.subject,
});
try {
const endpoint = 'https://api.pushover.net/1/messages.json';
const { title, message, url, url_title, priority } =
this.constructMessageDetails(type, payload);
await axios.post(endpoint, {
token: settings.options.accessToken,
user: settings.options.userToken,
title: title,
message: message,
url: url,
url_title: url_title,
priority: priority,
html: 1,
} as PushoverPayload);
return true;
} catch (e) {
logger.error('Error sending Pushover notification', {
// Send system notification
if (
hasNotificationType(type, settings.types ?? 0) &&
settings.enabled &&
settings.options.accessToken &&
settings.options.userToken
) {
logger.debug('Sending Pushover notification', {
label: 'Notifications',
type: Notification[type],
subject: payload.subject,
errorMessage: e.message,
response: e.response?.data,
});
return false;
try {
await axios.post(endpoint, {
...notificationPayload,
token: settings.options.accessToken,
user: settings.options.userToken,
} as PushoverPayload);
} catch (e) {
logger.error('Error sending Pushover notification', {
label: 'Notifications',
type: Notification[type],
subject: payload.subject,
errorMessage: e.message,
response: e.response?.data,
});
return false;
}
}
if (payload.notifyUser) {
// Send notification to the user who submitted the request
if (
payload.notifyUser.settings?.hasNotificationType(
NotificationAgentKey.PUSHOVER,
type
) &&
payload.notifyUser.settings?.pushoverApplicationToken &&
payload.notifyUser.settings?.pushoverUserKey &&
payload.notifyUser.settings.pushoverApplicationToken !==
settings.options.accessToken &&
payload.notifyUser.settings?.pushoverUserKey !==
settings.options.userToken
) {
logger.debug('Sending Pushover notification', {
label: 'Notifications',
recipient: payload.notifyUser.displayName,
type: Notification[type],
subject: payload.subject,
});
try {
await axios.post(endpoint, {
...notificationPayload,
token: payload.notifyUser.settings.pushoverApplicationToken,
user: payload.notifyUser.settings.pushoverUserKey,
} as PushoverPayload);
} catch (e) {
logger.error('Error sending Pushover notification', {
label: 'Notifications',
recipient: payload.notifyUser.displayName,
type: Notification[type],
subject: payload.subject,
errorMessage: e.message,
response: e.response?.data,
});
return false;
}
}
} else {
// Send notifications to all users with the Manage Requests permission
const userRepository = getRepository(User);
const users = await userRepository.find();
await Promise.all(
users
.filter(
(user) =>
user.hasPermission(Permission.MANAGE_REQUESTS) &&
user.settings?.hasNotificationType(
NotificationAgentKey.PUSHOVER,
type
) &&
// Check if it's the user's own auto-approved request
(type !== Notification.MEDIA_AUTO_APPROVED ||
user.id !== payload.request?.requestedBy.id)
)
.map(async (user) => {
if (
user.settings?.pushoverApplicationToken &&
user.settings?.pushoverUserKey &&
user.settings.pushoverApplicationToken !==
settings.options.accessToken &&
user.settings.pushoverUserKey !== settings.options.userToken
) {
logger.debug('Sending Pushover notification', {
label: 'Notifications',
recipient: user.displayName,
type: Notification[type],
subject: payload.subject,
});
try {
await axios.post(endpoint, {
...notificationPayload,
token: user.settings.pushoverApplicationToken,
user: user.settings.pushoverUserKey,
} as PushoverPayload);
} catch (e) {
logger.error('Error sending Pushover notification', {
label: 'Notifications',
recipient: user.displayName,
type: Notification[type],
subject: payload.subject,
errorMessage: e.message,
response: e.response?.data,
});
return false;
}
}
})
);
}
return true;
}
}

View File

@@ -46,11 +46,7 @@ class TelegramAgent
public shouldSend(): boolean {
const settings = this.getSettings();
if (
settings.enabled &&
settings.options.botAPI &&
settings.options.chatId
) {
if (settings.enabled && settings.options.botAPI) {
return true;
}
@@ -61,12 +57,10 @@ class TelegramAgent
return text ? text.replace(/[_*[\]()~>#+=|{}.!-]/gi, (x) => '\\' + x) : '';
}
private buildMessage(
private getNotificationPayload(
type: Notification,
payload: NotificationPayload,
chatId: string,
sendSilently: boolean
): TelegramMessagePayload | TelegramPhotoPayload {
payload: NotificationPayload
): Partial<TelegramMessagePayload | TelegramPhotoPayload> {
const settings = getSettings();
let message = '';
@@ -160,19 +154,15 @@ class TelegramAgent
/* eslint-enable */
return payload.image
? ({
? {
photo: payload.image,
caption: message,
parse_mode: 'MarkdownV2',
chat_id: chatId,
disable_notification: !!sendSilently,
} as TelegramPhotoPayload)
: ({
}
: {
text: message,
parse_mode: 'MarkdownV2',
chat_id: chatId,
disable_notification: !!sendSilently,
} as TelegramMessagePayload);
};
}
public async send(
@@ -180,13 +170,16 @@ class TelegramAgent
payload: NotificationPayload
): Promise<boolean> {
const settings = this.getSettings();
const endpoint = `${this.baseUrl}bot${settings.options.botAPI}/${
payload.image ? 'sendPhoto' : 'sendMessage'
}`;
const notificationPayload = this.getNotificationPayload(type, payload);
// Send system notification
if (hasNotificationType(type, settings.types ?? 0)) {
if (
hasNotificationType(type, settings.types ?? 0) &&
settings.options.chatId
) {
logger.debug('Sending Telegram notification', {
label: 'Notifications',
type: Notification[type],
@@ -194,15 +187,11 @@ class TelegramAgent
});
try {
await axios.post(
endpoint,
this.buildMessage(
type,
payload,
settings.options.chatId,
settings.options.sendSilently
)
);
await axios.post(endpoint, {
...notificationPayload,
chat_id: settings.options.chatId,
disable_notification: !!settings.options.sendSilently,
} as TelegramMessagePayload | TelegramPhotoPayload);
} catch (e) {
logger.error('Error sending Telegram notification', {
label: 'Notifications',
@@ -224,7 +213,7 @@ class TelegramAgent
type
) &&
payload.notifyUser.settings?.telegramChatId &&
payload.notifyUser.settings?.telegramChatId !== settings.options.chatId
payload.notifyUser.settings.telegramChatId !== settings.options.chatId
) {
logger.debug('Sending Telegram notification', {
label: 'Notifications',
@@ -234,15 +223,12 @@ class TelegramAgent
});
try {
await axios.post(
endpoint,
this.buildMessage(
type,
payload,
payload.notifyUser.settings.telegramChatId,
!!payload.notifyUser.settings.telegramSendSilently
)
);
await axios.post(endpoint, {
...notificationPayload,
chat_id: payload.notifyUser.settings.telegramChatId,
disable_notification:
!!payload.notifyUser.settings.telegramSendSilently,
} as TelegramMessagePayload | TelegramPhotoPayload);
} catch (e) {
logger.error('Error sending Telegram notification', {
label: 'Notifications',
@@ -287,15 +273,11 @@ class TelegramAgent
});
try {
await axios.post(
endpoint,
this.buildMessage(
type,
payload,
user.settings.telegramChatId,
!!user.settings?.telegramSendSilently
)
);
await axios.post(endpoint, {
...notificationPayload,
chat_id: user.settings.telegramChatId,
disable_notification: !!user.settings?.telegramSendSilently,
} as TelegramMessagePayload | TelegramPhotoPayload);
} catch (e) {
logger.error('Error sending Telegram notification', {
label: 'Notifications',

View File

@@ -206,6 +206,11 @@ class WebPushAgent
settings.vapidPrivate
);
const notificationPayload = Buffer.from(
JSON.stringify(this.getNotificationPayload(type, payload)),
'utf-8'
);
await Promise.all(
pushSubs.map(async (sub) => {
logger.debug('Sending web push notification', {
@@ -224,10 +229,7 @@ class WebPushAgent
p256dh: sub.p256dh,
},
},
Buffer.from(
JSON.stringify(this.getNotificationPayload(type, payload)),
'utf-8'
)
notificationPayload
);
} catch (e) {
logger.error(