kopia lustrzana https://github.com/dgtlmoon/changedetection.io
Notifications - tokens/jinja2 templating (#1184)
rodzic
a048e4a02d
commit
c12db2b725
|
@ -159,7 +159,7 @@ Just some examples
|
||||||
|
|
||||||
<img src="https://raw.githubusercontent.com/dgtlmoon/changedetection.io/master/docs/screenshot-notifications.png" style="max-width:100%;" alt="Self-hosted web page change monitoring notifications" title="Self-hosted web page change monitoring notifications" />
|
<img src="https://raw.githubusercontent.com/dgtlmoon/changedetection.io/master/docs/screenshot-notifications.png" style="max-width:100%;" alt="Self-hosted web page change monitoring notifications" title="Self-hosted web page change monitoring notifications" />
|
||||||
|
|
||||||
Now you can also customise your notification content!
|
Now you can also customise your notification content and use <a target="_new" href="https://jinja.palletsprojects.com/en/3.0.x/templates/">Jinja2 templating</a> for their title and body!
|
||||||
|
|
||||||
## JSON API Monitoring
|
## JSON API Monitoring
|
||||||
|
|
||||||
|
|
|
@ -193,7 +193,7 @@ class ValidateAppRiseServers(object):
|
||||||
message = field.gettext('\'%s\' is not a valid AppRise URL.' % (server_url))
|
message = field.gettext('\'%s\' is not a valid AppRise URL.' % (server_url))
|
||||||
raise ValidationError(message)
|
raise ValidationError(message)
|
||||||
|
|
||||||
class ValidateTokensList(object):
|
class ValidateJinja2Template(object):
|
||||||
"""
|
"""
|
||||||
Validates that a {token} is from a valid set
|
Validates that a {token} is from a valid set
|
||||||
"""
|
"""
|
||||||
|
@ -202,11 +202,24 @@ class ValidateTokensList(object):
|
||||||
|
|
||||||
def __call__(self, form, field):
|
def __call__(self, form, field):
|
||||||
from changedetectionio import notification
|
from changedetectionio import notification
|
||||||
regex = re.compile('{.*?}')
|
|
||||||
for p in re.findall(regex, field.data):
|
from jinja2 import Environment, BaseLoader, TemplateSyntaxError
|
||||||
if not p.strip('{}') in notification.valid_tokens:
|
from jinja2.meta import find_undeclared_variables
|
||||||
message = field.gettext('Token \'%s\' is not a valid token.')
|
|
||||||
raise ValidationError(message % (p))
|
|
||||||
|
try:
|
||||||
|
jinja2_env = Environment(loader=BaseLoader)
|
||||||
|
jinja2_env.globals.update(notification.valid_tokens)
|
||||||
|
rendered = jinja2_env.from_string(field.data).render()
|
||||||
|
except TemplateSyntaxError as e:
|
||||||
|
raise ValidationError(f"This is not a valid Jinja2 template: {e}") from e
|
||||||
|
|
||||||
|
ast = jinja2_env.parse(field.data)
|
||||||
|
undefined = ", ".join(find_undeclared_variables(ast))
|
||||||
|
if undefined:
|
||||||
|
raise ValidationError(
|
||||||
|
f"The following tokens used in the notification are not valid: {undefined}"
|
||||||
|
)
|
||||||
|
|
||||||
class validateURL(object):
|
class validateURL(object):
|
||||||
|
|
||||||
|
@ -225,6 +238,7 @@ class validateURL(object):
|
||||||
message = field.gettext('\'%s\' is not a valid URL.' % (field.data.strip()))
|
message = field.gettext('\'%s\' is not a valid URL.' % (field.data.strip()))
|
||||||
raise ValidationError(message)
|
raise ValidationError(message)
|
||||||
|
|
||||||
|
|
||||||
class ValidateListRegex(object):
|
class ValidateListRegex(object):
|
||||||
"""
|
"""
|
||||||
Validates that anything that looks like a regex passes as a regex
|
Validates that anything that looks like a regex passes as a regex
|
||||||
|
@ -333,11 +347,11 @@ class quickWatchForm(Form):
|
||||||
|
|
||||||
# Common to a single watch and the global settings
|
# Common to a single watch and the global settings
|
||||||
class commonSettingsForm(Form):
|
class commonSettingsForm(Form):
|
||||||
notification_urls = StringListField('Notification URL list', validators=[validators.Optional(), ValidateAppRiseServers()])
|
notification_urls = StringListField('Notification URL List', validators=[validators.Optional(), ValidateAppRiseServers()])
|
||||||
notification_title = StringField('Notification title', validators=[validators.Optional(), ValidateTokensList()])
|
notification_title = StringField('Notification Title', default='ChangeDetection.io Notification - {{ watch_url }}', validators=[validators.Optional(), ValidateJinja2Template()])
|
||||||
notification_body = TextAreaField('Notification body', validators=[validators.Optional(), ValidateTokensList()])
|
notification_body = TextAreaField('Notification Body', default='{{ watch_url }} had a change.', validators=[validators.Optional(), ValidateJinja2Template()])
|
||||||
notification_format = SelectField('Notification format', choices=valid_notification_formats.keys())
|
notification_format = SelectField('Notification format', choices=valid_notification_formats.keys())
|
||||||
fetch_backend = RadioField(u'Fetch method', choices=content_fetcher.available_fetchers(), validators=[ValidateContentFetcherIsReady()])
|
fetch_backend = RadioField(u'Fetch Method', choices=content_fetcher.available_fetchers(), validators=[ValidateContentFetcherIsReady()])
|
||||||
extract_title_as_title = BooleanField('Extract <title> from document and use as watch title', default=False)
|
extract_title_as_title = BooleanField('Extract <title> from document and use as watch title', default=False)
|
||||||
webdriver_delay = IntegerField('Wait seconds before extracting text', validators=[validators.Optional(), validators.NumberRange(min=1,
|
webdriver_delay = IntegerField('Wait seconds before extracting text', validators=[validators.Optional(), validators.NumberRange(min=1,
|
||||||
message="Should contain one or more seconds")])
|
message="Should contain one or more seconds")])
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import apprise
|
import apprise
|
||||||
|
from jinja2 import Environment, BaseLoader
|
||||||
from apprise import NotifyFormat
|
from apprise import NotifyFormat
|
||||||
|
import json
|
||||||
|
|
||||||
valid_tokens = {
|
valid_tokens = {
|
||||||
'base_url': '',
|
'base_url': '',
|
||||||
|
@ -16,8 +18,8 @@ valid_tokens = {
|
||||||
|
|
||||||
default_notification_format_for_watch = 'System default'
|
default_notification_format_for_watch = 'System default'
|
||||||
default_notification_format = 'Text'
|
default_notification_format = 'Text'
|
||||||
default_notification_body = '{watch_url} had a change.\n---\n{diff}\n---\n'
|
default_notification_body = '{{watch_url}} had a change.\n---\n{{diff}}\n---\n'
|
||||||
default_notification_title = 'ChangeDetection.io Notification - {watch_url}'
|
default_notification_title = 'ChangeDetection.io Notification - {{watch_url}}'
|
||||||
|
|
||||||
valid_notification_formats = {
|
valid_notification_formats = {
|
||||||
'Text': NotifyFormat.TEXT,
|
'Text': NotifyFormat.TEXT,
|
||||||
|
@ -27,25 +29,67 @@ valid_notification_formats = {
|
||||||
default_notification_format_for_watch: default_notification_format_for_watch
|
default_notification_format_for_watch: default_notification_format_for_watch
|
||||||
}
|
}
|
||||||
|
|
||||||
def process_notification(n_object, datastore):
|
# include the decorator
|
||||||
|
from apprise.decorators import notify
|
||||||
|
|
||||||
# Get the notification body from datastore
|
@notify(on="delete")
|
||||||
n_body = n_object.get('notification_body', default_notification_body)
|
@notify(on="deletes")
|
||||||
n_title = n_object.get('notification_title', default_notification_title)
|
@notify(on="get")
|
||||||
n_format = valid_notification_formats.get(
|
@notify(on="gets")
|
||||||
n_object['notification_format'],
|
@notify(on="post")
|
||||||
valid_notification_formats[default_notification_format],
|
@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
|
# Insert variables into the notification content
|
||||||
notification_parameters = create_notification_parameters(n_object, datastore)
|
notification_parameters = create_notification_parameters(n_object, datastore)
|
||||||
|
|
||||||
for n_k in notification_parameters:
|
# Get the notification body from datastore
|
||||||
token = '{' + n_k + '}'
|
jinja2_env = Environment(loader=BaseLoader)
|
||||||
val = notification_parameters[n_k]
|
n_body = jinja2_env.from_string(n_object.get('notification_body', default_notification_body)).render(**notification_parameters)
|
||||||
n_title = n_title.replace(token, val)
|
n_title = jinja2_env.from_string(n_object.get('notification_title', default_notification_title)).render(**notification_parameters)
|
||||||
n_body = n_body.replace(token, val)
|
n_format = valid_notification_formats.get(
|
||||||
|
n_object['notification_format'],
|
||||||
|
valid_notification_formats[default_notification_format],
|
||||||
|
)
|
||||||
|
|
||||||
# https://github.com/caronc/apprise/wiki/Development_LogCapture
|
# https://github.com/caronc/apprise/wiki/Development_LogCapture
|
||||||
# Anything higher than or equal to WARNING (which covers things like Connection errors)
|
# Anything higher than or equal to WARNING (which covers things like Connection errors)
|
||||||
# raise it as an exception
|
# raise it as an exception
|
||||||
|
@ -53,6 +97,7 @@ def process_notification(n_object, datastore):
|
||||||
sent_objs=[]
|
sent_objs=[]
|
||||||
from .apprise_asset import asset
|
from .apprise_asset import asset
|
||||||
for url in n_object['notification_urls']:
|
for url in n_object['notification_urls']:
|
||||||
|
url = jinja2_env.from_string(url).render(**notification_parameters)
|
||||||
apobj = apprise.Apprise(debug=True, asset=asset)
|
apobj = apprise.Apprise(debug=True, asset=asset)
|
||||||
url = url.strip()
|
url = url.strip()
|
||||||
if len(url):
|
if len(url):
|
||||||
|
@ -66,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
|
# 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 '&'
|
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'
|
url += k + 'avatar_url=https://raw.githubusercontent.com/dgtlmoon/changedetection.io/master/changedetectionio/static/images/avatar-256x256.png'
|
||||||
|
|
||||||
if url.startswith('tgram://'):
|
if url.startswith('tgram://'):
|
||||||
|
@ -144,7 +194,7 @@ def create_notification_parameters(n_object, datastore):
|
||||||
|
|
||||||
watch_url = n_object['watch_url']
|
watch_url = n_object['watch_url']
|
||||||
|
|
||||||
# Re #148 - Some people have just {base_url} in the body or title, but this may break some notification services
|
# Re #148 - Some people have just {{ base_url }} in the body or title, but this may break some notification services
|
||||||
# like 'Join', so it's always best to atleast set something obvious so that they are not broken.
|
# like 'Join', so it's always best to atleast set something obvious so that they are not broken.
|
||||||
if base_url == '':
|
if base_url == '':
|
||||||
base_url = "<base-url-env-var-not-set>"
|
base_url = "<base-url-env-var-not-set>"
|
||||||
|
|
|
@ -877,6 +877,9 @@ body.full-width {
|
||||||
.pure-form-message-inline {
|
.pure-form-message-inline {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
color: var(--color-text-input-description);
|
color: var(--color-text-input-description);
|
||||||
|
code {
|
||||||
|
font-size: .875em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -851,6 +851,8 @@ body.full-width .edit-form {
|
||||||
.edit-form .pure-form-message-inline {
|
.edit-form .pure-form-message-inline {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
color: var(--color-text-input-description); }
|
color: var(--color-text-input-description); }
|
||||||
|
.edit-form .pure-form-message-inline code {
|
||||||
|
font-size: .875em; }
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
padding-left: 1em;
|
padding-left: 1em;
|
||||||
|
|
|
@ -621,4 +621,44 @@ class ChangeDetectionStore:
|
||||||
watch['include_filters'] = [existing_filter]
|
watch['include_filters'] = [existing_filter]
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Convert old static notification tokens to jinja2 tokens
|
||||||
|
def update_9(self):
|
||||||
|
# Each watch
|
||||||
|
import re
|
||||||
|
# only { } not {{ or }}
|
||||||
|
r = r'(?<!{){(?!{)(\w+)(?<!})}(?!})'
|
||||||
|
for uuid, watch in self.data['watching'].items():
|
||||||
|
try:
|
||||||
|
n_body = watch.get('notification_body', '')
|
||||||
|
if n_body:
|
||||||
|
watch['notification_body'] = re.sub(r, r'{{\1}}', n_body)
|
||||||
|
|
||||||
|
n_title = watch.get('notification_title')
|
||||||
|
if n_title:
|
||||||
|
self.data['settings']['application']['notification_title'] = re.sub(r, r'{{\1}}', n_title)
|
||||||
|
|
||||||
|
n_urls = watch.get('notification_urls')
|
||||||
|
if n_urls:
|
||||||
|
for i, url in enumerate(n_urls):
|
||||||
|
watch['notification_urls'][i] = re.sub(r, r'{{\1}}', url)
|
||||||
|
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# System wide
|
||||||
|
n_body = self.data['settings']['application'].get('notification_body')
|
||||||
|
if n_body:
|
||||||
|
self.data['settings']['application']['notification_body'] = re.sub(r, r'{{\1}}', n_body)
|
||||||
|
|
||||||
|
n_title = self.data['settings']['application'].get('notification_title')
|
||||||
|
if n_body:
|
||||||
|
self.data['settings']['application']['notification_title'] = re.sub(r, r'{{\1}}', n_title)
|
||||||
|
|
||||||
|
n_urls = self.data['settings']['application'].get('notification_urls')
|
||||||
|
if n_urls:
|
||||||
|
for i, url in enumerate(n_urls):
|
||||||
|
self.data['settings']['application']['notification_urls'][i] = re.sub(r, r'{{\1}}', url)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
<li><code>discord://</code> only supports a maximum <strong>2,000 characters</strong> of notification text, including the title.</li>
|
<li><code>discord://</code> only supports a maximum <strong>2,000 characters</strong> of notification text, including the title.</li>
|
||||||
<li><code>tgram://</code> bots cant send messages to other bots, so you should specify chat ID of non-bot user.</li>
|
<li><code>tgram://</code> bots cant send messages to other bots, so you should specify chat ID of non-bot user.</li>
|
||||||
<li><code>tgram://</code> only supports very limited HTML and can fail when extra tags are sent, <a href="https://core.telegram.org/bots/api#html-style">read more here</a> (or use plaintext/markdown format)</li>
|
<li><code>tgram://</code> only supports very limited HTML and can fail when extra tags are sent, <a href="https://core.telegram.org/bots/api#html-style">read more here</a> (or use plaintext/markdown format)</li>
|
||||||
|
<li><code>gets://</code>, <code>posts://</code>, <code>puts://</code>, <code>deletes://</code> for direct API calls (or omit the "<code>s</code>" for non-SSL ie <code>get://</code>)</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="notifications-wrapper">
|
<div class="notifications-wrapper">
|
||||||
|
@ -41,8 +42,9 @@
|
||||||
<span class="pure-form-message-inline">Format for all notifications</span>
|
<span class="pure-form-message-inline">Format for all notifications</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-controls">
|
<div class="pure-controls">
|
||||||
<span class="pure-form-message-inline">
|
<p class="pure-form-message-inline">
|
||||||
These tokens can be used in the notification body and title to customise the notification text.
|
You can use <a target="_new" href="https://jinja.palletsprojects.com/en/3.0.x/templates/">Jinja2</a> templating in the notification title, body and URL.
|
||||||
|
</p>
|
||||||
|
|
||||||
<table class="pure-table" id="token-table">
|
<table class="pure-table" id="token-table">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -53,52 +55,49 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>{base_url}</code></td>
|
<td><code>{{ '{{ base_url }}' }}</code></td>
|
||||||
<td>The URL of the changedetection.io instance you are running.</td>
|
<td>The URL of the changedetection.io instance you are running.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>{watch_url}</code></td>
|
<td><code>{{ '{{ watch_url }}' }}</code></td>
|
||||||
<td>The URL being watched.</td>
|
<td>The URL being watched.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>{watch_uuid}</code></td>
|
<td><code>{{ '{{ watch_uuid }}' }}</code></td>
|
||||||
<td>The UUID of the watch.</td>
|
<td>The UUID of the watch.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>{watch_title}</code></td>
|
<td><code>{{ '{{ watch_title }}' }}</code></td>
|
||||||
<td>The title of the watch.</td>
|
<td>The title of the watch.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>{watch_tag}</code></td>
|
<td><code>{{ '{{ watch_tag }}' }}</code></td>
|
||||||
<td>The tag of the watch.</td>
|
<td>The tag of the watch.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>{preview_url}</code></td>
|
<td><code>{{ '{{ preview_url }}' }}</code></td>
|
||||||
<td>The URL of the preview page generated by changedetection.io.</td>
|
<td>The URL of the preview page generated by changedetection.io.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>{diff}</code></td>
|
<td><code>{{ '{{ diff_url }}' }}</code></td>
|
||||||
<td>The diff output - differences only</td>
|
<td>The diff output - differences only</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>{diff_full}</code></td>
|
<td><code>{{ '{{ diff_full }}' }}</code></td>
|
||||||
<td>The diff output - full difference output</td>
|
<td>The diff output - full difference output</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>{diff_url}</code></td>
|
<td><code>{{ '{{ current_snapshot }}' }}</code></td>
|
||||||
<td>The URL of the diff page generated by changedetection.io.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>{current_snapshot}</code></td>
|
|
||||||
<td>The current snapshot value, useful when combined with JSON or CSS filters
|
<td>The current snapshot value, useful when combined with JSON or CSS filters
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<br/>
|
<div class="pure-form-message-inline">
|
||||||
URLs generated by changedetection.io (such as <code>{diff_url}</code>) require the <code>BASE_URL</code> environment variable set.<br/>
|
<br>
|
||||||
Your <code>BASE_URL</code> var is currently "{{settings_application['current_base_url']}}"
|
URLs generated by changedetection.io (such as <code>{{ '{{ diff_url }}' }}</code>) require the <code>BASE_URL</code> environment variable set.<br/>
|
||||||
</span>
|
Your <code>BASE_URL</code> var is currently "{{settings_application['current_base_url']}}"
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
{{ render_field(form.application.form.base_url, placeholder="http://yoursite.com:5000/",
|
{{ render_field(form.application.form.base_url, placeholder="http://yoursite.com:5000/",
|
||||||
class="m-d") }}
|
class="m-d") }}
|
||||||
<span class="pure-form-message-inline">
|
<span class="pure-form-message-inline">
|
||||||
Base URL used for the <code>{base_url}</code> token in notifications and RSS links.<br/>Default value is the ENV var 'BASE_URL' (Currently "{{settings_application['current_base_url']}}"),
|
Base URL used for the <code>{{ '{{ base_url }}' }}</code> token in notifications and RSS links.<br/>Default value is the ENV var 'BASE_URL' (Currently "{{settings_application['current_base_url']}}"),
|
||||||
<a href="https://github.com/dgtlmoon/changedetection.io/wiki/Configurable-BASE_URL-setting">read more here</a>.
|
<a href="https://github.com/dgtlmoon/changedetection.io/wiki/Configurable-BASE_URL-setting">read more here</a>.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -73,17 +73,17 @@ def test_filter_doesnt_exist_then_exists_should_get_notification(client, live_se
|
||||||
|
|
||||||
# Just a regular notification setting, this will be used by the special 'filter not found' notification
|
# Just a regular notification setting, this will be used by the special 'filter not found' notification
|
||||||
notification_form_data = {"notification_urls": notification_url,
|
notification_form_data = {"notification_urls": notification_url,
|
||||||
"notification_title": "New ChangeDetection.io Notification - {watch_url}",
|
"notification_title": "New ChangeDetection.io Notification - {{watch_url}}",
|
||||||
"notification_body": "BASE URL: {base_url}\n"
|
"notification_body": "BASE URL: {{base_url}}\n"
|
||||||
"Watch URL: {watch_url}\n"
|
"Watch URL: {{watch_url}}\n"
|
||||||
"Watch UUID: {watch_uuid}\n"
|
"Watch UUID: {{watch_uuid}}\n"
|
||||||
"Watch title: {watch_title}\n"
|
"Watch title: {{watch_title}}\n"
|
||||||
"Watch tag: {watch_tag}\n"
|
"Watch tag: {{watch_tag}}\n"
|
||||||
"Preview: {preview_url}\n"
|
"Preview: {{preview_url}}\n"
|
||||||
"Diff URL: {diff_url}\n"
|
"Diff URL: {{diff_url}}\n"
|
||||||
"Snapshot: {current_snapshot}\n"
|
"Snapshot: {{current_snapshot}}\n"
|
||||||
"Diff: {diff}\n"
|
"Diff: {{diff}}\n"
|
||||||
"Diff Full: {diff_full}\n"
|
"Diff Full: {{diff_full}}\n"
|
||||||
":-)",
|
":-)",
|
||||||
"notification_format": "Text"}
|
"notification_format": "Text"}
|
||||||
|
|
||||||
|
|
|
@ -56,17 +56,17 @@ def run_filter_test(client, content_filter):
|
||||||
|
|
||||||
# Just a regular notification setting, this will be used by the special 'filter not found' notification
|
# Just a regular notification setting, this will be used by the special 'filter not found' notification
|
||||||
notification_form_data = {"notification_urls": notification_url,
|
notification_form_data = {"notification_urls": notification_url,
|
||||||
"notification_title": "New ChangeDetection.io Notification - {watch_url}",
|
"notification_title": "New ChangeDetection.io Notification - {{watch_url}}",
|
||||||
"notification_body": "BASE URL: {base_url}\n"
|
"notification_body": "BASE URL: {{base_url}}\n"
|
||||||
"Watch URL: {watch_url}\n"
|
"Watch URL: {{watch_url}}\n"
|
||||||
"Watch UUID: {watch_uuid}\n"
|
"Watch UUID: {{watch_uuid}}\n"
|
||||||
"Watch title: {watch_title}\n"
|
"Watch title: {{watch_title}}\n"
|
||||||
"Watch tag: {watch_tag}\n"
|
"Watch tag: {{watch_tag}}\n"
|
||||||
"Preview: {preview_url}\n"
|
"Preview: {{preview_url}}\n"
|
||||||
"Diff URL: {diff_url}\n"
|
"Diff URL: {{diff_url}}\n"
|
||||||
"Snapshot: {current_snapshot}\n"
|
"Snapshot: {{current_snapshot}}\n"
|
||||||
"Diff: {diff}\n"
|
"Diff: {{diff}}\n"
|
||||||
"Diff Full: {diff_full}\n"
|
"Diff Full: {{diff_full}}\n"
|
||||||
":-)",
|
":-)",
|
||||||
"notification_format": "Text"}
|
"notification_format": "Text"}
|
||||||
|
|
||||||
|
@ -84,6 +84,7 @@ def run_filter_test(client, content_filter):
|
||||||
data=notification_form_data,
|
data=notification_form_data,
|
||||||
follow_redirects=True
|
follow_redirects=True
|
||||||
)
|
)
|
||||||
|
|
||||||
assert b"Updated watch." in res.data
|
assert b"Updated watch." in res.data
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
|
|
||||||
|
|
|
@ -90,17 +90,17 @@ def test_check_notification(client, live_server):
|
||||||
print (">>>> Notification URL: "+notification_url)
|
print (">>>> Notification URL: "+notification_url)
|
||||||
|
|
||||||
notification_form_data = {"notification_urls": notification_url,
|
notification_form_data = {"notification_urls": notification_url,
|
||||||
"notification_title": "New ChangeDetection.io Notification - {watch_url}",
|
"notification_title": "New ChangeDetection.io Notification - {{watch_url}}",
|
||||||
"notification_body": "BASE URL: {base_url}\n"
|
"notification_body": "BASE URL: {{base_url}}\n"
|
||||||
"Watch URL: {watch_url}\n"
|
"Watch URL: {{watch_url}}\n"
|
||||||
"Watch UUID: {watch_uuid}\n"
|
"Watch UUID: {{watch_uuid}}\n"
|
||||||
"Watch title: {watch_title}\n"
|
"Watch title: {{watch_title}}\n"
|
||||||
"Watch tag: {watch_tag}\n"
|
"Watch tag: {{watch_tag}}\n"
|
||||||
"Preview: {preview_url}\n"
|
"Preview: {{preview_url}}\n"
|
||||||
"Diff URL: {diff_url}\n"
|
"Diff URL: {{diff_url}}\n"
|
||||||
"Snapshot: {current_snapshot}\n"
|
"Snapshot: {{current_snapshot}}\n"
|
||||||
"Diff: {diff}\n"
|
"Diff: {{diff}}\n"
|
||||||
"Diff Full: {diff_full}\n"
|
"Diff Full: {{diff_full}}\n"
|
||||||
":-)",
|
":-)",
|
||||||
"notification_screenshot": True,
|
"notification_screenshot": True,
|
||||||
"notification_format": "Text"}
|
"notification_format": "Text"}
|
||||||
|
@ -179,7 +179,6 @@ def test_check_notification(client, live_server):
|
||||||
logging.debug(">>> Skipping BASE_URL check")
|
logging.debug(">>> Skipping BASE_URL check")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# This should insert the {current_snapshot}
|
# This should insert the {current_snapshot}
|
||||||
set_more_modified_response()
|
set_more_modified_response()
|
||||||
client.get(url_for("form_watch_checknow"), follow_redirects=True)
|
client.get(url_for("form_watch_checknow"), follow_redirects=True)
|
||||||
|
@ -237,10 +236,9 @@ def test_check_notification(client, live_server):
|
||||||
follow_redirects=True
|
follow_redirects=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_notification_validation(client, live_server):
|
def test_notification_validation(client, live_server):
|
||||||
#live_server_setup(live_server)
|
time.sleep(1)
|
||||||
time.sleep(3)
|
|
||||||
# re #242 - when you edited an existing new entry, it would not correctly show the notification settings
|
# re #242 - when you edited an existing new entry, it would not correctly show the notification settings
|
||||||
# Add our URL to the import page
|
# Add our URL to the import page
|
||||||
test_url = url_for('test_endpoint', _external=True)
|
test_url = url_for('test_endpoint', _external=True)
|
||||||
|
@ -269,19 +267,33 @@ def test_notification_validation(client, live_server):
|
||||||
# assert b"Notification Body and Title is required when a Notification URL is used" in res.data
|
# assert b"Notification Body and Title is required when a Notification URL is used" in res.data
|
||||||
|
|
||||||
# Now adding a wrong token should give us an error
|
# Now adding a wrong token should give us an error
|
||||||
|
# Disabled for now
|
||||||
|
# res = client.post(
|
||||||
|
# url_for("settings_page"),
|
||||||
|
# data={"application-notification_title": "New ChangeDetection.io Notification - {{watch_url}}",
|
||||||
|
# "application-notification_body": "Rubbish: {{rubbish}}\n",
|
||||||
|
# "application-notification_format": "Text",
|
||||||
|
# "application-notification_urls": "json://localhost/foobar",
|
||||||
|
# "requests-time_between_check-minutes": 180,
|
||||||
|
# "fetch_backend": "html_requests"
|
||||||
|
# },
|
||||||
|
# follow_redirects=True
|
||||||
|
# )
|
||||||
|
# assert bytes("Token 'rubbish' is not a valid token or is unknown".encode('utf-8')) in res.data
|
||||||
|
|
||||||
|
# And trying to define an invalid Jinja2 template should also throw an error
|
||||||
res = client.post(
|
res = client.post(
|
||||||
url_for("settings_page"),
|
url_for("settings_page"),
|
||||||
data={"application-notification_title": "New ChangeDetection.io Notification - {watch_url}",
|
data={"application-notification_title": "New ChangeDetection.io Notification - {{ watch_url }}",
|
||||||
"application-notification_body": "Rubbish: {rubbish}\n",
|
"application-notification_body": "Rubbish: {{ rubbish }\n",
|
||||||
"application-notification_format": "Text",
|
"application-notification_urls": "json://foobar.com",
|
||||||
"application-notification_urls": "json://localhost/foobar",
|
"application-minutes_between_check": 180,
|
||||||
"requests-time_between_check-minutes": 180,
|
"application-fetch_backend": "html_requests"
|
||||||
"fetch_backend": "html_requests"
|
|
||||||
},
|
},
|
||||||
follow_redirects=True
|
follow_redirects=True
|
||||||
)
|
)
|
||||||
|
assert bytes("This is not a valid Jinja2 template".encode('utf-8')) in res.data
|
||||||
|
|
||||||
assert bytes("is not a valid token".encode('utf-8')) in res.data
|
|
||||||
|
|
||||||
# cleanup for the next
|
# cleanup for the next
|
||||||
client.get(
|
client.get(
|
||||||
|
@ -289,4 +301,55 @@ def test_notification_validation(client, live_server):
|
||||||
follow_redirects=True
|
follow_redirects=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_notification_jinja2(client, live_server):
|
||||||
|
#live_server_setup(live_server)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# 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": '{ "url" : "{{ watch_url }}", "secret": 444 }',
|
||||||
|
# https://github.com/caronc/apprise/wiki/Notify_Custom_JSON#get-parameter-manipulation
|
||||||
|
"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"),
|
||||||
|
data={"url": test_url, "tag": 'nice one'},
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert b"Watch added" in res.data
|
||||||
|
|
||||||
|
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_url = f.read()
|
||||||
|
assert 'xxx=http' in notification_url
|
||||||
|
os.unlink("test-datastore/notification-url.txt")
|
|
@ -149,6 +149,9 @@ def live_server_setup(live_server):
|
||||||
if data != None:
|
if data != None:
|
||||||
f.write(data)
|
f.write(data)
|
||||||
|
|
||||||
|
with open("test-datastore/notification-url.txt", "w") as f:
|
||||||
|
f.write(request.url)
|
||||||
|
|
||||||
print("\n>> Test notification endpoint was hit.\n", data)
|
print("\n>> Test notification endpoint was hit.\n", data)
|
||||||
return "Text was set"
|
return "Text was set"
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue