Notification rework v0.1

This commit is contained in:
Jokob-sk
2023-10-06 08:10:18 +11:00
parent 2b057d339c
commit 16de261477
8 changed files with 269 additions and 168 deletions

View File

@@ -30,8 +30,7 @@ from networkscan import process_scan
from initialise import importConfigs
from database import DB, get_all_devices
from reporting import send_notifications
from plugin_utils import check_and_run_user_event
from plugin import run_plugin_scripts
from plugin import run_plugin_scripts, check_and_run_user_event
#===============================================================================

View File

@@ -214,7 +214,7 @@ class DB():
initOrSetParam(self, 'Back_App_State','Initializing')
# -------------------------------------------------------------------------
# Parameters table setup DEPRECATED after 1/1/2024
# Nmap_Scan table setup DEPRECATED after 1/1/2024
# -------------------------------------------------------------------------
# indicates, if Nmap_Scan table is available
@@ -260,21 +260,6 @@ class DB():
self.sql.execute("DROP TABLE Nmap_Scan;")
nmapScanMissing = True
# if nmapScanMissing:
# mylog('verbose', ["[upgradeDB] Re-creating Nmap_Scan table"])
# self.sql.execute("""
# CREATE TABLE "Nmap_Scan" (
# "Index" INTEGER,
# "MAC" TEXT,
# "Port" TEXT,
# "Time" TEXT,
# "State" TEXT,
# "Service" TEXT,
# "Extra" TEXT,
# PRIMARY KEY("Index" AUTOINCREMENT)
# );
# """)
# -------------------------------------------------------------------------
# Plugins tables setup
# -------------------------------------------------------------------------

View File

@@ -528,6 +528,15 @@ class AppStateEncoder(json.JSONEncoder):
return obj.__dict__
return super().default(obj)
#-------------------------------------------------------------------------------
# Checks if the object has a __dict__ attribute. If it does, it assumes that it's an instance of a class and serializes its attributes dynamically.
class NotiStrucEncoder(json.JSONEncoder):
def default(self, obj):
if hasattr(obj, '__dict__'):
# If the object has a '__dict__', assume it's an instance of a class
return obj.__dict__
return super().default(obj)
#-------------------------------------------------------------------------------
# Creates a JSON object from a DB row
def row_to_json(names, row):
@@ -611,7 +620,17 @@ class json_struc:
#-------------------------------------------------------------------------------
class noti_struc:
def __init__(self, json, text, html):
def __init__(self, json, text, html, notificationType):
self.json = json
self.text = text
self.html = html
self.html = html
jsonFile = apiPath + f'/notifications_{notificationType}.json'
mylog('debug', [f"[Notifications] Writing {jsonFile}"])
if notificationType != '':
# Update .json file
with open(jsonFile, 'w') as jsonFile:
json.dump(self, jsonFile, cls=NotiStrucEncoder, indent=4)

View File

@@ -727,3 +727,85 @@ class plugin_object_class:
self.watchedHash = str(hash(tmp))
#===============================================================================
# Handling of user initialized front-end events
#===============================================================================
#-------------------------------------------------------------------------------
def check_and_run_user_event(db, pluginsState):
sql = db.sql # TO-DO
sql.execute(""" select * from Parameters where par_ID = "Front_Event" """)
rows = sql.fetchall()
event, param = ['','']
if len(rows) > 0 and rows[0]['par_Value'] != 'finished':
keyValue = rows[0]['par_Value'].split('|')
if len(keyValue) == 2:
event = keyValue[0]
param = keyValue[1]
else:
return pluginsState
if event == 'test':
handle_test(param)
if event == 'run':
pluginsState = handle_run(param, db, pluginsState)
# clear event execution flag
sql.execute ("UPDATE Parameters SET par_Value='finished' WHERE par_ID='Front_Event'")
# commit to DB
db.commitDB()
return pluginsState
#-------------------------------------------------------------------------------
def handle_run(runType, db, pluginsState):
mylog('minimal', ['[', timeNowTZ(), '] START Run: ', runType])
# run the plugin to run
for plugin in conf.plugins:
if plugin["unique_prefix"] == runType:
pluginsState = execute_plugin(db, plugin, pluginsState)
mylog('minimal', ['[', timeNowTZ(), '] END Run: ', runType])
return pluginsState
#-------------------------------------------------------------------------------
def handle_test(testType):
mylog('minimal', ['[', timeNowTZ(), '] START Test: ', testType])
# Open text sample
sample_txt = get_file_content(pialertPath + '/back/report_sample.txt')
# Open html sample
sample_html = get_file_content(pialertPath + '/back/report_sample.html')
# 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")
if testType == 'Email':
send_email(sample_msg)
elif testType == 'Webhooks':
send_webhook (sample_msg)
elif testType == 'Apprise':
send_apprise (sample_msg)
elif testType == 'NTFY':
send_ntfy (sample_msg)
elif testType == 'PUSHSAFER':
send_pushsafer (sample_msg)
else:
mylog('none', ['[Test Publishers] No test matches: ', testType])
mylog('minimal', ['[Test Publishers] END Test: ', testType])

View File

@@ -7,7 +7,6 @@ from const import pluginsPath, logPath
from helper import timeNowTZ, updateState, get_file_content, write_file, get_setting, get_setting_value
#-------------------------------------------------------------------------------
def logEventStatusCounts(objName, pluginEvents):
status_counts = {} # Dictionary to store counts for each status
@@ -186,83 +185,4 @@ def handle_empty(value):
return value
#===============================================================================
# Handling of user initialized front-end events
#===============================================================================
#-------------------------------------------------------------------------------
def check_and_run_user_event(db, pluginsState):
sql = db.sql # TO-DO
sql.execute(""" select * from Parameters where par_ID = "Front_Event" """)
rows = sql.fetchall()
event, param = ['','']
if len(rows) > 0 and rows[0]['par_Value'] != 'finished':
keyValue = rows[0]['par_Value'].split('|')
if len(keyValue) == 2:
event = keyValue[0]
param = keyValue[1]
else:
return pluginsState
if event == 'test':
handle_test(param)
if event == 'run':
pluginsState = handle_run(param, db, pluginsState)
# clear event execution flag
sql.execute ("UPDATE Parameters SET par_Value='finished' WHERE par_ID='Front_Event'")
# commit to DB
db.commitDB()
return pluginsState
#-------------------------------------------------------------------------------
def handle_run(runType, db, pluginsState):
mylog('minimal', ['[', timeNowTZ(), '] START Run: ', runType])
# run the plugin to run
for plugin in conf.plugins:
if plugin["unique_prefix"] == runType:
pluginsState = execute_plugin(db, plugin, pluginsState)
mylog('minimal', ['[', timeNowTZ(), '] END Run: ', runType])
return pluginsState
#-------------------------------------------------------------------------------
def handle_test(testType):
mylog('minimal', ['[', timeNowTZ(), '] START Test: ', testType])
# Open text sample
sample_txt = get_file_content(pialertPath + '/back/report_sample.txt')
# Open html sample
sample_html = get_file_content(pialertPath + '/back/report_sample.html')
# 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 )
if testType == 'Email':
send_email(sample_msg)
elif testType == 'Webhooks':
send_webhook (sample_msg)
elif testType == 'Apprise':
send_apprise (sample_msg)
elif testType == 'NTFY':
send_ntfy (sample_msg)
elif testType == 'PUSHSAFER':
send_pushsafer (sample_msg)
else:
mylog('none', ['[Test Publishers] No test matches: ', testType])
mylog('minimal', ['[Test Publishers] END Test: ', testType])

View File

@@ -50,10 +50,10 @@ json_final = []
#-------------------------------------------------------------------------------
def construct_notifications(db, sqlQuery, tableTitle, skipText = False, suppliedJsonStruct = None):
def construct_notifications(db, sqlQuery, tableTitle, skipText = False, suppliedJsonStruct = None, notificationType=''):
if suppliedJsonStruct is None and sqlQuery == "":
return noti_struc("", "", "")
return noti_struc("", "", "", notificationType)
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' "
@@ -97,7 +97,7 @@ def construct_notifications(db, sqlQuery, tableTitle, skipText = False, supplied
for header in headers:
html = format_table(html, header, thProps)
notiStruc = noti_struc(jsn, text, html)
notiStruc = noti_struc(jsn, text, html, notificationType)
if not notiStruc.json['data'] and not notiStruc.text and not notiStruc.html:
@@ -189,7 +189,7 @@ def send_notifications (db):
AND eve_EventType = 'New Device'
ORDER BY eve_DateTime"""
notiStruc = construct_notifications(db, sqlQuery, "New devices")
notiStruc = construct_notifications(db, sqlQuery, "New devices", "new_devices")
# collect "new_devices" for the webhook json
json_new_devices = notiStruc.json["data"]
@@ -205,7 +205,7 @@ def send_notifications (db):
AND eve_EventType = 'Device Down'
ORDER BY eve_DateTime"""
notiStruc = construct_notifications(db, sqlQuery, "Down devices")
notiStruc = construct_notifications(db, sqlQuery, "Down devices", "down_Devices")
# collect "down_devices" for the webhook json
json_down_devices = notiStruc.json["data"]
@@ -222,7 +222,7 @@ def send_notifications (db):
'IP Changed')
ORDER BY eve_DateTime"""
notiStruc = construct_notifications(db, sqlQuery, "Events")
notiStruc = construct_notifications(db, sqlQuery, "Events", "events")
# collect "events" for the webhook json
json_events = notiStruc.json["data"]
@@ -266,12 +266,15 @@ def send_notifications (db):
write_file (logPath + '/report_output.txt', mail_text)
write_file (logPath + '/report_output.html', mail_html)
# Write the notifications into the DB
# TODO
# Notify is something to report
if json_internet != [] or json_new_devices != [] or json_down_devices != [] or json_events != [] or json_ports != [] or plugins_report:
mylog('none', ['[Notification] Changes detected, sending reports'])
msg = noti_struc(json_final, mail_text, mail_html)
msg = noti_struc(json_final, mail_text, mail_html, 'master')
mylog('minimal', ['[Notification] Udating API files'])
send_api()
@@ -432,3 +435,56 @@ def skip_repeated_notifications (db):
db.commitDB()
#-------------------------------------------------------------------------------
# Notification object handling
#-------------------------------------------------------------------------------
class Notifications:
def __init__(self, db):
self.db = db
# Create Notifications table if missing
self.db.sql.execute("""CREATE TABLE IF NOT EXISTS "Notifications" (
"Index" INTEGER,
"DateTimeCreated" TEXT,
"DateTimePushed" TEXT,
"Status" TEXT,
"JSON" TEXT,
"Text" TEXT,
"HTML" TEXT,
"PublishedVia" TEXT,
"Extra" TEXT,
PRIMARY KEY("Index" AUTOINCREMENT)
);
""")
self.save()
def create(self, JSON, Text, HTML, Extra):
self.JSON = JSON
self.Text = Text
self.HTML = HTML
self.Extra = Extra
self.Status = "new"
# TODO Init values that can be auto initialized
# TODO Check for nulls
# TODO Index vs hash to minimize SQL calls, finish CRUD operations, expose via API, use API in plugins
# current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# self.db.sql.execute("""
# INSERT INTO Notifications (DateTimeCreated, DateTimePushed, Status, JSON, Text, HTML, PublishedVia, Extra)
# VALUES (?, ?, ?, ?, ?, ?, ?, ?)
# """, (current_time, DateTimePushed, Status, JSON, Text, HTML, PublishedVia, Extra))
def save(self):
# Commit changes
self.db.commitDB()