From 021af1233a46f24707332c1b93c2580023805408 Mon Sep 17 00:00:00 2001 From: 0xsid0703 Date: Thu, 15 Jan 2026 19:39:39 +0100 Subject: [PATCH] feat: Add enhanced Discord webhook alerts with timestamps and downtime duration - Add 'Went Offline' timestamp using Discord's timestamp format () - Add 'Back Online' timestamp using Discord's timestamp format () - Add 'Downtime Duration' using Discord's relative timestamp format () - Query database for last DOWN heartbeat to calculate downtime duration Resolves #5535 --- server/notification-providers/discord.js | 59 ++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js index 79768e3df..1d236d9cf 100644 --- a/server/notification-providers/discord.js +++ b/server/notification-providers/discord.js @@ -1,6 +1,7 @@ const NotificationProvider = require("./notification-provider"); const axios = require("axios"); const { DOWN, UP } = require("../../src/util"); +const { R } = require("redbean-node"); class Discord extends NotificationProvider { name = "discord"; @@ -48,6 +49,11 @@ class Discord extends NotificationProvider { // If heartbeatJSON is not null, we go into the normal alerting loop. let addess = this.extractAddress(monitorJSON); if (heartbeatJSON["status"] === DOWN) { + // Format timestamp for Discord using Discord's timestamp format + // where format is F for full date/time + const wentOfflineTimestamp = Math.floor(new Date(heartbeatJSON["time"]).getTime() / 1000); + const wentOfflineFormatted = ``; + let discorddowndata = { username: discordDisplayName, embeds: [ @@ -68,6 +74,10 @@ class Discord extends NotificationProvider { }, ] : []), + { + name: "Went Offline", + value: wentOfflineFormatted, + }, { name: `Time (${heartbeatJSON["timezone"]})`, value: heartbeatJSON["localDateTime"], @@ -93,6 +103,35 @@ class Discord extends NotificationProvider { await axios.post(webhookUrl.toString(), discorddowndata, config); return okMsg; } else if (heartbeatJSON["status"] === UP) { + // Format timestamp for Discord using Discord's timestamp format + const backOnlineTimestamp = Math.floor(new Date(heartbeatJSON["time"]).getTime() / 1000); + const backOnlineFormatted = ``; + + // Calculate downtime duration by finding the last DOWN heartbeat + let downtimeDuration = null; + let wentOfflineFormatted = null; + try { + if (monitorJSON && monitorJSON.id) { + const lastDownHeartbeat = await R.getRow( + "SELECT time FROM heartbeat WHERE monitor_id = ? AND status = ? ORDER BY time DESC LIMIT 1", + [monitorJSON.id, DOWN] + ); + + if (lastDownHeartbeat && lastDownHeartbeat.time) { + const wentOfflineTimestamp = Math.floor(new Date(lastDownHeartbeat.time).getTime() / 1000); + wentOfflineFormatted = ``; + + // Calculate downtime duration in seconds + const downtimeSeconds = backOnlineTimestamp - wentOfflineTimestamp; + // Format as relative time using Discord's relative timestamp (R format) + downtimeDuration = ``; + } + } + } catch (error) { + // If we can't calculate downtime, just continue without it + // Silently fail to avoid disrupting notification sending + } + let discordupdata = { username: discordDisplayName, embeds: [ @@ -113,6 +152,26 @@ class Discord extends NotificationProvider { }, ] : []), + { + name: "Back Online", + value: backOnlineFormatted, + }, + ...(wentOfflineFormatted + ? [ + { + name: "Went Offline", + value: wentOfflineFormatted, + }, + ] + : []), + ...(downtimeDuration + ? [ + { + name: "Downtime Duration", + value: downtimeDuration, + }, + ] + : []), { name: `Time (${heartbeatJSON["timezone"]})`, value: heartbeatJSON["localDateTime"],