From 9510345e01ea8e308c339163d8e8b030ce5ac7f1 Mon Sep 17 00:00:00 2001 From: dgtlmoon Date: Wed, 17 Jan 2024 22:35:29 +0100 Subject: [PATCH 1/9] Test - tidy up backup test (#2117) --- changedetectionio/tests/test_backup.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/changedetectionio/tests/test_backup.py b/changedetectionio/tests/test_backup.py index f03fcf37..9f381755 100644 --- a/changedetectionio/tests/test_backup.py +++ b/changedetectionio/tests/test_backup.py @@ -1,8 +1,8 @@ #!/usr/bin/python3 -from .util import set_original_response, set_modified_response, live_server_setup, wait_for_all_checks +from .util import set_original_response, live_server_setup, wait_for_all_checks from flask import url_for -from urllib.request import urlopen +import io from zipfile import ZipFile import re import time @@ -37,15 +37,10 @@ def test_backup(client, live_server): # Should be PK/ZIP stream assert res.data.count(b'PK') >= 2 - # ZipFile from buffer seems non-obvious, just save it instead - with open("download.zip", 'wb') as f: - f.write(res.data) - - zip = ZipFile('download.zip') - l = zip.namelist() + backup = ZipFile(io.BytesIO(res.data)) + l = backup.namelist() uuid4hex = re.compile('^[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}.*txt', re.I) newlist = list(filter(uuid4hex.match, l)) # Read Note below # Should be two txt files in the archive (history and the snapshot) assert len(newlist) == 2 - From 402f1e47e78ecd155b1e90f30cce424ff7763e0f Mon Sep 17 00:00:00 2001 From: dgtlmoon Date: Thu, 18 Jan 2024 22:29:44 +0100 Subject: [PATCH 2/9] Security update - Adding API token secure check for API endpoint `/api/v1/watch//history` @rozpuszczalny --- changedetectionio/api/api_v1.py | 1 + 1 file changed, 1 insertion(+) diff --git a/changedetectionio/api/api_v1.py b/changedetectionio/api/api_v1.py index 190bd449..92b4728c 100644 --- a/changedetectionio/api/api_v1.py +++ b/changedetectionio/api/api_v1.py @@ -133,6 +133,7 @@ class WatchHistory(Resource): # Get a list of available history for a watch by UUID # curl http://localhost:5000/api/v1/watch//history + @auth.check_token def get(self, uuid): """ @api {get} /api/v1/watch//history Get a list of all historical snapshots available for a watch From b30b718373c22eba3610cc9346b477af1c7325ad Mon Sep 17 00:00:00 2001 From: dgtlmoon Date: Fri, 19 Jan 2024 10:24:47 +0100 Subject: [PATCH 3/9] 0.45.13 --- changedetectionio/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changedetectionio/__init__.py b/changedetectionio/__init__.py index 5dbf208e..57f2f139 100644 --- a/changedetectionio/__init__.py +++ b/changedetectionio/__init__.py @@ -2,7 +2,7 @@ # Read more https://github.com/dgtlmoon/changedetection.io/wiki -__version__ = '0.45.12' +__version__ = '0.45.13' from distutils.util import strtobool from json.decoder import JSONDecodeError From 2e88872b7ea0972ba51b980e0e3c1a276bd21ef5 Mon Sep 17 00:00:00 2001 From: dgtlmoon Date: Fri, 19 Jan 2024 23:14:55 +0100 Subject: [PATCH 4/9] Update docker-compose.yml --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index 017a9c59..a7e7e166 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -95,6 +95,7 @@ services: # Used for fetching pages via Playwright+Chrome where you need Javascript support. # Note: works well but is deprecated, does not fetch full page screenshots (doesnt work with Visual Selector) and other issues + # More information about the advantages of playwright/browserless https://www.browserless.io/blog/2023/12/13/migrating-selenium-to-playwright/ # browser-chrome: # hostname: browser-chrome # image: selenium/standalone-chrome:4 From 7d67ad057cc9ab0115fdf83100abf40c7375a474 Mon Sep 17 00:00:00 2001 From: Andrew Peabody Date: Sat, 20 Jan 2024 01:03:24 -0800 Subject: [PATCH 5/9] Enable dependabot for github-actions (#2119) --- .github/dependabot.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..67ef6cd3 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: "weekly" + groups: + all: + patterns: + - "*" From 29ca2521eb663c73aaf8dc17193b49aba2b70c8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Jan 2024 11:47:14 +0100 Subject: [PATCH 6/9] Build maintenance - dependabot - Bump the all build helpers (#2121) --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/containers.yml | 2 +- .github/workflows/pypi-release.yml | 8 ++++---- .github/workflows/test-container-build.yml | 2 +- .github/workflows/test-only.yml | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index a45f0597..5394090d 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -34,7 +34,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -45,7 +45,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -59,4 +59,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/containers.yml b/.github/workflows/containers.yml index 244b4d04..0397c026 100644 --- a/.github/workflows/containers.yml +++ b/.github/workflows/containers.yml @@ -41,7 +41,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python 3.11 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.11 diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index f9839aad..0f38941f 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -9,7 +9,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.x" - name: Install pypa/build @@ -21,7 +21,7 @@ jobs: - name: Build a binary wheel and a source tarball run: python3 -m build - name: Store the distribution packages - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: python-package-distributions path: dist/ @@ -34,7 +34,7 @@ jobs: - build steps: - name: Download all the dists - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: python-package-distributions path: dist/ @@ -64,7 +64,7 @@ jobs: steps: - name: Download all the dists - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: python-package-distributions path: dist/ diff --git a/.github/workflows/test-container-build.yml b/.github/workflows/test-container-build.yml index 00857a9f..ff453523 100644 --- a/.github/workflows/test-container-build.yml +++ b/.github/workflows/test-container-build.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python 3.11 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.11 diff --git a/.github/workflows/test-only.yml b/.github/workflows/test-only.yml index 8655964f..6f762437 100644 --- a/.github/workflows/test-only.yml +++ b/.github/workflows/test-only.yml @@ -11,7 +11,7 @@ jobs: # Mainly just for link/flake8 - name: Set up Python 3.11 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.11' From 04391551271ddf427d7decb35c482f2c3314b4dd Mon Sep 17 00:00:00 2001 From: dgtlmoon Date: Mon, 22 Jan 2024 11:06:55 +0100 Subject: [PATCH 7/9] Notification - Templates - Adding an example of how to use |tojson for JSON payloads --- changedetectionio/templates/_common_fields.jinja | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changedetectionio/templates/_common_fields.jinja b/changedetectionio/templates/_common_fields.jinja index fa113e17..ddccaf82 100644 --- a/changedetectionio/templates/_common_fields.jinja +++ b/changedetectionio/templates/_common_fields.jinja @@ -115,6 +115,9 @@ Warning: Contents of {{ '{{diff}}' }}, {{ '{{diff_removed}}' }}, and {{ '{{diff_added}}' }} depend on how the difference algorithm perceives the change.
For example, an addition or removal could be perceived as a change in some cases. More Here

+

+ For JSON payloads, use |tojson without quotes for automatic escaping, for example - { "name": {{ '{{ watch_title|tojson }}' }} } +

From 96793890f859f0a19f4811288f3c7336e71ffeaa Mon Sep 17 00:00:00 2001 From: dgtlmoon Date: Mon, 22 Jan 2024 12:20:23 +0100 Subject: [PATCH 8/9] Notification - Templates - Adding an example of how to use URL encoding with tokens --- changedetectionio/templates/_common_fields.jinja | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changedetectionio/templates/_common_fields.jinja b/changedetectionio/templates/_common_fields.jinja index ddccaf82..8b44ebbd 100644 --- a/changedetectionio/templates/_common_fields.jinja +++ b/changedetectionio/templates/_common_fields.jinja @@ -118,6 +118,9 @@

For JSON payloads, use |tojson without quotes for automatic escaping, for example - { "name": {{ '{{ watch_title|tojson }}' }} }

+

+ URL encoding, use |urlencode, for example - gets://hook-website.com/test.php?title={{ '{{ watch_title|urlencode }}' }} +

From 677082723ced7c0f53b43cf860384b6198e81461 Mon Sep 17 00:00:00 2001 From: dgtlmoon Date: Tue, 23 Jan 2024 13:31:05 +0100 Subject: [PATCH 9/9] Restock tweaks - use a single regex, tidy up height detection (#2125) --- changedetectionio/res/stock-not-in-stock.js | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/changedetectionio/res/stock-not-in-stock.js b/changedetectionio/res/stock-not-in-stock.js index abc6578e..3d633303 100644 --- a/changedetectionio/res/stock-not-in-stock.js +++ b/changedetectionio/res/stock-not-in-stock.js @@ -1,3 +1,10 @@ +// Restock Detector +// (c) Leigh Morresi dgtlmoon@gmail.com +// +// Assumes the product is in stock to begin with, unless the following appears above the fold ; +// - outOfStockTexts appears above the fold (out of stock) +// - negateOutOfStockRegex (really is in stock) + function isItemInStock() { // @todo Pass these in so the same list can be used in non-JS fetchers const outOfStockTexts = [ @@ -56,6 +63,7 @@ function isItemInStock() { 'í’ˆė ˆ' ]; + const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0); function getElementBaseText(element) { // .textContent can include text from children which may give the wrong results // scan only immediate TEXT_NODEs, which will be a child of the element @@ -66,19 +74,13 @@ function isItemInStock() { return text.toLowerCase().trim(); } - const negateOutOfStockRegexs = [ - '[0-9] in stock' - ] - var negateOutOfStockRegexs_r = []; - for (let i = 0; i < negateOutOfStockRegexs.length; i++) { - negateOutOfStockRegexs_r.push(new RegExp(negateOutOfStockRegexs[0], 'g')); - } + const negateOutOfStockRegex = new RegExp('([0-9] in stock|add to cart)', 'ig'); // The out-of-stock or in-stock-text is generally always above-the-fold // and often below-the-fold is a list of related products that may or may not contain trigger text // so it's good to filter to just the 'above the fold' elements // and it should be atleast 100px from the top to ignore items in the toolbar, sometimes menu items like "Coming soon" exist - const elementsToScan = Array.from(document.getElementsByTagName('*')).filter(element => element.getBoundingClientRect().top + window.scrollY <= window.innerHeight && element.getBoundingClientRect().top + window.scrollY >= 100); + const elementsToScan = Array.from(document.getElementsByTagName('*')).filter(element => element.getBoundingClientRect().top + window.scrollY <= vh && element.getBoundingClientRect().top + window.scrollY >= 100); var elementText = ""; @@ -94,10 +96,8 @@ function isItemInStock() { if (elementText.length) { // try which ones could mean its in stock - for (let i = 0; i < negateOutOfStockRegexs.length; i++) { - if (negateOutOfStockRegexs_r[i].test(elementText)) { - return 'Possibly in stock'; - } + if (negateOutOfStockRegex.test(elementText)) { + return 'Possibly in stock'; } } }