class NotificationFacade { static create = container => new this(container.Logger, container.DateTimeProvider, container.TransportProvider, container.SubjectParser, container.NotificationRepository, container.Config); constructor(logger, dateTimeProvider, transportProvider, subjectParser, notificationRepository, config) { this.logger = logger; this.dateTimeProvider = dateTimeProvider; this.transportProvider = transportProvider; this.subjectParser = subjectParser; this.notificationRepository = notificationRepository; this.contacts = config.contacts.filter(contact => { if (transportProvider.byType(contact.type) === null) { logger.error(`Unknown transport type '${contact.type}' for contact '${contact.description}', skipping contact`) return false; } return true; }); this.publicUrl = config.publicUrl; this.reminders = config.reminders; } async postNotification(subject, message, priority) { var parsedSubject = this.subjectParser.parse(subject); const token = await this.notificationRepository.storeNotification(parsedSubject.id, parsedSubject.title); if (token === null) return; this._sendNotification({ id: parsedSubject.id, token: token, title: parsedSubject.title, message: message, priority: Number(priority) || 0, sound: parsedSubject.sound, timestamp: this.dateTimeProvider.unixTimestamp(), url: new URL('/#/n/' + token, this.publicUrl).href }); } async resetNotification(token) { await this.notificationRepository.resetNotification(token); } getLatchedNotifications(token) { const notifications = this.notificationRepository.getLatchedNotifications(token); return notifications !== null ? { reminders: { enabled: this.reminders.enabled, interval: this.reminders.interval }, notifications: notifications } : null; } async setReminders(token, enabled) { await this.notificationRepository.setReminders(token, enabled); } async sendReminders(interval, title, message, sound) { await this.notificationRepository.processReminderNotifications(interval, this._sendReminder.bind(this, title, message, sound)); } _sendNotification(notification) { this.contacts.forEach(contact => { const transport = this.transportProvider.byType(contact.type); this._retryableSend(transport, contact, notification); }); } _sendReminder(title, message, sound, token, notification) { const reminderNotification = { id: notification.id, token: token, title: title, message: message.replace('{title}', notification.title), priority: 0, timestamp: this.dateTimeProvider.unixTimestamp(), url: new URL('/#/n/' + token, this.publicUrl).href }; if (sound) reminderNotification.sound = sound; this._sendNotification(reminderNotification); } _delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async _retryableSend(transport, contact, notification) { let attempt = 1; const retryInterval = contact.retryIntervalSeconds || 0; const maxAttempts = contact.maxAttempts || 1; while (true) { try { this.logger.info(`Sending notification '${notification.id}' with token '${notification.token}' to '${contact.description}' (attempt ${attempt})`); await transport.send(this.logger, contact, notification); this.logger.info(`Notification '${notification.id}' succesfully sent to '${contact.description}'`); return; } catch (err) { if (attempt >= maxAttempts) { this.logger.info(`Error while sending notification '${notification.id}' to '${contact.description}', max attempts reached: ${err}`); return; } this.logger.info(`Error while sending notification '${notification.id}' to '${contact.description}', retrying in ${retryInterval} seconds: ${err}`); await this._delay(retryInterval * 1000); attempt++; } } } } module.exports = NotificationFacade;