From 42e1098e7b5bd324f3fdefbb4f86a1b85bcaafc5 Mon Sep 17 00:00:00 2001 From: dgtlmoon Date: Mon, 5 Dec 2022 19:01:09 +0100 Subject: [PATCH] Support for standard API calls post:// posts:// get:// gets:// delete:// deletes:// put:// puts:// --- changedetectionio/notification.py | 55 ++++++++++++++++++- .../templates/_common_fields.jinja | 2 +- changedetectionio/tests/test_notification.py | 47 ++++++++-------- 3 files changed, 79 insertions(+), 25 deletions(-) diff --git a/changedetectionio/notification.py b/changedetectionio/notification.py index ac0df079..492cdef8 100644 --- a/changedetectionio/notification.py +++ b/changedetectionio/notification.py @@ -1,6 +1,7 @@ import apprise from jinja2 import Environment, BaseLoader from apprise import NotifyFormat +import json valid_tokens = { 'base_url': '', @@ -28,6 +29,53 @@ valid_notification_formats = { default_notification_format_for_watch: default_notification_format_for_watch } +# include the decorator +from apprise.decorators import notify + +@notify(on="delete") +@notify(on="deletes") +@notify(on="get") +@notify(on="gets") +@notify(on="post") +@notify(on="posts") +@notify(on="put") +@notify(on="puts") +def apprise_custom_api_call_wrapper(body, title, notify_type, *args, **kwargs): + import requests + url = kwargs['meta'].get('url') + + if url.startswith('post'): + r = requests.post + elif url.startswith('get'): + r = requests.get + elif url.startswith('put'): + r = requests.put + elif url.startswith('delete'): + r = requests.delete + + url = url.replace('post://', 'http://') + url = url.replace('posts://', 'https://') + url = url.replace('put://', 'http://') + url = url.replace('puts://', 'https://') + url = url.replace('get://', 'http://') + url = url.replace('gets://', 'https://') + url = url.replace('put://', 'http://') + url = url.replace('puts://', 'https://') + url = url.replace('delete://', 'http://') + url = url.replace('deletes://', 'https://') + + # Try to auto-guess if it's JSON + headers = {} + try: + json.loads(body) + headers = {'Content-Type': 'application/json; charset=utf-8'} + except ValueError as e: + pass + + + r(url, headers=headers, data=body) + + def process_notification(n_object, datastore): # Insert variables into the notification content @@ -63,7 +111,12 @@ def process_notification(n_object, datastore): # So if no avatar_url is specified, add one so it can be correctly calculated into the total payload k = '?' if not '?' in url else '&' - if not 'avatar_url' in url and not url.startswith('mail'): + 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 + 'avatar_url=https://raw.githubusercontent.com/dgtlmoon/changedetection.io/master/changedetectionio/static/images/avatar-256x256.png' if url.startswith('tgram://'): diff --git a/changedetectionio/templates/_common_fields.jinja b/changedetectionio/templates/_common_fields.jinja index 07a4e357..0d1a7167 100644 --- a/changedetectionio/templates/_common_fields.jinja +++ b/changedetectionio/templates/_common_fields.jinja @@ -16,7 +16,7 @@
  • discord:// only supports a maximum 2,000 characters of notification text, including the title.
  • tgram:// bots cant 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)
  • -
  • GET & POST requests use json:// or jsons://, use - before each arg, for example jsons://foobar.com?-id={% raw -%}{{watch_uuid}}{%- endraw %} read more here
  • +
  • gets://, posts://, puts://, deletes:// for direct API calls (or omit the "s" for non-SSL ie get://)
  • diff --git a/changedetectionio/tests/test_notification.py b/changedetectionio/tests/test_notification.py index e9f88732..f1ab4b0d 100644 --- a/changedetectionio/tests/test_notification.py +++ b/changedetectionio/tests/test_notification.py @@ -237,7 +237,6 @@ def test_check_notification(client, live_server): ) def test_notification_validation(client, live_server): - #live_server_setup(live_server) time.sleep(1) # re #242 - when you edited an existing new entry, it would not correctly show the notification settings @@ -309,19 +308,24 @@ def test_notification_jinja2(client, live_server): # test_endpoint - that sends the contents of a file # test_notification_endpoint - that takes a POST and writes it to file (test-datastore/notification.txt) - test_notification_url = url_for('test_notification_endpoint', _external=True).replace('http://', 'json://') + # 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 }}" + res = client.post( url_for("settings_page"), data={"application-notification_title": "New ChangeDetection.io Notification - {{ watch_url }}", - "application-notification_body": "Got {{ watch_url }}\n", + "application-notification_body": '{ "url" : "{{ watch_url }}", "secret": 444 }', # https://github.com/caronc/apprise/wiki/Notify_Custom_JSON#get-parameter-manipulation - "application-notification_urls": test_notification_url+"?-XXX={{ watch_url }}", + "application-notification_urls": test_notification_url, "application-minutes_between_check": 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("form_quick_watch_add"), @@ -330,25 +334,22 @@ def test_notification_jinja2(client, live_server): ) assert b"Watch added" in res.data - time.sleep(2) - set_more_modified_response() - client.get(url_for("form_watch_checknow"), follow_redirects=True) - time.sleep(3) - # URL check + time.sleep(2) + set_modified_response() + + client.get(url_for("form_watch_checknow"), follow_redirects=True) + time.sleep(2) + + with open("test-datastore/notification.txt", 'r') as f: + x=f.read() + j = json.loads(x) + assert j['url'].startswith('http://localhost') + assert j['secret'] == 444 + + # URL check, this will always be converted to lowercase assert os.path.isfile("test-datastore/notification-url.txt") with open("test-datastore/notification-url.txt", 'r') as f: - notification = f.read() - - assert 'XXX=http' in notification - os.unlink("test-datastore/notification-url.txt") - - # BODY and TITLE check - assert os.path.isfile("test-datastore/notification.txt") - with open("test-datastore/notification.txt", 'r') as f: - notification = json.loads(f.read()) - assert notification - - assert 'New ChangeDetection.io Notification - http://localhost' in notification['title'] - assert 'Got http://localhost' in notification['message'] - + notification_url = f.read() + assert 'xxx=http' in notification_url + os.unlink("test-datastore/notification-url.txt") \ No newline at end of file