mirror of
				https://github.com/dgtlmoon/changedetection.io.git
				synced 2025-11-04 08:34:57 +00:00 
			
		
		
		
	Compare commits
	
		
			3 Commits
		
	
	
		
			color-fixe
			...
			3526-refac
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					951903287a | ||
| 
						 | 
					42c1f651e8 | ||
| 
						 | 
					4f5e7af950 | 
@@ -7,10 +7,10 @@ default_notification_title = 'ChangeDetection.io Notification - {{watch_url}}'
 | 
			
		||||
# The values (markdown etc) are from apprise NotifyFormat,
 | 
			
		||||
# But to avoid importing the whole heavy module just use the same strings here.
 | 
			
		||||
valid_notification_formats = {
 | 
			
		||||
    'Text': 'text',
 | 
			
		||||
    'Markdown': 'markdown',
 | 
			
		||||
    'Plain Text': 'text',
 | 
			
		||||
    'HTML': 'html',
 | 
			
		||||
    'HTML Color': 'htmlcolor',
 | 
			
		||||
    'Markdown to HTML': 'markdown',
 | 
			
		||||
    # Used only for editing a watch (not for global)
 | 
			
		||||
    default_notification_format_for_watch: default_notification_format_for_watch
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -53,6 +53,7 @@ def notification_format_align_with_apprise(n_format : str):
 | 
			
		||||
    """
 | 
			
		||||
    Correctly align changedetection's formats with apprise's formats
 | 
			
		||||
    Probably these are the same - but good to be sure.
 | 
			
		||||
    These set the expected OUTPUT format type
 | 
			
		||||
    :param n_format:
 | 
			
		||||
    :return:
 | 
			
		||||
    """
 | 
			
		||||
@@ -71,12 +72,63 @@ def notification_format_align_with_apprise(n_format : str):
 | 
			
		||||
 | 
			
		||||
    return n_format
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def apply_service_tweaks(url, n_body, n_title):
 | 
			
		||||
    # Re 323 - Limit discord length to their 2000 char limit total or it wont send.
 | 
			
		||||
    # Because different notifications may require different pre-processing, run each sequentially :(
 | 
			
		||||
    # 2000 bytes minus -
 | 
			
		||||
    #     200 bytes for the overhead of the _entire_ json payload, 200 bytes for {tts, wait, content} etc headers
 | 
			
		||||
    #     Length of URL - Incase they specify a longer custom avatar_url
 | 
			
		||||
 | 
			
		||||
    # So if no avatar_url is specified, add one so it can be correctly calculated into the total payload
 | 
			
		||||
    parsed = urlparse(url)
 | 
			
		||||
    k = '?' if not parsed.query else '&'
 | 
			
		||||
    if not 'avatar_url' in url \
 | 
			
		||||
            and not url.startswith('mail') \
 | 
			
		||||
            and not url.startswith('post') \
 | 
			
		||||
            and not url.startswith('get') \
 | 
			
		||||
            and not url.startswith('delete') \
 | 
			
		||||
            and not url.startswith('put'):
 | 
			
		||||
        url += k + f"avatar_url={APPRISE_AVATAR_URL}"
 | 
			
		||||
 | 
			
		||||
    if url.startswith('tgram://'):
 | 
			
		||||
        # Telegram only supports a limit subset of HTML, remove the '<br>' we place in.
 | 
			
		||||
        # re https://github.com/dgtlmoon/changedetection.io/issues/555
 | 
			
		||||
        # @todo re-use an existing library we have already imported to strip all non-allowed tags
 | 
			
		||||
        n_body = n_body.replace('<br>', '\n')
 | 
			
		||||
        n_body = n_body.replace('</br>', '\n')
 | 
			
		||||
        # real limit is 4096, but minus some for extra metadata
 | 
			
		||||
        payload_max_size = 3600
 | 
			
		||||
        body_limit = max(0, payload_max_size - len(n_title))
 | 
			
		||||
        n_title = n_title[0:payload_max_size]
 | 
			
		||||
        n_body = n_body[0:body_limit]
 | 
			
		||||
 | 
			
		||||
    elif url.startswith('discord://') or url.startswith('https://discordapp.com/api/webhooks') or url.startswith(
 | 
			
		||||
            'https://discord.com/api'):
 | 
			
		||||
        # real limit is 2000, but minus some for extra metadata
 | 
			
		||||
        payload_max_size = 1700
 | 
			
		||||
        body_limit = max(0, payload_max_size - len(n_title))
 | 
			
		||||
        n_title = n_title[0:payload_max_size]
 | 
			
		||||
        n_body = n_body[0:body_limit]
 | 
			
		||||
 | 
			
		||||
    return url, n_body, n_title
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def process_notification(n_object: NotificationContextData, datastore):
 | 
			
		||||
    from changedetectionio.jinja2_custom 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
 | 
			
		||||
 | 
			
		||||
    # Create list of custom handler protocols (both http and https versions)
 | 
			
		||||
    custom_handler_protocols = [f"{method}://" for method in SUPPORTED_HTTP_METHODS]
 | 
			
		||||
    custom_handler_protocols += [f"{method}s://" for method in SUPPORTED_HTTP_METHODS]
 | 
			
		||||
 | 
			
		||||
    has_custom_handler = any(
 | 
			
		||||
        url.startswith(tuple(custom_handler_protocols))
 | 
			
		||||
        for url in n_object['notification_urls']
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    if not isinstance(n_object, NotificationContextData):
 | 
			
		||||
        raise TypeError(f"Expected NotificationContextData, got {type(n_object)}")
 | 
			
		||||
 | 
			
		||||
@@ -87,20 +139,25 @@ def process_notification(n_object: NotificationContextData, datastore):
 | 
			
		||||
    # Insert variables into the notification content
 | 
			
		||||
    notification_parameters = create_notification_parameters(n_object, datastore)
 | 
			
		||||
 | 
			
		||||
    n_format = valid_notification_formats.get(
 | 
			
		||||
    requested_output_format = valid_notification_formats.get(
 | 
			
		||||
        n_object.get('notification_format', default_notification_format),
 | 
			
		||||
        valid_notification_formats[default_notification_format],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # If we arrived with 'System default' then look it up
 | 
			
		||||
    if n_format == default_notification_format_for_watch and datastore.data['settings']['application'].get('notification_format') != default_notification_format_for_watch:
 | 
			
		||||
    if requested_output_format == default_notification_format_for_watch and datastore.data['settings']['application'].get('notification_format') != default_notification_format_for_watch:
 | 
			
		||||
        # Initially text or whatever
 | 
			
		||||
        n_format = datastore.data['settings']['application'].get('notification_format', valid_notification_formats[default_notification_format]).lower()
 | 
			
		||||
        requested_output_format = datastore.data['settings']['application'].get('notification_format', valid_notification_formats[default_notification_format]).lower()
 | 
			
		||||
 | 
			
		||||
    n_format = notification_format_align_with_apprise(n_format=n_format)
 | 
			
		||||
    requested_output_format = notification_format_align_with_apprise(n_format=requested_output_format)
 | 
			
		||||
 | 
			
		||||
    logger.trace(f"Complete notification body including Jinja and placeholders calculated in  {time.time() - now:.2f}s")
 | 
			
		||||
 | 
			
		||||
    # If we have custom handlers, use invalid format to prevent conversion
 | 
			
		||||
    # Otherwise use the proper format
 | 
			
		||||
    if has_custom_handler:
 | 
			
		||||
        input_format = 'raw-no-convert'
 | 
			
		||||
 | 
			
		||||
    # https://github.com/caronc/apprise/wiki/Development_LogCapture
 | 
			
		||||
    # Anything higher than or equal to WARNING (which covers things like Connection errors)
 | 
			
		||||
    # raise it as an exception
 | 
			
		||||
@@ -117,6 +174,8 @@ def process_notification(n_object: NotificationContextData, datastore):
 | 
			
		||||
 | 
			
		||||
    with apprise.LogCapture(level=apprise.logging.DEBUG) as logs:
 | 
			
		||||
        for url in n_object['notification_urls']:
 | 
			
		||||
            parsed_url = urlparse(url)
 | 
			
		||||
            prefix_add_to_url = '?' if not parsed_url.query else '&'
 | 
			
		||||
 | 
			
		||||
            # Get the notification body from datastore
 | 
			
		||||
            n_body = jinja_render(template_str=n_object.get('notification_body', ''), **notification_parameters)
 | 
			
		||||
@@ -124,8 +183,32 @@ def process_notification(n_object: NotificationContextData, datastore):
 | 
			
		||||
            if n_object.get('markup_text_to_html'):
 | 
			
		||||
                n_body = markup_text_links_to_html(body=n_body)
 | 
			
		||||
 | 
			
		||||
            if n_format == NotifyFormat.HTML.value:
 | 
			
		||||
            # This actually means we request "Markdown to HTML"
 | 
			
		||||
            if requested_output_format == NotifyFormat.MARKDOWN.value:
 | 
			
		||||
                output_format = NotifyFormat.HTML.value
 | 
			
		||||
                input_format = NotifyFormat.MARKDOWN.value
 | 
			
		||||
                if not 'format=' in url.lower():
 | 
			
		||||
                    url = f"{url}{prefix_add_to_url}format={output_format}"
 | 
			
		||||
 | 
			
		||||
            # Deviation from apprise.
 | 
			
		||||
            # No conversion, its like they want to send raw HTML but we add linebreaks
 | 
			
		||||
            elif requested_output_format == NotifyFormat.HTML.value:
 | 
			
		||||
                # same in and out means apprise wont try to convert
 | 
			
		||||
                input_format = output_format = NotifyFormat.HTML.value
 | 
			
		||||
                n_body = n_body.replace("\n", '<br>')
 | 
			
		||||
                if not 'format=' in url.lower():
 | 
			
		||||
                    url = f"{url}{prefix_add_to_url}format={output_format}"
 | 
			
		||||
 | 
			
		||||
            else:
 | 
			
		||||
                # Nothing to be done, leave it as plaintext
 | 
			
		||||
                # `body_format` Tell apprise what format the INPUT is in
 | 
			
		||||
                # &format= in URL Tell apprise what format the OUTPUT should be in (it can convert between)
 | 
			
		||||
                input_format = output_format = NotifyFormat.TEXT.value
 | 
			
		||||
                if not 'format=' in url.lower():
 | 
			
		||||
                    url = f"{url}{prefix_add_to_url}format={output_format}"
 | 
			
		||||
 | 
			
		||||
            if has_custom_handler:
 | 
			
		||||
                input_format='raw-no-convert'
 | 
			
		||||
 | 
			
		||||
            n_title = jinja_render(template_str=n_object.get('notification_title', ''), **notification_parameters)
 | 
			
		||||
 | 
			
		||||
@@ -141,88 +224,24 @@ def process_notification(n_object: NotificationContextData, datastore):
 | 
			
		||||
            logger.info(f">> Process Notification: AppRise notifying {url}")
 | 
			
		||||
            url = jinja_render(template_str=url, **notification_parameters)
 | 
			
		||||
 | 
			
		||||
            # Re 323 - Limit discord length to their 2000 char limit total or it wont send.
 | 
			
		||||
            # Because different notifications may require different pre-processing, run each sequentially :(
 | 
			
		||||
            # 2000 bytes minus -
 | 
			
		||||
            #     200 bytes for the overhead of the _entire_ json payload, 200 bytes for {tts, wait, content} etc headers
 | 
			
		||||
            #     Length of URL - Incase they specify a longer custom avatar_url
 | 
			
		||||
 | 
			
		||||
            # So if no avatar_url is specified, add one so it can be correctly calculated into the total payload
 | 
			
		||||
            parsed = urlparse(url)
 | 
			
		||||
            k = '?' if not parsed.query else '&'
 | 
			
		||||
            if not 'avatar_url' in url \
 | 
			
		||||
                    and not url.startswith('mail') \
 | 
			
		||||
                    and not url.startswith('post') \
 | 
			
		||||
                    and not url.startswith('get') \
 | 
			
		||||
                    and not url.startswith('delete') \
 | 
			
		||||
                    and not url.startswith('put'):
 | 
			
		||||
                url += k + f"avatar_url={APPRISE_AVATAR_URL}"
 | 
			
		||||
 | 
			
		||||
            if url.startswith('tgram://'):
 | 
			
		||||
                # Telegram only supports a limit subset of HTML, remove the '<br>' we place in.
 | 
			
		||||
                # re https://github.com/dgtlmoon/changedetection.io/issues/555
 | 
			
		||||
                # @todo re-use an existing library we have already imported to strip all non-allowed tags
 | 
			
		||||
                n_body = n_body.replace('<br>', '\n')
 | 
			
		||||
                n_body = n_body.replace('</br>', '\n')
 | 
			
		||||
                # real limit is 4096, but minus some for extra metadata
 | 
			
		||||
                payload_max_size = 3600
 | 
			
		||||
                body_limit = max(0, payload_max_size - len(n_title))
 | 
			
		||||
                n_title = n_title[0:payload_max_size]
 | 
			
		||||
                n_body = n_body[0:body_limit]
 | 
			
		||||
 | 
			
		||||
            elif url.startswith('discord://') or url.startswith('https://discordapp.com/api/webhooks') or url.startswith(
 | 
			
		||||
                    'https://discord.com/api'):
 | 
			
		||||
                # real limit is 2000, but minus some for extra metadata
 | 
			
		||||
                payload_max_size = 1700
 | 
			
		||||
                body_limit = max(0, payload_max_size - len(n_title))
 | 
			
		||||
                n_title = n_title[0:payload_max_size]
 | 
			
		||||
                n_body = n_body[0:body_limit]
 | 
			
		||||
 | 
			
		||||
            # Add format parameter to mailto URLs to ensure proper text/html handling
 | 
			
		||||
            # https://github.com/caronc/apprise/issues/633#issuecomment-1191449321
 | 
			
		||||
            # Note: Custom handlers (post://, get://, etc.) don't need this as we handle them
 | 
			
		||||
            # differently by passing an invalid body_format to prevent HTML conversion
 | 
			
		||||
            if not 'format=' in url and url.startswith(('mailto', 'mailtos')):
 | 
			
		||||
                parsed = urlparse(url)
 | 
			
		||||
                prefix = '?' if not parsed.query else '&'
 | 
			
		||||
                # Apprise format is already lowercase from notification_format_align_with_apprise()
 | 
			
		||||
                url = f"{url}{prefix}format={n_format}"
 | 
			
		||||
            (url, n_body, n_title) = apply_service_tweaks(url=url, n_body=n_body, n_title=n_title)
 | 
			
		||||
 | 
			
		||||
            apobj.add(url)
 | 
			
		||||
 | 
			
		||||
            sent_objs.append({'title': n_title,
 | 
			
		||||
                              'body': n_body,
 | 
			
		||||
                              'url': url,
 | 
			
		||||
                              'body_format': n_format})
 | 
			
		||||
 | 
			
		||||
        # Blast off the notifications tht are set in .add()
 | 
			
		||||
        # Check if we have any custom HTTP handlers (post://, get://, etc.)
 | 
			
		||||
        # These handlers created with @notify decorator don't handle format conversion properly
 | 
			
		||||
        # and will strip HTML if we pass a valid format. So we pass an invalid format string
 | 
			
		||||
        # to prevent Apprise from converting HTML->TEXT
 | 
			
		||||
 | 
			
		||||
        # Create list of custom handler protocols (both http and https versions)
 | 
			
		||||
        custom_handler_protocols = [f"{method}://" for method in SUPPORTED_HTTP_METHODS]
 | 
			
		||||
        custom_handler_protocols += [f"{method}s://" for method in SUPPORTED_HTTP_METHODS]
 | 
			
		||||
 | 
			
		||||
        has_custom_handler = any(
 | 
			
		||||
            url.startswith(tuple(custom_handler_protocols))
 | 
			
		||||
            for url in n_object['notification_urls']
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # If we have custom handlers, use invalid format to prevent conversion
 | 
			
		||||
        # Otherwise use the proper format
 | 
			
		||||
        notify_format = 'raw-no-convert' if has_custom_handler else n_format
 | 
			
		||||
                              'url': url})
 | 
			
		||||
 | 
			
		||||
        apobj.notify(
 | 
			
		||||
            title=n_title,
 | 
			
		||||
            body=n_body,
 | 
			
		||||
            body_format=notify_format,
 | 
			
		||||
            # `body_format` Tell apprise what format the INPUT is in
 | 
			
		||||
            # &format= in URL Tell apprise what format the OUTPUT should be in (it can convert between)
 | 
			
		||||
            body_format=input_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
 | 
			
		||||
        log_value = logs.getvalue()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,4 @@
 | 
			
		||||
import json
 | 
			
		||||
import os
 | 
			
		||||
import time
 | 
			
		||||
import re
 | 
			
		||||
from flask import url_for
 | 
			
		||||
from email import message_from_string
 | 
			
		||||
from email.policy import default as email_policy
 | 
			
		||||
@@ -10,9 +7,9 @@ from changedetectionio.diff import REMOVED_STYLE, ADDED_STYLE
 | 
			
		||||
from changedetectionio.tests.util import set_original_response, set_modified_response, set_more_modified_response, live_server_setup, \
 | 
			
		||||
    wait_for_all_checks, \
 | 
			
		||||
    set_longer_modified_response, delete_all_watches
 | 
			
		||||
from changedetectionio.tests.util import extract_UUID_from_client
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
import base64
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# NOTE - RELIES ON mailserver as hostname running, see github build recipes
 | 
			
		||||
smtp_test_server = 'mailserver'
 | 
			
		||||
@@ -124,7 +121,7 @@ def test_check_notification_plaintext_format(client, live_server, measure_memory
 | 
			
		||||
        data={"application-notification_urls": notification_url,
 | 
			
		||||
              "application-notification_title": "fallback-title " + default_notification_title,
 | 
			
		||||
              "application-notification_body": "some text\n" + default_notification_body,
 | 
			
		||||
              "application-notification_format": 'Text',
 | 
			
		||||
              "application-notification_format": 'Plain Text',
 | 
			
		||||
              "requests-time_between_check-minutes": 180,
 | 
			
		||||
              'application-fetch_backend': "html_requests"},
 | 
			
		||||
        follow_redirects=True
 | 
			
		||||
@@ -134,18 +131,11 @@ def test_check_notification_plaintext_format(client, live_server, measure_memory
 | 
			
		||||
 | 
			
		||||
    # Add a watch and trigger a HTTP POST
 | 
			
		||||
    test_url = url_for('test_endpoint', _external=True)
 | 
			
		||||
    res = client.post(
 | 
			
		||||
        url_for("ui.ui_views.form_quick_watch_add"),
 | 
			
		||||
        data={"url": test_url, "tags": 'nice one'},
 | 
			
		||||
        follow_redirects=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    assert b"Watch added" in res.data
 | 
			
		||||
 | 
			
		||||
    wait_for_all_checks(client)
 | 
			
		||||
    set_longer_modified_response()
 | 
			
		||||
    uuid = client.application.config.get('DATASTORE').add_watch(url=test_url)
 | 
			
		||||
    client.get(url_for("ui.form_watch_checknow"), follow_redirects=True)
 | 
			
		||||
    time.sleep(2)
 | 
			
		||||
 | 
			
		||||
    set_longer_modified_response()
 | 
			
		||||
    client.get(url_for("ui.form_watch_checknow"), follow_redirects=True)
 | 
			
		||||
    wait_for_all_checks(client)
 | 
			
		||||
 | 
			
		||||
@@ -241,9 +231,76 @@ def test_check_notification_html_color_format(client, live_server, measure_memor
 | 
			
		||||
    assert 'some text<br>' in html_content
 | 
			
		||||
    delete_all_watches(client)
 | 
			
		||||
 | 
			
		||||
def test_check_notification_markdown_format(client, live_server, measure_memory_usage):
 | 
			
		||||
    set_original_response()
 | 
			
		||||
 | 
			
		||||
    notification_url = f'mailto://changedetection@{smtp_test_server}:11025/?to=fff@home.com'
 | 
			
		||||
 | 
			
		||||
    #####################
 | 
			
		||||
    # Set this up for when we remove the notification from the watch, it should fallback with these details
 | 
			
		||||
    res = client.post(
 | 
			
		||||
        url_for("settings.settings_page"),
 | 
			
		||||
        data={"application-notification_urls": notification_url,
 | 
			
		||||
              "application-notification_title": "fallback-title " + default_notification_title,
 | 
			
		||||
              "application-notification_body": "*header*\n\nsome text\n" + default_notification_body,
 | 
			
		||||
              "application-notification_format": 'Markdown to HTML',
 | 
			
		||||
              "requests-time_between_check-minutes": 180,
 | 
			
		||||
              'application-fetch_backend': "html_requests"},
 | 
			
		||||
        follow_redirects=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    assert b"Settings updated." in res.data
 | 
			
		||||
 | 
			
		||||
    # Add a watch and trigger a HTTP POST
 | 
			
		||||
    test_url = url_for('test_endpoint', _external=True)
 | 
			
		||||
    res = client.post(
 | 
			
		||||
        url_for("ui.ui_views.form_quick_watch_add"),
 | 
			
		||||
        data={"url": test_url, "tags": 'nice one'},
 | 
			
		||||
        follow_redirects=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    assert b"Watch added" in res.data
 | 
			
		||||
 | 
			
		||||
    wait_for_all_checks(client)
 | 
			
		||||
    set_longer_modified_response()
 | 
			
		||||
    time.sleep(2)
 | 
			
		||||
 | 
			
		||||
    client.get(url_for("ui.form_watch_checknow"), follow_redirects=True)
 | 
			
		||||
    wait_for_all_checks(client)
 | 
			
		||||
 | 
			
		||||
    time.sleep(3)
 | 
			
		||||
 | 
			
		||||
    msg_raw = get_last_message_from_smtp_server()
 | 
			
		||||
    assert len(msg_raw) >= 1
 | 
			
		||||
 | 
			
		||||
    # Parse the email properly using Python's email library
 | 
			
		||||
    msg = message_from_string(msg_raw, policy=email_policy)
 | 
			
		||||
 | 
			
		||||
    # The email should have two bodies (multipart/alternative with text/plain and text/html)
 | 
			
		||||
    assert msg.is_multipart()
 | 
			
		||||
    assert msg.get_content_type() == 'multipart/alternative'
 | 
			
		||||
 | 
			
		||||
    # Get the parts
 | 
			
		||||
    parts = list(msg.iter_parts())
 | 
			
		||||
    assert len(parts) == 2
 | 
			
		||||
 | 
			
		||||
    # First part should be text/plain (the auto-generated plaintext version)
 | 
			
		||||
    text_part = parts[0]
 | 
			
		||||
    assert text_part.get_content_type() == 'text/plain'
 | 
			
		||||
    text_content = text_part.get_content()
 | 
			
		||||
    assert '(added) So let\'s see what happens.\r\n' in text_content  # The plaintext part
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # Second part should be text/html and roughly converted from markdown to HTML
 | 
			
		||||
    html_part = parts[1]
 | 
			
		||||
    assert html_part.get_content_type() == 'text/html'
 | 
			
		||||
    html_content = html_part.get_content()
 | 
			
		||||
    assert '<p><em>header</em></p>' in html_content
 | 
			
		||||
    assert '(added) So let\'s see what happens.<br' in html_content
 | 
			
		||||
    delete_all_watches(client)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_check_notification_email_formats_default_Text_override_HTML(client, live_server, measure_memory_usage):
 | 
			
		||||
    ##  live_server_setup(live_server) # Setup on conftest per function
 | 
			
		||||
 | 
			
		||||
    # HTML problems? see this
 | 
			
		||||
    # https://github.com/caronc/apprise/issues/633
 | 
			
		||||
@@ -269,7 +326,7 @@ def test_check_notification_email_formats_default_Text_override_HTML(client, liv
 | 
			
		||||
        data={"application-notification_urls": notification_url,
 | 
			
		||||
              "application-notification_title": "fallback-title " + default_notification_title,
 | 
			
		||||
              "application-notification_body": notification_body,
 | 
			
		||||
              "application-notification_format": 'Text',
 | 
			
		||||
              "application-notification_format": 'Plain Text',
 | 
			
		||||
              "requests-time_between_check-minutes": 180,
 | 
			
		||||
              'application-fetch_backend': "html_requests"},
 | 
			
		||||
        follow_redirects=True
 | 
			
		||||
 
 | 
			
		||||
@@ -86,7 +86,7 @@ def test_filter_doesnt_exist_then_exists_should_get_notification(client, live_se
 | 
			
		||||
                                                   "Diff Full: {{diff_full}}\n"
 | 
			
		||||
                                                   "Diff as Patch: {{diff_patch}}\n"
 | 
			
		||||
                                                   ":-)",
 | 
			
		||||
                              "notification_format": "Text"}
 | 
			
		||||
                              "notification_format": 'Plain Text'}
 | 
			
		||||
 | 
			
		||||
    notification_form_data.update({
 | 
			
		||||
        "url": test_url,
 | 
			
		||||
 
 | 
			
		||||
@@ -63,7 +63,7 @@ def run_filter_test(client, live_server, content_filter, app_notification_format
 | 
			
		||||
                                       "Diff Full: {{diff_full}}\n"
 | 
			
		||||
                                       "Diff as Patch: {{diff_patch}}\n"
 | 
			
		||||
                                       ":-)",
 | 
			
		||||
                  "notification_format": "Text",
 | 
			
		||||
                  "notification_format": 'Plain Text',
 | 
			
		||||
                  "fetch_backend": "html_requests",
 | 
			
		||||
                  "filter_failure_notification_send": 'y',
 | 
			
		||||
                  "time_between_check_use_default": "y",
 | 
			
		||||
@@ -177,7 +177,7 @@ def test_check_include_filters_failure_notification(client, live_server, measure
 | 
			
		||||
    #   #  live_server_setup(live_server) # Setup on conftest per function
 | 
			
		||||
    run_filter_test(client=client, live_server=live_server, content_filter='#nope-doesnt-exist', app_notification_format=valid_notification_formats.get('HTML Color'))
 | 
			
		||||
    # Check markup send conversion didnt affect plaintext preference
 | 
			
		||||
    run_filter_test(client=client, live_server=live_server, content_filter='#nope-doesnt-exist', app_notification_format=valid_notification_formats.get('Text'))
 | 
			
		||||
    run_filter_test(client=client, live_server=live_server, content_filter='#nope-doesnt-exist', app_notification_format=valid_notification_formats.get('Plain Text'))
 | 
			
		||||
 | 
			
		||||
def test_check_xpath_filter_failure_notification(client, live_server, measure_memory_usage):
 | 
			
		||||
    #   #  live_server_setup(live_server) # Setup on conftest per function
 | 
			
		||||
 
 | 
			
		||||
@@ -195,7 +195,7 @@ def test_group_tag_notification(client, live_server, measure_memory_usage):
 | 
			
		||||
                                                   "Diff as Patch: {{diff_patch}}\n"
 | 
			
		||||
                                                   ":-)",
 | 
			
		||||
                              "notification_screenshot": True,
 | 
			
		||||
                              "notification_format": "Text",
 | 
			
		||||
                              "notification_format": 'Plain Text',
 | 
			
		||||
                              "title": "test-tag"}
 | 
			
		||||
 | 
			
		||||
    res = client.post(
 | 
			
		||||
 
 | 
			
		||||
@@ -101,7 +101,7 @@ def test_check_notification(client, live_server, measure_memory_usage):
 | 
			
		||||
                                                   "Diff as Patch: {{diff_patch}}\n"
 | 
			
		||||
                                                   ":-)",
 | 
			
		||||
                              "notification_screenshot": True,
 | 
			
		||||
                              "notification_format": "Text"}
 | 
			
		||||
                              "notification_format": 'Plain Text'}
 | 
			
		||||
 | 
			
		||||
    notification_form_data.update({
 | 
			
		||||
        "url": test_url,
 | 
			
		||||
@@ -267,7 +267,7 @@ def test_notification_validation(client, live_server, measure_memory_usage):
 | 
			
		||||
#        data={"notification_urls": 'json://localhost/foobar',
 | 
			
		||||
#              "notification_title": "",
 | 
			
		||||
#              "notification_body": "",
 | 
			
		||||
#              "notification_format": "Text",
 | 
			
		||||
#              "notification_format": 'Plain Text',
 | 
			
		||||
#              "url": test_url,
 | 
			
		||||
#              "tag": "my tag",
 | 
			
		||||
#              "title": "my title",
 | 
			
		||||
@@ -383,7 +383,7 @@ def test_notification_custom_endpoint_and_jinja2(client, live_server, measure_me
 | 
			
		||||
        assert 'second: hello world "space"' in notification_headers.lower()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # Should always be automatically detected as JSON content type even when we set it as 'Text' (default)
 | 
			
		||||
    # Should always be automatically detected as JSON content type even when we set it as 'Plain Text' (default)
 | 
			
		||||
    assert os.path.isfile("test-datastore/notification-content-type.txt")
 | 
			
		||||
    with open("test-datastore/notification-content-type.txt", 'r') as f:
 | 
			
		||||
        assert 'application/json' in f.read()
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ def test_check_notification_error_handling(client, live_server, measure_memory_u
 | 
			
		||||
        data={"notification_urls": f"{broken_notification_url}\r\n{working_notification_url}",
 | 
			
		||||
              "notification_title": "xxx",
 | 
			
		||||
              "notification_body": "xxxxx",
 | 
			
		||||
              "notification_format": "Text",
 | 
			
		||||
              "notification_format": 'Plain Text',
 | 
			
		||||
              "url": test_url,
 | 
			
		||||
              "tags": "",
 | 
			
		||||
              "title": "",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user