From 0d328e7c8ee599ca4e2036cb210d6869ca648550 Mon Sep 17 00:00:00 2001
From: Dj Isaac <contact@dejaydev.com>
Date: Wed, 9 Oct 2024 18:24:32 -0500
Subject: [PATCH 1/5] fix: misspelling of 'address' (#5180)

---
 server/notification-providers/discord.js               | 4 ++--
 server/notification-providers/notification-provider.js | 2 +-
 server/notification-providers/sevenio.js               | 2 +-
 server/notification-providers/signl4.js                | 2 +-
 server/notification-providers/slack.js                 | 2 +-
 server/notification-providers/smtp.js                  | 2 +-
 server/notification-providers/squadcast.js             | 2 +-
 server/notification-providers/teams.js                 | 2 +-
 server/notification-providers/zoho-cliq.js             | 2 +-
 9 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js
index ccb80bf40..6a52f8f3e 100644
--- a/server/notification-providers/discord.js
+++ b/server/notification-providers/discord.js
@@ -48,7 +48,7 @@ class Discord extends NotificationProvider {
                             },
                             {
                                 name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
-                                value: this.extractAdress(monitorJSON),
+                                value: this.extractAddress(monitorJSON),
                             },
                             {
                                 name: `Time (${heartbeatJSON["timezone"]})`,
@@ -85,7 +85,7 @@ class Discord extends NotificationProvider {
                             },
                             {
                                 name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
-                                value: this.extractAdress(monitorJSON),
+                                value: this.extractAddress(monitorJSON),
                             },
                             {
                                 name: `Time (${heartbeatJSON["timezone"]})`,
diff --git a/server/notification-providers/notification-provider.js b/server/notification-providers/notification-provider.js
index d26b791f6..b9fb3d863 100644
--- a/server/notification-providers/notification-provider.js
+++ b/server/notification-providers/notification-provider.js
@@ -24,7 +24,7 @@ class NotificationProvider {
      * @param {?object} monitorJSON Monitor details (For Up/Down only)
      * @returns {string} The extracted address based on the monitor type.
      */
-    extractAdress(monitorJSON) {
+    extractAddress(monitorJSON) {
         if (!monitorJSON) {
             return "";
         }
diff --git a/server/notification-providers/sevenio.js b/server/notification-providers/sevenio.js
index 9d805d7e6..eac38a26e 100644
--- a/server/notification-providers/sevenio.js
+++ b/server/notification-providers/sevenio.js
@@ -32,7 +32,7 @@ class SevenIO extends NotificationProvider {
                 return okMsg;
             }
 
-            let address = this.extractAdress(monitorJSON);
+            let address = this.extractAddress(monitorJSON);
             if (address !== "") {
                 address = `(${address}) `;
             }
diff --git a/server/notification-providers/signl4.js b/server/notification-providers/signl4.js
index e48983f59..8261a73f3 100644
--- a/server/notification-providers/signl4.js
+++ b/server/notification-providers/signl4.js
@@ -18,7 +18,7 @@ class SIGNL4 extends NotificationProvider {
                 msg,
                 // Source system
                 "X-S4-SourceSystem": "UptimeKuma",
-                monitorUrl: this.extractAdress(monitorJSON),
+                monitorUrl: this.extractAddress(monitorJSON),
             };
 
             const config = {
diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js
index 3676d6df3..f28a643e0 100644
--- a/server/notification-providers/slack.js
+++ b/server/notification-providers/slack.js
@@ -48,7 +48,7 @@ class Slack extends NotificationProvider {
 
         }
 
-        const address = this.extractAdress(monitorJSON);
+        const address = this.extractAddress(monitorJSON);
         if (address) {
             actions.push({
                 "type": "button",
diff --git a/server/notification-providers/smtp.js b/server/notification-providers/smtp.js
index 801e0e69d..9f3defa5e 100644
--- a/server/notification-providers/smtp.js
+++ b/server/notification-providers/smtp.js
@@ -93,7 +93,7 @@ class SMTP extends NotificationProvider {
 
         if (monitorJSON !== null) {
             monitorName = monitorJSON["name"];
-            monitorHostnameOrURL = this.extractAdress(monitorJSON);
+            monitorHostnameOrURL = this.extractAddress(monitorJSON);
         }
 
         let serviceStatus = "⚠️ Test";
diff --git a/server/notification-providers/squadcast.js b/server/notification-providers/squadcast.js
index 9074e9b76..5713783c0 100644
--- a/server/notification-providers/squadcast.js
+++ b/server/notification-providers/squadcast.js
@@ -34,7 +34,7 @@ class Squadcast extends NotificationProvider {
                     data.status = "resolve";
                 }
 
-                data.tags["AlertAddress"] = this.extractAdress(monitorJSON);
+                data.tags["AlertAddress"] = this.extractAddress(monitorJSON);
 
                 monitorJSON["tags"].forEach(tag => {
                     data.tags[tag["name"]] = {
diff --git a/server/notification-providers/teams.js b/server/notification-providers/teams.js
index 2fcea7077..2793604cc 100644
--- a/server/notification-providers/teams.js
+++ b/server/notification-providers/teams.js
@@ -225,7 +225,7 @@ class Teams extends NotificationProvider {
             const payload = this._notificationPayloadFactory({
                 heartbeatJSON: heartbeatJSON,
                 monitorName: monitorJSON.name,
-                monitorUrl: this.extractAdress(monitorJSON),
+                monitorUrl: this.extractAddress(monitorJSON),
                 dashboardUrl: dashboardUrl,
             });
 
diff --git a/server/notification-providers/zoho-cliq.js b/server/notification-providers/zoho-cliq.js
index 44681b7df..3a504de8c 100644
--- a/server/notification-providers/zoho-cliq.js
+++ b/server/notification-providers/zoho-cliq.js
@@ -85,7 +85,7 @@ class ZohoCliq extends NotificationProvider {
             const payload = this._notificationPayloadFactory({
                 monitorMessage: heartbeatJSON.msg,
                 monitorName: monitorJSON.name,
-                monitorUrl: this.extractAdress(monitorJSON),
+                monitorUrl: this.extractAddress(monitorJSON),
                 status: heartbeatJSON.status
             });
 

From c28710cdc6f0ff7bcfb155bcef6a6f3370737edc Mon Sep 17 00:00:00 2001
From: Dj Isaac <contact@dejaydev.com>
Date: Wed, 9 Oct 2024 17:03:48 -0500
Subject: [PATCH 2/5] fix: remove service name/type from discord notifier

---
 server/notification-providers/discord.js | 53 ++++++++----------------
 1 file changed, 17 insertions(+), 36 deletions(-)

diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js
index 6a52f8f3e..32b1d73d5 100644
--- a/server/notification-providers/discord.js
+++ b/server/notification-providers/discord.js
@@ -33,6 +33,21 @@ class Discord extends NotificationProvider {
                 return okMsg;
             }
 
+            const embedFields = [
+                {
+                    name: "Service Name",
+                    value: monitorJSON["name"],
+                },
+                {
+                    name: `Time (${heartbeatJSON["timezone"]})`,
+                    value: heartbeatJSON["localDateTime"],
+                },
+                {
+                    name: "Error",
+                    value: heartbeatJSON["msg"] == null ? "N/A" : heartbeatJSON["msg"],
+                },
+            ];
+
             // If heartbeatJSON is not null, we go into the normal alerting loop.
             if (heartbeatJSON["status"] === DOWN) {
                 let discorddowndata = {
@@ -41,24 +56,7 @@ class Discord extends NotificationProvider {
                         title: "❌ Your service " + monitorJSON["name"] + " went down. ❌",
                         color: 16711680,
                         timestamp: heartbeatJSON["time"],
-                        fields: [
-                            {
-                                name: "Service Name",
-                                value: monitorJSON["name"],
-                            },
-                            {
-                                name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
-                                value: this.extractAddress(monitorJSON),
-                            },
-                            {
-                                name: `Time (${heartbeatJSON["timezone"]})`,
-                                value: heartbeatJSON["localDateTime"],
-                            },
-                            {
-                                name: "Error",
-                                value: heartbeatJSON["msg"] == null ? "N/A" : heartbeatJSON["msg"],
-                            },
-                        ],
+                        fields: embedFields,
                     }],
                 };
                 if (notification.discordChannelType === "createNewForumPost") {
@@ -78,24 +76,7 @@ class Discord extends NotificationProvider {
                         title: "✅ Your service " + monitorJSON["name"] + " is up! ✅",
                         color: 65280,
                         timestamp: heartbeatJSON["time"],
-                        fields: [
-                            {
-                                name: "Service Name",
-                                value: monitorJSON["name"],
-                            },
-                            {
-                                name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
-                                value: this.extractAddress(monitorJSON),
-                            },
-                            {
-                                name: `Time (${heartbeatJSON["timezone"]})`,
-                                value: heartbeatJSON["localDateTime"],
-                            },
-                            {
-                                name: "Ping",
-                                value: heartbeatJSON["ping"] == null ? "N/A" : heartbeatJSON["ping"] + " ms",
-                            },
-                        ],
+                        fields: embedFields,
                     }],
                 };
 

From af230ba5fb9117e8d7b66652ce897a1819d46805 Mon Sep 17 00:00:00 2001
From: Dj Isaac <contact@dejaydev.com>
Date: Wed, 9 Oct 2024 19:50:57 -0500
Subject: [PATCH 3/5] Use `msg` rather than parse it out of the heartbeat

Co-authored-by: Frank Elsinga <frank@elsinga.de>
---
 server/notification-providers/discord.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js
index 32b1d73d5..6bacbd0fe 100644
--- a/server/notification-providers/discord.js
+++ b/server/notification-providers/discord.js
@@ -44,7 +44,7 @@ class Discord extends NotificationProvider {
                 },
                 {
                     name: "Error",
-                    value: heartbeatJSON["msg"] == null ? "N/A" : heartbeatJSON["msg"],
+                    value: msg,
                 },
             ];
 

From 8b82520d3d5df23938dfb7ee9696a32b26506b51 Mon Sep 17 00:00:00 2001
From: Dj Isaac <contact@dejaydev.com>
Date: Wed, 9 Oct 2024 20:35:26 -0500
Subject: [PATCH 4/5] Conditionally add service url

---
 server/notification-providers/discord.js | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js
index 6bacbd0fe..bd54cc7c6 100644
--- a/server/notification-providers/discord.js
+++ b/server/notification-providers/discord.js
@@ -33,11 +33,17 @@ class Discord extends NotificationProvider {
                 return okMsg;
             }
 
+            const address = this.extractAddress(monitorJSON);
+
             const embedFields = [
                 {
                     name: "Service Name",
                     value: monitorJSON["name"],
                 },
+                ...((address !== "" && address !== monitorJSON["hostname"]) ? [{
+                    name: "Service URL",
+                    value: address
+                }] : []),
                 {
                     name: `Time (${heartbeatJSON["timezone"]})`,
                     value: heartbeatJSON["localDateTime"],

From cd69146f334747e3e7f4d3f54930fb2645213176 Mon Sep 17 00:00:00 2001
From: Dj Isaac <contact@dejaydev.com>
Date: Sun, 20 Oct 2024 19:28:40 -0500
Subject: [PATCH 5/5] add discord buttons to payload

---
 server/notification-providers/discord.js | 70 ++++++++++++++++--------
 src/components/notifications/Discord.vue |  6 ++
 src/lang/en.json                         |  2 +
 3 files changed, 55 insertions(+), 23 deletions(-)

diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js
index bd54cc7c6..e74c0c5b7 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 { Settings } = require("../settings");
+const { DOWN, UP, getMonitorRelativeURL } = require("../../src/util");
 
 class Discord extends NotificationProvider {
     name = "discord";
@@ -19,8 +20,13 @@ class Discord extends NotificationProvider {
             }
 
             // If heartbeatJSON is null, assume we're testing.
+
+            const baseURL = await Settings.get("primaryBaseURL");
+            const address = this.extractAddress(monitorJSON);
+            const hasAddress = address !== "" && address !== monitorJSON.hostname;
+
             if (heartbeatJSON == null) {
-                let discordtestdata = {
+                const discordtestdata = {
                     username: discordDisplayName,
                     content: msg,
                 };
@@ -33,20 +39,18 @@ class Discord extends NotificationProvider {
                 return okMsg;
             }
 
-            const address = this.extractAddress(monitorJSON);
-
             const embedFields = [
                 {
                     name: "Service Name",
-                    value: monitorJSON["name"],
+                    value: monitorJSON.name,
                 },
-                ...((address !== "" && address !== monitorJSON["hostname"]) ? [{
+                ...(hasAddress ? [{
                     name: "Service URL",
                     value: address
                 }] : []),
                 {
-                    name: `Time (${heartbeatJSON["timezone"]})`,
-                    value: heartbeatJSON["localDateTime"],
+                    name: `Time (${heartbeatJSON.timezone})`,
+                    value: heartbeatJSON.localDateTime,
                 },
                 {
                     name: "Error",
@@ -54,50 +58,70 @@ class Discord extends NotificationProvider {
                 },
             ];
 
+            const components = [
+                {
+                    type: 1, // Action Row
+                    components: [
+                        baseURL && {
+                            type: 2, // Button
+                            style: 5, // Link Button,
+                            label: "Visit Uptime Kuma",
+                            url: baseURL + getMonitorRelativeURL(monitorJSON.id)
+                        },
+                        hasAddress && {
+                            type: 2, // Button
+                            style: 5, // Link Button,
+                            label: "Visit Service URL",
+                            url: address
+                        }
+                    ].filter(Boolean) // remove invalid data
+                }
+            ];
+
             // If heartbeatJSON is not null, we go into the normal alerting loop.
-            if (heartbeatJSON["status"] === DOWN) {
-                let discorddowndata = {
+            if (heartbeatJSON.status === DOWN) {
+                const discorddowndata = {
                     username: discordDisplayName,
+                    content: notification.discordPrefixMessage || "",
                     embeds: [{
-                        title: "❌ Your service " + monitorJSON["name"] + " went down. ❌",
+                        title: `❌ Your service ${monitorJSON.name} went down. ❌`,
                         color: 16711680,
-                        timestamp: heartbeatJSON["time"],
+                        timestamp: heartbeatJSON.time,
                         fields: embedFields,
                     }],
+                    components: components,
                 };
+
                 if (notification.discordChannelType === "createNewForumPost") {
                     discorddowndata.thread_name = notification.postName;
                 }
-                if (notification.discordPrefixMessage) {
-                    discorddowndata.content = notification.discordPrefixMessage;
-                }
 
                 await axios.post(webhookUrl.toString(), discorddowndata);
                 return okMsg;
+            }
 
-            } else if (heartbeatJSON["status"] === UP) {
-                let discordupdata = {
+            if (heartbeatJSON.status === UP) {
+                const discordupdata = {
                     username: discordDisplayName,
+                    content: notification.discordPrefixMessage || "",
                     embeds: [{
-                        title: "✅ Your service " + monitorJSON["name"] + " is up! ✅",
+                        title: `✅ Your service ${monitorJSON.name} is up! ✅`,
                         color: 65280,
-                        timestamp: heartbeatJSON["time"],
+                        timestamp: heartbeatJSON.time,
                         fields: embedFields,
                     }],
+                    components: components,
                 };
 
                 if (notification.discordChannelType === "createNewForumPost") {
                     discordupdata.thread_name = notification.postName;
                 }
 
-                if (notification.discordPrefixMessage) {
-                    discordupdata.content = notification.discordPrefixMessage;
-                }
-
                 await axios.post(webhookUrl.toString(), discordupdata);
                 return okMsg;
             }
         } catch (error) {
+            console.log(error);
             this.throwGeneralAxiosError(error);
         }
     }
diff --git a/src/components/notifications/Discord.vue b/src/components/notifications/Discord.vue
index 5d8334f5f..966da93e3 100644
--- a/src/components/notifications/Discord.vue
+++ b/src/components/notifications/Discord.vue
@@ -4,6 +4,12 @@
         <input id="discord-webhook-url" v-model="$parent.notification.discordWebhookUrl" type="text" class="form-control" required autocomplete="false">
         <div class="form-text">
             {{ $t("wayToGetDiscordURL") }}
+            <i18n-t keypath="wayToGetDiscordURLEnhanced">
+                <a
+                    href="https://discord.com/developers/docs/resources/webhook#create-webhook"
+                    target="_blank"
+                >{{ $t("withADiscordBot") }}</a>
+            </i18n-t>
         </div>
     </div>
 
diff --git a/src/lang/en.json b/src/lang/en.json
index 968a3d9f8..60d832444 100644
--- a/src/lang/en.json
+++ b/src/lang/en.json
@@ -541,6 +541,8 @@
     "smtpBCC": "BCC",
     "Discord Webhook URL": "Discord Webhook URL",
     "wayToGetDiscordURL": "You can get this by going to Server Settings -> Integrations -> View Webhooks -> New Webhook",
+    "wayToGetDiscordURLEnhanced": "Using the Discord API, you may also create the webhook {0} for button support.",
+    "withADiscordBot": "with a bot",
     "Bot Display Name": "Bot Display Name",
     "Prefix Custom Message": "Prefix Custom Message",
     "Hello @everyone is...": "Hello {'@'}everyone is…",