From 55e398dd105aa1443d30a44e251a6616d0b9a638 Mon Sep 17 00:00:00 2001 From: Jokob-sk Date: Sun, 22 Oct 2023 09:29:25 +1100 Subject: [PATCH] =?UTF-8?q?WEBHOOK=20conversion=20+=20cleanup=20work?= =?UTF-8?q?=F0=9F=8E=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/php/templates/language/en_us.json | 22 - front/plugins/README.md | 49 ++- front/plugins/_publisher_apprise/apprise.py | 2 +- front/plugins/_publisher_email/email_smtp.py | 2 +- front/plugins/_publisher_ntfy/ntfy.py | 2 +- .../plugins/_publisher_pushsafer/pushsafer.py | 13 +- front/plugins/_publisher_webhook/README.md | 8 + front/plugins/_publisher_webhook/config.json | 404 ++++++++++++++++++ .../plugins/_publisher_webhook/ignore_plugin | 1 - front/plugins/_publisher_webhook/webhook.py | 190 ++++++++ pialert/README.md | 11 - pialert/README_ES.md | 20 - pialert/conf.py | 16 - pialert/helper.py | 8 + pialert/initialise.py | 11 - pialert/publishers/webhook.py | 2 +- pialert/reporting.py | 18 +- 17 files changed, 652 insertions(+), 127 deletions(-) create mode 100755 front/plugins/_publisher_webhook/README.md create mode 100755 front/plugins/_publisher_webhook/config.json delete mode 100755 front/plugins/_publisher_webhook/ignore_plugin create mode 100755 front/plugins/_publisher_webhook/webhook.py diff --git a/front/php/templates/language/en_us.json b/front/php/templates/language/en_us.json index 8c7ff7d61..01b4081d7 100755 --- a/front/php/templates/language/en_us.json +++ b/front/php/templates/language/en_us.json @@ -513,30 +513,8 @@ "REPORT_MAIL_name" : "Enable email", "REPORT_MAIL_description" : "If enabled an email is sent out with a list of changes you nove subscribed to. Please also fill out all remaining settings related to the SMTP setup below. If facing issues, set LOG_LEVEL to debug and check the error log.", "SYSTEM_TITLE" : "System Information", - - "REPORT_TO_name" : "deprecated", - "REPORT_TO_description" : "deprecated", - "REPORT_FROM_name" : "deprecated", - "REPORT_FROM_description" : "deprecated", - - "Webhooks_display_name" : "Webhooks", - "Webhooks_icon" : "", - "REPORT_WEBHOOK_name" : "Enable Webhooks", - "REPORT_WEBHOOK_description" : "Enable webhooks for notifications. Webhooks help you to connect to a lot of 3rd party tools, such as IFTTT, Zapier or n8n to name a few. Check out this simple n8n guide here to get started. If enabled, configure related settings below.", - "WEBHOOK_URL_name" : "Target URL", - "WEBHOOK_URL_description" : "Target URL starting with http:// or https://.", - "WEBHOOK_PAYLOAD_name" : "Payload type", - "WEBHOOK_PAYLOAD_description" : "The Webhook payload data format for the body > attachments > text attribute in the payload json. See an example of the payload here. (e.g.: for discord use text)", - "WEBHOOK_REQUEST_METHOD_name" : "Request method", - "WEBHOOK_REQUEST_METHOD_description" : "The HTTP request method to be used for the webhook call.", - "WEBHOOK_SIZE_name" : "Max payload size", - "WEBHOOK_SIZE_description" : "The maximum size of the webhook payload as number of characters in the passed string. If above limit, it will be truncated and a (text was truncated) message is appended.", - "WEBHOOK_SECRET_name": "HMAC Secret", - "WEBHOOK_SECRET_description": "When set, use this secret to generate the SHA256-HMAC hex digest value of the request body, which will be passed as the X-Webhook-Signature header to the request. You can find more informations here.", - "REPORT_TITLE" : "Report", "REPORT_ERROR" : "The page you are looking for is temporarily unavailable, please try again after a few seconds", - "API_display_name" : "API", "API_icon" : "", "API_CUSTOM_SQL_name" : "Custom endpoint", diff --git a/front/plugins/README.md b/front/plugins/README.md index 9764f70da..a142a40f3 100755 --- a/front/plugins/README.md +++ b/front/plugins/README.md @@ -14,27 +14,33 @@ ### 🔌 Plugins & 📚 Docs -| Required | CurrentScan | Unique Prefix | Plugin Type | Link + Docs | -|-------------|-------------|-----------------------|------------------------|-----------------------------------------------------------| -| | Yes | ARPSCAN | Script | 📚[arp_scan](/front/plugins/arp_scan/) | -| | | CSVBCKP | Script | 📚[csv_backup](/front/plugins/csv_backup/) | -| Yes* | | DBCLNP | Script | 📚[db_cleanup](/front/plugins/db_cleanup/) | -| | | DDNS | Script | 📚[ddns_update](/front/plugins/ddns_update/) | -| | Yes | DHCPLSS | Script | 📚[dhcp_leases](/front/plugins/dhcp_leases/) | -| | | DHCPSRVS | Script | 📚[dhcp_servers](/front/plugins/dhcp_servers/) | -| | Yes | INTRNT | Script | 📚[internet_ip](/front/plugins/internet_ip/) | -| | | INTRSPD | Script | 📚[internet_speedtest](/front/plugins/internet_speedtest/)| -| Yes | | NEWDEV | Template | 📚[newdev_template](/front/plugins/newdev_template/) | -| | | PHOLUS | Script | 📚[pholus_scan](/front/plugins/pholus_scan/) | -| | | NMAP | Script | 📚[nmap_scan](/front/plugins/nmap_scan/) | -| | Yes | PIHOLE | External SQLite DB | 📚[pihole_scan](/front/plugins/pihole_scan/) | -| | | SETPWD | Script | 📚[set_password](/front/plugins/set_password/) | -| | | SNMPDSC | Script | 📚[snmp_discovery](/front/plugins/snmp_discovery/) | -| | Yes** | UNDIS | Script | 📚[undiscoverables](/front/plugins/undiscoverables/) | -| | Yes | UNFIMP | Script | 📚[unifi_import](/front/plugins/unifi_import/) | -| | | VNDRPDT | Script | 📚[vendor_update](/front/plugins/vendor_update/) | -| | | WEBMON | Script | 📚[website_monitor](/front/plugins/website_monitor/) | -| N/A | | N/A | SQL query | N/A, but the External SQLite DB plugins work similar | +| Required | CurrentScan | Unique Prefix | Data source | Type | Link + Docs | +|----------|-------------|---------------|--------------------|--------------|------------------------------------------------------------------| +| | | APPRISE | Script | 💬 publisher | 📚[_publisher_apprise](/front/plugins/_publisher_apprise/) | +| | Yes | ARPSCAN | Script | 🔍scanner | 📚[arp_scan](/front/plugins/arp_scan/) | +| | | CSVBCKP | Script | ⚙ system | 📚[csv_backup](/front/plugins/csv_backup/) | +| Yes* | | DBCLNP | Script | ⚙ system | 📚[db_cleanup](/front/plugins/db_cleanup/) | +| | | DDNS | Script | ⚙ system | 📚[ddns_update](/front/plugins/ddns_update/) | +| | Yes | DHCPLSS | Script | 🔍scanner | 📚[dhcp_leases](/front/plugins/dhcp_leases/) | +| | | DHCPSRVS | Script | 🔍scanner | 📚[dhcp_servers](/front/plugins/dhcp_servers/) | +| | Yes | INTRNT | Script | 🔍scanner | 📚[internet_ip](/front/plugins/internet_ip/) | +| | | INTRSPD | Script | 🔍scanner | 📚[internet_speedtest](/front/plugins/internet_speedtest/) | +| Yes | | NEWDEV | Template | ⚙ system | 📚[newdev_template](/front/plugins/newdev_template/) | +| | | PHOLUS | Script | 🔍scanner | 📚[pholus_scan](/front/plugins/pholus_scan/) | +| | | MQTT | Script | 💬 publisher | 📚[_publisher_mqtt](/front/plugins/_publisher_mqtt/) | +| | | NMAP | Script | 🔍scanner | 📚[nmap_scan](/front/plugins/nmap_scan/) | +| | | NTFY | Script | 💬 publisher | 📚[_publisher_ntfy](/front/plugins/_publisher_ntfy/) | +| | Yes | PIHOLE | External SQLite DB | 🔍scanner | 📚[pihole_scan](/front/plugins/pihole_scan/) | +| | | PUSHSAFER | Script | 💬 publisher | 📚[_publisher_pushsafer](/front/plugins/_publisher_pushsafer/) | +| | | SETPWD | Script | ⚙ system | 📚[set_password](/front/plugins/set_password/) | +| | | SMTP | Script | 💬 publisher | 📚[_publisher_email](/front/plugins/_publisher_email/) | +| | | SNMPDSC | Script | 🔍scanner | 📚[snmp_discovery](/front/plugins/snmp_discovery/) | +| | Yes** | UNDIS | Script | 🔍scanner | 📚[undiscoverables](/front/plugins/undiscoverables/) | +| | Yes | UNFIMP | Script | 🔍scanner | 📚[unifi_import](/front/plugins/unifi_import/) | +| | | VNDRPDT | Script | ⚙ system | 📚[vendor_update](/front/plugins/vendor_update/) | +| | | WEBHOOK | Script | 💬 publisher | 📚[_publisher_webhook](/front/plugins/_publisher_webhook/) | +| | | WEBMON | Script | 🔍scanner | 📚[website_monitor](/front/plugins/website_monitor/) | +| N/A | | N/A | SQL query | | N/A, but the External SQLite DB plugins work similar | > \* The database cleanup plugin (`DBCLNP`) is not _required_ but the app will become unusable after a while if not executed. @@ -135,6 +141,7 @@ Currently, these data sources are supported (valid `data_source` value). | Pialert DB query | `pialert-db-query` | yes | Executes a SQL query on the PiAlert database in the `CMD` setting. | | Template | `template` | no | Used to generate internal settings, such as default values. | | External SQLite DB query | `sqlite-db-query` | yes | Executes a SQL query from the `CMD` setting on an external SQLite database mapped in the `DB_PATH` setting. | +| Plugin type | `plugin_type` | no | Specifies the type of the plugin and in which section the Plugin settings are displayed (`|||`). | > 🔎Example diff --git a/front/plugins/_publisher_apprise/apprise.py b/front/plugins/_publisher_apprise/apprise.py index 0e8b97edb..32c026b73 100755 --- a/front/plugins/_publisher_apprise/apprise.py +++ b/front/plugins/_publisher_apprise/apprise.py @@ -46,7 +46,7 @@ def main(): # Retrieve new notifications new_notifications = notifications.getNew() - # Process the new notifications + # Process the new notifications (see the Notifications DB table for structure or check the /api/table_notifications.json endpoint) for notification in new_notifications: # Send notification diff --git a/front/plugins/_publisher_email/email_smtp.py b/front/plugins/_publisher_email/email_smtp.py index b0f239054..0b6846555 100755 --- a/front/plugins/_publisher_email/email_smtp.py +++ b/front/plugins/_publisher_email/email_smtp.py @@ -54,7 +54,7 @@ def main(): # Retrieve new notifications new_notifications = notifications.getNew() - # Process the new notifications + # Process the new notifications (see the Notifications DB table for structure or check the /api/table_notifications.json endpoint) for notification in new_notifications: # Send notification diff --git a/front/plugins/_publisher_ntfy/ntfy.py b/front/plugins/_publisher_ntfy/ntfy.py index 2125e0e51..1d7945c81 100755 --- a/front/plugins/_publisher_ntfy/ntfy.py +++ b/front/plugins/_publisher_ntfy/ntfy.py @@ -49,7 +49,7 @@ def main(): # Retrieve new notifications new_notifications = notifications.getNew() - # Process the new notifications + # Process the new notifications (see the Notifications DB table for structure or check the /api/table_notifications.json endpoint) for notification in new_notifications: # Send notification diff --git a/front/plugins/_publisher_pushsafer/pushsafer.py b/front/plugins/_publisher_pushsafer/pushsafer.py index 8a8479636..9e35f98d4 100755 --- a/front/plugins/_publisher_pushsafer/pushsafer.py +++ b/front/plugins/_publisher_pushsafer/pushsafer.py @@ -17,7 +17,7 @@ import conf from plugin_helper import Plugin_Objects, handleEmpty from logger import mylog, append_line_to_file -from helper import timeNowTZ, get_setting_value +from helper import timeNowTZ, get_setting_value, hide_string from notification import Notification_obj from database import DB @@ -48,11 +48,11 @@ def main(): # Retrieve new notifications new_notifications = notifications.getNew() - # Process the new notifications + # Process the new notifications (see the Notifications DB table for structure or check the /api/table_notifications.json endpoint) for notification in new_notifications: # Send notification - response_text, response_status_code = send(notification["HTML"], notification["Text"]) + response_text, response_status_code = send(notification["Text"]) # Log result plugin_objects.add_object( @@ -76,6 +76,11 @@ def send(text): response_text = '' response_status_code = '' + token = get_setting_value('PUSHSAFER_TOKEN') + + mylog('verbose', [f'[{pluginName}] PUSHSAFER_TOKEN: "{hide_string(token)}"']) + + try: url = 'https://www.pushsafer.com/api' post_fields = { @@ -88,7 +93,7 @@ def send(text): "d" : 'a', "u" : get_setting_value('REPORT_DASHBOARD_URL'), "ut" : 'Open Pi.Alert', - "k" : get_setting_value('PUSHSAFER_TOKEN'), + "k" : token, } response = requests.post(url, data=post_fields) diff --git a/front/plugins/_publisher_webhook/README.md b/front/plugins/_publisher_webhook/README.md new file mode 100755 index 000000000..8b5fc8e00 --- /dev/null +++ b/front/plugins/_publisher_webhook/README.md @@ -0,0 +1,8 @@ +## Overview + +A plugin to publish a notification via the Webhook gateway. Webhooks help you to connect to a lot of 3rd party tools, such as IFTTT, Zapier or n8n to name a few. Check out this simple n8n guide here to get started. If enabled, configure related settings below. + +### Usage + +- Go to settings and fill in relevant details. + diff --git a/front/plugins/_publisher_webhook/config.json b/front/plugins/_publisher_webhook/config.json new file mode 100755 index 000000000..bb0db76a0 --- /dev/null +++ b/front/plugins/_publisher_webhook/config.json @@ -0,0 +1,404 @@ +{ + "code_name": "_publisher_webhook", + "unique_prefix": "WEBHOOK", + "plugin_type": "publisher", + "enabled": true, + "data_source": "script", + "show_ui": true, + "localized": ["display_name", "description", "icon"], + "display_name" : [ + { + "language_code": "en_us", + "string" : "Webhook publisher" + }, + { + "language_code": "es_es", + "string" : "Habilitar Webhook" + } + ], + "icon":[{ + "language_code": "en_us", + "string" : "" + }], + "description": [ + { + "language_code": "en_us", + "string" : "A plugin to publish a notification via Webhooks." + } + ], + "params" : [ + ], + "database_column_definitions": + [ + { + "column": "Index", + "css_classes": "col-sm-2", + "show": false, + "type": "label", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code": "en_us", + "string" : "N/A" + }, + { + "language_code": "es_es", + "string" : "N/A" + }] + }, + { + "column": "Plugin", + "css_classes": "col-sm-2", + "show": false, + "type": "label", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code": "en_us", + "string" : "N/A" + }, + { + "language_code": "es_es", + "string" : "N/A" + }] + }, + { + "column": "Object_PrimaryID", + "css_classes": "col-sm-2", + "show": false, + "type": "label", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code": "en_us", + "string" : "N/A" + }] + }, + { + "column": "Object_SecondaryID", + "css_classes": "col-sm-2", + "show": true, + "type": "label", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code": "en_us", + "string" : "Sent when" + }] + }, + { + "column": "Watched_Value1", + "css_classes": "col-sm-3", + "show": true, + "type": "label", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code": "en_us", + "string" : "Notification GUID" + }] + }, + { + "column": "Watched_Value2", + "css_classes": "col-sm-2", + "show": true, + "type": "textarea_readonly", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code": "en_us", + "string" : "Response (stdout)" + }] + }, + { + "column": "Watched_Value3", + "css_classes": "col-sm-2", + "show": true, + "type": "textarea_readonly", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code": "en_us", + "string" : "Response (stderr)" + }] + }, + { + "column": "Watched_Value4", + "css_classes": "col-sm-2", + "show": false, + "type": "device_mac", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code": "en_us", + "string" : "Device" + } + ] + }, + { + "column": "UserData", + "css_classes": "col-sm-2", + "show": false, + "type": "textbox_save", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code": "en_us", + "string" : "Comments" + }, + { + "language_code": "es_es", + "string" : "Comentarios" + }] + }, + { + "column": "Status", + "css_classes": "col-sm-1", + "show": false, + "type": "replace", + "default_value":"", + "options": [ + { + "equals": "watched-not-changed", + "replacement": "
" + }, + { + "equals": "watched-changed", + "replacement": "
" + }, + { + "equals": "new", + "replacement": "
" + }, + { + "equals": "missing-in-last-scan", + "replacement": "
" + } + ], + "localized": ["name"], + "name":[{ + "language_code": "en_us", + "string" : "Status" + }, + { + "language_code": "es_es", + "string" : "Estado" + }] + }, + { + "column": "Extra", + "css_classes": "col-sm-3", + "show": false, + "type": "label", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code": "en_us", + "string" : "Extra" + }, + { + "language_code": "es_es", + "string" : "Extra" + }] + } + ], + "settings":[ + { + "function": "RUN", + "events": ["test"], + "type": "text.select", + "default_value":"disabled", + "options": ["disabled", "on_notification" ], + "localized": ["name", "description"], + "name" :[{ + "language_code": "en_us", + "string" : "When to run" + }, + { + "language_code": "es_es", + "string" : "Cuando ejecuta" + }], + "description": [ + { + "language_code": "en_us", + "string" : "Enable webhooks for notifications. Webhooks help you to connect to a lot of 3rd party tools, such as IFTTT, Zapier or n8n to name a few. Check out this simple n8n guide here to get started. If enabled, configure related settings below." + }, + { + "language_code": "es_es", + "string" : "Habilite webhooks para notificaciones. Los webhooks lo ayudan a conectarse a muchas herramientas de terceros, como IFTTT, Zapier o n8n, por nombrar algunas. Consulte esta sencilla guía de n8n aquí para obtener comenzó. Si está habilitado, configure los ajustes relacionados a continuación." + } + ] + }, + { + "function": "CMD", + "type": "readonly", + "default_value":"python3 /home/pi/pialert/front/plugins/_publisher_webhook/webhook.py", + "options": [], + "localized": ["name", "description"], + "name" : [{ + "language_code": "en_us", + "string" : "Command" + }, + { + "language_code": "es_es", + "string" : "Comando" + }], + "description": [{ + "language_code": "en_us", + "string" : "Command to run" + }, + { + "language_code": "es_es", + "string" : "Comando a ejecutar" + }] + }, + { + "function": "RUN_TIMEOUT", + "type": "integer", + "default_value": 10, + "options": [], + "localized": ["name", "description"], + "name" : [{ + "language_code": "en_us", + "string" : "Run timeout" + }, + { + "language_code": "es_es", + "string" : "Tiempo de espera de ejecución" + }, + { + "language_code": "de_de", + "string" : "Wartezeit" + }], + "description": [{ + "language_code": "en_us", + "string" : "Maximum time in seconds to wait for the script to finish. If this time is exceeded the script is aborted." + }, + { + "language_code": "es_es", + "string" : "Tiempo máximo en segundos para esperar a que finalice el script. Si se supera este tiempo, el script se cancela." + }] + }, + { + "function": "URL", + "type": "text", + "default_value": "", + "options": [], + "localized": ["name", "description"], + "name" : [{ + "language_code": "en_us", + "string" : "Target URL" + }, + { + "language_code": "es_es", + "string" : "URL de destino" + }], + "description": [{ + "language_code": "en_us", + "string" : "Target URL starting with http:// or https://." + }, + { + "language_code": "es_es", + "string" : "URL de destino comienza con http:// o https://." + }] + }, + { + "function": "PAYLOAD", + "type": "text.select", + "default_value": "json", + "options": ["json", "html", "text"], + "localized": ["name", "description"], + "name" : [{ + "language_code": "en_us", + "string" : "Payload type" + }, + { + "language_code": "es_es", + "string" : "Tipo de carga" + }], + "description": [{ + "language_code": "en_us", + "string" : "The Webhook payload data format for the body > attachments > text attribute in the payload json. See an example of the payload here. (e.g.: for discord use text)" + }, + { + "language_code": "es_es", + "string" : "El formato de datos de carga de Webhook para el atributo body > attachments > text en el json de carga. Vea un ejemplo de la carga aquí. (por ejemplo: para discord use text)" + }] + }, + { + "function": "REQUEST_METHOD", + "type": "text.select", + "default_value": "GET", + "options": ["GET", "POST", "PUT"], + "localized": ["name", "description"], + "name" : [{ + "language_code": "en_us", + "string" : "Request method" + }, + { + "language_code": "es_es", + "string" : "Método de solicitud" + }], + "description": [{ + "language_code": "en_us", + "string" : "The HTTP request method to be used for the webhook call." + }, + { + "language_code": "es_es", + "string" : "El método de solicitud HTTP que se utilizará para la llamada de webhook." + }] + }, + { + "function": "SIZE", + "type": "integer", + "default_value": 1024, + "options": [], + "localized": ["name", "description"], + "name" : [{ + "language_code": "en_us", + "string" : "Max payload size" + }, + { + "language_code": "es_es", + "string" : "Tamaño máximo de carga útil" + }], + "description": [{ + "language_code": "en_us", + "string" : "The maximum size of the webhook payload as number of characters in the passed string. If above limit, it will be truncated and a (text was truncated) message is appended." + }, + { + "language_code": "es_es", + "string" : "El tamaño máximo de la carga útil del webhook como número de caracteres en la cadena pasada. Si supera el límite, se truncará y se agregará un mensaje (text was truncated)." + }] + }, + { + "function": "SECRET", + "type": "text", + "default_value": "", + "options": [], + "localized": ["name", "description"], + "name" : [{ + "language_code": "en_us", + "string" : "HMAC Secret" + }, + { + "language_code": "es_es", + "string" : "" + }], + "description": [{ + "language_code": "en_us", + "string" : "When set, use this secret to generate the SHA256-HMAC hex digest value of the request body, which will be passed as the X-Webhook-Signature header to the request. You can find more information here." + }] + } + ] +} diff --git a/front/plugins/_publisher_webhook/ignore_plugin b/front/plugins/_publisher_webhook/ignore_plugin deleted file mode 100755 index 77ffa1c1f..000000000 --- a/front/plugins/_publisher_webhook/ignore_plugin +++ /dev/null @@ -1 +0,0 @@ -This plugin will not be loaded \ No newline at end of file diff --git a/front/plugins/_publisher_webhook/webhook.py b/front/plugins/_publisher_webhook/webhook.py new file mode 100755 index 000000000..8b66beeec --- /dev/null +++ b/front/plugins/_publisher_webhook/webhook.py @@ -0,0 +1,190 @@ + +#!/usr/bin/env python + +import json +import subprocess +import argparse +import os +import pathlib +import sys +import requests +from datetime import datetime +from base64 import b64encode +import hashlib +import hmac + +# Replace these paths with the actual paths to your Pi.Alert directories +sys.path.extend(["/home/pi/pialert/front/plugins", "/home/pi/pialert/pialert"]) + +# pialert modules +import conf +from const import logPath +from plugin_helper import Plugin_Objects, handleEmpty +from logger import mylog, append_line_to_file +from helper import timeNowTZ, get_setting_value, hide_string, write_file +from notification import Notification_obj +from database import DB + +CUR_PATH = str(pathlib.Path(__file__).parent.resolve()) +RESULT_FILE = os.path.join(CUR_PATH, 'last_result.log') + +pluginName = 'WEBHOOK' + +def main(): + + mylog('verbose', [f'[{pluginName}](publisher) In script']) + + # Check if basic config settings supplied + if check_config() == False: + mylog('none', [f'[{pluginName}] Error: Publisher notification gateway not set up correctly. Check your pialert.conf {pluginName}_* variables.']) + return + + # Create a database connection + db = DB() # instance of class DB + db.open() + + # Initialize the Plugin obj output file + plugin_objects = Plugin_Objects(RESULT_FILE) + + # Create a Notification_obj instance + notifications = Notification_obj(db) + + # Retrieve new notifications + new_notifications = notifications.getNew() + + # Process the new notifications (see the Notifications DB table for structure or check the /api/table_notifications.json endpoint) + for notification in new_notifications: + + # Send notification + response_stdout, response_stderr = send(notification["Text"], notification["HTML"], notification["JSON"]) + + # Log result + plugin_objects.add_object( + primaryId = pluginName, + secondaryId = timeNowTZ(), + watched1 = notification["GUID"], + watched2 = handleEmpty(response_stdout), + watched3 = handleEmpty(response_stderr), + watched4 = 'null', + extra = 'null', + foreignKey = notification["GUID"] + ) + + plugin_objects.write_result_file() + + +#------------------------------------------------------------------------------- +def check_config(): + if get_setting_value('WEBHOOK_URL') == '': + return False + else: + return True + +#------------------------------------------------------------------------------- + +def send (text_data, html_data, json_data): + + response_stderr = '' + response_stdout = '' + + # limit = 1024 * 1024 # 1MB limit (1024 bytes * 1024 bytes = 1MB) + limit = get_setting_value('WEBHOOK_SIZE') + payloadType = get_setting_value('WEBHOOK_PAYLOAD') + endpointUrl = get_setting_value('WEBHOOK_URL') + secret = get_setting_value('WEBHOOK_SECRET') + requestMethod = get_setting_value('WEBHOOK_REQUEST_METHOD') + + # use data type based on specified payload type + if payloadType == 'json': + # In this code, the truncate_json function is used to recursively traverse the JSON object + # and remove nodes that exceed the size limit. It checks the size of each node's JSON representation + # using json.dumps and includes only the nodes that are within the limit. + json_str = json.dumps(json_data) + + if len(json_str) <= limit: + payloadData = json_data + else: + def truncate_json(obj): + if isinstance(obj, dict): + return { + key: truncate_json(value) + for key, value in obj.items() + if len(json.dumps(value)) <= limit + } + elif isinstance(obj, list): + return [ + truncate_json(item) + for item in obj + if len(json.dumps(item)) <= limit + ] + else: + return obj + + payloadData = truncate_json(json_data) + if payloadType == 'html': + if len(html_data) > limit: + payloadData = html_data[:limit] + "

(text was truncated)

" + else: + payloadData = html_data + if payloadType == 'text': + if len(text_data) > limit: + payloadData = text_data[:limit] + " (text was truncated)" + else: + payloadData = text_data + + # Define slack-compatible payload + _json_payload = { "text": payloadData } if payloadType == 'text' else { + "username": "Pi.Alert", + "text": "There are new notifications", + "attachments": [{ + "title": "Pi.Alert Notifications", + "title_link": get_setting_value('REPORT_DASHBOARD_URL'), + "text": payloadData + }] + } + + # DEBUG - Write the json payload into a log file for debugging + write_file (logPath + '/webhook_payload.json', json.dumps(_json_payload)) + + # Using the Slack-Compatible Webhook endpoint for Discord so that the same payload can be used for both + # Consider: curl has the ability to load in data to POST from a file + piping + if(endpointUrl.startswith('https://discord.com/api/webhooks/') and not endpointUrl.endswith("/slack")): + _WEBHOOK_URL = f"{endpointUrl}/slack" + curlParams = ["curl","-i","-H", "Content-Type:application/json" ,"-d", json.dumps(_json_payload), _WEBHOOK_URL] + else: + _WEBHOOK_URL = endpointUrl + curlParams = ["curl","-i","-X", requestMethod , "-H", "Content-Type:application/json", "-d", json.dumps(_json_payload), _WEBHOOK_URL] + + # Add HMAC signature if configured + if(secret != ''): + h = hmac.new(secret.encode("UTF-8"), json.dumps(_json_payload, separators=(',', ':')).encode(), hashlib.sha256).hexdigest() + curlParams.insert(4,"-H") + curlParams.insert(5,f"X-Webhook-Signature: sha256={h}") + + try: + # Execute CURL call + mylog('debug', [f'[{pluginName}] curlParams: ', curlParams]) + result = subprocess.run(curlParams, capture_output=True, text=True) + + response_stderr = result.stderr + response_stdout = result.stdout + + # Write stdout and stderr into .log files for debugging if needed + mylog('debug', [f'[{pluginName}] stdout: ', response_stdout]) + mylog('debug', [f'[{pluginName}] stderr: ', response_stderr]) + + + + except subprocess.CalledProcessError as e: + # An error occurred, handle it + mylog('none', [f'[{pluginName}] Error: ', e.output]) + + response_stderr = e.output + + + return response_stdout, response_stderr + +# ------------------------------------------------------- +if __name__ == '__main__': + sys.exit(main()) + diff --git a/pialert/README.md b/pialert/README.md index 2b0a38586..1a8d6d4a1 100755 --- a/pialert/README.md +++ b/pialert/README.md @@ -21,18 +21,7 @@ The original pilaert.py code is now moved to this new folder and split into diff |```reporting.py```| Reporting generates the email, html and json reports to be sent by the publishers | |```scheduler.py```| All things scheduling | -## publishers -publishers generally have a check_config method as well as a send method. -| Module | Description | -|--------|-----------| -|```__init__.py```| an empty init file| -|```apprise.py```| use apprise to integrate to "everywhere" https://github.com/caronc/apprise | -|```email.py```| Configure and send the reports and notifications via email | -|```mqtt.py```| integrate with a MQTT broker and even make the devices automatically discoverable in Home-Assistant | -|```ntfy.py```| integrate with ntfy | -|```pushsafer.py```| integrate with pushsafer | -|```webhook.py```| integrate via webhook | diff --git a/pialert/README_ES.md b/pialert/README_ES.md index 664ce689f..b2856f555 100755 --- a/pialert/README_ES.md +++ b/pialert/README_ES.md @@ -23,24 +23,4 @@ El código pilaert.py original ahora se mueve a esta nueva carpeta y se divide e |```reporting.py```| La generación de informes genera los informes de correo electrónico, html y json que deben enviar los editores | |```scheduler.py```| Todo sobre la planificación | -## Editores -Los editores suelen tener un método check_config además de un método send. -| Módulo | Descripción | -|--------|-----------| -|```__init__.py```| Un archivo init vacío| -|```apprise.py```| Utilice apprise para integrarse en "todas partes" ([Github Apprise](https://github.com/caronc/apprise)). | -|```email.py```| Configurar y enviar los informes y notificaciones por correo electrónico | -|```mqtt.py```| Integrar con un broker MQTT e incluso hacer que los dispositivos sean automáticamente detectables en Home-Assistant | -|```ntfy.py```| Integración con ntfy | -|```pushsafer.py```| Integrar con pushsafer | -|```webhook.py```| Integración mediante webhook | - -## Escáneres -Diferentes métodos para escanear la red en busca de dispositivos o para encontrar más detalles sobre los dispositivos descubiertos - -| Módulo | Descripción | -|--------|-----------| -|```__init__.py```| Un archivo init vacío (oops falta en el repo)| -|```internet.py```| Descubra la interfaz de Internet y verifique la IP externa y también administre DNS dinámico | -|```nmapscan.py```| Utilice Nmap para descubrir más sobre los dispositivos | diff --git a/pialert/conf.py b/pialert/conf.py index 7d82039be..2e8e95eec 100755 --- a/pialert/conf.py +++ b/pialert/conf.py @@ -41,22 +41,6 @@ DAYS_TO_KEEP_EVENTS = 90 REPORT_DASHBOARD_URL = 'http://pi.alert/' -# ------------------------------------------- -# Notification gateways -# ------------------------------------------- - -# Webhooks -REPORT_WEBHOOK = False -WEBHOOK_URL = '' -WEBHOOK_PAYLOAD = 'json' -WEBHOOK_REQUEST_METHOD = 'GET' -WEBHOOK_SECRET = '' - - -# PUSHSAFER -REPORT_PUSHSAFER = False -PUSHSAFER_TOKEN = 'ApiKey' - # ------------------------------------------- # Misc # ------------------------------------------- diff --git a/pialert/helper.py b/pialert/helper.py index f56bfd6d5..57718e7f8 100755 --- a/pialert/helper.py +++ b/pialert/helper.py @@ -510,6 +510,14 @@ def hide_email(email): return email +#------------------------------------------------------------------------------- +def hide_string(input_string): + if len(input_string) < 3: + return input_string # Strings with 2 or fewer characters remain unchanged + else: + return input_string[0] + "*" * (len(input_string) - 2) + input_string[-1] + + #------------------------------------------------------------------------------- def removeDuplicateNewLines(text): if "\n\n\n" in text: diff --git a/pialert/initialise.py b/pialert/initialise.py index 1369bfde9..4cf4f76f8 100755 --- a/pialert/initialise.py +++ b/pialert/initialise.py @@ -117,17 +117,6 @@ def importConfigs (db): # ARPSCAN (+ more settings are provided by the ARPSCAN plugin) conf.SCAN_SUBNETS = ccd('SCAN_SUBNETS', ['192.168.1.0/24 --interface=eth1', '192.168.1.0/24 --interface=eth0'] , c_d, 'Subnets to scan', 'subnets', '', 'ARPSCAN') - # Notification gateways - # ---------------------------------------- - - # Webhooks - conf.REPORT_WEBHOOK = ccd('REPORT_WEBHOOK', False , c_d, 'Enable Webhooks', 'boolean', '', 'Webhooks', ['test']) - conf.WEBHOOK_URL = ccd('WEBHOOK_URL', '' , c_d, 'Target URL', 'text', '', 'Webhooks') - conf.WEBHOOK_PAYLOAD = ccd('WEBHOOK_PAYLOAD', 'json' , c_d, 'Payload type', 'text.select', "['json', 'html', 'text']", 'Webhooks') - conf.WEBHOOK_REQUEST_METHOD = ccd('WEBHOOK_REQUEST_METHOD', 'GET' , c_d, 'Req type', 'text.select', "['GET', 'POST', 'PUT']", 'Webhooks') - conf.WEBHOOK_SIZE = ccd('WEBHOOK_SIZE', 1024 , c_d, 'Payload size', 'integer', '', 'Webhooks') - conf.WEBHOOK_SECRET = ccd('WEBHOOK_SECRET', '' , c_d, 'Secret', 'text', '', 'Webhooks') - # Init timezone in case it changed conf.tz = timezone(conf.TIMEZONE) diff --git a/pialert/publishers/webhook.py b/pialert/publishers/webhook.py index 7397e714f..9c1c4669c 100755 --- a/pialert/publishers/webhook.py +++ b/pialert/publishers/webhook.py @@ -53,7 +53,7 @@ def truncate_json(obj): payloadData = truncate_json(json_data) if conf.WEBHOOK_PAYLOAD == 'html': if len(msg.html) > limit: - payloadData = msg.html[:limit] + "

(text was truncated)

" + payloadData = msg.html[:limit] + "

(text was truncated)

" else: payloadData = msg.html if conf.WEBHOOK_PAYLOAD == 'text': diff --git a/pialert/reporting.py b/pialert/reporting.py index 1d2091ba2..1dd98c5cb 100755 --- a/pialert/reporting.py +++ b/pialert/reporting.py @@ -26,8 +26,7 @@ from publishers.webhook import (check_config as webhook_check_config, send as send_webhook) -from publishers.pushsafer import (check_config as pushsafer_check_config, - send as send_pushsafer) + #=============================================================================== @@ -258,21 +257,6 @@ def get_notifications (db): return noti_obj(final_json, final_text, final_html) - - # if conf.REPORT_WEBHOOK and check_config('webhook'): - # updateState("Send: Webhook") - # mylog('minimal', ['[Notification] Sending report by Webhook']) - # send_webhook (msg) - # else : - # mylog('verbose', ['[Notification] Skip webhook']) - - # if conf.REPORT_PUSHSAFER and check_config('pushsafer'): - # updateState("Send: PUSHSAFER") - # mylog('minimal', ['[Notification] Sending report by PUSHSAFER']) - # send_pushsafer (msg) - # else : - # mylog('verbose', ['[Notification] Skip PUSHSAFER']) - #------------------------------------------------------------------------------- # Replacing table headers