diff --git a/server/model/monitor.js b/server/model/monitor.js
index 30b688065..e9f7380dc 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -1295,7 +1295,8 @@ class Monitor extends BeanModel {
try {
const heartbeatJSON = bean.toJSON();
const monitorData = [{ id: monitor.id,
- active: monitor.active
+ active: monitor.active,
+ name: monitor.name
}];
const preloadData = await Monitor.preparePreloadData(monitorData);
// Prevent if the msg is undefined, notifications such as Discord cannot send out.
diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js
index 6a52f8f3e..9ea260410 100644
--- a/server/notification-providers/discord.js
+++ b/server/notification-providers/discord.js
@@ -46,10 +46,10 @@ class Discord extends NotificationProvider {
name: "Service Name",
value: monitorJSON["name"],
},
- {
+ ...(!notification.disableUrl ? [{
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
value: this.extractAddress(monitorJSON),
- },
+ }] : []),
{
name: `Time (${heartbeatJSON["timezone"]})`,
value: heartbeatJSON["localDateTime"],
@@ -83,10 +83,10 @@ class Discord extends NotificationProvider {
name: "Service Name",
value: monitorJSON["name"],
},
- {
+ ...(!notification.disableUrl ? [{
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
value: this.extractAddress(monitorJSON),
- },
+ }] : []),
{
name: `Time (${heartbeatJSON["timezone"]})`,
value: heartbeatJSON["localDateTime"],
diff --git a/server/notification-providers/mattermost.js b/server/notification-providers/mattermost.js
index 32d890981..9946d02b2 100644
--- a/server/notification-providers/mattermost.js
+++ b/server/notification-providers/mattermost.js
@@ -79,13 +79,11 @@ class Mattermost extends NotificationProvider {
fallback:
"Your " +
monitorJSON.pathName +
- monitorJSON.name +
" service went " +
statusText,
color: color,
title:
monitorJSON.pathName +
- monitorJSON.name +
" service went " +
statusText,
title_link: monitorJSON.url,
diff --git a/server/notification-providers/onechat.js b/server/notification-providers/onechat.js
new file mode 100644
index 000000000..2d6ea1d5b
--- /dev/null
+++ b/server/notification-providers/onechat.js
@@ -0,0 +1,73 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+const { DOWN, UP } = require("../../src/util");
+
+class OneChat extends NotificationProvider {
+ name = "OneChat";
+
+ /**
+ * @inheritdoc
+ */
+ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+ const okMsg = "Sent Successfully.";
+ const url = "https://chat-api.one.th/message/api/v1/push_message";
+
+ try {
+ const config = {
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: "Bearer " + notification.accessToken,
+ },
+ };
+ if (heartbeatJSON == null) {
+ const testMessage = {
+ to: notification.recieverId,
+ bot_id: notification.botId,
+ type: "text",
+ message: "Test Successful!",
+ };
+ await axios.post(url, testMessage, config);
+ } else if (heartbeatJSON["status"] === DOWN) {
+ const downMessage = {
+ to: notification.recieverId,
+ bot_id: notification.botId,
+ type: "text",
+ message:
+ `UptimeKuma Alert:
+[🔴 Down]
+Name: ${monitorJSON["name"]}
+${heartbeatJSON["msg"]}
+Time (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`,
+ };
+ await axios.post(url, downMessage, config);
+ } else if (heartbeatJSON["status"] === UP) {
+ const upMessage = {
+ to: notification.recieverId,
+ bot_id: notification.botId,
+ type: "text",
+ message:
+ `UptimeKuma Alert:
+[🟢 Up]
+Name: ${monitorJSON["name"]}
+${heartbeatJSON["msg"]}
+Time (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`,
+ };
+ await axios.post(url, upMessage, config);
+ }
+
+ return okMsg;
+ } catch (error) {
+ // Handle errors and throw a descriptive message
+ if (error.response) {
+ const errorMessage =
+ error.response.data?.message ||
+ "Unknown API error occurred.";
+ throw new Error(`OneChat API Error: ${errorMessage}`);
+ } else {
+ this.throwGeneralAxiosError(error);
+ }
+ }
+ }
+}
+
+module.exports = OneChat;
diff --git a/server/notification-providers/pumble.js b/server/notification-providers/pumble.js
new file mode 100644
index 000000000..e1731c0ab
--- /dev/null
+++ b/server/notification-providers/pumble.js
@@ -0,0 +1,48 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+const { UP } = require("../../src/util");
+
+class Pumble extends NotificationProvider {
+ name = "pumble";
+
+ /**
+ * @inheritDoc
+ */
+ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+ const okMsg = "Sent Successfully.";
+
+ try {
+ if (heartbeatJSON === null && monitorJSON === null) {
+ let data = {
+ "attachments": [
+ {
+ "title": "Uptime Kuma Alert",
+ "text": msg,
+ "color": "#5BDD8B"
+ }
+ ]
+ };
+
+ await axios.post(notification.webhookURL, data);
+ return okMsg;
+ }
+
+ let data = {
+ "attachments": [
+ {
+ "title": `${monitorJSON["name"]} is ${heartbeatJSON["status"] === UP ? "up" : "down"}`,
+ "text": heartbeatJSON["msg"],
+ "color": (heartbeatJSON["status"] === UP ? "#5BDD8B" : "#DC3645"),
+ }
+ ]
+ };
+
+ await axios.post(notification.webhookURL, data);
+ return okMsg;
+ } catch (error) {
+ this.throwGeneralAxiosError(error);
+ }
+ }
+}
+
+module.exports = Pumble;
diff --git a/server/notification-providers/pushover.js b/server/notification-providers/pushover.js
index 8422b64c2..fdceeaa44 100644
--- a/server/notification-providers/pushover.js
+++ b/server/notification-providers/pushover.js
@@ -1,5 +1,6 @@
const { getMonitorRelativeURL } = require("../../src/util");
const { setting } = require("../util-server");
+const { UP } = require("../../src/util");
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
@@ -43,15 +44,20 @@ class Pushover extends NotificationProvider {
if (heartbeatJSON == null) {
await axios.post(url, data);
return okMsg;
- } else {
- data.message += `\nTime (${heartbeatJSON["timezone"]}):${heartbeatJSON["localDateTime"]}`;
- await axios.post(url, data);
- return okMsg;
}
+
+ if (heartbeatJSON.status === UP && notification.pushoversounds_up) {
+ // default = DOWN => DOWN-sound is also played for non-UP/DOWN notiifcations
+ data.sound = notification.pushoversounds_up;
+ }
+
+ data.message += `\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`;
+ await axios.post(url, data);
+ return okMsg;
+
} catch (error) {
this.throwGeneralAxiosError(error);
}
-
}
}
diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js
index 5e25a1fbc..455d787c7 100644
--- a/server/notification-providers/slack.js
+++ b/server/notification-providers/slack.js
@@ -145,6 +145,7 @@ class Slack extends NotificationProvider {
const title = "Uptime Kuma Alert";
let data = {
+ "text": msg,
"channel": notification.slackchannel,
"username": notification.slackusername,
"icon_emoji": notification.slackiconemo,
diff --git a/server/notification-providers/sms-planet.js b/server/notification-providers/sms-planet.js
new file mode 100644
index 000000000..97e9e715e
--- /dev/null
+++ b/server/notification-providers/sms-planet.js
@@ -0,0 +1,40 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+
+class SMSPlanet extends NotificationProvider {
+ name = "SMSPlanet";
+
+ /**
+ * @inheritdoc
+ */
+ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+ const okMsg = "Sent Successfully.";
+ const url = "https://api2.smsplanet.pl/sms";
+
+ try {
+ let config = {
+ headers: {
+ "Authorization": "Bearer " + notification.smsplanetApiToken,
+ "content-type": "multipart/form-data"
+ }
+ };
+
+ let data = {
+ "from": notification.smsplanetSenderName,
+ "to": notification.smsplanetPhoneNumbers,
+ "msg": msg.replace(/🔴/, "❌")
+ };
+
+ let response = await axios.post(url, data, config);
+ if (!response.data?.messageId) {
+ throw new Error(response.data?.errorMsg ?? "SMSPlanet server did not respond with the expected result");
+ }
+
+ return okMsg;
+ } catch (error) {
+ this.throwGeneralAxiosError(error);
+ }
+ }
+}
+
+module.exports = SMSPlanet;
diff --git a/server/notification-providers/smseagle.js b/server/notification-providers/smseagle.js
index 4e897006c..a123243a4 100644
--- a/server/notification-providers/smseagle.js
+++ b/server/notification-providers/smseagle.js
@@ -11,59 +11,127 @@ class SMSEagle extends NotificationProvider {
const okMsg = "Sent Successfully.";
try {
- let config = {
- headers: {
- "Content-Type": "application/json",
+ if (notification.smseagleApiType === "smseagle-apiv1") { // according to https://www.smseagle.eu/apiv1/
+ let config = {
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ }
+ };
+
+ let sendMethod;
+ let recipientType;
+ let duration;
+ let voiceId;
+
+ if (notification.smseagleRecipientType === "smseagle-contact") {
+ recipientType = "contactname";
+ sendMethod = "/send_tocontact";
+ } else if (notification.smseagleRecipientType === "smseagle-group") {
+ recipientType = "groupname";
+ sendMethod = "/send_togroup";
+ } else if (notification.smseagleRecipientType === "smseagle-to") {
+ recipientType = "to";
+ sendMethod = "/send_sms";
+ if (notification.smseagleMsgType !== "smseagle-sms") {
+ duration = notification.smseagleDuration ?? 10;
+
+ if (notification.smseagleMsgType === "smseagle-ring") {
+ sendMethod = "/ring_call";
+ } else if (notification.smseagleMsgType === "smseagle-tts") {
+ sendMethod = "/tts_call";
+ } else if (notification.smseagleMsgType === "smseagle-tts-advanced") {
+ sendMethod = "/tts_adv_call";
+ voiceId = notification.smseagleTtsModel ? notification.smseagleTtsModel : 1;
+ }
+ }
}
- };
- let postData;
- let sendMethod;
- let recipientType;
+ const url = new URL(notification.smseagleUrl + "/http_api" + sendMethod);
- let encoding = (notification.smseagleEncoding) ? "1" : "0";
- let priority = (notification.smseaglePriority) ? notification.smseaglePriority : "0";
-
- if (notification.smseagleRecipientType === "smseagle-contact") {
- recipientType = "contactname";
- sendMethod = "sms.send_tocontact";
- }
- if (notification.smseagleRecipientType === "smseagle-group") {
- recipientType = "groupname";
- sendMethod = "sms.send_togroup";
- }
- if (notification.smseagleRecipientType === "smseagle-to") {
- recipientType = "to";
- sendMethod = "sms.send_sms";
- }
-
- let params = {
- access_token: notification.smseagleToken,
- [recipientType]: notification.smseagleRecipient,
- message: msg,
- responsetype: "extended",
- unicode: encoding,
- highpriority: priority
- };
-
- postData = {
- method: sendMethod,
- params: params
- };
-
- let resp = await axios.post(notification.smseagleUrl + "/jsonrpc/sms", postData, config);
-
- if ((JSON.stringify(resp.data)).indexOf("message_id") === -1) {
- let error = "";
- if (resp.data.result && resp.data.result.error_text) {
- error = `SMSEagle API returned error: ${JSON.stringify(resp.data.result.error_text)}`;
+ url.searchParams.append("access_token", notification.smseagleToken);
+ url.searchParams.append(recipientType, notification.smseagleRecipient);
+ if (!notification.smseagleRecipientType || notification.smseagleRecipientType === "smseagle-sms") {
+ url.searchParams.append("unicode", (notification.smseagleEncoding) ? "1" : "0");
+ url.searchParams.append("highpriority", notification.smseaglePriority ?? "0");
} else {
- error = "SMSEagle API returned an unexpected response";
+ url.searchParams.append("duration", duration);
+ }
+ if (notification.smseagleRecipientType !== "smseagle-ring") {
+ url.searchParams.append("message", msg);
+ }
+ if (voiceId) {
+ url.searchParams.append("voice_id", voiceId);
}
- throw new Error(error);
- }
- return okMsg;
+ let resp = await axios.get(url.toString(), config);
+
+ if (resp.data.indexOf("OK") === -1) {
+ let error = `SMSEagle API returned error: ${resp.data}`;
+ throw new Error(error);
+ }
+
+ return okMsg;
+ } else if (notification.smseagleApiType === "smseagle-apiv2") { // according to https://www.smseagle.eu/docs/apiv2/
+ let config = {
+ headers: {
+ "access-token": notification.smseagleToken,
+ "Content-Type": "application/json",
+ }
+ };
+
+ let encoding = (notification.smseagleEncoding) ? "unicode" : "standard";
+ let priority = (notification.smseaglePriority) ?? 0;
+
+ let postData = {
+ text: msg,
+ encoding: encoding,
+ priority: priority
+ };
+
+ if (notification.smseagleRecipientContact) {
+ postData["contacts"] = notification.smseagleRecipientContact.split(",").map(Number);
+ }
+ if (notification.smseagleRecipientGroup) {
+ postData["groups"] = notification.smseagleRecipientGroup.split(",").map(Number);
+ }
+ if (notification.smseagleRecipientTo) {
+ postData["to"] = notification.smseagleRecipientTo.split(",");
+ }
+
+ let endpoint = "/messages/sms";
+
+ if (notification.smseagleMsgType !== "smseagle-sms") {
+
+ postData["duration"] = notification.smseagleDuration ?? 10;
+
+ if (notification.smseagleMsgType === "smseagle-ring") {
+ endpoint = "/calls/ring";
+ } else if (notification.smseagleMsgType === "smseagle-tts") {
+ endpoint = "/calls/tts";
+ } else if (notification.smseagleMsgType === "smseagle-tts-advanced") {
+ endpoint = "/calls/tts_advanced";
+ postData["voice_id"] = notification.smseagleTtsModel ?? 1;
+ }
+ }
+
+ let resp = await axios.post(notification.smseagleUrl + "/api/v2" + endpoint, postData, config);
+
+ const queuedCount = resp.data.filter(x => x.status === "queued").length;
+ const unqueuedCount = resp.data.length - queuedCount;
+
+ if (resp.status !== 200 || queuedCount === 0) {
+ if (!resp.data.length) {
+ throw new Error("SMSEagle API returned an empty response");
+ }
+ throw new Error(`SMSEagle API returned error: ${JSON.stringify(resp.data)}`);
+ }
+
+ if (unqueuedCount) {
+ return `Sent ${queuedCount}/${resp.data.length} Messages Successfully.`;
+ }
+
+ return okMsg;
+ }
} catch (error) {
this.throwGeneralAxiosError(error);
}
diff --git a/server/notification-providers/smtp.js b/server/notification-providers/smtp.js
index 980c7dfd3..d0ad5b728 100644
--- a/server/notification-providers/smtp.js
+++ b/server/notification-providers/smtp.js
@@ -42,6 +42,7 @@ class SMTP extends NotificationProvider {
// default values in case the user does not want to template
let subject = msg;
let body = msg;
+ let useHTMLBody = false;
if (heartbeatJSON) {
body = `${msg}\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`;
}
@@ -50,11 +51,11 @@ class SMTP extends NotificationProvider {
// cannot end with whitespace as this often raises spam scores
const customSubject = notification.customSubject?.trim() || "";
const customBody = notification.customBody?.trim() || "";
-
if (customSubject !== "") {
subject = await this.renderTemplate(customSubject, msg, monitorJSON, heartbeatJSON);
}
if (customBody !== "") {
+ useHTMLBody = notification.htmlBody || false;
body = await this.renderTemplate(customBody, msg, monitorJSON, heartbeatJSON);
}
}
@@ -67,7 +68,8 @@ class SMTP extends NotificationProvider {
bcc: notification.smtpBCC,
to: notification.smtpTo,
subject: subject,
- text: body,
+ // If the email body is custom, and the user wants it, set the email body as HTML
+ [useHTMLBody ? "html" : "text"]: body
});
return okMsg;
diff --git a/server/notification-providers/spugpush.js b/server/notification-providers/spugpush.js
new file mode 100644
index 000000000..b04a20cac
--- /dev/null
+++ b/server/notification-providers/spugpush.js
@@ -0,0 +1,37 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+const { DOWN, UP } = require("../../src/util");
+
+class SpugPush extends NotificationProvider {
+
+ name = "SpugPush";
+
+ /**
+ * @inheritdoc
+ */
+ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+ let okMsg = "Sent Successfully.";
+ try {
+ let formData = {
+ title: "Uptime Kuma Message",
+ content: msg
+ };
+ if (heartbeatJSON) {
+ if (heartbeatJSON["status"] === UP) {
+ formData.title = `UptimeKuma 「${monitorJSON["name"]}」 is Up`;
+ formData.content = `[✅ Up] ${heartbeatJSON["msg"]}`;
+ } else if (heartbeatJSON["status"] === DOWN) {
+ formData.title = `UptimeKuma 「${monitorJSON["name"]}」 is Down`;
+ formData.content = `[🔴 Down] ${heartbeatJSON["msg"]}`;
+ }
+ }
+ const apiUrl = `https://push.spug.cc/send/${notification.templateKey}`;
+ await axios.post(apiUrl, formData);
+ return okMsg;
+ } catch (error) {
+ this.throwGeneralAxiosError(error);
+ }
+ }
+}
+
+module.exports = SpugPush;
diff --git a/server/notification.js b/server/notification.js
index 0c222d932..468d026c0 100644
--- a/server/notification.js
+++ b/server/notification.js
@@ -30,9 +30,11 @@ const Mattermost = require("./notification-providers/mattermost");
const Nostr = require("./notification-providers/nostr");
const Ntfy = require("./notification-providers/ntfy");
const Octopush = require("./notification-providers/octopush");
+const OneChat = require("./notification-providers/onechat");
const OneBot = require("./notification-providers/onebot");
const Opsgenie = require("./notification-providers/opsgenie");
const PagerDuty = require("./notification-providers/pagerduty");
+const Pumble = require("./notification-providers/pumble");
const FlashDuty = require("./notification-providers/flashduty");
const PagerTree = require("./notification-providers/pagertree");
const PromoSMS = require("./notification-providers/promosms");
@@ -72,6 +74,8 @@ const Onesender = require("./notification-providers/onesender");
const Wpush = require("./notification-providers/wpush");
const SendGrid = require("./notification-providers/send-grid");
const YZJ = require("./notification-providers/yzj");
+const SMSPlanet = require("./notification-providers/sms-planet");
+const SpugPush = require("./notification-providers/spugpush");
class Notification {
@@ -119,6 +123,7 @@ class Notification {
new Nostr(),
new Ntfy(),
new Octopush(),
+ new OneChat(),
new OneBot(),
new Onesender(),
new Opsgenie(),
@@ -126,6 +131,7 @@ class Notification {
new FlashDuty(),
new PagerTree(),
new PromoSMS(),
+ new Pumble(),
new Pushbullet(),
new PushDeer(),
new Pushover(),
@@ -160,7 +166,9 @@ class Notification {
new Cellsynt(),
new Wpush(),
new SendGrid(),
- new YZJ()
+ new YZJ(),
+ new SMSPlanet(),
+ new SpugPush(),
];
for (let item of list) {
if (! item.name) {
diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue
index 56cae66c8..2e66de8e9 100644
--- a/src/components/NotificationDialog.vue
+++ b/src/components/NotificationDialog.vue
@@ -135,11 +135,13 @@ export default {
"nostr": "Nostr",
"ntfy": "Ntfy",
"octopush": "Octopush",
+ "OneChat": "OneChat",
"OneBot": "OneBot",
"Onesender": "Onesender",
"Opsgenie": "Opsgenie",
"PagerDuty": "PagerDuty",
"PagerTree": "PagerTree",
+ "pumble": "Pumble",
"pushbullet": "Pushbullet",
"PushByTechulus": "Push by Techulus",
"pushover": "Pushover",
@@ -183,9 +185,11 @@ export default {
"WeCom": "WeCom (企业微信群机器人)",
"ServerChan": "ServerChan (Server酱)",
"PushPlus": "PushPlus (推送加)",
+ "SpugPush": "SpugPush(Spug推送助手)",
"smsc": "SMSC",
"WPush": "WPush(wpush.cn)",
- "YZJ": "YZJ (云之家自定义机器人)"
+ "YZJ": "YZJ (云之家自定义机器人)",
+ "SMSPlanet": "SMSPlanet.pl"
};
// Sort by notification name
diff --git a/src/components/notifications/Discord.vue b/src/components/notifications/Discord.vue
index 5d8334f5f..40d2f204e 100644
--- a/src/components/notifications/Discord.vue
+++ b/src/components/notifications/Discord.vue
@@ -53,6 +53,13 @@
+
+
+
+
+
+
+
diff --git a/src/components/notifications/OneChat.vue b/src/components/notifications/OneChat.vue
new file mode 100644
index 000000000..b954d338b
--- /dev/null
+++ b/src/components/notifications/OneChat.vue
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
{{ $t("OneChatAccessToken") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/notifications/Pumble.vue b/src/components/notifications/Pumble.vue
new file mode 100644
index 000000000..c577e0404
--- /dev/null
+++ b/src/components/notifications/Pumble.vue
@@ -0,0 +1,9 @@
+
+
+ *
+
+
+
+
diff --git a/src/components/notifications/Pushover.vue b/src/components/notifications/Pushover.vue
index 7ee0eafb6..e2fecd29f 100644
--- a/src/components/notifications/Pushover.vue
+++ b/src/components/notifications/Pushover.vue
@@ -16,34 +16,24 @@
-
-