amazon-subscriptions 1.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- amazon_subscriptions-1.0.1/.github/workflows/automerge-schedule.yml +43 -0
- amazon_subscriptions-1.0.1/.github/workflows/lint.yml +87 -0
- amazon_subscriptions-1.0.1/.github/workflows/mr-test.yml +73 -0
- amazon_subscriptions-1.0.1/.github/workflows/pr-conventional-title.yml +23 -0
- amazon_subscriptions-1.0.1/.github/workflows/publish.yml +39 -0
- amazon_subscriptions-1.0.1/.github/workflows/release-please.yml +21 -0
- amazon_subscriptions-1.0.1/.gitignore +16 -0
- amazon_subscriptions-1.0.1/.markdownlint-cli2.yaml +13 -0
- amazon_subscriptions-1.0.1/.release-please-manifest.json +3 -0
- amazon_subscriptions-1.0.1/.yamllint.yml +17 -0
- amazon_subscriptions-1.0.1/CHANGELOG.md +39 -0
- amazon_subscriptions-1.0.1/CONTRIBUTING.md +189 -0
- amazon_subscriptions-1.0.1/LICENSE +21 -0
- amazon_subscriptions-1.0.1/PKG-INFO +314 -0
- amazon_subscriptions-1.0.1/README.md +301 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/__init__.py +0 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/cli.py +180 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/client.py +485 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/dates.py +63 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/exceptions.py +21 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/locales/__init__.py +68 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/locales/base.py +189 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/locales/en.py +65 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/models.py +163 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/parse/__init__.py +28 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/parse/acp.py +41 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/parse/deliveries.py +199 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/parse/detail.py +27 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/parse/matching.py +100 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/parse/product.py +202 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/parse/selectors.py +64 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/parse/subscriptions.py +85 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/parse/util.py +37 -0
- amazon_subscriptions-1.0.1/amazon_subscriptions/serialize.py +29 -0
- amazon_subscriptions-1.0.1/lychee.toml +17 -0
- amazon_subscriptions-1.0.1/mise.toml +28 -0
- amazon_subscriptions-1.0.1/pyproject.toml +48 -0
- amazon_subscriptions-1.0.1/release-please-config.json +38 -0
- amazon_subscriptions-1.0.1/renovate.json +12 -0
- amazon_subscriptions-1.0.1/scripts/anonymize_fixtures.py +377 -0
- amazon_subscriptions-1.0.1/scripts/extract_product_fixture.py +249 -0
- amazon_subscriptions-1.0.1/scripts/privacy_scan.py +161 -0
- amazon_subscriptions-1.0.1/scripts/vendor/README.md +8 -0
- amazon_subscriptions-1.0.1/scripts/vendor/anonymize_test_resources.py +559 -0
- amazon_subscriptions-1.0.1/tests/__init__.py +0 -0
- amazon_subscriptions-1.0.1/tests/conftest.py +222 -0
- amazon_subscriptions-1.0.1/tests/fixtures/de/deliveries-page-2.html +1 -0
- amazon_subscriptions-1.0.1/tests/fixtures/de/delivery-1.html +306 -0
- amazon_subscriptions-1.0.1/tests/fixtures/de/delivery-2.html +304 -0
- amazon_subscriptions-1.0.1/tests/fixtures/de/landing.html +309 -0
- amazon_subscriptions-1.0.1/tests/fixtures/de/product-alternative-offer.html +54 -0
- amazon_subscriptions-1.0.1/tests/fixtures/de/product-no-buybox.html +8 -0
- amazon_subscriptions-1.0.1/tests/fixtures/de/product-rrp.html +78 -0
- amazon_subscriptions-1.0.1/tests/fixtures/de/product-single-offer.html +15 -0
- amazon_subscriptions-1.0.1/tests/fixtures/de/product-unavailable.html +12 -0
- amazon_subscriptions-1.0.1/tests/fixtures/de/product-was.html +80 -0
- amazon_subscriptions-1.0.1/tests/fixtures/de/product.html +71 -0
- amazon_subscriptions-1.0.1/tests/fixtures/de/subscription-detail.html +293 -0
- amazon_subscriptions-1.0.1/tests/test_cli.py +228 -0
- amazon_subscriptions-1.0.1/tests/test_client.py +537 -0
- amazon_subscriptions-1.0.1/tests/test_dates.py +46 -0
- amazon_subscriptions-1.0.1/tests/test_locale.py +199 -0
- amazon_subscriptions-1.0.1/tests/test_matching.py +132 -0
- amazon_subscriptions-1.0.1/tests/test_parse_deliveries.py +163 -0
- amazon_subscriptions-1.0.1/tests/test_parse_detail.py +15 -0
- amazon_subscriptions-1.0.1/tests/test_parse_product.py +146 -0
- amazon_subscriptions-1.0.1/tests/test_parse_subscriptions.py +111 -0
- amazon_subscriptions-1.0.1/tests/test_serialize.py +46 -0
- amazon_subscriptions-1.0.1/uv.lock +952 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Schedule Release Automerge
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
schedule:
|
|
6
|
+
- cron: '0 9 * * 1' # Every Monday at 9am UTC
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
automerge:
|
|
11
|
+
if: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- name: Enable auto-merge on release-please PR
|
|
15
|
+
env:
|
|
16
|
+
GH_TOKEN: ${{ secrets.OVERRIDE_TOKEN }}
|
|
17
|
+
run: |
|
|
18
|
+
PRS=$(gh pr list \
|
|
19
|
+
--repo "${{ github.repository }}" \
|
|
20
|
+
--label "autorelease: pending" \
|
|
21
|
+
--base "${{ github.event.repository.default_branch }}" \
|
|
22
|
+
--state open \
|
|
23
|
+
--json number)
|
|
24
|
+
|
|
25
|
+
PR_COUNT=$(printf '%s' "$PRS" | jq 'length')
|
|
26
|
+
|
|
27
|
+
if [ "$PR_COUNT" -gt 1 ]; then
|
|
28
|
+
echo "Expected at most one open release-please PR, found $PR_COUNT"
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
PR=$(printf '%s' "$PRS" | jq -r '.[0].number // empty')
|
|
33
|
+
|
|
34
|
+
if [ -n "$PR" ]; then
|
|
35
|
+
# Auto-merge waits for the branch to be up to date with main, and release-please leaves it
|
|
36
|
+
# behind when the new commits don't change the release notes.
|
|
37
|
+
gh pr update-branch "$PR" --repo "${{ github.repository }}"
|
|
38
|
+
gh pr merge "$PR" --auto --squash --delete-branch \
|
|
39
|
+
--repo "${{ github.repository }}"
|
|
40
|
+
echo "Auto-merge enabled on PR #$PR"
|
|
41
|
+
else
|
|
42
|
+
echo "No open release-please PR found"
|
|
43
|
+
fi
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Lint
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
# Called by mr-test.yml, so its required job covers the linters too.
|
|
6
|
+
workflow_call:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
# Python is linted in mr-test.yml. Each linter here is its own job so a failure names the file type. The linters'
|
|
12
|
+
# versions are in mise.toml, where Renovate updates them.
|
|
13
|
+
jobs:
|
|
14
|
+
markdown:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
18
|
+
with:
|
|
19
|
+
persist-credentials: false
|
|
20
|
+
|
|
21
|
+
# Files and rules come from .markdownlint-cli2.yaml, whose globs add to the action's default of the root files.
|
|
22
|
+
- uses: DavidAnson/markdownlint-cli2-action@21c1be1b93ad9ed58fa840aacc3f279cde2a72ff # v24.2.0
|
|
23
|
+
|
|
24
|
+
markdown-links:
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
28
|
+
with:
|
|
29
|
+
persist-credentials: false
|
|
30
|
+
|
|
31
|
+
- name: Restore lychee cache
|
|
32
|
+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
33
|
+
with:
|
|
34
|
+
path: .lycheecache
|
|
35
|
+
key: lychee-${{ github.sha }}
|
|
36
|
+
restore-keys: lychee-
|
|
37
|
+
|
|
38
|
+
- uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0
|
|
39
|
+
with:
|
|
40
|
+
install_args: lychee
|
|
41
|
+
|
|
42
|
+
- name: lychee
|
|
43
|
+
env:
|
|
44
|
+
# Authenticated requests to GitHub links are not rate limited as quickly.
|
|
45
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
46
|
+
run: lychee --config lychee.toml --no-progress README.md
|
|
47
|
+
|
|
48
|
+
yaml:
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
52
|
+
with:
|
|
53
|
+
persist-credentials: false
|
|
54
|
+
|
|
55
|
+
- uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0
|
|
56
|
+
with:
|
|
57
|
+
install_args: python uv pypi:yamllint
|
|
58
|
+
|
|
59
|
+
# Files and rules come from .yamllint.yml.
|
|
60
|
+
- name: yamllint
|
|
61
|
+
run: yamllint --strict --format github .
|
|
62
|
+
|
|
63
|
+
actions:
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
steps:
|
|
66
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
67
|
+
with:
|
|
68
|
+
persist-credentials: false
|
|
69
|
+
|
|
70
|
+
- uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0
|
|
71
|
+
with:
|
|
72
|
+
install_args: actionlint shellcheck
|
|
73
|
+
|
|
74
|
+
# Also runs shellcheck over every `run:` block.
|
|
75
|
+
- name: actionlint
|
|
76
|
+
run: actionlint
|
|
77
|
+
|
|
78
|
+
json:
|
|
79
|
+
runs-on: ubuntu-latest
|
|
80
|
+
steps:
|
|
81
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
82
|
+
with:
|
|
83
|
+
persist-credentials: false
|
|
84
|
+
|
|
85
|
+
# Syntax only: release-please owns the formatting of the files it writes.
|
|
86
|
+
- name: jq
|
|
87
|
+
run: git ls-files -z '*.json' | xargs -0 -n1 jq empty
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: MR Test
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
push:
|
|
7
|
+
branches:
|
|
8
|
+
- main
|
|
9
|
+
pull_request:
|
|
10
|
+
branches:
|
|
11
|
+
- main
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
concurrency:
|
|
17
|
+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
18
|
+
cancel-in-progress: true
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
test:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
timeout-minutes: 15
|
|
24
|
+
strategy:
|
|
25
|
+
fail-fast: false
|
|
26
|
+
matrix:
|
|
27
|
+
# The oldest supported version (requires-python) and the one of mise.toml.
|
|
28
|
+
python: ["3.10", "3.14"]
|
|
29
|
+
env:
|
|
30
|
+
UV_PYTHON: ${{ matrix.python }}
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
33
|
+
with:
|
|
34
|
+
persist-credentials: false
|
|
35
|
+
|
|
36
|
+
- uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0
|
|
37
|
+
|
|
38
|
+
- name: Install dependencies
|
|
39
|
+
run: mise run install
|
|
40
|
+
|
|
41
|
+
- name: Lint
|
|
42
|
+
run: mise run lint
|
|
43
|
+
|
|
44
|
+
- name: Test
|
|
45
|
+
run: mise run test
|
|
46
|
+
|
|
47
|
+
build:
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
timeout-minutes: 10
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
52
|
+
with:
|
|
53
|
+
persist-credentials: false
|
|
54
|
+
|
|
55
|
+
- uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0
|
|
56
|
+
|
|
57
|
+
- name: Build
|
|
58
|
+
run: uv build
|
|
59
|
+
|
|
60
|
+
lint:
|
|
61
|
+
uses: ./.github/workflows/lint.yml
|
|
62
|
+
|
|
63
|
+
# One status check for the ruleset, whatever jobs are added later.
|
|
64
|
+
required:
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
needs: [test, build, lint]
|
|
67
|
+
if: always()
|
|
68
|
+
steps:
|
|
69
|
+
- name: Check result
|
|
70
|
+
run: |
|
|
71
|
+
if [[ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Conventional PR Title
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
pull_request:
|
|
6
|
+
types:
|
|
7
|
+
- opened
|
|
8
|
+
- reopened
|
|
9
|
+
- edited
|
|
10
|
+
- synchronize
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
pull-requests: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
# Required by the ruleset. Squash merges use the PR title as the commit
|
|
17
|
+
# message, which release-please reads.
|
|
18
|
+
conventional-pr-title:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
|
|
22
|
+
env:
|
|
23
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Publish to PyPI
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
release:
|
|
7
|
+
types: [published]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
timeout-minutes: 10
|
|
16
|
+
environment:
|
|
17
|
+
name: pypi
|
|
18
|
+
url: https://pypi.org/p/amazon-subscriptions
|
|
19
|
+
permissions:
|
|
20
|
+
id-token: write
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
23
|
+
with:
|
|
24
|
+
persist-credentials: false
|
|
25
|
+
|
|
26
|
+
- name: Refuse dependencies by URL
|
|
27
|
+
run: |
|
|
28
|
+
if grep -n ' @ git+' pyproject.toml; then
|
|
29
|
+
echo "::error file=pyproject.toml::PyPI rejects dependencies by URL. Require a released version instead."
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
- uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0
|
|
34
|
+
|
|
35
|
+
- name: Build
|
|
36
|
+
run: uv build
|
|
37
|
+
|
|
38
|
+
# Trusted publishing: PyPI trusts this workflow and the pypi environment, no token needed.
|
|
39
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: release-please
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
push:
|
|
7
|
+
branches:
|
|
8
|
+
- main
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
pull-requests: write
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
release-please:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
# OVERRIDE_TOKEN is required: a release created with GITHUB_TOKEN would not trigger publish.yml.
|
|
19
|
+
- uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
|
|
20
|
+
with:
|
|
21
|
+
token: ${{ secrets.OVERRIDE_TOKEN }}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: default
|
|
3
|
+
|
|
4
|
+
ignore: |
|
|
5
|
+
.venv/
|
|
6
|
+
.claude/
|
|
7
|
+
|
|
8
|
+
rules:
|
|
9
|
+
# `on:` is a workflow key, not a boolean.
|
|
10
|
+
truthy:
|
|
11
|
+
check-keys: false
|
|
12
|
+
# Workflow comments and step commands explain themselves on one line.
|
|
13
|
+
line-length:
|
|
14
|
+
max: 160
|
|
15
|
+
# SHA-pinned actions carry their version as `@sha # vX.Y.Z`.
|
|
16
|
+
comments:
|
|
17
|
+
min-spaces-from-content: 1
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [1.0.1](https://github.com/udondan/amazon-subscriptions/compare/v1.0.0...v1.0.1) (2026-10-28)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Documentation
|
|
7
|
+
|
|
8
|
+
* install from PyPI, where every release is published ([#22](https://github.com/udondan/amazon-subscriptions/issues/22)) ([f56bba4](https://github.com/udondan/amazon-subscriptions/commit/f56bba404a031c7476a8887f9b45e9c4448bebf7))
|
|
9
|
+
|
|
10
|
+
## [1.0.0](https://github.com/udondan/amazon-subscriptions/compare/v0.1.0...v1.0.0) (2026-10-28)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### ⚠ BREAKING CHANGES
|
|
14
|
+
|
|
15
|
+
* the English instead of the German pages are read, and the configured language package is ignored. The list price type "uvp" is now "rrp". amazon-orders comes from PyPI, version 5.2.0 or a later 5.x, which keeps the session of each site in its own file (e.g. cookies.amazon.de.json); a session stored in cookies.json is still used. Sites other than amazon.de are no longer rejected if they offer English.
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* parse the English Subscribe & Save pages ([#20](https://github.com/udondan/amazon-subscriptions/issues/20)) ([f6361e4](https://github.com/udondan/amazon-subscriptions/commit/f6361e4fecd94247b5b4f13ad34ce57749251b94))
|
|
20
|
+
|
|
21
|
+
## 0.1.0 (2026-10-13)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Features
|
|
25
|
+
|
|
26
|
+
* attribute product page prices to their offer and add list price type and alternative offer ([#5](https://github.com/udondan/amazon-subscriptions/issues/5)) ([07550d4](https://github.com/udondan/amazon-subscriptions/commit/07550d442b58ac87edddbb92bb5ff41bc0580a4d))
|
|
27
|
+
* read Subscribe & Save subscriptions and upcoming deliveries ([#1](https://github.com/udondan/amazon-subscriptions/issues/1)) ([0bdb700](https://github.com/udondan/amazon-subscriptions/commit/0bdb700d61ccac665897278de7c21d25a2697597))
|
|
28
|
+
* read the ASIN of backup products and, with --with-prices, regular, list and unit prices ([#3](https://github.com/udondan/amazon-subscriptions/issues/3)) ([22d96f0](https://github.com/udondan/amazon-subscriptions/commit/22d96f0a7e128b0a12a552e4ec974edf7816c9f9))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### Bug Fixes
|
|
32
|
+
|
|
33
|
+
* read the seller name of alternative offers from the offer row and add seller_id ([#6](https://github.com/udondan/amazon-subscriptions/issues/6)) ([0accfb9](https://github.com/udondan/amazon-subscriptions/commit/0accfb9d4ce017e0a3fe50e47d2d29d709fd1b38))
|
|
34
|
+
* return a partial result instead of failing when a delivery or product page cannot be read ([#7](https://github.com/udondan/amazon-subscriptions/issues/7)) ([3a3f08f](https://github.com/udondan/amazon-subscriptions/commit/3a3f08f8d797a0472b6add240c3ae454bb5755d2))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### Dependencies
|
|
38
|
+
|
|
39
|
+
* **deps:** update dependency ruff to v0.16.9 ([#9](https://github.com/udondan/amazon-subscriptions/issues/9)) ([ff25334](https://github.com/udondan/amazon-subscriptions/commit/ff25334c39d4e21857432ae0b9e83ddbfbcadf92))
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Pages of another Amazon site
|
|
4
|
+
|
|
5
|
+
amazon-subscriptions reads the English Subscribe & Save pages of every Amazon site that offers English, but only
|
|
6
|
+
amazon.de has been tested. The texts, the markup or the URLs of another site may differ. To find out, and to keep it
|
|
7
|
+
working, anonymized pages of that site are needed as test fixtures. This guide shows how to capture and anonymize them
|
|
8
|
+
step by step. You need a Subscribe & Save account on that site, and no knowledge of the code.
|
|
9
|
+
|
|
10
|
+
> [!WARNING]
|
|
11
|
+
> The raw pages you capture contain your name, your address, your subscriptions, order numbers, IDs and session
|
|
12
|
+
> tokens. Never share them, attach them or commit them unchanged. Only share the anonymized pages, after checking
|
|
13
|
+
> them yourself (step 5), and delete the raw pages when you are done (step 8).
|
|
14
|
+
|
|
15
|
+
The examples use `amazon.co.uk`. Replace it with your site, and `co.uk` with the part after `amazon.`, e.g. `com`,
|
|
16
|
+
`ca` or `com.au`.
|
|
17
|
+
|
|
18
|
+
### 1. Install
|
|
19
|
+
|
|
20
|
+
You need [git](https://git-scm.com/) and [uv](https://docs.astral.sh/uv/getting-started/installation/), which
|
|
21
|
+
installs Python itself if needed.
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
git clone https://github.com/udondan/amazon-subscriptions
|
|
25
|
+
cd amazon-subscriptions
|
|
26
|
+
uv sync
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
All commands below are run in this directory.
|
|
30
|
+
|
|
31
|
+
### 2. Log in
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
uv run amazon-subscriptions --domain amazon.co.uk login
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This asks for your e-mail address, your password and, if enabled, a one-time password. They are sent to Amazon only
|
|
38
|
+
and never stored. Only the session cookies are kept, in `~/.config/amazonorders/cookies.amazon.co.uk.json`, so the
|
|
39
|
+
next commands do not need to log in. If you already have a session for this site, `login` signs out of it first.
|
|
40
|
+
|
|
41
|
+
Check that it worked:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
uv run amazon-subscriptions --domain amazon.co.uk check-session
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 3. Capture the pages
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
uv run amazon-subscriptions --domain amazon.co.uk --debug --output-dir raw list --with-prices
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This only reads pages, it never changes a subscription. It writes every page it loads into the new directory `raw`:
|
|
54
|
+
the Subscribe & Save overview, its further pages, one page per upcoming delivery and the product page of each
|
|
55
|
+
subscription. Together, these are all pages that `list` and `upcoming` read, so you do not need to run `upcoming` as
|
|
56
|
+
well. The requests are logged to the terminal.
|
|
57
|
+
|
|
58
|
+
If the command fails or prints warnings, keep its output: it tells what is not understood yet (step 7). If it ends
|
|
59
|
+
with an error before writing any page, open an issue with the error message instead.
|
|
60
|
+
|
|
61
|
+
Always capture into a new, empty directory. If you capture again, delete `raw` first (`rm -rf raw`).
|
|
62
|
+
|
|
63
|
+
### 4. Anonymize the pages
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
uv run python scripts/anonymize_fixtures.py --from-debug-dir raw --output-dir tests/fixtures/co.uk \
|
|
67
|
+
--replace "Jane Doe=Test Person" --sensitive "Jane" --sensitive "Doe" --sensitive "High Street"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Replace `Jane Doe` with your name as Amazon shows it, and add a `--sensitive` for each part of your name and address
|
|
71
|
+
that must not appear in the result. Each option can be given several times.
|
|
72
|
+
|
|
73
|
+
The script sorts the pages by their content and writes `landing.html`, `delivery-1.html`, `delivery-2.html` and so
|
|
74
|
+
on, and, if the lists have further pages, `subscriptions-page-2.html` and `deliveries-page-2.html` to
|
|
75
|
+
`tests/fixtures/co.uk/`. It replaces:
|
|
76
|
+
|
|
77
|
+
- product titles with `Product 01`, `Product 02` and so on (followed by filler words), also in links,
|
|
78
|
+
- ASINs, subscription IDs, delivery IDs and session IDs with fake values,
|
|
79
|
+
- addresses, tokens and the values of your session cookies,
|
|
80
|
+
- every text given with `--replace`.
|
|
81
|
+
|
|
82
|
+
It does not replace free text, such as your name in a greeting or a message, and nothing the page shows in an
|
|
83
|
+
unknown place. Prices, quantities, intervals and dates are kept.
|
|
84
|
+
|
|
85
|
+
Afterwards, it checks every page for the sensitive values it has seen. If one is left, it prints `LEAK` and exits
|
|
86
|
+
with status 1. It also prints a `WARN` line for text that looks like personal data: e-mail addresses, phone numbers,
|
|
87
|
+
order and session numbers, customer IDs, names after "Hello" or "Deliver to", postcodes and product names in links.
|
|
88
|
+
The value itself is masked. Look at each of them; if it is personal, add a `--replace` or `--sensitive` for it, delete
|
|
89
|
+
`tests/fixtures/co.uk` and run the command again.
|
|
90
|
+
|
|
91
|
+
At the end, it prints a command for the product pages, like this:
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
python scripts/extract_product_fixture.py --output-dir tests/fixtures/co.uk --asin-map raw/asin-map.json \
|
|
95
|
+
raw/B0XXXXXXXX_0.html raw/B0YYYYYYYY_0.html …
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Run it with `uv run` in front:
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
uv run python scripts/extract_product_fixture.py --output-dir tests/fixtures/co.uk --asin-map raw/asin-map.json \
|
|
102
|
+
raw/B0XXXXXXXX_0.html raw/B0YYYYYYYY_0.html …
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
It reduces each product page to its title and price blocks, replaces titles, ASINs, seller names and IDs, and
|
|
106
|
+
writes it as `product-<fake ASIN>.html`. For each page, it prints what the parser reads from it:
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
ok tests/fixtures/co.uk/product-B0TEST0024.html: price 8.69, list price 11.99 (rrp, from buybox), unit price 57.93 per l
|
|
110
|
+
ok tests/fixtures/co.uk/product-B0TEST0046.html: no price
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Keep about five pages that show different things, e.g. a recommended retail price (`rrp`), a former price (`was`),
|
|
114
|
+
a product without price (not available), one with an alternative offer, and one with a plain price. Delete the
|
|
115
|
+
others. You may rename the ones you keep to say what they show, e.g. `product-rrp.html`.
|
|
116
|
+
|
|
117
|
+
### 5. Check the pages yourself
|
|
118
|
+
|
|
119
|
+
The scripts only replace what they know about. Search the anonymized pages for your own data, with every value you
|
|
120
|
+
can think of: your first and last name, street, postcode, town, e-mail address and phone number.
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
grep -rniE "jane|doe|high street|SW1A 1AA|jane@example.com|7700 900123" tests/fixtures/co.uk/
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Search for order numbers too; `000-0000000-0000000` is the placeholder of the scripts. This must find nothing:
|
|
127
|
+
|
|
128
|
+
```sh
|
|
129
|
+
grep -rnoE "[0-9]{3}-[0-9]{7}-[0-9]{7}" tests/fixtures/co.uk/ | grep -v 000-0000000-0000000
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Then run the scan of step 4 once more on all pages, including the product pages:
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
uv run python scripts/privacy_scan.py tests/fixtures/co.uk/*.html
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
It ends with "Nothing found" or lists `WARN` lines as in step 4. If something personal is left, add a `--replace` or
|
|
139
|
+
`--sensitive` for it in step 4, delete `tests/fixtures/co.uk` and run steps 4 and 5 again. For a product page, you
|
|
140
|
+
can also delete it.
|
|
141
|
+
|
|
142
|
+
### 6. Share the pages
|
|
143
|
+
|
|
144
|
+
Either way, describe in a few words what your subscriptions look like (e.g. "one paused subscription, one with a
|
|
145
|
+
backup product") and include the output and warnings of step 3, after checking them for personal data as well.
|
|
146
|
+
|
|
147
|
+
- **Pull request**: [Fork the repository](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo),
|
|
148
|
+
commit only the directory `tests/fixtures/co.uk/` and open a pull request. Its title follows
|
|
149
|
+
[Conventional Commits](https://www.conventionalcommits.org/), e.g. `test: add pages of amazon.co.uk`.
|
|
150
|
+
- **Issue**: If you do not want to open a pull request, pack the directory (`zip -r co.uk.zip tests/fixtures/co.uk`)
|
|
151
|
+
and attach the archive to a [new issue](https://github.com/udondan/amazon-subscriptions/issues/new).
|
|
152
|
+
|
|
153
|
+
### 7. Unknown texts, formats and markup
|
|
154
|
+
|
|
155
|
+
If step 3 printed warnings or the result misses values, the site shows something that is not known yet. You do not
|
|
156
|
+
need to fix it yourself; the pages from step 6 are enough to do it. If you want to, this is where it goes:
|
|
157
|
+
|
|
158
|
+
- **Texts** (e.g. "Next delivery:", "Arriving", "RRP", status and alert texts) and **date and amount formats**:
|
|
159
|
+
`amazon_subscriptions/locales/en.py`. The `TODO` comments there name the texts that are not known yet for any site.
|
|
160
|
+
Only take texts from real pages, never guess them.
|
|
161
|
+
- **Currency**: `SITE_CURRENCIES` in `amazon_subscriptions/locales/__init__.py`.
|
|
162
|
+
- **Markup**: the CSS selectors in `amazon_subscriptions/parse/selectors.py`.
|
|
163
|
+
- **URLs**: the allow-list in `amazon_subscriptions/client.py`. Only add pages that are read; never add a URL that
|
|
164
|
+
changes a subscription.
|
|
165
|
+
|
|
166
|
+
Add tests in `tests/` that read your fixtures, and run them with:
|
|
167
|
+
|
|
168
|
+
```sh
|
|
169
|
+
uv run ruff check .
|
|
170
|
+
uv run ruff format --check .
|
|
171
|
+
uv run mypy
|
|
172
|
+
uv run pytest
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
(With [mise](https://mise.jdx.dev/), `mise run lint` and `mise run test` do the same.)
|
|
176
|
+
|
|
177
|
+
### 8. Clean up
|
|
178
|
+
|
|
179
|
+
Delete the raw pages, and the pages that could not be read, if any were saved:
|
|
180
|
+
|
|
181
|
+
```sh
|
|
182
|
+
rm -rf raw ~/.cache/amazon-subscriptions/failed
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
To end the session as well:
|
|
186
|
+
|
|
187
|
+
```sh
|
|
188
|
+
uv run amazon-subscriptions --domain amazon.co.uk logout
|
|
189
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Daniel Schroeder
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|