diff --git a/changedetectionio/__init__.py b/changedetectionio/__init__.py index 556503122..4c8756fa9 100644 --- a/changedetectionio/__init__.py +++ b/changedetectionio/__init__.py @@ -370,20 +370,20 @@ def changedetection_app(config=None, datastore_o=None): from changedetectionio import forms limit_tag = request.args.get('tag') - pause_uuid = request.args.get('pause') - # Redirect for the old rss path which used the /?rss=true if request.args.get('rss'): return redirect(url_for('rss', tag=limit_tag)) - if pause_uuid: - try: - datastore.data['watching'][pause_uuid]['paused'] ^= True - datastore.needs_write = True + op = request.args.get('op') + if op: + uuid = request.args.get('uuid') + if op == 'pause': + datastore.data['watching'][uuid]['paused'] ^= True + elif op == 'mute': + datastore.data['watching'][uuid]['notification_muted'] ^= True - return redirect(url_for('index', tag = limit_tag)) - except KeyError: - pass + datastore.needs_write = True + return redirect(url_for('index', tag = limit_tag)) # Sort by last_changed and add the uuid which is usually the key.. sorted_watches = [] diff --git a/changedetectionio/model/Watch.py b/changedetectionio/model/Watch.py index b53688d3c..458a6fd2f 100644 --- a/changedetectionio/model/Watch.py +++ b/changedetectionio/model/Watch.py @@ -36,6 +36,7 @@ class model(dict): 'notification_title': default_notification_title, 'notification_body': default_notification_body, 'notification_format': default_notification_format, + 'notification_muted': False, 'css_filter': '', 'extract_text': [], # Extract text by regex after filters 'subtractive_selectors': [], diff --git a/changedetectionio/static/styles/styles.css b/changedetectionio/static/styles/styles.css index 1271c2048..34934f9a7 100644 --- a/changedetectionio/static/styles/styles.css +++ b/changedetectionio/static/styles/styles.css @@ -271,13 +271,15 @@ footer { #new-version-text a { color: #e07171; } -.watch-controls.state-False img { - opacity: 0.2; - transition: opacity 0.3s; } - -.watch-controls img:hover { - transition: opacity 0.3s; - opacity: 0.8; } +.watch-controls { + /* default */ } + .watch-controls .state-on img { + opacity: 0.8; } + .watch-controls img { + opacity: 0.2; } + .watch-controls img:hover { + transition: opacity 0.3s; + opacity: 0.8; } .monospaced-textarea textarea { width: 100%; diff --git a/changedetectionio/static/styles/styles.scss b/changedetectionio/static/styles/styles.scss index f16c4e0cb..acfcacbef 100644 --- a/changedetectionio/static/styles/styles.scss +++ b/changedetectionio/static/styles/styles.scss @@ -363,16 +363,24 @@ footer { } .watch-controls { - &.state-False img { - opacity: 0.2; - transition: opacity 0.3s; + .state-on { + img { + opacity: 0.8; + } } + + /* default */ + img { + opacity: 0.2; + } + img { &:hover { transition: opacity 0.3s; opacity: 0.8; } } + } .monospaced-textarea { diff --git a/changedetectionio/templates/watch-overview.html b/changedetectionio/templates/watch-overview.html index 0377dcf58..0ab08decf 100644 --- a/changedetectionio/templates/watch-overview.html +++ b/changedetectionio/templates/watch-overview.html @@ -56,11 +56,10 @@ {% if watch.newest_history_key| int > watch.last_viewed and watch.history_n>=2 %}unviewed{% endif %} {% if watch.uuid in queued_uuids %}queued{% endif %}"> {{ loop.index }} - - Pause checks - Mute notifications + + Pause checks + Mute notifications - {{watch.title if watch.title is not none and watch.title|length > 0 else watch.url}} diff --git a/changedetectionio/update_worker.py b/changedetectionio/update_worker.py index 02ea036ce..697abbc1f 100644 --- a/changedetectionio/update_worker.py +++ b/changedetectionio/update_worker.py @@ -21,6 +21,63 @@ class update_worker(threading.Thread): self.datastore = datastore super().__init__(*args, **kwargs) + def send_content_changed_notification(self, uuid): + n_object = {} + watch = self.datastore.data['watching'].get(uuid, False) + if not watch: + return + + watch_history = watch.history + dates = list(watch_history.keys()) + # Theoretically it's possible that this could be just 1 long, + # - In the case that the timestamp key was not unique + if len(dates) == 1: + raise ValueError( + "History index had 2 or more, but only 1 date loaded, timestamps were not unique? maybe two of the same timestamps got written, needs more delay?" + ) + + # Did it have any notification alerts to hit? + if len(watch['notification_urls']): + print(">>> Notifications queued for UUID from watch {}".format(uuid)) + n_object['notification_urls'] = watch['notification_urls'] + n_object['notification_title'] = watch['notification_title'] + n_object['notification_body'] = watch['notification_body'] + n_object['notification_format'] = watch['notification_format'] + + # No? maybe theres a global setting, queue them all + elif len(self.datastore.data['settings']['application']['notification_urls']): + 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_title'] = self.datastore.data['settings']['application']['notification_title'] + n_object['notification_body'] = self.datastore.data['settings']['application']['notification_body'] + n_object['notification_format'] = self.datastore.data['settings']['application']['notification_format'] + else: + print(">>> NO notifications queued, watch and global notification URLs were empty.") + + # Only prepare to notify if the rules above matched + if 'notification_urls' in n_object: + # HTML needs linebreak, but MarkDown and Text can use a linefeed + if n_object['notification_format'] == 'HTML': + line_feed_sep = "
" + else: + line_feed_sep = "\n" + + from changedetectionio import diff + + snapshot_contents = '' + with open(watch_history[dates[-1]], 'rb') as f: + snapshot_contents = f.read() + + n_object.update({ + 'watch_url': watch['url'], + 'uuid': uuid, + 'current_snapshot': snapshot_contents.decode('utf-8'), + 'diff': diff.render_diff(watch_history[dates[-2]], watch_history[dates[-1]], line_feed_sep=line_feed_sep), + 'diff_full': diff.render_diff(watch_history[dates[-2]], watch_history[dates[-1]], True, line_feed_sep=line_feed_sep) + }) + + self.notification_q.put(n_object) + def send_filter_failure_notification(self, uuid): threshold = self.datastore.data['settings']['application'].get('filter_failure_notification_threshold_attempts') @@ -105,8 +162,10 @@ class update_worker(threading.Thread): 0) print("Filter for {} not found, consecutive_filter_failures: {}".format(uuid, c)) if threshold > 0 and c >= threshold: - self.send_filter_failure_notification(uuid) + if self.datastore.data['watching'][uuid].get('notification_muted'): + self.send_filter_failure_notification(uuid) c = 0 + self.datastore.update_watch(uuid=uuid, update_obj={'consecutive_filter_failures': c}) process_changedetection_results = True @@ -143,66 +202,21 @@ class update_worker(threading.Thread): # For the FIRST time we check a site, or a change detected, save the snapshot. if changed_detected or not watch['last_checked']: # A change was detected - fname = watch.save_history_text(contents=contents, timestamp=str(round(time.time()))) + watch.save_history_text(contents=contents, timestamp=str(round(time.time()))) self.datastore.update_watch(uuid=uuid, update_obj=update_obj) # A change was detected if changed_detected: - n_object = {} print (">> Change detected in UUID {} - {}".format(uuid, watch['url'])) # Notifications should only trigger on the second time (first time, we gather the initial snapshot) if watch.history_n >= 2: # Atleast 2, means there really was a change self.datastore.update_watch(uuid=uuid, update_obj={'last_changed': round(now)}) + if not self.datastore.data['watching'][uuid].get('notification_muted'): + self.send_content_changed_notification(self, uuid) - watch_history = watch.history - dates = list(watch_history.keys()) - # Theoretically it's possible that this could be just 1 long, - # - In the case that the timestamp key was not unique - if len(dates) == 1: - raise ValueError( - "History index had 2 or more, but only 1 date loaded, timestamps were not unique? maybe two of the same timestamps got written, needs more delay?" - ) - prev_fname = watch_history[dates[-2]] - - # Did it have any notification alerts to hit? - if len(watch['notification_urls']): - print(">>> Notifications queued for UUID from watch {}".format(uuid)) - n_object['notification_urls'] = watch['notification_urls'] - n_object['notification_title'] = watch['notification_title'] - n_object['notification_body'] = watch['notification_body'] - n_object['notification_format'] = watch['notification_format'] - - # No? maybe theres a global setting, queue them all - elif len(self.datastore.data['settings']['application']['notification_urls']): - 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_title'] = self.datastore.data['settings']['application']['notification_title'] - n_object['notification_body'] = self.datastore.data['settings']['application']['notification_body'] - n_object['notification_format'] = self.datastore.data['settings']['application']['notification_format'] - else: - print(">>> NO notifications queued, watch and global notification URLs were empty.") - - # Only prepare to notify if the rules above matched - if 'notification_urls' in n_object: - # HTML needs linebreak, but MarkDown and Text can use a linefeed - if n_object['notification_format'] == 'HTML': - line_feed_sep = "
" - else: - line_feed_sep = "\n" - - from changedetectionio import diff - n_object.update({ - 'watch_url': watch['url'], - 'uuid': uuid, - 'current_snapshot': contents.decode('utf-8'), - 'diff': diff.render_diff(prev_fname, fname, line_feed_sep=line_feed_sep), - 'diff_full': diff.render_diff(prev_fname, fname, True, line_feed_sep=line_feed_sep) - }) - - self.notification_q.put(n_object) except Exception as e: # Catch everything possible here, so that if a worker crashes, we don't lose it until restart!