Compare commits
8 Commits
0.47.03
...
2727-notif
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd919dbb0b | ||
|
|
6e0a0b5d5f | ||
|
|
a1d8186546 | ||
|
|
d82f254382 | ||
|
|
63366a0a83 | ||
|
|
eba27dcd91 | ||
|
|
c1dd681643 | ||
|
|
ecafa27833 |
@@ -67,7 +67,6 @@ FlaskCompress(app)
|
||||
|
||||
# Stop browser caching of assets
|
||||
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
|
||||
|
||||
app.config.exit = Event()
|
||||
|
||||
app.config['NEW_VERSION_AVAILABLE'] = False
|
||||
@@ -470,7 +469,7 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
continue
|
||||
if watch.get('last_error'):
|
||||
errored_count += 1
|
||||
|
||||
|
||||
if search_q:
|
||||
if (watch.get('title') and search_q in watch.get('title').lower()) or search_q in watch.get('url', '').lower():
|
||||
sorted_watches.append(watch)
|
||||
@@ -533,7 +532,7 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
@login_optionally_required
|
||||
def ajax_callback_send_notification_test(watch_uuid=None):
|
||||
|
||||
# Watch_uuid could be unset in the case its used in tag editor, global setings
|
||||
# Watch_uuid could be unset in the case it`s used in tag editor, global settings
|
||||
import apprise
|
||||
import random
|
||||
from .apprise_asset import asset
|
||||
@@ -542,13 +541,15 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
from changedetectionio.apprise_plugin import apprise_custom_api_call_wrapper
|
||||
is_global_settings_form = request.args.get('mode', '') == 'global-settings'
|
||||
is_group_settings_form = request.args.get('mode', '') == 'group-settings'
|
||||
|
||||
# Use an existing random one on the global/main settings form
|
||||
if not watch_uuid and (is_global_settings_form or is_group_settings_form):
|
||||
if not watch_uuid and (is_global_settings_form or is_group_settings_form) \
|
||||
and datastore.data.get('watching'):
|
||||
|
||||
logger.debug(f"Send test notification - Choosing random Watch {watch_uuid}")
|
||||
watch_uuid = random.choice(list(datastore.data['watching'].keys()))
|
||||
|
||||
watch = datastore.data['watching'].get(watch_uuid)
|
||||
watch = datastore.data['watching'].get(watch_uuid)
|
||||
else:
|
||||
watch = None
|
||||
|
||||
notification_urls = request.form['notification_urls'].strip().splitlines()
|
||||
|
||||
@@ -1396,7 +1397,7 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
url = request.form.get('url').strip()
|
||||
if datastore.url_exists(url):
|
||||
flash(f'Warning, URL {url} already exists', "notice")
|
||||
|
||||
|
||||
add_paused = request.form.get('edit_and_watch_submit_button') != None
|
||||
processor = request.form.get('processor', 'text_json_diff')
|
||||
new_uuid = datastore.add_watch(url=url, tag=request.form.get('tags').strip(), extras={'paused': add_paused, 'processor': processor})
|
||||
|
||||
@@ -588,6 +588,8 @@ class globalSettingsApplicationForm(commonSettingsForm):
|
||||
global_ignore_text = StringListField('Ignore Text', [ValidateListRegex()])
|
||||
global_subtractive_selectors = StringListField('Remove elements', [ValidateCSSJSONXPATHInput(allow_json=False)])
|
||||
ignore_whitespace = BooleanField('Ignore whitespace')
|
||||
keep_history_n = IntegerField('Number of snapshots to keep in history for each watch')
|
||||
keep_history_seconds = IntegerField('Number of snapshots to keep - maximum age (todo/seconds)')
|
||||
password = SaltyPasswordField()
|
||||
pager_size = IntegerField('Pager size',
|
||||
render_kw={"style": "width: 5em;"},
|
||||
|
||||
@@ -40,6 +40,8 @@ class model(dict):
|
||||
'global_ignore_text': [], # List of text to ignore when calculating the comparison checksum
|
||||
'global_subtractive_selectors': [],
|
||||
'ignore_whitespace': True,
|
||||
'keep_history_n': None, # Number of snapshots to keep
|
||||
'keep_history_seconds': None, # Or time ago back to keep
|
||||
'notification_body': default_notification_body,
|
||||
'notification_format': default_notification_format,
|
||||
'notification_title': default_notification_title,
|
||||
|
||||
@@ -625,6 +625,9 @@ class model(watch_base):
|
||||
if index > 1 and os.path.isfile(filepath):
|
||||
os.remove(filepath)
|
||||
|
||||
def post_process(self):
|
||||
|
||||
x=1
|
||||
|
||||
@property
|
||||
def get_browsersteps_available_screenshots(self):
|
||||
|
||||
@@ -33,6 +33,8 @@ class watch_base(dict):
|
||||
'headers': {}, # Extra headers to send
|
||||
'ignore_text': [], # List of text to ignore when calculating the comparison checksum
|
||||
'in_stock_only': True, # Only trigger change on going to instock from out-of-stock
|
||||
'keep_history_n': None, # Number of snapshots to keep
|
||||
'keep_history_seconds': None, # Or time ago back to keep
|
||||
'include_filters': [],
|
||||
'last_checked': 0,
|
||||
'last_error': False,
|
||||
|
||||
@@ -331,13 +331,21 @@ class perform_site_check(difference_detection_processor):
|
||||
if result:
|
||||
blocked = True
|
||||
|
||||
# The main thing that all this at the moment comes down to :)
|
||||
if watch.get('previous_md5') != fetched_md5:
|
||||
changed_detected = True
|
||||
|
||||
# Looks like something changed, but did it match all the rules?
|
||||
if blocked:
|
||||
changed_detected = False
|
||||
else:
|
||||
# The main thing that all this at the moment comes down to :)
|
||||
if watch.get('previous_md5') != fetched_md5:
|
||||
changed_detected = True
|
||||
|
||||
# Always record the new checksum
|
||||
update_obj["previous_md5"] = fetched_md5
|
||||
|
||||
# On the first run of a site, watch['previous_md5'] will be None, set it the current one.
|
||||
if not watch.get('previous_md5'):
|
||||
watch['previous_md5'] = fetched_md5
|
||||
|
||||
logger.debug(f"Watch UUID {watch.get('uuid')} content check - Previous MD5: {watch.get('previous_md5')}, Fetched MD5 {fetched_md5}")
|
||||
|
||||
@@ -357,12 +365,6 @@ class perform_site_check(difference_detection_processor):
|
||||
else:
|
||||
logger.debug(f"check_unique_lines: UUID {watch.get('uuid')} had unique content")
|
||||
|
||||
# Always record the new checksum
|
||||
update_obj["previous_md5"] = fetched_md5
|
||||
|
||||
# On the first run of a site, watch['previous_md5'] will be None, set it the current one.
|
||||
if not watch.get('previous_md5'):
|
||||
watch['previous_md5'] = fetched_md5
|
||||
|
||||
# stripped_text_from_html - Everything after filters and NO 'ignored' content
|
||||
return changed_detected, update_obj, stripped_text_from_html
|
||||
|
||||
@@ -26,8 +26,7 @@ function set_active_tab() {
|
||||
if (tab.length) {
|
||||
tab[0].parentElement.className = "active";
|
||||
}
|
||||
// hash could move the page down
|
||||
window.scrollTo(0, 0);
|
||||
|
||||
}
|
||||
|
||||
function focus_error_tab() {
|
||||
|
||||
@@ -148,7 +148,7 @@ body.spinner-active {
|
||||
}
|
||||
|
||||
|
||||
.tabs ul li a {
|
||||
.tab-pane-inner {
|
||||
// .tab-pane-inner will have the #id that the tab button jumps/anchors to
|
||||
scroll-margin-top: 200px;
|
||||
}
|
||||
|
||||
@@ -605,8 +605,7 @@ body.spinner-active #pure-menu-horizontal-spinner {
|
||||
background-color: var(--color-background-menu-link-hover);
|
||||
color: var(--color-text-menu-link-hover); }
|
||||
|
||||
|
||||
.tabs ul li a {
|
||||
.tab-pane-inner {
|
||||
scroll-margin-top: 200px; }
|
||||
|
||||
section.content {
|
||||
|
||||
@@ -129,6 +129,13 @@
|
||||
Note: Simply changing the User-Agent often does not defeat anti-robot technologies, it's important to consider <a href="https://changedetection.io/tutorial/what-are-main-types-anti-robot-mechanisms">all of the ways that the browser is detected</a>.
|
||||
</span>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_field(form.application.form.keep_history_n) }}
|
||||
<span class="pure-form-message-inline">Blank - keep all</span>
|
||||
{{ render_field(form.application.form.keep_history_seconds) }}
|
||||
<span class="pure-form-message-inline">Blank - keep all</span>
|
||||
</div>
|
||||
|
||||
<div class="pure-control-group">
|
||||
<br>
|
||||
Tip: <a href="https://github.com/dgtlmoon/changedetection.io/wiki/Proxy-configuration#brightdata-proxy-support">Connect using Bright Data and Oxylabs Proxies, find out more here.</a>
|
||||
|
||||
@@ -65,11 +65,8 @@ def test_check_block_changedetection_text_NOT_present(client, live_server, measu
|
||||
live_server_setup(live_server)
|
||||
# Use a mix of case in ZzZ to prove it works case-insensitive.
|
||||
ignore_text = "out of stoCk\r\nfoobar"
|
||||
|
||||
set_original_ignore_response()
|
||||
|
||||
# Give the endpoint time to spin up
|
||||
time.sleep(1)
|
||||
|
||||
# Add our URL to the import page
|
||||
test_url = url_for('test_endpoint', _external=True)
|
||||
@@ -127,13 +124,24 @@ def test_check_block_changedetection_text_NOT_present(client, live_server, measu
|
||||
assert b'unviewed' not in res.data
|
||||
assert b'/test-endpoint' in res.data
|
||||
|
||||
# 2548
|
||||
# Going back to the ORIGINAL should NOT trigger a change
|
||||
set_original_ignore_response()
|
||||
client.get(url_for("form_watch_checknow"), follow_redirects=True)
|
||||
wait_for_all_checks(client)
|
||||
res = client.get(url_for("index"))
|
||||
assert b'unviewed' not in res.data
|
||||
|
||||
# Now we set a change where the text is gone, it should now trigger
|
||||
|
||||
# Now we set a change where the text is gone AND its different content, it should now trigger
|
||||
set_modified_response_minus_block_text()
|
||||
client.get(url_for("form_watch_checknow"), follow_redirects=True)
|
||||
wait_for_all_checks(client)
|
||||
res = client.get(url_for("index"))
|
||||
assert b'unviewed' in res.data
|
||||
|
||||
|
||||
|
||||
|
||||
res = client.get(url_for("form_delete", uuid="all"), follow_redirects=True)
|
||||
assert b'Deleted' in res.data
|
||||
|
||||
@@ -429,3 +429,24 @@ def test_global_send_test_notification(client, live_server, measure_memory_usage
|
||||
follow_redirects=True
|
||||
)
|
||||
|
||||
#2727 - be sure a test notification when there are zero watches works ( should all be deleted now)
|
||||
|
||||
os.unlink("test-datastore/notification.txt")
|
||||
|
||||
|
||||
######### Test global/system settings
|
||||
res = client.post(
|
||||
url_for("ajax_callback_send_notification_test")+"?mode=global-settings",
|
||||
data={"notification_urls": test_notification_url},
|
||||
follow_redirects=True
|
||||
)
|
||||
|
||||
assert res.status_code != 400
|
||||
assert res.status_code != 500
|
||||
|
||||
# Give apprise time to fire
|
||||
time.sleep(4)
|
||||
|
||||
with open("test-datastore/notification.txt", 'r') as f:
|
||||
x = f.read()
|
||||
assert 'change detection is cool 网站监测 内容更新了' in x
|
||||
|
||||
@@ -81,7 +81,8 @@ class update_worker(threading.Thread):
|
||||
'watch_url': watch.get('url') if watch else None,
|
||||
})
|
||||
|
||||
n_object.update(watch.extra_notification_token_values())
|
||||
if watch:
|
||||
n_object.update(watch.extra_notification_token_values())
|
||||
|
||||
logger.trace(f"Main rendered notification placeholders (diff_added etc) calculated in {time.time()-now:.3f}s")
|
||||
logger.debug("Queued notification for sending")
|
||||
@@ -568,6 +569,12 @@ class update_worker(threading.Thread):
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
try:
|
||||
watch.post_process()
|
||||
except Exception as e:
|
||||
logger.critical(e)
|
||||
|
||||
|
||||
self.datastore.update_watch(uuid=uuid, update_obj={'fetch_time': round(time.time() - now, 3),
|
||||
'last_checked': round(time.time()),
|
||||
'check_count': count
|
||||
|
||||
Reference in New Issue
Block a user