mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
@@ -1,5 +1,5 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Notification } from '..';
|
import { hasNotificationType, Notification } from '..';
|
||||||
import logger from '../../../logger';
|
import logger from '../../../logger';
|
||||||
import { getSettings, NotificationAgentDiscord } from '../../settings';
|
import { getSettings, NotificationAgentDiscord } from '../../settings';
|
||||||
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
|
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
|
||||||
@@ -196,10 +196,12 @@ class DiscordAgent
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add checking for type here once we add notification type filters for agents
|
public shouldSend(type: Notification): boolean {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
if (
|
||||||
public shouldSend(_type: Notification): boolean {
|
this.getSettings().enabled &&
|
||||||
if (this.getSettings().enabled && this.getSettings().options.webhookUrl) {
|
this.getSettings().options.webhookUrl &&
|
||||||
|
hasNotificationType(type, this.getSettings().types)
|
||||||
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
|
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
|
||||||
import { Notification } from '..';
|
import { hasNotificationType, Notification } from '..';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { getSettings, NotificationAgentEmail } from '../../settings';
|
import { getSettings, NotificationAgentEmail } from '../../settings';
|
||||||
import nodemailer from 'nodemailer';
|
import nodemailer from 'nodemailer';
|
||||||
@@ -22,12 +22,13 @@ class EmailAgent
|
|||||||
return settings.notifications.agents.email;
|
return settings.notifications.agents.email;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add checking for type here once we add notification type filters for agents
|
public shouldSend(type: Notification): boolean {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
public shouldSend(_type: Notification): boolean {
|
|
||||||
const settings = this.getSettings();
|
const settings = this.getSettings();
|
||||||
|
|
||||||
if (settings.enabled) {
|
if (
|
||||||
|
settings.enabled &&
|
||||||
|
hasNotificationType(type, this.getSettings().types)
|
||||||
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Notification } from '..';
|
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';
|
||||||
@@ -187,10 +187,12 @@ class SlackAgent
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add checking for type here once we add notification type filters for agents
|
public shouldSend(type: Notification): boolean {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
if (
|
||||||
public shouldSend(_type: Notification): boolean {
|
this.getSettings().enabled &&
|
||||||
if (this.getSettings().enabled && this.getSettings().options.webhookUrl) {
|
this.getSettings().options.webhookUrl &&
|
||||||
|
hasNotificationType(type, this.getSettings().types)
|
||||||
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { 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 { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
|
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
|
||||||
@@ -25,13 +25,12 @@ class TelegramAgent
|
|||||||
return settings.notifications.agents.telegram;
|
return settings.notifications.agents.telegram;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add checking for type here once we add notification type filters for agents
|
public shouldSend(type: Notification): boolean {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
public shouldSend(_type: Notification): boolean {
|
|
||||||
if (
|
if (
|
||||||
this.getSettings().enabled &&
|
this.getSettings().enabled &&
|
||||||
this.getSettings().options.botAPI &&
|
this.getSettings().options.botAPI &&
|
||||||
this.getSettings().options.chatId
|
this.getSettings().options.chatId &&
|
||||||
|
hasNotificationType(type, this.getSettings().types)
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,27 @@ export enum Notification {
|
|||||||
TEST_NOTIFICATION = 32,
|
TEST_NOTIFICATION = 32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const hasNotificationType = (
|
||||||
|
types: Notification | Notification[],
|
||||||
|
value: number
|
||||||
|
): boolean => {
|
||||||
|
let total = 0;
|
||||||
|
|
||||||
|
// If we are not checking any notifications, bail out and return true
|
||||||
|
if (types === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(types)) {
|
||||||
|
// Combine all notification values into one
|
||||||
|
total = types.reduce((a, v) => a + v, 0);
|
||||||
|
} else {
|
||||||
|
total = types;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!(value & total);
|
||||||
|
};
|
||||||
|
|
||||||
class NotificationManager {
|
class NotificationManager {
|
||||||
private activeAgents: NotificationAgent[] = [];
|
private activeAgents: NotificationAgent[] = [];
|
||||||
|
|
||||||
|
@@ -0,0 +1,70 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { NotificationItem, hasNotificationType } from '..';
|
||||||
|
|
||||||
|
interface NotificationTypeProps {
|
||||||
|
option: NotificationItem;
|
||||||
|
currentTypes: number;
|
||||||
|
parent?: NotificationItem;
|
||||||
|
onUpdate: (newTypes: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NotificationType: React.FC<NotificationTypeProps> = ({
|
||||||
|
option,
|
||||||
|
currentTypes,
|
||||||
|
onUpdate,
|
||||||
|
parent,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className={`relative flex items-start first:mt-0 mt-4 ${
|
||||||
|
!!parent?.value && hasNotificationType(parent.value, currentTypes)
|
||||||
|
? 'opacity-50'
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center h-5">
|
||||||
|
<input
|
||||||
|
id={option.id}
|
||||||
|
name="permissions"
|
||||||
|
type="checkbox"
|
||||||
|
className="w-4 h-4 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
||||||
|
disabled={
|
||||||
|
!!parent?.value && hasNotificationType(parent.value, currentTypes)
|
||||||
|
}
|
||||||
|
onClick={() => {
|
||||||
|
onUpdate(
|
||||||
|
hasNotificationType(option.value, currentTypes)
|
||||||
|
? currentTypes - option.value
|
||||||
|
: currentTypes + option.value
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
defaultChecked={
|
||||||
|
hasNotificationType(option.value, currentTypes) ||
|
||||||
|
(!!parent?.value &&
|
||||||
|
hasNotificationType(parent.value, currentTypes))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="ml-3 text-sm leading-5">
|
||||||
|
<label htmlFor={option.id} className="font-medium">
|
||||||
|
{option.name}
|
||||||
|
</label>
|
||||||
|
<p className="text-gray-500">{option.description}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{(option.children ?? []).map((child) => (
|
||||||
|
<div key={`notification-type-child-${child.id}`} className="pl-6 mt-4">
|
||||||
|
<NotificationType
|
||||||
|
option={child}
|
||||||
|
currentTypes={currentTypes}
|
||||||
|
onUpdate={(newTypes) => onUpdate(newTypes)}
|
||||||
|
parent={option}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NotificationType;
|
106
src/components/NotificationTypeSelector/index.tsx
Normal file
106
src/components/NotificationTypeSelector/index.tsx
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
import NotificationType from './NotificationType';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
mediarequested: 'Media Requested',
|
||||||
|
mediarequestedDescription:
|
||||||
|
'Sends a notification when new media is requested. For certain agents, this will only send the notification to admins or users with the "Manage Requests" permission.',
|
||||||
|
mediaapproved: 'Media Approved',
|
||||||
|
mediaapprovedDescription: 'Sends a notification when media is approved.',
|
||||||
|
mediaavailable: 'Media Available',
|
||||||
|
mediaavailableDescription:
|
||||||
|
'Sends a notification when media becomes available.',
|
||||||
|
mediafailed: 'Media Failed',
|
||||||
|
mediafailedDescription:
|
||||||
|
'Sends a notification when media fails to be added to services (Radarr/Sonarr). For certain agents, this will only send the notification to admins or users with the "Manage Requests" permission.',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const hasNotificationType = (
|
||||||
|
types: Notification | Notification[],
|
||||||
|
value: number
|
||||||
|
): boolean => {
|
||||||
|
let total = 0;
|
||||||
|
|
||||||
|
if (types === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(types)) {
|
||||||
|
total = types.reduce((a, v) => a + v, 0);
|
||||||
|
} else {
|
||||||
|
total = types;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!(value & total);
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum Notification {
|
||||||
|
MEDIA_PENDING = 2,
|
||||||
|
MEDIA_APPROVED = 4,
|
||||||
|
MEDIA_AVAILABLE = 8,
|
||||||
|
MEDIA_FAILED = 16,
|
||||||
|
TEST_NOTIFICATION = 32,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NotificationItem {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
value: Notification;
|
||||||
|
children?: NotificationItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NotificationTypeSelectorProps {
|
||||||
|
currentTypes: number;
|
||||||
|
onUpdate: (newTypes: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NotificationTypeSelector: React.FC<NotificationTypeSelectorProps> = ({
|
||||||
|
currentTypes,
|
||||||
|
onUpdate,
|
||||||
|
}) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const types: NotificationItem[] = [
|
||||||
|
{
|
||||||
|
id: 'media-requested',
|
||||||
|
name: intl.formatMessage(messages.mediarequested),
|
||||||
|
description: intl.formatMessage(messages.mediarequestedDescription),
|
||||||
|
value: Notification.MEDIA_PENDING,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'media-approved',
|
||||||
|
name: intl.formatMessage(messages.mediaapproved),
|
||||||
|
description: intl.formatMessage(messages.mediaapprovedDescription),
|
||||||
|
value: Notification.MEDIA_APPROVED,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'media-available',
|
||||||
|
name: intl.formatMessage(messages.mediaavailable),
|
||||||
|
description: intl.formatMessage(messages.mediaavailableDescription),
|
||||||
|
value: Notification.MEDIA_AVAILABLE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'media-failed',
|
||||||
|
name: intl.formatMessage(messages.mediafailed),
|
||||||
|
description: intl.formatMessage(messages.mediafailedDescription),
|
||||||
|
value: Notification.MEDIA_FAILED,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{types.map((type) => (
|
||||||
|
<NotificationType
|
||||||
|
key={`notification-type-${type.id}`}
|
||||||
|
option={type}
|
||||||
|
currentTypes={currentTypes}
|
||||||
|
onUpdate={onUpdate}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NotificationTypeSelector;
|
@@ -7,6 +7,7 @@ import { defineMessages, useIntl } from 'react-intl';
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
|
import NotificationTypeSelector from '../../NotificationTypeSelector';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
save: 'Save Changes',
|
save: 'Save Changes',
|
||||||
@@ -19,6 +20,7 @@ const messages = defineMessages({
|
|||||||
discordsettingsfailed: 'Discord notification settings failed to save.',
|
discordsettingsfailed: 'Discord notification settings failed to save.',
|
||||||
testsent: 'Test notification sent!',
|
testsent: 'Test notification sent!',
|
||||||
test: 'Test',
|
test: 'Test',
|
||||||
|
notificationtypes: 'Notification Types',
|
||||||
});
|
});
|
||||||
|
|
||||||
const NotificationsDiscord: React.FC = () => {
|
const NotificationsDiscord: React.FC = () => {
|
||||||
@@ -69,7 +71,7 @@ const NotificationsDiscord: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{({ errors, touched, isSubmitting, values, isValid }) => {
|
{({ errors, touched, isSubmitting, values, isValid, setFieldValue }) => {
|
||||||
const testSettings = async () => {
|
const testSettings = async () => {
|
||||||
await axios.post('/api/v1/settings/notifications/discord/test', {
|
await axios.post('/api/v1/settings/notifications/discord/test', {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -99,7 +101,7 @@ const NotificationsDiscord: React.FC = () => {
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="enabled"
|
id="enabled"
|
||||||
name="enabled"
|
name="enabled"
|
||||||
className="form-checkbox rounded-md h-6 w-6 text-indigo-600 transition duration-150 ease-in-out"
|
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -111,7 +113,7 @@ const NotificationsDiscord: React.FC = () => {
|
|||||||
{intl.formatMessage(messages.webhookUrl)}
|
{intl.formatMessage(messages.webhookUrl)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
||||||
<div className="max-w-lg flex rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="webhookUrl"
|
id="webhookUrl"
|
||||||
name="webhookUrl"
|
name="webhookUrl"
|
||||||
@@ -119,17 +121,41 @@ const NotificationsDiscord: React.FC = () => {
|
|||||||
placeholder={intl.formatMessage(
|
placeholder={intl.formatMessage(
|
||||||
messages.webhookUrlPlaceholder
|
messages.webhookUrlPlaceholder
|
||||||
)}
|
)}
|
||||||
className="flex-1 form-input block w-full min-w-0 rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500"
|
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.webhookUrl && touched.webhookUrl && (
|
{errors.webhookUrl && touched.webhookUrl && (
|
||||||
<div className="text-red-500 mt-2">{errors.webhookUrl}</div>
|
<div className="mt-2 text-red-500">{errors.webhookUrl}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-8 border-t border-gray-700 pt-5">
|
<div className="mt-6">
|
||||||
|
<div role="group" aria-labelledby="label-permissions">
|
||||||
|
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline">
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
className="text-base font-medium leading-6 text-gray-400 sm:text-sm sm:leading-5"
|
||||||
|
id="label-types"
|
||||||
|
>
|
||||||
|
{intl.formatMessage(messages.notificationtypes)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 sm:mt-0 sm:col-span-2">
|
||||||
|
<div className="max-w-lg">
|
||||||
|
<NotificationTypeSelector
|
||||||
|
currentTypes={values.types}
|
||||||
|
onUpdate={(newTypes) =>
|
||||||
|
setFieldValue('types', newTypes)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="pt-5 mt-8 border-t border-gray-700">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<span className="ml-3 inline-flex rounded-md shadow-sm">
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
<Button
|
<Button
|
||||||
buttonType="warning"
|
buttonType="warning"
|
||||||
disabled={isSubmitting || !isValid}
|
disabled={isSubmitting || !isValid}
|
||||||
@@ -142,7 +168,7 @@ const NotificationsDiscord: React.FC = () => {
|
|||||||
{intl.formatMessage(messages.test)}
|
{intl.formatMessage(messages.test)}
|
||||||
</Button>
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
<span className="ml-3 inline-flex rounded-md shadow-sm">
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
<Button
|
<Button
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
@@ -7,6 +7,7 @@ import { defineMessages, useIntl } from 'react-intl';
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
|
import NotificationTypeSelector from '../../NotificationTypeSelector';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
save: 'Save Changes',
|
save: 'Save Changes',
|
||||||
@@ -29,6 +30,7 @@ const messages = defineMessages({
|
|||||||
ssldisabletip:
|
ssldisabletip:
|
||||||
'SSL should be disabled on standard TLS connections (Port 587)',
|
'SSL should be disabled on standard TLS connections (Port 587)',
|
||||||
senderName: 'Sender Name',
|
senderName: 'Sender Name',
|
||||||
|
notificationtypes: 'Notification Types',
|
||||||
});
|
});
|
||||||
|
|
||||||
const NotificationsEmail: React.FC = () => {
|
const NotificationsEmail: React.FC = () => {
|
||||||
@@ -99,7 +101,7 @@ const NotificationsEmail: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{({ errors, touched, isSubmitting, values, isValid }) => {
|
{({ errors, touched, isSubmitting, values, isValid, setFieldValue }) => {
|
||||||
const testSettings = async () => {
|
const testSettings = async () => {
|
||||||
await axios.post('/api/v1/settings/notifications/email/test', {
|
await axios.post('/api/v1/settings/notifications/email/test', {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -292,11 +294,36 @@ const NotificationsEmail: React.FC = () => {
|
|||||||
id="authPass"
|
id="authPass"
|
||||||
name="authPass"
|
name="authPass"
|
||||||
type="password"
|
type="password"
|
||||||
|
autoComplete="off"
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="mt-6">
|
||||||
|
<div role="group" aria-labelledby="label-permissions">
|
||||||
|
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline">
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
className="text-base font-medium leading-6 text-gray-400 sm:text-sm sm:leading-5"
|
||||||
|
id="label-types"
|
||||||
|
>
|
||||||
|
{intl.formatMessage(messages.notificationtypes)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 sm:mt-0 sm:col-span-2">
|
||||||
|
<div className="max-w-lg">
|
||||||
|
<NotificationTypeSelector
|
||||||
|
currentTypes={values.types}
|
||||||
|
onUpdate={(newTypes) =>
|
||||||
|
setFieldValue('types', newTypes)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="pt-5 mt-8 border-t border-gray-700">
|
<div className="pt-5 mt-8 border-t border-gray-700">
|
||||||
<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">
|
||||||
|
@@ -8,6 +8,7 @@ import axios from 'axios';
|
|||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
import Alert from '../../../Common/Alert';
|
import Alert from '../../../Common/Alert';
|
||||||
|
import NotificationTypeSelector from '../../../NotificationTypeSelector';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
save: 'Save Changes',
|
save: 'Save Changes',
|
||||||
@@ -23,6 +24,7 @@ const messages = defineMessages({
|
|||||||
settingupslack: 'Setting up Slack Notifications',
|
settingupslack: 'Setting up Slack Notifications',
|
||||||
settingupslackDescription:
|
settingupslackDescription:
|
||||||
'To use Slack notifications, you will need to create an <WebhookLink>Incoming Webhook</WebhookLink> integration and use the provided webhook URL below.',
|
'To use Slack notifications, you will need to create an <WebhookLink>Incoming Webhook</WebhookLink> integration and use the provided webhook URL below.',
|
||||||
|
notificationtypes: 'Notification Types',
|
||||||
});
|
});
|
||||||
|
|
||||||
const NotificationsSlack: React.FC = () => {
|
const NotificationsSlack: React.FC = () => {
|
||||||
@@ -44,24 +46,22 @@ const NotificationsSlack: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p className="mb-">
|
<Alert title={intl.formatMessage(messages.settingupslack)} type="info">
|
||||||
<Alert title={intl.formatMessage(messages.settingupslack)} type="info">
|
{intl.formatMessage(messages.settingupslackDescription, {
|
||||||
{intl.formatMessage(messages.settingupslackDescription, {
|
WebhookLink: function WebhookLink(msg) {
|
||||||
WebhookLink: function WebhookLink(msg) {
|
return (
|
||||||
return (
|
<a
|
||||||
<a
|
href="https://my.slack.com/services/new/incoming-webhook/"
|
||||||
href="https://my.slack.com/services/new/incoming-webhook/"
|
className="text-indigo-100 hover:text-white hover:underline"
|
||||||
className="text-indigo-100 hover:text-white hover:underline"
|
target="_blank"
|
||||||
target="_blank"
|
rel="noreferrer"
|
||||||
rel="noreferrer"
|
>
|
||||||
>
|
{msg}
|
||||||
{msg}
|
</a>
|
||||||
</a>
|
);
|
||||||
);
|
},
|
||||||
},
|
})}
|
||||||
})}
|
</Alert>
|
||||||
</Alert>
|
|
||||||
</p>
|
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={{
|
initialValues={{
|
||||||
enabled: data.enabled,
|
enabled: data.enabled,
|
||||||
@@ -92,7 +92,14 @@ const NotificationsSlack: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{({ errors, touched, isSubmitting, values, isValid }) => {
|
{({
|
||||||
|
errors,
|
||||||
|
touched,
|
||||||
|
isSubmitting,
|
||||||
|
values,
|
||||||
|
isValid,
|
||||||
|
setFieldValue,
|
||||||
|
}) => {
|
||||||
const testSettings = async () => {
|
const testSettings = async () => {
|
||||||
await axios.post('/api/v1/settings/notifications/slack/test', {
|
await axios.post('/api/v1/settings/notifications/slack/test', {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -150,6 +157,30 @@ const NotificationsSlack: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="mt-6">
|
||||||
|
<div role="group" aria-labelledby="label-permissions">
|
||||||
|
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline">
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
className="text-base font-medium leading-6 text-gray-400 sm:text-sm sm:leading-5"
|
||||||
|
id="label-types"
|
||||||
|
>
|
||||||
|
{intl.formatMessage(messages.notificationtypes)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 sm:mt-0 sm:col-span-2">
|
||||||
|
<div className="max-w-lg">
|
||||||
|
<NotificationTypeSelector
|
||||||
|
currentTypes={values.types}
|
||||||
|
onUpdate={(newTypes) =>
|
||||||
|
setFieldValue('types', newTypes)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="pt-5 mt-8 border-t border-gray-700">
|
<div className="pt-5 mt-8 border-t border-gray-700">
|
||||||
<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">
|
||||||
|
@@ -8,6 +8,7 @@ import axios from 'axios';
|
|||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
import Alert from '../../Common/Alert';
|
import Alert from '../../Common/Alert';
|
||||||
|
import NotificationTypeSelector from '../../NotificationTypeSelector';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
save: 'Save Changes',
|
save: 'Save Changes',
|
||||||
@@ -26,6 +27,7 @@ const messages = defineMessages({
|
|||||||
'To setup Telegram you need to <CreateBotLink>create a bot</CreateBotLink> and get the bot API key.\
|
'To setup Telegram you need to <CreateBotLink>create a bot</CreateBotLink> and get the bot API key.\
|
||||||
Additionally, you need the chat id for the chat you want the bot to send notifications to.\
|
Additionally, you need the chat id for the chat you want the bot to send notifications to.\
|
||||||
You can do this by adding <GetIdBotLink>@get_id_bot</GetIdBotLink> to the chat or group chat.',
|
You can do this by adding <GetIdBotLink>@get_id_bot</GetIdBotLink> to the chat or group chat.',
|
||||||
|
notificationtypes: 'Notification Types',
|
||||||
});
|
});
|
||||||
|
|
||||||
const NotificationsTelegram: React.FC = () => {
|
const NotificationsTelegram: React.FC = () => {
|
||||||
@@ -81,7 +83,7 @@ const NotificationsTelegram: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{({ errors, touched, isSubmitting, values, isValid }) => {
|
{({ errors, touched, isSubmitting, values, isValid, setFieldValue }) => {
|
||||||
const testSettings = async () => {
|
const testSettings = async () => {
|
||||||
await axios.post('/api/v1/settings/notifications/telegram/test', {
|
await axios.post('/api/v1/settings/notifications/telegram/test', {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -100,39 +102,37 @@ const NotificationsTelegram: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p className="mb-">
|
<Alert
|
||||||
<Alert
|
title={intl.formatMessage(messages.settinguptelegram)}
|
||||||
title={intl.formatMessage(messages.settinguptelegram)}
|
type="info"
|
||||||
type="info"
|
>
|
||||||
>
|
{intl.formatMessage(messages.settinguptelegramDescription, {
|
||||||
{intl.formatMessage(messages.settinguptelegramDescription, {
|
CreateBotLink: function CreateBotLink(msg) {
|
||||||
CreateBotLink: function CreateBotLink(msg) {
|
return (
|
||||||
return (
|
<a
|
||||||
<a
|
href="https://core.telegram.org/bots#6-botfather"
|
||||||
href="https://core.telegram.org/bots#6-botfather"
|
className="text-indigo-100 hover:text-white hover:underline"
|
||||||
className="text-indigo-100 hover:text-white hover:underline"
|
target="_blank"
|
||||||
target="_blank"
|
rel="noreferrer"
|
||||||
rel="noreferrer"
|
>
|
||||||
>
|
{msg}
|
||||||
{msg}
|
</a>
|
||||||
</a>
|
);
|
||||||
);
|
},
|
||||||
},
|
GetIdBotLink: function GetIdBotLink(msg) {
|
||||||
GetIdBotLink: function GetIdBotLink(msg) {
|
return (
|
||||||
return (
|
<a
|
||||||
<a
|
href="https://telegram.me/get_id_bot"
|
||||||
href="https://telegram.me/get_id_bot"
|
className="text-indigo-100 hover:text-white hover:underline"
|
||||||
className="text-indigo-100 hover:text-white hover:underline"
|
target="_blank"
|
||||||
target="_blank"
|
rel="noreferrer"
|
||||||
rel="noreferrer"
|
>
|
||||||
>
|
{msg}
|
||||||
{msg}
|
</a>
|
||||||
</a>
|
);
|
||||||
);
|
},
|
||||||
},
|
})}
|
||||||
})}
|
</Alert>
|
||||||
</Alert>
|
|
||||||
</p>
|
|
||||||
<Form>
|
<Form>
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5">
|
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5">
|
||||||
<label
|
<label
|
||||||
@@ -158,17 +158,17 @@ const NotificationsTelegram: React.FC = () => {
|
|||||||
{intl.formatMessage(messages.botAPI)}
|
{intl.formatMessage(messages.botAPI)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
||||||
<div className="max-w-lg flex rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="botAPI"
|
id="botAPI"
|
||||||
name="botAPI"
|
name="botAPI"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={intl.formatMessage(messages.botAPI)}
|
placeholder={intl.formatMessage(messages.botAPI)}
|
||||||
className="flex-1 form-input block w-full min-w-0 rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500"
|
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.botAPI && touched.botAPI && (
|
{errors.botAPI && touched.botAPI && (
|
||||||
<div className="text-red-500 mt-2">{errors.botAPI}</div>
|
<div className="mt-2 text-red-500">{errors.botAPI}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<label
|
<label
|
||||||
@@ -178,23 +178,47 @@ const NotificationsTelegram: React.FC = () => {
|
|||||||
{intl.formatMessage(messages.chatId)}
|
{intl.formatMessage(messages.chatId)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
||||||
<div className="max-w-lg flex rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="chatId"
|
id="chatId"
|
||||||
name="chatId"
|
name="chatId"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={intl.formatMessage(messages.chatId)}
|
placeholder={intl.formatMessage(messages.chatId)}
|
||||||
className="flex-1 form-input block w-full min-w-0 rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500"
|
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.chatId && touched.chatId && (
|
{errors.chatId && touched.chatId && (
|
||||||
<div className="text-red-500 mt-2">{errors.chatId}</div>
|
<div className="mt-2 text-red-500">{errors.chatId}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-8 border-t border-gray-700 pt-5">
|
<div className="mt-6">
|
||||||
|
<div role="group" aria-labelledby="label-permissions">
|
||||||
|
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline">
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
className="text-base font-medium leading-6 text-gray-400 sm:text-sm sm:leading-5"
|
||||||
|
id="label-types"
|
||||||
|
>
|
||||||
|
{intl.formatMessage(messages.notificationtypes)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 sm:mt-0 sm:col-span-2">
|
||||||
|
<div className="max-w-lg">
|
||||||
|
<NotificationTypeSelector
|
||||||
|
currentTypes={values.types}
|
||||||
|
onUpdate={(newTypes) =>
|
||||||
|
setFieldValue('types', newTypes)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="pt-5 mt-8 border-t border-gray-700">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<span className="ml-3 inline-flex rounded-md shadow-sm">
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
<Button
|
<Button
|
||||||
buttonType="warning"
|
buttonType="warning"
|
||||||
disabled={isSubmitting || !isValid}
|
disabled={isSubmitting || !isValid}
|
||||||
@@ -207,7 +231,7 @@ const NotificationsTelegram: React.FC = () => {
|
|||||||
{intl.formatMessage(messages.test)}
|
{intl.formatMessage(messages.test)}
|
||||||
</Button>
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
<span className="ml-3 inline-flex rounded-md shadow-sm">
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
<Button
|
<Button
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
@@ -60,6 +60,14 @@
|
|||||||
"components.MovieDetails.viewfullcrew": "View Full Crew",
|
"components.MovieDetails.viewfullcrew": "View Full Crew",
|
||||||
"components.MovieDetails.viewrequest": "View Request",
|
"components.MovieDetails.viewrequest": "View Request",
|
||||||
"components.MovieDetails.watchtrailer": "Watch Trailer",
|
"components.MovieDetails.watchtrailer": "Watch Trailer",
|
||||||
|
"components.NotificationTypeSelector.mediaapproved": "Media Approved",
|
||||||
|
"components.NotificationTypeSelector.mediaapprovedDescription": "Sends a notification when media is approved.",
|
||||||
|
"components.NotificationTypeSelector.mediaavailable": "Media Available",
|
||||||
|
"components.NotificationTypeSelector.mediaavailableDescription": "Sends a notification when media becomes available.",
|
||||||
|
"components.NotificationTypeSelector.mediafailed": "Media Failed",
|
||||||
|
"components.NotificationTypeSelector.mediafailedDescription": "Sends a notification when media fails to be added to services (Radarr/Sonarr). For certain agents, this will only send the notification to admins or users with the \"Manage Requests\" permission.",
|
||||||
|
"components.NotificationTypeSelector.mediarequested": "Media Requested",
|
||||||
|
"components.NotificationTypeSelector.mediarequestedDescription": "Sends a notification when new media is requested. For certain agents, this will only send the notification to admins or users with the \"Manage Requests\" permission.",
|
||||||
"components.PersonDetails.appearsin": "Appears in",
|
"components.PersonDetails.appearsin": "Appears in",
|
||||||
"components.PersonDetails.ascharacter": "as {character}",
|
"components.PersonDetails.ascharacter": "as {character}",
|
||||||
"components.PersonDetails.crewmember": "Crew Member",
|
"components.PersonDetails.crewmember": "Crew Member",
|
||||||
@@ -105,6 +113,7 @@
|
|||||||
"components.RequestModal.status": "Status",
|
"components.RequestModal.status": "Status",
|
||||||
"components.Search.searchresults": "Search Results",
|
"components.Search.searchresults": "Search Results",
|
||||||
"components.Settings.Notifications.NotificationsSlack.agentenabled": "Agent Enabled",
|
"components.Settings.Notifications.NotificationsSlack.agentenabled": "Agent Enabled",
|
||||||
|
"components.Settings.Notifications.NotificationsSlack.notificationtypes": "Notification Types",
|
||||||
"components.Settings.Notifications.NotificationsSlack.save": "Save Changes",
|
"components.Settings.Notifications.NotificationsSlack.save": "Save Changes",
|
||||||
"components.Settings.Notifications.NotificationsSlack.saving": "Saving...",
|
"components.Settings.Notifications.NotificationsSlack.saving": "Saving...",
|
||||||
"components.Settings.Notifications.NotificationsSlack.settingupslack": "Setting up Slack Notifications",
|
"components.Settings.Notifications.NotificationsSlack.settingupslack": "Setting up Slack Notifications",
|
||||||
@@ -128,6 +137,7 @@
|
|||||||
"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!",
|
"components.Settings.Notifications.emailsettingssaved": "Email notification settings saved!",
|
||||||
"components.Settings.Notifications.enableSsl": "Enable SSL",
|
"components.Settings.Notifications.enableSsl": "Enable SSL",
|
||||||
|
"components.Settings.Notifications.notificationtypes": "Notification Types",
|
||||||
"components.Settings.Notifications.save": "Save Changes",
|
"components.Settings.Notifications.save": "Save Changes",
|
||||||
"components.Settings.Notifications.saving": "Saving…",
|
"components.Settings.Notifications.saving": "Saving…",
|
||||||
"components.Settings.Notifications.senderName": "Sender Name",
|
"components.Settings.Notifications.senderName": "Sender Name",
|
||||||
|
Reference in New Issue
Block a user