2022-09-21 18:01:10 -05:00
const axios = require ( 'axios' )
2024-08-18 14:32:05 -05:00
const Logger = require ( '../Logger' )
2022-11-24 15:53:58 -06:00
const SocketAuthority = require ( '../SocketAuthority' )
2023-07-04 18:14:44 -05:00
const Database = require ( '../Database' )
2022-09-22 18:12:48 -05:00
const { notificationData } = require ( '../utils/notifications' )
2022-09-20 18:08:41 -05:00
class NotificationManager {
2023-07-04 18:14:44 -05:00
constructor ( ) {
2022-09-25 10:19:44 -05:00
this . sendingNotification = false
this . notificationQueue = [ ]
2022-09-21 18:01:10 -05:00
}
2022-09-22 18:12:48 -05:00
getData ( ) {
return notificationData
}
2025-01-04 12:41:09 -06:00
/ * *
*
* @ param { import ( '../models/LibraryItem' ) } libraryItem
* @ param { import ( '../models/PodcastEpisode' ) } episode
* /
2023-07-22 14:25:20 -05:00
async onPodcastEpisodeDownloaded ( libraryItem , episode ) {
2023-07-04 18:14:44 -05:00
if ( ! Database . notificationSettings . isUseable ) return
2022-09-21 18:01:10 -05:00
2024-08-18 14:32:05 -05:00
if ( ! Database . notificationSettings . getHasActiveNotificationsForEvent ( 'onPodcastEpisodeDownloaded' ) ) {
Logger . debug ( ` [NotificationManager] onPodcastEpisodeDownloaded: No active notifications ` )
return
}
2025-01-04 12:41:09 -06:00
Logger . debug ( ` [NotificationManager] onPodcastEpisodeDownloaded: Episode " ${ episode . title } " for podcast ${ libraryItem . media . title } ` )
2024-08-24 16:09:54 -05:00
const library = await Database . libraryModel . findByPk ( libraryItem . libraryId )
2022-09-24 16:15:16 -05:00
const eventData = {
libraryItemId : libraryItem . id ,
libraryId : libraryItem . libraryId ,
2023-07-22 14:25:20 -05:00
libraryName : library ? . name || 'Unknown' ,
2023-02-15 15:57:04 -06:00
mediaTags : ( libraryItem . media . tags || [ ] ) . join ( ', ' ) ,
2025-01-04 12:41:09 -06:00
podcastTitle : libraryItem . media . title ,
podcastAuthor : libraryItem . media . author || '' ,
podcastDescription : libraryItem . media . description || '' ,
podcastGenres : ( libraryItem . media . genres || [ ] ) . join ( ', ' ) ,
2022-09-24 16:15:16 -05:00
episodeId : episode . id ,
2023-02-15 15:57:04 -06:00
episodeTitle : episode . title ,
episodeSubtitle : episode . subtitle || '' ,
episodeDescription : episode . description || ''
2022-09-24 16:15:16 -05:00
}
this . triggerNotification ( 'onPodcastEpisodeDownloaded' , eventData )
2022-09-21 18:01:10 -05:00
}
2024-08-18 14:32:05 -05:00
/ * *
*
* @ param { import ( '../objects/Backup' ) } backup
* @ param { number } totalBackupCount
* @ param { boolean } removedOldest - If oldest backup was removed
* /
async onBackupCompleted ( backup , totalBackupCount , removedOldest ) {
if ( ! Database . notificationSettings . isUseable ) return
if ( ! Database . notificationSettings . getHasActiveNotificationsForEvent ( 'onBackupCompleted' ) ) {
Logger . debug ( ` [NotificationManager] onBackupCompleted: No active notifications ` )
return
}
Logger . debug ( ` [NotificationManager] onBackupCompleted: Backup completed ` )
const eventData = {
completionTime : backup . createdAt ,
backupPath : backup . fullPath ,
backupSize : backup . fileSize ,
backupCount : totalBackupCount || 'Invalid' ,
removedOldest : removedOldest || 'false'
}
this . triggerNotification ( 'onBackupCompleted' , eventData )
}
2025-06-05 13:34:18 +02:00
/ * *
2025-06-09 16:21:05 -05:00
* Handles scheduled episode download RSS feed request failed
*
* @ param { string } feedUrl
* @ param { number } numFailed
* @ param { string } title
2025-06-05 13:34:18 +02:00
* /
async onRSSFeedFailed ( feedUrl , numFailed , title ) {
if ( ! Database . notificationSettings . isUseable ) return
if ( ! Database . notificationSettings . getHasActiveNotificationsForEvent ( 'onRSSFeedFailed' ) ) {
Logger . debug ( ` [NotificationManager] onRSSFeedFailed: No active notifications ` )
return
}
2025-06-09 16:21:05 -05:00
Logger . debug ( ` [NotificationManager] onRSSFeedFailed: RSS feed request failed for ${ feedUrl } ` )
2025-06-05 13:34:18 +02:00
const eventData = {
feedUrl : feedUrl ,
numFailed : numFailed || 0 ,
title : title || 'Unknown Title'
}
this . triggerNotification ( 'onRSSFeedFailed' , eventData )
}
/ * *
2025-06-09 16:21:05 -05:00
* Handles scheduled episode downloads disabled due to too many failed attempts
*
* @ param { string } feedUrl
* @ param { number } numFailed
* @ param { string } title
2025-06-05 13:34:18 +02:00
* /
async onRSSFeedDisabled ( feedUrl , numFailed , title ) {
if ( ! Database . notificationSettings . isUseable ) return
if ( ! Database . notificationSettings . getHasActiveNotificationsForEvent ( 'onRSSFeedDisabled' ) ) {
Logger . debug ( ` [NotificationManager] onRSSFeedDisabled: No active notifications ` )
return
}
2025-06-09 16:21:05 -05:00
Logger . debug ( ` [NotificationManager] onRSSFeedDisabled: Podcast scheduled episode download disabled due to ${ numFailed } failed requests for ${ feedUrl } ` )
2025-06-05 13:34:18 +02:00
const eventData = {
feedUrl : feedUrl ,
numFailed : numFailed || 0 ,
title : title || 'Unknown Title'
}
this . triggerNotification ( 'onRSSFeedDisabled' , eventData )
}
2024-08-18 14:32:05 -05:00
/ * *
*
* @ param { string } errorMsg
* /
async onBackupFailed ( errorMsg ) {
if ( ! Database . notificationSettings . isUseable ) return
if ( ! Database . notificationSettings . getHasActiveNotificationsForEvent ( 'onBackupFailed' ) ) {
Logger . debug ( ` [NotificationManager] onBackupFailed: No active notifications ` )
return
}
Logger . debug ( ` [NotificationManager] onBackupFailed: Backup failed ( ${ errorMsg } ) ` )
const eventData = {
errorMsg : errorMsg || 'Backup failed'
}
this . triggerNotification ( 'onBackupFailed' , eventData )
}
2022-09-21 18:01:10 -05:00
onTest ( ) {
this . triggerNotification ( 'onTest' )
}
2024-08-18 14:32:05 -05:00
/ * *
*
* @ param { string } eventName
* @ param { any } eventData
* @ param { boolean } [ intentionallyFail = false ] - If true , will intentionally fail the notification
* /
2022-09-24 16:15:16 -05:00
async triggerNotification ( eventName , eventData , intentionallyFail = false ) {
2023-07-04 18:14:44 -05:00
if ( ! Database . notificationSettings . isUseable ) return
2022-09-25 10:19:44 -05:00
// Will queue the notification if sendingNotification and queue is not full
if ( ! this . checkTriggerNotification ( eventName , eventData ) ) return
2022-09-21 18:01:10 -05:00
2023-07-04 18:14:44 -05:00
const notifications = Database . notificationSettings . getActiveNotificationsForEvent ( eventName )
2022-09-21 18:01:10 -05:00
for ( const notification of notifications ) {
Logger . debug ( ` [NotificationManager] triggerNotification: Sending ${ eventName } notification ${ notification . id } ` )
2022-09-24 16:15:16 -05:00
const success = intentionallyFail ? false : await this . sendNotification ( notification , eventData )
2022-09-21 18:01:10 -05:00
2022-09-24 16:15:16 -05:00
notification . updateNotificationFired ( success )
2024-08-18 14:32:05 -05:00
if ( ! success ) {
// Failed notification
2023-07-04 18:14:44 -05:00
if ( notification . numConsecutiveFailedAttempts >= Database . notificationSettings . maxFailedAttempts ) {
2022-09-21 18:01:10 -05:00
Logger . error ( ` [NotificationManager] triggerNotification: ${ notification . eventName } / ${ notification . id } reached max failed attempts ` )
2022-09-24 16:15:16 -05:00
notification . enabled = false
} else {
Logger . error ( ` [NotificationManager] triggerNotification: ${ notification . eventName } / ${ notification . id } ${ notification . numConsecutiveFailedAttempts } failed attempts ` )
2022-09-21 18:01:10 -05:00
}
}
}
2022-09-24 16:15:16 -05:00
2023-07-04 18:14:44 -05:00
await Database . updateSetting ( Database . notificationSettings )
SocketAuthority . emitter ( 'notifications_updated' , Database . notificationSettings . toJSON ( ) )
2022-09-25 10:19:44 -05:00
this . notificationFinished ( )
}
2024-08-18 14:32:05 -05:00
/ * *
*
* @ param { string } eventName
* @ param { any } eventData
* @ returns { boolean } - TRUE if notification should be triggered now
* /
2022-09-25 10:19:44 -05:00
checkTriggerNotification ( eventName , eventData ) {
if ( this . sendingNotification ) {
2023-07-04 18:14:44 -05:00
if ( this . notificationQueue . length >= Database . notificationSettings . maxNotificationQueue ) {
2022-09-25 10:19:44 -05:00
Logger . warn ( ` [NotificationManager] Notification queue is full - ignoring event ${ eventName } ` )
} else {
Logger . debug ( ` [NotificationManager] Queueing notification ${ eventName } (Queue size: ${ this . notificationQueue . length } ) ` )
this . notificationQueue . push ( { eventName , eventData } )
}
return false
}
this . sendingNotification = true
2022-09-24 16:15:16 -05:00
return true
}
2022-09-25 10:19:44 -05:00
notificationFinished ( ) {
// Delay between events then run next notification in queue
setTimeout ( ( ) => {
this . sendingNotification = false
2024-08-18 14:32:05 -05:00
if ( this . notificationQueue . length ) {
// Send next notification in queue
2022-09-25 10:19:44 -05:00
const nextNotificationEvent = this . notificationQueue . shift ( )
this . triggerNotification ( nextNotificationEvent . eventName , nextNotificationEvent . eventData )
}
2023-07-04 18:14:44 -05:00
} , Database . notificationSettings . notificationDelay )
2022-09-25 10:19:44 -05:00
}
2022-09-24 16:15:16 -05:00
sendTestNotification ( notification ) {
2024-08-18 14:32:05 -05:00
const eventData = notificationData . events . find ( ( e ) => e . name === notification . eventName )
2022-09-24 16:15:16 -05:00
if ( ! eventData ) {
Logger . error ( ` [NotificationManager] sendTestNotification: Event not found ${ notification . eventName } ` )
return false
}
return this . sendNotification ( notification , eventData . testData )
2022-09-21 18:01:10 -05:00
}
sendNotification ( notification , eventData ) {
const payload = notification . getApprisePayload ( eventData )
2024-08-18 14:32:05 -05:00
return axios
. post ( Database . notificationSettings . appriseApiUrl , payload , { timeout : 6000 } )
. then ( ( response ) => {
Logger . debug ( ` [NotificationManager] sendNotification: ${ notification . eventName } / ${ notification . id } response= ` , response . data )
return true
} )
. catch ( ( error ) => {
Logger . error ( ` [NotificationManager] sendNotification: ${ notification . eventName } / ${ notification . id } error= ` , error )
return false
} )
2022-09-20 18:08:41 -05:00
}
}
2024-09-27 17:33:23 -05:00
module . exports = new NotificationManager ( )