diff --git a/changedetectionio/notification/handler.py b/changedetectionio/notification/handler.py
index eba35b71..63f98b8f 100644
--- a/changedetectionio/notification/handler.py
+++ b/changedetectionio/notification/handler.py
@@ -360,6 +360,18 @@ def process_notification(n_object: NotificationContextData, datastore):
# texty types
n_body = n_body.replace(CUSTOM_LINEBREAK_PLACEHOLDER, '\r\n')
+ else:
+ # ?format was IN the apprise URL, they are kind of on their own here, we will try our best
+ if 'format=html' in url:
+ n_body = n_body.replace(CUSTOM_LINEBREAK_PLACEHOLDER, '
\r\n')
+ # This will also prevent apprise from doing conversion
+ apprise_input_format = NotifyFormat.HTML.value
+ requested_output_format = NotifyFormat.HTML.value
+ elif 'format=text' in url:
+ n_body = n_body.replace(CUSTOM_LINEBREAK_PLACEHOLDER, '\r\n')
+ apprise_input_format = NotifyFormat.TEXT.value
+ requested_output_format = NotifyFormat.TEXT.value
+
sent_objs.append({'title': n_title,
'body': n_body,
'url': url})
diff --git a/changedetectionio/tests/smtp/test_notification_smtp.py b/changedetectionio/tests/smtp/test_notification_smtp.py
index a0ed0c15..a963cdd5 100644
--- a/changedetectionio/tests/smtp/test_notification_smtp.py
+++ b/changedetectionio/tests/smtp/test_notification_smtp.py
@@ -4,7 +4,7 @@ from email import message_from_string
from email.policy import default as email_policy
from changedetectionio.diff import HTML_REMOVED_STYLE, HTML_ADDED_STYLE, HTML_CHANGED_STYLE
-from changedetectionio.notification_service import NotificationContextData
+from changedetectionio.notification_service import NotificationContextData, CUSTOM_LINEBREAK_PLACEHOLDER
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
@@ -99,6 +99,7 @@ def test_check_notification_email_formats_default_HTML(client, live_server, meas
text_content = text_part.get_content()
assert '(added) So let\'s see what happens.\r\n' in text_content # The plaintext part
assert 'fallback-body\r\n' in text_content # The plaintext part
+ assert CUSTOM_LINEBREAK_PLACEHOLDER not in text_content
# Second part should be text/html
html_part = parts[1]
@@ -107,6 +108,7 @@ def test_check_notification_email_formats_default_HTML(client, live_server, meas
assert 'some text
' in html_content # We converted \n from the notification body
assert 'fallback-body
' in html_content # kept the original
assert '(added) So let\'s see what happens.
' in html_content # the html part
+ assert CUSTOM_LINEBREAK_PLACEHOLDER not in html_content
delete_all_watches(client)
@@ -680,3 +682,73 @@ def test_check_html_document_plaintext_notification(client, live_server, measure
delete_all_watches(client)
+def test_check_html_notification_with_apprise_format_is_html(client, live_server, measure_memory_usage):
+ ## live_server_setup(live_server) # Setup on conftest per function
+ set_original_response()
+
+ notification_url = f'mailto://changedetection@{smtp_test_server}:11025/?to=fff@home.com&format=html'
+
+ #####################
+ # 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": "some text\nfallback-body
" + default_notification_body,
+ "application-notification_format": '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
+ assert 'fallback-body\r\n' in text_content # The plaintext part
+ assert CUSTOM_LINEBREAK_PLACEHOLDER not in text_content
+
+ # Second part should be text/html
+ html_part = parts[1]
+ assert html_part.get_content_type() == 'text/html'
+ html_content = html_part.get_content()
+ assert 'some text
' in html_content # We converted \n from the notification body
+ assert 'fallback-body
' in html_content # kept the original
+ assert '(added) So let\'s see what happens.
' in html_content # the html part
+ assert CUSTOM_LINEBREAK_PLACEHOLDER not in html_content
+ delete_all_watches(client)
\ No newline at end of file