Merge pull request #2948 from bluewave-labs/feat-discord-format

Add Discord embed message formatting for notifications
This commit is contained in:
Alexander Holliday
2025-09-12 09:03:19 -07:00
committed by GitHub
4 changed files with 78 additions and 13 deletions

View File

@@ -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"
}
}

View File

@@ -16,7 +16,7 @@ class NotificationService {
return NotificationService.SERVICE_NAME;
}
sendNotification = async ({ notification, subject, content, html }) => {
sendNotification = async ({ notification, subject, content, html, discordContent = null }) => {
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 = null }) {
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;

View File

@@ -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 ? dn.uptimeAlert : dn.downtimeAlert,
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: (Array.isArray(disk) ? 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) => {

View File

@@ -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);
}