mirror of
https://github.com/Second-Hand-Friends/kleinanzeigen-bot.git
synced 2026-07-09 12:41:05 +02:00
main
1044 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
36c76f64c9 |
fix(shipping): use state-based checkbox sync for REPLACE mode (#962)
## ℹ️ Description *Provide a concise summary of the changes introduced in this pull request.* - Link to the related issue(s): Issue #956 - This fixes a regression where `shipping_options` in REPLACE mode could invert selected carriers (for example `DHL_5` ending up with Hermes selected) because the code assumed all carrier checkboxes were pre-checked after clicking "Weiter". Live DOM verification showed this assumption is not stable across size groups. ## 📋 Changes Summary - Reworked `__set_shipping_options()` to use state-based checkbox synchronization for both REPLACE and MODIFY modes: - read each carrier checkbox `checked` state - compute target state from configured `shipping_options` - toggle only when current state differs from desired state - Added a focused regression test for issue #956: - `test_replace_mode_dom_verified_unchecked_defaults_select_wanted_carrier` - verifies REPLACE mode correctly selects wanted `DHL_002` and does not toggle unwanted `HERMES_003` when defaults are unchecked - Updated an existing shipping mapping test fixture to mock carrier checkbox lookups under the new state-based behavior. - No dependency, schema, or configuration changes were introduced. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Unified shipping carrier selection to a single state-based synchronization flow, improving consistency and reliability when toggling carrier options. * **Tests** * Expanded shipping-options tests with richer DOM mocks and a new regression test to verify correct carrier toggling behavior; removed an outdated unit test related to special-attribute string coercion. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
5391f4d37b |
chore: Update Python dependencies (#960)
✔ Update types-requests 2.33.0.20260402 -> 2.33.0.20260408 successful ✔ Update pytest 9.0.2 -> 9.0.3 successful Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
728acd48e6 |
fix(publish): reload form pages to prevent stale image state on retry (#961)
## ℹ️ Description This pull request fixes retry behavior when image uploads time out during publish and the next attempt inherits stale form image state. - Link to the related issue(s): Issue #958 - Describe the motivation and context for this change. - Retries should start from a clean publish/edit form state to avoid appending new uploads onto leftover images from a failed attempt. ## 📋 Changes Summary - Force `publish_ad` to open publish/edit pages with `reload_if_already_open = True` for both REPLACE and MODIFY flows. - Keep the existing thumbnail cleanup logic and polish its comment to reflect defensive pre-populated-form cleanup. - Extend the pre-submit timeout retryability test to cover both REPLACE and MODIFY and assert the forced-reload navigation contract. - Consolidate duplicate post-submit uncertainty tests into one parametrized test covering both `TimeoutError` and `ProtocolException`. - Remove one low-value brittle log-wording assertion from image-upload marker fallback tests while preserving behavior assertions. - No new dependencies or configuration changes. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Improvements** * Optimized page navigation behavior during ad creation and updates, improving handling when target pages are already open in the browser. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
727366faef |
fix(publish): rewrite shipping dialog selectors for redesigned DOM (#956) (#957)
## ℹ️ Description - Link to the related issue(s): Issue #956 - The shipping dialog on kleinanzeigen.de was completely redesigned, breaking all DOM selectors used by `__set_shipping_options()`. Radio buttons lost their stable IDs (`#radio-button-SMALL` etc.) and checkboxes lost their `data-testid` attributes — both now use React-generated IDs. The fix targets stable `value` attributes instead, which are API-level identifiers used by the site's JS. ## 📋 Changes Summary - Add shared shipping constants (`CARRIER_CODE_BY_OPTION`, `OPTION_NAME_BY_CARRIER_CODE`, `SIZE_INFO_BY_CARRIER_CODE`, `CARRIER_CODES_BY_SIZE`) to `ad_model.py` as single source of truth for both publishing and extraction - Rewrite `__set_shipping_options()` in `__init__.py` with new selectors: - Size radios: `input[@type="radio" and @value="SMALL|MEDIUM|LARGE"]` - Carrier checkboxes: `input[@type="checkbox" and @value="HERMES_001"]` etc. - Use `attrs.get("checked") is not None` consistently (matches existing pattern at line 2540) - Re-raise `TimeoutError` with descriptive translatable message instead of silently swallowing - Update `extract.py` to use shared `OPTION_NAME_BY_CARRIER_CODE` (DRY) - Add German translation for new error message - Update `docs/AD_CONFIGURATION.md` with shipping options reference table - Verified against live site: all 3 size groups (Klein/Mittel/Groß) and individual shipping pass ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test` — 990 passed, 4 skipped). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint` — 0 errors, 0 warnings). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added a Shipping Options Reference table, clarified size-group rules, tightened sell_directly requirements, and updated the example ad to use predefined shipping options. * **Bug Fixes** * Enforce that selected shipping options belong to a single package size. * Missing shipping options now raise an explicit error. * Improved dialog interaction, timeout handling, and reliability when configuring shipping. * **Tests** * Updated dialog tests, added coverage for selection modes and error cases, and removed obsolete async helper tests. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
ccfcf435ef |
fix(publish,download): support condition_s for categories with combobox instead of dialog (#955)
## ℹ️ Description - Link to the related issue(s): Issue #952 - In certain categories (e.g. 185/249 Modellbau), the condition field is rendered as a `<button role="combobox">` with id `modellbau.condition` instead of a dialog with radio buttons. Publishing failed because `__set_condition` raised `TimeoutError` for these categories, and the method was short-circuited with `continue` — skipping the generic XPath-based handler that already supports combobox controls. Downloading returned `special_attributes: {}` because `ad_attributes` metadata can be empty/missing for some ads, with no fallback to scrape the DOM. ## 📋 Changes Summary - Publishing (`__init__.py`): Catch `TimeoutError` from `__set_condition` and fall through to the generic special-attribute handler, which correctly handles `button[role=combobox]` controls (e.g. `modellbau.condition` in category 185/249). - Downloading (`extract.py`): Add DOM-based fallback in `_extract_special_attributes_from_ad_page` that scrapes `#viewad-details .addetailslist--detail` entries when `ad_attributes` is empty/missing, using `removesuffix` for robust label extraction. - i18n (`translations.de.yaml`): German translation for the new INFO-level fallback log message. - Tests: Added regression tests for both publishing fallback and DOM-based download extraction. Cleaned up duplicate and superficial mock-call-assertion tests (4 items per test-scout-cleanup). - Verified end-to-end against live site via `verify_dom_assumptions.py`: `set_result.ok: True`, `all_matched: True`, condition correctly set via `button#modellbau.condition` combobox. ### ⚙️ Type of Change - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Condition handling is more resilient: if the condition dialog times out, the flow logs the event and falls back to the generic handler instead of aborting. * **New Features** * DOM-based fallback extraction of item attributes when primary attribute data is missing, with mapping of common condition labels to internal values. * **Tests** * Added tests for DOM fallback extraction and condition-setting fallback behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
054dc5ea9d |
fix(publish): use elem.select() + CDP Backspace to clear React/Vue inputs (#954)
## ℹ️ Description - Link to the related issue(s): Issue #945 - When retrying a combobox value (e.g. `brand_s: rene_lezard`), the bot concatenated the old value with the new search text (e.g. "Sonstigesrene lezard" instead of "rene lezard"). React/Vue-controlled inputs intercept and revert programmatic `element.value = ""` changes — even when `input`/`change` DOM events are dispatched. PR #947's JS-based `apply()` clearing was still unreliable for these framework-managed inputs. ## 📋 Changes Summary - New `_clear_input()` method in `WebScrapingMixin`: uses the standard DOM `elem.select()` method via `apply()` to select all text, then sends a CDP Backspace keyDown+keyUp pair via `_tab.send`. This goes through the browser's native editing pipeline, triggering the same `InputEvent` with `inputType: 'deleteContentBackward'` that React observes — so the framework's internal state is updated correctly. Cross-platform by design (no OS-specific modifier keys). - Updated `web_input()`: replaced broken `clear_input()` with `_clear_input()`. - Updated `web_select_combobox()`: replaced JS-based `apply()` clearing with `_clear_input()`. - Test scout cleanup: removed low-value implementation-detail tests (`test_clear_input`, `test_dispatch_arrow_down_and_enter`), merged duplicate underscore/hyphen tests into a single parametrized test, replaced CDP call-count assertions in `test_web_input_success_returns_element` with behavioral checks. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. |
||
|
|
8bb14370ba |
fix(ci): replace deprecated tibdex/github-app-token with actions/create-github-app-token (#951)
## ℹ️ Description *Provide a concise summary of the changes introduced in this pull request.* - Link to the related issue(s): Issue #949 - The `tibdex/github-app-token` action is deprecated and runs on Node.js 20, which produces deprecation warnings and will stop working after September 2026. Its README redirects to the official replacement `actions/create-github-app-token`. This PR replaces it. ## 📋 Changes Summary - Replaced `tibdex/github-app-token@v2.1.0` (Node.js 20, deprecated) with `actions/create-github-app-token@v3.0.0` (Node.js 24, GitHub official) in: - `.github/workflows/publish-release.yml` - `.github/workflows/update-python-deps.yml` - Mapped input names from snake_case (`app_id`, `private_key`) to kebab-case (`app-id`, `private-key`) as required by the new action. - No secret changes needed — both actions use the same `DEPS_UPDATER_APP_ID` and `DEPS_UPDATER_PRIVATE_KEY` secrets. - No downstream changes needed — the output `token` is named identically in both actions. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated CI/CD workflow automation to utilize newer GitHub Actions components, improving the reliability of the release and deployment processes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
5997248ce9 |
ci(deps): bump pdm-project/setup-pdm from 4.4 to 4.5 in the all-actions group (#950)
Bumps the all-actions group with 1 update: [pdm-project/setup-pdm](https://github.com/pdm-project/setup-pdm). Updates `pdm-project/setup-pdm` from 4.4 to 4.5 <details> <summary>Commits</summary> <ul> <li><a href=" |
||
|
|
06cef5f978 |
fix(publish): normalize underscores in combobox search and clear with DOM events (#947)
## ℹ️ Description - Link to the related issue(s): #945, #946 - Regressions from #944: after the typeless-combobox routing fix, two issues appeared when config values contain underscores (e.g. `rene_lezard` for brand_s). ## 📋 Changes Summary - underscore conversion: Config values like `rene_lezard` are now converted to `rene lezard` before being typed into the combobox. The JS `normalize()` function in dropdown matching also converts `_` to spaces so that `<li>` labels are matched correctly. Hyphens are preserved since they can be legitimate characters in brand names. - robust input clearing: Replaced `nodriver`'s `clear_input()` (which only sets `element.value=""` without DOM events) with an `apply()` call that dispatches `input` + `change` events. This ensures React/Vue framework state is properly reset, preventing stale values from being concatenated on retries (e.g. `"Sonstigesrene_lezard"`). - Updated 5 existing tests to use `apply` mocks instead of `clear_input` mocks (with `side_effect` to model clearing + verification calls). - Added 2 new tests verifying underscore-to-space conversion and hyphen preservation in combobox search values. ### ⚙️ Type of Change - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved combobox selection reliability with enhanced text normalization (underscores converted to spaces; consistent whitespace and case handling). * Strengthened dropdown option matching to reduce false negatives and timeouts. * **Refactor** * Streamlined input clearing and verification for more consistent and robust selection behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
d02f58c580 |
fix(publish): use button-combobox dropdown for WANTED ad shipping selection (#948)
## ℹ️ Description - Link to the related issue(s): Issue #943 - WANTED ads on kleinanzeigen.de render shipping as a special-attribute combobox dropdown (`<button role="combobox">`) rather than radio buttons. The previous code tried to click `#ad-shipping-enabled-no` / `#ad-shipping-enabled-yes` radio buttons that do not exist in WANTED mode, causing `TimeoutError` crashes when publishing WANTED ads with `shipping_type: PICKUP` or `SHIPPING`. ## 📋 Changes Summary - New `web_select_button_combobox()` method in `web_scraping_mixin.py` — display-text-based selection from `<button role="combobox">` dropdowns using standard DOM APIs (no React fiber internals). Intended as the future replacement for the React-fiber-based `__select_button_combobox` (see issue #930). - `_WANTED_SHIPPING_LABELS` constant mapping `SHIPPING` → `"Versand möglich"` and `PICKUP` → `"Nur Abholung"` for the WANTED shipping dropdown. - WANTED shipping dispatch rewritten in `__init__.py` — CSS selector `[role="combobox"][id$=".versand"]` lookup + `web_select_button_combobox` call replacing broken radio-button code. - WANTED guard on `sell_directly` — wraps the entire sell_directly radio-button block in `if ad_cfg.type != "WANTED"` since those controls are absent in WANTED mode. - German translations added for 2 new user-facing strings. - Test scout cleanup (8 items across 2 rounds): - Removed 5 log-wording-only tests asserting `caplog.text` strings - Merged 2 duplicate combobox error-path tests into 1 parametrized test - Removed 1 duplicate timeout test superseded by parametrized coverage - Added `TestWebSelectButtonCombobox` with 2 direct unit tests (happy path + error path) - Rewrote `TestWantedShippingSelection` with 5 dropdown-based tests ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. |
||
|
|
0893ca1674 |
fix(publish): handle typeless combobox brand field with ArrowDown+Enter fallback (#944)
## ℹ️ Description - Link to the related issue(s): #938 - When publishing ads with special attributes like `brand_s`, the brand combobox `<input role="combobox">` on kleinanzeigen.de has no `type` attribute (HTML defaults to `text` but the attribute is absent). The code checked `elem_type == "text"` which failed since `elem_type` was `""`, causing the field to fall through to `web_input()` — which types the value but never confirms the autocomplete suggestion, leaving the field empty. ## 📋 Changes Summary - `__special_attribute_candidate_priority()`: Match `elem_type in {"text", ""}` instead of `elem_type == "text"` so typeless combobox `<input>` elements get priority rank `(1, 0)`. - `__set_special_attributes()` routing: Route `elem_role == "combobox" and elem_type in {"text", ""}` to `web_select_combobox()` instead of falling through to `web_input()`. - `web_select_combobox()` rewritten with 3-tier fallback: 1. `aria-controls` → find dropdown by ID, click matching `<li>` (existing path) 2. No `aria-controls` → search for `[role="listbox"]` by CSS selector 3. No listbox found → dispatch ArrowDown + Enter key events via CDP to confirm the autocomplete suggestion - New `_dispatch_arrow_down_and_enter()` method: Uses CDP `input_.dispatch_key_event` to send ArrowDown (highlight first suggestion) and Enter (confirm selection). - New test `test_special_attributes_typeless_combobox_routed_correctly`: Verifies a typeless combobox `<input>` is routed to `web_select_combobox` rather than `web_input`. - Updated tests for the new fallback behavior (previously expected `TimeoutError`, now expect ArrowDown+Enter fallback). ### ⚙️ Type of Change - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Combobox interactions now gracefully fall back to keyboard selection (ArrowDown+Enter) or alternate listbox lookup when dropdown linkage is missing, reducing spurious timeouts. * Combobox detection/handling also accepts inputs without an explicit type attribute, ensuring the correct input is targeted and verified before failing. * **Tests** * Unit tests expanded to cover the new fallback flows, verification behavior, and typeless-combobox routing. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
747663839b |
fix(publish): prioritize interactive special-attribute controls (#942)
## ℹ️ Description - Fixes #938 — special attributes (Marke, Farbe, Größe) silently failed to persist when publishing ads in the new DOM for categories like Herrenbekleidung/Jacken & Mäntel and Hemden. - The bot used `web_find()` which returns the first DOM match. Each special attribute now has multiple matching elements — a hidden backing input plus an interactive combobox control. The bot always picked the hidden input first, so values didn't stick. ## 📋 Changes Summary - Replaced `web_find()` with `web_find_all()` + candidate-ranking in `__set_special_attributes` - Added `__special_attribute_candidate_priority()` — ranks interactive controls (priority 0–4) above hidden inputs (priority 9) - Added `__pick_special_attribute_candidate()` and `__describe_special_attribute_candidate()` helpers - Fixed unsafe `attrs.type` / `attrs.get("role")` access patterns - Updated existing tests to mock `web_find_all`; added 2 new tests for button-combobox and text-combobox priority over hidden inputs ### ⚙️ Type of Change - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Bug Fixes** * Fixed handling of special characters in XPath expressions when values contain both single and double quotes. * **Tests** * Updated unit tests to verify improved robustness in special attribute element selection, including better preference handling for visible combobox elements over hidden inputs. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
6f9e2b33d6 |
chore: ✔ Update click 8.3.1 -> 8.3.2 (#941)
✔ Update click 8.3.1 -> 8.3.2 successful Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
8cf40dda30 |
fix(publish): align post-ad selectors with current DOM (#936)
## ℹ️ Description *Provide a concise summary of the changes introduced in this pull request.* - Link to the related issue(s): #920 - Describe the motivation and context for this change. Publishing failed on live ads because the Kleinanzeigen post-ad DOM changed (dotted special-attribute keys, new condition dialog structure, and shipping radios). This PR updates selector logic to the current DOM contract, hardens XPath handling, and adds regression coverage. ## 📋 Changes Summary - Updated publish selector handling in `KleinanzeigenBot`: - WANTED shipping now uses `ad-shipping-enabled-yes/no` radios with quick DOM checks. - `__set_condition()` now opens the new dialog via `.condition` label context and selects by radio `value`. - `__set_special_attributes()` now normalizes keys for current dotted DOM fields, validates selector tokens, safely escapes XPath literals, supports compound `@name` cases, and handles missing `id` attributes via XPath fallback. - `__set_shipping()` removed legacy `.versand_s` assumptions and now uses explicit step-scoped timeout errors for dialog handling. - Added/updated unit tests in `tests/unit/test_init.py` for: - condition dialog selection behavior, - shipping radio behavior (including already-selected paths), - special attribute non-string conversion and compound-name lookup. - Updated docs in `docs/AD_CONFIGURATION.md` to clarify that `condition_s` uses API values from downloaded ads. - Updated German translations for newly introduced/adjusted user-facing error strings. Mention any dependencies, configuration changes, or additional requirements introduced. - No dependency or config schema changes. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Clarified condition rules: require API condition values, listed common values, added example mappings and YAML guidance. * **Bug Fixes** * More reliable condition selection; strengthened special-attribute validation and lookup; improved shipping toggle and dialog flows with clearer localized timeout errors and consistent cost input formatting. * **Tests** * Expanded unit tests for condition selection, compound special-attribute resolution, shipping dialog flows, and wanted-ad shipping behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
e3089d8c71 |
chore: Update Python dependencies (#937)
✔ Update more-itertools 10.8.0 -> 11.0.1 successful ✔ Update ruff 0.15.8 -> 0.15.9 successful Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
1abe0ec027 |
fix(upload): avoid stale marker false positives in image upload completion (#933)
## ℹ️ Description Fix issue #929 by making image upload completion detection ignore stale hidden `adImages[*].url` markers from previous form state, so publish waits only for progress from the current upload attempt. - Link to the related issue(s): Issue #929 - Describe the motivation and context for this change. - Hidden marker fallback was counting absolute marker totals and could complete early when pre-existing markers were present. - This caused false positives after retries/replace flows where stale DOM marker inputs survived. ## 📋 Changes Summary - Capture a hidden-marker baseline before uploading images in `__upload_images`. - Compute marker completion using count-delta (`max(0, current_marker_count - baseline_marker_count)`) and keep `max(thumbnail_count, effective_marker_count)` as the completion signal. - Add a short settle sleep after thumbnail removals before baseline capture to reduce async DOM race risk. - Expand regression tests in `TestImageUploadProcessedMarkerFallback` to cover stale markers, baseline timeout fallback, max(thumbnail vs marker-delta) coexistence, and timeout reporting. - Apply CodeRabbit nitpicks by making the upload dependency helper a staticmethod and adding an inline explanatory test comment. - Mention any dependencies, configuration changes, or additional requirements introduced. - No dependency or configuration changes. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved image upload verification by tracking newly processed images and ignoring pre-existing/stale markers; added a short pause after image removal to ensure async updates settle before subsequent uploads. * **Tests** * Added async regression tests and helpers covering baseline capture, timeout fallback, marker-delta clamping, and verification that multiple image uploads are awaited. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
85a856f177 |
fix(download): handle redesigned overview pagination safely (#934)
## ℹ️ Description *Provide a concise summary of the changes introduced in this pull request.* - Link to the related issue(s): Issue #917 - Describe the motivation and context for this change. The redesigned overview page no longer exposes the legacy `.Pagination` container. As a result, `download --ads=all` could stop after page 1 (25 ads) or repeatedly process the same page, missing remaining ads. ## 📋 Changes Summary - Updated overview pagination navigation to use the current global `button[aria-label=\"Nächste\"]` controls instead of the removed `.Pagination` parent container. - Added stricter enabled-button filtering (`disabled` and `aria-disabled`) before clicking next in pagination loops. - Added a duplicate-page guard during URL extraction: if a page yields only already-seen refs, pagination stops to avoid reprocessing loops. - Updated pagination and extraction unit tests to match the live DOM behavior and new pagination flow. - Added/updated German translations for new/changed log messages. - Mention any dependencies, configuration changes, or additional requirements introduced. No new dependencies or configuration changes were introduced. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * More reliable next-page detection and handling of disabled "Next" controls, improving pagination stability. * Order-preserving deduplication of ad links so duplicate refs on the same page are ignored. * Pagination now stops automatically when a page reload yields no new items, avoiding infinite loops. * **Tests** * Updated and added unit tests to cover the new pagination detection and deduplication behaviors. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
cf6edc70f3 |
fix(download): align INFO download logs with final destinations (#935)
## ℹ️ Description *Provide a concise summary of the changes introduced in this pull request.* - Link to the related issue(s): None (no linked issue) - Describe the motivation and context for this change. This change fixes confusing download logs that exposed staging (`.tmp-*`) paths in user-facing INFO output. It keeps staging details at DEBUG level, logs the final YAML destination at INFO after the atomic rename succeeds, and adds per-item download progress logging. ## 📋 Changes Summary - Downgraded `Saving [%s]...` logging in `utils/dicts.py` from INFO to DEBUG so staging write paths are no longer shown in normal user output. - Added `Saved ad config to: %s` INFO log in `extract.download_ad()` after successful staging-to-final directory rename. - Added `Downloading %d/%d ads...` progress logs in `download_ads()` for `--ads=all`, `--ads=new`, and numeric ID selectors. - Updated `--ads=all` summary denominator to report against valid download candidates (excluding invalid ad IDs), matching the new progress reporting semantics. - Added German translations for the two new log messages. - Mention any dependencies, configuration changes, or additional requirements introduced. No dependencies or configuration changes were introduced. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. |
||
|
|
2a87fc4642 |
chore: Update Python dependencies (#931)
✔ Update types-requests 2.33.0.20260327 -> 2.33.0.20260402 successful ✔ Update charset-normalizer 3.4.6 -> 3.4.7 successful ✔ Update basedpyright 1.38.4 -> 1.39.0 successful Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
54d3d21343 |
fix: update HTML element IDs after Kleinanzeigen site redesign (#920) (#924)
## Summary Fixes #920 — Kleinanzeigen.de renamed all form element IDs site-wide. This PR updates all selectors to match the new `ad-*` prefix pattern. ### Login form changes | Old | New | |-----|-----| | `login-email` | `username` | | `login-password` | `password` | | `form#login-form button[type='submit']` | `button[type='submit']` | ### Ad form changes | Old | New | |-----|-----| | `adType2` | `ad-type-WANTED` | | `postad-title` | `ad-title` | | `select#price-type-react` (SELECT element) | `button#ad-price-type` + `li#ad-price-type-menu-option-{N}` (dropdown) | | `input#pstad-price` | `input#ad-price-amount` | | `#pstad-descrptn` | `#ad-description` | | `pstad-zip` | `ad-zip-code` | | `#pstad-citychsr` (SELECT with options) | `ad-city` (text input) | | `pstad-street` | `ad-street` | | `addressVisibility` | `ad-address-visibility` | | `postad-contactname` | `ad-name` | | `postad-phonenumber` | `ad-phone` | | `phoneNumberVisibility` | `ad-phone-visibility` | | `pstad-submit` / `fieldset#postad-publish` | `//button[contains(., 'Anzeige aufgeben')]` | ### Category selection changes | Old | New | |-----|-----| | `pstad-descrptn` (trigger click) | `ad-description` | | `postad-category-path` | `ad-category-path` | | `pstad-lnk-chngeCtgry` | XPath by text `//a[contains(., 'Kategorie')] \| //button[contains(., 'Kategorie')]` | | `//*[@id='postad-step1-sbmt']/button` | `//button[@type='submit']` | ### Notable behavioral changes - **Price type**: The price type selector changed from a `<select>` element to a button-triggered dropdown (`<ul>` with `<li>` options). The mapping is: `FIXED` → option 0, `NEGOTIABLE` → option 1, `GIVE_AWAY` → option 2. - **City**: Changed from a `<select>` dropdown populated after ZIP entry to a plain text `<input>` (`ad-city`). - **Submit**: `pstad-submit` no longer exists; the form now has a `<button>` with text "Anzeige aufgeben" (no `id` attribute). ## Test plan - [ ] Verify login works with new `username`/`password` field IDs - [ ] Verify ad title field `ad-title` accepts input - [ ] Verify price type dropdown opens via `ad-price-type` button and selects correct option - [ ] Verify description sets via `#ad-description` - [ ] Verify ZIP/city fields `ad-zip-code` and `ad-city` work - [ ] Verify ad submits via `//button[contains(., 'Anzeige aufgeben')]` - [ ] Verify category selection flow with updated XPath selectors 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Restored ad publishing flow to match the site's revised UI: updated field handling for title/description, pricing, buy‑now, submit/confirmation, and more resilient category detection and dialog handling. * Improved pricing and submission reliability; timeouts now surface locally. * **Chores** * Updated contact, address, shipping and special-attribute handling to align with the site's changed controls and React-managed inputs. * **Tests** * Adjusted unit tests and mocks to reflect the updated page interactions. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Jens <1742418+1cu@users.noreply.github.com> |
||
|
|
9bbac7903c | fix(ci): remove obsolete pip-audit ignore for Pygments CVE (#928) | ||
|
|
bcc63b7f1d | fix(pagination): continue when paging.last is absent (#922) | ||
|
|
d79a6cf6b6 |
chore: Update Python dependencies (#918)
✔ Update requests 2.33.0 -> 2.33.1 successful ✔ Update pygments 2.19.2 -> 2.20.0 successful ✔ Update pyinstaller-hooks-contrib 2026.3 -> 2026.4 successful ✔ Update mypy 1.19.1 -> 1.20.0 successful ✔ Update nodejs-wheel-binaries 24.14.0 -> 24.14.1 successful Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
2ab167952c |
fix(download): show final directory path in INFO logs instead of staging path (#916)
## ℹ️ Description - Link to the related issue(s): Issue N/A - Improves logging clarity for the `download` command by addressing two user-facing issues: 1. **Path accuracy**: INFO logs now show the final download directory instead of the temporary staging directory, eliminating confusion about where files actually end up. 2. **Message clarity**: Log messages now distinguish between the two fetch operations — JSON API (metadata) vs HTML scan (navigation URLs) — so the dual-fetch design is explicit rather than appearing redundant. ## 📋 Changes Summary - Demoted staging directory path to DEBUG level; added INFO log for final download path - Updated fetch-related log messages to indicate purpose (metadata vs navigation URLs) ### ⚙️ Type of Change - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Improved logging and progress messages to separately report staging and final download paths and to clarify scanning progress when discovering ad URLs. * Clarified wording to describe the downloaded API payload as "ad metadata (status, expiry dates)" and to use "ad URL" when reporting counts. * **Localization** * Updated German pluralization to correctly form "Anzeigen‑URLs" where applicable. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
498d0934d2 | ci(deps): bump the all-actions group with 2 updates (#919) | ||
|
|
a3b0b4bb5b |
feat(download): resolve inactive state for all/new selectors (#915)
## ℹ️ Description This PR implements the feature to set downloaded own ads to `active=False` when their published state is not "active" for "all" and "new" selectors. Previously, only numeric ID downloads (`--ads=123,456`) correctly resolved the activity state - this extends that behavior to the "all" and "new" selectors. ### Motivation and Context When downloading ads using `--ads=all` or `--ads=new`, ads that were paused, inactive, or in other non-active states on Kleinanzeigen were incorrectly saved with `active: true`. This could cause confusion when users would later publish these ads - they would be published as active even though they were originally inactive. This PR ensures that the downloaded ad configuration accurately reflects the actual state of the ad on Kleinanzeigen: - Ads with `state="active"` → saved with `active: true` - Ads with `state="inactive"`, `state="paused"`, or any other state → saved with `active: false` ## 📋 Changes Summary ### Core Implementation - Extended `_resolve_download_ad_activity()` usage from numeric IDs to "all" and "new" selectors - Created `ResolvedAdState` NamedTuple to replace `tuple[bool, bool]` for better type safety and self-documenting code - Created `_download_ad_with_resolved_state()` helper method to centralize resolution + logging + download logic for "all" and "new" selectors - Updated "all" selector to resolve and pass correct active state - Updated "new" selector to resolve and pass correct active state - Updated numeric selector to use NamedTuple access (kept inline due to different warning semantics) ### Code Quality Improvements - Fixed variable naming: `add_url` → `ad_url` (3 occurrences in "all" selector) - Fixed comment typo: "adds" → "ads" ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [ ] 🐞 Bug fix (non-breaking change which fixes an issue) - [x] ✨ New feature (adds new functionality without breaking existing usage) - [x] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * More accurate handling of ad active/ownership status (treats missing or unexpected state as inactive) * Downloads now skip invalid ad links and gracefully skip when navigation fails * Improved warning and debug messages for inaccessible, foreign, or inactive ads * **Tests** * Added comprehensive tests for all ad download modes, state handling, error cases, and edge scenarios <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
8e73072550 | fix(web): make browser shutdown tolerant of disappearing psutil process | ||
|
|
b9c98f4165 | fix: handle buy-now dialog opt-out for PICKUP shipping type (#891) | ||
|
|
f836d93ac6 | docs(publish_ad): standardize docstring to Google-style Args format (#912) | ||
|
|
14bdd44111 |
chore: Update Python dependencies (#914)
✔ Update types-requests 2.32.4.20260324 -> 2.33.0.20260327 successful ✔ Update ruff 0.15.7 -> 0.15.8 successful Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
f2a6cfdb25 |
fix(cli): align --ads error messages to mention comma-separated keywords (#913)
## ℹ️ Description - Link to the related issue(s): Issue #903 - The CLI error messages for invalid `--ads` selectors did not mention that comma-separated keyword combinations (e.g., `changed,due`) are valid, even though `_is_valid_ads_selector()` accepts them and README documents them. ## 📋 Changes Summary - Updated 3 English error messages in `__init__.py` to mention comma-separated keyword combinations - Updated 3 corresponding German translations in `translations.de.yaml` - Fixed a line-length issue (E501) at line 1989 by wrapping a long statement - The `extend` command message is unchanged since only `all` is a valid keyword there ### ⚙️ Type of Change - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [ ] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. |
||
|
|
6100eb09f7 |
test(extract): migrate description-affix tests to staging path (#911)
## ℹ️ Description There is no need to remove any legacy code - that has already been done. But there are three tests that need an update. - Link to the related issue(s): Issue #866 - Describe the motivation and context for this change. The description-affix tests were still calling `_extract_ad_page_info(...)` directly, while runtime uses the staging directory flow via `_extract_ad_page_info_with_directory_handling(...)`. This change aligns tests with the active path and keeps timeout behavior explicit. ## 📋 Changes Summary - Rewrote three description-affix tests in `tests/unit/test_extract.py` to call `_extract_ad_page_info_with_directory_handling(...)` instead of `_extract_ad_page_info(...)`. - Added `tmp_path`-backed base directory setup in those tests to exercise path handling through the staging wrapper. - Updated `web_text` mock sequencing to account for the additional title read done by the staging wrapper. - Made timeout expectation deterministic with `pytest.raises(TimeoutError)`. - No runtime production code changes; only test updates. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Tests** * Updated internal testing infrastructure for content-extraction validation. No user-facing changes. --- **Note:** This release contains internal development improvements only. End-users will not experience any functional changes or new features. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
b143b26c24 |
enh(download): enforce single-use placeholders in naming templates (#909)
## ℹ️ Description This pull request finalizes PR5 by simplifying download naming template rules and making truncation behavior deterministic and reviewable. - Link to the related issue(s): Issue #841 - Describe the motivation and context for this change. Repeated placeholders and implicit truncation priority made naming outcomes hard to predict. This change enforces single-use placeholders and aligns validation, rendering, docs, and tests around explicit priority rules under tight budgets. ## 📋 Changes Summary - Enforced template validation rule: each placeholder (`{id}`, `{title}`) may appear at most once in: - `download.folder_name_template` - `download.ad_file_name_template` - Added validation error path and i18n entry for repeated placeholders. - Refined `_render_download_name_with_budget` behavior to deterministic priority: - `{id}` protected as much as possible - literals preserved after `{id}` - `{title}` truncated first under budget pressure - Added truncation warnings and bounded log previews for template/title/rendered values. - Improved snippet truncation behavior to keep preview length within configured caps, including small/zero limit handling. - Expanded tests in `test_config_model.py` and `test_extract.py` to cover: - repeated placeholder rejection - id/title truncation warnings - helper edge cases for log snippet truncation - Updated configuration documentation with: - valid/invalid template examples - tight-budget examples reflecting current priority behavior - explicit note about last-resort `{id}` truncation - Updated generated artifacts where applicable: - `docs/config.default.yaml` - `schemas/config.schema.json` - No new runtime dependencies or new config keys introduced. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [ ] 🐞 Bug fix (non-breaking change which fixes an issue) - [x] ✨ New feature (adds new functionality without breaking existing usage) - [x] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. |
||
|
|
e0ed4741ea | chore: Update Python dependencies (#910) | ||
|
|
ae9a720bbb | fix: save foreign numeric-id downloads as inactive (#906) | ||
|
|
5d1e9f1f80 | fix: ignore CVE-2026-4539 in pip-audit until upstream fix (#907) | ||
|
|
c0d8788613 | ci: remove scope restrictions and add refactor type to PR validation (#908) | ||
|
|
ba47c4c76b |
fix: clear existing image thumbnails before upload for all modes (#904)
## Problem When publishing multiple ads in sequence, stale image thumbnails from a previous ad (or a failed retry) could remain on the page. The thumbnail count check then failed with errors like: \\\ Not all images were uploaded within timeout. Expected 2, found 20 thumbnails. \\\ This happened because existing thumbnails were only removed in \MODIFY\ mode (editing an existing ad), but not in \REPLACE\ mode (publishing a new ad). On retries, the page was reloaded but could still retain old thumbnails, causing them to accumulate. ## Fix Always clear existing thumbnails before uploading new images, regardless of the publish mode. The cleanup uses a short \quick_dom\ timeout and is wrapped in \ ry/except TimeoutError\ so it gracefully handles fresh forms where no thumbnails exist yet. ## Changes - Removed the \if mode == AdUpdateStrategy.MODIFY\ guard around the thumbnail cleanup - Added \ ry/except TimeoutError\ to handle fresh pages where the thumbnail list is empty - Added a log message when thumbnails are being removed <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Thumbnail cleanup now runs for both modification and replacement workflows, improving image update reliability. * DOM lookup for existing thumbnails gains a timeout and now treats timeouts as no thumbnails found to avoid publish failures. * Added a short pause after each thumbnail removal to let the UI settle. * Info-level logging reports how many thumbnails were removed prior to upload. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
67b641ea86 |
chore: ✔ Update types-requests 2.32.4.20260107 -> 2.32.4.20260324 (#905)
✔ Update types-requests 2.32.4.20260107 -> 2.32.4.20260324 successful Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
e1bb54ef53 |
enh: replace login UNKNOWN state with boolean + detection reason (#890)
## ℹ️ Description This PR implements issue #872 by replacing the tri-state login model (`LOGGED_IN` / `LOGGED_OUT` / `UNKNOWN`) with an explicit login detection result object that separates control flow from diagnostics. - Link to the related issue(s): Issue #872 - Describe the motivation and context for this change. - `UNKNOWN` previously mixed two concerns: decision logic and observability. In practice, callers mostly needed a binary decision (logged in or not), while diagnostics needed the *reason* for inconclusive detection. - This change makes login decisions explicit (`is_logged_in`) and keeps diagnostics/context explicit (`reason`), reducing ambiguity and making behavior easier to reason about and test. ## 📋 Changes Summary - Replaced `LoginState` with: - `LoginDetectionReason` enum (`USER_INFO_MATCH`, `CTA_MATCH`, `SELECTOR_TIMEOUT`) - `LoginDetectionResult` frozen dataclass (`is_logged_in`, `reason`) with invariant checks in `__post_init__` - Updated `get_login_state()` to return `LoginDetectionResult` while preserving detection order and runtime behavior: 1. DOM logged-in selector match -> `USER_INFO_MATCH` 2. Logged-out CTA selector match -> `CTA_MATCH` 3. Inconclusive fallback -> `SELECTOR_TIMEOUT` (+ optional diagnostics capture) - Updated login flow call sites to branch on `result.is_logged_in` and include reason context in post-login failure handling. - Updated diagnostics text from UNKNOWN-specific wording to inconclusive wording. - Renamed diagnostics artifact prefix from `login_detection_unknown` to `login_detection_inconclusive` and aligned diagnostics utility docs. - Migrated and expanded unit tests: - switched enum-based assertions/mocks to `LoginDetectionResult` - added invariant tests for invalid result combinations - Updated docs to match current behavior and config keys: - removed outdated auth-probe fallback wording - documented `CTA_MATCH` / `SELECTOR_TIMEOUT` meaning and troubleshooting guidance - added migration note from `diagnostics.login_detection_capture` to `diagnostics.capture_on.login_detection` Mention any dependencies, configuration changes, or additional requirements introduced. - No new runtime dependencies introduced. - No user-facing config schema changes introduced. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [ ] 🐞 Bug fix (non-breaking change which fixes an issue) - [x] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * DOM-first login detection with a logged-out CTA check, explicit inconclusive outcome (SELECTOR_TIMEOUT), and reason-aware detection surfaced in logs/errors. * **Documentation** * Updated troubleshooting and configuration docs and examples to use diagnostics.capture_on.login_detection and added migration guidance for the legacy key. * **Tests** * Updated and added tests covering new detection outcomes, reason validation, and diagnostic capture behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
a705e4e316 |
fix: harden downloaded ad staging and rollback (#900)
## ℹ️ Description This pull request hardens ad download writes so updates are staged and only swapped into place after successful extraction and YAML serialization, with explicit rollback safeguards for failure paths. - Link to the related issue(s): Issue #893 - Describe the motivation and context for this change. The original PR #851 split identified download directory replacement as a data-safety risk when failures occur mid-write. This change makes replacement safer by staging output first, restoring prior data when swap fails, and tightening cleanup behavior to avoid using dirty staging directories. ## 📋 Changes Summary - Refactored `AdExtractor.download_ad()` to use staged output directories (`.tmp-*`) and backup swap directories (`.bak-*`) before finalizing writes. - Changed YAML write flow to write into staging first, then swap staging to final only after successful serialization. - Added `backup_created_by_us` guard so rollback restores only backups created by the current run. - Added defensive handling for backup/staging cleanup failures with structured warning/error logging. - Added stale staging cleanup hardening: stale directories are cleaned before reuse, and extraction aborts if stale cleanup fails and directory remains. - Preserved existing `rename_existing_folders` behavior while routing extraction output through staging. - Added/updated German translations for new user-facing operational messages. - Expanded unit coverage in `tests/unit/test_extract.py` for: - replace-on-success behavior - YAML write failures (with and without existing final directory) - swap rename rollback - stale backup fail-fast - backup cleanup warning handling - backup-restore failure logging - stale staging cleanup success/failure behavior - explicit backup removal assertion on successful swap - Mention any dependencies, configuration changes, or additional requirements introduced. No new dependencies or configuration keys were introduced. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [x] 🐞 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Safer downloads using a staging-then-commit flow to prevent partial writes. * Automatic backup creation, atomic replacement, and robust rollback on errors. * Improved cleanup and guarded handling for stale staging and failed rename/cleanup operations. * **Tests** * Expanded unit tests covering staging→final swaps, backup/restore, cleanup, failure paths, and rollback behaviors. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
804f8a7e61 |
ci(deps): bump the all-actions group with 2 updates (#901)
Bumps the all-actions group with 2 updates: [codecov/codecov-action](https://github.com/codecov/codecov-action) and [github/codeql-action](https://github.com/github/codeql-action). Updates `codecov/codecov-action` from 5.5.2 to 5.5.3 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/codecov/codecov-action/releases">codecov/codecov-action's releases</a>.</em></p> <blockquote> <h2>v5.5.3</h2> <h2>What's Changed</h2> <ul> <li>build(deps): bump actions/github-script from 7.0.1 to 8.0.0 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/codecov/codecov-action/pull/1874">codecov/codecov-action#1874</a></li> <li>chore(release): bump to 5.5.3 by <a href="https://github.com/thomasrockhu-codecov"><code>@thomasrockhu-codecov</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1922">codecov/codecov-action#1922</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/codecov/codecov-action/compare/v5.5.2...v5.5.3">https://github.com/codecov/codecov-action/compare/v5.5.2...v5.5.3</a></p> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md">codecov/codecov-action's changelog</a>.</em></p> <blockquote> <h2>v5.5.2</h2> <h3>What's Changed</h3> <p><strong>Full Changelog</strong>: <a href="https://github.com/codecov/codecov-action/compare/v5.5.1..v5.5.2">https://github.com/codecov/codecov-action/compare/v5.5.1..v5.5.2</a></p> <h2>v5.5.1</h2> <h3>What's Changed</h3> <ul> <li>fix: overwrite pr number on fork by <a href="https://github.com/thomasrockhu-codecov"><code>@thomasrockhu-codecov</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1871">codecov/codecov-action#1871</a></li> <li>build(deps): bump actions/checkout from 4.2.2 to 5.0.0 by <code>@app/dependabot</code> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1868">codecov/codecov-action#1868</a></li> <li>build(deps): bump github/codeql-action from 3.29.9 to 3.29.11 by <code>@app/dependabot</code> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1867">codecov/codecov-action#1867</a></li> <li>fix: update to use local app/ dir by <a href="https://github.com/thomasrockhu-codecov"><code>@thomasrockhu-codecov</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1872">codecov/codecov-action#1872</a></li> <li>docs: fix typo in README by <a href="https://github.com/datalater"><code>@datalater</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1866">codecov/codecov-action#1866</a></li> <li>Document a <code>codecov-cli</code> version reference example by <a href="https://github.com/webknjaz"><code>@webknjaz</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1774">codecov/codecov-action#1774</a></li> <li>build(deps): bump github/codeql-action from 3.28.18 to 3.29.9 by <code>@app/dependabot</code> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1861">codecov/codecov-action#1861</a></li> <li>build(deps): bump ossf/scorecard-action from 2.4.1 to 2.4.2 by <code>@app/dependabot</code> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1833">codecov/codecov-action#1833</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/codecov/codecov-action/compare/v5.5.0..v5.5.1">https://github.com/codecov/codecov-action/compare/v5.5.0..v5.5.1</a></p> <h2>v5.5.0</h2> <h3>What's Changed</h3> <ul> <li>feat: upgrade wrapper to 0.2.4 by <a href="https://github.com/jviall"><code>@jviall</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1864">codecov/codecov-action#1864</a></li> <li>Pin actions/github-script by Git SHA by <a href="https://github.com/martincostello"><code>@martincostello</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1859">codecov/codecov-action#1859</a></li> <li>fix: check reqs exist by <a href="https://github.com/joseph-sentry"><code>@joseph-sentry</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1835">codecov/codecov-action#1835</a></li> <li>fix: Typo in README by <a href="https://github.com/spalmurray"><code>@spalmurray</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1838">codecov/codecov-action#1838</a></li> <li>docs: Refine OIDC docs by <a href="https://github.com/spalmurray"><code>@spalmurray</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1837">codecov/codecov-action#1837</a></li> <li>build(deps): bump github/codeql-action from 3.28.17 to 3.28.18 by <code>@app/dependabot</code> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1829">codecov/codecov-action#1829</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/codecov/codecov-action/compare/v5.4.3..v5.5.0">https://github.com/codecov/codecov-action/compare/v5.4.3..v5.5.0</a></p> <h2>v5.4.3</h2> <h3>What's Changed</h3> <ul> <li>build(deps): bump github/codeql-action from 3.28.13 to 3.28.17 by <code>@app/dependabot</code> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1822">codecov/codecov-action#1822</a></li> <li>fix: OIDC on forks by <a href="https://github.com/joseph-sentry"><code>@joseph-sentry</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1823">codecov/codecov-action#1823</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/codecov/codecov-action/compare/v5.4.2..v5.4.3">https://github.com/codecov/codecov-action/compare/v5.4.2..v5.4.3</a></p> <h2>v5.4.2</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href=" |
||
|
|
2abe0ccb71 | feat: add configurable download naming templates (#896) | ||
|
|
813753dd41 | fix: guard browser cleanup pid handling for mocked sessions (#898) | ||
|
|
556af67fa0 |
chore: ✔ Update pytest-cov 7.0.0 -> 7.1.0 (#899)
✔ Update pytest-cov 7.0.0 -> 7.1.0 successful Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
a4d8b7e2cb |
chore: ✔ Update jaraco-context 6.1.1 -> 6.1.2 (#897)
✔ Update jaraco-context 6.1.1 -> 6.1.2 successful Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
2d02d206ec | feat: add configurable download directory (#894) | ||
|
|
94c3a1d260 |
chore: Update Python dependencies (#895)
✔ Update attrs 25.4.0 -> 26.1.0 successful ✔ Update ruff 0.15.6 -> 0.15.7 successful Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
d25d63f0ce |
chore: Update Python dependencies (#889)
✔ Update cyclonedx-python-lib 11.6.0 -> 11.7.0 successful ✔ Update basedpyright 1.38.2 -> 1.38.3 successful Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
e7fed79aa6 | chore: ✔ Update coverage 7.13.4 -> 7.13.5 (#888) |