Compare commits

..

1 Commits

Author SHA1 Message Date
dgtlmoon
eba27dcd91 WIP 2024-10-17 21:08:05 +02:00
15 changed files with 47 additions and 117 deletions

View File

@@ -13,7 +13,6 @@ from loguru import logger
def apprise_custom_api_call_wrapper(body, title, notify_type, *args, **kwargs):
import requests
import json
from urllib.parse import unquote_plus
from apprise.utils import parse_url as apprise_parse_url
from apprise import URLBase
@@ -48,7 +47,7 @@ def apprise_custom_api_call_wrapper(body, title, notify_type, *args, **kwargs):
if results:
# Add our headers that the user can potentially over-ride if they wish
# to to our returned result set and tidy entries by unquoting them
headers = {unquote_plus(x): unquote_plus(y)
headers = {URLBase.unquote(x): URLBase.unquote(y)
for x, y in results['qsd+'].items()}
# https://github.com/caronc/apprise/wiki/Notify_Custom_JSON#get-parameter-manipulation
@@ -56,14 +55,14 @@ def apprise_custom_api_call_wrapper(body, title, notify_type, *args, **kwargs):
# but here we are making straight requests, so we need todo convert this against apprise's logic
for k, v in results['qsd'].items():
if not k.strip('+-') in results['qsd+'].keys():
params[unquote_plus(k)] = unquote_plus(v)
params[URLBase.unquote(k)] = URLBase.unquote(v)
# Determine Authentication
auth = ''
if results.get('user') and results.get('password'):
auth = (unquote_plus(results.get('user')), unquote_plus(results.get('user')))
auth = (URLBase.unquote(results.get('user')), URLBase.unquote(results.get('user')))
elif results.get('user'):
auth = (unquote_plus(results.get('user')))
auth = (URLBase.unquote(results.get('user')))
# Try to auto-guess if it's JSON
h = 'application/json; charset=utf-8'

View File

@@ -67,6 +67,7 @@ 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
@@ -469,7 +470,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)
@@ -532,7 +533,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 it`s used in tag editor, global settings
# Watch_uuid could be unset in the case its used in tag editor, global setings
import apprise
import random
from .apprise_asset import asset
@@ -541,15 +542,13 @@ 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) \
and datastore.data.get('watching'):
# 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):
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)
else:
watch = None
watch = datastore.data['watching'].get(watch_uuid)
notification_urls = request.form['notification_urls'].strip().splitlines()
@@ -1397,7 +1396,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})

View File

@@ -496,7 +496,7 @@ class processor_text_json_diff_form(commonSettingsForm):
text_should_not_be_present = StringListField('Block change-detection while text matches', [validators.Optional(), ValidateListRegex()])
webdriver_js_execute_code = TextAreaField('Execute JavaScript before change detection', render_kw={"rows": "5"}, validators=[validators.Optional()])
save_button = SubmitField('Save', render_kw={"class": "pure-button button-small pure-button-primary"})
save_button = SubmitField('Save', render_kw={"class": "pure-button pure-button-primary"})
proxy = RadioField('Proxy')
filter_failure_notification_send = BooleanField(
@@ -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;"},
@@ -616,7 +618,7 @@ class globalSettingsForm(Form):
requests = FormField(globalSettingsRequestForm)
application = FormField(globalSettingsApplicationForm)
save_button = SubmitField('Save', render_kw={"class": "pure-button button-small pure-button-primary"})
save_button = SubmitField('Save', render_kw={"class": "pure-button pure-button-primary"})
class extractDataForm(Form):

View File

@@ -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,

View File

@@ -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):

View File

@@ -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,

View File

@@ -153,8 +153,7 @@ html[data-darkmode="true"] {
border: 1px solid transparent;
vertical-align: top;
font: 1em monospace;
text-align: left;
overflow: clip; }
text-align: left; }
#diff-ui pre {
white-space: pre-wrap; }
@@ -173,9 +172,7 @@ ins {
text-decoration: none; }
#result {
white-space: pre-wrap;
word-break: break-word;
overflow-wrap: break-word; }
white-space: pre-wrap; }
#settings {
background: rgba(0, 0, 0, 0.05);
@@ -234,12 +231,3 @@ td#diff-col div {
border-radius: 5px;
background: var(--color-background);
box-shadow: 1px 1px 4px var(--color-shadow-jump); }
.pure-form button.reset-margin {
margin: 0px; }
.diff-fieldset {
display: flex;
align-items: center;
gap: 4px;
flex-wrap: wrap; }

View File

@@ -24,7 +24,6 @@
vertical-align: top;
font: 1em monospace;
text-align: left;
overflow: clip; // clip overflowing contents to cell boundariess
}
pre {
@@ -51,8 +50,6 @@ ins {
#result {
white-space: pre-wrap;
word-break: break-word;
overflow-wrap: break-word;
.change {
span {}
@@ -137,15 +134,3 @@ td#diff-col div {
background: var(--color-background);
box-shadow: 1px 1px 4px var(--color-shadow-jump);
}
// resets button margin to 0px
.pure-form button.reset-margin {
margin: 0px;
}
.diff-fieldset {
display: flex;
align-items: center;
gap: 4px;
flex-wrap: wrap;
}

View File

@@ -11,22 +11,7 @@ ul#requests-extra_browsers {
/* each proxy entry is a `table` */
table {
tr {
display: table-row; // default display for small screens
input[type=text] {
width: 100%;
}
}
}
// apply inline display for larger screens
@media only screen and (min-width: 1280px) {
table {
tr {
display: inline;
input[type=text] {
width: 100%;
}
}
display: inline;
}
}
}

View File

@@ -11,19 +11,7 @@ ul#requests-extra_proxies {
/* each proxy entry is a `table` */
table {
tr {
display: table-row; // default display for small screens
input[type=text] {
width: 100%;
}
}
}
// apply inline display for large screens
@media only screen and (min-width: 1024px) {
table {
tr {
display: inline;
}
display: inline;
}
}
}

View File

@@ -112,12 +112,7 @@ ul#requests-extra_proxies {
ul#requests-extra_proxies li > label {
display: none; }
ul#requests-extra_proxies table tr {
display: table-row; }
ul#requests-extra_proxies table tr input[type=text] {
width: 100%; }
@media only screen and (min-width: 1024px) {
ul#requests-extra_proxies table tr {
display: inline; } }
display: inline; }
#request {
/* Auto proxy scan/checker */ }
@@ -166,14 +161,7 @@ ul#requests-extra_browsers {
ul#requests-extra_browsers li > label {
display: none; }
ul#requests-extra_browsers table tr {
display: table-row; }
ul#requests-extra_browsers table tr input[type=text] {
width: 100%; }
@media only screen and (min-width: 1280px) {
ul#requests-extra_browsers table tr {
display: inline; }
ul#requests-extra_browsers table tr input[type=text] {
width: 100%; } }
display: inline; }
#extra-browsers-setting {
border: 1px solid var(--color-grey-800);

View File

@@ -14,7 +14,7 @@
<div id="settings">
<form class="pure-form " action="" method="GET" id="diff-form">
<fieldset class="diff-fieldset">
<fieldset>
{% if versions|length >= 1 %}
<strong>Compare</strong>
<del class="change"><span>from</span></del>
@@ -33,7 +33,7 @@
</option>
{% endfor %}
</select>
<button type="submit" class="pure-button pure-button-primary reset-margin">Go</button>
<button type="submit" class="pure-button pure-button-primary">Go</button>
{% endif %}
</fieldset>
<fieldset>

View File

@@ -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>
@@ -276,7 +283,7 @@ nav
<div class="pure-control-group">
{{ render_button(form.save_button) }}
<a href="{{url_for('index')}}" class="pure-button button-small button-cancel">Back</a>
<a href="{{url_for('clear_all_history')}}" class="pure-button button-small button-error">Clear Snapshot History</a>
<a href="{{url_for('clear_all_history')}}" class="pure-button button-small button-cancel">Clear Snapshot History</a>
</div>
</div>
</form>

View File

@@ -284,7 +284,7 @@ def test_notification_custom_endpoint_and_jinja2(client, live_server, measure_me
# CUSTOM JSON BODY CHECK for POST://
set_original_response()
# https://github.com/caronc/apprise/wiki/Notify_Custom_JSON#header-manipulation
test_notification_url = url_for('test_notification_endpoint', _external=True).replace('http://', 'post://')+"?xxx={{ watch_url }}&+custom-header=123&+second=hello+world%20%22space%22"
test_notification_url = url_for('test_notification_endpoint', _external=True).replace('http://', 'post://')+"?xxx={{ watch_url }}&+custom-header=123"
res = client.post(
url_for("settings_page"),
@@ -326,7 +326,6 @@ def test_notification_custom_endpoint_and_jinja2(client, live_server, measure_me
assert j['secret'] == 444
assert j['somebug'] == '网站监测 内容更新了'
# 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:
@@ -338,7 +337,6 @@ def test_notification_custom_endpoint_and_jinja2(client, live_server, measure_me
with open("test-datastore/notification-headers.txt", 'r') as f:
notification_headers = f.read()
assert 'custom-header: 123' in notification_headers.lower()
assert 'second: hello world "space"' in notification_headers.lower()
# Should always be automatically detected as JSON content type even when we set it as 'Text' (default)
@@ -431,24 +429,3 @@ 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

View File

@@ -81,8 +81,7 @@ class update_worker(threading.Thread):
'watch_url': watch.get('url') if watch else None,
})
if watch:
n_object.update(watch.extra_notification_token_values())
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")
@@ -569,6 +568,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