1044 Commits

Author SHA1 Message Date
dependabot[bot]
305ee52576 ci(deps): bump github/codeql-action from 4.35.2 to 4.35.3 in the all-actions group (#1029)
Bumps the all-actions group with 1 update:
[github/codeql-action](https://github.com/github/codeql-action).

Updates `github/codeql-action` from 4.35.2 to 4.35.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/github/codeql-action/releases">github/codeql-action's
releases</a>.</em></p>
<blockquote>
<h2>v4.35.3</h2>
<ul>
<li><em>Upcoming breaking change</em>: Add a deprecation warning for
customers using CodeQL version 2.19.3 and earlier. These versions of
CodeQL were discontinued on 9 April 2026 alongside GitHub Enterprise
Server 3.15, and will be unsupported by the next minor release of the
CodeQL Action. <a
href="https://redirect.github.com/github/codeql-action/pull/3837">#3837</a></li>
<li>Configurations for private registries that use Cloudsmith or GCP
OIDC are now accepted. <a
href="https://redirect.github.com/github/codeql-action/pull/3850">#3850</a></li>
<li>Best-effort connection tests for private registries now use
<code>GET</code> requests instead of <code>HEAD</code> for better
compatibility with various registry implementations. For NuGet feeds,
the test is now always performed against the service index. <a
href="https://redirect.github.com/github/codeql-action/pull/3853">#3853</a></li>
<li>Fixed a bug where two diagnostics produced within the same
millisecond could overwrite each other on disk, causing one of them to
be lost. <a
href="https://redirect.github.com/github/codeql-action/pull/3852">#3852</a></li>
<li>Update default CodeQL bundle version to <a
href="https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.3">2.25.3</a>.
<a
href="https://redirect.github.com/github/codeql-action/pull/3865">#3865</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/github/codeql-action/blob/main/CHANGELOG.md">github/codeql-action's
changelog</a>.</em></p>
<blockquote>
<h1>CodeQL Action Changelog</h1>
<p>See the <a
href="https://github.com/github/codeql-action/releases">releases
page</a> for the relevant changes to the CodeQL CLI and language
packs.</p>
<h2>[UNRELEASED]</h2>
<p>No user facing changes.</p>
<h2>4.35.3 - 01 May 2026</h2>
<ul>
<li><em>Upcoming breaking change</em>: Add a deprecation warning for
customers using CodeQL version 2.19.3 and earlier. These versions of
CodeQL were discontinued on 9 April 2026 alongside GitHub Enterprise
Server 3.15, and will be unsupported by the next minor release of the
CodeQL Action. <a
href="https://redirect.github.com/github/codeql-action/pull/3837">#3837</a></li>
<li>Configurations for private registries that use Cloudsmith or GCP
OIDC are now accepted. <a
href="https://redirect.github.com/github/codeql-action/pull/3850">#3850</a></li>
<li>Best-effort connection tests for private registries now use
<code>GET</code> requests instead of <code>HEAD</code> for better
compatibility with various registry implementations. For NuGet feeds,
the test is now always performed against the service index. <a
href="https://redirect.github.com/github/codeql-action/pull/3853">#3853</a></li>
<li>Fixed a bug where two diagnostics produced within the same
millisecond could overwrite each other on disk, causing one of them to
be lost. <a
href="https://redirect.github.com/github/codeql-action/pull/3852">#3852</a></li>
<li>Update default CodeQL bundle version to <a
href="https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.3">2.25.3</a>.
<a
href="https://redirect.github.com/github/codeql-action/pull/3865">#3865</a></li>
</ul>
<h2>4.35.2 - 15 Apr 2026</h2>
<ul>
<li>The undocumented TRAP cache cleanup feature that could be enabled
using the <code>CODEQL_ACTION_CLEANUP_TRAP_CACHES</code> environment
variable is deprecated and will be removed in May 2026. If you are
affected by this, we recommend disabling TRAP caching by passing the
<code>trap-caching: false</code> input to the <code>init</code> Action.
<a
href="https://redirect.github.com/github/codeql-action/pull/3795">#3795</a></li>
<li>The Git version 2.36.0 requirement for improved incremental analysis
now only applies to repositories that contain submodules. <a
href="https://redirect.github.com/github/codeql-action/pull/3789">#3789</a></li>
<li>Python analysis on GHES no longer extracts the standard library,
relying instead on models of the standard library. This should result in
significantly faster extraction and analysis times, while the effect on
alerts should be minimal. <a
href="https://redirect.github.com/github/codeql-action/pull/3794">#3794</a></li>
<li>Fixed a bug in the validation of OIDC configurations for private
registries that was added in CodeQL Action 4.33.0 / 3.33.0. <a
href="https://redirect.github.com/github/codeql-action/pull/3807">#3807</a></li>
<li>Update default CodeQL bundle version to <a
href="https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.2">2.25.2</a>.
<a
href="https://redirect.github.com/github/codeql-action/pull/3823">#3823</a></li>
</ul>
<h2>4.35.1 - 27 Mar 2026</h2>
<ul>
<li>Fix incorrect minimum required Git version for <a
href="https://redirect.github.com/github/roadmap/issues/1158">improved
incremental analysis</a>: it should have been 2.36.0, not 2.11.0. <a
href="https://redirect.github.com/github/codeql-action/pull/3781">#3781</a></li>
</ul>
<h2>4.35.0 - 27 Mar 2026</h2>
<ul>
<li>Reduced the minimum Git version required for <a
href="https://redirect.github.com/github/roadmap/issues/1158">improved
incremental analysis</a> from 2.38.0 to 2.11.0. <a
href="https://redirect.github.com/github/codeql-action/pull/3767">#3767</a></li>
<li>Update default CodeQL bundle version to <a
href="https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.1">2.25.1</a>.
<a
href="https://redirect.github.com/github/codeql-action/pull/3773">#3773</a></li>
</ul>
<h2>4.34.1 - 20 Mar 2026</h2>
<ul>
<li>Downgrade default CodeQL bundle version to <a
href="https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.24.3">2.24.3</a>
due to issues with a small percentage of Actions and JavaScript
analyses. <a
href="https://redirect.github.com/github/codeql-action/pull/3762">#3762</a></li>
</ul>
<h2>4.34.0 - 20 Mar 2026</h2>
<ul>
<li>Added an experimental change which disables TRAP caching when <a
href="https://redirect.github.com/github/roadmap/issues/1158">improved
incremental analysis</a> is enabled, since improved incremental analysis
supersedes TRAP caching. This will improve performance and reduce
Actions cache usage. We expect to roll this change out to everyone in
March. <a
href="https://redirect.github.com/github/codeql-action/pull/3569">#3569</a></li>
<li>We are rolling out improved incremental analysis to C/C++ analyses
that use build mode <code>none</code>. We expect this rollout to be
complete by the end of April 2026. <a
href="https://redirect.github.com/github/codeql-action/pull/3584">#3584</a></li>
<li>Update default CodeQL bundle version to <a
href="https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.0">2.25.0</a>.
<a
href="https://redirect.github.com/github/codeql-action/pull/3585">#3585</a></li>
</ul>
<h2>4.33.0 - 16 Mar 2026</h2>
<ul>
<li>
<p>Upcoming change: Starting April 2026, the CodeQL Action will skip
collecting file coverage information on pull requests to improve
analysis performance. File coverage information will still be computed
on non-PR analyses. Pull request analyses will log a warning about this
upcoming change. <a
href="https://redirect.github.com/github/codeql-action/pull/3562">#3562</a></p>
<p>To opt out of this change:</p>
<ul>
<li><strong>Repositories owned by an organization:</strong> Create a
custom repository property with the name
<code>github-codeql-file-coverage-on-prs</code> and the type
&quot;True/false&quot;, then set this property to <code>true</code> in
the repository's settings. For more information, see <a
href="https://docs.github.com/en/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization">Managing
custom properties for repositories in your organization</a>.
Alternatively, if you are using an advanced setup workflow, you can set
the <code>CODEQL_ACTION_FILE_COVERAGE_ON_PRS</code> environment variable
to <code>true</code> in your workflow.</li>
<li><strong>User-owned repositories using default setup:</strong> Switch
to an advanced setup workflow and set the
<code>CODEQL_ACTION_FILE_COVERAGE_ON_PRS</code> environment variable to
<code>true</code> in your workflow.</li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e46ed2cbd0"><code>e46ed2c</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/3867">#3867</a>
from github/update-v4.35.3-8c6e48dbe</li>
<li><a
href="b73d1d1634"><code>b73d1d1</code></a>
Add changelog entry for <a
href="https://redirect.github.com/github/codeql-action/issues/3853">#3853</a></li>
<li><a
href="24e0bb00a9"><code>24e0bb0</code></a>
Reorder changelog entries</li>
<li><a
href="ec298daba7"><code>ec298da</code></a>
Update changelog for v4.35.3</li>
<li><a
href="8c6e48dbe0"><code>8c6e48d</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/3865">#3865</a>
from github/update-bundle/codeql-bundle-v2.25.3</li>
<li><a
href="719098349e"><code>7190983</code></a>
Add changelog note</li>
<li><a
href="2bb209555a"><code>2bb2095</code></a>
Update default bundle to codeql-bundle-v2.25.3</li>
<li><a
href="7851e55dc3"><code>7851e55</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/3850">#3850</a>
from github/mbg/private-registry/cloudsmith-gcp</li>
<li><a
href="262a15f6cf"><code>262a15f</code></a>
Add generic non-printable chars test for OIDC configs</li>
<li><a
href="a6109b1c07"><code>a6109b1</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/3853">#3853</a>
from github/mbg/start-proxy/improved-checks</li>
<li>Additional commits viewable in <a
href="95e58e9a2c...e46ed2cbd0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github/codeql-action&package-manager=github_actions&previous-version=4.35.2&new-version=4.35.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-04 21:26:45 +02:00
Arne Becker
c9894d4aea docs: unify Linux and Mac installation command (#1028)
## ℹ️ Description
Autoselect kernel name and arch

- Link to the related issue(s): non
- Motiviation: The current install command hardcodes `amd64` as the
target architecture. This caused me quite some frustration when setting
up the bot on my Apple Silicon Mac — I initially downloaded the wrong
binary without realizing it, and it took me a while to figure out that
architecture-specific binaries exist for both `amd64` and `arm64`.

This change makes the install command automatically detect the correct
architecture and OS at runtime using `uname`, so users get the right
binary without having to know or think about their system architecture.
This should lower the barrier for new users and prevent silent failures
on Apple Silicon machines.

## 📋 Changes Summary

Autoselect kernel name and arch for install command

### ⚙️ 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**
* Simplified Linux/macOS installation instructions by consolidating them
into a single step with automatic OS detection, removing the need for
separate OS-specific commands.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-04 11:05:37 +02:00
kleinanzeigen-bot-tu[bot]
e0857fb9d6 chore: ✔ Update types-requests 2.33.0.20260408 -> 2.33.0.20260503 (#1027)
✔ Update types-requests 2.33.0.20260408 -> 2.33.0.20260503 successful

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-05-04 08:23:19 +02:00
kleinanzeigen-bot-tu[bot]
bd422fbf0f chore: ✔ Update typer 0.25.0 -> 0.25.1 (#1026) 2026-05-03 08:31:02 +02:00
Jens
5674d65bf4 fix(publish): prioritize mapped condition values (#1025)
## ℹ️ Description
Fixes the legacy condition probe order from issue #1002 by trying the
mapped/API value first and keeping the legacy value as fallback.

## 📋 Changes Summary
- Reorder `__set_condition()` candidate probing so the mapped API value
is tried before the configured legacy value.
- Keep the minimal regression coverage for mapped-first and fallback
behavior.

### ⚙️ 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.

Fixes #1002

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **Bug Fixes**
* Improved condition value handling by prioritizing API-mapped values
over legacy configuration tokens for better compatibility
* Enhanced fallback mechanism for condition selection when preferred UI
elements are unavailable, ensuring proper value translation and fallback
to generic handlers

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-29 11:56:00 +02:00
dependabot[bot]
d843672865 ci(deps): bump vegardit/fast-apt-mirror.sh from 1.4.3 to 1.4.4 in the all-actions group (#1022)
Bumps the all-actions group with 1 update:
[vegardit/fast-apt-mirror.sh](https://github.com/vegardit/fast-apt-mirror.sh).

Updates `vegardit/fast-apt-mirror.sh` from 1.4.3 to 1.4.4
<details>
<summary>Commits</summary>
<ul>
<li><a
href="c02f624f2c"><code>c02f624</code></a>
feat: use python3 as fallback HTTP client</li>
<li>See full diff in <a
href="776a8b231d...c02f624f2c">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=vegardit/fast-apt-mirror.sh&package-manager=github_actions&previous-version=1.4.3&new-version=1.4.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-27 19:35:17 +02:00
Jens
56ba21b899 feat(publish): warn on legacy German condition values (#1021)
## ℹ️ Description
*Provide a concise summary of the changes introduced in this pull
request.*

- Link to the related issue(s): Issue #1001
- Describe the motivation and context for this change: surface a warning
when legacy German condition tokens are still used so users can migrate
to the current English API values.

## 📋 Changes Summary

- Logs a warning in `KleinanzeigenBot.__set_condition()` when a legacy
German condition value is mapped to a newer API value.
- Preserves the existing fallback behavior so legacy configs continue to
work.
- Adds unit coverage for legacy warning behavior, English values, and
the dialog-unavailable path.
- Adds a German translation for the warning message.

### ⚙️ 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**
* Added deprecation warning when using legacy condition configuration
values, guiding users to update their settings.

* **Tests**
* Enhanced test coverage for condition value handling and deprecation
messaging to ensure proper system behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-27 19:23:00 +02:00
kleinanzeigen-bot-tu[bot]
e0ca47ba0b chore: ✔ Update pathspec 1.1.0 -> 1.1.1 (#1020)
✔ Update pathspec 1.1.0 -> 1.1.1 successful

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-27 17:08:39 +02:00
Jens
2c45a51d3f fix: guard condition mappings against API drift (#1019)
## ℹ️ Description
*Provide a concise summary of the changes introduced in this pull
request.*

- Link to the related issue(s): Issue #1004
- Add import-time validation to catch condition API drift early.

## 📋 Changes Summary

- Introduce a shared canonical set of allowed condition API values in
`src/kleinanzeigen_bot/model/ad_model.py`.
- Validate the publish and extract condition mapping tables at import
time.
- Keep the two mapping tables independent while failing fast on unknown
API codes.
- Clean up a few low-value tests in the touched area to focus on
behavior instead of constant snapshots.

### ⚙️ 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`).
- [ ] 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

* **Tests**
* Added a unit test to ensure invalid condition mappings are rejected
with a clear error.
* Enhanced a test to assert that a truncation warning is logged when
title length limits are hit.

* **Chores**
* Added upfront validation of condition mappings at initialization to
surface invalid configurations early with detailed error messages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-27 17:08:11 +02:00
Jens
ea7c799fec fix(logging): clarify delete, upload and price logs (#1018)
## ℹ️ Description
- Improve logging for ad deletion outcomes, image upload progress, and
auto price reduction transitions.
- Clean up nearby tests by removing brittle log-wording assertions while
preserving behavior coverage.

- Link to the related issue(s): N/A
- Describe the motivation and context for this change.

## 📋 Changes Summary
- Log success/skipped outcomes for delete operations.
- Log image uploads as `uploading image 1/16`.
- Include price transitions in auto price reduction logs.
- Update translations and tests accordingly.

### ⚙️ 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**
* Enhanced logging clarity for auto price reduction operations with
improved price transition reporting
* Improved ad deletion lifecycle logs with clearer messaging for skipped
and successful deletions
* Added progress indicators (image index/total) to image upload logging
for better operation tracking

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-27 09:41:39 +02:00
kleinanzeigen-bot-tu[bot]
1182b3f75e chore: Update Python dependencies (#1017)
✔ Update typer 0.24.2 -> 0.25.0 successful
  ✔ Update packaging 26.1 -> 26.2 successful
  ✔ Update ruff 0.15.11 -> 0.15.12 successful

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jens <1742418+1cu@users.noreply.github.com>
2026-04-27 08:00:49 +02:00
Alex Strutsysnkyi
2856feae8d fix(publish): handle category-suggestion radio picker fallback (#1013)
## Summary

Kleinanzeigen's React redesign shows a category-suggestion radio picker
(`fieldset#ad-category-picker`, radios `name="category-suggestions"`) on
the posting form when the configured category path cannot be resolved by
the category-change flow (outdated or ambiguous path).

Before this fix, `__set_category` did not detect the picker, so
submission proceeded with an empty hidden `categoryId`, the server
rejected it, and the retry loop tripped the submit-limit duplicate-guard
**without publishing** — surfacing as:

```
[WARN] Versuch 1/3 für [<title>] hat die Submit-Grenze erreicht, ist aber fehlgeschlagen: submission may have succeeded before failure. Kein erneuter Versuch, um doppelte Anzeigen zu vermeiden.
[WARN] Manuelle Wiederherstellung für [<title>] erforderlich.
```

## Change

New helper `__resolve_category_suggestions(category)` runs after the
category-change "Weiter" click:

- If `fieldset#ad-category-picker` is not present → no-op.
- If present: match path segments of configured `category` (deepest
first) against the offered radio `value` attrs. Click the associated
`<label for="...">` (the radio input itself is `sr-only` so label is the
click target).
- If the picker is present but no segment matches → raise `TimeoutError`
listing the offered IDs so the user can correct the ad's `category`.
- If the picker is present but no radios have rendered yet (transient /
unrelated fieldset) → skip silently so we don't false-positive on DOM
reuse.

## Test plan

- [x] New `TestCategorySuggestionPicker` class with 4 tests:
  - picker absent → no `web_find_all` / `web_click`
- picker present, leaf segment matches → label click targets
`#ad-category-picker`
- picker present, no match → `TimeoutError` with offered IDs in message
  - picker present, deepest segment preferred over parent
- [x] Updated `test_set_category_uses_probe_for_auto_selected_marker` to
use a selective `probe` side-effect (returns the marker for
`ad-category-path`, `None` for `ad-category-picker`) so the test no
longer implicitly depends on a single `web_probe` call.
- [x] `pdm run pytest tests/unit -q` — 1044 passed, 4 skipped
- [x] `pdm run ruff check src/kleinanzeigen_bot/__init__.py
tests/unit/test_init.py` — clean
- [x] Translations added under `__resolve_category_suggestions` in
`translations.de.yaml` for all three strings
(`test_all_log_messages_have_translations[de]` passes).

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Automatically detects Kleinanzeigen’ category-suggestion picker and
matches configured category paths against offered suggestions.

* **Bug Fixes**
* Fails fast when a category cannot be deterministically resolved (no
retries), and logs/skips affected ads.
* Improved error feedback listing available suggestion IDs when no match
is found.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Jens <1742418+1cu@users.noreply.github.com>
2026-04-24 15:23:28 +02:00
Jens
96734cc5ed docs: clarify download naming examples (#1016)
## ℹ️ Description
Documentation-only update to clarify how `ad_files` differs from
download naming templates and how published IDs are handled.

- Link to the related discussion:
https://github.com/Second-Hand-Friends/kleinanzeigen-bot/discussions/839
- Describe the motivation and context for this change: reduce confusion
around downloaded ad naming and local file behavior.

## 📋 Changes Summary
- Clarified `ad_files` as a file-selection glob, separate from download
naming.
- Added concrete sample ID/title render examples for folder and file
templates.
- Documented that publish updates the YAML `id` in place and does not
auto-rename local files or folders.
- Regenerated config docs and schema artifacts to keep generated output
in sync.

### ⚙️ Type of Change
Select the type(s) of change(s) included in this pull request:
- [ ] 🐞 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**
* Expanded ad configuration and setup documentation with improved
guidance on file pattern matching and download directory configuration.
* Clarified how placeholder templates work in file naming, including
required and optional parameters.
* Added comprehensive examples demonstrating template rendering and
configuration best practices.
* Documented republish behavior and workflow guidance for managing
downloaded ad files.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-23 22:15:01 +02:00
kleinanzeigen-bot-tu[bot]
78e9a82467 chore: Update Python dependencies (#1015)
✔ Update typer 0.24.1 -> 0.24.2 successful
  ✔ Update idna 3.12 -> 3.13 successful
  ✔ Update click 8.3.2 -> 8.3.3 successful
  ✔ Update mss 10.1.0 -> 10.2.0 successful
  ✔ Update certifi 2026.2.25 -> 2026.4.22 successful
  ✔ Update pathspec 1.0.4 -> 1.1.0 successful
  ✔ Update pyinstaller 6.19.0 -> 6.20.0 successful

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-23 21:18:40 +02:00
Jens
a0011e6104 docs: introduce AGENTS.md (#1014)
## ℹ️ Description
Introduce `AGENTS.md` as the repo's first agent playbook and contributor
quick guide.

- Link to the related issue(s): N/A
- Describe the motivation and context for this change.

## 📋 Changes Summary

- Added `AGENTS.md` to provide repo-specific guidance for contributors
and AI agents.
- Documented the source-of-truth hierarchy, testing expectations, PR
requirements, and validation steps.
- Kept the guidance concise and aligned with the existing repo docs and
workflows.

### ⚙️ 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)
- [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

* **Documentation**
* Added contributor guidelines documenting repository standards,
conventions, testing practices, and pre-merge validation steps.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-22 22:34:25 +02:00
Yannik Sembritzki
d26f39de03 fix(publish): persist individual shipping costs past React re-render (#1011) 2026-04-22 16:10:41 +02:00
kleinanzeigen-bot-tu[bot]
53d0c9646e chore: Update Python dependencies (#1012)
✔ Update idna 3.11 -> 3.12 successful
  ✔ Update mypy 1.20.1 -> 1.20.2 successful

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-22 13:43:44 +02:00
kleinanzeigen-bot-tu[bot]
2089115b64 chore: Update Python dependencies (#1010) 2026-04-21 20:03:31 +02:00
kleinanzeigen-bot-tu[bot]
5a4c9a4bc9 chore: Update Python dependencies (#1008) 2026-04-20 19:11:02 +02:00
dependabot[bot]
71dcfbf818 ci(deps): bump the all-actions group with 2 updates (#1009) 2026-04-20 19:10:45 +02:00
Jens
0c5083192b chore: add a link to kleinanzeigen-bot-ui (#1007)
## ℹ️ Description
*Provide a concise summary of the changes introduced in this pull
request.*

- Link to the related issue(s): Discussion #997 
- Add a link to the ui project at
https://github.com/bkd3sign/kleinanzeigen-bot-ui

## 📋 Changes Summary

Just the additional link in the README.md

### ⚙️ 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

* **Documentation**
* Added reference to a related TypeScript-based web interface tool in
the project documentation.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-18 14:07:46 +02:00
Jens
fd3bf64a12 fix(download): retry stale staging cleanup on Windows (#1005)
## ℹ️ Description
*Fix intermittent Windows cleanup failures for stale staging dirs in
issue #998.*

- Link to the related issue(s): Issue #998
- Retry cleanup of stale `.tmp-*` dirs while preserving the existing
abort-on-dirty-dir behavior.

## 📋 Changes Summary
- Add a retrying `rmtree` helper with Windows read-only recovery.
- Reuse it for stale staging, rollback staging, and backup cleanup.
- Add regression tests for transient, persistent, and non-retryable
cleanup failures.

### ⚙️ 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**
* More reliable cleanup during extraction: rmtree operations now retry
on transient permission/OS errors and include improved Windows
permission handling, reducing failed or stalled extractions.
* **Tests**
* Expanded and consolidated unit tests covering retry behavior, error
classification, Windows vs POSIX cleanup handling, and various
failure/success scenarios to ensure robustness.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-17 14:22:55 +02:00
kleinanzeigen-bot-tu[bot]
2f869f8420 chore: Update Python dependencies (#1006)
✔ Update pydantic 2.13.1 -> 2.13.2 successful
  ✔ Update pydantic-core 2.46.1 -> 2.46.2 successful
  ✔ Update ruff 0.15.10 -> 0.15.11 successful
  ✔ Update basedpyright 1.39.0 -> 1.39.2 successful

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-17 14:12:18 +02:00
Jens
b2970eb4f0 feat(delete): add after_delete config for post-delete YAML cleanup (#1000)
## ℹ️ Description

- Link to the related issue(s): Issue #994
- Adds a `deleting.after_delete` config toggle that controls what
happens to the local ad YAML file after the standalone `delete` command
runs. Three modes: `NONE` (default, no change), `RESET` (clear
server-side metadata so the ad is eligible for republication as "new"),
`DISABLE` (set `active: false` so the ad is skipped until manually
re-enabled).

## 📋 Changes Summary

- Added `DeletingConfig` model with `after_delete: Literal["NONE",
"RESET", "DISABLE"]` field (default `"NONE"`)
- Added `deleting: DeletingConfig` field to `Config` class
- Added `_apply_after_delete_policy()` helper that mutates `Ad` model
and dict in-place based on mode
- Integrated policy in `delete_ads` with detection heuristic: cleanup
runs when `delete_ad` returns `True` (200) or when `ad_cfg.id`
transitions from non-None to None (404)
- Policy does NOT apply during publish-time `delete_ad` call sites —
only the standalone delete command
- Regenerated `docs/config.default.yaml` and
`schemas/config.schema.json`

### ⚙️ 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**
* Added configurable post-delete cleanup options to control how local ad
records are handled after deletion (leave, reset metadata, or disable).

* **Bug Fixes**
* More accurate detection of attempted vs. successful deletions and
correct incrementing of deletion counts.
* Ensure metadata cleanup is skipped when no targets are found while
still handling 404-style removals appropriately.

* **Tests**
  * Added unit tests covering post-delete behaviours and edge cases.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-17 07:58:49 +02:00
Alex Strutsysnkyi
2b7a8bfde0 fix(publish): handle missing PICKUP toggle (books/comics) and map legacy German condition_s values (#999)
## ℹ️ Description

Two small publish-time failures observed against the current
Kleinanzeigen DOM for ads in specific categories.

### 1. PICKUP skip for categories without a shipping toggle

Books (`76/77`) and comics (`76/77/15156`) render the "Eigenschaften und
Versand" fieldset with only price/description/images — there is no
`ad-shipping-enabled-yes/no` radio because the site treats these
categories as pickup-only. Today `__set_shipping` raises `Failed to set
shipping attribute for type 'PICKUP'!` because the radio is absent.

Fix: probe for the radio first; if absent, treat that as "already
PICKUP" (which matches the configured `shipping_type: PICKUP`) and
return without an error.

Live DOM diagnostic captured during the failure:
```
{"url":"…/p-anzeige-aufgeben-schritt2.html",
 "ids":[],
 "headings":[{"tag":"LEGEND","text":"Eigenschaften und Versand"}]}
```
No `[id*=shipping]` / `[id*=versand]` elements exist for these
categories.

### 2. German → English condition value mapping

The condition dialog radios now use English API codes:

```
{value:"new"}, {value:"like_new"}, {value:"ok"}, {value:"alright"}
```

Ad YAMLs written against the previous DOM use German tokens (`neu`,
`wie_neu`, `sehr_gut`, `gut`, `in_ordnung`, `defekt`), which no longer
match any radio and produce `Failed to set attribute 'condition_s'`.

Fix: try the configured value first (preserves behaviour for English
configs), then fall back to the mapped equivalent. `wie_neu` and the
legacy `sehr_gut` both collapse to `like_new` because the site now
exposes a single "like new" tier.

Mapping:

| Legacy German   | Current English |
|-----------------|-----------------|
| `neu`           | `new`           |
| `wie_neu`       | `like_new`      |
| `sehr_gut`      | `like_new`      |
| `gut`           | `ok`            |
| `in_ordnung`    | `alright`       |
| `defekt`        | `defect`        |

## 📋 Changes Summary

- `__set_shipping`: probe `ad-shipping-enabled-no` before the state
check; return on absence with a debug log.
- `__set_condition`: switch to `web_probe` with a candidate-value list
(configured value → mapped API code); preserves the existing
label-click-then-radio-click flow.
- New constant `_CONDITION_GERMAN_TO_API` mapping legacy tokens to
current API codes.

No behavioural change for ads in categories that do render the shipping
toggle, or for ad YAMLs already using English condition codes.

### ⚙️ 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.
- [ ] I have tested my changes and ensured that all tests pass (`pdm run
test`) — verified manually against the live site; unit tests for the new
branches not yet added.
- [ ] I have formatted the code (`pdm run format`).
- [ ] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary (no user-facing docs
change needed; the YAML token format is unchanged because the old tokens
continue to work via the fallback map).

Happy to add focused unit tests for both branches and run `pdm run
format`/`lint` if the direction looks right.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved compatibility with German condition values for item listings.
* Enhanced robustness when setting item conditions by probing multiple
candidate values instead of failing immediately.
* Fixed pickup-only shipping type handling to gracefully manage missing
toggle controls.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-17 07:45:54 +02:00
kleinanzeigen-bot-tu[bot]
5fd67ed851 chore: Update Python dependencies (#996) 2026-04-16 14:03:20 +02:00
Jens
26a199a7a6 fix(delete): report actual deletion outcome and reduce log noise (#995)
## ℹ️ Description

- Link to the related issue(s): Issue #991
- `delete_ad` previously always returned `True` and always cleared
`ad_cfg.id`, regardless of whether the ad was actually found or deleted.
The title-based and ID-based matching paths behaved differently, and 404
responses were treated inconsistently. This made it impossible to
distinguish "ad confirmed deleted" from "ad already gone" from "no
matching ad found" — causing misleading log output and preventing
accurate deletion tracking.

## 📋 Changes Summary

- Restructure `delete_ad` into two clear phases: **Phase A** (unified
target selection via `ids_to_delete: set[int]`) and **Phase B** (shared
delete execution loop)
- Return `True` only when at least one delete request returned HTTP 200
(confirmed deleted); return `False` for no matches or all 404s
- Clear `ad_cfg.id = None` whenever a delete was **attempted** (both 200
and 404), but preserve it on early return (no targets found)
- Fix falsy-zero bug: `elif ad_cfg.id:` → `elif ad_cfg.id is not None:`
to correctly handle `id == 0`
- Replace `published_ad.get("id", -1)` with explicit None check +
`try/except` around `int()` to avoid silent invalid delete attempts
- Update `delete_ads` orchestrator to track `deleted_count` accurately
- Replace 3 old tests with 9 behavior-focused tests covering: ID-only
match, title match, multiple title matches, title+ID deduplication, zero
ID, invalid published ad ID, 404 handling, no-match early return,
exception preserves ID

### ⚙️ 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**
* Summary now reports the actual number of ads deleted (not processed
attempts).
* Skips invalid/missing IDs and avoids unnecessary page loads when
nothing to delete.
* Treats HTTP 404 as missing ad (warn) while counting only successful
deletions.
* Ensures ad configuration is cleared after deletion attempts; preserves
config if deletion errors occur.

* **Tests**
* Expanded scenario tests covering title/ID deletion, deduplication,
mixed responses, invalid IDs, and error propagation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-15 23:13:21 +02:00
Jens
69d5b69310 fix(publish): add confirmation redirect fallback to recover ad ID (#993)
## ℹ️ Description

- Link to the related issue(s): Issue #991 (related delete bug), Issue
#992 (diagnostics follow-up)
- During a publish run, the Kleinanzeigen confirmation page
auto-redirected to the user's ad management page
(`m-meine-anzeigen.html`) before the bot's URL polling could observe the
confirmation URL containing the new ad ID. This caused
`PublishSubmissionUncertainError` even though the ad was actually
published successfully (verified via diagnostics HTML showing ad ID
3382410263 in Belen tracking data). The fix adds a two-layer fallback to
recover the ad ID from page tracking data when URL polling times out.

## 📋 Changes Summary

- Add `_try_recover_ad_id_from_redirect()` helper with two-layer
fallback: Layer 1 checks `document.referrer` for the confirmation URL;
Layer 2 scans inline `<script>` tags for the confirmation URL pattern
containing `adId`.
- Restructure the confirmation block in `publish_ad` to attempt recovery
before raising `PublishSubmissionUncertainError`. On successful
recovery, logs a WARNING about the fast redirect and continues normally.
- Add defensive `RuntimeError` guard (unreachable in current code) with
i18n-translated message including `ad_file` context.
- Add DEBUG log for the deferred title set (explaining why title appears
empty before submit).
- Add stale-ID assumption comment documenting why `document.referrer`
cannot contain a previous ad's ID.
- Add German translations for new WARNING log message and RuntimeError
guard message.
- DRY test setup: extract `_mock_post_submit_dependencies()`
(ExitStack-based context manager) and `_build_publish_ad_cfg()` helpers,
eliminating ~80 lines of duplicated mock boilerplate across 4
integration tests.
- Test scout cleanup: merge duplicate
`test_extract_ad_id_handles_empty_referrer` +
`test_extract_ad_id_handles_none_referrer` into one parametrized test
(both hit identical code path via `str(... or "")`).
- Move `web_sleep` mock to unconditional patch list, eliminating real
`asyncio.sleep` delays in error-path 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.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved ad publication reliability with a new fallback for retrieving
confirmation details when initial extraction fails.
* Expanded error handling to mark uncertain submissions appropriately
and prevent silent failures.
* Added targeted debug logging to aid troubleshooting of the publish
flow.

* **Tests**
* Added comprehensive unit tests covering fallback recovery,
confirmation timing scenarios, and error paths.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-15 20:03:38 +02:00
kleinanzeigen-bot-tu[bot]
31d1f53ab6 chore: Update Python dependencies (#990)
✔ Update filelock 3.25.2 -> 3.28.0 successful
  ✔ Update packaging 26.0 -> 26.1 successful

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-15 16:06:14 +02:00
sebthom
dfe73bc81e ci: replace github-app app id with client id 2026-04-13 20:27:08 +02:00
Jens
ae6c1cfde7 enh(publish): normalize continue to break at max-retries exhaustion (#989)
## ℹ️ Description
*Provide a concise summary of the changes introduced in this pull
request.*

- Link to the related issue(s): Issue #976
- The `publish_ads` method used `continue` to exit its inner retry loop
at max-retries exhaustion, while the newer `update_ads` method correctly
uses `break`. Since this is always the last loop iteration, both produce
identical runtime behavior, but `continue` reads as "skip to the next
attempt" while `break` reads as "done with this ad's retries." This
normalizes the control flow to match the consistent pattern.

## 📋 Changes Summary

- Changed `continue` to `break` in `publish_ads` retry exhaustion path
(line 2226 of `src/kleinanzeigen_bot/__init__.py`)
- No behavior change — purely a readability improvement
- Matches the identical pattern already used in `update_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)
- [ ] 💥 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 error handling for ad publishing failures, ensuring proper
status reporting when an ad fails to publish after maximum retry
attempts.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-13 19:14:37 +02:00
dependabot[bot]
154af7c206 ci(deps): bump the all-actions group with 3 updates (#987)
Bumps the all-actions group with 3 updates:
[actions/upload-artifact](https://github.com/actions/upload-artifact),
[actions/create-github-app-token](https://github.com/actions/create-github-app-token)
and
[peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request).

Updates `actions/upload-artifact` from 7.0.0 to 7.0.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/upload-artifact/releases">actions/upload-artifact's
releases</a>.</em></p>
<blockquote>
<h2>v7.0.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Update the readme with direct upload details by <a
href="https://github.com/danwkennedy"><code>@​danwkennedy</code></a> in
<a
href="https://redirect.github.com/actions/upload-artifact/pull/795">actions/upload-artifact#795</a></li>
<li>Readme: bump all the example versions to v7 by <a
href="https://github.com/danwkennedy"><code>@​danwkennedy</code></a> in
<a
href="https://redirect.github.com/actions/upload-artifact/pull/796">actions/upload-artifact#796</a></li>
<li>Include changes in typespec/ts-http-runtime 0.3.5 by <a
href="https://github.com/yacaovsnc"><code>@​yacaovsnc</code></a> in <a
href="https://redirect.github.com/actions/upload-artifact/pull/797">actions/upload-artifact#797</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/upload-artifact/compare/v7...v7.0.1">https://github.com/actions/upload-artifact/compare/v7...v7.0.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="043fb46d1a"><code>043fb46</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/upload-artifact/issues/797">#797</a>
from actions/yacaovsnc/update-dependency</li>
<li><a
href="634250c138"><code>634250c</code></a>
Include changes in typespec/ts-http-runtime 0.3.5</li>
<li><a
href="e454baaac2"><code>e454baa</code></a>
Readme: bump all the example versions to v7 (<a
href="https://redirect.github.com/actions/upload-artifact/issues/796">#796</a>)</li>
<li><a
href="74fad66b98"><code>74fad66</code></a>
Update the readme with direct upload details (<a
href="https://redirect.github.com/actions/upload-artifact/issues/795">#795</a>)</li>
<li>See full diff in <a
href="bbbca2ddaa...043fb46d1a">compare
view</a></li>
</ul>
</details>
<br />

Updates `actions/create-github-app-token` from 3.0.0 to 3.1.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/create-github-app-token/releases">actions/create-github-app-token's
releases</a>.</em></p>
<blockquote>
<h2>v3.1.1</h2>
<h2><a
href="https://github.com/actions/create-github-app-token/compare/v3.1.0...v3.1.1">3.1.1</a>
(2026-04-11)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>improve error message when app identifier is empty (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/362">#362</a>)
(<a
href="07e2b76066">07e2b76</a>),
closes <a
href="https://redirect.github.com/actions/create-github-app-token/issues/249">#249</a></li>
</ul>
<h2>v3.1.0</h2>
<h1><a
href="https://github.com/actions/create-github-app-token/compare/v3.0.0...v3.1.0">3.1.0</a>
(2026-04-11)</h1>
<h3>Bug Fixes</h3>
<ul>
<li><strong>deps:</strong> bump p-retry from 7.1.1 to 8.0.0 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/357">#357</a>)
(<a
href="3bbe07d928">3bbe07d</a>)</li>
</ul>
<h3>Features</h3>
<ul>
<li>add <code>client-id</code> input and deprecate <code>app-id</code>
(<a
href="https://redirect.github.com/actions/create-github-app-token/issues/353">#353</a>)
(<a
href="e6bd4e6970">e6bd4e6</a>)</li>
<li>update permission inputs (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/358">#358</a>)
(<a
href="076e9480ca">076e948</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="1b10c78c78"><code>1b10c78</code></a>
build(release): 3.1.1 [skip ci]</li>
<li><a
href="07e2b76066"><code>07e2b76</code></a>
fix: improve error message when app identifier is empty (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/362">#362</a>)</li>
<li><a
href="ea0121618b"><code>ea01216</code></a>
ci: remove publish-immutable-action workflow (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/361">#361</a>)</li>
<li><a
href="7bd0371149"><code>7bd0371</code></a>
build(release): 3.1.0 [skip ci]</li>
<li><a
href="e6bd4e6970"><code>e6bd4e6</code></a>
feat: add <code>client-id</code> input and deprecate <code>app-id</code>
(<a
href="https://redirect.github.com/actions/create-github-app-token/issues/353">#353</a>)</li>
<li><a
href="076e9480ca"><code>076e948</code></a>
feat: update permission inputs (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/358">#358</a>)</li>
<li><a
href="3bbe07d928"><code>3bbe07d</code></a>
fix(deps): bump p-retry from 7.1.1 to 8.0.0 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/357">#357</a>)</li>
<li><a
href="28a99e369c"><code>28a99e3</code></a>
build(deps-dev): bump c8 from 10.1.3 to 11.0.0</li>
<li><a
href="4df50600ef"><code>4df5060</code></a>
build(deps-dev): bump open-cli from 8.0.0 to 9.0.0</li>
<li><a
href="4843c538d9"><code>4843c53</code></a>
build(deps-dev): bump the development-dependencies group with 3
updates</li>
<li>See full diff in <a
href="f8d387b68d...1b10c78c78">compare
view</a></li>
</ul>
</details>
<br />

Updates `peter-evans/create-pull-request` from 8.1.0 to 8.1.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/peter-evans/create-pull-request/releases">peter-evans/create-pull-request's
releases</a>.</em></p>
<blockquote>
<h2>Create Pull Request v8.1.1</h2>
<h2>What's Changed</h2>
<ul>
<li>build(deps-dev): bump the npm group with 2 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4305">peter-evans/create-pull-request#4305</a></li>
<li>build(deps): bump minimatch by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4311">peter-evans/create-pull-request#4311</a></li>
<li>build(deps): bump the github-actions group with 2 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4316">peter-evans/create-pull-request#4316</a></li>
<li>build(deps): bump <code>@​tootallnate/once</code> and
jest-environment-jsdom by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4323">peter-evans/create-pull-request#4323</a></li>
<li>build(deps-dev): bump undici from 6.23.0 to 6.24.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4328">peter-evans/create-pull-request#4328</a></li>
<li>build(deps-dev): bump flatted from 3.3.1 to 3.4.2 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4334">peter-evans/create-pull-request#4334</a></li>
<li>build(deps): bump picomatch by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4339">peter-evans/create-pull-request#4339</a></li>
<li>build(deps-dev): bump handlebars from 4.7.8 to 4.7.9 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4344">peter-evans/create-pull-request#4344</a></li>
<li>build(deps-dev): bump the npm group with 3 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4349">peter-evans/create-pull-request#4349</a></li>
<li>fix: retry post-creation API calls on 422 eventual consistency
errors by <a
href="https://github.com/peter-evans"><code>@​peter-evans</code></a> in
<a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4356">peter-evans/create-pull-request#4356</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/peter-evans/create-pull-request/compare/v8.1.0...v8.1.1">https://github.com/peter-evans/create-pull-request/compare/v8.1.0...v8.1.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="5f6978faf0"><code>5f6978f</code></a>
fix: retry post-creation API calls on 422 eventual consistency errors
(<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4356">#4356</a>)</li>
<li><a
href="d32e88dac7"><code>d32e88d</code></a>
build(deps-dev): bump the npm group with 3 updates (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4349">#4349</a>)</li>
<li><a
href="8170bccad1"><code>8170bcc</code></a>
build(deps-dev): bump handlebars from 4.7.8 to 4.7.9 (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4344">#4344</a>)</li>
<li><a
href="00418193b4"><code>0041819</code></a>
build(deps): bump picomatch (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4339">#4339</a>)</li>
<li><a
href="b993918c85"><code>b993918</code></a>
build(deps-dev): bump flatted from 3.3.1 to 3.4.2 (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4334">#4334</a>)</li>
<li><a
href="36d7c8468b"><code>36d7c84</code></a>
build(deps-dev): bump undici from 6.23.0 to 6.24.0 (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4328">#4328</a>)</li>
<li><a
href="a45d1fb447"><code>a45d1fb</code></a>
build(deps): bump <code>@​tootallnate/once</code> and
jest-environment-jsdom (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4323">#4323</a>)</li>
<li><a
href="3499eb6183"><code>3499eb6</code></a>
build(deps): bump the github-actions group with 2 updates (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4316">#4316</a>)</li>
<li><a
href="3f3b473b8c"><code>3f3b473</code></a>
build(deps): bump minimatch (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4311">#4311</a>)</li>
<li><a
href="6699836a21"><code>6699836</code></a>
build(deps-dev): bump the npm group with 2 updates (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4305">#4305</a>)</li>
<li>See full diff in <a
href="c0f553fe54...5f6978faf0">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-13 18:24:05 +02:00
Jens
a13fb4f106 feat(timing): tune default timeouts and migrate optional probes to web_probe (#985)
## ℹ️ Description

- Link to the related issue(s): Issue #
- Analyzed 524 timing records across 18 sessions from real-world usage
to identify genuine timeout failures vs expected element-absence probes.
Three timeout defaults were too tight for observed latency spikes, and
four call sites were wasting time on retry/backoff for elements that are
intentionally absent in most flows.

## 📋 Changes Summary

### Timeout default changes (`config_model.py` → generated
`config.default.yaml` + `config.schema.json`)

| Key | Old | New | Rationale |
|---|---|---|---|
| `sms_verification` | 4.0 | **5.0** | ~11% margin over observed 4.46s
failure |
| `email_verification` | 4.0 | **5.0** | ~23% margin over observed 4.05s
failure |
| `login_detection` | 10.0 | **12.0** | ~11% margin over observed
10.6–10.8s failures |
| `captcha_detection` | 2.0 | unchanged | No evidence of benefit from
timing data |

### Call-site migration (`__init__.py`)

Migrated 4 optional-probe methods from `web_find` (retry-backed, raises
`TimeoutError`) to `web_probe` (single attempt, returns `None`):

- **`_check_sms_verification`**: `web_find` → `web_probe` with explicit
`sms_verification` timeout, returns early with debug log when absent
- **`_check_email_verification`**: Same pattern with
`email_verification` timeout
- **`_dismiss_consent_banner`**: Eliminated double-lookup (`web_find` +
`web_click`) → single `web_probe` + `element.click()` + `web_sleep()`
- **`_click_gdpr_banner`**: Same double-lookup elimination, added debug
log on absent path

Removed 4 `try/except TimeoutError` wrappers from callers
(`handle_after_login_logic` and `login`) since `web_probe` never raises
`TimeoutError`.

### Performance impact

- Worst-case wait on absent elements reduced from ~19s → 5s (SMS/email
verification with retry/backoff vs single probe)
- GDPR banner clicks: double DOM lookup eliminated → single lookup

### Test cleanup (`test_init.py`)

- **Added 8 behavioral tests** for `_dismiss_consent_banner` (found +
absent), `_check_sms_verification` (found + absent),
`_check_email_verification` (found + absent), and `_click_gdpr_banner`
absent path
- **Removed 3 low-value tests**: `test_handle_after_login_logic`
(structural mock wiring),
`test_is_logged_in_returns_true_with_alternative_element` (duplicate
branch),
`test_wait_for_post_auth0_submit_transition_sleep_fallback_when_login_not_confirmed`
(duplicate outcome)
- **Upgraded 1 test**: `test_fill_login_data_and_send` from mock-count
assertions to behavioral argument assertions verifying correct selectors
and credentials

### ⚙️ 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.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Post-login flows now safely handle absent SMS/email prompts and
consent/GDPR banners, avoiding interruptions and clicking banners when
present.

* **Improvements**
* Increased default timeouts for SMS verification, email verification,
and login detection to improve login reliability.

* **Documentation**
* Configuration docs now reference authoritative default timeout values
instead of embedding examples.

* **Tests**
* Strengthened unit tests for login interactions, verification prompts,
and banner dismissal behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-13 18:18:30 +02:00
kleinanzeigen-bot-tu[bot]
fd568637c5 chore: Update Python dependencies (#986)
✔ Update pydantic 2.12.5 -> 2.13.0 successful
  ✔ Update pydantic-core 2.41.5 -> 2.46.0 successful
  ✔ Update mypy 1.20.0 -> 1.20.1 successful

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-13 14:58:09 +02:00
Jens
cf61c99233 fix(core): correct progress counter in publish/update ad loops (#984)
## ℹ️ Description
- Link to the related issue(s): Issue #977
- The "Processing N/M" progress counter in `publish_ads()`,
`update_ads()`, and `update_content_hashes()` used the same `count`
variable for both display and tally. Since `count` only incremented for
ads that passed skip checks, paused/skipped ads caused the counter to
stall or repeat (e.g. `1/5, 1/5, 2/5`). This decouples the display
counter from the processing tally using `enumerate()`.

## 📋 Changes Summary

- **`publish_ads()`**: Use `enumerate(ad_cfgs, start=1)` with `idx` for
the display counter instead of `count + 1`.
- **`update_ads()`**: Same enumerate swap; moved the "Processing" log
before both skip checks (not-found and paused) so the counter is always
sequential even for skipped ads.
- **`update_content_hashes()`**: Same enumerate swap; renamed `count` →
`changed` for clarity since `idx` is now the display counter.
- **`delete_ads()`**: Left untouched (already correct — no skip paths,
counter increments before log).
- Added `TestDisplayCounterProgression` class with 4 regression tests
covering counter progression with paused, active, and not-found ads.
- Removed duplicate `test_update_ads_summary_reports_mixed_outcomes`
(same scenario as continuation test, only unique assertion was a log
format-string template check).
- Removed duplicate `location_non_convergence` parametrize case (second
TimeoutError hitting the same branch).

### ⚙️ 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**
* Progress counter now accurately reports only changed items instead of
all processed items during content updates.

* **Tests**
* Added comprehensive regression tests for progress counter display
across ad processing workflows, including edge cases with paused and
not-found ads.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-13 11:55:36 +02:00
Jens
04de9025f6 fix(ad): reject titles exceeding 65 characters (#982)
## ℹ️ Description

- Link to the related issue(s): Issue #979
- kleinanzeigen.de enforces a hard 65-character limit on ad titles. When
a title exceeds this limit, the site silently rejects it, causing the
bot to hang during publishing. This fix adds early validation so users
get a clear, actionable error before any browser interaction occurs.

## 📋 Changes Summary

- Added `MAX_TITLE_LENGTH = 65` constant to `ad_model.py`
- Applied `Field(max_length=MAX_TITLE_LENGTH)` to the `title` field in
`AdPartial`, leveraging Pydantic's built-in validation (semantic error
type, auto-localized messages)
- Regenerated `schemas/ad.schema.json` — now includes `"maxLength": 65`
on the title property
- Added parametrized test covering both boundary cases (65 chars
accepted, 66 chars rejected)
- Cleaned up stale defensive `model_construct` tests that tested
unreachable code paths

### ⚙️ 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

* **Bug Fixes**
* Enforced both minimum (10) and maximum (65) title length limits and
improved validation so titles outside bounds produce clear validation
errors.

* **Tests**
* Added unit tests covering title-length boundary behavior and updated
related test setups.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-12 23:50:29 +02:00
Jens
c346720743 refactor(publish): rename __react_input to __set_input_value (#980)
## ℹ️ Description

- Link to the related issue(s): Issue #930
- The method `__react_input` uses the native
`HTMLInputElement`/`HTMLTextAreaElement` value setter — a standard DOM
API, not a React-specific one. The rename makes the name match what the
method actually does.

## 📋 Changes Summary

- Rename `__react_input` → `__set_input_value` (definition + 6 call
sites + test mock)
- Update docstring: "React-controlled" → "framework-controlled"

### ⚙️ Type of Change
- [x] 🐞 Bug fix (non-breaking change which fixes an issue)

##  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

* **Refactor**
* Internal improvements to input handling mechanisms with updated method
naming and documentation to enhance code maintainability and clarity
across form field operations. Test infrastructure has been aligned with
these changes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-12 19:20:59 +02:00
kleinanzeigen-bot-tu[bot]
f659c98aca chore: ✔ Update rich 14.3.4 -> 15.0.0 (#978)
✔ Update rich 14.3.4 -> 15.0.0 successful

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-12 19:00:21 +02:00
Jens
668768bd1b fix(update): harden per-ad retries and location convergence (#975)
## ℹ️ Description
This PR implements issue #940 by making the update flow resilient per ad
and hardening ZIP/city selection so unresolved location states fail
closed instead of silently proceeding.

- Link to the related issue(s): Issue #940
- Describe the motivation and context for this change.

## 📋 Changes Summary
- Added per-ad retry/isolation semantics to `update_ads` (re-raise
cancellation, no-retry for `PublishSubmissionUncertainError`,
retry-and-continue for `TimeoutError`/`ProtocolException`, non-fatal
publish-result timeout, mixed success/failure summary).
- Hardened contact/location handling: ZIP set now fails closed for the
ad, city combobox selection waits for option load + post-click
convergence, and ambiguous city-only matches now fail closed.
- Improved city selection read-back robustness by preferring live DOM
value/text (`element.apply`) over stale serialized attributes.
- Renamed `PUBLISH_MAX_RETRIES` to `SUBMISSION_MAX_RETRIES` and updated
related diagnostics/tests/translations.
- Added focused unit coverage for update resilience and contact/location
hardening.
- Live verification succeeded on this branch using real ads:
- `pdm run python -m kleinanzeigen_bot -v --ads=due publish` (2 ads
republished)
- `pdm run python -m kleinanzeigen_bot -v --ads=changed update` (2 ads
updated)

Mention any dependencies, configuration changes, or additional
requirements introduced.
No new dependencies 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

* **Bug Fixes**
* Improved per-ad retry behavior for submissions: retries occur only for
transient failures, certain uncertain failures stop retries immediately,
and confirmation timeouts no longer trigger retries.
* Stricter contact handling: zip code and city selection are now
validated more rigorously and fail fast on ambiguous or non-converging
selections.
* Enhanced diagnostics and clearer completion reporting for mixed
success/failure runs.

* **Tests**
* Added comprehensive unit tests covering retry resilience and
contact-location hardening.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-12 10:46:28 +02:00
Jens
1892399fbc fix(update): stabilize edit-flow probes and image cleanup (#973)
## ℹ️ Description
This PR fixes update-flow instability by switching absence-valid UI
checks to probe semantics and by fixing image replacement on edit pages.

- Link to the related issue(s): Issue #N/A
- Describe the motivation and context for this change.
- The update flow used stale thumbnail/remove selectors on edit pages,
so existing images were not removed and configured images were uploaded
again, causing duplicates.

## 📋 Changes Summary
- Updated update/publish optional checks (captcha in publish flow,
buy-now optional branch, phone field presence,
imprint/payment/without-image prompts) to use probe-first handling to
avoid noisy timeout retries when elements are absent by design.
- Reworked pre-upload cleanup to use the current edit-page remove
control (`button[aria-label='Bild entfernen']`) and hidden image
markers, ensuring old images are removed before uploading configured
images.
- Simplified upload completion detection to marker-delta tracking and
reduced retry noise for marker lookups.
- Added/updated regression tests for probe behavior and image
cleanup/upload flows.
- Performed targeted test-scout cleanup in `tests/unit/test_init.py`
(removed low-signal URL concatenation/log-wording checks and
consolidated duplicate workspace-skip tests).
- Updated German translation key for the new image-removal log message.
- No additional dependencies or configuration changes required.

### ⚙️ 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

* **New Features**
* Marker-based image tracking for upload, cleanup, and accurate progress
counting.

* **Bug Fixes**
* More reliable captcha detection that returns early when absent and
handles present captchas consistently.
* Safer “buy now” / sell-directly handling with explicit error when
required control is missing.
  * Robust image removal loop with clear failure reporting.
  * Simplified post-submit/payment probing to reduce flakiness.
* Improved phone-field visibility handling and clearer shipping-cost
validation messages.

* **Tests**
* Expanded/refactored tests for image cleanup, upload completion,
captcha, buy-now scenarios, and validation rules; consolidated
workspace-resolution tests.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-11 21:33:49 +02:00
kleinanzeigen-bot-tu[bot]
8214ba312f chore: ✔ Update rich 14.3.3 -> 14.3.4 (#974)
✔ Update rich 14.3.3 -> 14.3.4 successful

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-11 17:45:34 +02:00
Jens
c65d1b67b0 test: improve test quality by replacing brittle assertions (#972)
## ℹ️ Description
This PR improves overall test quality by removing brittle assertion
patterns, consolidating duplicate coverage, and migrating tests to
clearer ownership while preserving behavior coverage.

- Link to the related issue(s): Issue #N/A
- Describe the motivation and context for this change.
- Reduce flaky/wording-coupled tests and improve long-term
maintainability.
- Keep high-signal behavior assertions while preserving existing
coverage paths.

## 📋 Changes Summary
- Replaced gettext/log-wording-dependent assertions in price reduction
tests with stable behavior-oriented checks.
- Cleaned low-value log assertions in extract/pagination/bot tests.
- Simplified mixed-category assertions in init/diagnostics tests and
removed redundant debug log checks.
- Extracted shared `PYTEST_CURRENT_TEST` fixture handling in chrome
version tests.
- Removed duplicate/subsumed tests in ad-model and update-checker
suites; simplified brittle full-message log checks.
- Parametrized legacy migration tests in config-model for clearer, less
repetitive coverage.
- Removed tests in `test_bot.py` that were subsumed by `test_init.py`,
then migrated remaining unique tests and deleted `test_bot.py`.
- No runtime behavior changes; this is test-suite quality and
maintainability work.

### ⚙️ 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

* **Tests**
* Refactored and simplified the unit test suite for maintainability:
consolidated legacy migration checks into parametrized tests, introduced
shared fixtures, and streamlined test setup/formatting.
* Removed many log-capture assertions and several redundant or
overly-specific tests, and adjusted assertions to focus on functional
outcomes.
  * No production code or user-facing behavior changed.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-10 17:43:47 +02:00
Jens
c3584d0b2e feat(pricing): support update-mode reductions and verify previews (#970)
## ℹ️ Description
Automatic price reduction now works reliably during `update` runs
(MODIFY mode), not only during `publish`, while staying backward
compatible for existing configurations. The verify flow also explains
what will happen in both modes before any command touches live ads.

- Link to the related issue(s): Issue # N/A
- Describe the motivation and context for this change.
- Update-mode reductions needed to be explicitly opt-in so existing
setups remain unchanged by default.
- Pricing behavior had to stay deterministic across retries and mixed
publish/update workflows.
- Preview output and documentation now mirror real runtime behavior so
configuration decisions are easier to trust.

## 📋 Changes Summary
- Added `auto_price_reduction.on_update` (`bool`, default `false`) in
model/schema so update-mode reductions are explicitly opt-in.
- Refactored price reduction flow into decision + apply steps:
  - `evaluate_auto_price_reduction(...)` computes mode-aware outcomes
- `apply_auto_price_reduction(...)` applies mutations and logs from that
decision
- Implemented restore-first behavior so effective reduced price is
recomputed from base `price` + `price_reduction_count` before deciding a
new cycle.
- Updated `verify` to preview both publish and update outcomes.
- Kept update-mode semantics explicit:
  - `delay_days` applies
  - `delay_reposts` is ignored in MODIFY mode
- Fixed retry idempotency in `publish_ads` by restoring baseline `price`
and `price_reduction_count` before each retry.
- Addressed follow-up review feedback:
- no-visible-change cycles advance counters (for fractional
accumulation)
  - verify output stays non-misleading for no-visible-change cases
  - tightened a unit assertion from `>= 1` to `== 1`
- tightened smoke behavior for `create-config` when config already
exists
- clarified README/docs and documented `price_reduction_count` as
auto-managed
- Expanded/updated tests for:
  - MODIFY `on_update` behavior
  - restore-first invariant
  - cross-mode interactions
  - retry baseline reset
  - verify mode previews
  - floor-clamped no-visible-change counter advancement

Mention any dependencies, configuration changes, or additional
requirements introduced.
- Configuration change: new optional field
`auto_price_reduction.on_update` (default `false`, backward compatible).
- No external runtime dependency changes.
- Existing configurations continue to work unchanged unless `on_update`
is enabled.

### ⚙️ 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)
- [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**
* `verify` now previews automatic price-reduction outcomes for both
publish and update modes.
* New config option (auto_price_reduction.on_update, default false) to
enable reductions during update runs.

* **Documentation**
* Clarified publish vs. update reduction behavior, timing rules,
restore-first semantics, logging, examples, and troubleshooting.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-10 15:49:15 +02:00
kleinanzeigen-bot-tu[bot]
6482d2dbaf chore: Update Python dependencies (#971)
✔ Update more-itertools 11.0.1 -> 11.0.2 successful
  ✔ Update librt 0.8.1 -> 0.9.0 successful
  ✔ Update ruff 0.15.9 -> 0.15.10 successful

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-10 13:35:21 +02:00
Jens
80eea4e6c2 fix(publish): fix multi-image uploads with real waits and fresh file inputs (#969)
## ℹ️ Description
*Provide a concise summary of the changes introduced in this pull
request.*

- Link to the related issue(s): Issue #958, Issue #964
- Describe the motivation and context for this change.

Publishing ads with many images could fail on first try because two
independent issues stacked:

1) Requested waits were effectively skipped because nodriver
`Tab.sleep()` returns near-instantly (`asyncio.wait(...,
return_when=FIRST_COMPLETED)` with a fast task), so upload pacing and
polling were too aggressive.
2) After the first image upload, the page replaced the hidden
`input[type=file]`; reusing the old element handle meant subsequent
`send_file(...)` calls had no effect.

Technical evidence gathered during live probes:
- Before the sleep fix, a 12-image run produced only ~0.108s cumulative
delay across 17 `web_sleep` calls (expected ~20–40s).
- After switching to `asyncio.sleep`, timing probes showed real waits:
- upload-phase waits (~1.0–2.2s requested) measured ~1019–2238ms actual,
- `web_await` polling waits (0.5s requested) measured ~500–501ms actual.
- Stale-input probe showed `isConnected` becomes `False` after first
upload when reusing the same file input; refetching per image restored
marker increments on each upload.

## 📋 Changes Summary

- Replaced `page.sleep(...)` with `asyncio.sleep(...)` in `web_sleep()`
so requested delays are actually honored.
- Replaced `page.sleep(...)` with `asyncio.sleep(...)` in `web_await()`
polling to avoid near-zero busy-loop behavior.
- Fixed image upload loop to re-fetch `input[type=file]` per image,
preventing stale-element uploads after the first file.
- Updated and added regression tests:
  - timing-based tests for real sleep behavior,
  - per-image file-input refetch behavior,
- consolidated timeout marker tests into one parametrized,
behavior-focused test.
- Live verification after fix: 15-image smoke upload completed with
marker delta `15/15`.

Mention any dependencies, configuration changes, or additional
requirements 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**
* Improved image upload reliability by refetching file input elements
during multi-image uploads, preventing stale element errors.
* Enhanced timeout and delay behavior for more consistent performance
during web scraping operations.

* **Tests**
* Updated tests to verify actual timing behavior and improved error
reporting for upload operations.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-10 08:13:27 +02:00
Jens
5b7621b0de fix(publish): stabilize condition trigger handling and speed probe checks (#968)
## ℹ️ Description
*Provide a concise summary of the changes introduced in this pull
request.*

- Link to the related issue(s): Issue #956, Issue #938
- This change addresses a selector-collision regression where
`condition_s` handling could resolve to shipping controls
(`ad-shipping-options`) and open the wrong dialog, and improves publish
performance by replacing retry-heavy branch probes with a single-attempt
probe API for settled DOM checks.

## 📋 Changes Summary

- Added `web_probe(...)` in `WebScrapingMixin` for single-attempt,
no-retry DOM presence checks (returns `Element | None`).
- Migrated two high-cost probe call sites to `web_probe`:
- category auto-selected marker lookup (`ad-category-path`) in
`__set_category`
- individual shipping price visibility probe
(`ad-individual-shipping-price`) in `__set_shipping`
- Polished `condition_s` handling to avoid condition/shipping trigger
collisions and keep fallback behavior explicit when dialog handling is
not applicable.
- Added/updated high-signal tests for:
  - probe semantics (no retry even when global retries are enabled)
  - migrated category/shipping probe branches
  - condition trigger collision and fallback behavior
- Applied targeted test-scout cleanup in touched files:
  - removed one low-signal version getter test
- consolidated duplicate invalid-pid close-session tests into one
parametrized test
- 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**
* Reduced spurious errors and blocking waits when setting condition,
category, and shipping options; probe-based checks improve automatic
selection, fallback behavior, and avoid incorrect clicks.

* **Tests**
* Expanded and refactored unit tests to cover probe behavior, dialog
handling, fallback scenarios, and category/shipping regressions to
ensure more robust, faster interactions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-10 07:01:50 +02:00
Sebastian Thomschke
45170b457f ci: build Linux ARM binaries (#967) 2026-04-09 21:18:28 +02:00
kleinanzeigen-bot-tu[bot]
5bffe8eb27 chore: ✔ Update platformdirs 4.9.4 -> 4.9.6 (#965)
✔ Update platformdirs 4.9.4 -> 4.9.6 successful

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-09 20:40:02 +02:00
Alex Strutsysnkyi
825480089e fix(publish): add 'Anzeige speichern' to submit button selector (#966)
## Summary

- Kleinanzeigen renamed the edit-page submit button from `Änderungen
speichern` to `Anzeige speichern`
- This caused the bot to timeout when clicking the submit button during
ad updates, resulting in `PublishSubmissionUncertainError` for every
update
- Added the new button label to the XPath selector alongside the
existing labels

## Test plan

- [x] Verified manually: navigated to edit page via Chrome DevTools,
confirmed button text is `Anzeige speichern`
- [x] Confirmed update succeeds with the fix (clicks the button,
proceeds to confirmation)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved reliability of posting and editing by broadening detection of
submit buttons to recognize additional label variants (including
"Anzeige speichern"), reducing failed submissions when interface labels
differ.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-09 20:39:44 +02:00
sebthom
2c06944cca fix: restore ASCII art logo. Fixes #963 2026-04-09 19:04:13 +02:00