diff --git a/server/src/service/infrastructure/notificationProviders/discord.ts b/server/src/service/infrastructure/notificationProviders/discord.ts index 560c18aae..dcf8927be 100644 --- a/server/src/service/infrastructure/notificationProviders/discord.ts +++ b/server/src/service/infrastructure/notificationProviders/discord.ts @@ -3,6 +3,7 @@ import type { Monitor, Notification, MonitorStatusResponse } from "@/types/index import { INotificationProvider } from "@/service/index.js"; import type { MonitorActionDecision } from "@/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js"; import { buildHardwareAlerts, buildDiscordBody, getTestMessage } from "@/service/infrastructure/notificationProviders/utils.js"; +import type { NotificationMessage } from "@/types/notificationMessage.js"; import got from "got"; export class DiscordProvider implements INotificationProvider { @@ -86,4 +87,115 @@ export class DiscordProvider implements INotificationProvider { return false; } }; + + /** + * New unified message format implementation + */ + async sendMessage(notification: Notification, message: NotificationMessage): Promise { + if (!notification.address) { + this.logger.warn({ + message: "Discord notification missing webhook URL", + service: SERVICE_NAME, + method: "sendMessage", + }); + return false; + } + + const embed = this.buildDiscordEmbed(message); + + try { + await got.post(notification.address, { + json: { embeds: [embed] }, + headers: { + "Content-Type": "application/json", + }, + }); + this.logger.info({ + message: "[NEW] Discord notification sent via sendMessage", + service: SERVICE_NAME, + method: "sendMessage", + }); + return true; + } catch (error) { + const err = error as Error; + this.logger.warn({ + message: "Discord notification failed", + service: SERVICE_NAME, + method: "sendMessage", + stack: err?.stack, + }); + return false; + } + } + + private buildDiscordEmbed(message: NotificationMessage): any { + // Map severity to Discord embed colors + const colorMap: Record = { + critical: 0xdc2626, // red-600 + warning: 0xf59e0b, // amber-500 + info: 0x3b82f6, // blue-500 + success: 0x10b981, // green-500 + }; + + const color = colorMap[message.severity] || colorMap.info; + + const fields: Array<{ name: string; value: string; inline?: boolean }> = []; + + // Add monitor details + fields.push({ + name: "Monitor", + value: message.monitor.name, + inline: true, + }); + + fields.push({ + name: "Type", + value: message.monitor.type.toUpperCase(), + inline: true, + }); + + fields.push({ + name: "Status", + value: message.monitor.status.charAt(0).toUpperCase() + message.monitor.status.slice(1), + inline: true, + }); + + // Add monitor URL + fields.push({ + name: "URL", + value: message.monitor.url, + inline: false, + }); + + // Add threshold breaches if present + if (message.content.thresholds && message.content.thresholds.length > 0) { + const thresholdLines = message.content.thresholds + .map((t) => `• **${t.metric.toUpperCase()}**: ${t.formattedValue} (threshold: ${t.threshold}${t.unit})`) + .join("\n"); + + fields.push({ + name: "Threshold Breaches", + value: thresholdLines, + inline: false, + }); + } + + // Add details if present + if (message.content.details && message.content.details.length > 0) { + const detailsText = message.content.details.join("\n"); + fields.push({ + name: "Details", + value: detailsText, + inline: false, + }); + } + + return { + title: message.content.title, + description: message.content.summary, + color, + fields, + timestamp: message.content.timestamp.toISOString(), + }; + } } diff --git a/server/src/service/infrastructure/notificationsService.ts b/server/src/service/infrastructure/notificationsService.ts index eec41a0a5..a26a39d33 100644 --- a/server/src/service/infrastructure/notificationsService.ts +++ b/server/src/service/infrastructure/notificationsService.ts @@ -108,6 +108,15 @@ export class NotificationsService implements INotificationsService { return await this.pagerDutyProvider.sendMessage(notification, notificationMessage); } + if (notification.type === "discord" && this.discordProvider.sendMessage && notificationMessage) { + this.logger.info({ + message: "[NEW] Using sendMessage for discord", + service: SERVICE_NAME, + method: "send", + }); + return await this.discordProvider.sendMessage(notification, notificationMessage); + } + // Fallback to existing sendAlert for all providers switch (notification.type) { case "email":