mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-02-20 00:59:40 +00:00
Update:Notifications onTest for testing and parse title/body template #996
This commit is contained in:
parent
8e8046541e
commit
0ef2a2e4b6
9 changed files with 202 additions and 139 deletions
|
|
@ -65,7 +65,7 @@ class Server {
|
|||
this.auth = new Auth(this.db)
|
||||
|
||||
// Managers
|
||||
this.notificationManager = new NotificationManager(this.db)
|
||||
this.notificationManager = new NotificationManager(this.db, this.emitter.bind(this))
|
||||
this.backupManager = new BackupManager(this.db, this.emitter.bind(this))
|
||||
this.logManager = new LogManager(this.db)
|
||||
this.cacheManager = new CacheManager()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
const Logger = require('../Logger')
|
||||
const { version } = require('../../package.json')
|
||||
|
||||
class NotificationController {
|
||||
constructor() { }
|
||||
|
|
@ -22,6 +23,11 @@ class NotificationController {
|
|||
res.json(this.notificationManager.getData())
|
||||
}
|
||||
|
||||
async fireTestEvent(req, res) {
|
||||
await this.notificationManager.triggerNotification('onTest', { version: `v${version}` }, req.query.fail === '1')
|
||||
res.sendStatus(200)
|
||||
}
|
||||
|
||||
async createNotification(req, res) {
|
||||
const success = this.db.notificationSettings.createNotification(req.body)
|
||||
|
||||
|
|
@ -40,17 +46,18 @@ class NotificationController {
|
|||
|
||||
async updateNotification(req, res) {
|
||||
const success = this.db.notificationSettings.updateNotification(req.body)
|
||||
console.log('Update notification', success, req.body)
|
||||
if (success) {
|
||||
await this.db.updateEntity('settings', this.db.notificationSettings)
|
||||
}
|
||||
res.json(this.db.notificationSettings)
|
||||
}
|
||||
|
||||
sendNotificationTest(req, res) {
|
||||
if (!this.db.notificationSettings.isUsable) return res.status(500).send('Apprise is not configured')
|
||||
this.notificationManager.onTest()
|
||||
res.sendStatus(200)
|
||||
async sendNotificationTest(req, res) {
|
||||
if (!this.db.notificationSettings.isUseable) return res.status(500).send('Apprise is not configured')
|
||||
|
||||
const success = await this.notificationManager.sendTestNotification(req.notification)
|
||||
if (success) res.sendStatus(200)
|
||||
else res.sendStatus(500)
|
||||
}
|
||||
|
||||
middleware(req, res, next) {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ const Logger = require("../Logger")
|
|||
const { notificationData } = require('../utils/notifications')
|
||||
|
||||
class NotificationManager {
|
||||
constructor(db) {
|
||||
constructor(db, emitter) {
|
||||
this.db = db
|
||||
this.emitter = emitter
|
||||
|
||||
this.notificationFailedMap = {}
|
||||
}
|
||||
|
|
@ -17,38 +18,59 @@ class NotificationManager {
|
|||
if (!this.db.notificationSettings.isUseable) return
|
||||
|
||||
Logger.debug(`[NotificationManager] onPodcastEpisodeDownloaded: Episode "${episode.title}" for podcast ${libraryItem.media.metadata.title}`)
|
||||
this.triggerNotification('onPodcastEpisodeDownloaded', { libraryItem, episode })
|
||||
const library = this.db.libraries.find(lib => lib.id === libraryItem.libraryId)
|
||||
const eventData = {
|
||||
libraryItemId: libraryItem.id,
|
||||
libraryId: libraryItem.libraryId,
|
||||
libraryName: library ? library.name : 'Unknown',
|
||||
podcastTitle: libraryItem.media.metadata.title,
|
||||
episodeId: episode.id,
|
||||
episodeTitle: episode.title
|
||||
}
|
||||
this.triggerNotification('onPodcastEpisodeDownloaded', eventData)
|
||||
}
|
||||
|
||||
onTest() {
|
||||
this.triggerNotification('onTest')
|
||||
}
|
||||
|
||||
async triggerNotification(eventName, eventData) {
|
||||
if (!this.db.notificationSettings.isUseable) return
|
||||
async triggerNotification(eventName, eventData, intentionallyFail = false) {
|
||||
if (!this.db.notificationSettings.isUseable) return false
|
||||
|
||||
const notifications = this.db.notificationSettings.getNotificationsForEvent(eventName)
|
||||
for (const notification of notifications) {
|
||||
Logger.debug(`[NotificationManager] triggerNotification: Sending ${eventName} notification ${notification.id}`)
|
||||
const success = await this.sendNotification(notification, eventData)
|
||||
const success = intentionallyFail ? false : await this.sendNotification(notification, eventData)
|
||||
|
||||
notification.updateNotificationFired(success)
|
||||
if (!success) { // Failed notification
|
||||
if (!this.notificationFailedMap[notification.id]) this.notificationFailedMap[notification.id] = 1
|
||||
else this.notificationFailedMap[notification.id]++
|
||||
|
||||
if (this.notificationFailedMap[notification.id] > 2) {
|
||||
if (notification.numConsecutiveFailedAttempts > 2) {
|
||||
Logger.error(`[NotificationManager] triggerNotification: ${notification.eventName}/${notification.id} reached max failed attempts`)
|
||||
// TODO: Do something like disable the notification
|
||||
notification.enabled = false
|
||||
} else {
|
||||
Logger.error(`[NotificationManager] triggerNotification: ${notification.eventName}/${notification.id} ${notification.numConsecutiveFailedAttempts} failed attempts`)
|
||||
}
|
||||
} else { // Successful notification
|
||||
delete this.notificationFailedMap[notification.id]
|
||||
}
|
||||
}
|
||||
|
||||
await this.db.updateEntity('settings', this.db.notificationSettings)
|
||||
this.emitter('notifications_updated', this.db.notificationSettings)
|
||||
return true
|
||||
}
|
||||
|
||||
sendTestNotification(notification) {
|
||||
const eventData = notificationData.events.find(e => e.name === notification.eventName)
|
||||
if (!eventData) {
|
||||
Logger.error(`[NotificationManager] sendTestNotification: Event not found ${notification.eventName}`)
|
||||
return false
|
||||
}
|
||||
|
||||
return this.sendNotification(notification, eventData.testData)
|
||||
}
|
||||
|
||||
sendNotification(notification, eventData) {
|
||||
const payload = notification.getApprisePayload(eventData)
|
||||
return axios.post(`${this.db.notificationSettings.appriseApiUrl}/notify`, payload, { timeout: 6000 }).then((response) => {
|
||||
return axios.post(this.db.notificationSettings.appriseApiUrl, payload, { timeout: 6000 }).then((response) => {
|
||||
Logger.debug(`[NotificationManager] sendNotification: ${notification.eventName}/${notification.id} response=`, response.data)
|
||||
return true
|
||||
}).catch((error) => {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ class Notification {
|
|||
this.type = 'info'
|
||||
this.enabled = false
|
||||
|
||||
this.lastFiredAt = null
|
||||
this.lastAttemptFailed = false
|
||||
this.numConsecutiveFailedAttempts = 0
|
||||
this.numTimesFired = 0
|
||||
this.createdAt = null
|
||||
|
||||
if (notification) {
|
||||
|
|
@ -27,6 +31,10 @@ class Notification {
|
|||
this.bodyTemplate = notification.bodyTemplate || ''
|
||||
this.type = notification.type || 'info'
|
||||
this.enabled = !!notification.enabled
|
||||
this.lastFiredAt = notification.lastFiredAt || null
|
||||
this.lastAttemptFailed = !!notification.lastAttemptFailed
|
||||
this.numConsecutiveFailedAttempts = notification.numConsecutiveFailedAttempts || 0
|
||||
this.numTimesFired = notification.numTimesFired || 0
|
||||
this.createdAt = notification.createdAt
|
||||
}
|
||||
|
||||
|
|
@ -40,6 +48,10 @@ class Notification {
|
|||
bodyTemplate: this.bodyTemplate,
|
||||
enabled: this.enabled,
|
||||
type: this.type,
|
||||
lastFiredAt: this.lastFiredAt,
|
||||
lastAttemptFailed: this.lastAttemptFailed,
|
||||
numConsecutiveFailedAttempts: this.numConsecutiveFailedAttempts,
|
||||
numTimesFired: this.numTimesFired,
|
||||
createdAt: this.createdAt
|
||||
}
|
||||
}
|
||||
|
|
@ -57,6 +69,13 @@ class Notification {
|
|||
}
|
||||
|
||||
update(payload) {
|
||||
if (!this.enabled && payload.enabled) {
|
||||
// Reset
|
||||
this.lastFiredAt = null
|
||||
this.lastAttemptFailed = false
|
||||
this.numConsecutiveFailedAttempts = 0
|
||||
}
|
||||
|
||||
const keysToUpdate = ['libraryId', 'eventName', 'urls', 'titleTemplate', 'bodyTemplate', 'enabled', 'type']
|
||||
var hasUpdated = false
|
||||
for (const key of keysToUpdate) {
|
||||
|
|
@ -75,14 +94,32 @@ class Notification {
|
|||
return hasUpdated
|
||||
}
|
||||
|
||||
updateNotificationFired(success) {
|
||||
this.lastFiredAt = Date.now()
|
||||
this.lastAttemptFailed = !success
|
||||
this.numConsecutiveFailedAttempts = success ? 0 : this.numConsecutiveFailedAttempts + 1
|
||||
this.numTimesFired++
|
||||
}
|
||||
|
||||
replaceVariablesInTemplate(templateText, data) {
|
||||
const ptrn = /{{ ?([a-zA-Z]+) ?}}/mg
|
||||
|
||||
var match
|
||||
var updatedTemplate = templateText
|
||||
while ((match = ptrn.exec(templateText)) != null) {
|
||||
if (data[match[1]]) {
|
||||
updatedTemplate = updatedTemplate.replace(match[0], data[match[1]])
|
||||
}
|
||||
}
|
||||
return updatedTemplate
|
||||
}
|
||||
|
||||
parseTitleTemplate(data) {
|
||||
// TODO: Implement template parsing
|
||||
return 'Test Title'
|
||||
return this.replaceVariablesInTemplate(this.titleTemplate, data)
|
||||
}
|
||||
|
||||
parseBodyTemplate(data) {
|
||||
// TODO: Implement template parsing
|
||||
return 'Test Body'
|
||||
return this.replaceVariablesInTemplate(this.bodyTemplate, data)
|
||||
}
|
||||
|
||||
getApprisePayload(data) {
|
||||
|
|
|
|||
|
|
@ -207,6 +207,7 @@ class ApiRouter {
|
|||
this.router.get('/notifications', NotificationController.middleware.bind(this), NotificationController.get.bind(this))
|
||||
this.router.patch('/notifications', NotificationController.middleware.bind(this), NotificationController.update.bind(this))
|
||||
this.router.get('/notificationdata', NotificationController.middleware.bind(this), NotificationController.getData.bind(this))
|
||||
this.router.get('/notifications/test', NotificationController.middleware.bind(this), NotificationController.fireTestEvent.bind(this))
|
||||
this.router.post('/notifications', NotificationController.middleware.bind(this), NotificationController.createNotification.bind(this))
|
||||
this.router.delete('/notifications/:id', NotificationController.middleware.bind(this), NotificationController.deleteNotification.bind(this))
|
||||
this.router.patch('/notifications/:id', NotificationController.middleware.bind(this), NotificationController.updateNotification.bind(this))
|
||||
|
|
|
|||
|
|
@ -1,12 +1,37 @@
|
|||
const { version } = require('../../package.json')
|
||||
|
||||
module.exports.notificationData = {
|
||||
events: [
|
||||
{
|
||||
name: 'onPodcastEpisodeDownloaded',
|
||||
requiresLibrary: true,
|
||||
libraryMediaType: 'podcast',
|
||||
description: 'Triggered when a podcast episode is auto-downloaded',
|
||||
variables: ['libraryItemId', 'libraryId', 'podcastTitle', 'episodeTitle', 'libraryName', 'episodeId'],
|
||||
defaults: {
|
||||
title: 'New {{podcastTitle}} Episode!',
|
||||
body: '{{episodeTitle}} has been added to {{libraryName}} library.'
|
||||
},
|
||||
testData: {
|
||||
libraryItemId: 'li_notification_test',
|
||||
libraryId: 'lib_test',
|
||||
libraryName: 'Podcasts',
|
||||
podcastTitle: 'Abs Test Podcast',
|
||||
episodeId: 'ep_notification_test',
|
||||
episodeTitle: 'Successful Test'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'onTest',
|
||||
requiresLibrary: false,
|
||||
description: 'Notification for testing',
|
||||
variables: ['version'],
|
||||
defaults: {
|
||||
title: 'Test Title',
|
||||
body: 'Test Body'
|
||||
title: 'Test Notification on Abs {{version}}',
|
||||
body: 'Test notificataion body for abs {{version}}.'
|
||||
},
|
||||
testData: {
|
||||
version: 'v' + version
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue