diff --git a/changedetectionio/blueprint/settings/templates/settings.html b/changedetectionio/blueprint/settings/templates/settings.html index 60d5e138f..cb1b9d24d 100644 --- a/changedetectionio/blueprint/settings/templates/settings.html +++ b/changedetectionio/blueprint/settings/templates/settings.html @@ -5,6 +5,7 @@ {% from '_common_fields.html' import render_common_settings_form %} diff --git a/changedetectionio/blueprint/ui/notification.py b/changedetectionio/blueprint/ui/notification.py index a9e523be0..41e0182c2 100644 --- a/changedetectionio/blueprint/ui/notification.py +++ b/changedetectionio/blueprint/ui/notification.py @@ -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/", 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/", 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") diff --git a/changedetectionio/notification/apprise_plugin/custom_handlers.py b/changedetectionio/notification/apprise_plugin/custom_handlers.py index 1fd28b416..a3293407e 100644 --- a/changedetectionio/notification/apprise_plugin/custom_handlers.py +++ b/changedetectionio/notification/apprise_plugin/custom_handlers.py @@ -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 + diff --git a/changedetectionio/notification/handler.py b/changedetectionio/notification/handler.py index fc7628fe3..c955c9f69 100644 --- a/changedetectionio/notification/handler.py +++ b/changedetectionio/notification/handler.py @@ -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 diff --git a/changedetectionio/static/js/notifications.js b/changedetectionio/static/js/notifications.js index dda20c714..61e2f8787 100644 --- a/changedetectionio/static/js/notifications.js +++ b/changedetectionio/static/js/notifications.js @@ -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 = ` @@ -30,7 +62,7 @@ $(document).ready(function () { } - ${content} + ${data['body']} `; } @@ -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 () { }); - }); diff --git a/changedetectionio/templates/edit.html b/changedetectionio/templates/edit.html index 648848bb2..1fafe7932 100644 --- a/changedetectionio/templates/edit.html +++ b/changedetectionio/templates/edit.html @@ -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)}}";