From ffa791da8b7567d540251343edbfdf1e2e0329e5 Mon Sep 17 00:00:00 2001 From: zjiecode Date: Sun, 5 Jul 2026 06:28:43 -0700 Subject: [PATCH] feat: Add WxPusher SPT (WxPusher simple push token) notification provider (#7569) Co-authored-by: wxpusher --- server/notification-providers/wxpusher.js | 73 +++++++++++++++++++++++ server/notification.js | 2 + src/components/NotificationDialog.vue | 1 + src/components/notifications/WxPusher.vue | 48 +++++++++++++++ src/components/notifications/index.js | 2 + src/lang/en.json | 3 + 6 files changed, 129 insertions(+) create mode 100644 server/notification-providers/wxpusher.js create mode 100644 src/components/notifications/WxPusher.vue diff --git a/server/notification-providers/wxpusher.js b/server/notification-providers/wxpusher.js new file mode 100644 index 000000000..0eb84277a --- /dev/null +++ b/server/notification-providers/wxpusher.js @@ -0,0 +1,73 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class WxPusher extends NotificationProvider { + name = "WxPusher"; + + // WxPusher's simple-push accepts at most 10 SPTs per request. + static SPT_PER_REQUEST = 10; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + // Accept one or multiple SPTs, comma-separated. + const sptList = String(notification.wxpusherSPT || "") + .split(",") + .map((spt) => spt.trim()) + .filter((spt) => spt.length > 0); + + try { + if (sptList.length === 0) { + throw new Error("No WxPusher SPT is configured"); + } + + const summary = this.checkStatus(heartbeatJSON, monitorJSON).slice(0, 100); + const config = this.getAxiosConfigWithProxy({}); + + // Send in batches so more than 10 SPTs are all delivered, never silently dropped. + for (let i = 0; i < sptList.length; i += WxPusher.SPT_PER_REQUEST) { + const context = { + content: msg, + summary, + contentType: 1, + sptList: sptList.slice(i, i + WxPusher.SPT_PER_REQUEST), + }; + const result = await axios.post( + "https://wxpusher.zjiecode.com/api/send/message/simple-push", + context, + config + ); + if (result.data.code !== 1000) { + throw result.data.msg; + } + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Get the formatted title for message + * @param {?object} heartbeatJSON Heartbeat details (For Up/Down only) + * @param {?object} monitorJSON Monitor details (For Up/Down only) + * @returns {string} Formatted title + */ + checkStatus(heartbeatJSON, monitorJSON) { + let title = "UptimeKuma Message"; + if (heartbeatJSON != null && heartbeatJSON["status"] === UP) { + title = "UptimeKuma Monitor Up " + monitorJSON["name"]; + } + if (heartbeatJSON != null && heartbeatJSON["status"] === DOWN) { + title = "UptimeKuma Monitor Down " + monitorJSON["name"]; + } + return title; + } +} + +module.exports = WxPusher; diff --git a/server/notification.js b/server/notification.js index 8e2f4d754..9cd327047 100644 --- a/server/notification.js +++ b/server/notification.js @@ -82,6 +82,7 @@ const GtxMessaging = require("./notification-providers/gtx-messaging"); const Cellsynt = require("./notification-providers/cellsynt"); const Onesender = require("./notification-providers/onesender"); const Wpush = require("./notification-providers/wpush"); +const WxPusher = require("./notification-providers/wxpusher"); const SendGrid = require("./notification-providers/send-grid"); const Brevo = require("./notification-providers/brevo"); const Resend = require("./notification-providers/resend"); @@ -193,6 +194,7 @@ class Notification { new GtxMessaging(), new Cellsynt(), new Wpush(), + new WxPusher(), new Brevo(), new Resend(), new YZJ(), diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue index 4a34c5e1f..4fa6d5a32 100644 --- a/src/components/NotificationDialog.vue +++ b/src/components/NotificationDialog.vue @@ -335,6 +335,7 @@ export default { smsc: "SMSC", smsir: "SMS.IR", WPush: "WPush(wpush.cn)", + WxPusher: "WxPusher SPT Push (WxPusher极简推送)", YZJ: "YZJ (云之家自定义机器人)", SMSPlanet: "SMSPlanet.pl", VK: "VK", diff --git a/src/components/notifications/WxPusher.vue b/src/components/notifications/WxPusher.vue new file mode 100644 index 000000000..2f8e431b1 --- /dev/null +++ b/src/components/notifications/WxPusher.vue @@ -0,0 +1,48 @@ + + + diff --git a/src/components/notifications/index.js b/src/components/notifications/index.js index 44e3c2036..ec31a77db 100644 --- a/src/components/notifications/index.js +++ b/src/components/notifications/index.js @@ -81,6 +81,7 @@ import Whatsapp360messenger from "./360messenger.vue"; import Evolution from "./Evolution.vue"; import Cellsynt from "./Cellsynt.vue"; import WPush from "./WPush.vue"; +import WxPusher from "./WxPusher.vue"; import SIGNL4 from "./SIGNL4.vue"; import SendGrid from "./SendGrid.vue"; import Brevo from "./Brevo.vue"; @@ -184,6 +185,7 @@ const NotificationFormList = { gtxmessaging: GtxMessaging, Cellsynt: Cellsynt, WPush: WPush, + WxPusher: WxPusher, SendGrid: SendGrid, Brevo: Brevo, Resend: Resend, diff --git a/src/lang/en.json b/src/lang/en.json index 4595c9d02..9c538de25 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -973,6 +973,9 @@ "PushDeer Server": "PushDeer Server", "pushDeerServerDescription": "Leave blank to use the official server", "PushDeer Key": "PushDeer Key", + "wxPusherSptDescription": "WxPusher Simple Push (SPT) delivers through the standalone WxPusher app across 6 platforms — Android, iOS, HarmonyOS, Windows, macOS and Linux ({download}). Scan the QR code below to get your SPT and you are ready to send. Separate multiple SPTs with a comma (,) — see the {docs} for more.", + "wxPusherDownloadApp": "download the app", + "wxPusherDocs": "official guide", "SpugPush Template Code": "Template Code", "wayToGetClickSendSMSToken": "You can get API Username and API Key from {here}.", "wayToGetEgoSMSToken": "You can get your EgoSMS API username and password from {here}.",