diff --git a/overseerr-api.yml b/overseerr-api.yml index 7c28dcfde..51b5f9e26 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -1196,9 +1196,6 @@ components: enabled: type: boolean example: true - autoapprovalEnabled: - type: boolean - example: false NotificationEmailSettings: type: object properties: diff --git a/server/entity/MediaRequest.ts b/server/entity/MediaRequest.ts index 658aee679..7f0203fcf 100644 --- a/server/entity/MediaRequest.ts +++ b/server/entity/MediaRequest.ts @@ -144,7 +144,7 @@ export class MediaRequest { * auto approved content */ @AfterUpdate() - public async notifyApprovedOrDeclined(): Promise { + public async notifyApprovedOrDeclined(autoApproved = false): Promise { if ( this.status === MediaRequestStatus.APPROVED || this.status === MediaRequestStatus.DECLINED @@ -171,7 +171,9 @@ export class MediaRequest { const movie = await tmdb.getMovie({ movieId: this.media.tmdbId }); notificationManager.sendNotification( this.status === MediaRequestStatus.APPROVED - ? Notification.MEDIA_APPROVED + ? autoApproved + ? Notification.MEDIA_AUTO_APPROVED + : Notification.MEDIA_APPROVED : Notification.MEDIA_DECLINED, { subject: movie.title, @@ -211,13 +213,8 @@ export class MediaRequest { @AfterInsert() public async autoapprovalNotification(): Promise { - const settings = getSettings().notifications; - - if ( - settings.autoapprovalEnabled && - this.status === MediaRequestStatus.APPROVED - ) { - this.notifyApprovedOrDeclined(); + if (this.status === MediaRequestStatus.APPROVED) { + this.notifyApprovedOrDeclined(true); } } diff --git a/server/lib/notifications/agents/agent.ts b/server/lib/notifications/agents/agent.ts index 4db8966a0..517913222 100644 --- a/server/lib/notifications/agents/agent.ts +++ b/server/lib/notifications/agents/agent.ts @@ -21,6 +21,12 @@ export abstract class BaseAgent { } protected abstract getSettings(): T; + + protected userNotificationTypes: Notification[] = [ + Notification.MEDIA_APPROVED, + Notification.MEDIA_DECLINED, + Notification.MEDIA_AVAILABLE, + ]; } export interface NotificationAgent { diff --git a/server/lib/notifications/agents/discord.ts b/server/lib/notifications/agents/discord.ts index 0e695ebc7..7358d786f 100644 --- a/server/lib/notifications/agents/discord.ts +++ b/server/lib/notifications/agents/discord.ts @@ -122,6 +122,7 @@ class DiscordAgent }); break; case Notification.MEDIA_APPROVED: + case Notification.MEDIA_AUTO_APPROVED: color = EmbedColors.PURPLE; fields.push({ name: 'Status', @@ -201,7 +202,7 @@ class DiscordAgent type: Notification, payload: NotificationPayload ): Promise { - logger.debug('Sending discord notification', { label: 'Notifications' }); + logger.debug('Sending Discord notification', { label: 'Notifications' }); try { const { botUsername, @@ -217,6 +218,7 @@ class DiscordAgent let content = undefined; if ( + this.userNotificationTypes.includes(type) && payload.notifyUser.settings?.enableNotifications && payload.notifyUser.settings?.discordId ) { diff --git a/server/lib/notifications/agents/email.ts b/server/lib/notifications/agents/email.ts index 750aaf685..175e5a0c3 100644 --- a/server/lib/notifications/agents/email.ts +++ b/server/lib/notifications/agents/email.ts @@ -7,6 +7,7 @@ import { getRepository } from 'typeorm'; import { User } from '../../../entity/User'; import { Permission } from '../../permissions'; import PreparedEmail from '../../email'; +import { MediaType } from '../../../constants/media'; class EmailAgent extends BaseAgent @@ -57,7 +58,9 @@ class EmailAgent to: user.email, }, 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, imageUrl: payload.image, timestamp: new Date().toTimeString(), @@ -67,13 +70,15 @@ class EmailAgent : undefined, applicationUrl, applicationTitle, - requestType: 'New Request', + requestType: `New ${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Request`, }, }); }); return true; } catch (e) { - logger.error('Mail notification failed to send', { + logger.error('Email notification failed to send', { label: 'Notifications', message: e.message, }); @@ -103,8 +108,11 @@ class EmailAgent to: user.email, }, locals: { - body: - "A user's new request has failed to add to Sonarr or Radarr", + body: `A new request for the following ${ + payload.media?.mediaType === MediaType.TV ? 'series' : 'movie' + } could not be added to ${ + payload.media?.mediaType === MediaType.TV ? 'Sonarr' : 'Radarr' + }`, mediaName: payload.subject, imageUrl: payload.image, timestamp: new Date().toTimeString(), @@ -114,13 +122,15 @@ class EmailAgent : undefined, applicationUrl, applicationTitle, - requestType: 'Failed Request', + requestType: `Failed ${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Request`, }, }); }); return true; } catch (e) { - logger.error('Mail notification failed to send', { + logger.error('Email notification failed to send', { label: 'Notifications', message: e.message, }); @@ -143,7 +153,9 @@ class EmailAgent to: payload.notifyUser.email, }, 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, imageUrl: payload.image, timestamp: new Date().toTimeString(), @@ -153,12 +165,64 @@ class EmailAgent : undefined, applicationUrl, applicationTitle, - requestType: 'Request Approved', + requestType: `${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Request Approved`, }, }); return true; } 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', message: e.message, }); @@ -181,7 +245,9 @@ class EmailAgent to: payload.notifyUser.email, }, 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, imageUrl: payload.image, timestamp: new Date().toTimeString(), @@ -191,12 +257,14 @@ class EmailAgent : undefined, applicationUrl, applicationTitle, - requestType: 'Request Declined', + requestType: `${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Request Declined`, }, }); return true; } catch (e) { - logger.error('Mail notification failed to send', { + logger.error('Email notification failed to send', { label: 'Notifications', message: e.message, }); @@ -219,7 +287,9 @@ class EmailAgent to: payload.notifyUser.email, }, 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, imageUrl: payload.image, timestamp: new Date().toTimeString(), @@ -229,12 +299,14 @@ class EmailAgent : undefined, applicationUrl, applicationTitle, - requestType: 'Now Available', + requestType: `${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Now Available`, }, }); return true; } catch (e) { - logger.error('Mail notification failed to send', { + logger.error('Email notification failed to send', { label: 'Notifications', message: e.message, }); @@ -261,7 +333,7 @@ class EmailAgent }); return true; } catch (e) { - logger.error('Mail notification failed to send', { + logger.error('Email notification failed to send', { label: 'Notifications', message: e.message, }); @@ -282,6 +354,9 @@ class EmailAgent case Notification.MEDIA_APPROVED: this.sendMediaApprovedEmail(payload); break; + case Notification.MEDIA_AUTO_APPROVED: + this.sendMediaAutoApprovedEmail(payload); + break; case Notification.MEDIA_DECLINED: this.sendMediaDeclinedEmail(payload); break; diff --git a/server/lib/notifications/agents/pushbullet.ts b/server/lib/notifications/agents/pushbullet.ts index c7becfab0..ef40bff31 100644 --- a/server/lib/notifications/agents/pushbullet.ts +++ b/server/lib/notifications/agents/pushbullet.ts @@ -3,6 +3,7 @@ import { hasNotificationType, Notification } from '..'; import logger from '../../../logger'; import { getSettings, NotificationAgentPushbullet } from '../../settings'; import { BaseAgent, NotificationAgent, NotificationPayload } from './agent'; +import { MediaType } from '../../../constants/media'; interface PushbulletPayload { title: string; @@ -50,7 +51,9 @@ class PushbulletAgent switch (type) { case Notification.MEDIA_PENDING: - messageTitle = 'New Request'; + messageTitle = `New ${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Request`; message += `${title}`; if (plot) { message += `\n\n${plot}`; @@ -59,7 +62,20 @@ class PushbulletAgent message += `\nStatus: Pending Approval`; break; 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}`; if (plot) { message += `\n\n${plot}`; @@ -68,7 +84,9 @@ class PushbulletAgent message += `\nStatus: Processing`; break; case Notification.MEDIA_AVAILABLE: - messageTitle = 'Now Available'; + messageTitle = `${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Now Available`; message += `${title}`; if (plot) { message += `\n\n${plot}`; @@ -77,7 +95,9 @@ class PushbulletAgent message += `\nStatus: Available`; break; case Notification.MEDIA_DECLINED: - messageTitle = 'Request Declined'; + messageTitle = `${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Request Declined`; message += `${title}`; if (plot) { message += `\n\n${plot}`; @@ -86,7 +106,9 @@ class PushbulletAgent message += `\nStatus: Declined`; break; case Notification.MEDIA_FAILED: - messageTitle = 'Failed Request'; + messageTitle = `Failed ${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Request`; message += `${title}`; if (plot) { message += `\n\n${plot}`; diff --git a/server/lib/notifications/agents/pushover.ts b/server/lib/notifications/agents/pushover.ts index 19c6d6d91..588b46c7b 100644 --- a/server/lib/notifications/agents/pushover.ts +++ b/server/lib/notifications/agents/pushover.ts @@ -3,6 +3,7 @@ import { hasNotificationType, Notification } from '..'; import logger from '../../../logger'; import { getSettings, NotificationAgentPushover } from '../../settings'; import { BaseAgent, NotificationAgent, NotificationPayload } from './agent'; +import { MediaType } from '../../../constants/media'; interface PushoverPayload { token: string; @@ -64,7 +65,9 @@ class PushoverAgent switch (type) { case Notification.MEDIA_PENDING: - messageTitle = 'New Request'; + messageTitle = `New ${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Request`; message += `${title}`; if (plot) { message += `\n${plot}`; @@ -73,7 +76,20 @@ class PushoverAgent message += `\n\nStatus\nPending Approval`; break; case Notification.MEDIA_APPROVED: - messageTitle = 'Request Approved'; + messageTitle = `${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Request Approved`; + message += `${title}`; + if (plot) { + message += `\n${plot}`; + } + message += `\n\nRequested By\n${username}`; + message += `\n\nStatus\nProcessing`; + break; + case Notification.MEDIA_AUTO_APPROVED: + messageTitle = `${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Request Automatically Approved`; message += `${title}`; if (plot) { message += `\n${plot}`; @@ -82,7 +98,9 @@ class PushoverAgent message += `\n\nStatus\nProcessing`; break; case Notification.MEDIA_AVAILABLE: - messageTitle = 'Now Available'; + messageTitle = `${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Now Available`; message += `${title}`; if (plot) { message += `\n${plot}`; @@ -91,7 +109,9 @@ class PushoverAgent message += `\n\nStatus\nAvailable`; break; case Notification.MEDIA_DECLINED: - messageTitle = 'Request Declined'; + messageTitle = `${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Request Declined`; message += `${title}`; if (plot) { message += `\n${plot}`; @@ -101,7 +121,9 @@ class PushoverAgent priority = 1; break; case Notification.MEDIA_FAILED: - messageTitle = 'Failed Request'; + messageTitle = `Failed ${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Request`; message += `${title}`; if (plot) { message += `\n${plot}`; diff --git a/server/lib/notifications/agents/slack.ts b/server/lib/notifications/agents/slack.ts index 70a527f19..fc6643d6a 100644 --- a/server/lib/notifications/agents/slack.ts +++ b/server/lib/notifications/agents/slack.ts @@ -3,6 +3,7 @@ import { hasNotificationType, Notification } from '..'; import logger from '../../../logger'; import { getSettings, NotificationAgentSlack } from '../../settings'; import { BaseAgent, NotificationAgent, NotificationPayload } from './agent'; +import { MediaType } from '../../../constants/media'; interface EmbedField { type: 'plain_text' | 'mrkdwn'; @@ -72,35 +73,54 @@ class SlackAgent switch (type) { case Notification.MEDIA_PENDING: - header = 'New Request'; + header = `New ${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Request`; fields.push({ type: 'mrkdwn', text: '*Status*\nPending Approval', }); break; 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({ type: 'mrkdwn', text: '*Status*\nProcessing', }); break; case Notification.MEDIA_AVAILABLE: - header = 'Now Available'; + header = `${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Now Available`; fields.push({ type: 'mrkdwn', text: '*Status*\nAvailable', }); break; case Notification.MEDIA_DECLINED: - header = 'Request 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 Request'; + header = `Failed ${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Request`; fields.push({ type: 'mrkdwn', text: '*Status*\nFailed', @@ -206,7 +226,7 @@ class SlackAgent type: Notification, payload: NotificationPayload ): Promise { - logger.debug('Sending slack notification', { label: 'Notifications' }); + logger.debug('Sending Slack notification', { label: 'Notifications' }); try { const webhookUrl = this.getSettings().options.webhookUrl; diff --git a/server/lib/notifications/agents/telegram.ts b/server/lib/notifications/agents/telegram.ts index c3c890172..f26c5cbb8 100644 --- a/server/lib/notifications/agents/telegram.ts +++ b/server/lib/notifications/agents/telegram.ts @@ -2,6 +2,7 @@ import axios from 'axios'; import { hasNotificationType, Notification } from '..'; import logger from '../../../logger'; import { getSettings, NotificationAgentTelegram } from '../../settings'; +import { MediaType } from '../../../constants/media'; import { BaseAgent, NotificationAgent, NotificationPayload } from './agent'; interface TelegramMessagePayload { @@ -66,7 +67,9 @@ class TelegramAgent /* eslint-disable no-useless-escape */ switch (type) { case Notification.MEDIA_PENDING: - message += `\*New Request\*`; + message += `\*New ${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Request\*`; message += `\n\n\*${title}\*`; if (plot) { message += `\n${plot}`; @@ -75,7 +78,20 @@ class TelegramAgent message += `\n\n\*Status\*\nPending Approval`; break; 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}\*`; if (plot) { message += `\n${plot}`; @@ -84,7 +100,9 @@ class TelegramAgent message += `\n\n\*Status\*\nProcessing`; break; case Notification.MEDIA_AVAILABLE: - message += `\*Now Available\*`; + message += `\*${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Now Available\*`; message += `\n\n\*${title}\*`; if (plot) { message += `\n${plot}`; @@ -93,7 +111,9 @@ class TelegramAgent message += `\n\n\*Status\*\nAvailable`; break; case Notification.MEDIA_DECLINED: - message += `\*Request Declined\*`; + message += `\*${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Request Declined\*`; message += `\n\n\*${title}\*`; if (plot) { message += `\n${plot}`; @@ -102,7 +122,9 @@ class TelegramAgent message += `\n\n\*Status\*\nDeclined`; break; case Notification.MEDIA_FAILED: - message += `\*Failed Request\*`; + message += `\*Failed ${ + payload.media?.mediaType === MediaType.TV ? 'Series' : 'Movie' + } Request\*`; message += `\n\n\*${title}\*`; if (plot) { message += `\n${plot}`; @@ -129,12 +151,13 @@ class TelegramAgent type: Notification, payload: NotificationPayload ): Promise { - logger.debug('Sending telegram notification', { label: 'Notifications' }); + logger.debug('Sending Telegram notification', { label: 'Notifications' }); try { const endpoint = `${this.baseUrl}bot${ this.getSettings().options.botAPI }/${payload.image ? 'sendPhoto' : 'sendMessage'}`; + // Send system notification await (payload.image ? axios.post(endpoint, { photo: payload.image, @@ -150,7 +173,9 @@ class TelegramAgent disable_notification: this.getSettings().options.sendSilently, } as TelegramMessagePayload)); + // Send user notification if ( + this.userNotificationTypes.includes(type) && payload.notifyUser.settings?.enableNotifications && payload.notifyUser.settings?.telegramChatId && payload.notifyUser.settings?.telegramChatId !== diff --git a/server/lib/notifications/agents/webhook.ts b/server/lib/notifications/agents/webhook.ts index 5b846d728..fa3058fe9 100644 --- a/server/lib/notifications/agents/webhook.ts +++ b/server/lib/notifications/agents/webhook.ts @@ -138,7 +138,7 @@ class WebhookAgent return true; } catch (e) { - logger.error('Error sending Webhook notification', { + logger.error('Error sending webhook notification', { label: 'Notifications', errorMessage: e.message, }); diff --git a/server/lib/notifications/index.ts b/server/lib/notifications/index.ts index a50a2932c..b9cc84a9b 100644 --- a/server/lib/notifications/index.ts +++ b/server/lib/notifications/index.ts @@ -9,6 +9,7 @@ export enum Notification { MEDIA_FAILED = 16, TEST_NOTIFICATION = 32, MEDIA_DECLINED = 64, + MEDIA_AUTO_APPROVED = 128, } export const hasNotificationType = ( diff --git a/server/lib/settings.ts b/server/lib/settings.ts index 9566f2124..4debc8016 100644 --- a/server/lib/settings.ts +++ b/server/lib/settings.ts @@ -163,7 +163,6 @@ interface NotificationAgents { interface NotificationSettings { enabled: boolean; - autoapprovalEnabled: boolean; agents: NotificationAgents; } @@ -213,7 +212,6 @@ class Settings { }, notifications: { enabled: true, - autoapprovalEnabled: false, agents: { email: { enabled: false, diff --git a/server/routes/settings/notifications.ts b/server/routes/settings/notifications.ts index 58be3a4f2..fbf1ce1e5 100644 --- a/server/routes/settings/notifications.ts +++ b/server/routes/settings/notifications.ts @@ -15,7 +15,6 @@ notificationRoutes.get('/', (_req, res) => { const settings = getSettings().notifications; return res.status(200).json({ enabled: settings.enabled, - autoapprovalEnabled: settings.autoapprovalEnabled, }); }); @@ -24,13 +23,11 @@ notificationRoutes.post('/', (req, res) => { Object.assign(settings.notifications, { enabled: req.body.enabled, - autoapprovalEnabled: req.body.autoapprovalEnabled, }); settings.save(); return res.status(200).json({ enabled: settings.notifications.enabled, - autoapprovalEnabled: settings.notifications.autoapprovalEnabled, }); }); diff --git a/src/components/NotificationTypeSelector/index.tsx b/src/components/NotificationTypeSelector/index.tsx index c84631671..e58dc50ad 100644 --- a/src/components/NotificationTypeSelector/index.tsx +++ b/src/components/NotificationTypeSelector/index.tsx @@ -8,16 +8,19 @@ const messages = defineMessages({ 'Sends a notification when media is requested and requires approval.', mediaapproved: 'Media Approved', mediaapprovedDescription: - 'Sends a notification when media is approved.\ - By default, automatically approved requests will not trigger notifications.', + 'Sends a notification when requested media is manually approved.', + mediaAutoApproved: 'Media Automatically Approved', + mediaAutoApprovedDescription: + 'Sends a notification when requested media is automatically approved.', mediaavailable: 'Media Available', mediaavailableDescription: - 'Sends a notification when media becomes available.', + 'Sends a notification when requested media becomes available.', mediafailed: 'Media Failed', 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', - mediadeclinedDescription: 'Sends a notification when a request is declined.', + mediadeclinedDescription: + 'Sends a notification when a media request is declined.', }); export const hasNotificationType = ( @@ -46,6 +49,7 @@ export enum Notification { MEDIA_FAILED = 16, TEST_NOTIFICATION = 32, MEDIA_DECLINED = 64, + MEDIA_AUTO_APPROVED = 128, } export interface NotificationItem { @@ -74,6 +78,12 @@ const NotificationTypeSelector: React.FC = ({ description: intl.formatMessage(messages.mediarequestedDescription), 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', name: intl.formatMessage(messages.mediaapproved), diff --git a/src/components/Settings/Notifications/NotificationsEmail.tsx b/src/components/Settings/Notifications/NotificationsEmail.tsx index c01545c9a..644fb628e 100644 --- a/src/components/Settings/Notifications/NotificationsEmail.tsx +++ b/src/components/Settings/Notifications/NotificationsEmail.tsx @@ -32,10 +32,13 @@ const messages = defineMessages({ senderName: 'Sender Name', notificationtypes: 'Notification Types', validationEmail: 'You must provide a valid email address', - emailNotificationTypesAlert: 'Notification Email Recipients', + emailNotificationTypesAlert: 'Email Notification Recipients', emailNotificationTypesAlertDescription: - 'For the "Media Requested" and "Media Failed" notification types,\ - notifications will only be sent to users with the "Manage Requests" permission.', + 'Media Requested, Media Automatically Approved, and Media Failed\ + email notifications are sent to all users with the Manage Requests permission.', + emailNotificationTypesAlertDescriptionPt2: + 'Media Approved, Media Declined, and Media Available\ + email notifications are sent to the user who submitted the request.', }); const NotificationsEmail: React.FC = () => { @@ -134,9 +137,34 @@ const NotificationsEmail: React.FC = () => { title={intl.formatMessage(messages.emailNotificationTypesAlert)} type="info" > - {intl.formatMessage( - messages.emailNotificationTypesAlertDescription - )} +

+ {intl.formatMessage( + messages.emailNotificationTypesAlertDescription, + { + strong: function strong(msg) { + return ( + + {msg} + + ); + }, + } + )} +

+

+ {intl.formatMessage( + messages.emailNotificationTypesAlertDescriptionPt2, + { + strong: function strong(msg) { + return ( + + {msg} + + ); + }, + } + )} +

diff --git a/src/components/Settings/Notifications/NotificationsTelegram.tsx b/src/components/Settings/Notifications/NotificationsTelegram.tsx index 870a36600..f80ba7b52 100644 --- a/src/components/Settings/Notifications/NotificationsTelegram.tsx +++ b/src/components/Settings/Notifications/NotificationsTelegram.tsx @@ -27,7 +27,7 @@ const messages = defineMessages({ settinguptelegramDescription: 'To configure Telegram notifications, you will need to create a bot 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 @get_id_bot to the chat.', + You can find this by adding @get_id_bot to the chat and issuing the /my_id command.', notificationtypes: 'Notification Types', sendSilently: 'Send Silently', sendSilentlyTip: 'Send notifications with no sound', @@ -143,6 +143,9 @@ const NotificationsTelegram: React.FC = () => { ); }, + code: function code(msg) { + return {msg}; + }, })} diff --git a/src/components/Settings/SettingsNotifications.tsx b/src/components/Settings/SettingsNotifications.tsx index d849eb647..64b9ba2f3 100644 --- a/src/components/Settings/SettingsNotifications.tsx +++ b/src/components/Settings/SettingsNotifications.tsx @@ -28,7 +28,6 @@ const messages = defineMessages({ notificationsettingssaved: 'Notification settings saved successfully!', notificationsettingsfailed: 'Notification settings failed to save.', enablenotifications: 'Enable Notifications', - autoapprovedrequests: 'Enable Notifications for Automatic Approvals', email: 'Email', webhook: 'Webhook', }); @@ -187,14 +186,12 @@ const SettingsNotifications: React.FC = ({ children }) => { { try { await axios.post('/api/v1/settings/notifications', { enabled: values.enabled, - autoapprovalEnabled: values.autoapprovalEnabled, }); addToast(intl.formatMessage(messages.notificationsettingssaved), { appearance: 'success', @@ -233,26 +230,6 @@ const SettingsNotifications: React.FC = ({ children }) => { />
-
- -
- { - setFieldValue( - 'autoapprovalEnabled', - !values.autoapprovalEnabled - ); - }} - /> -
-
diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index a2c56a1f9..48d77ec23 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -95,14 +95,16 @@ "components.MovieDetails.view": "View", "components.MovieDetails.viewfullcrew": "View Full Crew", "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.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.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.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.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.mediarequestedDescription": "Sends a notification when media is requested and requires approval.", "components.PermissionEdit.admin": "Admin", @@ -318,8 +320,9 @@ "components.Settings.Notifications.chatId": "Chat ID", "components.Settings.Notifications.discordsettingsfailed": "Discord notification settings failed to save.", "components.Settings.Notifications.discordsettingssaved": "Discord notification settings saved successfully!", - "components.Settings.Notifications.emailNotificationTypesAlert": "Notification Email 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.emailNotificationTypesAlert": "Email Notification Recipients", + "components.Settings.Notifications.emailNotificationTypesAlertDescription": "Media Requested, Media Automatically Approved, and Media Failed email notifications are sent to all users with the Manage Requests permission.", + "components.Settings.Notifications.emailNotificationTypesAlertDescriptionPt2": "Media Approved, Media Declined, and Media Available email notifications are sent to the user who submitted the request.", "components.Settings.Notifications.emailsender": "Sender Address", "components.Settings.Notifications.emailsettingsfailed": "Email notification settings failed to save.", "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.senderName": "Sender Name", "components.Settings.Notifications.settinguptelegram": "Setting Up Telegram Notifications", - "components.Settings.Notifications.settinguptelegramDescription": "To configure Telegram notifications, you will need to create a bot 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 @get_id_bot to the chat.", + "components.Settings.Notifications.settinguptelegramDescription": "To configure Telegram notifications, you will need to create a bot 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 @get_id_bot to the chat and issuing the /my_id command.", "components.Settings.Notifications.smtpHost": "SMTP Host", "components.Settings.Notifications.smtpPort": "SMTP Port", "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.applicationTitle": "Application Title", "components.Settings.applicationurl": "Application URL", - "components.Settings.autoapprovedrequests": "Enable Notifications for Automatic Approvals", "components.Settings.cancelscan": "Cancel Scan", "components.Settings.copied": "Copied API key to clipboard.", "components.Settings.csrfProtection": "Enable CSRF Protection", diff --git a/src/styles/globals.css b/src/styles/globals.css index a5a88dcb6..1a973b8b9 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -63,7 +63,7 @@ a.slider-title { } .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 {