mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2025-12-12 02:55:43 +00:00
* Re #185 - [feature] Custom text templates for the notification per monitored entry as override. Bonus points: Adding validation for apprise URLs
This commit is contained in:
@@ -403,14 +403,15 @@ def changedetection_app(config=None, datastore_o=None):
|
|||||||
if form.fetch_backend.data == datastore.data['settings']['application']['fetch_backend']:
|
if form.fetch_backend.data == datastore.data['settings']['application']['fetch_backend']:
|
||||||
form.fetch_backend.data = None
|
form.fetch_backend.data = None
|
||||||
|
|
||||||
|
|
||||||
update_obj = {'url': form.url.data.strip(),
|
update_obj = {'url': form.url.data.strip(),
|
||||||
'minutes_between_check': form.minutes_between_check.data,
|
'minutes_between_check': form.minutes_between_check.data,
|
||||||
'tag': form.tag.data.strip(),
|
'tag': form.tag.data.strip(),
|
||||||
'title': form.title.data.strip(),
|
'title': form.title.data.strip(),
|
||||||
'headers': form.headers.data,
|
'headers': form.headers.data,
|
||||||
'fetch_backend': form.fetch_backend.data,
|
'fetch_backend': form.fetch_backend.data,
|
||||||
'trigger_text': form.trigger_text.data
|
'trigger_text': form.trigger_text.data,
|
||||||
|
'notification_title': form.notification_title.data,
|
||||||
|
'notification_body': form.notification_body.data
|
||||||
}
|
}
|
||||||
|
|
||||||
# Notification URLs
|
# Notification URLs
|
||||||
@@ -443,8 +444,10 @@ def changedetection_app(config=None, datastore_o=None):
|
|||||||
|
|
||||||
if form.trigger_check.data:
|
if form.trigger_check.data:
|
||||||
n_object = {'watch_url': form.url.data.strip(),
|
n_object = {'watch_url': form.url.data.strip(),
|
||||||
'notification_urls': form.notification_urls.data
|
'notification_urls': form.notification_urls.data,
|
||||||
}
|
'notification_title': form.notification_title.data,
|
||||||
|
'notification_body' : form.notification_body.data
|
||||||
|
}
|
||||||
notification_q.put(n_object)
|
notification_q.put(n_object)
|
||||||
|
|
||||||
flash('Notifications queued.')
|
flash('Notifications queued.')
|
||||||
@@ -513,7 +516,10 @@ def changedetection_app(config=None, datastore_o=None):
|
|||||||
|
|
||||||
if form.trigger_check.data and len(form.notification_urls.data):
|
if form.trigger_check.data and len(form.notification_urls.data):
|
||||||
n_object = {'watch_url': "Test from changedetection.io!",
|
n_object = {'watch_url': "Test from changedetection.io!",
|
||||||
'notification_urls': form.notification_urls.data}
|
'notification_urls': form.notification_urls.data,
|
||||||
|
'notification_title': form.notification_title.data,
|
||||||
|
'notification_body': form.notification_body.data
|
||||||
|
}
|
||||||
notification_q.put(n_object)
|
notification_q.put(n_object)
|
||||||
flash('Notifications queued.')
|
flash('Notifications queued.')
|
||||||
|
|
||||||
|
|||||||
@@ -116,6 +116,23 @@ class ValidateContentFetcherIsReady(object):
|
|||||||
raise ValidationError(message % (field.data, e))
|
raise ValidationError(message % (field.data, e))
|
||||||
|
|
||||||
|
|
||||||
|
class ValidateAppRiseServers(object):
|
||||||
|
"""
|
||||||
|
Validates that each URL given is compatible with AppRise
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, message=None):
|
||||||
|
self.message = message
|
||||||
|
|
||||||
|
def __call__(self, form, field):
|
||||||
|
import apprise
|
||||||
|
apobj = apprise.Apprise()
|
||||||
|
|
||||||
|
for server_url in field.data:
|
||||||
|
if not apobj.add(server_url):
|
||||||
|
message = field.gettext('\'%s\' is not a valid AppRise URL.' % (server_url))
|
||||||
|
raise ValidationError(message)
|
||||||
|
|
||||||
class ValidateTokensList(object):
|
class ValidateTokensList(object):
|
||||||
"""
|
"""
|
||||||
Validates that a {token} is from a valid set
|
Validates that a {token} is from a valid set
|
||||||
@@ -172,42 +189,40 @@ class ValidateCSSJSONInput(object):
|
|||||||
message = field.gettext('\'%s\' is not a valid JSONPath expression. (%s)')
|
message = field.gettext('\'%s\' is not a valid JSONPath expression. (%s)')
|
||||||
raise ValidationError(message % (input, str(e)))
|
raise ValidationError(message % (input, str(e)))
|
||||||
|
|
||||||
|
|
||||||
class quickWatchForm(Form):
|
class quickWatchForm(Form):
|
||||||
# https://wtforms.readthedocs.io/en/2.3.x/fields/#module-wtforms.fields.html5
|
# https://wtforms.readthedocs.io/en/2.3.x/fields/#module-wtforms.fields.html5
|
||||||
# `require_tld` = False is needed even for the test harness "http://localhost:5005.." to run
|
# `require_tld` = False is needed even for the test harness "http://localhost:5005.." to run
|
||||||
|
|
||||||
url = html5.URLField('URL', [validators.URL(require_tld=False)])
|
url = html5.URLField('URL', [validators.URL(require_tld=False)])
|
||||||
tag = StringField('Group tag', [validators.Optional(), validators.Length(max=35)])
|
tag = StringField('Group tag', [validators.Optional(), validators.Length(max=35)])
|
||||||
|
|
||||||
class watchForm(quickWatchForm):
|
class commonSettingsForm(Form):
|
||||||
|
|
||||||
|
notification_urls = StringListField('Notification URL List', validators=[validators.Optional(), ValidateAppRiseServers()])
|
||||||
|
notification_title = StringField('Notification Title', default='ChangeDetection.io Notification - {watch_url}', validators=[validators.Optional(), ValidateTokensList()])
|
||||||
|
notification_body = TextAreaField('Notification Body', default='{watch_url} had a change.', validators=[validators.Optional(), ValidateTokensList()])
|
||||||
|
trigger_check = BooleanField('Send test notification on save')
|
||||||
|
fetch_backend = RadioField(u'Fetch Method', choices=content_fetcher.available_fetchers(), validators=[ValidateContentFetcherIsReady()])
|
||||||
|
|
||||||
|
class watchForm(commonSettingsForm):
|
||||||
|
|
||||||
|
url = html5.URLField('URL', [validators.URL(require_tld=False)])
|
||||||
|
tag = StringField('Group tag', [validators.Optional(), validators.Length(max=35)])
|
||||||
|
|
||||||
minutes_between_check = html5.IntegerField('Maximum time in minutes until recheck',
|
minutes_between_check = html5.IntegerField('Maximum time in minutes until recheck',
|
||||||
[validators.Optional(), validators.NumberRange(min=1)])
|
[validators.Optional(), validators.NumberRange(min=1)])
|
||||||
css_filter = StringField('CSS/JSON Filter', [ValidateCSSJSONInput()])
|
css_filter = StringField('CSS/JSON Filter', [ValidateCSSJSONInput()])
|
||||||
title = StringField('Title')
|
title = StringField('Title')
|
||||||
|
|
||||||
fetch_backend = RadioField(u'Fetch Method', choices=content_fetcher.available_fetchers(), validators=[ValidateContentFetcherIsReady()])
|
|
||||||
|
|
||||||
ignore_text = StringListField('Ignore Text', [ValidateListRegex()])
|
ignore_text = StringListField('Ignore Text', [ValidateListRegex()])
|
||||||
notification_urls = StringListField('Notification URL List')
|
|
||||||
headers = StringDictKeyValue('Request Headers')
|
headers = StringDictKeyValue('Request Headers')
|
||||||
trigger_check = BooleanField('Send test notification on save')
|
|
||||||
trigger_text = StringListField('Trigger/wait for text', [validators.Optional(), ValidateListRegex()])
|
trigger_text = StringListField('Trigger/wait for text', [validators.Optional(), ValidateListRegex()])
|
||||||
|
|
||||||
|
|
||||||
class globalSettingsForm(Form):
|
class globalSettingsForm(commonSettingsForm):
|
||||||
|
|
||||||
password = SaltyPasswordField()
|
password = SaltyPasswordField()
|
||||||
|
|
||||||
minutes_between_check = html5.IntegerField('Maximum time in minutes until recheck',
|
minutes_between_check = html5.IntegerField('Maximum time in minutes until recheck',
|
||||||
[validators.NumberRange(min=1)])
|
[validators.NumberRange(min=1)])
|
||||||
|
|
||||||
notification_urls = StringListField('Notification URL List')
|
|
||||||
|
|
||||||
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')
|
extract_title_as_title = BooleanField('Extract <title> from document and use as watch title')
|
||||||
trigger_check = BooleanField('Send test notification on save')
|
|
||||||
|
|
||||||
notification_title = StringField('Notification Title', validators=[validators.Optional(), ValidateTokensList()])
|
|
||||||
notification_body = TextAreaField('Notification Body', validators=[validators.Optional(), ValidateTokensList()])
|
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ def process_notification(n_object, datastore):
|
|||||||
apobj = apprise.Apprise(debug=True)
|
apobj = apprise.Apprise(debug=True)
|
||||||
|
|
||||||
for url in n_object['notification_urls']:
|
for url in n_object['notification_urls']:
|
||||||
print (">> Process Notification: AppRise notifying {}".format(url.strip()))
|
url = url.strip()
|
||||||
apobj.add(url.strip())
|
print (">> Process Notification: AppRise notifying {}".format(url))
|
||||||
|
apobj.add(url)
|
||||||
|
|
||||||
# Get the notification body from datastore
|
# Get the notification body from datastore
|
||||||
n_body = datastore.data['settings']['application']['notification_body']
|
n_body = n_object['notification_body']
|
||||||
# Get the notification title from the datastore
|
n_title = n_object['notification_title']
|
||||||
n_title = datastore.data['settings']['application']['notification_title']
|
|
||||||
|
|
||||||
# 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)
|
||||||
@@ -37,13 +37,11 @@ def process_notification(n_object, datastore):
|
|||||||
n_title = n_title.replace(token, val)
|
n_title = n_title.replace(token, val)
|
||||||
n_body = n_body.replace(token, val)
|
n_body = n_body.replace(token, val)
|
||||||
|
|
||||||
|
|
||||||
apobj.notify(
|
apobj.notify(
|
||||||
body=n_body,
|
body=n_body,
|
||||||
title=n_title
|
title=n_title
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Notification title + body content parameters get created here.
|
# Notification title + body content parameters get created here.
|
||||||
def create_notification_parameters(n_object, datastore):
|
def create_notification_parameters(n_object, datastore):
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ class ChangeDetectionStore:
|
|||||||
'fetch_backend': 'html_requests',
|
'fetch_backend': 'html_requests',
|
||||||
'notification_urls': [], # Apprise URL list
|
'notification_urls': [], # Apprise URL list
|
||||||
# Custom notification content
|
# Custom notification content
|
||||||
'notification_title': 'ChangeDetection.io Notification - {watch_url}',
|
'notification_title': None,
|
||||||
'notification_body': '{watch_url} had a change.'
|
'notification_body': None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,10 @@ class ChangeDetectionStore:
|
|||||||
'headers': {}, # Extra headers to send
|
'headers': {}, # Extra headers to send
|
||||||
'history': {}, # Dict of timestamp and output stripped filename
|
'history': {}, # Dict of timestamp and output stripped filename
|
||||||
'ignore_text': [], # List of text to ignore when calculating the comparison checksum
|
'ignore_text': [], # List of text to ignore when calculating the comparison checksum
|
||||||
|
# Custom notification content
|
||||||
'notification_urls': [], # List of URLs to add to the notification Queue (Usually AppRise)
|
'notification_urls': [], # List of URLs to add to the notification Queue (Usually AppRise)
|
||||||
|
'notification_title': None,
|
||||||
|
'notification_body': None,
|
||||||
'css_filter': "",
|
'css_filter': "",
|
||||||
'trigger_text': [], # List of text or regex to wait for until a change is detected
|
'trigger_text': [], # List of text or regex to wait for until a change is detected
|
||||||
'fetch_backend': None,
|
'fetch_backend': None,
|
||||||
|
|||||||
90
changedetectionio/templates/_common_fields.jinja
Normal file
90
changedetectionio/templates/_common_fields.jinja
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
|
||||||
|
{% from '_helpers.jinja' import render_field %}
|
||||||
|
|
||||||
|
{% macro render_notifications_field(form) %}
|
||||||
|
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<div class="field-group">
|
||||||
|
<div class="pure-control-group">
|
||||||
|
{{ render_field(form.notification_urls, rows=5, placeholder="Examples:
|
||||||
|
Gitter - gitter://token/room
|
||||||
|
Office365 - o365://TenantID:AccountEmail/ClientID/ClientSecret/TargetEmail
|
||||||
|
AWS SNS - sns://AccessKeyID/AccessSecretKey/RegionName/+PhoneNo
|
||||||
|
SMTPS - mailtos://user:pass@mail.domain.com?to=receivingAddress@example.com")
|
||||||
|
}}
|
||||||
|
<div class="pure-form-message-inline">Use <a target=_new
|
||||||
|
href="https://github.com/caronc/apprise">AppRise
|
||||||
|
URLs</a> for notification to just about any service!
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="notification-customisation">
|
||||||
|
<div class="pure-control-group">
|
||||||
|
{{ render_field(form.notification_title, class="m-d") }}
|
||||||
|
<span class="pure-form-message-inline">Title for all notifications</span>
|
||||||
|
</div>
|
||||||
|
<div class="pure-control-group">
|
||||||
|
{{ render_field(form.notification_body , rows=5) }}
|
||||||
|
<span class="pure-form-message-inline">Body for all notifications</span>
|
||||||
|
</div>
|
||||||
|
<div class="pure-controls">
|
||||||
|
<span class="pure-form-message-inline">
|
||||||
|
These tokens can be used in the notification body and title to
|
||||||
|
customise the notification text.
|
||||||
|
</span>
|
||||||
|
<table class="pure-table" id="token-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Token</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>{base_url}</code></td>
|
||||||
|
<td>The URL of the changedetection.io instance you are running.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>{watch_url}</code></td>
|
||||||
|
<td>The URL being watched.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>{watch_uuid}</code></td>
|
||||||
|
<td>The UUID of the watch.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>{watch_title}</code></td>
|
||||||
|
<td>The title of the watch.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>{watch_tag}</code></td>
|
||||||
|
<td>The tag of the watch.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>{preview_url}</code></td>
|
||||||
|
<td>The URL of the preview page generated by changedetection.io.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>{diff_url}</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>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<span 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/>
|
||||||
|
Your <code>BASE_URL</code> var is currently "{{base_url}}"
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pure-control-group">
|
||||||
|
{{ render_field(form.trigger_check) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
{% endmacro %}
|
||||||
@@ -22,4 +22,6 @@
|
|||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% from '_helpers.jinja' import render_field %}
|
{% from '_helpers.jinja' import render_field %}
|
||||||
|
{% from '_common_fields.jinja' import render_notifications_field %}
|
||||||
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='tabs.js')}}" defer></script>
|
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='tabs.js')}}" defer></script>
|
||||||
|
|
||||||
<div class="edit-form monospaced-textarea">
|
<div class="edit-form monospaced-textarea">
|
||||||
@@ -57,25 +58,12 @@ User-Agent: wonderbra 1.0") }}
|
|||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane-inner" id="notifications">
|
|
||||||
<fieldset>
|
|
||||||
<div class="pure-control-group">
|
|
||||||
{{ render_field(form.notification_urls, rows=5, placeholder="Examples:
|
|
||||||
Gitter - gitter://token/room
|
|
||||||
Office365 - o365://TenantID:AccountEmail/ClientID/ClientSecret/TargetEmail
|
|
||||||
AWS SNS - sns://AccessKeyID/AccessSecretKey/RegionName/+PhoneNo
|
|
||||||
SMTPS - mailtos://user:pass@mail.domain.com?to=receivingAddress@example.com
|
|
||||||
") }}
|
|
||||||
<span class="pure-form-message-inline">Use <a target=_new
|
|
||||||
href="https://github.com/caronc/apprise">AppRise URLs</a> for notification to just about any service!</span>
|
|
||||||
<span class="pure-form-message-inline">Note: This overrides any global settings notification URLs</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="pure-controls">
|
<div class="tab-pane-inner" id="notifications">
|
||||||
{{ render_field(form.trigger_check, rows=5) }}
|
<strong>Note: <i>These settings override the global settings.</i></strong>
|
||||||
</div>
|
{{ render_notifications_field(form) }}
|
||||||
</fieldset>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane-inner" id="filters">
|
<div class="tab-pane-inner" id="filters">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% from '_helpers.jinja' import render_field %}
|
{% from '_helpers.jinja' import render_field %}
|
||||||
|
{% from '_common_fields.jinja' import render_notifications_field %}
|
||||||
|
|
||||||
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='settings.js')}}" defer></script>
|
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='settings.js')}}" defer></script>
|
||||||
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='tabs.js')}}" defer></script>
|
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='tabs.js')}}" defer></script>
|
||||||
@@ -37,91 +38,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane-inner" id="notifications">
|
|
||||||
<fieldset>
|
|
||||||
<div class="field-group">
|
|
||||||
<div class="pure-control-group">
|
|
||||||
{{ render_field(form.notification_urls, rows=5, placeholder="Examples:
|
|
||||||
Gitter - gitter://token/room
|
|
||||||
Office365 - o365://TenantID:AccountEmail/ClientID/ClientSecret/TargetEmail
|
|
||||||
AWS SNS - sns://AccessKeyID/AccessSecretKey/RegionName/+PhoneNo
|
|
||||||
SMTPS - mailtos://user:pass@mail.domain.com?to=receivingAddress@example.com")
|
|
||||||
}}
|
|
||||||
<div class="pure-form-message-inline">Use <a target=_new
|
|
||||||
href="https://github.com/caronc/apprise">AppRise
|
|
||||||
URLs</a> for notification to just about any service!
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="notification-customisation">
|
|
||||||
<div class="pure-control-group">
|
|
||||||
{{ render_field(form.notification_title, class="m-d") }}
|
|
||||||
<span class="pure-form-message-inline">Title for all notifications</span>
|
|
||||||
</div>
|
|
||||||
<div class="pure-control-group">
|
|
||||||
{{ render_field(form.notification_body , rows=5) }}
|
|
||||||
<span class="pure-form-message-inline">Body for all notifications</span>
|
|
||||||
</div>
|
|
||||||
<div class="pure-controls">
|
|
||||||
<span class="pure-form-message-inline">
|
|
||||||
These tokens can be used in the notification body and title to
|
|
||||||
customise the notification text.
|
|
||||||
</span>
|
|
||||||
<table class="pure-table" id="token-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Token</th>
|
|
||||||
<th>Description</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td><code>{base_url}</code></td>
|
|
||||||
<td>The URL of the changedetection.io instance you are running.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>{watch_url}</code></td>
|
|
||||||
<td>The URL being watched.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>{watch_uuid}</code></td>
|
|
||||||
<td>The UUID of the watch.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>{watch_title}</code></td>
|
|
||||||
<td>The title of the watch.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>{watch_tag}</code></td>
|
|
||||||
<td>The tag of the watch.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>{preview_url}</code></td>
|
|
||||||
<td>The URL of the preview page generated by changedetection.io.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>{diff_url}</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>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<span 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/>
|
|
||||||
Your <code>BASE_URL</code> var is currently "{{base_url}}"
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="pure-control-group">
|
|
||||||
{{ render_field(form.trigger_check) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</fieldset>
|
<div class="tab-pane-inner" id="notifications">
|
||||||
|
{{ render_notifications_field(form) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane-inner" id="fetching">
|
<div class="tab-pane-inner" id="fetching">
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
{{ render_field(form.fetch_backend) }}
|
{{ render_field(form.fetch_backend) }}
|
||||||
|
|||||||
@@ -35,6 +35,16 @@ def test_check_notification(client, live_server):
|
|||||||
res = client.post(
|
res = client.post(
|
||||||
url_for("edit_page", uuid="first"),
|
url_for("edit_page", uuid="first"),
|
||||||
data={"notification_urls": notification_url,
|
data={"notification_urls": notification_url,
|
||||||
|
"notification_title": "New ChangeDetection.io Notification - {watch_url}",
|
||||||
|
"notification_body": "BASE URL: {base_url}\n"
|
||||||
|
"Watch URL: {watch_url}\n"
|
||||||
|
"Watch UUID: {watch_uuid}\n"
|
||||||
|
"Watch title: {watch_title}\n"
|
||||||
|
"Watch tag: {watch_tag}\n"
|
||||||
|
"Preview: {preview_url}\n"
|
||||||
|
"Diff URL: {diff_url}\n"
|
||||||
|
"Snapshot: {current_snapshot}\n"
|
||||||
|
":-)",
|
||||||
"url": test_url,
|
"url": test_url,
|
||||||
"tag": "my tag",
|
"tag": "my tag",
|
||||||
"title": "my title",
|
"title": "my title",
|
||||||
@@ -66,7 +76,6 @@ def test_check_notification(client, live_server):
|
|||||||
|
|
||||||
os.unlink("test-datastore/notification.txt")
|
os.unlink("test-datastore/notification.txt")
|
||||||
|
|
||||||
|
|
||||||
set_modified_response()
|
set_modified_response()
|
||||||
|
|
||||||
# Trigger a check
|
# Trigger a check
|
||||||
@@ -100,18 +109,9 @@ def test_check_notification(client, live_server):
|
|||||||
res = client.post(
|
res = client.post(
|
||||||
url_for("settings_page"),
|
url_for("settings_page"),
|
||||||
data={"notification_title": "New ChangeDetection.io Notification - {watch_url}",
|
data={"notification_title": "New ChangeDetection.io Notification - {watch_url}",
|
||||||
"notification_body": "BASE URL: {base_url}\n"
|
|
||||||
"Watch URL: {watch_url}\n"
|
|
||||||
"Watch UUID: {watch_uuid}\n"
|
|
||||||
"Watch title: {watch_title}\n"
|
|
||||||
"Watch tag: {watch_tag}\n"
|
|
||||||
"Preview: {preview_url}\n"
|
|
||||||
"Diff URL: {diff_url}\n"
|
|
||||||
"Snapshot: {current_snapshot}\n"
|
|
||||||
":-)",
|
|
||||||
"notification_urls": "json://foobar.com", #Re #143 should not see that it sent without [test checkbox]
|
"notification_urls": "json://foobar.com", #Re #143 should not see that it sent without [test checkbox]
|
||||||
"minutes_between_check": 180,
|
"minutes_between_check": 180,
|
||||||
"fetch_backend": "html_requests"
|
"fetch_backend": "html_requests",
|
||||||
},
|
},
|
||||||
follow_redirects=True
|
follow_redirects=True
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ def test_check_watch_field_storage(client, live_server):
|
|||||||
|
|
||||||
res = client.post(
|
res = client.post(
|
||||||
url_for("edit_page", uuid="first"),
|
url_for("edit_page", uuid="first"),
|
||||||
data={ "notification_urls": "http://myapi.com",
|
data={ "notification_urls": "json://myapi.com",
|
||||||
"minutes_between_check": 126,
|
"minutes_between_check": 126,
|
||||||
"css_filter" : ".fooclass",
|
"css_filter" : ".fooclass",
|
||||||
"title" : "My title",
|
"title" : "My title",
|
||||||
@@ -39,7 +39,7 @@ def test_check_watch_field_storage(client, live_server):
|
|||||||
follow_redirects=True
|
follow_redirects=True
|
||||||
)
|
)
|
||||||
|
|
||||||
assert b"http://myapi.com" in res.data
|
assert b"json://myapi.com" in res.data
|
||||||
assert b"126" in res.data
|
assert b"126" in res.data
|
||||||
assert b".fooclass" in res.data
|
assert b".fooclass" in res.data
|
||||||
assert b"My title" in res.data
|
assert b"My title" in res.data
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ def live_server_setup(live_server):
|
|||||||
|
|
||||||
|
|
||||||
# Where we POST to as a notification
|
# Where we POST to as a notification
|
||||||
@live_server.app.route('/test_notification_endpoint', methods=['POST'])
|
@live_server.app.route('/test_notification_endpoint', methods=['POST', 'GET'])
|
||||||
def test_notification_endpoint():
|
def test_notification_endpoint():
|
||||||
from flask import request
|
from flask import request
|
||||||
|
|
||||||
|
|||||||
@@ -85,12 +85,16 @@ class update_worker(threading.Thread):
|
|||||||
if len(watch['notification_urls']):
|
if len(watch['notification_urls']):
|
||||||
print(">>> Notifications queued for UUID from watch {}".format(uuid))
|
print(">>> Notifications queued for UUID from watch {}".format(uuid))
|
||||||
n_object['notification_urls'] = watch['notification_urls']
|
n_object['notification_urls'] = watch['notification_urls']
|
||||||
|
n_object['notification_title'] = watch['notification_title']
|
||||||
|
n_object['notification_body'] = watch['notification_body']
|
||||||
self.notification_q.put(n_object)
|
self.notification_q.put(n_object)
|
||||||
|
|
||||||
# No? maybe theres a global setting, queue them all
|
# No? maybe theres a global setting, queue them all
|
||||||
elif len(self.datastore.data['settings']['application']['notification_urls']):
|
elif len(self.datastore.data['settings']['application']['notification_urls']):
|
||||||
print(">>> Watch notification URLs were empty, using GLOBAL notifications for UUID: {}".format(uuid))
|
print(">>> Watch notification URLs were empty, using GLOBAL notifications for UUID: {}".format(uuid))
|
||||||
n_object['notification_urls'] = self.datastore.data['settings']['application']['notification_urls']
|
n_object['notification_urls'] = self.datastore.data['settings']['application']['notification_urls']
|
||||||
|
n_object['notification_title'] = self.datastore.data['settings']['application']['notification_title']
|
||||||
|
n_object['notification_body'] = self.datastore.data['settings']['application']['notification_body']
|
||||||
self.notification_q.put(n_object)
|
self.notification_q.put(n_object)
|
||||||
else:
|
else:
|
||||||
print(">>> NO notifications queued, watch and global notification URLs were empty.")
|
print(">>> NO notifications queued, watch and global notification URLs were empty.")
|
||||||
|
|||||||
Reference in New Issue
Block a user