From 2e5e10343469dc2c3fa9857c94703f36e2a6b670 Mon Sep 17 00:00:00 2001 From: booooza Date: Fri, 14 Jun 2024 12:25:19 +0200 Subject: [PATCH] New notification provider: Threema Gateway --- server/notification-providers/threema.js | 83 ++++++++++++++++++++++++ server/notification.js | 2 + src/components/NotificationDialog.vue | 1 + src/components/notifications/Threema.vue | 78 ++++++++++++++++++++++ src/components/notifications/index.js | 2 + src/lang/en.json | 14 +++- 6 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 server/notification-providers/threema.js create mode 100644 src/components/notifications/Threema.vue diff --git a/server/notification-providers/threema.js b/server/notification-providers/threema.js new file mode 100644 index 000000000..cf60876f2 --- /dev/null +++ b/server/notification-providers/threema.js @@ -0,0 +1,83 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Threema extends NotificationProvider { + name = "threema"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const url = "https://msgapi.threema.ch/send_simple"; + + const config = { + headers: { + "Accept": "*/*", + "Content-Type": "application/x-www-form-urlencoded; charset=utf-8" + } + }; + + const data = { + from: notification.threemaSenderIdentity, + secret: notification.threemaSecret, + text: msg + }; + + switch (notification.threemaRecipientType) { + case "identity": + data.to = notification.threemaRecipient; + break; + case "phone": + data.phone = notification.threemaRecipient; + break; + case "email": + data.email = notification.threemaRecipient; + break; + default: + throw new Error(`Unsupported recipient type: ${notification.threemaRecipientType}`); + } + + try { + await axios.post(url, new URLSearchParams(data), config); + return "Threema notification sent successfully."; + } catch (error) { + this.handleApiError(error); + this.throwGeneralAxiosError(error); + } + } + + /** + * Handle Threema API errors + * @param {any} error The error to handle + * @returns {void} + */ + handleApiError(error) { + if (error.response) { + const status = error.response.status; + switch (status) { + case 400: + error.message = "Invalid recipient identity or account not set up for basic mode (400)."; + break; + case 401: + error.message = "Incorrect API identity or secret (401)."; + break; + case 402: + error.message = "No credits remaining (402)."; + break; + case 404: + error.message = "Recipient not found (404)."; + break; + case 413: + error.message = "Message is too long (413)."; + break; + case 500: + error.message = "Temporary internal server error (500)."; + break; + default: + break; + } + } + } +} + +module.exports = Threema; diff --git a/server/notification.js b/server/notification.js index 8feb5bd9a..28b0db758 100644 --- a/server/notification.js +++ b/server/notification.js @@ -51,6 +51,7 @@ const Stackfield = require("./notification-providers/stackfield"); const Teams = require("./notification-providers/teams"); const TechulusPush = require("./notification-providers/techulus-push"); const Telegram = require("./notification-providers/telegram"); +const Threema = require("./notification-providers/threema"); const Twilio = require("./notification-providers/twilio"); const Splunk = require("./notification-providers/splunk"); const Webhook = require("./notification-providers/webhook"); @@ -133,6 +134,7 @@ class Notification { new Teams(), new TechulusPush(), new Telegram(), + new Threema(), new Twilio(), new Splunk(), new Webhook(), diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue index 1c4636d56..427366619 100644 --- a/src/components/NotificationDialog.vue +++ b/src/components/NotificationDialog.vue @@ -152,6 +152,7 @@ export default { "stackfield": "Stackfield", "teams": "Microsoft Teams", "telegram": "Telegram", + "threema": "Threema", "twilio": "Twilio", "Splunk": "Splunk", "webhook": "Webhook", diff --git a/src/components/notifications/Threema.vue b/src/components/notifications/Threema.vue new file mode 100644 index 000000000..79ec89ed5 --- /dev/null +++ b/src/components/notifications/Threema.vue @@ -0,0 +1,78 @@ + + diff --git a/src/components/notifications/index.js b/src/components/notifications/index.js index 0fcdf0504..5d49b8a4a 100644 --- a/src/components/notifications/index.js +++ b/src/components/notifications/index.js @@ -52,6 +52,7 @@ import STMP from "./SMTP.vue"; import Teams from "./Teams.vue"; import TechulusPush from "./TechulusPush.vue"; import Telegram from "./Telegram.vue"; +import Threema from "./Threema.vue"; import Twilio from "./Twilio.vue"; import Webhook from "./Webhook.vue"; import WeCom from "./WeCom.vue"; @@ -119,6 +120,7 @@ const NotificationFormList = { "stackfield": Stackfield, "teams": Teams, "telegram": Telegram, + "threema": Threema, "twilio": Twilio, "Splunk": Splunk, "webhook": Webhook, diff --git a/src/lang/en.json b/src/lang/en.json index fcf46ff4a..8f7b34ef2 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -942,5 +942,17 @@ "Allow Long SMS": "Allow Long SMS", "cellsyntSplitLongMessages": "Split long messages into up to 6 parts. 153 x 6 = 918 characters.", "max 15 digits": "max 15 digits", - "max 11 alphanumeric characters": "max 11 alphanumeric characters" + "max 11 alphanumeric characters": "max 11 alphanumeric characters", + "wayToGetThreemaGateway": "You can register for Threema Gateway {0}.", + "threemaRecipient": "Recipient", + "threemaRecipientType": "Recipient Type", + "threemaRecipientTypeIdentity": "Threema-ID", + "threemaRecipientTypeIdentityFormat": "8 characters", + "threemaRecipientTypePhone": "Phone Number", + "threemaRecipientTypePhoneFormat": "E.164, without leading +", + "threemaRecipientTypeEmail": "Email Address", + "threemaSenderIdentity": "Sender", + "threemaSenderIdentityFormat": "8 characters, usually starts with '*'", + "threemaApiAuthenticationSecret": "API authentication secret", + "threemaBasicModeInfo": "Note: This integration uses Threema Gateway in basic mode (server-based encryption). Further details can be found {0}." }