This commit is contained in:
dgtlmoon
2025-08-28 11:33:46 +02:00
parent 9f0bc0688c
commit e7d82bb346
7 changed files with 90 additions and 28 deletions
@@ -5,6 +5,7 @@
{% from '_common_fields.html' import render_common_settings_form %}
<script>
const notification_base_url="{{url_for('ui.ui_notification.ajax_callback_send_notification_test', mode="global-settings")}}";
const notification_test_render_preview_rul="{{url_for('ui.ui_notification.ajax_callback_test_render_preview', mode="global-settings")}}";
{% if emailprefix %}
const email_notification_prefix=JSON.parse('{{emailprefix|tojson}}');
{% endif %}
@@ -4,6 +4,7 @@
{% from '_common_fields.html' import render_common_settings_form %}
<script>
const notification_base_url="{{url_for('ui.ui_notification.ajax_callback_send_notification_test', mode="group-settings")}}";
const notification_test_render_preview_rul="{{url_for('ui.ui_notification.ajax_callback_test_render_preview', mode="global-settings")}}";
</script>
<script src="{{url_for('static_content', group='js', filename='tabs.js')}}" defer></script>
+16 -6
View File
@@ -1,4 +1,4 @@
from flask import Blueprint, request, make_response
from flask import Blueprint, request, make_response, jsonify
import random
from loguru import logger
@@ -8,20 +8,28 @@ from changedetectionio.auth_decorator import login_optionally_required
def construct_blueprint(datastore: ChangeDetectionStore):
notification_blueprint = Blueprint('ui_notification', __name__, template_folder="../ui/templates")
@notification_blueprint.route("/notification/render-preview/<string:watch_uuid>", methods=['POST'])
@notification_blueprint.route("/notification/render-preview", methods=['POST'])
@notification_blueprint.route("/notification/render-preview/", methods=['POST'])
@login_optionally_required
def ajax_callback_test_render_preview(watch_uuid=None):
return ajax_callback_send_notification_test(watch_uuid=watch_uuid, send_as_null_test=True)
# AJAX endpoint for sending a test
@notification_blueprint.route("/notification/send-test/<string:watch_uuid>", methods=['POST'])
@notification_blueprint.route("/notification/send-test", methods=['POST'])
@notification_blueprint.route("/notification/send-test/", methods=['POST'])
@login_optionally_required
def ajax_callback_send_notification_test(watch_uuid=None):
def ajax_callback_send_notification_test(watch_uuid=None, send_as_null_test=False):
# Watch_uuid could be unset in the case it`s used in tag editor, global settings
import apprise
from changedetectionio.notification.apprise_plugin.assets import apprise_asset
# Necessary so that we import our custom handlers
from changedetectionio.notification.apprise_plugin.custom_handlers import apprise_http_custom_handler
from changedetectionio.notification.apprise_plugin.custom_handlers import apprise_http_custom_handler, apprise_null_custom_handler
apobj = apprise.Apprise(asset=apprise_asset)
sent_obj = {}
@@ -40,10 +48,12 @@ def construct_blueprint(datastore: ChangeDetectionStore):
watch = datastore.data['watching'].get(watch_uuid)
notification_urls = None
notification_urls = []
if send_as_null_test:
notification_urls.append('null://null-test-just-to-render-everything-on-the-same-codepath-and-get-preview')
if request.form.get('notification_urls'):
notification_urls = request.form['notification_urls'].strip().splitlines()
notification_urls += request.form['notification_urls'].strip().splitlines()
if not notification_urls:
logger.debug("Test notification - Trying by group/tag in the edit form if available")
@@ -19,6 +19,11 @@ def notify_supported_methods(func):
return func
def notify_null_method(func):
func = notify(on="null")(func)
return func
def _get_auth(parsed_url: dict) -> str | tuple[str, str]:
user: str | None = parsed_url.get("user")
password: str | None = parsed_url.get("password")
@@ -110,3 +115,21 @@ def apprise_http_custom_handler(
except Exception as e:
logger.error(f"Unexpected error occurred while sending custom notification to {url}: {e}")
return False
@notify_null_method
def apprise_null_custom_handler(
body: str,
title: str,
notify_type: str,
meta: dict,
*args,
**kwargs,
) -> bool:
url: str = meta.get("url")
schema: str = meta.get("schema")
method: str = re.sub(r"s$", "", schema).upper()
logger.info(f"Processed 'null' notification")
return True
+13 -9
View File
@@ -8,7 +8,10 @@ def process_notification(n_object, datastore):
from changedetectionio.safe_jinja import render as jinja_render
from . import default_notification_format_for_watch, default_notification_format, valid_notification_formats
# be sure its registered
from .apprise_plugin.custom_handlers import apprise_http_custom_handler
from .apprise_plugin.custom_handlers import apprise_http_custom_handler, apprise_null_custom_handler
n_body = ''
n_title = ''
now = time.time()
if n_object.get('notification_timestamp'):
@@ -118,14 +121,15 @@ def process_notification(n_object, datastore):
'url': url,
'body_format': n_format})
# Blast off the notifications tht are set in .add()
apobj.notify(
title=n_title,
body=n_body,
body_format=n_format,
# False is not an option for AppRise, must be type None
attach=n_object.get('screenshot', None)
)
if n_object.get('notification_urls'):
# Blast off the notifications tht are set in .add()
apobj.notify(
title=n_title,
body=n_body,
body_format=n_format,
# False is not an option for AppRise, must be type None
attach=n_object.get('screenshot', None)
)
# Returns empty string if nothing found, multi-line string otherwise
+35 -13
View File
@@ -1,5 +1,18 @@
$(document).ready(function () {
function getNotificationData() {
data = {
notification_body: $('#notification_body').val(),
notification_format: $('#notification_format').val(),
notification_title: $('#notification_title').val(),
notification_urls: $('.notification-urls').val(),
tags: $('#tags').val(),
window_url: window.location.href,
}
return data
}
$('#add-email-helper').click(function (e) {
e.preventDefault();
email = prompt("Destination email");
@@ -15,7 +28,26 @@ $(document).ready(function () {
"Preview": "#notification-preview"
});
function setPreview(content) {
$(document).on('click', '[data-target="#notification-preview"]', function (e) {
var data = getNotificationData();
$.ajax({
type: "POST",
url: notification_test_render_preview_rul,
data: data,
/*
statusCode: {
400: function (data) {
$("#notification-test-log>span").text(data.responseText);
},
}
*/
}).done(function (data) {
setPreview(data['result']);
})
});
function setPreview(data) {
const iframe = document.getElementById("notification-iframe");
iframe.srcdoc = `
<html>
@@ -30,7 +62,7 @@ $(document).ready(function () {
}
</style>
</head>
<body>${content}</body>
<body>${data['body']}</body>
</html>`;
}
@@ -38,14 +70,7 @@ $(document).ready(function () {
$('#send-test-notification').click(function (e) {
e.preventDefault();
data = {
notification_body: $('#notification_body').val(),
notification_format: $('#notification_format').val(),
notification_title: $('#notification_title').val(),
notification_urls: $('.notification-urls').val(),
tags: $('#tags').val(),
window_url: window.location.href,
}
var data = getNotificationData();
$('.notifications-wrapper .spinner').fadeIn();
$('#notification-test-log').show();
@@ -60,8 +85,6 @@ $(document).ready(function () {
}
}).done(function (data) {
$("#notification-test-log>span").text(data['status']);
setPreview(data['result']['body']);
}).fail(function (jqXHR, textStatus, errorThrown) {
// Handle connection refused or other errors
if (textStatus === "error" && errorThrown === "") {
@@ -77,6 +100,5 @@ $(document).ready(function () {
});
});
+1
View File
@@ -21,6 +21,7 @@
const email_notification_prefix=JSON.parse('{{ emailprefix|tojson }}');
{% endif %}
const notification_base_url="{{url_for('ui.ui_notification.ajax_callback_send_notification_test', watch_uuid=uuid)}}";
const notification_test_render_preview_rul="{{url_for('ui.ui_notification.ajax_callback_test_render_preview', watch_uuid=uuid)}}";
const playwright_enabled={% if playwright_enabled %}true{% else %}false{% endif %};
const recheck_proxy_start_url="{{url_for('check_proxies.start_check', uuid=uuid)}}";
const proxy_recheck_status_url="{{url_for('check_proxies.get_recheck_status', uuid=uuid)}}";