From e7d3fa6a78444cf6fbeab9ed949de20bbecf14a6 Mon Sep 17 00:00:00 2001 From: Aleksander <81246293+AxeKam333@users.noreply.github.com> Date: Sun, 27 Apr 2025 18:37:23 +0200 Subject: [PATCH] Update SMSEagle support adding APIv2 and calls integration (#5756) Co-authored-by: Frank Elsinga --- server/notification-providers/smseagle.js | 162 +++++++++++++++------- src/components/notifications/SMSEagle.vue | 135 +++++++++++++++--- src/lang/en.json | 18 ++- 3 files changed, 250 insertions(+), 65 deletions(-) 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/src/components/notifications/SMSEagle.vue b/src/components/notifications/SMSEagle.vue index ec781313a..16277a6ef 100644 --- a/src/components/notifications/SMSEagle.vue +++ b/src/components/notifications/SMSEagle.vue @@ -1,31 +1,123 @@ @@ -36,5 +128,16 @@ export default { components: { HiddenInput, }, + mounted() { + if (!this.$parent.notification.smseagleApiType) { + this.$parent.notification.smseagleApiType = "smseagle-apiv1"; + } + if (!this.$parent.notification.smseagleMsgType) { + this.$parent.notification.smseagleMsgType = "smseagle-sms"; + } + if (!this.$parent.notification.smseagleRecipientType) { + this.$parent.notification.smseagleRecipientType = "smseagle-to"; + } + } }; diff --git a/src/lang/en.json b/src/lang/en.json index b53fa1350..772820c59 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -757,12 +757,26 @@ "smseagleTo": "Phone number(s)", "smseagleGroup": "Phonebook group name(s)", "smseagleContact": "Phonebook contact name(s)", + "smseagleGroupV2": "Phonebook group ID(s)", + "smseagleContactV2": "Phonebook contact ID(s)", "smseagleRecipientType": "Recipient type", "smseagleRecipient": "Recipient(s) (multiple must be separated with comma)", "smseagleToken": "API Access token", "smseagleUrl": "Your SMSEagle device URL", - "smseagleEncoding": "Send as Unicode", - "smseaglePriority": "Message priority (0-9, default = 0)", + "smseagleEncoding": "Send as Unicode (default=GSM-7)", + "smseaglePriority": "Message priority (0-9, highest priority = 9)", + "smseagleMsgType": "Message type", + "smseagleMsgSms": "Sms message (default)", + "smseagleMsgRing": "Ring call", + "smseagleMsgTts": "Text-to-speech call", + "smseagleMsgTtsAdvanced": "Text-to-speech Advanced call", + "smseagleDuration": "Duration (in seconds)", + "smseagleTtsModel": "Text-to-speech model ID", + "smseagleApiType": "API version", + "smseagleApiv1": "APIv1 (for existing projects and backward compatibility)", + "smseagleApiv2": "APIv2 (recommended for new integrations)", + "smseagleDocs": "Check documentation or APIv2 availability: {0}", + "smseagleComma": "Multiple must be separated with comma", "smspartnerApiurl": "You can find your API key in your dashboard at {0}", "smspartnerPhoneNumber": "Phone number(s)", "smspartnerPhoneNumberHelptext": "The number must be in the international format {0}, {1}. Multiple numbers must be separated by {2}",