diff --git a/changedetectionio/notification.py b/changedetectionio/notification.py
index 6c0f53f6..93cd304e 100644
--- a/changedetectionio/notification.py
+++ b/changedetectionio/notification.py
@@ -46,6 +46,9 @@ from apprise.decorators import notify
@notify(on="puts")
def apprise_custom_api_call_wrapper(body, title, notify_type, *args, **kwargs):
import requests
+ from apprise.utils import parse_url as apprise_parse_url
+ from apprise.URLBase import URLBase
+
url = kwargs['meta'].get('url')
if url.startswith('post'):
@@ -68,16 +71,45 @@ def apprise_custom_api_call_wrapper(body, title, notify_type, *args, **kwargs):
url = url.replace('delete://', 'http://')
url = url.replace('deletes://', 'https://')
- # Try to auto-guess if it's JSON
headers = {}
+ params = {}
+ auth = None
+
+ # Convert /foobar?+some-header=hello to proper header dictionary
+ results = apprise_parse_url(url)
+ if results:
+ # Add our headers that the user can potentially over-ride if they wish
+ # to to our returned result set and tidy entries by unquoting them
+ headers = {URLBase.unquote(x): URLBase.unquote(y)
+ for x, y in results['qsd+'].items()}
+
+ # https://github.com/caronc/apprise/wiki/Notify_Custom_JSON#get-parameter-manipulation
+ # In Apprise, it relies on prefixing each request arg with "-", because it uses say &method=update as a flag for apprise
+ # but here we are making straight requests, so we need todo convert this against apprise's logic
+ for k, v in results['qsd'].items():
+ if not k.strip('+-') in results['qsd+'].keys():
+ params[URLBase.unquote(k)] = URLBase.unquote(v)
+
+ # Determine Authentication
+ auth = ''
+ if results.get('user') and results.get('password'):
+ auth = (URLBase.unquote(results.get('user')), URLBase.unquote(results.get('user')))
+ elif results.get('user'):
+ auth = (URLBase.unquote(results.get('user')))
+
+ # Try to auto-guess if it's JSON
try:
json.loads(body)
- headers = {'Content-Type': 'application/json; charset=utf-8'}
+ headers['Content-Type'] = 'application/json; charset=utf-8'
except ValueError as e:
pass
-
- r(url, headers=headers, data=body)
+ r(results.get('url'),
+ auth=auth,
+ data=body,
+ headers=headers,
+ params=params
+ )
def process_notification(n_object, datastore):
diff --git a/changedetectionio/templates/_common_fields.jinja b/changedetectionio/templates/_common_fields.jinja
index cb27476e..fa113e17 100644
--- a/changedetectionio/templates/_common_fields.jinja
+++ b/changedetectionio/templates/_common_fields.jinja
@@ -16,7 +16,7 @@
discord://
(or https://discord.com/api/webhooks...
)) only supports a maximum 2,000 characters of notification text, including the title.
tgram://
bots can't send messages to other bots, so you should specify chat ID of non-bot user.
tgram://
only supports very limited HTML and can fail when extra tags are sent, read more here (or use plaintext/markdown format)
- gets://
, posts://
, puts://
, deletes://
for direct API calls (or omit the "s
" for non-SSL ie get://
)
+ gets://
, posts://
, puts://
, deletes://
for direct API calls (or omit the "s
" for non-SSL ie get://
) more help here
Accepts the {{ '{{token}}' }}
placeholders listed below
diff --git a/changedetectionio/tests/conftest.py b/changedetectionio/tests/conftest.py
index 754ec1fc..79ea5bc8 100644
--- a/changedetectionio/tests/conftest.py
+++ b/changedetectionio/tests/conftest.py
@@ -13,22 +13,17 @@ global app
def cleanup(datastore_path):
+ import glob
# Unlink test output files
- files = [
- 'count.txt',
- 'endpoint-content.txt'
- 'headers.txt',
- 'headers-testtag.txt',
- 'notification.txt',
- 'secret.txt',
- 'url-watches.json',
- 'output.txt',
- ]
- for file in files:
- try:
- os.unlink("{}/{}".format(datastore_path, file))
- except FileNotFoundError:
- pass
+
+ for g in ["*.txt", "*.json", "*.pdf"]:
+ files = glob.glob(os.path.join(datastore_path, g))
+ for f in files:
+ if 'proxies.json' in f:
+ # Usually mounted by docker container during test time
+ continue
+ if os.path.isfile(f):
+ os.unlink(f)
@pytest.fixture(scope='session')
def app(request):
diff --git a/changedetectionio/tests/test_notification.py b/changedetectionio/tests/test_notification.py
index 7d0e5ff2..3c6674f8 100644
--- a/changedetectionio/tests/test_notification.py
+++ b/changedetectionio/tests/test_notification.py
@@ -281,7 +281,8 @@ def test_notification_custom_endpoint_and_jinja2(client, live_server):
# CUSTOM JSON BODY CHECK for POST://
set_original_response()
- test_notification_url = url_for('test_notification_endpoint', _external=True).replace('http://', 'post://')+"?xxx={{ watch_url }}"
+ # https://github.com/caronc/apprise/wiki/Notify_Custom_JSON#header-manipulation
+ test_notification_url = url_for('test_notification_endpoint', _external=True).replace('http://', 'post://')+"?xxx={{ watch_url }}&+custom-header=123"
res = client.post(
url_for("settings_page"),
@@ -297,10 +298,7 @@ def test_notification_custom_endpoint_and_jinja2(client, live_server):
follow_redirects=True
)
assert b'Settings updated' in res.data
- client.get(
- url_for("form_delete", uuid="all"),
- follow_redirects=True
- )
+
# Add a watch and trigger a HTTP POST
test_url = url_for('test_endpoint', _external=True)
res = client.post(
@@ -315,7 +313,9 @@ def test_notification_custom_endpoint_and_jinja2(client, live_server):
set_modified_response()
client.get(url_for("form_watch_checknow"), follow_redirects=True)
- time.sleep(2)
+ wait_for_all_checks(client)
+
+ time.sleep(2) # plus extra delay for notifications to fire
with open("test-datastore/notification.txt", 'r') as f:
x = f.read()
@@ -328,6 +328,13 @@ def test_notification_custom_endpoint_and_jinja2(client, live_server):
with open("test-datastore/notification-url.txt", 'r') as f:
notification_url = f.read()
assert 'xxx=http' in notification_url
+ # apprise style headers should be stripped
+ assert 'custom-header' not in notification_url
+
+ with open("test-datastore/notification-headers.txt", 'r') as f:
+ notification_headers = f.read()
+ assert 'custom-header: 123' in notification_headers.lower()
+
# Should always be automatically detected as JSON content type even when we set it as 'Text' (default)
assert os.path.isfile("test-datastore/notification-content-type.txt")
@@ -335,3 +342,8 @@ def test_notification_custom_endpoint_and_jinja2(client, live_server):
assert 'application/json' in f.read()
os.unlink("test-datastore/notification-url.txt")
+
+ client.get(
+ url_for("form_delete", uuid="all"),
+ follow_redirects=True
+ )
diff --git a/changedetectionio/tests/util.py b/changedetectionio/tests/util.py
index 904c1b62..ed1e424e 100644
--- a/changedetectionio/tests/util.py
+++ b/changedetectionio/tests/util.py
@@ -205,6 +205,9 @@ def live_server_setup(live_server):
with open("test-datastore/notification-url.txt", "w") as f:
f.write(request.url)
+ with open("test-datastore/notification-headers.txt", "w") as f:
+ f.write(str(request.headers))
+
if request.content_type:
with open("test-datastore/notification-content-type.txt", "w") as f:
f.write(request.content_type)