diff --git a/server/src/locales/en.json b/server/src/locales/en.json index 74296d0a5..22ab0d157 100755 --- a/server/src/locales/en.json +++ b/server/src/locales/en.json @@ -162,5 +162,18 @@ "monitorDownAlert": "Downtime Alert: One of your monitors went offline.\nšŸ“Œ Monitor: {monitorName}\nšŸ“… Time: {time}\nāš ļø Status: DOWN\nšŸ“Ÿ Status Code: {code}\n\u200B\n", "sendTestEmail": "Test email sent successfully", "errorForValidEmailAddress": "A valid recipient email address is required.", - "testEmailSubject": "Test E-mail from Checkmate" + "testEmailSubject": "Test E-mail from Checkmate", + "discordNotification": { + "monitor": "Monitor", + "status": "Status", + "statusCode": "Status Code", + "time": "Time", + "up": "UP", + "down": "DOWN", + "checkmate": "Checkmate", + "url": "URL", + "unknown": "Unknown", + "uptimeAlert": "Uptime Alert: One of your monitors is back online", + "downtimeAlert": "Downtime Alert: One of your monitors went offline" + } } diff --git a/server/src/service/infrastructure/notificationService.js b/server/src/service/infrastructure/notificationService.js index 8c34519f3..ff92360e2 100644 --- a/server/src/service/infrastructure/notificationService.js +++ b/server/src/service/infrastructure/notificationService.js @@ -16,7 +16,7 @@ class NotificationService { return NotificationService.SERVICE_NAME; } - sendNotification = async ({ notification, subject, content, html }) => { + sendNotification = async ({ notification, subject, content, html, discordContent }) => { const { type, address } = notification; if (type === "email") { @@ -28,7 +28,7 @@ class NotificationService { // Create a body for webhooks let body = { text: content }; if (type === "discord") { - body = { content }; + body = !discordContent ? { content } : discordContent; } if (type === "slack" || type === "discord" || type === "webhook") { @@ -55,7 +55,6 @@ class NotificationService { const notificationIDs = networkResponse.monitor?.notifications ?? []; if (notificationIDs.length === 0) return false; - if (networkResponse.monitor.type === "hardware") { const thresholds = networkResponse?.monitor?.thresholds; @@ -63,30 +62,29 @@ class NotificationService { const metrics = networkResponse?.payload?.data ?? null; if (metrics === null) return false; // No metrics, we're done - const alerts = await this.notificationUtils.buildHardwareAlerts(networkResponse); + const [alerts, discordContent] = await this.notificationUtils.buildHardwareAlerts(networkResponse); if (alerts.length === 0) return false; const { subject, html } = await this.notificationUtils.buildHardwareEmail(networkResponse, alerts); const content = await this.notificationUtils.buildHardwareNotificationMessage(alerts); - const success = await this.notifyAll({ notificationIDs, subject, html, content }); + const success = await this.notifyAll({ notificationIDs, subject, html, content, discordContent }); return success; } // Status monitors const { subject, html } = await this.notificationUtils.buildStatusEmail(networkResponse); - const content = await this.notificationUtils.buildWebhookMessage(networkResponse); - const success = this.notifyAll({ notificationIDs, subject, html, content }); + const [content, discordContent] = await this.notificationUtils.buildWebhookMessage(networkResponse); + const success = this.notifyAll({ notificationIDs, subject, html, content, discordContent }); return success; } - async notifyAll({ notificationIDs, subject, html, content }) { + async notifyAll({ notificationIDs, subject, html, content, discordContent }) { const notifications = await this.db.notificationModule.getNotificationsByIds(notificationIDs); - // Map each notification to a test promise const promises = notifications.map(async (notification) => { try { - await this.sendNotification({ notification, subject, content, html }); + await this.sendNotification({ notification, subject, content, html, discordContent }); return true; } catch (err) { return false; diff --git a/server/src/service/infrastructure/notificationUtils.js b/server/src/service/infrastructure/notificationUtils.js index 22c7f4df7..62404ee29 100644 --- a/server/src/service/infrastructure/notificationUtils.js +++ b/server/src/service/infrastructure/notificationUtils.js @@ -70,7 +70,25 @@ class NotificationUtils { .replace("{time}", formattedTime) .replace("{code}", code || "Unknown"); } - return messageText; + const dn = this.stringService.discordNotification; + let discordMessageText = { + embeds: [ + { + title: status ? "Uptime Alert: One of your monitors is back online" : "Downtime Alert: One of your monitors went offline", + color: status ? 5763719 : 15548997, + + fields: [ + { name: dn.monitor, value: monitor?.name ?? dn.unknown, inline: true }, + { name: dn.status, value: status ? dn.up : dn.down, inline: true }, + { name: dn.statusCode, value: String(code ?? dn.unknown), inline: true }, + { name: dn.time, value: formattedTime, inline: true }, + { name: dn.url, value: monitor?.url ?? dn.unknown, inline: false }, + ], + footer: { text: dn.checkmate }, + }, + ], + }; + return [messageText, discordMessageText]; }; buildHardwareAlerts = async (networkResponse) => { @@ -88,6 +106,36 @@ class NotificationUtils { }; const alertsToSend = []; + const discordEmbeds = []; + const formatDiscordAlert = { + cpu: () => ({ + title: "CPU alert", + description: `Your current CPU usage (${(cpuUsage * 100).toFixed(0)}%) is above your threshold (${(cpuThreshold * 100).toFixed(0)}%)`, + color: 15548997, + + footer: { text: "Checkmate" }, + }), + + memory: () => ({ + title: "Memory alert", + description: `Your current memory usage (${(memoryUsage * 100).toFixed(0)}%) is above your threshold (${(memoryThreshold * 100).toFixed(0)}%)`, + color: 15548997, + + footer: { text: "Checkmate" }, + }), + + disk: () => ({ + title: "Disk alert", + description: `Your current disk usage is above your threshold (${(diskThreshold * 100).toFixed(0)}%)`, + color: 15548997, + footer: { text: "Checkmate" }, + fields: disk.map((d, idx) => ({ + name: `Disk ${idx}`, + value: `${(d.usage_percent * 100).toFixed(0)}%`, + inline: true, + })), + }), + }; const alertTypes = ["cpu", "memory", "disk"]; for (const type of alertTypes) { // Iterate over each alert type to see if any need to be decremented @@ -108,11 +156,13 @@ class NotificationUtils { .join(", ")} is above your threshold (${(diskThreshold * 100).toFixed(0)}%)`, }; alertsToSend.push(formatAlert[type]()); + discordEmbeds.push(formatDiscordAlert[type]()); } } } await monitor.save(); - return alertsToSend; + const discordPayload = discordEmbeds.length ? { embeds: discordEmbeds } : null; + return [alertsToSend, discordPayload]; }; buildHardwareEmail = async (networkResponse, alerts) => { diff --git a/server/src/service/system/stringService.js b/server/src/service/system/stringService.js index 5df3410f9..231550be7 100755 --- a/server/src/service/system/stringService.js +++ b/server/src/service/system/stringService.js @@ -216,6 +216,10 @@ class StringService { return this.translationService.getTranslation("monitorDownAlert"); } + get discordNotification() { + return this.translationService.getTranslation("discordNotification"); + } + getWebhookUnsupportedPlatform(platform) { return this.translationService.getTranslation("webhookUnsupportedPlatform").replace("{platform}", platform); }