From 695f1593c6d993815ae1b40271cfad47cb6eb29f Mon Sep 17 00:00:00 2001 From: Jokob-sk Date: Sat, 7 Oct 2023 13:00:28 +1100 Subject: [PATCH] Notification rework v0.3 --- front/php/templates/language/en_us.json | 16 +- front/plugins/_publisher_apprise/apprise.py | 75 ++++-- front/plugins/_publisher_apprise/config.json | 213 +++++++++--------- .../plugins/_publisher_apprise/ignore_plugin | 1 - front/settings.php | 3 +- pialert/__main__.py | 19 +- pialert/appevent.py | 125 ++++++++++ pialert/database.py | 11 +- pialert/helper.py | 4 +- pialert/initialise.py | 10 - pialert/{notifications.py => notification.py} | 33 ++- pialert/plugin.py | 2 +- pialert/publishers/apprise.py | 4 +- pialert/publishers/email.py | 4 +- pialert/publishers/ntfy.py | 4 +- pialert/publishers/pushsafer.py | 4 +- pialert/publishers/webhook.py | 4 +- pialert/reporting.py | 18 +- 18 files changed, 351 insertions(+), 199 deletions(-) delete mode 100755 front/plugins/_publisher_apprise/ignore_plugin create mode 100644 pialert/appevent.py rename pialert/{notifications.py => notification.py} (80%) diff --git a/front/php/templates/language/en_us.json b/front/php/templates/language/en_us.json index 2faee1ae..27620372 100755 --- a/front/php/templates/language/en_us.json +++ b/front/php/templates/language/en_us.json @@ -536,17 +536,7 @@ "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.", - "Apprise_display_name" : "Apprise", - "Apprise_icon" : "", - "REPORT_APPRISE_name" : "Enable Apprise", - "REPORT_APPRISE_description" : "Enable sending notifications via Apprise.", - "APPRISE_HOST_name" : "Apprise host URL", - "APPRISE_HOST_description" : "Apprise host URL starting with http:// or https://. (do not forget to include /notify at the end)", - "APPRISE_URL_name" : "Apprise notification URL", - "APPRISE_URL_description" : "Apprise notification target URL. For example for Telegram it would be tgram://{bot_token}/{chat_id}.", - "APPRISE_SIZE_name" : "Max payload size", - "APPRISE_SIZE_description" : "The maximum size of the apprise 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_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.", "NTFY_display_name" : "NTFY", "NTFY_icon" : "", "REPORT_NTFY_name" : "Enable NTFY", @@ -564,9 +554,7 @@ "REPORT_PUSHSAFER_name" : "Enable Pushsafer", "REPORT_PUSHSAFER_description" : "Enable sending notifications via Pushsafer.", "PUSHSAFER_TOKEN_name" : "Pushsafer token", - "PUSHSAFER_TOKEN_description" : "Your secret Pushsafer API key (token).", - "APPRISE_PAYLOAD_name" : "Payload type", - "APPRISE_PAYLOAD_description" : "Select the payoad type sent to Apprise. For example html works well with emails, text with chat apps, such as Telegram.", + "PUSHSAFER_TOKEN_description" : "Your secret Pushsafer API key (token).", "MQTT_display_name" : "MQTT", "MQTT_icon" : "", "REPORT_TITLE" : "Report", diff --git a/front/plugins/_publisher_apprise/apprise.py b/front/plugins/_publisher_apprise/apprise.py index ac2fc7d0..c3ddb3c8 100755 --- a/front/plugins/_publisher_apprise/apprise.py +++ b/front/plugins/_publisher_apprise/apprise.py @@ -15,7 +15,9 @@ sys.path.extend(["/home/pi/pialert/front/plugins", "/home/pi/pialert/pialert"]) import conf from plugin_helper import Plugin_Objects from logger import mylog, append_line_to_file -from helper import timeNowTZ, noti_struc +from helper import timeNowTZ, noti_obj +from notification import Notification_obj +from database import DB CUR_PATH = str(pathlib.Path(__file__).parent.resolve()) @@ -24,41 +26,60 @@ RESULT_FILE = os.path.join(CUR_PATH, 'last_result.log') def main(): mylog('verbose', ['[APPRISE](publisher) In script']) + + # Check if basic config settings supplied + if check_config() == False: + mylog('none', ['[Check Config] Error: Apprise service not set up correctly. Check your pialert.conf APPRISE_* variables.']) + return - parser = argparse.ArgumentParser(description='APPRISE publisher Plugin') - values = parser.parse_args() + # Create a database connection + db = DB() # instance of class DB + db.open() + # parser = argparse.ArgumentParser(description='APPRISE publisher Plugin') + # values = parser.parse_args() + + # Initialize the Plugin obj output file plugin_objects = Plugin_Objects(RESULT_FILE) - speedtest_result = send() + # Create a Notification_obj instance + Notification_obj(db) - plugin_objects.add_object( - primaryId = 'APPRISE', - secondaryId = timeNowTZ(), - watched1 = speedtest_result['download_speed'], - watched2 = speedtest_result['upload_speed'], - watched3 = 'null', - watched4 = 'null', - extra = 'null', - foreignKey = 'null' - ) + # Retrieve new notifications + new_notifications = notifications.getNew() + + # Process the new notifications + for notification in new_notifications: + + # Send notification + result = send(notification["HTML"], notification["Text"]) + + # Log result + plugin_objects.add_object( + primaryId = 'APPRISE', + secondaryId = timeNowTZ(), + watched1 = notification["GUID"], + watched2 = result, + watched3 = 'null', + watched4 = 'null', + extra = 'null', + foreignKey = 'null' + ) plugin_objects.write_result_file() #------------------------------------------------------------------------------- def check_config(): - if conf.APPRISE_URL == '' or conf.APPRISE_HOST == '': - mylog('none', ['[Check Config] Error: Apprise service not set up correctly. Check your pialert.conf APPRISE_* variables.']) + if conf.APPRISE_URL == '' or conf.APPRISE_HOST == '': return False else: return True #------------------------------------------------------------------------------- -def send(msg: noti_struc): - html = msg.html - text = msg.text +def send(html, text): payloadData = '' + result = '' # limit = 1024 * 1024 # 1MB limit (1024 bytes * 1024 bytes = 1MB) limit = conf.APPRISE_SIZE @@ -66,7 +87,7 @@ def send(msg: noti_struc): # truncate size if conf.APPRISE_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.APPRISE_PAYLOAD == 'text': @@ -88,14 +109,22 @@ def send(msg: noti_struc): # try runnning a subprocess p = subprocess.Popen(["curl","-i","-X", "POST" ,"-H", "Content-Type:application/json" ,"-d", json.dumps(_json_payload), conf.APPRISE_HOST], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, stderr = p.communicate() - # write stdout and stderr into .log files for debugging if needed - + # write stdout and stderr into .log files for debugging if needed # Log the stdout and stderr - mylog('debug', [stdout, stderr]) # TO-DO should be changed to mylog + mylog('debug', [stdout, stderr]) + + # log result + result = stdout + except subprocess.CalledProcessError as e: # An error occurred, handle it mylog('none', [e.output]) + # log result + result = e.output + + return result + if __name__ == '__main__': sys.exit(main()) diff --git a/front/plugins/_publisher_apprise/config.json b/front/plugins/_publisher_apprise/config.json index aaa42858..7443919a 100755 --- a/front/plugins/_publisher_apprise/config.json +++ b/front/plugins/_publisher_apprise/config.json @@ -1,22 +1,30 @@ { - "code_name": "internet_speedtest", - "unique_prefix": "INTRSPD", + "code_name": "_publisher_apprise", + "unique_prefix": "APPRISE", "enabled": true, "data_source": "script", "show_ui": true, "localized": ["display_name", "description", "icon"], - "display_name" : [{ + "display_name" : [ + { "language_code": "en_us", - "string" : "Internet speedtest" - }], + "string" : "Apprise publisher" + }, + { + "language_code": "es_es", + "string" : "Habilitar Apprise" + } + ], "icon":[{ "language_code": "en_us", - "string" : "" + "string" : "" }], - "description": [{ + "description": [ + { "language_code": "en_us", - "string" : "A plugin to perform a scheduled internet speedtest." - }], + "string" : "A plugin to publish a notification via the Apprise gateway." + } + ], "params" : [], "database_column_definitions": [ @@ -94,7 +102,7 @@ "localized": ["name"], "name":[{ "language_code": "en_us", - "string" : "Test run on" + "string" : "Sent when" }] }, { @@ -118,60 +126,26 @@ "column": "Watched_Value1", "css_classes": "col-sm-2", "show": true, - "type": "threshold", + "type": "label", "default_value":"", - "options": [ - { - "maximum": 1, - "hexColor": "#D33115" - }, - { - "maximum": 5, - "hexColor": "#792D86" - }, - { - "maximum": 10, - "hexColor": "#7D862D" - }, - { - "maximum": 100, - "hexColor": "#05483C" - } - ], + "options": [], "localized": ["name"], "name":[{ "language_code": "en_us", - "string" : "Download" + "string" : "Notification GUID" }] }, { "column": "Watched_Value2", "css_classes": "col-sm-2", "show": true, - "type": "threshold", + "type": "label", "default_value":"", - "options": [ - { - "maximum": 1, - "hexColor": "#D33115" - }, - { - "maximum": 5, - "hexColor": "#792D86" - }, - { - "maximum": 10, - "hexColor": "#7D862D" - }, - { - "maximum": 100, - "hexColor": "#05483C" - } - ], + "options": [], "localized": ["name"], "name":[{ "language_code": "en_us", - "string" : "Upload" + "string" : "Result" }] }, { @@ -283,7 +257,7 @@ "events": ["run"], "type": "text.select", "default_value":"disabled", - "options": ["disabled", "once", "schedule", "always_after_scan" ], + "options": ["disabled", "on_notification" ], "localized": ["name", "description"], "name" :[{ "language_code": "en_us", @@ -293,15 +267,21 @@ "language_code": "es_es", "string" : "Cuando ejecuta" }], - "description": [{ + "description": [ + { "language_code": "en_us", - "string" : "Enable a regular internet speedtest. If you select schedule the scheduling settings from below are applied. If you select once the scan is run only once on start of the application (container) for the time specified in INTRSPD_RUN_TIMEOUT setting." - }] + "string" : "Enable sending notifications via Apprise." + }, + { + "language_code": "es_es", + "string" : "Habilitar el envío de notificaciones a través de Apprise." + } + ] }, { "function": "CMD", "type": "readonly", - "default_value":"python3 /home/pi/pialert/front/plugins/internet_speedtest/script.py", + "default_value":"python3 /home/pi/pialert/front/plugins/_publisher_apprise/apprise.py", "options": [], "localized": ["name", "description"], "name" : [{ @@ -321,33 +301,10 @@ "string" : "Comando a ejecutar" }] }, - { - "function": "RUN_SCHD", - "type": "text", - "default_value":"*/30 * * * *", - "options": [], - "localized": ["name", "description"], - "name" : [{ - "language_code": "en_us", - "string" : "Schedule" - }, - { - "language_code": "es_es", - "string" : "Schedule" - }], - "description": [{ - "language_code": "en_us", - "string" : "Only enabled if you select schedule in the INTRSPD_RUN setting. Make sure you enter the schedule in the correct cron-like format (e.g. validate at crontab.guru). For example entering 0 4 * * * will run the scan after 4 am in the TIMEZONE you set above. Will be run NEXT time the time passes." - }, - { - "language_code": "es_es", - "string": "Solo habilitado si selecciona schedule en la configuración INTRSPD_RUN. Asegúrese de ingresar el schedule en el formato similar a cron correcto (por ejemplo, valide en crontab.guru). Por ejemplo, ingrese 0 4 * * * ejecutará el escaneo después de las 4 am en el TIMEZONE que configuró arriba . Se ejecutará la PRÓXIMA vez que pase el tiempo." - }] - }, { "function": "RUN_TIMEOUT", "type": "integer", - "default_value":60, + "default_value": 10, "options": [], "localized": ["name", "description"], "name" : [{ @@ -372,46 +329,96 @@ }] }, { - "function": "WATCH", - "type": "text.multiselect", - "default_value":[], - "options": ["Watched_Value1","Watched_Value2","Watched_Value3","Watched_Value4"], + "function": "HOST", + "type": "text", + "default_value": "", + "options": [], "localized": ["name", "description"], - "name" :[{ + "name" : [{ "language_code": "en_us", - "string" : "Watched" + "string" : "Apprise host URL" }, { "language_code": "es_es", - "string" : "Visto" - }], - "description":[{ + "string" : "URL del host de Apprise" + }], + "description": [{ "language_code": "en_us", - "string" : "Send a notification if selected values change. Use CTRL + Click to select/deselect. " - }] + "string" : "Apprise host URL starting with http:// or https://. (do not forget to include /notify at the end)" + }, + { + "language_code": "es_es", + "string" : "URL del host de Apprise que comienza con http:// o https://. (no olvide incluir /notify al final)" + }] }, { - "function": "REPORT_ON", - "type": "text.multiselect", - "default_value":[], - "options": ["new","watched-changed","watched-not-changed", "missing-in-last-scan"], + "function": "URL", + "type": "text", + "default_value": "", + "options": [], "localized": ["name", "description"], - "name" :[{ + "name" : [{ "language_code": "en_us", - "string" : "Report on" + "string" : "Apprise notification URL" }, { "language_code": "es_es", - "string" : "Informar sobre" - }] , - "description":[{ + "string" : "URL de notificación de Apprise" + }], + "description": [{ "language_code": "en_us", - "string" : "Send a notification only on these statuses. new means a new unique (unique combination of PrimaryId and SecondaryId) object was discovered. watched-changed means that selected Watched_ValueN columns changed." + "string" : "Apprise notification target URL. For example for Telegram it would be tgram://{bot_token}/{chat_id}." }, { "language_code": "es_es", - "string" : "Envíe una notificación solo en estos estados. new significa que se descubrió un nuevo objeto único (combinación única de PrimaryId y SecondaryId). watched-changed significa que seleccionó Watched_ValueN Las columnas cambiaron." - }] - } + "string" : "Informar de la URL de destino de la notificación. Por ejemplo, para Telegram sería tgram://{bot_token}/{chat_id}." + }] + }, + { + "function": "PAYLOAD", + "type": "text.select", + "default_value": "html", + "options": ["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" : "Select the payoad type sent to Apprise. For example html works well with emails, text with chat apps, such as Telegram." + }, + { + "language_code": "es_es", + "string" : "Seleccione el tipo de carga útil enviada a Apprise. Por ejemplo, html funciona bien con correos electrónicos, text con aplicaciones de chat, como Telegram." + }] + }, + { + "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 apprise 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 de información como número de caracteres en la cadena pasada. Si supera el límite, se truncará y se agregará un mensaje (text was truncated)." + }] + } ] } diff --git a/front/plugins/_publisher_apprise/ignore_plugin b/front/plugins/_publisher_apprise/ignore_plugin deleted file mode 100755 index 77ffa1c1..00000000 --- a/front/plugins/_publisher_apprise/ignore_plugin +++ /dev/null @@ -1 +0,0 @@ -This plugin will not be loaded \ No newline at end of file diff --git a/front/settings.php b/front/settings.php index d18cbcf1..d2d48b85 100755 --- a/front/settings.php +++ b/front/settings.php @@ -749,8 +749,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) { // ----------------------------------------------------------------------------- // --------------------------------------------------------- - // Show last time settings have been imported - // getParam("lastImportedTime", "Back_Settings_Imported", skipCache = true); + // Show last time settings have been imported handleLoadingDialog() diff --git a/pialert/__main__.py b/pialert/__main__.py index fe7d5221..fd01c6b6 100755 --- a/pialert/__main__.py +++ b/pialert/__main__.py @@ -24,13 +24,13 @@ import multiprocessing import conf from const import * from logger import mylog -from helper import filePermissions, timeNowTZ, updateState, get_setting_value, noti_struc +from helper import filePermissions, timeNowTZ, updateState, get_setting_value, noti_obj from api import update_api from networkscan import process_scan from initialise import importConfigs -from database import DB, get_all_devices +from database import DB from reporting import get_notifications -from notifications import Notifications +from notification import Notification_obj from plugin import run_plugin_scripts, check_and_run_user_event @@ -154,16 +154,13 @@ def main (): notiStructure = get_notifications(db) # Write the notifications into the DB - notification = Notifications(db) + notification = Notification_obj(db) + hasNotification = notification.create(notiStructure.json, notiStructure.text, notiStructure.html, "") - # mylog('debug', f"[MAIN] notiStructure.text: {notiStructure.text} ") - # mylog('debug', f"[MAIN] notiStructure.json: {notiStructure.json} ") - # mylog('debug', f"[MAIN] notiStructure.html: {notiStructure.html} ") - - hasNotifications = notification.create(notiStructure.json, notiStructure.text, notiStructure.html, "") - - if hasNotifications: + # run all enabled publisher gateways + if hasNotification: pluginsState = run_plugin_scripts(db, 'on_notification', pluginsState) + notification.setAllProcessed() # Commit SQL diff --git a/pialert/appevent.py b/pialert/appevent.py new file mode 100644 index 00000000..acbbe245 --- /dev/null +++ b/pialert/appevent.py @@ -0,0 +1,125 @@ +import datetime +import json +import uuid + +# PiAlert modules +import conf +import const +from const import pialertPath, logPath, apiPath +from logger import logResult, mylog, print_log +from helper import timeNowTZ + +#------------------------------------------------------------------------------- +# Execution object handling +#------------------------------------------------------------------------------- +class AppEvent_obj: + def __init__(self, db): + self.db = db + + # Create AppEvent table if missing + self.db.sql.execute("""CREATE TABLE IF NOT EXISTS "AppEvents" ( + "Index" INTEGER, + "GUID" TEXT UNIQUE, + "DateTimeCreated" TEXT, + "ObjectType" TEXT, -- ObjectType (Plugins, Notifications, Events) + "ObjectGUID" TEXT, + "ObjectPlugin" TEXT, + "ObjectMAC" TEXT, + "ObjectIP" TEXT, + "ObjectPrimaryID" TEXT, + "ObjectSecondaryID" TEXT, + "ObjectForeignKey" TEXT, + "ObjectIndex" TEXT, + "ObjectRowID" TEXT, + "ObjectStatusColumn" TEXT, -- Status (Notifications, Plugins), eve_EventType (Events) + "ObjectStatus" TEXT, -- new_devices, down_devices, events, new, watched-changed, watched-not-changed, missing-in-last-scan, Device down, New Device, IP Changed, Connected, Disconnected, VOIDED - Disconnected, VOIDED - Connected, + "AppEventStatus" TEXT, -- TBD "new", "used", "cleanup-next" + "Extra" TEXT, + PRIMARY KEY("Index" AUTOINCREMENT) + ); + """) + + self.save() + + # Create a new DB entry if new notifications are available, otherwise skip + def create(self, Extra="", **kwargs): + # Check if nothing to report, end + if not any(kwargs.values()): + return False + + # Continue and save into DB if notifications are available + self.GUID = str(uuid.uuid4()) + self.DateTimeCreated = timeNowTZ() + self.ObjectType = "Plugins" # Modify ObjectType as needed + + # Optional parameters + self.ObjectGUID = kwargs.get("ObjectGUID", "") + self.ObjectPlugin = kwargs.get("ObjectPlugin", "") + self.ObjectMAC = kwargs.get("ObjectMAC", "") + self.ObjectIP = kwargs.get("ObjectIP", "") + self.ObjectPrimaryID = kwargs.get("ObjectPrimaryID", "") + self.ObjectSecondaryID = kwargs.get("ObjectSecondaryID", "") + self.ObjectForeignKey = kwargs.get("ObjectForeignKey", "") + self.ObjectIndex = kwargs.get("ObjectIndex", "") + self.ObjectRowID = kwargs.get("ObjectRowID", "") + self.ObjectStatusColumn = kwargs.get("ObjectStatusColumn", "") + self.ObjectStatus = kwargs.get("ObjectStatus", "") + + self.AppEventStatus = "new" # Modify AppEventStatus as needed + self.Extra = Extra + + self.upsert() + + return True + + # Update the status of the entry + def updateStatus(self, newStatus): + self.ObjectStatus = newStatus + self.upsert() + + def upsert(self): + self.db.sql.execute(""" + INSERT OR REPLACE INTO AppEvents ( + "GUID", + "DateTimeCreated", + "ObjectType", + "ObjectGUID", + "ObjectPlugin", + "ObjectMAC", + "ObjectIP", + "ObjectPrimaryID", + "ObjectSecondaryID", + "ObjectForeignKey", + "ObjectIndex", + "ObjectRowID", + "ObjectStatusColumn", + "ObjectStatus", + "AppEventStatus", + "Extra" + ) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """, ( + self.GUID, + self.DateTimeCreated, + self.ObjectType, + self.ObjectGUID, + self.ObjectPlugin, + self.ObjectMAC, + self.ObjectIP, + self.ObjectPrimaryID, + self.ObjectSecondaryID, + self.ObjectForeignKey, + self.ObjectIndex, + self.ObjectRowID, + self.ObjectStatusColumn, + self.ObjectStatus, + self.AppEventStatus, + self.Extra + )) + + self.save() + + def save(self): + # Commit changes + self.db.commitDB() + diff --git a/pialert/database.py b/pialert/database.py index eab5fa05..a9e42e51 100755 --- a/pialert/database.py +++ b/pialert/database.py @@ -6,7 +6,7 @@ import sqlite3 from const import fullDbPath, sql_devices_stats, sql_devices_all from logger import mylog -from helper import json_struc, initOrSetParam, row_to_json, timeNowTZ #, updateState +from helper import json_obj, initOrSetParam, row_to_json, timeNowTZ #, updateState @@ -208,10 +208,7 @@ class DB(): "par_ID" TEXT PRIMARY KEY, "par_Value" TEXT ); - """) - - # Initialize Parameters if unavailable - initOrSetParam(self, 'Back_App_State','Initializing') + """) # ------------------------------------------------------------------------- # Nmap_Scan table setup DEPRECATED after 1/1/2024 @@ -384,7 +381,7 @@ class DB(): rows = self.sql.fetchall() except sqlite3.Error as e: mylog('none',[ '[Database] - SQL ERROR: ', e]) - return None + return json_obj({}, []) # return empty object result = {"data":[]} for row in rows: @@ -392,7 +389,7 @@ class DB(): result["data"].append(tmp) # mylog('debug',[ '[Database] - get_table_as_json - returning ', len(rows), " rows with columns: ", columnNames]) - return json_struc(result, columnNames) + return json_obj(result, columnNames) #------------------------------------------------------------------------------- # referece from here: https://codereview.stackexchange.com/questions/241043/interface-class-for-sqlite-databases diff --git a/pialert/helper.py b/pialert/helper.py index 712635b4..1207ec92 100755 --- a/pialert/helper.py +++ b/pialert/helper.py @@ -613,13 +613,13 @@ def initOrSetParam(db, parID, parValue): db.commitDB() #------------------------------------------------------------------------------- -class json_struc: +class json_obj: def __init__(self, jsn, columnNames): self.json = jsn self.columnNames = columnNames #------------------------------------------------------------------------------- -class noti_struc: +class noti_obj: def __init__(self, json, text, html): self.json = json self.text = text diff --git a/pialert/initialise.py b/pialert/initialise.py index 233bdc0c..68690d26 100755 --- a/pialert/initialise.py +++ b/pialert/initialise.py @@ -134,13 +134,6 @@ def importConfigs (db): conf.WEBHOOK_SIZE = ccd('WEBHOOK_SIZE', 1024 , c_d, 'Payload size', 'integer', '', 'Webhooks') conf.WEBHOOK_SECRET = ccd('WEBHOOK_SECRET', '' , c_d, 'Secret', 'text', '', 'Webhooks') - # Apprise - conf.REPORT_APPRISE = ccd('REPORT_APPRISE', False , c_d, 'Enable Apprise', 'boolean', '', 'Apprise', ['test']) - conf.APPRISE_HOST = ccd('APPRISE_HOST', '' , c_d, 'Apprise host URL', 'text', '', 'Apprise') - conf.APPRISE_URL = ccd('APPRISE_URL', '' , c_d, 'Apprise notification URL', 'text', '', 'Apprise') - conf.APPRISE_PAYLOAD = ccd('APPRISE_PAYLOAD', 'html' , c_d, 'Payload type', 'text.select', "['html', 'text']", 'Apprise') - conf.APPRISE_SIZE = ccd('APPRISE_SIZE', 1024 , c_d, 'Payload size', 'integer', '', 'Apprise') - # NTFY conf.REPORT_NTFY = ccd('REPORT_NTFY', False , c_d, 'Enable NTFY', 'boolean', '', 'NTFY', ['test']) conf.NTFY_HOST = ccd('NTFY_HOST', 'https://ntfy.sh' , c_d, 'NTFY host URL', 'text', '', 'NTFY') @@ -261,9 +254,6 @@ def importConfigs (db): sql.execute ("DELETE FROM Settings") sql.executemany ("""INSERT INTO Settings ("Code_Name", "Display_Name", "Description", "Type", "Options", "RegEx", "Value", "Group", "Events" ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""", conf.mySettingsSQLsafe) - - # Is used to display a message in the UI when old (outdated) settings are loaded - initOrSetParam(db, "Back_Settings_Imported",(round(time.time() * 1000),) ) #commitDB(sql_connection) db.commitDB() diff --git a/pialert/notifications.py b/pialert/notification.py similarity index 80% rename from pialert/notifications.py rename to pialert/notification.py index 875d0591..b9d8a052 100644 --- a/pialert/notifications.py +++ b/pialert/notification.py @@ -12,7 +12,7 @@ from helper import timeNowTZ #------------------------------------------------------------------------------- # Notification object handling #------------------------------------------------------------------------------- -class Notifications: +class Notification_obj: def __init__(self, db): self.db = db @@ -35,11 +35,9 @@ class Notifications: self.save() # Create a new DB entry if new notiifcations available, otherwise skip - def create(self, JSON, Text, HTML, Extra=""): - - # Check if empty JSON - # _json = json.loads(JSON) - # Check if nothing to report + def create(self, JSON, Text, HTML, Extra=""): + + # Check if nothing to report, end if JSON["internet"] == [] and JSON["new_devices"] == [] and JSON["down_devices"] == [] and JSON["events"] == [] and JSON["plugins"] == []: self.HasNotifications = False # end if nothing to report @@ -75,6 +73,7 @@ class Notifications: # TODO Index vs hash to minimize SQL calls, finish CRUD operations, expose via API, use API in plugins + # create or update a notification def upsert(self): self.db.sql.execute(""" INSERT OR REPLACE INTO Notifications (GUID, DateTimeCreated, DateTimePushed, Status, JSON, Text, HTML, PublishedVia, Extra) @@ -83,6 +82,28 @@ class Notifications: self.save() + # Get all with the "new" status + def getNew(self): + self.db.sql.execute(""" + SELECT * FROM Notifications + WHERE Status = "new" + """) + return self.db.sql.fetchall() + + # Set all to "processed" status + def setAllProcessed(self): + + # Execute an SQL query to update the status of all notifications + self.db.sql.execute(""" + UPDATE Notifications + SET Status = "processed" + WHERE Status = "new" + """) + + self.save() + + + def save(self): # Commit changes self.db.commitDB() \ No newline at end of file diff --git a/pialert/plugin.py b/pialert/plugin.py index 6dea2247..70557dcd 100755 --- a/pialert/plugin.py +++ b/pialert/plugin.py @@ -791,7 +791,7 @@ def handle_test(testType): # Open json sample and get only the payload part sample_json_payload = json.loads(get_file_content(pialertPath + '/back/webhook_json_sample.json'))[0]["body"]["attachments"][0]["text"] - sample_msg = noti_struc(sample_json_payload, sample_txt, sample_html, "test_sample") + sample_msg = noti_obj(sample_json_payload, sample_txt, sample_html, "test_sample") if testType == 'Email': diff --git a/pialert/publishers/apprise.py b/pialert/publishers/apprise.py index 0f2ed35c..6be9f0e5 100755 --- a/pialert/publishers/apprise.py +++ b/pialert/publishers/apprise.py @@ -2,7 +2,7 @@ import json import subprocess import conf -from helper import noti_struc +from helper import noti_obj from logger import logResult, mylog #------------------------------------------------------------------------------- @@ -14,7 +14,7 @@ def check_config(): return True #------------------------------------------------------------------------------- -def send(msg: noti_struc): +def send(msg: noti_obj): html = msg.html text = msg.text diff --git a/pialert/publishers/email.py b/pialert/publishers/email.py index c957d006..acf37834 100755 --- a/pialert/publishers/email.py +++ b/pialert/publishers/email.py @@ -7,7 +7,7 @@ import smtplib import conf import socket -from helper import hide_email, noti_struc +from helper import hide_email, noti_obj from logger import mylog, print_log #------------------------------------------------------------------------------- @@ -19,7 +19,7 @@ def check_config (): return True #------------------------------------------------------------------------------- -def send (msg: noti_struc): +def send (msg: noti_obj): pText = msg.text pHTML = msg.html diff --git a/pialert/publishers/ntfy.py b/pialert/publishers/ntfy.py index 957657cf..fd613f78 100755 --- a/pialert/publishers/ntfy.py +++ b/pialert/publishers/ntfy.py @@ -4,7 +4,7 @@ import requests from base64 import b64encode from logger import mylog -from helper import noti_struc +from helper import noti_obj #------------------------------------------------------------------------------- def check_config(): @@ -15,7 +15,7 @@ def check_config(): return True #------------------------------------------------------------------------------- -def send (msg: noti_struc): +def send (msg: noti_obj): headers = { "Title": "Pi.Alert Notification", diff --git a/pialert/publishers/pushsafer.py b/pialert/publishers/pushsafer.py index b8252209..eaf97c57 100755 --- a/pialert/publishers/pushsafer.py +++ b/pialert/publishers/pushsafer.py @@ -3,7 +3,7 @@ import requests import conf -from helper import noti_struc +from helper import noti_obj from logger import mylog #------------------------------------------------------------------------------- @@ -15,7 +15,7 @@ def check_config(): return True #------------------------------------------------------------------------------- -def send ( msg:noti_struc ): +def send ( msg:noti_obj ): _Text = msg.text url = 'https://www.pushsafer.com/api' post_fields = { diff --git a/pialert/publishers/webhook.py b/pialert/publishers/webhook.py index d530ac7a..7397e714 100755 --- a/pialert/publishers/webhook.py +++ b/pialert/publishers/webhook.py @@ -5,7 +5,7 @@ import hmac import conf from const import logPath -from helper import noti_struc, write_file +from helper import noti_obj, write_file from logger import logResult, mylog #------------------------------------------------------------------------------- @@ -18,7 +18,7 @@ def check_config(): #------------------------------------------------------------------------------- -def send (msg: noti_struc): +def send (msg: noti_obj): # limit = 1024 * 1024 # 1MB limit (1024 bytes * 1024 bytes = 1MB) limit = conf.WEBHOOK_SIZE diff --git a/pialert/reporting.py b/pialert/reporting.py index 7157535d..7cf512a4 100755 --- a/pialert/reporting.py +++ b/pialert/reporting.py @@ -21,7 +21,7 @@ from json2table import convert import conf import const from const import pialertPath, logPath, apiPath -from helper import noti_struc, generate_mac_links, removeDuplicateNewLines, timeNowTZ, hide_email, updateState, get_file_content, write_file +from helper import noti_obj, generate_mac_links, removeDuplicateNewLines, timeNowTZ, hide_email, updateState, get_file_content, write_file from logger import logResult, mylog, print_log from publishers.email import (check_config as email_check_config, @@ -51,7 +51,7 @@ json_final = [] def construct_notifications(db, sqlQuery, tableTitle, skipText = False, suppliedJsonStruct = None): if suppliedJsonStruct is None and sqlQuery == "": - return noti_struc("", "", "") + return noti_obj("", "", "") table_attributes = {"style" : "border-collapse: collapse; font-size: 12px; color:#70707", "width" : "100%", "cellspacing" : 0, "cellpadding" : "3px", "bordercolor" : "#C0C0C0", "border":"1"} headerProps = "width='120px' style='color:white; font-size: 16px;' bgcolor='#64a0d6' " @@ -61,11 +61,11 @@ def construct_notifications(db, sqlQuery, tableTitle, skipText = False, supplied text_line = '{}\t{}\n' if suppliedJsonStruct is None: - json_struc = db.get_table_as_json(sqlQuery) + json_obj = db.get_table_as_json(sqlQuery) else: - json_struc = suppliedJsonStruct + json_obj = suppliedJsonStruct - jsn = json_struc.json + jsn = json_obj.json html = "" text = "" @@ -78,7 +78,7 @@ def construct_notifications(db, sqlQuery, tableTitle, skipText = False, supplied # Cleanup the generated HTML table notification html = format_table(html, "data", headerProps, tableTitle).replace('