From 86b86fae55f30a70e1109e74e9ab984e824f6a47 Mon Sep 17 00:00:00 2001 From: WaMessenger <107600336+WaMessenger@users.noreply.github.com> Date: Wed, 25 Feb 2026 14:11:59 +0330 Subject: [PATCH] feat: add whatsApp (360messenger) notification provider (#7046) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- server/notification-providers/360messenger.js | 161 ++++++++++ server/notification.js | 2 + src/components/NotificationDialog.vue | 1 + src/components/notifications/360messenger.vue | 300 ++++++++++++++++++ src/components/notifications/index.js | 2 + src/lang/en.json | 18 ++ 6 files changed, 484 insertions(+) create mode 100644 server/notification-providers/360messenger.js create mode 100644 src/components/notifications/360messenger.vue diff --git a/server/notification-providers/360messenger.js b/server/notification-providers/360messenger.js new file mode 100644 index 000000000..90d926d94 --- /dev/null +++ b/server/notification-providers/360messenger.js @@ -0,0 +1,161 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Whatsapp360messenger extends NotificationProvider { + name = "Whatsapp360messenger"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = { + headers: { + Accept: "application/json", + "Content-Type": "application/json", + Authorization: "Bearer " + notification.Whatsapp360messengerAuthToken, + }, + }; + config = this.getAxiosConfigWithProxy(config); + + // Use custom template if enabled + let message = msg; + if (notification.Whatsapp360messengerUseTemplate && notification.Whatsapp360messengerTemplate) { + message = this.applyTemplate( + notification.Whatsapp360messengerTemplate, + msg, + monitorJSON, + heartbeatJSON + ); + } + + // Normalize recipients: support comma/semicolon-separated list + const recipients = (notification.Whatsapp360messengerRecipient || "") + .split(/[;,]/) + .map((r) => r.trim()) + .filter((r) => r !== ""); + + // Normalize group IDs: support array (multi-select) and fallback to single value / delimited string + const rawGroupIds = + notification.Whatsapp360messengerGroupIds || notification.Whatsapp360messengerGroupId || ""; + + let groupIds = []; + if (Array.isArray(rawGroupIds)) { + groupIds = rawGroupIds + .map((g) => { + if (typeof g === "string") { + return g.trim(); + } + if (g && typeof g === "object" && g.id) { + return String(g.id).trim(); + } + return ""; + }) + .filter((g) => g !== ""); + } else if (typeof rawGroupIds === "string" && rawGroupIds.trim() !== "") { + groupIds = rawGroupIds + .split(/[;,]/) + .map((g) => g.trim()) + .filter((g) => g !== ""); + } + + const hasGroupId = groupIds.length > 0; + const hasRecipient = recipients.length > 0; + + // Send to both if both are provided + if (hasGroupId && hasRecipient) { + // Send to all individual recipients + await Promise.all( + recipients.map((recipient) => { + const recipientData = { + phonenumber: recipient, + text: message, + }; + return axios.post("https://api.360messenger.com/v2/sendMessage", recipientData, config); + }) + ); + + // Send to all selected groups + await Promise.all( + groupIds.map((groupId) => { + const groupData = { + groupId, + text: message, + }; + return axios.post("https://api.360messenger.com/v2/sendGroup", groupData, config); + }) + ); + + return `${okMsg} (Sent to ${recipients.length} recipient(s) and ${groupIds.length} group(s))`; + } else if (hasGroupId) { + // Send to group(s) only + await Promise.all( + groupIds.map((groupId) => { + const data = { + groupId, + text: message, + }; + return axios.post("https://api.360messenger.com/v2/sendGroup", data, config); + }) + ); + + return `${okMsg} (Sent to ${groupIds.length} group(s))`; + } else if (hasRecipient) { + // Send to recipient(s) only + await Promise.all( + recipients.map((recipient) => { + const data = { + phonenumber: recipient, + text: message, + }; + return axios.post("https://api.360messenger.com/v2/sendMessage", data, config); + }) + ); + + return `${okMsg} (Sent to ${recipients.length} recipient(s))`; + } else { + throw new Error("No recipient or group specified"); + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Apply template with variables + * @param {string} template - Template string + * @param {string} msg - Default message + * @param {object} monitorJSON - Monitor data + * @param {object} heartbeatJSON - Heartbeat data + * @returns {string} Formatted message + */ + applyTemplate(template, msg, monitorJSON, heartbeatJSON) { + try { + // Simple template replacement + let result = template; + + // Replace monitor variables + if (monitorJSON) { + result = result.replace(/{{ monitorJSON\['name'\] }}/g, monitorJSON.name || ""); + result = result.replace(/{{ monitorJSON\['url'\] }}/g, monitorJSON.url || ""); + } + + // Replace message variable + result = result.replace(/{{ msg }}/g, msg); + + // Handle conditional blocks (simple if statements) + result = result.replace(/{% if monitorJSON %}([\s\S]*?){% endif %}/g, (match, content) => { + return monitorJSON ? content : ""; + }); + + return result; + } catch (error) { + // If template parsing fails, return original message + return msg; + } + } +} + +module.exports = Whatsapp360messenger; diff --git a/server/notification.js b/server/notification.js index 666d0c982..8afb32095 100644 --- a/server/notification.js +++ b/server/notification.js @@ -86,6 +86,7 @@ const SMSPlanet = require("./notification-providers/sms-planet"); const SpugPush = require("./notification-providers/spugpush"); const SMSIR = require("./notification-providers/smsir"); const { commandExists } = require("./util-server"); +const Whatsapp360messenger = require("./notification-providers/360messenger"); const Webpush = require("./notification-providers/Webpush"); const HaloPSA = require("./notification-providers/HaloPSA"); @@ -189,6 +190,7 @@ class Notification { new Notifery(), new SMSIR(), new SendGrid(), + new Whatsapp360messenger(), new Webpush(), new HaloPSA(), ]; diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue index 9dd6f4517..566bd6b24 100644 --- a/src/components/NotificationDialog.vue +++ b/src/components/NotificationDialog.vue @@ -244,6 +244,7 @@ export default { whapi: "WhatsApp (Whapi)", evolution: "WhatsApp (Evolution)", waha: "WhatsApp (WAHA)", + Whatsapp360messenger: "WhatsApp (360messenger)", }; // Push Services - Push notification services diff --git a/src/components/notifications/360messenger.vue b/src/components/notifications/360messenger.vue new file mode 100644 index 000000000..74c5e91c1 --- /dev/null +++ b/src/components/notifications/360messenger.vue @@ -0,0 +1,300 @@ + + + + + diff --git a/src/components/notifications/index.js b/src/components/notifications/index.js index 8a2ff2043..648f8f7ed 100644 --- a/src/components/notifications/index.js +++ b/src/components/notifications/index.js @@ -73,6 +73,7 @@ import SpugPush from "./SpugPush.vue"; import SevenIO from "./SevenIO.vue"; import Whapi from "./Whapi.vue"; import WAHA from "./WAHA.vue"; +import Whatsapp360messenger from "./360messenger.vue"; import Evolution from "./Evolution.vue"; import Cellsynt from "./Cellsynt.vue"; import WPush from "./WPush.vue"; @@ -168,6 +169,7 @@ const NotificationFormList = { evolution: Evolution, notifery: Notifery, waha: WAHA, + Whatsapp360messenger: Whatsapp360messenger, gtxmessaging: GtxMessaging, Cellsynt: Cellsynt, WPush: WPush, diff --git a/src/lang/en.json b/src/lang/en.json index 585db61fe..1abfa995e 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -1314,6 +1314,24 @@ "wayToGetWahaApiKey": "API Key is WHATSAPP_API_KEY environment variable value you used to run WAHA.", "wayToGetWahaSession": "From this session WAHA sends notifications to Chat ID. You can find it in WAHA Dashboard.", "wayToWriteWahaChatId": "The phone number with the international prefix, but without the plus sign at the start ({0}), the Contact ID ({1}) or the Group ID ({2}). Notifications are sent to this Chat ID from WAHA Session.", + "360messengerAuthToken": "360messenger API Key", + "360messengerRecipient": "Recipient phone number(s)", + "360messengerGroupId": "360messenger Group ID", + "360messengerUseTemplate": "Use a custom message template", + "360messengerTemplate": "360messenger Message Template", + "360messengerGroupList": "WhatsApp groups", + "360messengerSelectGroupList": "Select a group to add", + "360messengerSelectedGroupID": "Selected Group ID(s)", + "360messengerEnableSendToGroup": "Enable sending to WhatsApp group(s)", + "360messengerCustomMessageTemplate": "Custom message template", + "360messengerEnableCustomMessage": "Enable a custom message template instead of the default message.", + "360messengerMessageTemplate": "Message template", + "360messengerWayToGetUrlAndToken": "You can get your 360messenger API key from {0}.", + "360messengerWayToWriteRecipient": "Enter one or more phone numbers in international format without a leading plus (e.g. {0}). Separate multiple numbers with commas.", + "360messengerErrorNoApiKey": "Please enter your 360messenger API key first.", + "360messengerErrorNoGroups": "No WhatsApp groups were found for this account.", + "360messengerErrorApi": "Unable to load the WhatsApp group list (Error {statusCode}: {message}).", + "360messengerErrorGeneric": "Unable to load the WhatsApp group list: {message}", "YZJ Webhook URL": "YZJ Webhook URL", "YZJ Robot Token": "YZJ Robot token", "Plain Text": "Plain Text",