Merge branch 'main' into feat/german-translation
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
Report Date: <REPORT_DATE>
|
Report Date: <REPORT_DATE>
|
||||||
Server: <SERVER_NAME>
|
Server: <SERVER_NAME>
|
||||||
|
|
||||||
<SECTION_NEW_DEVICES>
|
<NEW_DEVICES_TABLE>
|
||||||
<SECTION_DEVICES_DOWN>
|
<DOWN_DEVICES_TABLE>
|
||||||
<SECTION_EVENTS>
|
<EVENTS_TABLE>
|
||||||
<PLUGINS_TABLE>
|
<PLUGINS_TABLE>
|
||||||
|
|||||||
@@ -1,90 +1,76 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"headers": {
|
"headers": {
|
||||||
"host": "192.168.1.82:5678",
|
"host": "192.168.1.82:5678",
|
||||||
"user-agent": "curl/7.74.0",
|
"user-agent": "curl/7.74.0",
|
||||||
"accept": "*/*",
|
"accept": "*/*",
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
"content-length": "872"
|
"content-length": "872"
|
||||||
},
|
},
|
||||||
"params": {},
|
"params": {},
|
||||||
"query": {},
|
"query": {},
|
||||||
"body": {
|
"body": {
|
||||||
"username": "Pi.Alert",
|
"username": "Pi.Alert",
|
||||||
"text": "There are new notifications",
|
"text": "There are new notifications",
|
||||||
"attachments": [
|
"attachments": [
|
||||||
{
|
{
|
||||||
"title": "Pi.Alert Notifications",
|
"title": "Pi.Alert Notifications",
|
||||||
"title_link": "",
|
"title_link": "",
|
||||||
"text": {
|
"text": {
|
||||||
"internet": [],
|
"internet": [],
|
||||||
"new_devices": [{
|
"new_devices": [
|
||||||
"MAC": "74:ac:74:ac:74:ac",
|
{
|
||||||
"Datetime": "2023-01-30 22:15:09",
|
"MAC": "74:ac:74:ac:74:ac",
|
||||||
"IP": "192.168.1.1",
|
"Datetime": "2023-01-30 22:15:09",
|
||||||
"Event Type": "New Device",
|
"IP": "192.168.1.1",
|
||||||
"Device name": "(name not found)",
|
"Event Type": "New Device",
|
||||||
"Comments": null,
|
"Device name": "(name not found)",
|
||||||
"Device Vendor": null
|
"Comments": null,
|
||||||
}],
|
"Device Vendor": null
|
||||||
"down_devices": [],
|
}
|
||||||
"events": [{
|
],
|
||||||
"MAC": "74:ac:74:ac:74:ac",
|
"down_devices": [],
|
||||||
"Datetime": "2023-01-30 22:15:09",
|
"events": [
|
||||||
"IP": "192.168.1.92",
|
{
|
||||||
"Event Type": "Disconnected",
|
"MAC": "74:ac:74:ac:74:ac",
|
||||||
"Device name": "(name not found)",
|
"Datetime": "2023-01-30 22:15:09",
|
||||||
"Comments": null,
|
"IP": "192.168.1.92",
|
||||||
"Device Vendor": null
|
"Event Type": "Disconnected",
|
||||||
}, {
|
"Device name": "(name not found)",
|
||||||
"MAC": "74:ac:74:ac:74:ac",
|
"Comments": null,
|
||||||
"Datetime": "2023-01-30 22:15:09",
|
"Device Vendor": null
|
||||||
"IP": "192.168.1.150",
|
},
|
||||||
"Event Type": "Disconnected",
|
{
|
||||||
"Device name": "(name not found)",
|
"MAC": "74:ac:74:ac:74:ac",
|
||||||
"Comments": null,
|
"Datetime": "2023-01-30 22:15:09",
|
||||||
"Device Vendor": null
|
"IP": "192.168.1.150",
|
||||||
}],
|
"Event Type": "Disconnected",
|
||||||
"ports": [{
|
"Device name": "(name not found)",
|
||||||
"new": {
|
"Comments": null,
|
||||||
"Name": "New device",
|
"Device Vendor": null
|
||||||
"MAC": "74:ac:74:ac:74:ac",
|
}
|
||||||
"Port": "22/tcp",
|
],
|
||||||
"State": "open",
|
"plugins": [
|
||||||
"Service": "ssh",
|
{
|
||||||
"Extra": ""
|
"Index": 138,
|
||||||
}
|
"Plugin": "INTRSPD",
|
||||||
}, {
|
"Object_PrimaryID": "Speedtest",
|
||||||
"new": {
|
"Object_SecondaryID": "2023-10-08 02:01:16+02:00",
|
||||||
"Name": "New device",
|
"DateTimeCreated": "2023-10-08 02:01:16",
|
||||||
"MAC": "74:ac:74:ac:74:ac",
|
"DateTimeChanged": "2023-10-08 02:32:15",
|
||||||
"Port": "53/tcp",
|
"Watched_Value1": "-1",
|
||||||
"State": "open",
|
"Watched_Value2": "-1",
|
||||||
"Service": "domain",
|
"Watched_Value3": "null",
|
||||||
"Extra": ""
|
"Watched_Value4": "null",
|
||||||
}
|
"Status": "missing-in-last-scan",
|
||||||
}, {
|
"Extra": "null",
|
||||||
"new": {
|
"UserData": "null",
|
||||||
"Name": "New device",
|
"ForeignKey": "null"
|
||||||
"MAC": "74:ac:74:ac:74:ac",
|
}
|
||||||
"Port": "80/tcp",
|
|
||||||
"State": "open",
|
|
||||||
"Service": "http",
|
|
||||||
"Extra": ""
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
"new": {
|
|
||||||
"Name": "New device",
|
|
||||||
"MAC": "74:ac:74:ac:74:ac",
|
|
||||||
"Port": "443/tcp",
|
|
||||||
"State": "open",
|
|
||||||
"Service": "https",
|
|
||||||
"Extra": ""
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
## Icons overview
|
## Icons overview
|
||||||
|
|
||||||
Icons are used to visually distinguish devices in the app in most of the device listing tables and the [network tree](/docs/NETWORK_TREE.md). Currently only free [Font Awesome](https://fontawesome.com/search?o=r&m=free) icons (up-to v 6.4.0) are supported (I have an unblockable [sponsorship goal](https://github.com/sponsors/jokob-sk) to add the material design icon pack).
|
Icons are used to visually distinguish devices in the app in most of the device listing tables and the [network tree](/docs/NETWORK_TREE.md). Currently only free [Font Awesome](https://fontawesome.com/search?o=r&m=free) icons (up-to v 6.4.0) are supported.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -8,6 +8,8 @@ Icons are used to visually distinguish devices in the app in most of the device
|
|||||||
|
|
||||||
You can assign icons individually on each device in the Details tab.
|
You can assign icons individually on each device in the Details tab.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
- You can click into the `Icon` field or click the Pencil (2) icon in the above screenshot to enter any text. Only [free Font Awesome](https://fontawesome.com/search?o=r&m=free) icons in the following format will work:
|
- You can click into the `Icon` field or click the Pencil (2) icon in the above screenshot to enter any text. Only [free Font Awesome](https://fontawesome.com/search?o=r&m=free) icons in the following format will work:
|
||||||
|
|||||||
BIN
docs/img/ICONS/device_icons_preview.gif
Executable file
BIN
docs/img/ICONS/device_icons_preview.gif
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
@@ -718,6 +718,10 @@ input[readonly] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Devices */
|
/* Devices */
|
||||||
|
#txtIconFA {
|
||||||
|
min-width: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
.drp-edit
|
.drp-edit
|
||||||
{
|
{
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -796,6 +800,7 @@ input[readonly] {
|
|||||||
{
|
{
|
||||||
float:left;
|
float:left;
|
||||||
display:inline;
|
display:inline;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#networkTree .portBckgIcon
|
#networkTree .portBckgIcon
|
||||||
@@ -817,6 +822,7 @@ input[readonly] {
|
|||||||
width: 25px;;
|
width: 25px;;
|
||||||
float:left;
|
float:left;
|
||||||
display:inline;
|
display:inline;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
#networkTree .netCollapse
|
#networkTree .netCollapse
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -196,9 +196,9 @@
|
|||||||
</label>
|
</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input class="form-control" id="txtIcon" type="text" value="--">
|
<span class="input-group-addon"><i class="fa" id="txtIconFA" onclick="editDrp('txtIcon');"></i></span>
|
||||||
|
<input class="form-control" id="txtIcon" type="text" value="--">
|
||||||
<span class="input-group-addon" title='<?= lang('DevDetail_button_OverwriteIcons_Tooltip');?>'><i class="fa fa-copy pointer" onclick="askOverwriteIconType();"></i></span>
|
<span class="input-group-addon" title='<?= lang('DevDetail_button_OverwriteIcons_Tooltip');?>'><i class="fa fa-copy pointer" onclick="askOverwriteIconType();"></i></span>
|
||||||
<span class="input-group-addon"><i class="fa fa-pencil pointer" onclick="editDrp('txtIcon');"></i></span>
|
|
||||||
<div class="input-group-btn">
|
<div class="input-group-btn">
|
||||||
<button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
<button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||||
<span class="fa fa-caret-down"></span>
|
<span class="fa fa-caret-down"></span>
|
||||||
@@ -749,6 +749,11 @@ function main () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Show device icon as it changes
|
||||||
|
$('#txtIcon').on('change input', function() {
|
||||||
|
$('#txtIconFA').removeClass().addClass(`fa fa-${$(this).val()} pointer`)
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1286,6 +1291,7 @@ function getDeviceData (readAllData=false) {
|
|||||||
$('#txtDeviceType').val (deviceData['dev_DeviceType']);
|
$('#txtDeviceType').val (deviceData['dev_DeviceType']);
|
||||||
$('#txtVendor').val (deviceData['dev_Vendor']);
|
$('#txtVendor').val (deviceData['dev_Vendor']);
|
||||||
$('#txtIcon').val (initDefault(deviceData['dev_Icon'], 'laptop'));
|
$('#txtIcon').val (initDefault(deviceData['dev_Icon'], 'laptop'));
|
||||||
|
$('#txtIcon').trigger('change')
|
||||||
|
|
||||||
if (deviceData['dev_Favorite'] == 1) {$('#chkFavorite').iCheck('check');} else {$('#chkFavorite').iCheck('uncheck');}
|
if (deviceData['dev_Favorite'] == 1) {$('#chkFavorite').iCheck('check');} else {$('#chkFavorite').iCheck('uncheck');}
|
||||||
$('#txtGroup').val (deviceData['dev_Group']);
|
$('#txtGroup').val (deviceData['dev_Group']);
|
||||||
@@ -1700,6 +1706,7 @@ function setTextValue (textElement, textValue) {
|
|||||||
$('#'+textElement).attr ('data-myvalue', textValue);
|
$('#'+textElement).attr ('data-myvalue', textValue);
|
||||||
$('#'+textElement).val (textValue);
|
$('#'+textElement).val (textValue);
|
||||||
}
|
}
|
||||||
|
$('#'+textElement).trigger('change')
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -628,11 +628,8 @@
|
|||||||
|
|
||||||
$("#networkTree").attr('style', `height:${treeAreaHeight}px; width:${$('.content-header').width()}px`)
|
$("#networkTree").attr('style', `height:${treeAreaHeight}px; width:${$('.content-header').width()}px`)
|
||||||
|
|
||||||
console.log('here')
|
|
||||||
|
|
||||||
myTree = Treeviz.create({
|
myTree = Treeviz.create({
|
||||||
htmlId: "networkTree",
|
htmlId: "networkTree",
|
||||||
|
|
||||||
renderNode: nodeData => {
|
renderNode: nodeData => {
|
||||||
var fontSize = "font-size:"+emSize+"em;";
|
var fontSize = "font-size:"+emSize+"em;";
|
||||||
|
|
||||||
@@ -641,31 +638,32 @@
|
|||||||
(port == "" || port == 0 ) ? portBckgIcon = `<i class="fa fa-wifi"></i>` : portBckgIcon = `<i class="fa fa-ethernet"></i>`;
|
(port == "" || port == 0 ) ? portBckgIcon = `<i class="fa fa-wifi"></i>` : portBckgIcon = `<i class="fa fa-ethernet"></i>`;
|
||||||
|
|
||||||
// Build HTML for individual nodes in the network diagram
|
// Build HTML for individual nodes in the network diagram
|
||||||
deviceIcon = (!emptyArr.includes(nodeData.data.icon )) ? "<div class='netIcon ' ><i class='fa fa-"+nodeData.data.icon +"'></i></div>" : "";
|
deviceIcon = (!emptyArr.includes(nodeData.data.icon )) ? `<div class="netIcon"><i class="fa fa-${nodeData.data.icon}"></i></div>` : "";
|
||||||
devicePort = `<div class='netPort ' style="width:${emSize*sizeCoefficient}em;height:${emSize*sizeCoefficient}em" >${port}</div> <div class="portBckgIcon" style="margin-left:-${emSize*sizeCoefficient}em;">${portBckgIcon}</div>`;
|
devicePort = `<div class="netPort" style="width:${emSize*sizeCoefficient}em;height:${emSize*sizeCoefficient}em">${port}</div> <div class="portBckgIcon" style="margin-left:-${emSize*sizeCoefficient}em;">${portBckgIcon}</div>`;
|
||||||
collapseExpandIcon = nodeData.data.hiddenChildren ? "square-plus" :"square-minus";
|
collapseExpandIcon = nodeData.data.hiddenChildren ? "square-plus" : "square-minus";
|
||||||
collapseExpandHtml = (nodeData.data.hasChildren) ? "<div class='netCollapse' style='font-size:"+emSize*sizeCoefficient+"em;' data-mytreepath='"+nodeData.data.path+"' data-mytreemac='"+nodeData.data.mac+"'><i class='fa fa-"+ collapseExpandIcon +" pointer'></i></div>" : "";
|
collapseExpandHtml = nodeData.data.hasChildren ? `<div class="netCollapse" style="font-size:${emSize*sizeCoefficient}em;" data-mytreepath='${nodeData.data.path}" data-mytreemac="${nodeData.data.mac}"><i class='fa fa-${collapseExpandIcon} pointer"></i></div>` : "";
|
||||||
statusCss = " netStatus-" + nodeData.data.status;
|
statusCss = ` netStatus-${nodeData.data.status}`;
|
||||||
|
|
||||||
selectedNodeMac = $(".nav-tabs-custom .active a").attr('data-mytabmac')
|
selectedNodeMac = $(".nav-tabs-custom .active a").attr('data-mytabmac')
|
||||||
|
|
||||||
highlightedCss = nodeData.data.mac == selectedNodeMac ? " highlightedNode" : "";
|
highlightedCss = nodeData.data.mac == selectedNodeMac ? " highlightedNode" : "";
|
||||||
|
|
||||||
return result = `<div class='box ${(nodeData.data.hasChildren)? "pointer":""} ${statusCss} ${highlightedCss}'
|
return result = `<div class="box ${nodeData.data.hasChildren ? "pointer":""} ${statusCss} ${highlightedCss}"
|
||||||
data-mytreemacmain='${nodeData.data.mac}'
|
data-mytreemacmain="${nodeData.data.mac}"
|
||||||
style='height:${nodeData.settings.nodeHeight}px;${fontSize}
|
style="height:${nodeData.settings.nodeHeight}px;${fontSize}"
|
||||||
>
|
>
|
||||||
<div class='netNodeText '>\
|
<div class="netNodeText">
|
||||||
<strong>${devicePort} ${deviceIcon}
|
<strong>${devicePort} ${deviceIcon}
|
||||||
<span class='spanNetworkTree anonymizeDev'>${nodeData.data.name}</span>\
|
<span class="spanNetworkTree anonymizeDev">${nodeData.data.name}</span>
|
||||||
</strong>
|
</strong>
|
||||||
${collapseExpandHtml}
|
${collapseExpandHtml}
|
||||||
</div></div>`;
|
</div>
|
||||||
},
|
</div>`;
|
||||||
|
},
|
||||||
|
|
||||||
onNodeClick: nodeData => {
|
onNodeClick: nodeData => {
|
||||||
console.log(this)
|
console.log(this)
|
||||||
},
|
},
|
||||||
mainAxisNodeSpacing: 'auto',
|
mainAxisNodeSpacing: 'auto',
|
||||||
// mainAxisNodeSpacing: 3,
|
// mainAxisNodeSpacing: 3,
|
||||||
secondaryAxisNodeSpacing: 0.3,
|
secondaryAxisNodeSpacing: 0.3,
|
||||||
@@ -679,13 +677,11 @@
|
|||||||
linkWidth: (nodeData) => 3,
|
linkWidth: (nodeData) => 3,
|
||||||
linkColor: (nodeData) => "#ffcc80",
|
linkColor: (nodeData) => "#ffcc80",
|
||||||
onNodeClick: (nodeData) => handleNodeClick(nodeData),
|
onNodeClick: (nodeData) => handleNodeClick(nodeData),
|
||||||
relationnalField: "children",
|
relationnalField: "children",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
console.log(myHierarchy)
|
console.log(myHierarchy)
|
||||||
|
|
||||||
|
|
||||||
myTree.refresh(myHierarchy);
|
myTree.refresh(myHierarchy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -504,25 +504,13 @@
|
|||||||
"Email_icon" : "<i class=\"fa fa-at\"></i>",
|
"Email_icon" : "<i class=\"fa fa-at\"></i>",
|
||||||
"REPORT_MAIL_name" : "Enable email",
|
"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 <code>LOG_LEVEL</code> to <code>debug</code> and check the <a href=\"/maintenance.php#tab_Logging\">error log</a>.",
|
"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 <code>LOG_LEVEL</code> to <code>debug</code> and check the <a href=\"/maintenance.php#tab_Logging\">error log</a>.",
|
||||||
"SMTP_SERVER_name" : "SMTP server URL",
|
|
||||||
"SMTP_SERVER_description" : "The SMTP server host URL. For example <code>smtp-relay.sendinblue.com</code>. To use Gmail as an SMTP server <a target=\"_blank\" href=\"https://github.com/jokob-sk/Pi.Alert/blob/main/docs/SMTP.md\">follow this guide</a>",
|
|
||||||
"SMTP_PORT_name" : "SMTP server PORT",
|
|
||||||
"SMTP_PORT_description" : "Port number used for the SMTP connection. Set to <code>0</code> if you do not want to use a port when connecting to the SMTP server.",
|
|
||||||
"SMTP_SKIP_LOGIN_name" : "Skip authentication",
|
|
||||||
"SMTP_SKIP_LOGIN_description" : "Do not use authentication when connecting to the SMTP server.",
|
|
||||||
"SMTP_USER_name" : "SMTP user",
|
|
||||||
"SMTP_USER_description" : "The user name used to login into the SMTP server (sometimes a full email address).",
|
|
||||||
"SMTP_PASS_name" : "SMTP password",
|
|
||||||
"SMTP_PASS_description" : "The SMTP server password. ",
|
|
||||||
"SMTP_SKIP_TLS_name" : "Do not use TLS",
|
|
||||||
"SMTP_SKIP_TLS_description" : "Disable TLS when connecting to your SMTP server.",
|
|
||||||
"SMTP_FORCE_SSL_name" : "Force SSL",
|
|
||||||
"SMTP_FORCE_SSL_description" : "Force SSL when connecting to your SMTP server.",
|
|
||||||
"SYSTEM_TITLE" : "System Information",
|
"SYSTEM_TITLE" : "System Information",
|
||||||
"REPORT_TO_name" : "Send email to",
|
|
||||||
"REPORT_TO_description" : "Email address to which the notification will be send to.",
|
"REPORT_TO_name" : "deprecated",
|
||||||
"REPORT_FROM_name" : "Email subject",
|
"REPORT_TO_description" : "deprecated",
|
||||||
"REPORT_FROM_description" : "Notification email subject line. Some SMTP servers need this to be an email.",
|
"REPORT_FROM_name" : "deprecated",
|
||||||
|
"REPORT_FROM_description" : "deprecated",
|
||||||
|
|
||||||
"Webhooks_display_name" : "Webhooks",
|
"Webhooks_display_name" : "Webhooks",
|
||||||
"Webhooks_icon" : "<i class=\"fa fa-circle-nodes\"></i>",
|
"Webhooks_icon" : "<i class=\"fa fa-circle-nodes\"></i>",
|
||||||
"REPORT_WEBHOOK_name" : "Enable Webhooks",
|
"REPORT_WEBHOOK_name" : "Enable Webhooks",
|
||||||
@@ -537,16 +525,6 @@
|
|||||||
"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 <code>(text was truncated)</code> message is appended.",
|
"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 <code>(text was truncated)</code> message is appended.",
|
||||||
"WEBHOOK_SECRET_name": "HMAC Secret",
|
"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 <code>X-Webhook-Signature</code> header to the request. You can find more informations <a target=\"_blank\" href=\"https://github.com/jokob-sk/Pi.Alert/blob/main/docs/WEBHOOK_SECRET.md\">here</a>.",
|
"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 <code>X-Webhook-Signature</code> header to the request. You can find more informations <a target=\"_blank\" href=\"https://github.com/jokob-sk/Pi.Alert/blob/main/docs/WEBHOOK_SECRET.md\">here</a>.",
|
||||||
"Apprise_display_name" : "Apprise",
|
|
||||||
"Apprise_icon" : "<i class=\"fa fa-bullhorn\"></i>",
|
|
||||||
"REPORT_APPRISE_name" : "Enable Apprise",
|
|
||||||
"REPORT_APPRISE_description" : "Enable sending notifications via <a target=\"_blank\" href=\"https://hub.docker.com/r/caronc/apprise\">Apprise</a>.",
|
|
||||||
"APPRISE_HOST_name" : "Apprise host URL",
|
|
||||||
"APPRISE_HOST_description" : "Apprise host URL starting with <code>http://</code> or <code>https://</code>. (do not forget to include <code>/notify</code> at the end)",
|
|
||||||
"APPRISE_URL_name" : "Apprise notification URL",
|
|
||||||
"APPRISE_URL_description" : "Apprise notification target URL. For example for Telegram it would be <code>tgram://{bot_token}/{chat_id}</code>.",
|
|
||||||
"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 <code>(text was truncated)</code> message is appended.",
|
|
||||||
"NTFY_display_name" : "NTFY",
|
"NTFY_display_name" : "NTFY",
|
||||||
"NTFY_icon" : "<i class=\"fa fa-terminal\"></i>",
|
"NTFY_icon" : "<i class=\"fa fa-terminal\"></i>",
|
||||||
"REPORT_NTFY_name" : "Enable NTFY",
|
"REPORT_NTFY_name" : "Enable NTFY",
|
||||||
@@ -565,8 +543,6 @@
|
|||||||
"REPORT_PUSHSAFER_description" : "Enable sending notifications via <a target=\"_blank\" href=\"https://www.pushsafer.com/\">Pushsafer</a>.",
|
"REPORT_PUSHSAFER_description" : "Enable sending notifications via <a target=\"_blank\" href=\"https://www.pushsafer.com/\">Pushsafer</a>.",
|
||||||
"PUSHSAFER_TOKEN_name" : "Pushsafer token",
|
"PUSHSAFER_TOKEN_name" : "Pushsafer token",
|
||||||
"PUSHSAFER_TOKEN_description" : "Your secret Pushsafer API key (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 <code>html</code> works well with emails, <code>text</code> with chat apps, such as Telegram.",
|
|
||||||
"MQTT_display_name" : "MQTT",
|
"MQTT_display_name" : "MQTT",
|
||||||
"MQTT_icon" : "<i class=\"fa fa-square-rss\"></i>",
|
"MQTT_icon" : "<i class=\"fa fa-square-rss\"></i>",
|
||||||
"REPORT_TITLE" : "Report",
|
"REPORT_TITLE" : "Report",
|
||||||
|
|||||||
@@ -542,8 +542,8 @@ Required attributes are:
|
|||||||
| `"name"` | Displayed on the Settings page. An array of localized strings. See Localized strings below. |
|
| `"name"` | Displayed on the Settings page. An array of localized strings. See Localized strings below. |
|
||||||
| `"description"` | Displayed on the Settings page. An array of localized strings. See Localized strings below. |
|
| `"description"` | Displayed on the Settings page. An array of localized strings. See Localized strings below. |
|
||||||
| (optional) `"events"` | Specifies whether to generate an execution button next to the input field of the setting. Supported values: |
|
| (optional) `"events"` | Specifies whether to generate an execution button next to the input field of the setting. Supported values: |
|
||||||
| | - `test` |
|
| | - `"test"` - For notification plugins testing |
|
||||||
| | - `run` |
|
| | - `"run"` - Regular plugins testing |
|
||||||
| (optional) `"override_value"` | Used to determine a user-defined override for the setting. Useful for template-based plugins, where you can choose to leave the current value or override it with the value defined in the setting. (Work in progress) |
|
| (optional) `"override_value"` | Used to determine a user-defined override for the setting. Useful for template-based plugins, where you can choose to leave the current value or override it with the value defined in the setting. (Work in progress) |
|
||||||
| (optional) `"events"` | Used to trigger the plugin. Usually used on the `RUN` setting. Not fully tested in all scenarios. Will show a play button next to the setting. After clicking, an event is generated for the backend in the `Parameters` database table to process the front-end event on the next run. |
|
| (optional) `"events"` | Used to trigger the plugin. Usually used on the `RUN` setting. Not fully tested in all scenarios. Will show a play button next to the setting. After clicking, an event is generated for the backend in the `Parameters` database table to process the front-end event on the next run. |
|
||||||
|
|
||||||
@@ -619,7 +619,8 @@ The UI will adjust how columns are displayed in the UI based on the resolvers de
|
|||||||
| Supported Types | Description |
|
| Supported Types | Description |
|
||||||
| -------------- | ----------- |
|
| -------------- | ----------- |
|
||||||
| `label` | Displays a column only. |
|
| `label` | Displays a column only. |
|
||||||
| `text` | Makes a column editable, and a save icon is displayed next to it. See below for information on `threshold`, `replace`. |
|
| `textarea_readonly` | Generates a read only text area and cleans up the text to display it somewhat formatted with new lines preserved. |
|
||||||
|
| See below for information on `threshold`, `replace`. | |
|
||||||
| | |
|
| | |
|
||||||
| `options` Property | Used in conjunction with types like `threshold`, `replace`, `regex`. |
|
| `options` Property | Used in conjunction with types like `threshold`, `replace`, `regex`. |
|
||||||
| `threshold` | The `options` array contains objects ordered from the lowest `maximum` to the highest. The corresponding `hexColor` is used for the value background color if it's less than the specified `maximum` but more than the previous one in the `options` array. |
|
| `threshold` | The `options` array contains objects ordered from the lowest `maximum` to the highest. The corresponding `hexColor` is used for the value background color if it's less than the specified `maximum` but more than the previous one in the `options` array. |
|
||||||
|
|||||||
8
front/plugins/_publisher_apprise/README.md
Executable file
8
front/plugins/_publisher_apprise/README.md
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
## Overview
|
||||||
|
|
||||||
|
[Apprise](front/plugins/arp_scan/README.md) is a notification gateway/publisher that allows you to push notifications to 80+ different services.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
- Go to settings and fill in relevant details.
|
||||||
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# Based on the work of https://github.com/leiweibau/Pi.Alert
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -15,87 +14,115 @@ sys.path.extend(["/home/pi/pialert/front/plugins", "/home/pi/pialert/pialert"])
|
|||||||
import conf
|
import conf
|
||||||
from plugin_helper import Plugin_Objects
|
from plugin_helper import Plugin_Objects
|
||||||
from logger import mylog, append_line_to_file
|
from logger import mylog, append_line_to_file
|
||||||
from helper import timeNowTZ, noti_struc
|
from helper import timeNowTZ, noti_obj, get_setting_value
|
||||||
|
from notification import Notification_obj
|
||||||
|
from database import DB
|
||||||
|
|
||||||
|
|
||||||
CUR_PATH = str(pathlib.Path(__file__).parent.resolve())
|
CUR_PATH = str(pathlib.Path(__file__).parent.resolve())
|
||||||
RESULT_FILE = os.path.join(CUR_PATH, 'last_result.log')
|
RESULT_FILE = os.path.join(CUR_PATH, 'last_result.log')
|
||||||
|
|
||||||
|
pluginName = 'APPRISE'
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
||||||
mylog('verbose', ['[APPRISE](publisher) In script'])
|
mylog('verbose', [f'[{pluginName}](publisher) In script'])
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description='APPRISE publisher Plugin')
|
# Check if basic config settings supplied
|
||||||
values = parser.parse_args()
|
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)
|
plugin_objects = Plugin_Objects(RESULT_FILE)
|
||||||
|
|
||||||
speedtest_result = send()
|
# Create a Notification_obj instance
|
||||||
|
notifications = Notification_obj(db)
|
||||||
|
|
||||||
plugin_objects.add_object(
|
# Retrieve new notifications
|
||||||
primaryId = 'APPRISE',
|
new_notifications = notifications.getNew()
|
||||||
secondaryId = timeNowTZ(),
|
|
||||||
watched1 = speedtest_result['download_speed'],
|
# Process the new notifications
|
||||||
watched2 = speedtest_result['upload_speed'],
|
for notification in new_notifications:
|
||||||
watched3 = 'null',
|
|
||||||
watched4 = 'null',
|
# Send notification
|
||||||
extra = 'null',
|
result = send(notification["HTML"], notification["Text"])
|
||||||
foreignKey = 'null'
|
|
||||||
)
|
# Log result
|
||||||
|
plugin_objects.add_object(
|
||||||
|
primaryId = pluginName,
|
||||||
|
secondaryId = timeNowTZ(),
|
||||||
|
watched1 = notification["GUID"],
|
||||||
|
watched2 = result,
|
||||||
|
watched3 = 'null',
|
||||||
|
watched4 = 'null',
|
||||||
|
extra = 'null',
|
||||||
|
foreignKey = 'null'
|
||||||
|
)
|
||||||
|
|
||||||
plugin_objects.write_result_file()
|
plugin_objects.write_result_file()
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
def check_config():
|
def check_config():
|
||||||
if conf.APPRISE_URL == '' or conf.APPRISE_HOST == '':
|
if get_setting_value('APPRISE_URL') == '' or get_setting_value('APPRISE_HOST') == '':
|
||||||
mylog('none', ['[Check Config] Error: Apprise service not set up correctly. Check your pialert.conf APPRISE_* variables.'])
|
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
def send(msg: noti_struc):
|
def send(html, text):
|
||||||
html = msg.html
|
|
||||||
text = msg.text
|
|
||||||
|
|
||||||
payloadData = ''
|
payloadData = ''
|
||||||
|
result = ''
|
||||||
|
|
||||||
# limit = 1024 * 1024 # 1MB limit (1024 bytes * 1024 bytes = 1MB)
|
# limit = 1024 * 1024 # 1MB limit (1024 bytes * 1024 bytes = 1MB)
|
||||||
limit = conf.APPRISE_SIZE
|
limit = get_setting_value('APPRISE_SIZE')
|
||||||
|
|
||||||
# truncate size
|
# truncate size
|
||||||
if conf.APPRISE_PAYLOAD == 'html':
|
if get_setting_value('APPRISE_PAYLOAD') == 'html':
|
||||||
if len(msg.html) > limit:
|
if len(html) > limit:
|
||||||
payloadData = msg.html[:limit] + " <h1> (text was truncated)</h1>"
|
payloadData = html[:limit] + "<h1>(text was truncated)</h1>"
|
||||||
else:
|
else:
|
||||||
payloadData = msg.html
|
payloadData = html
|
||||||
if conf.APPRISE_PAYLOAD == 'text':
|
if get_setting_value('APPRISE_PAYLOAD') == 'text':
|
||||||
if len(msg.text) > limit:
|
if len(text) > limit:
|
||||||
payloadData = msg.text[:limit] + " (text was truncated)"
|
payloadData = text[:limit] + " (text was truncated)"
|
||||||
else:
|
else:
|
||||||
payloadData = msg.text
|
payloadData = text
|
||||||
|
|
||||||
# Define Apprise compatible payload (https://github.com/caronc/apprise-api#stateless-solution)
|
# Define Apprise compatible payload (https://github.com/caronc/apprise-api#stateless-solution)
|
||||||
|
|
||||||
_json_payload = {
|
_json_payload = {
|
||||||
"urls": conf.APPRISE_URL,
|
"urls": get_setting_value('APPRISE_URL'),
|
||||||
"title": "Pi.Alert Notifications",
|
"title": "Pi.Alert Notifications",
|
||||||
"format": conf.APPRISE_PAYLOAD,
|
"format": get_setting_value('APPRISE_PAYLOAD'),
|
||||||
"body": payloadData
|
"body": payloadData
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# try runnning a subprocess
|
# 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)
|
p = subprocess.Popen(["curl","-i","-X", "POST" ,"-H", "Content-Type:application/json" ,"-d", json.dumps(_json_payload), get_setting_value('APPRISE_HOST')], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
stdout, stderr = p.communicate()
|
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
|
# 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:
|
except subprocess.CalledProcessError as e:
|
||||||
# An error occurred, handle it
|
# An error occurred, handle it
|
||||||
mylog('none', [e.output])
|
mylog('none', [e.output])
|
||||||
|
|
||||||
|
# log result
|
||||||
|
result = e.output
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(main())
|
sys.exit(main())
|
||||||
|
|||||||
@@ -1,22 +1,30 @@
|
|||||||
{
|
{
|
||||||
"code_name": "internet_speedtest",
|
"code_name": "_publisher_apprise",
|
||||||
"unique_prefix": "INTRSPD",
|
"unique_prefix": "APPRISE",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"data_source": "script",
|
"data_source": "script",
|
||||||
"show_ui": true,
|
"show_ui": true,
|
||||||
"localized": ["display_name", "description", "icon"],
|
"localized": ["display_name", "description", "icon"],
|
||||||
"display_name" : [{
|
"display_name" : [
|
||||||
|
{
|
||||||
"language_code": "en_us",
|
"language_code": "en_us",
|
||||||
"string" : "Internet speedtest"
|
"string" : "Apprise publisher"
|
||||||
}],
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string" : "Habilitar Apprise"
|
||||||
|
}
|
||||||
|
],
|
||||||
"icon":[{
|
"icon":[{
|
||||||
"language_code": "en_us",
|
"language_code": "en_us",
|
||||||
"string" : "<i class=\"fa-solid fa-gauge-high\"></i>"
|
"string" : "<i class=\"fa-solid fa-bullhorn\"></i>"
|
||||||
}],
|
}],
|
||||||
"description": [{
|
"description": [
|
||||||
|
{
|
||||||
"language_code": "en_us",
|
"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" : [],
|
"params" : [],
|
||||||
"database_column_definitions":
|
"database_column_definitions":
|
||||||
[
|
[
|
||||||
@@ -94,7 +102,7 @@
|
|||||||
"localized": ["name"],
|
"localized": ["name"],
|
||||||
"name":[{
|
"name":[{
|
||||||
"language_code": "en_us",
|
"language_code": "en_us",
|
||||||
"string" : "Test run on"
|
"string" : "Sent when"
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -118,60 +126,26 @@
|
|||||||
"column": "Watched_Value1",
|
"column": "Watched_Value1",
|
||||||
"css_classes": "col-sm-2",
|
"css_classes": "col-sm-2",
|
||||||
"show": true,
|
"show": true,
|
||||||
"type": "threshold",
|
"type": "label",
|
||||||
"default_value":"",
|
"default_value":"",
|
||||||
"options": [
|
"options": [],
|
||||||
{
|
|
||||||
"maximum": 1,
|
|
||||||
"hexColor": "#D33115"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"maximum": 5,
|
|
||||||
"hexColor": "#792D86"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"maximum": 10,
|
|
||||||
"hexColor": "#7D862D"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"maximum": 100,
|
|
||||||
"hexColor": "#05483C"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"localized": ["name"],
|
"localized": ["name"],
|
||||||
"name":[{
|
"name":[{
|
||||||
"language_code": "en_us",
|
"language_code": "en_us",
|
||||||
"string" : "Download"
|
"string" : "Notification GUID"
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"column": "Watched_Value2",
|
"column": "Watched_Value2",
|
||||||
"css_classes": "col-sm-2",
|
"css_classes": "col-sm-8",
|
||||||
"show": true,
|
"show": true,
|
||||||
"type": "threshold",
|
"type": "textarea_readonly",
|
||||||
"default_value":"",
|
"default_value":"",
|
||||||
"options": [
|
"options": [],
|
||||||
{
|
|
||||||
"maximum": 1,
|
|
||||||
"hexColor": "#D33115"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"maximum": 5,
|
|
||||||
"hexColor": "#792D86"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"maximum": 10,
|
|
||||||
"hexColor": "#7D862D"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"maximum": 100,
|
|
||||||
"hexColor": "#05483C"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"localized": ["name"],
|
"localized": ["name"],
|
||||||
"name":[{
|
"name":[{
|
||||||
"language_code": "en_us",
|
"language_code": "en_us",
|
||||||
"string" : "Upload"
|
"string" : "Result"
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -280,10 +254,10 @@
|
|||||||
"settings":[
|
"settings":[
|
||||||
{
|
{
|
||||||
"function": "RUN",
|
"function": "RUN",
|
||||||
"events": ["run"],
|
"events": ["test"],
|
||||||
"type": "text.select",
|
"type": "text.select",
|
||||||
"default_value":"disabled",
|
"default_value":"disabled",
|
||||||
"options": ["disabled", "once", "schedule", "always_after_scan" ],
|
"options": ["disabled", "on_notification" ],
|
||||||
"localized": ["name", "description"],
|
"localized": ["name", "description"],
|
||||||
"name" :[{
|
"name" :[{
|
||||||
"language_code": "en_us",
|
"language_code": "en_us",
|
||||||
@@ -293,15 +267,21 @@
|
|||||||
"language_code": "es_es",
|
"language_code": "es_es",
|
||||||
"string" : "Cuando ejecuta"
|
"string" : "Cuando ejecuta"
|
||||||
}],
|
}],
|
||||||
"description": [{
|
"description": [
|
||||||
|
{
|
||||||
"language_code": "en_us",
|
"language_code": "en_us",
|
||||||
"string" : "Enable a regular internet speedtest. If you select <code>schedule</code> the scheduling settings from below are applied. If you select <code>once</code> the scan is run only once on start of the application (container) for the time specified in <a href=\"#INTRSPD_RUN_TIMEOUT\"><code>INTRSPD_RUN_TIMEOUT</code> setting</a>."
|
"string" : "Enable sending notifications via <a target=\"_blank\" href=\"https://hub.docker.com/r/caronc/apprise\">Apprise</a>."
|
||||||
}]
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string" : "Habilitar el envío de notificaciones a través de <a target=\"_blank\" href=\"https://hub.docker.com/r/caronc/apprise\">Apprise</a>."
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"function": "CMD",
|
"function": "CMD",
|
||||||
"type": "readonly",
|
"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": [],
|
"options": [],
|
||||||
"localized": ["name", "description"],
|
"localized": ["name", "description"],
|
||||||
"name" : [{
|
"name" : [{
|
||||||
@@ -321,33 +301,10 @@
|
|||||||
"string" : "Comando a ejecutar"
|
"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 <code>schedule</code> in the <a href=\"#INTRSPD_RUN\"><code>INTRSPD_RUN</code> setting</a>. Make sure you enter the schedule in the correct cron-like format (e.g. validate at <a href=\"https://crontab.guru/\" target=\"_blank\">crontab.guru</a>). For example entering <code>0 4 * * *</code> will run the scan after 4 am in the <a onclick=\"toggleAllSettings()\" href=\"#TIMEZONE\"><code>TIMEZONE</code> you set above</a>. Will be run NEXT time the time passes."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"language_code": "es_es",
|
|
||||||
"string": "Solo habilitado si selecciona <code>schedule</code> en la configuración <a href=\"#INTRSPD_RUN\"><code>INTRSPD_RUN</code></a>. Asegúrese de ingresar el schedule en el formato similar a cron correcto (por ejemplo, valide en <a href=\"https://crontab.guru/\" target=\"_blank\">crontab.guru</a>). Por ejemplo, ingrese <code >0 4 * * *</code> ejecutará el escaneo después de las 4 am en el <a onclick=\"toggleAllSettings()\" href=\"#TIMEZONE\"><code>TIMEZONE</code> que configuró arriba </a>. Se ejecutará la PRÓXIMA vez que pase el tiempo."
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"function": "RUN_TIMEOUT",
|
"function": "RUN_TIMEOUT",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"default_value":60,
|
"default_value": 10,
|
||||||
"options": [],
|
"options": [],
|
||||||
"localized": ["name", "description"],
|
"localized": ["name", "description"],
|
||||||
"name" : [{
|
"name" : [{
|
||||||
@@ -372,45 +329,95 @@
|
|||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"function": "WATCH",
|
"function": "HOST",
|
||||||
"type": "text.multiselect",
|
"type": "text",
|
||||||
"default_value":[],
|
"default_value": "",
|
||||||
"options": ["Watched_Value1","Watched_Value2","Watched_Value3","Watched_Value4"],
|
"options": [],
|
||||||
"localized": ["name", "description"],
|
"localized": ["name", "description"],
|
||||||
"name" :[{
|
"name" : [{
|
||||||
"language_code": "en_us",
|
"language_code": "en_us",
|
||||||
"string" : "Watched"
|
"string" : "Apprise host URL"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"language_code": "es_es",
|
"language_code": "es_es",
|
||||||
"string" : "Visto"
|
"string" : "URL del host de Apprise"
|
||||||
}],
|
}],
|
||||||
"description":[{
|
"description": [{
|
||||||
"language_code": "en_us",
|
"language_code": "en_us",
|
||||||
"string" : "Send a notification if selected values change. Use <code>CTRL + Click</code> to select/deselect. <ul> <li><code>Watched_Value1</code> is Download speed (not recommended)</li><li><code>Watched_Value2</code> is Upload speed (not recommended)</li><li><code>Watched_Value3</code> unused </li><li><code>Watched_Value4</code> unused </li></ul>"
|
"string" : "Apprise host URL starting with <code>http://</code> or <code>https://</code>. (do not forget to include <code>/notify</code> at the end)"
|
||||||
}]
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string" : "URL del host de Apprise que comienza con <code>http://</code> o <code>https://</code>. (no olvide incluir <code>/notify</code> al final)"
|
||||||
|
}]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"function": "REPORT_ON",
|
"function": "URL",
|
||||||
"type": "text.multiselect",
|
"type": "text",
|
||||||
"default_value":[],
|
"default_value": "",
|
||||||
"options": ["new","watched-changed","watched-not-changed", "missing-in-last-scan"],
|
"options": [],
|
||||||
"localized": ["name", "description"],
|
"localized": ["name", "description"],
|
||||||
"name" :[{
|
"name" : [{
|
||||||
"language_code": "en_us",
|
"language_code": "en_us",
|
||||||
"string" : "Report on"
|
"string" : "Apprise notification URL"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"language_code": "es_es",
|
"language_code": "es_es",
|
||||||
"string" : "Informar sobre"
|
"string" : "URL de notificación de Apprise"
|
||||||
}] ,
|
}],
|
||||||
"description":[{
|
"description": [{
|
||||||
"language_code": "en_us",
|
"language_code": "en_us",
|
||||||
"string" : "Send a notification only on these statuses. <code>new</code> means a new unique (unique combination of PrimaryId and SecondaryId) object was discovered. <code>watched-changed</code> means that selected <code>Watched_ValueN</code> columns changed."
|
"string" : "Apprise notification target URL. For example for Telegram it would be <code>tgram://{bot_token}/{chat_id}</code>."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"language_code": "es_es",
|
"language_code": "es_es",
|
||||||
"string" : "Envíe una notificación solo en estos estados. <code>new</code> significa que se descubrió un nuevo objeto único (combinación única de PrimaryId y SecondaryId). <code>watched-changed</code> significa que seleccionó <code>Watched_ValueN Las columnas </code> cambiaron."
|
"string" : "Informar de la URL de destino de la notificación. Por ejemplo, para Telegram sería <code>tgram://{bot_token}/{chat_id}</code>."
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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 <code>html</code> works well with emails, <code>text</code> with chat apps, such as Telegram."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string" : "Seleccione el tipo de carga útil enviada a Apprise. Por ejemplo, <code>html</code> funciona bien con correos electrónicos, <code>text</code> 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 <code>(text was truncated)</code> 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 <code>(text was truncated)</code>."
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
This plugin will not be loaded
|
|
||||||
8
front/plugins/_publisher_email/README.md
Executable file
8
front/plugins/_publisher_email/README.md
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
## Overview
|
||||||
|
|
||||||
|
[Apprise](front/plugins/arp_scan/README.md) is a notification gateway/publisher that allows you to push notifications to 80+ different services.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
- Go to settings and fill in relevant details.
|
||||||
|
|
||||||
683
front/plugins/_publisher_email/config.json
Executable file
683
front/plugins/_publisher_email/config.json
Executable file
@@ -0,0 +1,683 @@
|
|||||||
|
{
|
||||||
|
"code_name": "_publisher_email",
|
||||||
|
"unique_prefix": "SMTP",
|
||||||
|
"enabled": true,
|
||||||
|
"data_source": "script",
|
||||||
|
"show_ui": true,
|
||||||
|
"localized": [
|
||||||
|
"display_name",
|
||||||
|
"description",
|
||||||
|
"icon"
|
||||||
|
],
|
||||||
|
"display_name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Email publisher (SMTP)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "Habilitar email (SMTP)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"icon": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "<i class=\"fa-solid fa-at\"></i>"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "A plugin to publish a notification via Email (SMTP) gateway."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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": "url",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "N/A"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": "Object_SecondaryID",
|
||||||
|
"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": "DateTimeCreated",
|
||||||
|
"css_classes": "col-sm-2",
|
||||||
|
"show": true,
|
||||||
|
"type": "label",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Sent when"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": "DateTimeChanged",
|
||||||
|
"css_classes": "col-sm-2",
|
||||||
|
"show": false,
|
||||||
|
"type": "label",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Changed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "Cambiado"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": "Watched_Value1",
|
||||||
|
"css_classes": "col-sm-2",
|
||||||
|
"show": true,
|
||||||
|
"type": "label",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Notification GUID"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": "Watched_Value2",
|
||||||
|
"css_classes": "col-sm-8",
|
||||||
|
"show": true,
|
||||||
|
"type": "textarea_readonly",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Result"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": "Watched_Value3",
|
||||||
|
"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": "Watched_Value4",
|
||||||
|
"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": "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": "<div style='text-align:center'><i class='fa-solid fa-square-check'></i><div></div>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"equals": "watched-changed",
|
||||||
|
"replacement": "<div style='text-align:center'><i class='fa-solid fa-triangle-exclamation'></i></div>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"equals": "new",
|
||||||
|
"replacement": "<div style='text-align:center'><i class='fa-solid fa-circle-plus'></i></div>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"equals": "missing-in-last-scan",
|
||||||
|
"replacement": "<div style='text-align:center'><i class='fa-solid fa-question'></i></div>"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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 sending notifications via the Email (SMTP) gateway."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "Si está habilitado, se envía un correo electrónico con una lista de cambios a los que se ha suscrito. Complete también todas las configuraciones restantes relacionadas con la configuración de SMTP a continuación"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "CMD",
|
||||||
|
"type": "readonly",
|
||||||
|
"default_value": "python3 /home/pi/pialert/front/plugins/_publisher_email/email_smtp.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": 20,
|
||||||
|
"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": "SERVER",
|
||||||
|
"type": "text",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "SMTP server URL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "URL del servidor SMTP"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "The SMTP server host URL. For example <code>smtp-relay.sendinblue.com</code>. To use Gmail as an SMTP server <a target=\"_blank\" href=\"https://github.com/jokob-sk/Pi.Alert/blob/main/docs/SMTP.md\">follow this guide</a>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "La URL del host del servidor SMTP. Por ejemplo, <code>smtp-relay.sendinblue.com</code>. Para utilizar Gmail como servidor SMTP <a target=\"_blank\" href=\"https://github.com/jokob-sk/Pi.Alert/blob/main/docs/SMTP.md\">siga esta guía</a >"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "PORT",
|
||||||
|
"type": "integer",
|
||||||
|
"default_value": 587,
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "SMTP server PORT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "Puerto del servidor SMTP"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Port number used for the SMTP connection. Set to <code>0</code> if you do not want to use a port when connecting to the SMTP server."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "Número de puerto utilizado para la conexión SMTP. Establézcalo en <code>0</code> si no desea utilizar un puerto al conectarse al servidor SMTP."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "SKIP_LOGIN",
|
||||||
|
"type": "boolean",
|
||||||
|
"default_value": false,
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Skip authentication"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "Omitir autenticación"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Do not use authentication when connecting to the SMTP server."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "No utilice la autenticación cuando se conecte al servidor SMTP."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "USER",
|
||||||
|
"type": "text",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "SMTP user"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "Nombre de usuario SMTP"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "The user name used to login into the SMTP server (sometimes a full email address)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "El nombre de usuario utilizado para iniciar sesión en el servidor SMTP (a veces, una dirección de correo electrónico completa)."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "PASS",
|
||||||
|
"type": "password",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "SMTP password"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "Contraseña de SMTP"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "The SMTP server password."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "La contraseña del servidor SMTP."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "SKIP_TLS",
|
||||||
|
"type": "boolean",
|
||||||
|
"default_value": false,
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Do not use TLS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "No usar TLS"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Disable TLS when connecting to your SMTP server."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "Deshabilite TLS cuando se conecte a su servidor SMTP."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "FORCE_SSL",
|
||||||
|
"type": "boolean",
|
||||||
|
"default_value": false,
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Force SSL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "Forzar SSL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Force SSL when connecting to your SMTP server."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "Forzar SSL al conectarse a su servidor SMTP"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "REPORT_TO",
|
||||||
|
"type": "text",
|
||||||
|
"default_value": "user@gmail.com",
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Send email to"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "Enviar el email a"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Email address to which the notification will be send to."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "Dirección de correo electrónico a la que se enviará la notificación."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "REPORT_FROM",
|
||||||
|
"type": "text",
|
||||||
|
"default_value": "Pi.Alert <user@gmail.com>",
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Email subject"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "Asunto del email"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Notification email subject line. Some SMTP servers need this to be an email."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language_code": "es_es",
|
||||||
|
"string": "Asunto del correo electrónico de notificación."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
160
front/plugins/_publisher_email/email_smtp.py
Executable file
160
front/plugins/_publisher_email/email_smtp.py
Executable file
@@ -0,0 +1,160 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
import smtplib
|
||||||
|
import socket
|
||||||
|
|
||||||
|
# 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 plugin_helper import Plugin_Objects
|
||||||
|
from logger import mylog, append_line_to_file, print_log
|
||||||
|
from helper import timeNowTZ, noti_obj, get_setting_value, hide_email
|
||||||
|
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 = 'SMTP'
|
||||||
|
|
||||||
|
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
|
||||||
|
for notification in new_notifications:
|
||||||
|
|
||||||
|
# Send notification
|
||||||
|
result = send(notification["HTML"], notification["Text"])
|
||||||
|
|
||||||
|
# Log result
|
||||||
|
plugin_objects.add_object(
|
||||||
|
primaryId = pluginName,
|
||||||
|
secondaryId = timeNowTZ(),
|
||||||
|
watched1 = notification["GUID"],
|
||||||
|
watched2 = result,
|
||||||
|
watched3 = 'null',
|
||||||
|
watched4 = 'null',
|
||||||
|
extra = 'null',
|
||||||
|
foreignKey = 'null'
|
||||||
|
)
|
||||||
|
|
||||||
|
plugin_objects.write_result_file()
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
def check_config ():
|
||||||
|
if get_setting_value('SMTP_SERVER') == '' or get_setting_value('SMTP_REPORT_FROM') == '' or get_setting_value('SMTP_REPORT_TO') == '':
|
||||||
|
mylog('none', ['[Email Check Config] Error: Email service not set up correctly. Check your pialert.conf SMTP_*, SMTP_REPORT_FROM and SMTP_REPORT_TO variables.'])
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
def send(pHTML, pText):
|
||||||
|
|
||||||
|
mylog('debug', [f'[{pluginName}] SMTP_REPORT_TO: {hide_email(str(get_setting_value("SMTP_REPORT_TO")))} SMTP_USER: {hide_email(str(get_setting_value("SMTP_USER")))}'])
|
||||||
|
|
||||||
|
# Compose email
|
||||||
|
msg = MIMEMultipart('alternative')
|
||||||
|
msg['Subject'] = 'Pi.Alert Report'
|
||||||
|
msg['From'] = get_setting_value('SMTP_REPORT_FROM')
|
||||||
|
msg['To'] = get_setting_value('SMTP_REPORT_TO')
|
||||||
|
msg.attach (MIMEText (pText, 'plain'))
|
||||||
|
msg.attach (MIMEText (pHTML, 'html'))
|
||||||
|
|
||||||
|
failedAt = ''
|
||||||
|
|
||||||
|
failedAt = print_log ('SMTP try')
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Send mail
|
||||||
|
failedAt = print_log('Trying to open connection to ' + str(get_setting_value('SMTP_SERVER')) + ':' + str(get_setting_value('SMTP_PORT')))
|
||||||
|
|
||||||
|
# Set a timeout for the SMTP connection (in seconds)
|
||||||
|
smtp_timeout = 30
|
||||||
|
|
||||||
|
if get_setting_value('SMTP_FORCE_SSL'):
|
||||||
|
failedAt = print_log('SMTP_FORCE_SSL == True so using .SMTP_SSL()')
|
||||||
|
if get_setting_value('SMTP_PORT') == 0:
|
||||||
|
failedAt = print_log('SMTP_PORT == 0 so sending .SMTP_SSL(SMTP_SERVER)')
|
||||||
|
smtp_connection = smtplib.SMTP_SSL(get_setting_value('SMTP_SERVER'))
|
||||||
|
else:
|
||||||
|
failedAt = print_log('SMTP_PORT == 0 so sending .SMTP_SSL(SMTP_SERVER, SMTP_PORT)')
|
||||||
|
smtp_connection = smtplib.SMTP_SSL(get_setting_value('SMTP_SERVER'), get_setting_value('SMTP_PORT'), timeout=smtp_timeout)
|
||||||
|
|
||||||
|
else:
|
||||||
|
failedAt = print_log('SMTP_FORCE_SSL == False so using .SMTP()')
|
||||||
|
if get_setting_value('SMTP_PORT') == 0:
|
||||||
|
failedAt = print_log('SMTP_PORT == 0 so sending .SMTP(SMTP_SERVER)')
|
||||||
|
smtp_connection = smtplib.SMTP (get_setting_value('SMTP_SERVER'))
|
||||||
|
else:
|
||||||
|
failedAt = print_log('SMTP_PORT == 0 so sending .SMTP(SMTP_SERVER, SMTP_PORT)')
|
||||||
|
smtp_connection = smtplib.SMTP (get_setting_value('SMTP_SERVER'), get_setting_value('SMTP_PORT'))
|
||||||
|
|
||||||
|
failedAt = print_log('Setting SMTP debug level')
|
||||||
|
|
||||||
|
# Log level set to debug of the communication between SMTP server and client
|
||||||
|
if get_setting_value('LOG_LEVEL') == 'debug':
|
||||||
|
smtp_connection.set_debuglevel(1)
|
||||||
|
|
||||||
|
failedAt = print_log( 'Sending .ehlo()')
|
||||||
|
smtp_connection.ehlo()
|
||||||
|
|
||||||
|
if not get_setting_value('SMTP_SKIP_TLS'):
|
||||||
|
failedAt = print_log('SMTP_SKIP_TLS == False so sending .starttls()')
|
||||||
|
smtp_connection.starttls()
|
||||||
|
failedAt = print_log('SMTP_SKIP_TLS == False so sending .ehlo()')
|
||||||
|
smtp_connection.ehlo()
|
||||||
|
if not get_setting_value('SMTP_SKIP_LOGIN'):
|
||||||
|
failedAt = print_log('SMTP_SKIP_LOGIN == False so sending .login()')
|
||||||
|
smtp_connection.login (get_setting_value('SMTP_USER'), get_setting_value('SMTP_PASS'))
|
||||||
|
|
||||||
|
failedAt = print_log('Sending .sendmail()')
|
||||||
|
smtp_connection.sendmail (get_setting_value('SMTP_REPORT_FROM'), get_setting_value('SMTP_REPORT_TO'), msg.as_string())
|
||||||
|
smtp_connection.quit()
|
||||||
|
except smtplib.SMTPAuthenticationError as e:
|
||||||
|
mylog('none', [' ERROR: Failed at - ', failedAt])
|
||||||
|
mylog('none', [' ERROR: Couldn\'t connect to the SMTP server (SMTPAuthenticationError), skipping Email (enable LOG_LEVEL=debug for more logging)'])
|
||||||
|
mylog('none', [' ERROR: ', str(e)])
|
||||||
|
except smtplib.SMTPServerDisconnected as e:
|
||||||
|
mylog('none', [' ERROR: Failed at - ', failedAt])
|
||||||
|
mylog('none', [' ERROR: Couldn\'t connect to the SMTP server (SMTPServerDisconnected), skipping Email (enable LOG_LEVEL=debug for more logging)'])
|
||||||
|
mylog('none', [' ERROR: ', str(e)])
|
||||||
|
except socket.gaierror as e:
|
||||||
|
mylog('none', [' ERROR: Failed at - ', failedAt])
|
||||||
|
mylog('none', [' ERROR: Could not resolve hostname (socket.gaierror), skipping Email (enable LOG_LEVEL=debug for more logging)'])
|
||||||
|
mylog('none', [' ERROR: ', str(e)])
|
||||||
|
|
||||||
|
mylog('debug', [f'[{pluginName}] Last executed - {str(failedAt)}'])
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
@@ -1 +0,0 @@
|
|||||||
This plugin will not be loaded
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# Based on the work of https://github.com/leiweibau/Pi.Alert
|
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
|
|||||||
@@ -89,6 +89,11 @@ function processColumnValue(dbColumnDef, value, index, type) {
|
|||||||
case 'label':
|
case 'label':
|
||||||
value = `<span>${value}<span>`;
|
value = `<span>${value}<span>`;
|
||||||
break;
|
break;
|
||||||
|
case 'textarea_readonly':
|
||||||
|
value = `<textarea cols="70" rows="3" wrap="off" readonly style="white-space: pre-wrap;">
|
||||||
|
${value.replace(/^b'(.*)'$/gm, '$1').replace(/\\n/g, '\n').replace(/\\r/g, '\r')}
|
||||||
|
</textarea>`;
|
||||||
|
break;
|
||||||
case 'textbox_save':
|
case 'textbox_save':
|
||||||
|
|
||||||
value = value == 'null' ? '' : value; // hide 'null' values
|
value = value == 'null' ? '' : value; // hide 'null' values
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
|||||||
const settingGroups = [];
|
const settingGroups = [];
|
||||||
const settingKeyOfLists = [];
|
const settingKeyOfLists = [];
|
||||||
// core groups are the ones not generated by plugins
|
// core groups are the ones not generated by plugins
|
||||||
const settingCoreGroups = ['General', 'Email', 'Webhooks', 'Apprise', 'NTFY', 'PUSHSAFER', 'MQTT', 'DynDNS', 'API'];
|
const settingCoreGroups = ['General'];
|
||||||
|
|
||||||
|
|
||||||
// Loop through the settingsArray and collect unique settingGroups
|
// Loop through the settingsArray and collect unique settingGroups
|
||||||
@@ -750,7 +750,6 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
|||||||
|
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
// Show last time settings have been imported
|
// Show last time settings have been imported
|
||||||
// getParam("lastImportedTime", "Back_Settings_Imported", skipCache = true);
|
|
||||||
|
|
||||||
handleLoadingDialog()
|
handleLoadingDialog()
|
||||||
|
|
||||||
|
|||||||
@@ -24,12 +24,13 @@ import multiprocessing
|
|||||||
import conf
|
import conf
|
||||||
from const import *
|
from const import *
|
||||||
from logger import mylog
|
from logger import mylog
|
||||||
from helper import filePermissions, timeNowTZ, updateState, get_setting_value
|
from helper import filePermissions, timeNowTZ, updateState, get_setting_value, noti_obj
|
||||||
from api import update_api
|
from api import update_api
|
||||||
from networkscan import process_scan
|
from networkscan import process_scan
|
||||||
from initialise import importConfigs
|
from initialise import importConfigs
|
||||||
from database import DB, get_all_devices
|
from database import DB
|
||||||
from reporting import send_notifications
|
from reporting import get_notifications
|
||||||
|
from notification import Notification_obj
|
||||||
from plugin import run_plugin_scripts, check_and_run_user_event
|
from plugin import run_plugin_scripts, check_and_run_user_event
|
||||||
|
|
||||||
|
|
||||||
@@ -146,8 +147,38 @@ def main ():
|
|||||||
# run all plugins registered to be run when new devices are found
|
# run all plugins registered to be run when new devices are found
|
||||||
pluginsState = run_plugin_scripts(db, 'on_new_device', pluginsState)
|
pluginsState = run_plugin_scripts(db, 'on_new_device', pluginsState)
|
||||||
|
|
||||||
|
# Notification handling
|
||||||
|
# ----------------------------------------
|
||||||
|
|
||||||
# send all configured notifications
|
# send all configured notifications
|
||||||
send_notifications(db)
|
notiStructure = get_notifications(db)
|
||||||
|
|
||||||
|
# Write the notifications into the DB
|
||||||
|
notification = Notification_obj(db)
|
||||||
|
notificationObj = notification.create(notiStructure.json, notiStructure.text, notiStructure.html, "")
|
||||||
|
|
||||||
|
# run all enabled publisher gateways
|
||||||
|
if notificationObj.HasNotifications:
|
||||||
|
pluginsState = run_plugin_scripts(db, 'on_notification', pluginsState)
|
||||||
|
notification.setAllProcessed()
|
||||||
|
|
||||||
|
# Clean Pending Alert Events
|
||||||
|
sql.execute ("""UPDATE Devices SET dev_LastNotification = ?
|
||||||
|
WHERE dev_MAC IN (
|
||||||
|
SELECT eve_MAC FROM Events
|
||||||
|
WHERE eve_PendingAlertEmail = 1
|
||||||
|
)
|
||||||
|
""", (timeNowTZ(),) )
|
||||||
|
sql.execute ("""UPDATE Events SET eve_PendingAlertEmail = 0
|
||||||
|
WHERE eve_PendingAlertEmail = 1""")
|
||||||
|
|
||||||
|
# clear plugin events
|
||||||
|
sql.execute ("DELETE FROM Plugins_Events")
|
||||||
|
|
||||||
|
# DEBUG - print number of rows updated
|
||||||
|
mylog('minimal', ['[Notification] Notifications changes: ', sql.rowcount])
|
||||||
|
else:
|
||||||
|
mylog('verbose', ['[Notification] No changes to report'])
|
||||||
|
|
||||||
# Commit SQL
|
# Commit SQL
|
||||||
db.commitDB()
|
db.commitDB()
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ import json
|
|||||||
|
|
||||||
# pialert modules
|
# pialert modules
|
||||||
import conf
|
import conf
|
||||||
from const import (apiPath, sql_devices_all, sql_events_pending_alert,
|
from const import (apiPath, sql_devices_all, sql_events_pending_alert, sql_settings, sql_plugins_events, sql_plugins_history, sql_plugins_objects,sql_language_strings, sql_notifications_all)
|
||||||
sql_settings, sql_plugins_events, sql_plugins_history, sql_plugins_objects,sql_language_strings)
|
|
||||||
from logger import mylog
|
from logger import mylog
|
||||||
from helper import write_file
|
from helper import write_file
|
||||||
|
|
||||||
@@ -19,8 +18,6 @@ def update_api(db, isNotification = False, updateOnlyDataSources = []):
|
|||||||
|
|
||||||
folder = apiPath
|
folder = apiPath
|
||||||
|
|
||||||
# update notifications moved to reporting send_api()
|
|
||||||
|
|
||||||
# Save plugins
|
# Save plugins
|
||||||
write_file(folder + 'plugins.json' , json.dumps({"data" : conf.plugins}))
|
write_file(folder + 'plugins.json' , json.dumps({"data" : conf.plugins}))
|
||||||
|
|
||||||
@@ -33,6 +30,7 @@ def update_api(db, isNotification = False, updateOnlyDataSources = []):
|
|||||||
["plugins_history", sql_plugins_history],
|
["plugins_history", sql_plugins_history],
|
||||||
["plugins_objects", sql_plugins_objects],
|
["plugins_objects", sql_plugins_objects],
|
||||||
["plugins_language_strings", sql_language_strings],
|
["plugins_language_strings", sql_language_strings],
|
||||||
|
["notifications", sql_notifications_all],
|
||||||
["custom_endpoint", conf.API_CUSTOM_SQL],
|
["custom_endpoint", conf.API_CUSTOM_SQL],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
125
pialert/appevent.py
Normal file
125
pialert/appevent.py
Normal file
@@ -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, <missing event>
|
||||||
|
"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()
|
||||||
|
|
||||||
@@ -70,12 +70,6 @@ WEBHOOK_PAYLOAD = 'json'
|
|||||||
WEBHOOK_REQUEST_METHOD = 'GET'
|
WEBHOOK_REQUEST_METHOD = 'GET'
|
||||||
WEBHOOK_SECRET = ''
|
WEBHOOK_SECRET = ''
|
||||||
|
|
||||||
# Apprise
|
|
||||||
REPORT_APPRISE = False
|
|
||||||
APPRISE_HOST = ''
|
|
||||||
APPRISE_URL = ''
|
|
||||||
APPRISE_PAYLOAD = 'html'
|
|
||||||
|
|
||||||
# NTFY
|
# NTFY
|
||||||
REPORT_NTFY = False
|
REPORT_NTFY = False
|
||||||
NTFY_HOST = 'https://ntfy.sh'
|
NTFY_HOST = 'https://ntfy.sh'
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ sql_events_pending_alert = "SELECT * FROM Events where eve_PendingAlertEmail is
|
|||||||
sql_settings = "SELECT * FROM Settings"
|
sql_settings = "SELECT * FROM Settings"
|
||||||
sql_plugins_objects = "SELECT * FROM Plugins_Objects"
|
sql_plugins_objects = "SELECT * FROM Plugins_Objects"
|
||||||
sql_language_strings = "SELECT * FROM Plugins_Language_Strings"
|
sql_language_strings = "SELECT * FROM Plugins_Language_Strings"
|
||||||
|
sql_notifications_all = "SELECT * FROM Notifications"
|
||||||
sql_plugins_events = "SELECT * FROM Plugins_Events"
|
sql_plugins_events = "SELECT * FROM Plugins_Events"
|
||||||
sql_plugins_history = "SELECT * FROM Plugins_History ORDER BY DateTimeChanged DESC"
|
sql_plugins_history = "SELECT * FROM Plugins_History ORDER BY DateTimeChanged DESC"
|
||||||
sql_new_devices = """SELECT * FROM (
|
sql_new_devices = """SELECT * FROM (
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import sqlite3
|
|||||||
from const import fullDbPath, sql_devices_stats, sql_devices_all
|
from const import fullDbPath, sql_devices_stats, sql_devices_all
|
||||||
|
|
||||||
from logger import mylog
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -210,9 +210,6 @@ class DB():
|
|||||||
);
|
);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
# Initialize Parameters if unavailable
|
|
||||||
initOrSetParam(self, 'Back_App_State','Initializing')
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
# Nmap_Scan table setup DEPRECATED after 1/1/2024
|
# Nmap_Scan table setup DEPRECATED after 1/1/2024
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
@@ -384,7 +381,7 @@ class DB():
|
|||||||
rows = self.sql.fetchall()
|
rows = self.sql.fetchall()
|
||||||
except sqlite3.Error as e:
|
except sqlite3.Error as e:
|
||||||
mylog('none',[ '[Database] - SQL ERROR: ', e])
|
mylog('none',[ '[Database] - SQL ERROR: ', e])
|
||||||
return None
|
return json_obj({}, []) # return empty object
|
||||||
|
|
||||||
result = {"data":[]}
|
result = {"data":[]}
|
||||||
for row in rows:
|
for row in rows:
|
||||||
@@ -392,7 +389,7 @@ class DB():
|
|||||||
result["data"].append(tmp)
|
result["data"].append(tmp)
|
||||||
|
|
||||||
# mylog('debug',[ '[Database] - get_table_as_json - returning ', len(rows), " rows with columns: ", columnNames])
|
# 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
|
# referece from here: https://codereview.stackexchange.com/questions/241043/interface-class-for-sqlite-databases
|
||||||
|
|||||||
@@ -14,17 +14,10 @@ def save_scanned_devices (db):
|
|||||||
sql = db.sql #TO-DO
|
sql = db.sql #TO-DO
|
||||||
|
|
||||||
|
|
||||||
# #76 Add Local MAC of default local interface
|
# Add Local MAC of default local interface
|
||||||
# BUGFIX #106 - Device that pialert is running
|
|
||||||
# local_mac_cmd = ["bash -lc ifconfig `ip route list default | awk {'print $5'}` | grep ether | awk '{print $2}'"]
|
|
||||||
# local_mac_cmd = ["/sbin/ifconfig `ip route list default | sort -nk11 | head -1 | awk {'print $5'}` | grep ether | awk '{print $2}'"]
|
|
||||||
local_mac_cmd = ["/sbin/ifconfig `ip -o route get 1 | sed 's/^.*dev \\([^ ]*\\).*$/\\1/;q'` | grep ether | awk '{print $2}'"]
|
local_mac_cmd = ["/sbin/ifconfig `ip -o route get 1 | sed 's/^.*dev \\([^ ]*\\).*$/\\1/;q'` | grep ether | awk '{print $2}'"]
|
||||||
local_mac = subprocess.Popen (local_mac_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0].decode().strip()
|
local_mac = subprocess.Popen (local_mac_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0].decode().strip()
|
||||||
|
|
||||||
# local_dev_cmd = ["ip -o route get 1 | sed 's/^.*dev \\([^ ]*\\).*$/\\1/;q'"]
|
|
||||||
# local_dev = subprocess.Popen (local_dev_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0].decode().strip()
|
|
||||||
|
|
||||||
# local_ip_cmd = ["ip route list default | awk {'print $7'}"]
|
|
||||||
local_ip_cmd = ["ip -o route get 1 | sed 's/^.*src \\([^ ]*\\).*$/\\1/;q'"]
|
local_ip_cmd = ["ip -o route get 1 | sed 's/^.*src \\([^ ]*\\).*$/\\1/;q'"]
|
||||||
local_ip = subprocess.Popen (local_ip_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0].decode().strip()
|
local_ip = subprocess.Popen (local_ip_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0].decode().strip()
|
||||||
|
|
||||||
@@ -149,7 +142,7 @@ def create_new_devices (db):
|
|||||||
FROM CurrentScan"""
|
FROM CurrentScan"""
|
||||||
|
|
||||||
|
|
||||||
# mylog('debug',f'[New Devices] 2 Create devices SQL: {sqlQuery}')
|
mylog('debug',f'[New Devices] 2 Create devices SQL: {sqlQuery}')
|
||||||
|
|
||||||
sql.execute (sqlQuery, (startTime, startTime) )
|
sql.execute (sqlQuery, (startTime, startTime) )
|
||||||
|
|
||||||
|
|||||||
@@ -243,36 +243,60 @@ def write_file(pPath, pText):
|
|||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# Return whole setting touple
|
# Return whole setting touple
|
||||||
def get_setting(key):
|
def get_setting(key):
|
||||||
result = None
|
|
||||||
# index order: key, name, desc, inputtype, options, regex, result, group, events
|
|
||||||
for set in conf.mySettings:
|
|
||||||
if set[0] == key:
|
|
||||||
result = set
|
|
||||||
|
|
||||||
if result is None:
|
settingsFile = apiPath + 'table_settings.json'
|
||||||
mylog('minimal', [' Error - setting_missing - Setting not found for key: ', key])
|
|
||||||
mylog('minimal', [' Error - logging the settings into file: ', logPath + '/setting_missing.json'])
|
try:
|
||||||
write_file (logPath + '/setting_missing.json', json.dumps({ 'data' : conf.mySettings}))
|
with open(settingsFile, 'r') as json_file:
|
||||||
|
|
||||||
|
data = json.load(json_file)
|
||||||
|
|
||||||
|
for item in data.get("data",[]):
|
||||||
|
if item.get("Code_Name") == key:
|
||||||
|
return item
|
||||||
|
|
||||||
|
mylog('debug', [f'[Settings] Error - setting_missing - Setting not found for key: {key} in file {settingsFile}'])
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
except (FileNotFoundError, json.JSONDecodeError, ValueError) as e:
|
||||||
|
# Handle the case when the file is not found, JSON decoding fails, or data is not in the expected format
|
||||||
|
mylog('none', [f'[Settings] Error - JSONDecodeError or FileNotFoundError for file {settingsFile}'])
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# Return setting value
|
# Return setting value
|
||||||
def get_setting_value(key):
|
def get_setting_value(key):
|
||||||
|
|
||||||
set = get_setting(key)
|
setting = get_setting(key)
|
||||||
|
|
||||||
if get_setting(key) is not None:
|
if setting is not None:
|
||||||
|
|
||||||
setVal = set[6] # setting value
|
set_value = setting["Value"] # Setting value
|
||||||
setTyp = set[3] # setting type
|
set_type = setting["Type"] # Setting type
|
||||||
|
|
||||||
return setVal
|
# Handle different types of settings
|
||||||
|
if set_type in ['text', 'string', 'password', 'readonly', 'text.select']:
|
||||||
|
return str(set_value)
|
||||||
|
elif set_type in ['boolean', 'integer.checkbox']:
|
||||||
|
return bool(set_value)
|
||||||
|
elif set_type in ['integer.select', 'integer']:
|
||||||
|
return int(set_value)
|
||||||
|
elif set_type in ['text.multiselect', 'list', 'subnets']:
|
||||||
|
# Assuming set_value is a list in this case
|
||||||
|
return set_value
|
||||||
|
elif set_type == '.template':
|
||||||
|
# Assuming set_value is a JSON object in this case
|
||||||
|
return json.loads(set_value)
|
||||||
|
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# IP validation methods
|
# IP validation methods
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
@@ -613,24 +637,15 @@ def initOrSetParam(db, parID, parValue):
|
|||||||
db.commitDB()
|
db.commitDB()
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
class json_struc:
|
class json_obj:
|
||||||
def __init__(self, jsn, columnNames):
|
def __init__(self, jsn, columnNames):
|
||||||
self.json = jsn
|
self.json = jsn
|
||||||
self.columnNames = columnNames
|
self.columnNames = columnNames
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
class noti_struc:
|
class noti_obj:
|
||||||
def __init__(self, json, text, html, notificationType):
|
def __init__(self, json, text, html):
|
||||||
self.json = json
|
self.json = json
|
||||||
self.text = text
|
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)
|
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ from cron_converter import Cron
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
|
import shutil
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
import conf
|
import conf
|
||||||
from const import fullConfPath
|
from const import fullConfPath
|
||||||
@@ -62,6 +65,9 @@ def importConfigs (db):
|
|||||||
# Only import file if the file was modifed since last import.
|
# Only import file if the file was modifed since last import.
|
||||||
# this avoids time zone issues as we just compare the previous timestamp to the current time stamp
|
# this avoids time zone issues as we just compare the previous timestamp to the current time stamp
|
||||||
|
|
||||||
|
# rename settings that have changed names due to code cleanup and migration to plugins
|
||||||
|
renameSettings(config_file)
|
||||||
|
|
||||||
fileModifiedTime = os.path.getmtime(config_file)
|
fileModifiedTime = os.path.getmtime(config_file)
|
||||||
|
|
||||||
mylog('debug', ['[Import Config] checking config file '])
|
mylog('debug', ['[Import Config] checking config file '])
|
||||||
@@ -114,18 +120,6 @@ def importConfigs (db):
|
|||||||
# Notification gateways
|
# Notification gateways
|
||||||
# ----------------------------------------
|
# ----------------------------------------
|
||||||
|
|
||||||
# Email
|
|
||||||
conf.REPORT_MAIL = ccd('REPORT_MAIL', False , c_d, 'Enable email', 'boolean', '', 'Email', ['test'])
|
|
||||||
conf.SMTP_SERVER = ccd('SMTP_SERVER', '' , c_d,'SMTP server URL', 'text', '', 'Email')
|
|
||||||
conf.SMTP_PORT = ccd('SMTP_PORT', 587 , c_d, 'SMTP port', 'integer', '', 'Email')
|
|
||||||
conf.REPORT_TO = ccd('REPORT_TO', 'user@gmail.com' , c_d, 'Email to', 'text', '', 'Email')
|
|
||||||
conf.REPORT_FROM = ccd('REPORT_FROM', 'Pi.Alert <user@gmail.com>' , c_d, 'Email Subject', 'text', '', 'Email')
|
|
||||||
conf.SMTP_SKIP_LOGIN = ccd('SMTP_SKIP_LOGIN', False , c_d, 'SMTP skip login', 'boolean', '', 'Email')
|
|
||||||
conf.SMTP_USER = ccd('SMTP_USER', '' , c_d, 'SMTP user', 'text', '', 'Email')
|
|
||||||
conf.SMTP_PASS = ccd('SMTP_PASS', '' , c_d, 'SMTP password', 'password', '', 'Email')
|
|
||||||
conf.SMTP_SKIP_TLS = ccd('SMTP_SKIP_TLS', False , c_d, 'SMTP skip TLS', 'boolean', '', 'Email')
|
|
||||||
conf.SMTP_FORCE_SSL = ccd('SMTP_FORCE_SSL', False , c_d, 'Force SSL', 'boolean', '', 'Email')
|
|
||||||
|
|
||||||
# Webhooks
|
# Webhooks
|
||||||
conf.REPORT_WEBHOOK = ccd('REPORT_WEBHOOK', False , c_d, 'Enable Webhooks', 'boolean', '', 'Webhooks', ['test'])
|
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_URL = ccd('WEBHOOK_URL', '' , c_d, 'Target URL', 'text', '', 'Webhooks')
|
||||||
@@ -134,13 +128,6 @@ def importConfigs (db):
|
|||||||
conf.WEBHOOK_SIZE = ccd('WEBHOOK_SIZE', 1024 , c_d, 'Payload size', 'integer', '', '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')
|
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
|
# NTFY
|
||||||
conf.REPORT_NTFY = ccd('REPORT_NTFY', False , c_d, 'Enable NTFY', 'boolean', '', 'NTFY', ['test'])
|
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')
|
conf.NTFY_HOST = ccd('NTFY_HOST', 'https://ntfy.sh' , c_d, 'NTFY host URL', 'text', '', 'NTFY')
|
||||||
@@ -262,9 +249,6 @@ def importConfigs (db):
|
|||||||
sql.executemany ("""INSERT INTO Settings ("Code_Name", "Display_Name", "Description", "Type", "Options",
|
sql.executemany ("""INSERT INTO Settings ("Code_Name", "Display_Name", "Description", "Type", "Options",
|
||||||
"RegEx", "Value", "Group", "Events" ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""", conf.mySettingsSQLsafe)
|
"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)
|
#commitDB(sql_connection)
|
||||||
db.commitDB()
|
db.commitDB()
|
||||||
|
|
||||||
@@ -294,3 +278,55 @@ def read_config_file(filename):
|
|||||||
confDict = {} # config dictionary
|
confDict = {} # config dictionary
|
||||||
exec(code, {"__builtins__": {}}, confDict)
|
exec(code, {"__builtins__": {}}, confDict)
|
||||||
return confDict
|
return confDict
|
||||||
|
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
replacements = {
|
||||||
|
r'\bREPORT_TO\b': 'SMTP_REPORT_TO',
|
||||||
|
r'\bREPORT_FROM\b': 'SMTP_REPORT_FROM'
|
||||||
|
}
|
||||||
|
|
||||||
|
def renameSettings(config_file):
|
||||||
|
# Check if the file contains any of the old setting code names
|
||||||
|
contains_old_settings = False
|
||||||
|
|
||||||
|
# Open the original config_file for reading
|
||||||
|
with open(str(config_file), 'r') as original_file: # Convert config_file to a string
|
||||||
|
for line in original_file:
|
||||||
|
# Use regular expressions with word boundaries to check for the old setting code names
|
||||||
|
if any(re.search(key, line) for key in replacements.keys()):
|
||||||
|
contains_old_settings = True
|
||||||
|
break # Exit the loop if any old setting is found
|
||||||
|
|
||||||
|
# If the file contains old settings, proceed with renaming and backup
|
||||||
|
if contains_old_settings:
|
||||||
|
# Create a backup file with the suffix "_old_setting_names" and timestamp
|
||||||
|
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
||||||
|
backup_file = f"{config_file}_old_setting_names_{timestamp}.bak"
|
||||||
|
|
||||||
|
mylog('debug', f'[Config] Old setting names will be replaced and a backup ({backup_file}) of the config created.')
|
||||||
|
|
||||||
|
shutil.copy(str(config_file), backup_file) # Convert config_file to a string
|
||||||
|
|
||||||
|
# Open the original config_file for reading and create a temporary file for writing
|
||||||
|
with open(str(config_file), 'r') as original_file, open(str(config_file) + "_temp", 'w') as temp_file: # Convert config_file to a string
|
||||||
|
for line in original_file:
|
||||||
|
# Use regular expressions with word boundaries for replacements
|
||||||
|
for key, value in replacements.items():
|
||||||
|
line = re.sub(key, value, line)
|
||||||
|
|
||||||
|
# Write the modified line to the temporary file
|
||||||
|
temp_file.write(line)
|
||||||
|
|
||||||
|
# Close both files
|
||||||
|
original_file.close()
|
||||||
|
temp_file.close()
|
||||||
|
|
||||||
|
# Replace the original config_file with the temporary file
|
||||||
|
shutil.move(str(config_file) + "_temp", str(config_file)) # Convert config_file to a string
|
||||||
|
else:
|
||||||
|
mylog('debug', '[Config] No old setting names found in the file. No changes made.')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
114
pialert/notification.py
Normal file
114
pialert/notification.py
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
# Notification object handling
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
class Notification_obj:
|
||||||
|
def __init__(self, db):
|
||||||
|
self.db = db
|
||||||
|
|
||||||
|
# Create Notifications table if missing
|
||||||
|
self.db.sql.execute("""CREATE TABLE IF NOT EXISTS "Notifications" (
|
||||||
|
"Index" INTEGER,
|
||||||
|
"GUID" TEXT UNIQUE,
|
||||||
|
"DateTimeCreated" TEXT,
|
||||||
|
"DateTimePushed" TEXT,
|
||||||
|
"Status" TEXT,
|
||||||
|
"JSON" TEXT,
|
||||||
|
"Text" TEXT,
|
||||||
|
"HTML" TEXT,
|
||||||
|
"PublishedVia" TEXT,
|
||||||
|
"Extra" TEXT,
|
||||||
|
PRIMARY KEY("Index" AUTOINCREMENT)
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
# Create a new DB entry if new notiifcations available, otherwise skip
|
||||||
|
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
|
||||||
|
else:
|
||||||
|
self.HasNotifications = True
|
||||||
|
|
||||||
|
self.GUID = str(uuid.uuid4())
|
||||||
|
self.DateTimeCreated = timeNowTZ()
|
||||||
|
self.DateTimePushed = ""
|
||||||
|
self.Status = "new"
|
||||||
|
self.JSON = JSON
|
||||||
|
self.Text = Text
|
||||||
|
self.HTML = HTML
|
||||||
|
self.PublishedVia = ""
|
||||||
|
self.Extra = Extra
|
||||||
|
|
||||||
|
if self.HasNotifications:
|
||||||
|
self.upsert()
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
# Only updates the status
|
||||||
|
def updateStatus(self, newStatus):
|
||||||
|
self.Status = newStatus
|
||||||
|
self.upsert()
|
||||||
|
|
||||||
|
# Updates the Published properties
|
||||||
|
def updatePublishedVia(self, newPublishedVia):
|
||||||
|
self.PublishedVia = newPublishedVia
|
||||||
|
self.DateTimePushed = timeNowTZ()
|
||||||
|
self.upsert()
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
""", (self.GUID, self.DateTimeCreated, self.DateTimePushed, self.Status, json.dumps(self.JSON), self.Text, self.HTML, self.PublishedVia, self.Extra))
|
||||||
|
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
# Remove notification object by GUID
|
||||||
|
def remove(self, GUID):
|
||||||
|
# Execute an SQL query to delete the notification with the specified GUID
|
||||||
|
self.db.sql.execute("""
|
||||||
|
DELETE FROM Notifications
|
||||||
|
WHERE GUID = ?
|
||||||
|
""", (GUID,))
|
||||||
|
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()
|
||||||
@@ -9,11 +9,12 @@ from collections import namedtuple
|
|||||||
|
|
||||||
# pialert modules
|
# pialert modules
|
||||||
import conf
|
import conf
|
||||||
from const import pluginsPath, logPath
|
from const import pluginsPath, logPath, pialertPath
|
||||||
from logger import mylog
|
from logger import mylog
|
||||||
from helper import timeNowTZ, updateState, get_file_content, write_file, get_setting, get_setting_value
|
from helper import timeNowTZ, updateState, get_file_content, write_file, get_setting, get_setting_value
|
||||||
from api import update_api
|
from api import update_api
|
||||||
from plugin_utils import logEventStatusCounts, get_plugin_string, get_plugin_setting, print_plugin_info, flatten_array, combine_plugin_objects, resolve_wildcards_arr, get_plugin_setting_value, handle_empty, custom_plugin_decoder
|
from plugin_utils import logEventStatusCounts, get_plugin_string, get_plugin_setting, print_plugin_info, list_to_csv, combine_plugin_objects, resolve_wildcards_arr, handle_empty, custom_plugin_decoder
|
||||||
|
from notification import Notification_obj
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
@@ -27,8 +28,8 @@ class plugin_param:
|
|||||||
inputValue = get_setting(param["value"])
|
inputValue = get_setting(param["value"])
|
||||||
|
|
||||||
if inputValue != None:
|
if inputValue != None:
|
||||||
setVal = inputValue[6] # setting value
|
setVal = inputValue["Value"] # setting value
|
||||||
setTyp = inputValue[3] # setting type
|
setTyp = inputValue["Type"] # setting type
|
||||||
|
|
||||||
noConversion = ['text', 'string', 'integer', 'boolean', 'password', 'readonly', 'integer.select', 'text.select', 'integer.checkbox' ]
|
noConversion = ['text', 'string', 'integer', 'boolean', 'password', 'readonly', 'integer.select', 'text.select', 'integer.checkbox' ]
|
||||||
arrayConversion = ['text.multiselect', 'list', 'subnets']
|
arrayConversion = ['text.multiselect', 'list', 'subnets']
|
||||||
@@ -45,11 +46,8 @@ class plugin_param:
|
|||||||
|
|
||||||
elif setTyp in arrayConversion:
|
elif setTyp in arrayConversion:
|
||||||
# make them safely passable to a python or linux script
|
# make them safely passable to a python or linux script
|
||||||
resolved = flatten_array(setVal)
|
resolved = list_to_csv(setVal)
|
||||||
|
|
||||||
elif setTyp in arrayConversionBase64:
|
|
||||||
# make them safely passable to a python or linux script by converting them to a base64 string if necessary (if the arg contains spaces)
|
|
||||||
resolved = flatten_array(setVal)
|
|
||||||
else:
|
else:
|
||||||
for item in jsonConversion:
|
for item in jsonConversion:
|
||||||
if setTyp.endswith(item):
|
if setTyp.endswith(item):
|
||||||
@@ -66,7 +64,7 @@ class plugin_param:
|
|||||||
paramValuesCount = len(inputValue)
|
paramValuesCount = len(inputValue)
|
||||||
|
|
||||||
# make them safely passable to a python or linux script
|
# make them safely passable to a python or linux script
|
||||||
resolved = flatten_array(inputValue)
|
resolved = list_to_csv(inputValue)
|
||||||
|
|
||||||
|
|
||||||
mylog('debug', f'[Plugins] Resolved value: {resolved}')
|
mylog('debug', f'[Plugins] Resolved value: {resolved}')
|
||||||
@@ -487,9 +485,6 @@ def process_plugin_events(db, plugin, pluginsState, plugEventsArr):
|
|||||||
history_to_insert = []
|
history_to_insert = []
|
||||||
objects_to_update = []
|
objects_to_update = []
|
||||||
|
|
||||||
statuses_to_report_on = get_plugin_setting_value(plugin, "REPORT_ON")
|
|
||||||
|
|
||||||
mylog('debug', ['[Plugins] statuses_to_report_on: ', statuses_to_report_on])
|
|
||||||
|
|
||||||
for plugObj in pluginObjects:
|
for plugObj in pluginObjects:
|
||||||
# keep old createdTime time if the plugObj already was created before
|
# keep old createdTime time if the plugObj already was created before
|
||||||
@@ -508,6 +503,8 @@ def process_plugin_events(db, plugin, pluginsState, plugEventsArr):
|
|||||||
objects_to_update.append(values + (plugObj.index,)) # Include index for UPDATE
|
objects_to_update.append(values + (plugObj.index,)) # Include index for UPDATE
|
||||||
|
|
||||||
# only generate events that we want to be notified on
|
# only generate events that we want to be notified on
|
||||||
|
statuses_to_report_on = get_setting_value(plugObj.pluginPref + "_REPORT_ON")
|
||||||
|
|
||||||
if plugObj.status in statuses_to_report_on:
|
if plugObj.status in statuses_to_report_on:
|
||||||
events_to_insert.append(values)
|
events_to_insert.append(values)
|
||||||
|
|
||||||
@@ -750,7 +747,7 @@ def check_and_run_user_event(db, pluginsState):
|
|||||||
return pluginsState
|
return pluginsState
|
||||||
|
|
||||||
if event == 'test':
|
if event == 'test':
|
||||||
handle_test(param)
|
pluginsState = handle_test(param, db, pluginsState)
|
||||||
if event == 'run':
|
if event == 'run':
|
||||||
pluginsState = handle_run(param, db, pluginsState)
|
pluginsState = handle_run(param, db, pluginsState)
|
||||||
|
|
||||||
@@ -778,34 +775,26 @@ def handle_run(runType, db, pluginsState):
|
|||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
def handle_test(testType):
|
def handle_test(runType, db, pluginsState):
|
||||||
|
|
||||||
mylog('minimal', ['[', timeNowTZ(), '] START Test: ', testType])
|
mylog('minimal', ['[', timeNowTZ(), '] [Test] START Test: ', runType])
|
||||||
|
|
||||||
# Open text sample
|
# Prepare test samples
|
||||||
sample_txt = get_file_content(pialertPath + '/back/report_sample.txt')
|
sample_txt = get_file_content(pialertPath + '/back/report_sample.txt')
|
||||||
|
|
||||||
# Open html sample
|
|
||||||
sample_html = get_file_content(pialertPath + '/back/report_sample.html')
|
sample_html = get_file_content(pialertPath + '/back/report_sample.html')
|
||||||
|
sample_json = json.loads(get_file_content(pialertPath + '/back/webhook_json_sample.json'))[0]["body"]["attachments"][0]["text"]
|
||||||
|
|
||||||
# Open json sample and get only the payload part
|
# Create fake notification
|
||||||
sample_json_payload = json.loads(get_file_content(pialertPath + '/back/webhook_json_sample.json'))[0]["body"]["attachments"][0]["text"]
|
notification = Notification_obj(db)
|
||||||
|
notificationObj = notification.create(sample_json, sample_txt, sample_html, "")
|
||||||
|
|
||||||
sample_msg = noti_struc(sample_json_payload, sample_txt, sample_html, "test_sample")
|
# Run test
|
||||||
|
pluginsState = handle_run(runType, db, pluginsState)
|
||||||
|
|
||||||
|
# Remove sample notification
|
||||||
|
notificationObj.remove(notificationObj.GUID)
|
||||||
|
|
||||||
if testType == 'Email':
|
mylog('minimal', ['[Test] END Test: ', runType])
|
||||||
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])
|
return pluginsState
|
||||||
|
|
||||||
|
|||||||
@@ -71,30 +71,39 @@ def get_plugin_string(props, el):
|
|||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
def flatten_array(arr):
|
# generates a comma separated list of values from a list (or a string representing a list)
|
||||||
|
def list_to_csv(arr):
|
||||||
tmp = ''
|
tmp = ''
|
||||||
arrayItemStr = ''
|
arrayItemStr = ''
|
||||||
|
|
||||||
mylog('debug', '[Plugins] Flattening the below array')
|
mylog('debug', '[Plugins] Flattening the below array')
|
||||||
|
|
||||||
mylog('debug', arr)
|
mylog('debug', arr)
|
||||||
|
mylog('debug', f'[Plugins] isinstance(arr, list) : {isinstance(arr, list)} | isinstance(arr, str) : {isinstance(arr, str)}')
|
||||||
|
|
||||||
for arrayItem in arr:
|
if isinstance(arr, str):
|
||||||
# only one column flattening is supported
|
return arr.replace('[','').replace(']','').replace("'", '') # removing brackets and single quotes (not allowed)
|
||||||
if isinstance(arrayItem, list):
|
|
||||||
arrayItemStr = str(arrayItem[0]).replace("'", '') # removing single quotes - not allowed
|
elif isinstance(arr, list):
|
||||||
else:
|
for arrayItem in arr:
|
||||||
# is string already
|
# only one column flattening is supported
|
||||||
arrayItemStr = arrayItem
|
if isinstance(arrayItem, list):
|
||||||
|
arrayItemStr = str(arrayItem[0]).replace("'", '') # removing single quotes - not allowed
|
||||||
|
else:
|
||||||
|
# is string already
|
||||||
|
arrayItemStr = arrayItem
|
||||||
|
|
||||||
|
|
||||||
tmp += f'{arrayItemStr},'
|
tmp += f'{arrayItemStr},'
|
||||||
|
|
||||||
tmp = tmp[:-1] # Remove last comma ','
|
tmp = tmp[:-1] # Remove last comma ','
|
||||||
|
|
||||||
mylog('debug', f'[Plugins] Flattened array: {tmp}')
|
mylog('debug', f'[Plugins] Flattened array: {tmp}')
|
||||||
|
|
||||||
|
return tmp
|
||||||
|
|
||||||
|
else:
|
||||||
|
mylog('none', f'[Plugins] ERROR Could not convert array: {arr}')
|
||||||
|
|
||||||
return tmp
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -160,18 +169,6 @@ def get_plugins_configs():
|
|||||||
return pluginsList # Return the list of plugin configurations
|
return pluginsList # Return the list of plugin configurations
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# Gets the setting value
|
|
||||||
def get_plugin_setting_value(plugin, function_key):
|
|
||||||
|
|
||||||
resultObj = get_plugin_setting(plugin, function_key)
|
|
||||||
|
|
||||||
if resultObj != None:
|
|
||||||
return resultObj["value"]
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
def custom_plugin_decoder(pluginDict):
|
def custom_plugin_decoder(pluginDict):
|
||||||
return namedtuple('X', pluginDict.keys())(*pluginDict.values())
|
return namedtuple('X', pluginDict.keys())(*pluginDict.values())
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
|
|
||||||
import json
|
|
||||||
import subprocess
|
|
||||||
import conf
|
|
||||||
from helper import noti_struc
|
|
||||||
from logger import logResult, mylog
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
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.'])
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
def send(msg: noti_struc):
|
|
||||||
html = msg.html
|
|
||||||
text = msg.text
|
|
||||||
|
|
||||||
payloadData = ''
|
|
||||||
|
|
||||||
# limit = 1024 * 1024 # 1MB limit (1024 bytes * 1024 bytes = 1MB)
|
|
||||||
limit = conf.APPRISE_SIZE
|
|
||||||
|
|
||||||
# truncate size
|
|
||||||
if conf.APPRISE_PAYLOAD == 'html':
|
|
||||||
if len(msg.html) > limit:
|
|
||||||
payloadData = msg.html[:limit] + " <h1> (text was truncated)</h1>"
|
|
||||||
else:
|
|
||||||
payloadData = msg.html
|
|
||||||
if conf.APPRISE_PAYLOAD == 'text':
|
|
||||||
if len(msg.text) > limit:
|
|
||||||
payloadData = msg.text[:limit] + " (text was truncated)"
|
|
||||||
else:
|
|
||||||
payloadData = msg.text
|
|
||||||
|
|
||||||
# Define Apprise compatible payload (https://github.com/caronc/apprise-api#stateless-solution)
|
|
||||||
|
|
||||||
_json_payload = {
|
|
||||||
"urls": conf.APPRISE_URL,
|
|
||||||
"title": "Pi.Alert Notifications",
|
|
||||||
"format": conf.APPRISE_PAYLOAD,
|
|
||||||
"body": payloadData
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 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
|
|
||||||
logResult (stdout, stderr) # TO-DO should be changed to mylog
|
|
||||||
|
|
||||||
# Log the stdout and stderr
|
|
||||||
mylog('debug', [stdout, stderr]) # TO-DO should be changed to mylog
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
# An error occurred, handle it
|
|
||||||
mylog('none', [e.output])
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
""" Pi.Alert module to send notification emails """
|
|
||||||
|
|
||||||
from email.mime.multipart import MIMEMultipart
|
|
||||||
from email.mime.text import MIMEText
|
|
||||||
import smtplib
|
|
||||||
|
|
||||||
|
|
||||||
import conf
|
|
||||||
import socket
|
|
||||||
from helper import hide_email, noti_struc
|
|
||||||
from logger import mylog, print_log
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
def check_config ():
|
|
||||||
if conf.SMTP_SERVER == '' or conf.REPORT_FROM == '' or conf.REPORT_TO == '':
|
|
||||||
mylog('none', ['[Email Check Config] Error: Email service not set up correctly. Check your pialert.conf SMTP_*, REPORT_FROM and REPORT_TO variables.'])
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
def send (msg: noti_struc):
|
|
||||||
|
|
||||||
pText = msg.text
|
|
||||||
pHTML = msg.html
|
|
||||||
|
|
||||||
mylog('debug', '[Send Email] REPORT_TO: ' + hide_email(str(conf.REPORT_TO)) + ' SMTP_USER: ' + hide_email(str(conf.SMTP_USER)))
|
|
||||||
|
|
||||||
# Compose email
|
|
||||||
msg = MIMEMultipart('alternative')
|
|
||||||
msg['Subject'] = 'Pi.Alert Report'
|
|
||||||
msg['From'] = conf.REPORT_FROM
|
|
||||||
msg['To'] = conf.REPORT_TO
|
|
||||||
msg.attach (MIMEText (pText, 'plain'))
|
|
||||||
msg.attach (MIMEText (pHTML, 'html'))
|
|
||||||
|
|
||||||
failedAt = ''
|
|
||||||
|
|
||||||
failedAt = print_log ('SMTP try')
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Send mail
|
|
||||||
failedAt = print_log('Trying to open connection to ' + str(conf.SMTP_SERVER) + ':' + str(conf.SMTP_PORT))
|
|
||||||
|
|
||||||
# Set a timeout for the SMTP connection (in seconds)
|
|
||||||
smtp_timeout = 30
|
|
||||||
|
|
||||||
if conf.SMTP_FORCE_SSL:
|
|
||||||
failedAt = print_log('SMTP_FORCE_SSL == True so using .SMTP_SSL()')
|
|
||||||
if conf.SMTP_PORT == 0:
|
|
||||||
failedAt = print_log('SMTP_PORT == 0 so sending .SMTP_SSL(SMTP_SERVER)')
|
|
||||||
smtp_connection = smtplib.SMTP_SSL(conf.SMTP_SERVER)
|
|
||||||
else:
|
|
||||||
failedAt = print_log('SMTP_PORT == 0 so sending .SMTP_SSL(SMTP_SERVER, SMTP_PORT)')
|
|
||||||
smtp_connection = smtplib.SMTP_SSL(conf.SMTP_SERVER, conf.SMTP_PORT, timeout=smtp_timeout)
|
|
||||||
|
|
||||||
else:
|
|
||||||
failedAt = print_log('SMTP_FORCE_SSL == False so using .SMTP()')
|
|
||||||
if conf.SMTP_PORT == 0:
|
|
||||||
failedAt = print_log('SMTP_PORT == 0 so sending .SMTP(SMTP_SERVER)')
|
|
||||||
smtp_connection = smtplib.SMTP (conf.SMTP_SERVER)
|
|
||||||
else:
|
|
||||||
failedAt = print_log('SMTP_PORT == 0 so sending .SMTP(SMTP_SERVER, SMTP_PORT)')
|
|
||||||
smtp_connection = smtplib.SMTP (conf.SMTP_SERVER, conf.SMTP_PORT)
|
|
||||||
|
|
||||||
failedAt = print_log('Setting SMTP debug level')
|
|
||||||
|
|
||||||
# Log level set to debug of the communication between SMTP server and client
|
|
||||||
if conf.LOG_LEVEL == 'debug':
|
|
||||||
smtp_connection.set_debuglevel(1)
|
|
||||||
|
|
||||||
failedAt = print_log( 'Sending .ehlo()')
|
|
||||||
smtp_connection.ehlo()
|
|
||||||
|
|
||||||
if not conf.SMTP_SKIP_TLS:
|
|
||||||
failedAt = print_log('SMTP_SKIP_TLS == False so sending .starttls()')
|
|
||||||
smtp_connection.starttls()
|
|
||||||
failedAt = print_log('SMTP_SKIP_TLS == False so sending .ehlo()')
|
|
||||||
smtp_connection.ehlo()
|
|
||||||
if not conf.SMTP_SKIP_LOGIN:
|
|
||||||
failedAt = print_log('SMTP_SKIP_LOGIN == False so sending .login()')
|
|
||||||
smtp_connection.login (conf.SMTP_USER, conf.SMTP_PASS)
|
|
||||||
|
|
||||||
failedAt = print_log('Sending .sendmail()')
|
|
||||||
smtp_connection.sendmail (conf.REPORT_FROM, conf.REPORT_TO, msg.as_string())
|
|
||||||
smtp_connection.quit()
|
|
||||||
except smtplib.SMTPAuthenticationError as e:
|
|
||||||
mylog('none', [' ERROR: Failed at - ', failedAt])
|
|
||||||
mylog('none', [' ERROR: Couldn\'t connect to the SMTP server (SMTPAuthenticationError), skipping Email (enable LOG_LEVEL=debug for more logging)'])
|
|
||||||
mylog('none', [' ERROR: ', str(e)])
|
|
||||||
except smtplib.SMTPServerDisconnected as e:
|
|
||||||
mylog('none', [' ERROR: Failed at - ', failedAt])
|
|
||||||
mylog('none', [' ERROR: Couldn\'t connect to the SMTP server (SMTPServerDisconnected), skipping Email (enable LOG_LEVEL=debug for more logging)'])
|
|
||||||
mylog('none', [' ERROR: ', str(e)])
|
|
||||||
except socket.gaierror as e:
|
|
||||||
mylog('none', [' ERROR: Failed at - ', failedAt])
|
|
||||||
mylog('none', [' ERROR: Could not resolve hostname (socket.gaierror), skipping Email (enable LOG_LEVEL=debug for more logging)'])
|
|
||||||
mylog('none', [' ERROR: ', str(e)])
|
|
||||||
|
|
||||||
mylog('debug', '[Send Email] Last executed - ' + str(failedAt))
|
|
||||||
@@ -4,7 +4,7 @@ import requests
|
|||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
|
|
||||||
from logger import mylog
|
from logger import mylog
|
||||||
from helper import noti_struc
|
from helper import noti_obj
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
def check_config():
|
def check_config():
|
||||||
@@ -15,7 +15,7 @@ def check_config():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
def send (msg: noti_struc):
|
def send (msg: noti_obj):
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
"Title": "Pi.Alert Notification",
|
"Title": "Pi.Alert Notification",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import requests
|
|||||||
|
|
||||||
|
|
||||||
import conf
|
import conf
|
||||||
from helper import noti_struc
|
from helper import noti_obj
|
||||||
from logger import mylog
|
from logger import mylog
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
@@ -15,7 +15,7 @@ def check_config():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
def send ( msg:noti_struc ):
|
def send ( msg:noti_obj ):
|
||||||
_Text = msg.text
|
_Text = msg.text
|
||||||
url = 'https://www.pushsafer.com/api'
|
url = 'https://www.pushsafer.com/api'
|
||||||
post_fields = {
|
post_fields = {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import hmac
|
|||||||
|
|
||||||
import conf
|
import conf
|
||||||
from const import logPath
|
from const import logPath
|
||||||
from helper import noti_struc, write_file
|
from helper import noti_obj, write_file
|
||||||
from logger import logResult, mylog
|
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 = 1024 * 1024 # 1MB limit (1024 bytes * 1024 bytes = 1MB)
|
||||||
limit = conf.WEBHOOK_SIZE
|
limit = conf.WEBHOOK_SIZE
|
||||||
|
|||||||
@@ -12,9 +12,7 @@
|
|||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
|
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import requests
|
import requests
|
||||||
from json2table import convert
|
from json2table import convert
|
||||||
@@ -23,15 +21,11 @@ from json2table import convert
|
|||||||
import conf
|
import conf
|
||||||
import const
|
import const
|
||||||
from const import pialertPath, logPath, apiPath
|
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 logger import logResult, mylog, print_log
|
||||||
|
|
||||||
from publishers.email import (check_config as email_check_config,
|
|
||||||
send as send_email )
|
|
||||||
from publishers.ntfy import (check_config as ntfy_check_config,
|
from publishers.ntfy import (check_config as ntfy_check_config,
|
||||||
send as send_ntfy )
|
send as send_ntfy )
|
||||||
from publishers.apprise import (check_config as apprise_check_config,
|
|
||||||
send as send_apprise)
|
|
||||||
from publishers.webhook import (check_config as webhook_check_config,
|
from publishers.webhook import (check_config as webhook_check_config,
|
||||||
send as send_webhook)
|
send as send_webhook)
|
||||||
from publishers.pushsafer import (check_config as pushsafer_check_config,
|
from publishers.pushsafer import (check_config as pushsafer_check_config,
|
||||||
@@ -50,10 +44,10 @@ json_final = []
|
|||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
def construct_notifications(db, sqlQuery, tableTitle, skipText = False, suppliedJsonStruct = None, notificationType=''):
|
def construct_notifications(db, sqlQuery, tableTitle, skipText = False, suppliedJsonStruct = None):
|
||||||
|
|
||||||
if suppliedJsonStruct is None and sqlQuery == "":
|
if suppliedJsonStruct is None and sqlQuery == "":
|
||||||
return noti_struc("", "", "", notificationType)
|
return noti_obj("", "", "")
|
||||||
|
|
||||||
table_attributes = {"style" : "border-collapse: collapse; font-size: 12px; color:#70707", "width" : "100%", "cellspacing" : 0, "cellpadding" : "3px", "bordercolor" : "#C0C0C0", "border":"1"}
|
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' "
|
headerProps = "width='120px' style='color:white; font-size: 16px;' bgcolor='#64a0d6' "
|
||||||
@@ -63,11 +57,11 @@ def construct_notifications(db, sqlQuery, tableTitle, skipText = False, supplied
|
|||||||
text_line = '{}\t{}\n'
|
text_line = '{}\t{}\n'
|
||||||
|
|
||||||
if suppliedJsonStruct is None:
|
if suppliedJsonStruct is None:
|
||||||
json_struc = db.get_table_as_json(sqlQuery)
|
json_obj = db.get_table_as_json(sqlQuery)
|
||||||
else:
|
else:
|
||||||
json_struc = suppliedJsonStruct
|
json_obj = suppliedJsonStruct
|
||||||
|
|
||||||
jsn = json_struc.json
|
jsn = json_obj.json
|
||||||
html = ""
|
html = ""
|
||||||
text = ""
|
text = ""
|
||||||
|
|
||||||
@@ -80,7 +74,7 @@ def construct_notifications(db, sqlQuery, tableTitle, skipText = False, supplied
|
|||||||
# Cleanup the generated HTML table notification
|
# Cleanup the generated HTML table notification
|
||||||
html = format_table(html, "data", headerProps, tableTitle).replace('<ul>','<ul style="list-style:none;padding-left:0">').replace("<td>null</td>", "<td></td>")
|
html = format_table(html, "data", headerProps, tableTitle).replace('<ul>','<ul style="list-style:none;padding-left:0">').replace("<td>null</td>", "<td></td>")
|
||||||
|
|
||||||
headers = json_struc.columnNames
|
headers = json_obj.columnNames
|
||||||
|
|
||||||
# prepare text-only message
|
# prepare text-only message
|
||||||
if skipText == False:
|
if skipText == False:
|
||||||
@@ -97,7 +91,7 @@ def construct_notifications(db, sqlQuery, tableTitle, skipText = False, supplied
|
|||||||
for header in headers:
|
for header in headers:
|
||||||
html = format_table(html, header, thProps)
|
html = format_table(html, header, thProps)
|
||||||
|
|
||||||
notiStruc = noti_struc(jsn, text, html, notificationType)
|
notiStruc = noti_obj(jsn, text, html)
|
||||||
|
|
||||||
|
|
||||||
if not notiStruc.json['data'] and not notiStruc.text and not notiStruc.html:
|
if not notiStruc.json['data'] and not notiStruc.text and not notiStruc.html:
|
||||||
@@ -108,7 +102,7 @@ def construct_notifications(db, sqlQuery, tableTitle, skipText = False, supplied
|
|||||||
return notiStruc
|
return notiStruc
|
||||||
|
|
||||||
|
|
||||||
def send_notifications (db):
|
def get_notifications (db):
|
||||||
|
|
||||||
sql = db.sql #TO-DO
|
sql = db.sql #TO-DO
|
||||||
global mail_text, mail_html, json_final, partial_html, partial_txt, partial_json
|
global mail_text, mail_html, json_final, partial_html, partial_txt, partial_json
|
||||||
@@ -184,53 +178,50 @@ def send_notifications (db):
|
|||||||
|
|
||||||
if 'new_devices' in conf.INCLUDED_SECTIONS :
|
if 'new_devices' in conf.INCLUDED_SECTIONS :
|
||||||
# Compose New Devices Section
|
# Compose New Devices Section
|
||||||
sqlQuery = """SELECT eve_MAC as MAC, eve_DateTime as Datetime, dev_LastIP as IP, eve_EventType as "Event Type", dev_Name as "Device name", dev_Comments as Comments, dev_Vendor as "Device Vendor"
|
sqlQuery = """SELECT eve_MAC as MAC, eve_DateTime as Datetime, dev_LastIP as IP, eve_EventType as "Event Type", dev_Name as "Device name", dev_Comments as Comments FROM Events_Devices
|
||||||
FROM Events_Devices
|
|
||||||
WHERE eve_PendingAlertEmail = 1
|
WHERE eve_PendingAlertEmail = 1
|
||||||
AND eve_EventType = 'New Device'
|
AND eve_EventType = 'New Device'
|
||||||
ORDER BY eve_DateTime"""
|
ORDER BY eve_DateTime"""
|
||||||
|
|
||||||
notiStruc = construct_notifications(db, sqlQuery, "New devices", "new_devices")
|
notiStruc = construct_notifications(db, sqlQuery, "New devices")
|
||||||
|
|
||||||
# collect "new_devices" for the webhook json
|
# collect "new_devices" for the webhook json
|
||||||
json_new_devices = notiStruc.json["data"]
|
json_new_devices = notiStruc.json["data"]
|
||||||
|
|
||||||
mail_text = mail_text.replace ('<SECTION_NEW_DEVICES>', notiStruc.text + '\n')
|
mail_text = mail_text.replace ('<NEW_DEVICES_TABLE>', notiStruc.text + '\n')
|
||||||
mail_html = mail_html.replace ('<NEW_DEVICES_TABLE>', notiStruc.html)
|
mail_html = mail_html.replace ('<NEW_DEVICES_TABLE>', notiStruc.html)
|
||||||
mylog('verbose', ['[Notification] New Devices sections done.'])
|
mylog('verbose', ['[Notification] New Devices sections done.'])
|
||||||
|
|
||||||
if 'down_devices' in conf.INCLUDED_SECTIONS :
|
if 'down_devices' in conf.INCLUDED_SECTIONS :
|
||||||
# Compose Devices Down Section
|
# Compose Devices Down Section
|
||||||
sqlQuery = """SELECT eve_MAC as MAC, eve_DateTime as Datetime, dev_LastIP as IP, eve_EventType as "Event Type", dev_Name as "Device name", dev_Comments as Comments, dev_Vendor as "Device Vendor"
|
sqlQuery = """SELECT eve_MAC as MAC, eve_DateTime as Datetime, dev_LastIP as IP, eve_EventType as "Event Type", dev_Name as "Device name", dev_Comments as Comments FROM Events_Devices
|
||||||
FROM Events_Devices
|
|
||||||
WHERE eve_PendingAlertEmail = 1
|
WHERE eve_PendingAlertEmail = 1
|
||||||
AND eve_EventType = 'Device Down'
|
AND eve_EventType = 'Device Down'
|
||||||
ORDER BY eve_DateTime"""
|
ORDER BY eve_DateTime"""
|
||||||
|
|
||||||
notiStruc = construct_notifications(db, sqlQuery, "Down devices", "down_Devices")
|
notiStruc = construct_notifications(db, sqlQuery, "Down devices")
|
||||||
|
|
||||||
# collect "down_devices" for the webhook json
|
# collect "down_devices" for the webhook json
|
||||||
json_down_devices = notiStruc.json["data"]
|
json_down_devices = notiStruc.json["data"]
|
||||||
|
|
||||||
mail_text = mail_text.replace ('<SECTION_DEVICES_DOWN>', notiStruc.text + '\n')
|
mail_text = mail_text.replace ('<DOWN_DEVICES_TABLE>', notiStruc.text + '\n')
|
||||||
mail_html = mail_html.replace ('<DOWN_DEVICES_TABLE>', notiStruc.html)
|
mail_html = mail_html.replace ('<DOWN_DEVICES_TABLE>', notiStruc.html)
|
||||||
mylog('verbose', ['[Notification] Down Devices sections done.'])
|
mylog('verbose', ['[Notification] Down Devices sections done.'])
|
||||||
|
|
||||||
if 'events' in conf.INCLUDED_SECTIONS :
|
if 'events' in conf.INCLUDED_SECTIONS :
|
||||||
# Compose Events Section
|
# Compose Events Section
|
||||||
sqlQuery = """SELECT eve_MAC as MAC, eve_DateTime as Datetime, dev_LastIP as IP, eve_EventType as "Event Type", dev_Name as "Device name", dev_Comments as Comments, dev_Vendor as "Device Vendor"
|
sqlQuery = """SELECT eve_MAC as MAC, eve_DateTime as Datetime, dev_LastIP as IP, eve_EventType as "Event Type", dev_Name as "Device name", dev_Comments as Comments FROM Events_Devices
|
||||||
FROM Events_Devices
|
|
||||||
WHERE eve_PendingAlertEmail = 1
|
WHERE eve_PendingAlertEmail = 1
|
||||||
AND eve_EventType IN ('Connected','Disconnected',
|
AND eve_EventType IN ('Connected','Disconnected',
|
||||||
'IP Changed')
|
'IP Changed')
|
||||||
ORDER BY eve_DateTime"""
|
ORDER BY eve_DateTime"""
|
||||||
|
|
||||||
notiStruc = construct_notifications(db, sqlQuery, "Events", "events")
|
notiStruc = construct_notifications(db, sqlQuery, "Events")
|
||||||
|
|
||||||
# collect "events" for the webhook json
|
# collect "events" for the webhook json
|
||||||
json_events = notiStruc.json["data"]
|
json_events = notiStruc.json["data"]
|
||||||
|
|
||||||
mail_text = mail_text.replace ('<SECTION_EVENTS>', notiStruc.text + '\n')
|
mail_text = mail_text.replace ('<EVENTS_TABLE>', notiStruc.text + '\n')
|
||||||
mail_html = mail_html.replace ('<EVENTS_TABLE>', notiStruc.html)
|
mail_html = mail_html.replace ('<EVENTS_TABLE>', notiStruc.html)
|
||||||
mylog('verbose', ['[Notification] Events sections done.'])
|
mylog('verbose', ['[Notification] Events sections done.'])
|
||||||
|
|
||||||
@@ -250,116 +241,67 @@ def send_notifications (db):
|
|||||||
plugins_report = len(json_plugins) > 0
|
plugins_report = len(json_plugins) > 0
|
||||||
mylog('verbose', ['[Notification] Plugins sections done.'])
|
mylog('verbose', ['[Notification] Plugins sections done.'])
|
||||||
|
|
||||||
json_final = {
|
final_json = {
|
||||||
"internet": json_internet,
|
"internet": json_internet,
|
||||||
"new_devices": json_new_devices,
|
"new_devices": json_new_devices,
|
||||||
"down_devices": json_down_devices,
|
"down_devices": json_down_devices,
|
||||||
"events": json_events,
|
"events": json_events,
|
||||||
"ports": json_ports,
|
|
||||||
"plugins": json_plugins,
|
"plugins": json_plugins,
|
||||||
}
|
}
|
||||||
|
|
||||||
mail_text = removeDuplicateNewLines(mail_text)
|
final_text = removeDuplicateNewLines(mail_text)
|
||||||
|
|
||||||
# Create clickable MAC links
|
# Create clickable MAC links
|
||||||
mail_html = generate_mac_links (mail_html, deviceUrl)
|
final_html = generate_mac_links (mail_html, deviceUrl)
|
||||||
|
|
||||||
# Write output emails for debug
|
# Write output emails for debug
|
||||||
write_file (logPath + '/report_output.json', json.dumps(json_final))
|
write_file (logPath + '/report_output.json', json.dumps(final_json))
|
||||||
write_file (logPath + '/report_output.txt', mail_text)
|
write_file (logPath + '/report_output.txt', final_text)
|
||||||
write_file (logPath + '/report_output.html', mail_html)
|
write_file (logPath + '/report_output.html', final_html)
|
||||||
|
|
||||||
# Write the notifications into the DB
|
mylog('minimal', ['[Notification] Udating API files'])
|
||||||
# TODO
|
send_api()
|
||||||
|
|
||||||
# Notify is something to report
|
return noti_obj(final_json, final_text, final_html)
|
||||||
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, 'master')
|
|
||||||
|
|
||||||
mylog('minimal', ['[Notification] Udating API files'])
|
|
||||||
send_api()
|
|
||||||
|
|
||||||
if conf.REPORT_MAIL and check_config('email'):
|
|
||||||
updateState("Send: Email")
|
|
||||||
mylog('minimal', ['[Notification] Sending report by Email'])
|
|
||||||
send_email (msg )
|
|
||||||
else :
|
|
||||||
mylog('verbose', ['[Notification] Skip email'])
|
|
||||||
if conf.REPORT_APPRISE and check_config('apprise'):
|
|
||||||
updateState("Send: Apprise")
|
|
||||||
mylog('minimal', ['[Notification] Sending report by Apprise'])
|
|
||||||
send_apprise (msg)
|
|
||||||
else :
|
|
||||||
mylog('verbose', ['[Notification] Skip Apprise'])
|
|
||||||
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_NTFY and check_config('ntfy'):
|
|
||||||
updateState("Send: NTFY")
|
|
||||||
mylog('minimal', ['[Notification] Sending report by NTFY'])
|
|
||||||
send_ntfy (msg)
|
|
||||||
else :
|
|
||||||
mylog('verbose', ['[Notification] Skip NTFY'])
|
|
||||||
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'])
|
|
||||||
# Update MQTT entities
|
|
||||||
if conf.REPORT_MQTT and check_config('mqtt'):
|
|
||||||
updateState("Send: MQTT")
|
|
||||||
mylog('minimal', ['[Notification] Establishing MQTT thread'])
|
|
||||||
mqtt_start(db)
|
|
||||||
else :
|
|
||||||
mylog('verbose', ['[Notification] Skip MQTT'])
|
|
||||||
else :
|
|
||||||
mylog('verbose', ['[Notification] No changes to report'])
|
|
||||||
|
|
||||||
# Clean Pending Alert Events
|
|
||||||
sql.execute ("""UPDATE Devices SET dev_LastNotification = ?
|
|
||||||
WHERE dev_MAC IN (SELECT eve_MAC FROM Events
|
|
||||||
WHERE eve_PendingAlertEmail = 1)
|
|
||||||
""", (datetime.datetime.now(conf.tz),) )
|
|
||||||
sql.execute ("""UPDATE Events SET eve_PendingAlertEmail = 0
|
|
||||||
WHERE eve_PendingAlertEmail = 1""")
|
|
||||||
|
|
||||||
# clear plugin events
|
|
||||||
sql.execute ("DELETE FROM Plugins_Events")
|
|
||||||
|
|
||||||
# DEBUG - print number of rows updated
|
|
||||||
mylog('minimal', ['[Notification] Notifications changes: ', sql.rowcount])
|
|
||||||
|
|
||||||
# Commit changes
|
|
||||||
db.commitDB()
|
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
def check_config(service):
|
|
||||||
|
|
||||||
if service == 'email':
|
# if conf.REPORT_MAIL and check_config('email'):
|
||||||
return email_check_config()
|
# updateState("Send: Email")
|
||||||
|
# mylog('minimal', ['[Notification] Sending report by Email'])
|
||||||
|
# send_email (msg )
|
||||||
|
# else :
|
||||||
|
# mylog('verbose', ['[Notification] Skip email'])
|
||||||
|
#
|
||||||
|
|
||||||
if service == 'apprise':
|
# if conf.REPORT_WEBHOOK and check_config('webhook'):
|
||||||
return apprise_check_config()
|
# updateState("Send: Webhook")
|
||||||
|
# mylog('minimal', ['[Notification] Sending report by Webhook'])
|
||||||
|
# send_webhook (msg)
|
||||||
|
# else :
|
||||||
|
# mylog('verbose', ['[Notification] Skip webhook'])
|
||||||
|
# if conf.REPORT_NTFY and check_config('ntfy'):
|
||||||
|
# updateState("Send: NTFY")
|
||||||
|
# mylog('minimal', ['[Notification] Sending report by NTFY'])
|
||||||
|
# send_ntfy (msg)
|
||||||
|
# else :
|
||||||
|
# mylog('verbose', ['[Notification] Skip NTFY'])
|
||||||
|
# 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'])
|
||||||
|
# # Update MQTT entities
|
||||||
|
# if conf.REPORT_MQTT and check_config('mqtt'):
|
||||||
|
# updateState("Send: MQTT")
|
||||||
|
# mylog('minimal', ['[Notification] Establishing MQTT thread'])
|
||||||
|
# mqtt_start(db)
|
||||||
|
# else :
|
||||||
|
# mylog('verbose', ['[Notification] Skip MQTT'])
|
||||||
|
# else :
|
||||||
|
# mylog('verbose', ['[Notification] No changes to report'])
|
||||||
|
|
||||||
if service == 'webhook':
|
|
||||||
return webhook_check_config()
|
|
||||||
|
|
||||||
if service == 'ntfy':
|
|
||||||
return ntfy_check_config ()
|
|
||||||
|
|
||||||
if service == 'pushsafer':
|
|
||||||
return pushsafer_check_config()
|
|
||||||
|
|
||||||
if service == 'mqtt':
|
|
||||||
return mqtt_check_config()
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# Replacing table headers
|
# Replacing table headers
|
||||||
@@ -438,56 +380,6 @@ def skip_repeated_notifications (db):
|
|||||||
db.commitDB()
|
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()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user