feat(notif): add "Media Automatically Approved" notification type (#1137)
This commit is contained in:
@@ -1196,9 +1196,6 @@ components:
|
|||||||
enabled:
|
enabled:
|
||||||
type: boolean
|
type: boolean
|
||||||
example: true
|
example: true
|
||||||
autoapprovalEnabled:
|
|
||||||
type: boolean
|
|
||||||
example: false
|
|
||||||
NotificationEmailSettings:
|
NotificationEmailSettings:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ export class MediaRequest {
|
|||||||
* auto approved content
|
* auto approved content
|
||||||
*/
|
*/
|
||||||
@AfterUpdate()
|
@AfterUpdate()
|
||||||
public async notifyApprovedOrDeclined(): Promise<void> {
|
public async notifyApprovedOrDeclined(autoApproved = false): Promise<void> {
|
||||||
if (
|
if (
|
||||||
this.status === MediaRequestStatus.APPROVED ||
|
this.status === MediaRequestStatus.APPROVED ||
|
||||||
this.status === MediaRequestStatus.DECLINED
|
this.status === MediaRequestStatus.DECLINED
|
||||||
@@ -171,7 +171,9 @@ export class MediaRequest {
|
|||||||
const movie = await tmdb.getMovie({ movieId: this.media.tmdbId });
|
const movie = await tmdb.getMovie({ movieId: this.media.tmdbId });
|
||||||
notificationManager.sendNotification(
|
notificationManager.sendNotification(
|
||||||
this.status === MediaRequestStatus.APPROVED
|
this.status === MediaRequestStatus.APPROVED
|
||||||
? Notification.MEDIA_APPROVED
|
? autoApproved
|
||||||
|
? Notification.MEDIA_AUTO_APPROVED
|
||||||
|
: Notification.MEDIA_APPROVED
|
||||||
: Notification.MEDIA_DECLINED,
|
: Notification.MEDIA_DECLINED,
|
||||||
{
|
{
|
||||||
subject: movie.title,
|
subject: movie.title,
|
||||||
@@ -211,13 +213,8 @@ export class MediaRequest {
|
|||||||
|
|
||||||
@AfterInsert()
|
@AfterInsert()
|
||||||
public async autoapprovalNotification(): Promise<void> {
|
public async autoapprovalNotification(): Promise<void> {
|
||||||
const settings = getSettings().notifications;
|
if (this.status === MediaRequestStatus.APPROVED) {
|
||||||
|
this.notifyApprovedOrDeclined(true);
|
||||||
if (
|
|
||||||
settings.autoapprovalEnabled &&
|
|
||||||
this.status === MediaRequestStatus.APPROVED
|
|
||||||
) {
|
|
||||||
this.notifyApprovedOrDeclined();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,12 @@ export abstract class BaseAgent<T extends NotificationAgentConfig> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected abstract getSettings(): T;
|
protected abstract getSettings(): T;
|
||||||
|
|
||||||
|
protected userNotificationTypes: Notification[] = [
|
||||||
|
Notification.MEDIA_APPROVED,
|
||||||
|
Notification.MEDIA_DECLINED,
|
||||||
|
Notification.MEDIA_AVAILABLE,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NotificationAgent {
|
export interface NotificationAgent {
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ class DiscordAgent
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_APPROVED:
|
case Notification.MEDIA_APPROVED:
|
||||||
|
case Notification.MEDIA_AUTO_APPROVED:
|
||||||
color = EmbedColors.PURPLE;
|
color = EmbedColors.PURPLE;
|
||||||
fields.push({
|
fields.push({
|
||||||
name: 'Status',
|
name: 'Status',
|
||||||
@@ -201,7 +202,7 @@ class DiscordAgent
|
|||||||
type: Notification,
|
type: Notification,
|
||||||
payload: NotificationPayload
|
payload: NotificationPayload
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
logger.debug('Sending discord notification', { label: 'Notifications' });
|
logger.debug('Sending Discord notification', { label: 'Notifications' });
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
botUsername,
|
botUsername,
|
||||||
@@ -217,6 +218,7 @@ class DiscordAgent
|
|||||||
let content = undefined;
|
let content = undefined;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
this.userNotificationTypes.includes(type) &&
|
||||||
payload.notifyUser.settings?.enableNotifications &&
|
payload.notifyUser.settings?.enableNotifications &&
|
||||||
payload.notifyUser.settings?.discordId
|
payload.notifyUser.settings?.discordId
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { getRepository } from 'typeorm';
|
|||||||
import { User } from '../../../entity/User';
|
import { User } from '../../../entity/User';
|
||||||
import { Permission } from '../../permissions';
|
import { Permission } from '../../permissions';
|
||||||
import PreparedEmail from '../../email';
|
import PreparedEmail from '../../email';
|
||||||
|
import { MediaType } from '../../../constants/media';
|
||||||
|
|
||||||
class EmailAgent
|
class EmailAgent
|
||||||
extends BaseAgent<NotificationAgentEmail>
|
extends BaseAgent<NotificationAgentEmail>
|
||||||
@@ -57,7 +58,9 @@ class EmailAgent
|
|||||||
to: user.email,
|
to: user.email,
|
||||||
},
|
},
|
||||||
locals: {
|
locals: {
|
||||||
body: 'A user has requested new media!',
|
body: `A user has requested a new ${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||||
|
}!`,
|
||||||
mediaName: payload.subject,
|
mediaName: payload.subject,
|
||||||
imageUrl: payload.image,
|
imageUrl: payload.image,
|
||||||
timestamp: new Date().toTimeString(),
|
timestamp: new Date().toTimeString(),
|
||||||
@@ -67,13 +70,15 @@ class EmailAgent
|
|||||||
: undefined,
|
: undefined,
|
||||||
applicationUrl,
|
applicationUrl,
|
||||||
applicationTitle,
|
applicationTitle,
|
||||||
requestType: 'New Request',
|
requestType: `New ${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Mail notification failed to send', {
|
logger.error('Email notification failed to send', {
|
||||||
label: 'Notifications',
|
label: 'Notifications',
|
||||||
message: e.message,
|
message: e.message,
|
||||||
});
|
});
|
||||||
@@ -103,8 +108,11 @@ class EmailAgent
|
|||||||
to: user.email,
|
to: user.email,
|
||||||
},
|
},
|
||||||
locals: {
|
locals: {
|
||||||
body:
|
body: `A new request for the following ${
|
||||||
"A user's new request has failed to add to Sonarr or Radarr",
|
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||||
|
} could not be added to ${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Sonarr' : 'Radarr'
|
||||||
|
}`,
|
||||||
mediaName: payload.subject,
|
mediaName: payload.subject,
|
||||||
imageUrl: payload.image,
|
imageUrl: payload.image,
|
||||||
timestamp: new Date().toTimeString(),
|
timestamp: new Date().toTimeString(),
|
||||||
@@ -114,13 +122,15 @@ class EmailAgent
|
|||||||
: undefined,
|
: undefined,
|
||||||
applicationUrl,
|
applicationUrl,
|
||||||
applicationTitle,
|
applicationTitle,
|
||||||
requestType: 'Failed Request',
|
requestType: `Failed ${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Mail notification failed to send', {
|
logger.error('Email notification failed to send', {
|
||||||
label: 'Notifications',
|
label: 'Notifications',
|
||||||
message: e.message,
|
message: e.message,
|
||||||
});
|
});
|
||||||
@@ -143,7 +153,9 @@ class EmailAgent
|
|||||||
to: payload.notifyUser.email,
|
to: payload.notifyUser.email,
|
||||||
},
|
},
|
||||||
locals: {
|
locals: {
|
||||||
body: 'Your request for the following media has been approved:',
|
body: `Your request for the following ${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||||
|
} has been approved:`,
|
||||||
mediaName: payload.subject,
|
mediaName: payload.subject,
|
||||||
imageUrl: payload.image,
|
imageUrl: payload.image,
|
||||||
timestamp: new Date().toTimeString(),
|
timestamp: new Date().toTimeString(),
|
||||||
@@ -153,12 +165,64 @@ class EmailAgent
|
|||||||
: undefined,
|
: undefined,
|
||||||
applicationUrl,
|
applicationUrl,
|
||||||
applicationTitle,
|
applicationTitle,
|
||||||
requestType: 'Request Approved',
|
requestType: `${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request Approved`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Mail notification failed to send', {
|
logger.error('Email notification failed to send', {
|
||||||
|
label: 'Notifications',
|
||||||
|
message: e.message,
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async sendMediaAutoApprovedEmail(payload: NotificationPayload) {
|
||||||
|
// This is getting main settings for the whole app
|
||||||
|
const { applicationUrl, applicationTitle } = getSettings().main;
|
||||||
|
try {
|
||||||
|
const userRepository = getRepository(User);
|
||||||
|
const users = await userRepository.find();
|
||||||
|
|
||||||
|
// Send to all users with the manage requests permission (or admins)
|
||||||
|
users
|
||||||
|
.filter((user) => user.hasPermission(Permission.MANAGE_REQUESTS))
|
||||||
|
.forEach((user) => {
|
||||||
|
const email = new PreparedEmail();
|
||||||
|
|
||||||
|
email.send({
|
||||||
|
template: path.join(
|
||||||
|
__dirname,
|
||||||
|
'../../../templates/email/media-request'
|
||||||
|
),
|
||||||
|
message: {
|
||||||
|
to: user.email,
|
||||||
|
},
|
||||||
|
locals: {
|
||||||
|
body: `A new request for the following ${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||||
|
} has been automatically approved:`,
|
||||||
|
mediaName: payload.subject,
|
||||||
|
imageUrl: payload.image,
|
||||||
|
timestamp: new Date().toTimeString(),
|
||||||
|
requestedBy: payload.notifyUser.displayName,
|
||||||
|
actionUrl: applicationUrl
|
||||||
|
? `${applicationUrl}/${payload.media?.mediaType}/${payload.media?.tmdbId}`
|
||||||
|
: undefined,
|
||||||
|
applicationUrl,
|
||||||
|
applicationTitle,
|
||||||
|
requestType: `${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request Automatically Approved`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
logger.error('Email notification failed to send', {
|
||||||
label: 'Notifications',
|
label: 'Notifications',
|
||||||
message: e.message,
|
message: e.message,
|
||||||
});
|
});
|
||||||
@@ -181,7 +245,9 @@ class EmailAgent
|
|||||||
to: payload.notifyUser.email,
|
to: payload.notifyUser.email,
|
||||||
},
|
},
|
||||||
locals: {
|
locals: {
|
||||||
body: 'Your request for the following media was declined:',
|
body: `Your request for the following ${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||||
|
} was declined:`,
|
||||||
mediaName: payload.subject,
|
mediaName: payload.subject,
|
||||||
imageUrl: payload.image,
|
imageUrl: payload.image,
|
||||||
timestamp: new Date().toTimeString(),
|
timestamp: new Date().toTimeString(),
|
||||||
@@ -191,12 +257,14 @@ class EmailAgent
|
|||||||
: undefined,
|
: undefined,
|
||||||
applicationUrl,
|
applicationUrl,
|
||||||
applicationTitle,
|
applicationTitle,
|
||||||
requestType: 'Request Declined',
|
requestType: `${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request Declined`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Mail notification failed to send', {
|
logger.error('Email notification failed to send', {
|
||||||
label: 'Notifications',
|
label: 'Notifications',
|
||||||
message: e.message,
|
message: e.message,
|
||||||
});
|
});
|
||||||
@@ -219,7 +287,9 @@ class EmailAgent
|
|||||||
to: payload.notifyUser.email,
|
to: payload.notifyUser.email,
|
||||||
},
|
},
|
||||||
locals: {
|
locals: {
|
||||||
body: 'Your requested media is now available!',
|
body: `The following ${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'series' : 'movie'
|
||||||
|
} you requested is now available!`,
|
||||||
mediaName: payload.subject,
|
mediaName: payload.subject,
|
||||||
imageUrl: payload.image,
|
imageUrl: payload.image,
|
||||||
timestamp: new Date().toTimeString(),
|
timestamp: new Date().toTimeString(),
|
||||||
@@ -229,12 +299,14 @@ class EmailAgent
|
|||||||
: undefined,
|
: undefined,
|
||||||
applicationUrl,
|
applicationUrl,
|
||||||
applicationTitle,
|
applicationTitle,
|
||||||
requestType: 'Now Available',
|
requestType: `${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Now Available`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Mail notification failed to send', {
|
logger.error('Email notification failed to send', {
|
||||||
label: 'Notifications',
|
label: 'Notifications',
|
||||||
message: e.message,
|
message: e.message,
|
||||||
});
|
});
|
||||||
@@ -261,7 +333,7 @@ class EmailAgent
|
|||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Mail notification failed to send', {
|
logger.error('Email notification failed to send', {
|
||||||
label: 'Notifications',
|
label: 'Notifications',
|
||||||
message: e.message,
|
message: e.message,
|
||||||
});
|
});
|
||||||
@@ -282,6 +354,9 @@ class EmailAgent
|
|||||||
case Notification.MEDIA_APPROVED:
|
case Notification.MEDIA_APPROVED:
|
||||||
this.sendMediaApprovedEmail(payload);
|
this.sendMediaApprovedEmail(payload);
|
||||||
break;
|
break;
|
||||||
|
case Notification.MEDIA_AUTO_APPROVED:
|
||||||
|
this.sendMediaAutoApprovedEmail(payload);
|
||||||
|
break;
|
||||||
case Notification.MEDIA_DECLINED:
|
case Notification.MEDIA_DECLINED:
|
||||||
this.sendMediaDeclinedEmail(payload);
|
this.sendMediaDeclinedEmail(payload);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { hasNotificationType, Notification } from '..';
|
|||||||
import logger from '../../../logger';
|
import logger from '../../../logger';
|
||||||
import { getSettings, NotificationAgentPushbullet } from '../../settings';
|
import { getSettings, NotificationAgentPushbullet } from '../../settings';
|
||||||
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
|
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
|
||||||
|
import { MediaType } from '../../../constants/media';
|
||||||
|
|
||||||
interface PushbulletPayload {
|
interface PushbulletPayload {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -50,7 +51,9 @@ class PushbulletAgent
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Notification.MEDIA_PENDING:
|
case Notification.MEDIA_PENDING:
|
||||||
messageTitle = 'New Request';
|
messageTitle = `New ${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request`;
|
||||||
message += `${title}`;
|
message += `${title}`;
|
||||||
if (plot) {
|
if (plot) {
|
||||||
message += `\n\n${plot}`;
|
message += `\n\n${plot}`;
|
||||||
@@ -59,7 +62,20 @@ class PushbulletAgent
|
|||||||
message += `\nStatus: Pending Approval`;
|
message += `\nStatus: Pending Approval`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_APPROVED:
|
case Notification.MEDIA_APPROVED:
|
||||||
messageTitle = 'Request Approved';
|
messageTitle = `${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request Approved`;
|
||||||
|
message += `${title}`;
|
||||||
|
if (plot) {
|
||||||
|
message += `\n\n${plot}`;
|
||||||
|
}
|
||||||
|
message += `\n\nRequested By: ${username}`;
|
||||||
|
message += `\nStatus: Processing`;
|
||||||
|
break;
|
||||||
|
case Notification.MEDIA_AUTO_APPROVED:
|
||||||
|
messageTitle = `${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request Automatically Approved`;
|
||||||
message += `${title}`;
|
message += `${title}`;
|
||||||
if (plot) {
|
if (plot) {
|
||||||
message += `\n\n${plot}`;
|
message += `\n\n${plot}`;
|
||||||
@@ -68,7 +84,9 @@ class PushbulletAgent
|
|||||||
message += `\nStatus: Processing`;
|
message += `\nStatus: Processing`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_AVAILABLE:
|
case Notification.MEDIA_AVAILABLE:
|
||||||
messageTitle = 'Now Available';
|
messageTitle = `${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Now Available`;
|
||||||
message += `${title}`;
|
message += `${title}`;
|
||||||
if (plot) {
|
if (plot) {
|
||||||
message += `\n\n${plot}`;
|
message += `\n\n${plot}`;
|
||||||
@@ -77,7 +95,9 @@ class PushbulletAgent
|
|||||||
message += `\nStatus: Available`;
|
message += `\nStatus: Available`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_DECLINED:
|
case Notification.MEDIA_DECLINED:
|
||||||
messageTitle = 'Request Declined';
|
messageTitle = `${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request Declined`;
|
||||||
message += `${title}`;
|
message += `${title}`;
|
||||||
if (plot) {
|
if (plot) {
|
||||||
message += `\n\n${plot}`;
|
message += `\n\n${plot}`;
|
||||||
@@ -86,7 +106,9 @@ class PushbulletAgent
|
|||||||
message += `\nStatus: Declined`;
|
message += `\nStatus: Declined`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_FAILED:
|
case Notification.MEDIA_FAILED:
|
||||||
messageTitle = 'Failed Request';
|
messageTitle = `Failed ${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request`;
|
||||||
message += `${title}`;
|
message += `${title}`;
|
||||||
if (plot) {
|
if (plot) {
|
||||||
message += `\n\n${plot}`;
|
message += `\n\n${plot}`;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { hasNotificationType, Notification } from '..';
|
|||||||
import logger from '../../../logger';
|
import logger from '../../../logger';
|
||||||
import { getSettings, NotificationAgentPushover } from '../../settings';
|
import { getSettings, NotificationAgentPushover } from '../../settings';
|
||||||
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
|
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
|
||||||
|
import { MediaType } from '../../../constants/media';
|
||||||
|
|
||||||
interface PushoverPayload {
|
interface PushoverPayload {
|
||||||
token: string;
|
token: string;
|
||||||
@@ -64,7 +65,9 @@ class PushoverAgent
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Notification.MEDIA_PENDING:
|
case Notification.MEDIA_PENDING:
|
||||||
messageTitle = 'New Request';
|
messageTitle = `New ${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request`;
|
||||||
message += `<b>${title}</b>`;
|
message += `<b>${title}</b>`;
|
||||||
if (plot) {
|
if (plot) {
|
||||||
message += `\n${plot}`;
|
message += `\n${plot}`;
|
||||||
@@ -73,7 +76,20 @@ class PushoverAgent
|
|||||||
message += `\n\n<b>Status</b>\nPending Approval`;
|
message += `\n\n<b>Status</b>\nPending Approval`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_APPROVED:
|
case Notification.MEDIA_APPROVED:
|
||||||
messageTitle = 'Request Approved';
|
messageTitle = `${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request Approved`;
|
||||||
|
message += `<b>${title}</b>`;
|
||||||
|
if (plot) {
|
||||||
|
message += `\n${plot}`;
|
||||||
|
}
|
||||||
|
message += `\n\n<b>Requested By</b>\n${username}`;
|
||||||
|
message += `\n\n<b>Status</b>\nProcessing`;
|
||||||
|
break;
|
||||||
|
case Notification.MEDIA_AUTO_APPROVED:
|
||||||
|
messageTitle = `${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request Automatically Approved`;
|
||||||
message += `<b>${title}</b>`;
|
message += `<b>${title}</b>`;
|
||||||
if (plot) {
|
if (plot) {
|
||||||
message += `\n${plot}`;
|
message += `\n${plot}`;
|
||||||
@@ -82,7 +98,9 @@ class PushoverAgent
|
|||||||
message += `\n\n<b>Status</b>\nProcessing`;
|
message += `\n\n<b>Status</b>\nProcessing`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_AVAILABLE:
|
case Notification.MEDIA_AVAILABLE:
|
||||||
messageTitle = 'Now Available';
|
messageTitle = `${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Now Available`;
|
||||||
message += `<b>${title}</b>`;
|
message += `<b>${title}</b>`;
|
||||||
if (plot) {
|
if (plot) {
|
||||||
message += `\n${plot}`;
|
message += `\n${plot}`;
|
||||||
@@ -91,7 +109,9 @@ class PushoverAgent
|
|||||||
message += `\n\n<b>Status</b>\nAvailable`;
|
message += `\n\n<b>Status</b>\nAvailable`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_DECLINED:
|
case Notification.MEDIA_DECLINED:
|
||||||
messageTitle = 'Request Declined';
|
messageTitle = `${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request Declined`;
|
||||||
message += `<b>${title}</b>`;
|
message += `<b>${title}</b>`;
|
||||||
if (plot) {
|
if (plot) {
|
||||||
message += `\n${plot}`;
|
message += `\n${plot}`;
|
||||||
@@ -101,7 +121,9 @@ class PushoverAgent
|
|||||||
priority = 1;
|
priority = 1;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_FAILED:
|
case Notification.MEDIA_FAILED:
|
||||||
messageTitle = 'Failed Request';
|
messageTitle = `Failed ${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request`;
|
||||||
message += `<b>${title}</b>`;
|
message += `<b>${title}</b>`;
|
||||||
if (plot) {
|
if (plot) {
|
||||||
message += `\n${plot}`;
|
message += `\n${plot}`;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { hasNotificationType, Notification } from '..';
|
|||||||
import logger from '../../../logger';
|
import logger from '../../../logger';
|
||||||
import { getSettings, NotificationAgentSlack } from '../../settings';
|
import { getSettings, NotificationAgentSlack } from '../../settings';
|
||||||
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
|
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
|
||||||
|
import { MediaType } from '../../../constants/media';
|
||||||
|
|
||||||
interface EmbedField {
|
interface EmbedField {
|
||||||
type: 'plain_text' | 'mrkdwn';
|
type: 'plain_text' | 'mrkdwn';
|
||||||
@@ -72,35 +73,54 @@ class SlackAgent
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Notification.MEDIA_PENDING:
|
case Notification.MEDIA_PENDING:
|
||||||
header = 'New Request';
|
header = `New ${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request`;
|
||||||
fields.push({
|
fields.push({
|
||||||
type: 'mrkdwn',
|
type: 'mrkdwn',
|
||||||
text: '*Status*\nPending Approval',
|
text: '*Status*\nPending Approval',
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_APPROVED:
|
case Notification.MEDIA_APPROVED:
|
||||||
header = 'Request Approved';
|
header = `${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request Approved`;
|
||||||
|
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({
|
fields.push({
|
||||||
type: 'mrkdwn',
|
type: 'mrkdwn',
|
||||||
text: '*Status*\nProcessing',
|
text: '*Status*\nProcessing',
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_AVAILABLE:
|
case Notification.MEDIA_AVAILABLE:
|
||||||
header = 'Now Available';
|
header = `${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Now Available`;
|
||||||
fields.push({
|
fields.push({
|
||||||
type: 'mrkdwn',
|
type: 'mrkdwn',
|
||||||
text: '*Status*\nAvailable',
|
text: '*Status*\nAvailable',
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_DECLINED:
|
case Notification.MEDIA_DECLINED:
|
||||||
header = 'Request Declined';
|
header = `${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request Declined`;
|
||||||
fields.push({
|
fields.push({
|
||||||
type: 'mrkdwn',
|
type: 'mrkdwn',
|
||||||
text: '*Status*\nDeclined',
|
text: '*Status*\nDeclined',
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_FAILED:
|
case Notification.MEDIA_FAILED:
|
||||||
header = 'Failed Request';
|
header = `Failed ${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request`;
|
||||||
fields.push({
|
fields.push({
|
||||||
type: 'mrkdwn',
|
type: 'mrkdwn',
|
||||||
text: '*Status*\nFailed',
|
text: '*Status*\nFailed',
|
||||||
@@ -206,7 +226,7 @@ class SlackAgent
|
|||||||
type: Notification,
|
type: Notification,
|
||||||
payload: NotificationPayload
|
payload: NotificationPayload
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
logger.debug('Sending slack notification', { label: 'Notifications' });
|
logger.debug('Sending Slack notification', { label: 'Notifications' });
|
||||||
try {
|
try {
|
||||||
const webhookUrl = this.getSettings().options.webhookUrl;
|
const webhookUrl = this.getSettings().options.webhookUrl;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import axios from 'axios';
|
|||||||
import { hasNotificationType, Notification } from '..';
|
import { hasNotificationType, Notification } from '..';
|
||||||
import logger from '../../../logger';
|
import logger from '../../../logger';
|
||||||
import { getSettings, NotificationAgentTelegram } from '../../settings';
|
import { getSettings, NotificationAgentTelegram } from '../../settings';
|
||||||
|
import { MediaType } from '../../../constants/media';
|
||||||
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
|
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
|
||||||
|
|
||||||
interface TelegramMessagePayload {
|
interface TelegramMessagePayload {
|
||||||
@@ -66,7 +67,9 @@ class TelegramAgent
|
|||||||
/* eslint-disable no-useless-escape */
|
/* eslint-disable no-useless-escape */
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Notification.MEDIA_PENDING:
|
case Notification.MEDIA_PENDING:
|
||||||
message += `\*New Request\*`;
|
message += `\*New ${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request\*`;
|
||||||
message += `\n\n\*${title}\*`;
|
message += `\n\n\*${title}\*`;
|
||||||
if (plot) {
|
if (plot) {
|
||||||
message += `\n${plot}`;
|
message += `\n${plot}`;
|
||||||
@@ -75,7 +78,20 @@ class TelegramAgent
|
|||||||
message += `\n\n\*Status\*\nPending Approval`;
|
message += `\n\n\*Status\*\nPending Approval`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_APPROVED:
|
case Notification.MEDIA_APPROVED:
|
||||||
message += `\*Request Approved\*`;
|
message += `\*${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request Approved\*`;
|
||||||
|
message += `\n\n\*${title}\*`;
|
||||||
|
if (plot) {
|
||||||
|
message += `\n${plot}`;
|
||||||
|
}
|
||||||
|
message += `\n\n\*Requested By\*\n${user}`;
|
||||||
|
message += `\n\n\*Status\*\nProcessing`;
|
||||||
|
break;
|
||||||
|
case Notification.MEDIA_AUTO_APPROVED:
|
||||||
|
message += `\*${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request Automatically Approved\*`;
|
||||||
message += `\n\n\*${title}\*`;
|
message += `\n\n\*${title}\*`;
|
||||||
if (plot) {
|
if (plot) {
|
||||||
message += `\n${plot}`;
|
message += `\n${plot}`;
|
||||||
@@ -84,7 +100,9 @@ class TelegramAgent
|
|||||||
message += `\n\n\*Status\*\nProcessing`;
|
message += `\n\n\*Status\*\nProcessing`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_AVAILABLE:
|
case Notification.MEDIA_AVAILABLE:
|
||||||
message += `\*Now Available\*`;
|
message += `\*${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Now Available\*`;
|
||||||
message += `\n\n\*${title}\*`;
|
message += `\n\n\*${title}\*`;
|
||||||
if (plot) {
|
if (plot) {
|
||||||
message += `\n${plot}`;
|
message += `\n${plot}`;
|
||||||
@@ -93,7 +111,9 @@ class TelegramAgent
|
|||||||
message += `\n\n\*Status\*\nAvailable`;
|
message += `\n\n\*Status\*\nAvailable`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_DECLINED:
|
case Notification.MEDIA_DECLINED:
|
||||||
message += `\*Request Declined\*`;
|
message += `\*${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request Declined\*`;
|
||||||
message += `\n\n\*${title}\*`;
|
message += `\n\n\*${title}\*`;
|
||||||
if (plot) {
|
if (plot) {
|
||||||
message += `\n${plot}`;
|
message += `\n${plot}`;
|
||||||
@@ -102,7 +122,9 @@ class TelegramAgent
|
|||||||
message += `\n\n\*Status\*\nDeclined`;
|
message += `\n\n\*Status\*\nDeclined`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_FAILED:
|
case Notification.MEDIA_FAILED:
|
||||||
message += `\*Failed Request\*`;
|
message += `\*Failed ${
|
||||||
|
payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie'
|
||||||
|
} Request\*`;
|
||||||
message += `\n\n\*${title}\*`;
|
message += `\n\n\*${title}\*`;
|
||||||
if (plot) {
|
if (plot) {
|
||||||
message += `\n${plot}`;
|
message += `\n${plot}`;
|
||||||
@@ -129,12 +151,13 @@ class TelegramAgent
|
|||||||
type: Notification,
|
type: Notification,
|
||||||
payload: NotificationPayload
|
payload: NotificationPayload
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
logger.debug('Sending telegram notification', { label: 'Notifications' });
|
logger.debug('Sending Telegram notification', { label: 'Notifications' });
|
||||||
try {
|
try {
|
||||||
const endpoint = `${this.baseUrl}bot${
|
const endpoint = `${this.baseUrl}bot${
|
||||||
this.getSettings().options.botAPI
|
this.getSettings().options.botAPI
|
||||||
}/${payload.image ? 'sendPhoto' : 'sendMessage'}`;
|
}/${payload.image ? 'sendPhoto' : 'sendMessage'}`;
|
||||||
|
|
||||||
|
// Send system notification
|
||||||
await (payload.image
|
await (payload.image
|
||||||
? axios.post(endpoint, {
|
? axios.post(endpoint, {
|
||||||
photo: payload.image,
|
photo: payload.image,
|
||||||
@@ -150,7 +173,9 @@ class TelegramAgent
|
|||||||
disable_notification: this.getSettings().options.sendSilently,
|
disable_notification: this.getSettings().options.sendSilently,
|
||||||
} as TelegramMessagePayload));
|
} as TelegramMessagePayload));
|
||||||
|
|
||||||
|
// Send user notification
|
||||||
if (
|
if (
|
||||||
|
this.userNotificationTypes.includes(type) &&
|
||||||
payload.notifyUser.settings?.enableNotifications &&
|
payload.notifyUser.settings?.enableNotifications &&
|
||||||
payload.notifyUser.settings?.telegramChatId &&
|
payload.notifyUser.settings?.telegramChatId &&
|
||||||
payload.notifyUser.settings?.telegramChatId !==
|
payload.notifyUser.settings?.telegramChatId !==
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ class WebhookAgent
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Error sending Webhook notification', {
|
logger.error('Error sending webhook notification', {
|
||||||
label: 'Notifications',
|
label: 'Notifications',
|
||||||
errorMessage: e.message,
|
errorMessage: e.message,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export enum Notification {
|
|||||||
MEDIA_FAILED = 16,
|
MEDIA_FAILED = 16,
|
||||||
TEST_NOTIFICATION = 32,
|
TEST_NOTIFICATION = 32,
|
||||||
MEDIA_DECLINED = 64,
|
MEDIA_DECLINED = 64,
|
||||||
|
MEDIA_AUTO_APPROVED = 128,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const hasNotificationType = (
|
export const hasNotificationType = (
|
||||||
|
|||||||
@@ -163,7 +163,6 @@ interface NotificationAgents {
|
|||||||
|
|
||||||
interface NotificationSettings {
|
interface NotificationSettings {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
autoapprovalEnabled: boolean;
|
|
||||||
agents: NotificationAgents;
|
agents: NotificationAgents;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +212,6 @@ class Settings {
|
|||||||
},
|
},
|
||||||
notifications: {
|
notifications: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
autoapprovalEnabled: false,
|
|
||||||
agents: {
|
agents: {
|
||||||
email: {
|
email: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ notificationRoutes.get('/', (_req, res) => {
|
|||||||
const settings = getSettings().notifications;
|
const settings = getSettings().notifications;
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
enabled: settings.enabled,
|
enabled: settings.enabled,
|
||||||
autoapprovalEnabled: settings.autoapprovalEnabled,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -24,13 +23,11 @@ notificationRoutes.post('/', (req, res) => {
|
|||||||
|
|
||||||
Object.assign(settings.notifications, {
|
Object.assign(settings.notifications, {
|
||||||
enabled: req.body.enabled,
|
enabled: req.body.enabled,
|
||||||
autoapprovalEnabled: req.body.autoapprovalEnabled,
|
|
||||||
});
|
});
|
||||||
settings.save();
|
settings.save();
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
enabled: settings.notifications.enabled,
|
enabled: settings.notifications.enabled,
|
||||||
autoapprovalEnabled: settings.notifications.autoapprovalEnabled,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,16 +8,19 @@ const messages = defineMessages({
|
|||||||
'Sends a notification when media is requested and requires approval.',
|
'Sends a notification when media is requested and requires approval.',
|
||||||
mediaapproved: 'Media Approved',
|
mediaapproved: 'Media Approved',
|
||||||
mediaapprovedDescription:
|
mediaapprovedDescription:
|
||||||
'Sends a notification when media is approved.\
|
'Sends a notification when requested media is manually approved.',
|
||||||
By default, automatically approved requests will not trigger notifications.',
|
mediaAutoApproved: 'Media Automatically Approved',
|
||||||
|
mediaAutoApprovedDescription:
|
||||||
|
'Sends a notification when requested media is automatically approved.',
|
||||||
mediaavailable: 'Media Available',
|
mediaavailable: 'Media Available',
|
||||||
mediaavailableDescription:
|
mediaavailableDescription:
|
||||||
'Sends a notification when media becomes available.',
|
'Sends a notification when requested media becomes available.',
|
||||||
mediafailed: 'Media Failed',
|
mediafailed: 'Media Failed',
|
||||||
mediafailedDescription:
|
mediafailedDescription:
|
||||||
'Sends a notification when media fails to be added to Radarr or Sonarr.',
|
'Sends a notification when requested media fails to be added to Radarr or Sonarr.',
|
||||||
mediadeclined: 'Media Declined',
|
mediadeclined: 'Media Declined',
|
||||||
mediadeclinedDescription: 'Sends a notification when a request is declined.',
|
mediadeclinedDescription:
|
||||||
|
'Sends a notification when a media request is declined.',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const hasNotificationType = (
|
export const hasNotificationType = (
|
||||||
@@ -46,6 +49,7 @@ export enum Notification {
|
|||||||
MEDIA_FAILED = 16,
|
MEDIA_FAILED = 16,
|
||||||
TEST_NOTIFICATION = 32,
|
TEST_NOTIFICATION = 32,
|
||||||
MEDIA_DECLINED = 64,
|
MEDIA_DECLINED = 64,
|
||||||
|
MEDIA_AUTO_APPROVED = 128,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NotificationItem {
|
export interface NotificationItem {
|
||||||
@@ -74,6 +78,12 @@ const NotificationTypeSelector: React.FC<NotificationTypeSelectorProps> = ({
|
|||||||
description: intl.formatMessage(messages.mediarequestedDescription),
|
description: intl.formatMessage(messages.mediarequestedDescription),
|
||||||
value: Notification.MEDIA_PENDING,
|
value: Notification.MEDIA_PENDING,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'media-auto-approved',
|
||||||
|
name: intl.formatMessage(messages.mediaAutoApproved),
|
||||||
|
description: intl.formatMessage(messages.mediaAutoApprovedDescription),
|
||||||
|
value: Notification.MEDIA_AUTO_APPROVED,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'media-approved',
|
id: 'media-approved',
|
||||||
name: intl.formatMessage(messages.mediaapproved),
|
name: intl.formatMessage(messages.mediaapproved),
|
||||||
|
|||||||
@@ -32,10 +32,13 @@ const messages = defineMessages({
|
|||||||
senderName: 'Sender Name',
|
senderName: 'Sender Name',
|
||||||
notificationtypes: 'Notification Types',
|
notificationtypes: 'Notification Types',
|
||||||
validationEmail: 'You must provide a valid email address',
|
validationEmail: 'You must provide a valid email address',
|
||||||
emailNotificationTypesAlert: 'Notification Email Recipients',
|
emailNotificationTypesAlert: 'Email Notification Recipients',
|
||||||
emailNotificationTypesAlertDescription:
|
emailNotificationTypesAlertDescription:
|
||||||
'For the "Media Requested" and "Media Failed" notification types,\
|
'<strong>Media Requested</strong>, <strong>Media Automatically Approved</strong>, and <strong>Media Failed</strong>\
|
||||||
notifications will only be sent to users with the "Manage Requests" permission.',
|
email notifications are sent to all users with the <strong>Manage Requests</strong> permission.',
|
||||||
|
emailNotificationTypesAlertDescriptionPt2:
|
||||||
|
'<strong>Media Approved</strong>, <strong>Media Declined</strong>, and <strong>Media Available</strong>\
|
||||||
|
email notifications are sent to the user who submitted the request.',
|
||||||
});
|
});
|
||||||
|
|
||||||
const NotificationsEmail: React.FC = () => {
|
const NotificationsEmail: React.FC = () => {
|
||||||
@@ -134,9 +137,34 @@ const NotificationsEmail: React.FC = () => {
|
|||||||
title={intl.formatMessage(messages.emailNotificationTypesAlert)}
|
title={intl.formatMessage(messages.emailNotificationTypesAlert)}
|
||||||
type="info"
|
type="info"
|
||||||
>
|
>
|
||||||
{intl.formatMessage(
|
<p className="mb-2">
|
||||||
messages.emailNotificationTypesAlertDescription
|
{intl.formatMessage(
|
||||||
)}
|
messages.emailNotificationTypesAlertDescription,
|
||||||
|
{
|
||||||
|
strong: function strong(msg) {
|
||||||
|
return (
|
||||||
|
<strong className="font-normal text-indigo-100">
|
||||||
|
{msg}
|
||||||
|
</strong>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{intl.formatMessage(
|
||||||
|
messages.emailNotificationTypesAlertDescriptionPt2,
|
||||||
|
{
|
||||||
|
strong: function strong(msg) {
|
||||||
|
return (
|
||||||
|
<strong className="font-normal text-indigo-100">
|
||||||
|
{msg}
|
||||||
|
</strong>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
</Alert>
|
</Alert>
|
||||||
<Form className="section">
|
<Form className="section">
|
||||||
<div className="form-row">
|
<div className="form-row">
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const messages = defineMessages({
|
|||||||
settinguptelegramDescription:
|
settinguptelegramDescription:
|
||||||
'To configure Telegram notifications, you will need to <CreateBotLink>create a bot</CreateBotLink> and get the bot API key.\
|
'To configure Telegram notifications, you will need to <CreateBotLink>create a bot</CreateBotLink> and get the bot API key.\
|
||||||
Additionally, you will need the chat ID for the chat to which you would like to send notifications.\
|
Additionally, you will need the chat ID for the chat to which you would like to send notifications.\
|
||||||
You can get this by adding <GetIdBotLink>@get_id_bot</GetIdBotLink> to the chat.',
|
You can find this by adding <GetIdBotLink>@get_id_bot</GetIdBotLink> to the chat and issuing the <code>/my_id</code> command.',
|
||||||
notificationtypes: 'Notification Types',
|
notificationtypes: 'Notification Types',
|
||||||
sendSilently: 'Send Silently',
|
sendSilently: 'Send Silently',
|
||||||
sendSilentlyTip: 'Send notifications with no sound',
|
sendSilentlyTip: 'Send notifications with no sound',
|
||||||
@@ -143,6 +143,9 @@ const NotificationsTelegram: React.FC = () => {
|
|||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
code: function code(msg) {
|
||||||
|
return <code className="bg-opacity-50">{msg}</code>;
|
||||||
|
},
|
||||||
})}
|
})}
|
||||||
</Alert>
|
</Alert>
|
||||||
<Form className="section">
|
<Form className="section">
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ const messages = defineMessages({
|
|||||||
notificationsettingssaved: 'Notification settings saved successfully!',
|
notificationsettingssaved: 'Notification settings saved successfully!',
|
||||||
notificationsettingsfailed: 'Notification settings failed to save.',
|
notificationsettingsfailed: 'Notification settings failed to save.',
|
||||||
enablenotifications: 'Enable Notifications',
|
enablenotifications: 'Enable Notifications',
|
||||||
autoapprovedrequests: 'Enable Notifications for Automatic Approvals',
|
|
||||||
email: 'Email',
|
email: 'Email',
|
||||||
webhook: 'Webhook',
|
webhook: 'Webhook',
|
||||||
});
|
});
|
||||||
@@ -187,14 +186,12 @@ const SettingsNotifications: React.FC = ({ children }) => {
|
|||||||
<Formik
|
<Formik
|
||||||
initialValues={{
|
initialValues={{
|
||||||
enabled: data.enabled,
|
enabled: data.enabled,
|
||||||
autoapprovalEnabled: data.autoapprovalEnabled,
|
|
||||||
}}
|
}}
|
||||||
enableReinitialize
|
enableReinitialize
|
||||||
onSubmit={async (values) => {
|
onSubmit={async (values) => {
|
||||||
try {
|
try {
|
||||||
await axios.post('/api/v1/settings/notifications', {
|
await axios.post('/api/v1/settings/notifications', {
|
||||||
enabled: values.enabled,
|
enabled: values.enabled,
|
||||||
autoapprovalEnabled: values.autoapprovalEnabled,
|
|
||||||
});
|
});
|
||||||
addToast(intl.formatMessage(messages.notificationsettingssaved), {
|
addToast(intl.formatMessage(messages.notificationsettingssaved), {
|
||||||
appearance: 'success',
|
appearance: 'success',
|
||||||
@@ -233,26 +230,6 @@ const SettingsNotifications: React.FC = ({ children }) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-row">
|
|
||||||
<label htmlFor="name" className="checkbox-label">
|
|
||||||
<span>
|
|
||||||
{intl.formatMessage(messages.autoapprovedrequests)}
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<div className="form-input">
|
|
||||||
<Field
|
|
||||||
type="checkbox"
|
|
||||||
id="autoapprovalEnabled"
|
|
||||||
name="autoapprovalEnabled"
|
|
||||||
onChange={() => {
|
|
||||||
setFieldValue(
|
|
||||||
'autoapprovalEnabled',
|
|
||||||
!values.autoapprovalEnabled
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
|
|||||||
@@ -95,14 +95,16 @@
|
|||||||
"components.MovieDetails.view": "View",
|
"components.MovieDetails.view": "View",
|
||||||
"components.MovieDetails.viewfullcrew": "View Full Crew",
|
"components.MovieDetails.viewfullcrew": "View Full Crew",
|
||||||
"components.MovieDetails.watchtrailer": "Watch Trailer",
|
"components.MovieDetails.watchtrailer": "Watch Trailer",
|
||||||
|
"components.NotificationTypeSelector.mediaAutoApproved": "Media Automatically Approved",
|
||||||
|
"components.NotificationTypeSelector.mediaAutoApprovedDescription": "Sends a notification when requested media is automatically approved.",
|
||||||
"components.NotificationTypeSelector.mediaapproved": "Media Approved",
|
"components.NotificationTypeSelector.mediaapproved": "Media Approved",
|
||||||
"components.NotificationTypeSelector.mediaapprovedDescription": "Sends a notification when media is approved. By default, automatically approved requests will not trigger notifications.",
|
"components.NotificationTypeSelector.mediaapprovedDescription": "Sends a notification when requested media is manually approved.",
|
||||||
"components.NotificationTypeSelector.mediaavailable": "Media Available",
|
"components.NotificationTypeSelector.mediaavailable": "Media Available",
|
||||||
"components.NotificationTypeSelector.mediaavailableDescription": "Sends a notification when media becomes available.",
|
"components.NotificationTypeSelector.mediaavailableDescription": "Sends a notification when requested media becomes available.",
|
||||||
"components.NotificationTypeSelector.mediadeclined": "Media Declined",
|
"components.NotificationTypeSelector.mediadeclined": "Media Declined",
|
||||||
"components.NotificationTypeSelector.mediadeclinedDescription": "Sends a notification when a request is declined.",
|
"components.NotificationTypeSelector.mediadeclinedDescription": "Sends a notification when a media request is declined.",
|
||||||
"components.NotificationTypeSelector.mediafailed": "Media Failed",
|
"components.NotificationTypeSelector.mediafailed": "Media Failed",
|
||||||
"components.NotificationTypeSelector.mediafailedDescription": "Sends a notification when media fails to be added to Radarr or Sonarr.",
|
"components.NotificationTypeSelector.mediafailedDescription": "Sends a notification when requested media fails to be added to Radarr or Sonarr.",
|
||||||
"components.NotificationTypeSelector.mediarequested": "Media Requested",
|
"components.NotificationTypeSelector.mediarequested": "Media Requested",
|
||||||
"components.NotificationTypeSelector.mediarequestedDescription": "Sends a notification when media is requested and requires approval.",
|
"components.NotificationTypeSelector.mediarequestedDescription": "Sends a notification when media is requested and requires approval.",
|
||||||
"components.PermissionEdit.admin": "Admin",
|
"components.PermissionEdit.admin": "Admin",
|
||||||
@@ -318,8 +320,9 @@
|
|||||||
"components.Settings.Notifications.chatId": "Chat ID",
|
"components.Settings.Notifications.chatId": "Chat ID",
|
||||||
"components.Settings.Notifications.discordsettingsfailed": "Discord notification settings failed to save.",
|
"components.Settings.Notifications.discordsettingsfailed": "Discord notification settings failed to save.",
|
||||||
"components.Settings.Notifications.discordsettingssaved": "Discord notification settings saved successfully!",
|
"components.Settings.Notifications.discordsettingssaved": "Discord notification settings saved successfully!",
|
||||||
"components.Settings.Notifications.emailNotificationTypesAlert": "Notification Email Recipients",
|
"components.Settings.Notifications.emailNotificationTypesAlert": "Email Notification Recipients",
|
||||||
"components.Settings.Notifications.emailNotificationTypesAlertDescription": "For the \"Media Requested\" and \"Media Failed\" notification types, notifications will only be sent to users with the \"Manage Requests\" permission.",
|
"components.Settings.Notifications.emailNotificationTypesAlertDescription": "<strong>Media Requested</strong>, <strong>Media Automatically Approved</strong>, and <strong>Media Failed</strong> email notifications are sent to all users with the <strong>Manage Requests</strong> permission.",
|
||||||
|
"components.Settings.Notifications.emailNotificationTypesAlertDescriptionPt2": "<strong>Media Approved</strong>, <strong>Media Declined</strong>, and <strong>Media Available</strong> email notifications are sent to the user who submitted the request.",
|
||||||
"components.Settings.Notifications.emailsender": "Sender Address",
|
"components.Settings.Notifications.emailsender": "Sender Address",
|
||||||
"components.Settings.Notifications.emailsettingsfailed": "Email notification settings failed to save.",
|
"components.Settings.Notifications.emailsettingsfailed": "Email notification settings failed to save.",
|
||||||
"components.Settings.Notifications.emailsettingssaved": "Email notification settings saved successfully!",
|
"components.Settings.Notifications.emailsettingssaved": "Email notification settings saved successfully!",
|
||||||
@@ -331,7 +334,7 @@
|
|||||||
"components.Settings.Notifications.sendSilentlyTip": "Send notifications with no sound",
|
"components.Settings.Notifications.sendSilentlyTip": "Send notifications with no sound",
|
||||||
"components.Settings.Notifications.senderName": "Sender Name",
|
"components.Settings.Notifications.senderName": "Sender Name",
|
||||||
"components.Settings.Notifications.settinguptelegram": "Setting Up Telegram Notifications",
|
"components.Settings.Notifications.settinguptelegram": "Setting Up Telegram Notifications",
|
||||||
"components.Settings.Notifications.settinguptelegramDescription": "To configure Telegram notifications, you will need to <CreateBotLink>create a bot</CreateBotLink> and get the bot API key. Additionally, you will need the chat ID for the chat to which you would like to send notifications. You can get this by adding <GetIdBotLink>@get_id_bot</GetIdBotLink> to the chat.",
|
"components.Settings.Notifications.settinguptelegramDescription": "To configure Telegram notifications, you will need to <CreateBotLink>create a bot</CreateBotLink> and get the bot API key. Additionally, you will need the chat ID for the chat to which you would like to send notifications. You can find this by adding <GetIdBotLink>@get_id_bot</GetIdBotLink> to the chat and issuing the <code>/my_id</code> command.",
|
||||||
"components.Settings.Notifications.smtpHost": "SMTP Host",
|
"components.Settings.Notifications.smtpHost": "SMTP Host",
|
||||||
"components.Settings.Notifications.smtpPort": "SMTP Port",
|
"components.Settings.Notifications.smtpPort": "SMTP Port",
|
||||||
"components.Settings.Notifications.ssldisabletip": "SSL should be disabled on standard TLS connections (port 587)",
|
"components.Settings.Notifications.ssldisabletip": "SSL should be disabled on standard TLS connections (port 587)",
|
||||||
@@ -507,7 +510,6 @@
|
|||||||
"components.Settings.apikey": "API Key",
|
"components.Settings.apikey": "API Key",
|
||||||
"components.Settings.applicationTitle": "Application Title",
|
"components.Settings.applicationTitle": "Application Title",
|
||||||
"components.Settings.applicationurl": "Application URL",
|
"components.Settings.applicationurl": "Application URL",
|
||||||
"components.Settings.autoapprovedrequests": "Enable Notifications for Automatic Approvals",
|
|
||||||
"components.Settings.cancelscan": "Cancel Scan",
|
"components.Settings.cancelscan": "Cancel Scan",
|
||||||
"components.Settings.copied": "Copied API key to clipboard.",
|
"components.Settings.copied": "Copied API key to clipboard.",
|
||||||
"components.Settings.csrfProtection": "Enable CSRF Protection",
|
"components.Settings.csrfProtection": "Enable CSRF Protection",
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ a.slider-title {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
@apply max-w-2xl mt-1 text-sm leading-5 text-gray-500;
|
@apply max-w-4xl mt-1 text-sm leading-5 text-gray-500;
|
||||||
}
|
}
|
||||||
|
|
||||||
img.avatar-sm {
|
img.avatar-sm {
|
||||||
|
|||||||
Reference in New Issue
Block a user