amazon-orders 4.4.4__tar.gz → 4.4.6__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_orders-4.4.4 → amazon_orders-4.4.6}/CHANGELOG.md +14 -1
- {amazon_orders-4.4.4/amazon_orders.egg-info → amazon_orders-4.4.6}/PKG-INFO +2 -2
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/README.md +1 -1
- {amazon_orders-4.4.4 → amazon_orders-4.4.6/amazon_orders.egg-info}/PKG-INFO +2 -2
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/__init__.py +1 -1
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/cli.py +2 -2
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/contrib/browser/playwright.py +58 -13
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/pyproject.toml +1 -1
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/LICENSE +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/MANIFEST.in +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazon_orders.egg-info/SOURCES.txt +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazon_orders.egg-info/dependency_links.txt +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazon_orders.egg-info/entry_points.txt +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazon_orders.egg-info/requires.txt +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazon_orders.egg-info/top_level.txt +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/banner.txt +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/conf.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/constants.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/contrib/__init__.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/contrib/browser/__init__.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/contrib/waf/__init__.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/contrib/waf/anticaptcha.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/contrib/waf/base.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/contrib/waf/capsolver.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/contrib/waf/twocaptcha.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/entity/__init__.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/entity/item.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/entity/order.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/entity/parsable.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/entity/recipient.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/entity/seller.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/entity/shipment.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/entity/transaction.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/exception.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/forms.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/orders.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/selectors.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/session.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/transactions.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/amazonorders/util.py +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/setup.cfg +0 -0
- {amazon_orders-4.4.4 → amazon_orders-4.4.6}/tests/testcase.py +0 -0
|
@@ -4,7 +4,20 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
-
## [Unreleased](https://github.com/alexdlaird/amazon-orders/compare/4.4.
|
|
7
|
+
## [Unreleased](https://github.com/alexdlaird/amazon-orders/compare/4.4.6...HEAD)
|
|
8
|
+
|
|
9
|
+
## [4.4.6](https://github.com/alexdlaird/amazon-orders/compare/4.4.5...4.4.6) - 2026-07-20
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- `PlaywrightManualWafForm` alongside `PlaywrightAcicForm` now opens a visible browser to solve ACIC-embedded challenges manually.
|
|
14
|
+
|
|
15
|
+
## [4.4.5](https://github.com/alexdlaird/amazon-orders/compare/4.4.4...4.4.5) - 2026-07-15
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- CLI `history` crash when using `--last-30-days` or `--last-3-months`.
|
|
20
|
+
- CLI `history --start-index` now validates non-numeric input.
|
|
8
21
|
|
|
9
22
|
## [4.4.4](https://github.com/alexdlaird/amazon-orders/compare/4.4.3...4.4.4) - 2026-07-07
|
|
10
23
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: amazon-orders
|
|
3
|
-
Version: 4.4.
|
|
3
|
+
Version: 4.4.6
|
|
4
4
|
Summary: A Python library (and CLI) for Amazon order history
|
|
5
5
|
Author-email: Alex Laird <contact@alexlaird.com>
|
|
6
6
|
Maintainer-email: Alex Laird <contact@alexlaird.com>
|
|
@@ -72,7 +72,7 @@ Requires-Dist: sphinx-sitemap; extra == "docs"
|
|
|
72
72
|
Requires-Dist: sphinxext-opengraph; extra == "docs"
|
|
73
73
|
Dynamic: license-file
|
|
74
74
|
|
|
75
|
-
<p align="center"><img alt="amazon-orders - A Python library (and CLI) for Amazon order history" src="https://amazon-orders.readthedocs.io/_images/logo.png" /></p>
|
|
75
|
+
<p align="center"><img alt="amazon-orders - A Python library (and CLI) for Amazon order history" src="https://amazon-orders.readthedocs.io/en/latest/_images/logo.png" /></p>
|
|
76
76
|
|
|
77
77
|
[](https://pypi.org/project/amazon-orders)
|
|
78
78
|
[](https://pypi.org/project/amazon-orders)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<p align="center"><img alt="amazon-orders - A Python library (and CLI) for Amazon order history" src="https://amazon-orders.readthedocs.io/_images/logo.png" /></p>
|
|
1
|
+
<p align="center"><img alt="amazon-orders - A Python library (and CLI) for Amazon order history" src="https://amazon-orders.readthedocs.io/en/latest/_images/logo.png" /></p>
|
|
2
2
|
|
|
3
3
|
[](https://pypi.org/project/amazon-orders)
|
|
4
4
|
[](https://pypi.org/project/amazon-orders)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: amazon-orders
|
|
3
|
-
Version: 4.4.
|
|
3
|
+
Version: 4.4.6
|
|
4
4
|
Summary: A Python library (and CLI) for Amazon order history
|
|
5
5
|
Author-email: Alex Laird <contact@alexlaird.com>
|
|
6
6
|
Maintainer-email: Alex Laird <contact@alexlaird.com>
|
|
@@ -72,7 +72,7 @@ Requires-Dist: sphinx-sitemap; extra == "docs"
|
|
|
72
72
|
Requires-Dist: sphinxext-opengraph; extra == "docs"
|
|
73
73
|
Dynamic: license-file
|
|
74
74
|
|
|
75
|
-
<p align="center"><img alt="amazon-orders - A Python library (and CLI) for Amazon order history" src="https://amazon-orders.readthedocs.io/_images/logo.png" /></p>
|
|
75
|
+
<p align="center"><img alt="amazon-orders - A Python library (and CLI) for Amazon order history" src="https://amazon-orders.readthedocs.io/en/latest/_images/logo.png" /></p>
|
|
76
76
|
|
|
77
77
|
[](https://pypi.org/project/amazon-orders)
|
|
78
78
|
[](https://pypi.org/project/amazon-orders)
|
|
@@ -119,7 +119,7 @@ def amazon_orders_cli(ctx: Context,
|
|
|
119
119
|
help="Get Order history for the last 30 days.")
|
|
120
120
|
@click.option("--last-3-months", "last_3_months", is_flag=True, default=False,
|
|
121
121
|
help="Get Order history for the past 3 months.")
|
|
122
|
-
@click.option("--start-index",
|
|
122
|
+
@click.option("--start-index", type=int,
|
|
123
123
|
help="The index of the Order from which to start fetching in the history.")
|
|
124
124
|
@click.option("--single-page", is_flag=True, default=False,
|
|
125
125
|
help="Only one page should be fetched.")
|
|
@@ -147,7 +147,7 @@ def history(ctx: Context,
|
|
|
147
147
|
order_filter = kwargs["order_filter"]
|
|
148
148
|
|
|
149
149
|
exclusive_flags = [year, last_3_months, last_30_days]
|
|
150
|
-
if
|
|
150
|
+
if sum(1 for item in exclusive_flags if item) > 1:
|
|
151
151
|
ctx.fail("Only one of --last-30-days, --last-3-months, or --year may be used at a time.")
|
|
152
152
|
|
|
153
153
|
# Determine time filter
|
|
@@ -19,6 +19,7 @@ from amazonorders.forms import AuthForm
|
|
|
19
19
|
from amazonorders.util import AmazonSessionResponse
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
22
|
+
from amazonorders.contrib.waf.base import AwsWafForm
|
|
22
23
|
from amazonorders.session import AmazonSession
|
|
23
24
|
|
|
24
25
|
logger = logging.getLogger(__name__)
|
|
@@ -44,6 +45,10 @@ class PlaywrightAuthForm(AuthForm):
|
|
|
44
45
|
#: Whether to launch the browser in headless mode. Defaults to ``True``.
|
|
45
46
|
#: Set to ``False`` in subclasses that require user interaction.
|
|
46
47
|
self.headless: bool = True
|
|
48
|
+
#: Whether this form solves challenges by handing off to a human in a visible
|
|
49
|
+
#: browser window (as opposed to an automated third-party solver). Forms with
|
|
50
|
+
#: this set act as the free, manual solver for an embedded ACIC challenge.
|
|
51
|
+
self.manual: bool = False
|
|
47
52
|
|
|
48
53
|
def fill_form(self,
|
|
49
54
|
additional_attrs: Optional[Dict[str, Any]] = None) -> None:
|
|
@@ -197,10 +202,16 @@ class PlaywrightAuthForm(AuthForm):
|
|
|
197
202
|
class PlaywrightAcicForm(PlaywrightAuthForm):
|
|
198
203
|
"""
|
|
199
204
|
Handles Amazon's ACIC (Amazon Challenge and Identity Component) JavaScript challenge
|
|
200
|
-
by running it in a headless browser. If an embedded AWS WAF challenge
|
|
201
|
-
the ACIC page, it will be solved automatically using the first
|
|
205
|
+
by running it in a headless browser. If an embedded AWS WAF challenge or visual grid
|
|
206
|
+
Puzzle is present on the ACIC page, it will be solved automatically using the first
|
|
202
207
|
:class:`~amazonorders.contrib.waf.base.AwsWafForm` found in ``auth_forms_classes``.
|
|
203
208
|
|
|
209
|
+
If no automated solver is registered but a manual solver
|
|
210
|
+
(:class:`~amazonorders.contrib.browser.playwright.PlaywrightManualWafForm`) is, a
|
|
211
|
+
**visible** browser window is opened instead so the user can solve the embedded
|
|
212
|
+
challenge themselves, for free. An automated solver takes precedence when both are
|
|
213
|
+
registered.
|
|
214
|
+
|
|
204
215
|
Detects the challenge via the ``#aa-challenge-page-captcha-container`` element and
|
|
205
216
|
waits for navigation away from ``/ax/aaut/verify/ap/challenge``.
|
|
206
217
|
|
|
@@ -224,9 +235,50 @@ class PlaywrightAcicForm(PlaywrightAuthForm):
|
|
|
224
235
|
:return: ``True`` if an ACIC challenge was detected, ``False`` otherwise.
|
|
225
236
|
"""
|
|
226
237
|
self.amazon_session = amazon_session
|
|
227
|
-
|
|
238
|
+
detected = bool(parsed.select_one(self.config.selectors.ACIC_CHALLENGE_SELECTOR))
|
|
239
|
+
if detected:
|
|
240
|
+
self.headless = not self._manual_mode()
|
|
241
|
+
return detected
|
|
242
|
+
|
|
243
|
+
def _manual_mode(self) -> bool:
|
|
244
|
+
"""
|
|
245
|
+
Return ``True`` if the embedded challenge should be handed off to a human in a
|
|
246
|
+
visible browser window rather than an automated solver. This is the case when a
|
|
247
|
+
manual solver form is registered and no automated :class:`AwsWafForm` is; an
|
|
248
|
+
automated solver takes precedence when both are present, since it is
|
|
249
|
+
non-interactive.
|
|
250
|
+
"""
|
|
251
|
+
return self._find_manual_solver() is not None and self._find_waf_solver() is None
|
|
252
|
+
|
|
253
|
+
def _find_waf_solver(self) -> Optional["AwsWafForm"]:
|
|
254
|
+
if not self.amazon_session:
|
|
255
|
+
return None # pragma: no cover
|
|
256
|
+
from amazonorders.contrib.waf.base import AwsWafForm
|
|
257
|
+
return next(
|
|
258
|
+
(f for f in self.amazon_session.auth_forms if isinstance(f, AwsWafForm)),
|
|
259
|
+
None,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
def _find_manual_solver(self) -> Optional["PlaywrightAuthForm"]:
|
|
263
|
+
if not self.amazon_session:
|
|
264
|
+
return None # pragma: no cover
|
|
265
|
+
return next(
|
|
266
|
+
(f for f in self.amazon_session.auth_forms
|
|
267
|
+
if isinstance(f, PlaywrightAuthForm) and f.manual),
|
|
268
|
+
None,
|
|
269
|
+
)
|
|
228
270
|
|
|
229
271
|
def _on_challenge_page(self, page: Any, context: Any, output_dir: Optional[str]) -> None:
|
|
272
|
+
if self._manual_mode():
|
|
273
|
+
message = (
|
|
274
|
+
"Info: A browser window has opened — solve the challenge, then return "
|
|
275
|
+
"here when done."
|
|
276
|
+
)
|
|
277
|
+
logger.info(message)
|
|
278
|
+
if self.amazon_session:
|
|
279
|
+
self.amazon_session.io.echo(message)
|
|
280
|
+
return
|
|
281
|
+
|
|
230
282
|
max_solves = self.config.max_auth_attempts
|
|
231
283
|
solves = 0
|
|
232
284
|
while solves < max_solves:
|
|
@@ -281,11 +333,7 @@ class PlaywrightAcicForm(PlaywrightAuthForm):
|
|
|
281
333
|
if not self.amazon_session:
|
|
282
334
|
return False # pragma: no cover
|
|
283
335
|
|
|
284
|
-
|
|
285
|
-
waf_form = next(
|
|
286
|
-
(f for f in self.amazon_session.auth_forms if isinstance(f, AwsWafForm)),
|
|
287
|
-
None,
|
|
288
|
-
)
|
|
336
|
+
waf_form = self._find_waf_solver()
|
|
289
337
|
if not waf_form:
|
|
290
338
|
logger.debug("No AwsWafForm configured, skipping WAF challenge.")
|
|
291
339
|
return False
|
|
@@ -339,11 +387,7 @@ class PlaywrightAcicForm(PlaywrightAuthForm):
|
|
|
339
387
|
if not self.amazon_session:
|
|
340
388
|
return False # pragma: no cover
|
|
341
389
|
|
|
342
|
-
|
|
343
|
-
waf_form = next(
|
|
344
|
-
(f for f in self.amazon_session.auth_forms if isinstance(f, AwsWafForm)),
|
|
345
|
-
None,
|
|
346
|
-
)
|
|
390
|
+
waf_form = self._find_waf_solver()
|
|
347
391
|
if not waf_form:
|
|
348
392
|
logger.debug("No AwsWafForm configured, skipping Puzzle.")
|
|
349
393
|
return False
|
|
@@ -502,6 +546,7 @@ class PlaywrightManualWafForm(PlaywrightAuthForm):
|
|
|
502
546
|
config: AmazonOrdersConfig) -> None:
|
|
503
547
|
super().__init__(config)
|
|
504
548
|
self.headless = False
|
|
549
|
+
self.manual = True
|
|
505
550
|
|
|
506
551
|
def select_form(self,
|
|
507
552
|
amazon_session: "AmazonSession",
|
|
@@ -137,7 +137,7 @@ directory = "build/coverage"
|
|
|
137
137
|
[tool.flake8]
|
|
138
138
|
max-line-length = 119
|
|
139
139
|
statistics = true
|
|
140
|
-
exclude = "scripts/*,docs/*,venv/*,build/*,dist/*,.egg-info/*,.mypy_cache/*"
|
|
140
|
+
exclude = "scripts/*,docs/*,venv/*,build/*,dist/*,.egg-info/*,.mypy_cache/*,.claude/*"
|
|
141
141
|
|
|
142
142
|
[[tool.mypy.overrides]]
|
|
143
143
|
module = "amazoncaptcha.*"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|