From ee6b3bb232886019422c84c1efeb9283cb15e0b2 Mon Sep 17 00:00:00 2001 From: Jonatan <190036+jcastro@users.noreply.github.com> Date: Fri, 16 Oct 2026 20:39:43 +0200 Subject: [PATCH 1/3] Clarify Slack and Matrix setup and preserve Apprise recovery severity --- .../tests/test_command_descriptions.py | 5 + .github/workflows/test-apprise-services.yml | 50 +++++++ AppImage/components/apprise-service-help.tsx | 31 ++++ AppImage/components/notification-settings.tsx | 2 + AppImage/messages/en/common.json | 6 + AppImage/scripts/notification_channels.py | 2 +- tests/test_apprise_service_help.cjs | 30 ++++ tests/test_apprise_services.py | 137 ++++++++++++++++++ 8 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/test-apprise-services.yml create mode 100644 AppImage/components/apprise-service-help.tsx create mode 100644 tests/test_apprise_service_help.cjs create mode 100644 tests/test_apprise_services.py diff --git a/.github/scripts/tests/test_command_descriptions.py b/.github/scripts/tests/test_command_descriptions.py index ec35c3b2..019ccb0c 100644 --- a/.github/scripts/tests/test_command_descriptions.py +++ b/.github/scripts/tests/test_command_descriptions.py @@ -200,6 +200,11 @@ class CommandDescriptionsTests(unittest.TestCase): if lang != "en": messages = json.loads(path.read_text()) english = catalog("en") + # Provider help ships in English before the translation bot. + for key in ("appriseServiceHelp", "appriseSlackSteps", "appriseSlackDocs", + "appriseMatrixSteps", "appriseMatrixDocs", "appriseServiceFinish"): + messages["settings"]["notifications"]["ui"].setdefault( + key, english["settings"]["notifications"]["ui"][key]) for section, key in (("encryption", "pbsHelp"), ("keyfileActions", "pveKeyDescription")): messages["backup"][section].setdefault(key, english["backup"][section][key]) diff --git a/.github/workflows/test-apprise-services.yml b/.github/workflows/test-apprise-services.yml new file mode 100644 index 00000000..59df1fd0 --- /dev/null +++ b/.github/workflows/test-apprise-services.yml @@ -0,0 +1,50 @@ +name: Apprise Slack and Matrix + +on: + pull_request: + paths: + - '.github/workflows/test-apprise-services.yml' + - 'AppImage/scripts/notification_channels.py' + - 'AppImage/components/apprise-service-help.tsx' + - 'AppImage/components/notification-settings.tsx' + - 'tests/test_apprise_services.py' + - 'tests/test_apprise_service_help.cjs' + push: + branches: [main, develop] + paths: + - 'AppImage/scripts/notification_channels.py' + - 'AppImage/components/apprise-service-help.tsx' + - 'tests/test_apprise_services.py' + - 'tests/test_apprise_service_help.cjs' + workflow_dispatch: + +permissions: + contents: read + +jobs: + apprise-services: + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + matrix: + apprise: ['1.7.0', '2.0.1'] + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - uses: actions/setup-node@v6 + with: + node-version: '22.14.0' + - name: Install fixture dependencies + run: | + python -m pip install 'apprise==${{ matrix.apprise }}' + npm ci --prefix .github/ci/offline-node --ignore-scripts --no-audit --no-fund + - name: Test real provider plugins with mocked HTTP + run: python -B -m unittest discover -s tests -p test_apprise_services.py -v + - name: Render provider help and locale fallback + env: + NODE_PATH: ${{ github.workspace }}/.github/ci/offline-node/node_modules + run: node tests/test_apprise_service_help.cjs diff --git a/AppImage/components/apprise-service-help.tsx b/AppImage/components/apprise-service-help.tsx new file mode 100644 index 00000000..db6eae71 --- /dev/null +++ b/AppImage/components/apprise-service-help.tsx @@ -0,0 +1,31 @@ +"use client" + +import { useT } from "../lib/i18n/provider" + +// These are examples, never defaults: opening help must not replace a saved +// destination or put placeholder credentials into the notification settings. +export function AppriseServiceHelp() { + const t = useT() + return
+ {t("settings.notifications.ui.appriseServiceHelp")} +
+
+

Slack

+

{t("settings.notifications.ui.appriseSlackSteps")}

+ https://hooks.slack.com/services/TOKEN_A/TOKEN_B/TOKEN_C + + {t("settings.notifications.ui.appriseSlackDocs")} + +
+
+

Matrix

+

{t("settings.notifications.ui.appriseMatrixSteps")}

+ matrixs://USER:PASSWORD@matrix.example.com/!ROOM_ID:matrix.example.com + + {t("settings.notifications.ui.appriseMatrixDocs")} + +
+
+

{t("settings.notifications.ui.appriseServiceFinish")}

+
+} diff --git a/AppImage/components/notification-settings.tsx b/AppImage/components/notification-settings.tsx index ac9733f1..e84590ee 100644 --- a/AppImage/components/notification-settings.tsx +++ b/AppImage/components/notification-settings.tsx @@ -1,5 +1,6 @@ "use client" +import { AppriseServiceHelp } from "./apprise-service-help" import { useState, useEffect, useCallback } from "react" import { useTheme } from "next-themes" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card" @@ -2061,6 +2062,7 @@ export function NotificationSettings() { Enable toggle + Test button pattern as the other channels. */} +
diff --git a/AppImage/messages/en/common.json b/AppImage/messages/en/common.json index ca7e536d..157093e4 100644 --- a/AppImage/messages/en/common.json +++ b/AppImage/messages/en/common.json @@ -2094,6 +2094,12 @@ "pushoverCredentialsHint": "Create an application in Pushover and copy its API token and your user or group key.", "enableApprise": "Enable Apprise", "urlFormats": "+URL formats", + "appriseServiceHelp": "Slack and Matrix setup", + "appriseSlackSteps": "Create an Incoming Webhook in your Slack app for the target channel, then paste its complete HTTPS URL into Apprise URL. No conversion is needed.", + "appriseSlackDocs": "Slack URL formats and setup", + "appriseMatrixSteps": "Use a notification account that can post to the room. Replace the example user, password, homeserver and full room ID; URL-encode special characters in credentials. The matrixs scheme uses HTTPS. See the documentation for token, SSO and encrypted-room support in your installed Apprise version.", + "appriseMatrixDocs": "Matrix URL formats and setup", + "appriseServiceFinish": "Enable Apprise, enter your URL, choose the notification categories or individual events, save, then use Send Test. These examples do not change your saved URL. This Apprise channel has one destination; use Apprise API for several services at once.", "appriseUrl": "Apprise URL", "showUrl": "Show URL", "hideUrl": "Hide URL", diff --git a/AppImage/scripts/notification_channels.py b/AppImage/scripts/notification_channels.py index c2bff230..ba9aaa4d 100644 --- a/AppImage/scripts/notification_channels.py +++ b/AppImage/scripts/notification_channels.py @@ -1564,7 +1564,7 @@ class AppriseChannel(NotificationChannel): return apprise_mod.NotifyType.FAILURE if sev == 'WARNING': return apprise_mod.NotifyType.WARNING - if sev == 'SUCCESS': + if sev in ('SUCCESS', 'OK'): return apprise_mod.NotifyType.SUCCESS return apprise_mod.NotifyType.INFO diff --git a/tests/test_apprise_service_help.cjs b/tests/test_apprise_service_help.cjs new file mode 100644 index 00000000..d2f0d131 --- /dev/null +++ b/tests/test_apprise_service_help.cjs @@ -0,0 +1,30 @@ +// Render the real help component; only its translation provider is replaced. +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const vm = require('node:vm'); +const ts = require('typescript'); +const React = require('react'); +const { renderToStaticMarkup } = require('react-dom/server'); +const source = fs.readFileSync('AppImage/components/apprise-service-help.tsx', 'utf8'); +const js = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, jsx: ts.JsxEmit.ReactJSX } }).outputText; +const english = JSON.parse(fs.readFileSync('AppImage/messages/en/common.json')); +function resolve(catalog, key) { return key.split('.').reduce((value, part) => value?.[part], catalog); } +for (const locale of ['en', 'es', 'de', 'fr', 'it', 'pt', 'sk', 'sv', 'missing']) { + const catalog = locale === 'missing' ? {} : JSON.parse(fs.readFileSync(`AppImage/messages/${locale}/common.json`)); + const context = { exports: {}, require: name => name === '../lib/i18n/provider' ? { + useT: () => key => resolve(catalog, key) ?? resolve(english, key), + } : require(name) }; + vm.runInNewContext(js, context); + const html = renderToStaticMarkup(React.createElement(context.exports.AppriseServiceHelp)); + assert.ok(html.includes('https://hooks.slack.com/services/TOKEN_A/TOKEN_B/TOKEN_C')); + assert.ok(html.includes('matrixs://USER:PASSWORD@matrix.example.com/!ROOM_ID:matrix.example.com')); + assert.ok(html.includes('https://appriseit.com/services/slack/')); + assert.ok(html.includes('https://appriseit.com/services/matrix/')); + assert.ok(!html.includes('settings.notifications.'), `${locale}: no unresolved labels`); +} +for (const [, key] of source.matchAll(/t\("([^"]+)"\)/g)) assert.ok(resolve(english, key), key); +assert.ok(!source.includes('onChange') && !source.includes('fetch(') && !source.includes('updateChannel')); +const settings = fs.readFileSync('AppImage/components/notification-settings.tsx', 'utf8'); +const section = settings.slice(settings.indexOf('', settings.indexOf('', 'renderChannelCategories("apprise")', 'handleTest("apprise")']) assert.ok(section.includes(part)); +console.log('PASS: real provider-help JSX, shipped locales and English fallback, no secret mutation, native category/test controls retained'); diff --git a/tests/test_apprise_services.py b/tests/test_apprise_services.py new file mode 100644 index 00000000..fffb158e --- /dev/null +++ b/tests/test_apprise_services.py @@ -0,0 +1,137 @@ +"""Real Apprise Slack/Matrix plugins with the HTTP boundary replaced. + +No provider account or network is used. These contracts run against both +the minimum bundled Apprise version and the current tested version. +""" +import gc +import json +from pathlib import Path +import sys +import unittest +from unittest.mock import patch +from urllib.parse import urlparse + +import apprise +import requests + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "AppImage/scripts")) +from notification_channels import create_channel + +SLACK = "https://hooks.slack.com/services/T00000000/B00000000/fixturesecret1234567890" +MATRIX = "matrixs://notify:fixture-password@matrix.example.test/!room:matrix.example.test" + + +class AppriseServiceTests(unittest.TestCase): + def setUp(self): + self.calls = [] + self.status = 200 + self.transport = patch.object(requests.Session, "request", side_effect=self.http) + self.transport.start() + self.addCleanup(self.transport.stop) + self.sleep = patch("notification_channels.time.sleep") + self.sleep.start() + self.addCleanup(self.sleep.stop) + self.addCleanup(gc.collect) + + def http(self, method, url, **kwargs): + self.calls.append((method, url, kwargs)) + response = requests.Response() + response.status_code = self.status + response.url = url + response.reason = "OK" if self.status == 200 else "Rejected" + response._content_consumed = True + host, path = urlparse(url).hostname, urlparse(url).path + if host == "hooks.slack.com": + self.assertTrue(path.startswith("/services/")) + response._content = b"ok" if self.status == 200 else b"invalid_auth" + else: + self.assertEqual(host, "matrix.example.test", "no unexpected destinations") + if self.status != 200: + payload = {"errcode": "M_FORBIDDEN", "error": "Invalid credentials"} + elif path == "/.well-known/matrix/client": + payload = {"m.homeserver": {"base_url": "https://" + host}} + elif path.endswith("/login"): + payload = {"access_token": "fixture-token", "home_server": host, "user_id": "@notify:" + host} + elif path.endswith("/joined_rooms"): + payload = {"joined_rooms": ["!room:" + host]} + elif "/join/" in path: + payload = {"room_id": "!room:" + host} + elif "/send/" in path: + payload = {"event_id": "$fixture"} + elif path.endswith("/logout"): + payload = {} + elif path.endswith("/versions"): + payload = {"versions": ["v1.1"]} + else: + raise AssertionError("Unhandled Matrix request: " + path) + response._content = json.dumps(payload).encode() + return response + + def send(self, url): + channel = create_channel("apprise", {"url": url}) + self.assertIsNotNone(channel) + return channel.send("ProxMenux probe", "Controlled message", "WARNING") + + def test_slack_accepts_the_native_webhook_without_conversion(self): + result = self.send(SLACK) + self.assertTrue(result["success"], result) + self.assertTrue(any(url == SLACK for _, url, _ in self.calls)) + self.assertIn("Controlled message", str(self.calls)) + + def test_slack_apprise_scheme_uses_the_same_transport(self): + result = self.send(SLACK.replace("https://hooks.slack.com/services/", "slack://")) + self.assertTrue(result["success"], result) + self.assertTrue(any(url == SLACK for _, url, _ in self.calls)) + + def test_matrix_authenticates_and_delivers_to_the_full_room_id_over_https(self): + result = self.send(MATRIX) + self.assertTrue(result["success"], result) + sends = [(url, kwargs) for _, url, kwargs in self.calls if "/send/" in url] + self.assertTrue(sends, self.calls) + self.assertTrue(all(url.startswith("https://matrix.example.test/") for url, _ in sends)) + self.assertTrue(all(kwargs.get("verify", True) for _, kwargs in sends)) + self.assertIn("Controlled message", str(sends)) + self.assertTrue(any(url.endswith("/login") for _, url, _ in self.calls)) + + def test_test_button_contract_uses_the_same_provider(self): + ok, error = create_channel("apprise", {"url": SLACK}).test() + self.assertTrue(ok, error) + self.assertTrue(self.calls) + + def test_rejected_slack_webhook_is_not_reported_as_delivered(self): + self.status = 403 + result = self.send(SLACK) + self.assertFalse(result["success"]) + self.assertTrue(result["error"]) + + def test_invalid_matrix_credentials_are_not_reported_as_delivered(self): + self.status = 403 + result = self.send(MATRIX) + self.assertFalse(result["success"]) + self.assertFalse(any("/send/" in url for _, url, _ in self.calls)) + + def test_invalid_urls_and_missing_library_fail_before_any_request(self): + channel = create_channel("apprise", {"url": "not-a-url"}) + self.assertFalse(channel.send("Probe", "Body")["success"]) + channel = create_channel("apprise", {"url": SLACK}) + with patch.object(channel, "_load_apprise", return_value=None): + self.assertFalse(channel.send("Probe", "Body")["success"]) + self.assertEqual(self.calls, []) + + def test_connection_failure_never_claims_delivery(self): + for url in (SLACK, MATRIX): + with patch.object(requests.Session, "request", side_effect=requests.ConnectionError("fixture connection failure")): + result = self.send(url) + self.assertFalse(result["success"]) + self.assertTrue(result["error"]) + + def test_severity_mapping_keeps_native_notification_semantics(self): + channel = create_channel("apprise", {"url": SLACK}) + for severity, expected in (("CRITICAL", apprise.NotifyType.FAILURE), ("WARNING", apprise.NotifyType.WARNING), + ("INFO", apprise.NotifyType.INFO), ("OK", apprise.NotifyType.SUCCESS), + ("SUCCESS", apprise.NotifyType.SUCCESS)): + self.assertEqual(channel._severity_to_notify_type(apprise, severity), expected) + + +if __name__ == "__main__": + unittest.main() From 8772b286da35c175f0602620f60ac04c8d4f7b1c Mon Sep 17 00:00:00 2001 From: Jonatan <190036+jcastro@users.noreply.github.com> Date: Fri, 16 Oct 2026 23:00:38 +0200 Subject: [PATCH 2/3] Complete translations for all supported languages --- AppImage/messages/de/common.json | 8 +++++++- AppImage/messages/es/common.json | 8 +++++++- AppImage/messages/fr/common.json | 8 +++++++- AppImage/messages/it/common.json | 8 +++++++- AppImage/messages/pt/common.json | 8 +++++++- AppImage/messages/sk/common.json | 8 +++++++- AppImage/messages/sv/common.json | 8 +++++++- 7 files changed, 49 insertions(+), 7 deletions(-) diff --git a/AppImage/messages/de/common.json b/AppImage/messages/de/common.json index 4093529a..b726a4bc 100644 --- a/AppImage/messages/de/common.json +++ b/AppImage/messages/de/common.json @@ -2108,7 +2108,13 @@ "inactiveFooter": "Aktivieren Sie Benachrichtigungen, um System-, Gesundheits- und Sicherheitswarnungen über die konfigurierten Kanäle zu erhalten.", "notificationLanguage": "Benachrichtigungssprache", "selectNotificationLanguage": "Benachrichtigungssprache auswählen", - "notificationLanguageHint": "Wird für deterministische Laufzeitbenachrichtigungen verwendet, auch wenn KI deaktiviert ist." + "notificationLanguageHint": "Wird für deterministische Laufzeitbenachrichtigungen verwendet, auch wenn KI deaktiviert ist.", + "appriseServiceHelp": "Slack und Matrix einrichten", + "appriseSlackSteps": "Erstellen Sie in Ihrer Slack-App einen eingehenden Webhook für den Zielkanal und fügen Sie dessen vollständige HTTPS-URL in das Feld Apprise-URL ein. Eine Umwandlung ist nicht erforderlich.", + "appriseSlackDocs": "Slack-URL-Formate und Einrichtung", + "appriseMatrixSteps": "Verwenden Sie ein Benachrichtigungskonto, das in den Raum schreiben darf. Ersetzen Sie den Beispielbenutzer, das Passwort, den Homeserver und die vollständige Raum-ID; kodieren Sie Sonderzeichen in Zugangsdaten für URLs. Das Schema matrixs verwendet HTTPS. Informationen zur Unterstützung von Tokens, SSO und verschlüsselten Räumen in Ihrer installierten Apprise-Version finden Sie in der Dokumentation.", + "appriseMatrixDocs": "Matrix-URL-Formate und Einrichtung", + "appriseServiceFinish": "Aktivieren Sie Apprise, geben Sie Ihre URL ein, wählen Sie Benachrichtigungskategorien oder einzelne Ereignisse aus, speichern Sie und verwenden Sie anschließend Test senden. Diese Beispiele ändern Ihre gespeicherte URL nicht. Dieser Apprise-Kanal hat ein Ziel; verwenden Sie die Apprise-API für mehrere Dienste gleichzeitig." }, "ai": { "title": "KI-Verbesserung", diff --git a/AppImage/messages/es/common.json b/AppImage/messages/es/common.json index 166bf6a5..9e939506 100644 --- a/AppImage/messages/es/common.json +++ b/AppImage/messages/es/common.json @@ -2108,7 +2108,13 @@ "inactiveFooter": "Habilite notificaciones para recibir alertas del sistema, de salud y de seguridad a través de los canales configurados.", "notificationLanguage": "Idioma de notificación", "selectNotificationLanguage": "Seleccione el idioma de notificación", - "notificationLanguageHint": "Se utiliza para notificaciones deterministas en tiempo de ejecución incluso cuando la IA está deshabilitada." + "notificationLanguageHint": "Se utiliza para notificaciones deterministas en tiempo de ejecución incluso cuando la IA está deshabilitada.", + "appriseServiceHelp": "Configuración de Slack y Matrix", + "appriseSlackSteps": "Crea un webhook entrante en tu aplicación de Slack para el canal de destino y pega su URL HTTPS completa en URL de Apprise. No hace falta convertirla.", + "appriseSlackDocs": "Formatos de URL y configuración de Slack", + "appriseMatrixSteps": "Utiliza una cuenta de notificaciones que pueda publicar en la sala. Sustituye el usuario, la contraseña, el servidor de origen y el ID completo de la sala del ejemplo; codifica para URL los caracteres especiales de las credenciales. El esquema matrixs utiliza HTTPS. Consulta en la documentación la compatibilidad con tokens, SSO y salas cifradas de tu versión de Apprise.", + "appriseMatrixDocs": "Formatos de URL y configuración de Matrix", + "appriseServiceFinish": "Activa Apprise, introduce tu URL, elige las categorías de notificación o los eventos individuales, guarda y utiliza Enviar prueba. Estos ejemplos no cambian tu URL guardada. Este canal de Apprise tiene un único destino; utiliza la API de Apprise para varios servicios a la vez." }, "ai": { "title": "mejora de la IA", diff --git a/AppImage/messages/fr/common.json b/AppImage/messages/fr/common.json index 80582c89..80c11bdc 100644 --- a/AppImage/messages/fr/common.json +++ b/AppImage/messages/fr/common.json @@ -2108,7 +2108,13 @@ "inactiveFooter": "Activez les notifications pour recevoir des alertes système, de santé et de sécurité via les canaux configurés.", "notificationLanguage": "Langue de notification", "selectNotificationLanguage": "Sélectionnez la langue de notification", - "notificationLanguageHint": "Utilisé pour les notifications d'exécution déterministes, même lorsque l'IA est désactivée." + "notificationLanguageHint": "Utilisé pour les notifications d'exécution déterministes, même lorsque l'IA est désactivée.", + "appriseServiceHelp": "Configuration de Slack et Matrix", + "appriseSlackSteps": "Créez un webhook entrant dans votre application Slack pour le canal cible, puis collez son URL HTTPS complète dans le champ URL Apprise. Aucune conversion n'est nécessaire.", + "appriseSlackDocs": "Formats d'URL et configuration de Slack", + "appriseMatrixSteps": "Utilisez un compte de notification autorisé à publier dans le salon. Remplacez l'utilisateur, le mot de passe, le serveur d'origine et l'identifiant complet du salon de l'exemple ; encodez les caractères spéciaux des identifiants pour les URL. Le schéma matrixs utilise HTTPS. Consultez la documentation pour la prise en charge des jetons, du SSO et des salons chiffrés dans votre version d'Apprise.", + "appriseMatrixDocs": "Formats d'URL et configuration de Matrix", + "appriseServiceFinish": "Activez Apprise, saisissez votre URL, choisissez les catégories de notification ou les événements individuels, enregistrez, puis utilisez Envoyer un test. Ces exemples ne modifient pas votre URL enregistrée. Ce canal Apprise possède une destination ; utilisez l'API Apprise pour plusieurs services à la fois." }, "ai": { "title": "Amélioration de l'IA", diff --git a/AppImage/messages/it/common.json b/AppImage/messages/it/common.json index 27a5a76b..1b486c31 100644 --- a/AppImage/messages/it/common.json +++ b/AppImage/messages/it/common.json @@ -2108,7 +2108,13 @@ "inactiveFooter": "Abilita le notifiche per ricevere avvisi di sistema, integrità e sicurezza attraverso i canali configurati.", "notificationLanguage": "Lingua notifiche", "selectNotificationLanguage": "Seleziona lingua notifiche", - "notificationLanguageHint": "Usata per le notifiche runtime deterministiche anche quando l’IA è disabilitata." + "notificationLanguageHint": "Usata per le notifiche runtime deterministiche anche quando l’IA è disabilitata.", + "appriseServiceHelp": "Configurazione di Slack e Matrix", + "appriseSlackSteps": "Crea un webhook in ingresso nell'app Slack per il canale di destinazione, quindi incolla il suo URL HTTPS completo nel campo URL di Apprise. Non è necessaria alcuna conversione.", + "appriseSlackDocs": "Formati URL e configurazione di Slack", + "appriseMatrixSteps": "Usa un account di notifica autorizzato a pubblicare nella stanza. Sostituisci utente, password, homeserver e ID completo della stanza dell'esempio; codifica per URL i caratteri speciali nelle credenziali. Lo schema matrixs usa HTTPS. Consulta la documentazione per il supporto di token, SSO e stanze cifrate nella versione di Apprise installata.", + "appriseMatrixDocs": "Formati URL e configurazione di Matrix", + "appriseServiceFinish": "Abilita Apprise, inserisci il tuo URL, scegli le categorie di notifica o i singoli eventi, salva e poi usa Invia test. Questi esempi non modificano l'URL salvato. Questo canale Apprise ha una sola destinazione; usa l'API Apprise per più servizi contemporaneamente." }, "ai": { "title": "Miglioramento con IA", diff --git a/AppImage/messages/pt/common.json b/AppImage/messages/pt/common.json index 2c688625..44e2aafd 100644 --- a/AppImage/messages/pt/common.json +++ b/AppImage/messages/pt/common.json @@ -2108,7 +2108,13 @@ "inactiveFooter": "Habilite notificações para receber alertas de sistema, integridade e segurança por meio dos canais configurados.", "notificationLanguage": "Idioma de notificação", "selectNotificationLanguage": "Selecione o idioma de notificação", - "notificationLanguageHint": "Usado para notificações determinísticas de tempo de execução, mesmo quando a IA está desabilitada." + "notificationLanguageHint": "Usado para notificações determinísticas de tempo de execução, mesmo quando a IA está desabilitada.", + "appriseServiceHelp": "Configuração do Slack e do Matrix", + "appriseSlackSteps": "Crie um webhook de entrada na sua aplicação Slack para o canal de destino e cole o seu URL HTTPS completo no campo URL do Apprise. Não é necessária qualquer conversão.", + "appriseSlackDocs": "Formatos de URL e configuração do Slack", + "appriseMatrixSteps": "Utilize uma conta de notificações que possa publicar na sala. Substitua o utilizador, a palavra-passe, o homeserver e o ID completo da sala do exemplo; codifique os caracteres especiais das credenciais para URL. O esquema matrixs usa HTTPS. Consulte a documentação sobre o suporte de tokens, SSO e salas cifradas na versão do Apprise instalada.", + "appriseMatrixDocs": "Formatos de URL e configuração do Matrix", + "appriseServiceFinish": "Ative o Apprise, introduza o seu URL, escolha as categorias de notificação ou os eventos individuais, guarde e utilize Enviar teste. Estes exemplos não alteram o URL guardado. Este canal do Apprise tem um destino; utilize a API do Apprise para vários serviços em simultâneo." }, "ai": { "title": "Aprimoramento de IA", diff --git a/AppImage/messages/sk/common.json b/AppImage/messages/sk/common.json index dc5b7b80..fe0316ee 100644 --- a/AppImage/messages/sk/common.json +++ b/AppImage/messages/sk/common.json @@ -2107,7 +2107,13 @@ "inactiveFooter": "Zapnite notifikácie a dostávajte upozornenia o systéme, stave a bezpečnosti cez nastavené kanály.", "notificationLanguage": "Jazyk notifikácií", "selectNotificationLanguage": "Vyberte jazyk notifikácií", - "notificationLanguageHint": "Používa sa pre spoľahlivé runtime notifikácie aj vtedy, keď je AI vypnutá." + "notificationLanguageHint": "Používa sa pre spoľahlivé runtime notifikácie aj vtedy, keď je AI vypnutá.", + "appriseServiceHelp": "Nastavenie Slack a Matrix", + "appriseSlackSteps": "V aplikácii Slack vytvorte prichádzajúci webhook pre cieľový kanál a vložte jeho úplnú HTTPS URL do poľa URL Apprise. Nie je potrebný žiadny prevod.", + "appriseSlackDocs": "Formáty URL a nastavenie Slack", + "appriseMatrixSteps": "Použite účet pre oznámenia, ktorý môže odosielať správy do miestnosti. Nahraďte vzorového používateľa, heslo, domovský server a úplné ID miestnosti; špeciálne znaky v prihlasovacích údajoch zakódujte pre URL. Schéma matrixs používa HTTPS. Podporu tokenov, SSO a šifrovaných miestností v nainštalovanej verzii Apprise nájdete v dokumentácii.", + "appriseMatrixDocs": "Formáty URL a nastavenie Matrix", + "appriseServiceFinish": "Povoľte Apprise, zadajte svoju URL, vyberte kategórie oznámení alebo jednotlivé udalosti, uložte a potom použite Odoslať test. Tieto príklady nemenia uloženú URL. Tento kanál Apprise má jeden cieľ; pre viac služieb naraz použite API Apprise." }, "ai": { "title": "AI vylepšenie", diff --git a/AppImage/messages/sv/common.json b/AppImage/messages/sv/common.json index a40378c9..83be229d 100644 --- a/AppImage/messages/sv/common.json +++ b/AppImage/messages/sv/common.json @@ -2108,7 +2108,13 @@ "inactiveFooter": "Aktivera aviseringar för att ta emot system-, hälso- och säkerhetsvarningar via de konfigurerade kanalerna.", "notificationLanguage": "Aviseringsspråk", "selectNotificationLanguage": "Välj språk för meddelanden", - "notificationLanguageHint": "Används för deterministiska körtidsmeddelanden även när AI är inaktiverat." + "notificationLanguageHint": "Används för deterministiska körtidsmeddelanden även när AI är inaktiverat.", + "appriseServiceHelp": "Konfiguration av Slack och Matrix", + "appriseSlackSteps": "Skapa en inkommande webhook i din Slack-app för målkanalen och klistra sedan in dess fullständiga HTTPS-URL i fältet Apprise-URL. Ingen omvandling behövs.", + "appriseSlackDocs": "URL-format och konfiguration för Slack", + "appriseMatrixSteps": "Använd ett aviseringskonto som får skicka meddelanden till rummet. Ersätt exemplets användare, lösenord, hemserver och fullständiga rums-ID; URL-koda specialtecken i inloggningsuppgifterna. Schemat matrixs använder HTTPS. Se dokumentationen för stöd för token, SSO och krypterade rum i din installerade Apprise-version.", + "appriseMatrixDocs": "URL-format och konfiguration för Matrix", + "appriseServiceFinish": "Aktivera Apprise, ange din URL, välj aviseringskategorier eller enskilda händelser, spara och använd sedan Skicka test. Dessa exempel ändrar inte din sparade URL. Denna Apprise-kanal har ett mål; använd Apprise-API för flera tjänster samtidigt." }, "ai": { "title": "AI-förbättring", From 9a94045c33d262bd2f645014596dada6dfab8373 Mon Sep 17 00:00:00 2001 From: MacRimi <123239993+MacRimi@users.noreply.github.com> Date: Fri, 16 Oct 2026 23:25:01 +0200 Subject: [PATCH 3/3] Show the external link icon on the Apprise service help links --- AppImage/components/apprise-service-help.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/AppImage/components/apprise-service-help.tsx b/AppImage/components/apprise-service-help.tsx index db6eae71..de9d132b 100644 --- a/AppImage/components/apprise-service-help.tsx +++ b/AppImage/components/apprise-service-help.tsx @@ -1,5 +1,6 @@ "use client" +import { ExternalLink } from "lucide-react" import { useT } from "../lib/i18n/provider" // These are examples, never defaults: opening help must not replace a saved @@ -13,16 +14,16 @@ export function AppriseServiceHelp() {

Slack

{t("settings.notifications.ui.appriseSlackSteps")}

https://hooks.slack.com/services/TOKEN_A/TOKEN_B/TOKEN_C - - {t("settings.notifications.ui.appriseSlackDocs")} + + {t("settings.notifications.ui.appriseSlackDocs")}

Matrix

{t("settings.notifications.ui.appriseMatrixSteps")}

matrixs://USER:PASSWORD@matrix.example.com/!ROOM_ID:matrix.example.com - - {t("settings.notifications.ui.appriseMatrixDocs")} + + {t("settings.notifications.ui.appriseMatrixDocs")}