diff --git a/README.md b/README.md
index a1ec80c..8b7c107 100644
--- a/README.md
+++ b/README.md
@@ -287,30 +287,58 @@ The `--config` and `--logfile` flags override only their specific paths. They do
- **Linux:** System-wide follows XDG Base Directory spec; portable stays in the current working directory.
- **macOS:** System-wide uses `~/Library/Application Support/kleinanzeigen-bot` (and related dirs); portable stays in the current directory.
-### 1) Main configuration
+### 1) Main configuration ⚙️
-When executing the app it by default looks for a `config.yaml` file in the current directory. If it does not exist it will be created automatically.
+The main configuration file (`config.yaml`) is **required** to run the bot. It contains your login credentials and controls all bot behavior.
-The configuration file to be used can also be specified using the `--config ` command line parameter. It must point to a YAML or JSON file.
-Valid file extensions are `.json`, `.yaml` and `.yml`
-
-The configuration file supports many options including login credentials, ad file patterns, browser settings, timeouts, and update check configuration. To generate a default configuration file with all current defaults, run:
+**Quick start:**
```bash
+# Generate a config file with all defaults
kleinanzeigen-bot create-config
+
+# Or specify a custom location
+kleinanzeigen-bot --config /path/to/config.yaml publish
```
-For the complete configuration reference with all available options and detailed explanations, see [Configuration Reference](docs/CONFIGURATION.md).
+**Minimal config.yaml:**
-### 2) Ad configuration
+```yaml
+# yaml-language-server: $schema=https://raw.githubusercontent.com/Second-Hand-Friends/kleinanzeigen-bot/main/schemas/config.schema.json
+login:
+ username: "your_username"
+ password: "your_password"
+```
-Each ad is described in a separate JSON or YAML file with prefix `ad_`. The prefix is configurable in config file.
+📖 **[Complete Configuration Reference →](docs/CONFIGURATION.md)**
-Parameter values specified in the `ad_defaults` section of the `config.yaml` file don't need to be specified again in the ad configuration file.
+Full documentation including timeout tuning, browser settings, ad defaults, diagnostics, and all available options.
-For the complete ad configuration reference including automatic price reduction, shipping options, and description prefix/suffix, see [Ad Configuration Reference](docs/AD_CONFIGURATION.md).
+### 2) Ad configuration 📝
-### 3) Using an existing browser window
+Each ad is defined in a separate YAML/JSON file (default pattern: `ad_*.yaml`). These files specify the title, description, price, category, images, and other ad-specific settings.
+
+**Quick example (`ad_laptop.yaml`):**
+
+```yaml
+# yaml-language-server: $schema=https://raw.githubusercontent.com/Second-Hand-Friends/kleinanzeigen-bot/main/schemas/ad.schema.json
+active: true
+title: "Gaming Laptop - RTX 3060"
+description: |
+ Powerful gaming laptop in excellent condition.
+ Includes original box and charger.
+category: "Elektronik > Notebooks"
+price: 450
+price_type: NEGOTIABLE
+images:
+ - "laptop/*.jpg" # Relative to ad file location (or use absolute paths); glob patterns supported
+```
+
+📖 **[Complete Ad Configuration Reference →](docs/AD_CONFIGURATION.md)**
+
+Full documentation including automatic price reduction, shipping options, category IDs, and special attributes.
+
+### 3) Using an existing browser window (Optional)
By default a new browser process will be launched. To reuse a manually launched browser window/process, you can enable remote debugging. This is useful for debugging or when you want to keep your browser session open.
diff --git a/docs/AD_CONFIGURATION.md b/docs/AD_CONFIGURATION.md
index c344d7d..b2e0532 100644
--- a/docs/AD_CONFIGURATION.md
+++ b/docs/AD_CONFIGURATION.md
@@ -26,7 +26,11 @@ kleinanzeigen-bot download --ads=1,2,3
For full JSON schema with IDE autocompletion support, see:
-- [schemas/ad.schema.json](../schemas/ad.schema.json)
+- [schemas/ad.schema.json](https://raw.githubusercontent.com/Second-Hand-Friends/kleinanzeigen-bot/main/schemas/ad.schema.json)
+
+📖 **[Complete Main Configuration Reference →](CONFIGURATION.md)**
+
+Full documentation for `config.yaml` including all options, timeouts, browser settings, update checks, and ad_defaults.
## Configuration Structure
@@ -35,11 +39,13 @@ For full JSON schema with IDE autocompletion support, see:
Description values can be multiline. See for YAML syntax examples.
```yaml
-# yaml-language-server: $schema=https://raw.githubusercontent.com/Second-Hand-Friends/kleinanzeigen-bot/refs/heads/main/schemas/ad.schema.json
-active: # true or false (default: true)
-type: # one of: OFFER, WANTED (default: OFFER)
-title: # Ad title
-description: # Ad description
+# yaml-language-server: $schema=https://raw.githubusercontent.com/Second-Hand-Friends/kleinanzeigen-bot/main/schemas/ad.schema.json
+active: true
+type: OFFER
+title: "Your Ad Title"
+description: |
+ Your ad description here.
+ Supports multiple lines.
```
### Description Prefix and Suffix
diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md
index a6c53f4..7188c01 100644
--- a/docs/CONFIGURATION.md
+++ b/docs/CONFIGURATION.md
@@ -12,20 +12,51 @@ kleinanzeigen-bot create-config
For full JSON schema with IDE autocompletion support, see:
-- [schemas/config.schema.json](../schemas/config.schema.json)
+- [schemas/config.schema.json](https://raw.githubusercontent.com/Second-Hand-Friends/kleinanzeigen-bot/main/schemas/config.schema.json)
To enable IDE autocompletion in `config.yaml`, add this at the top of the file:
```yaml
-# yaml-language-server: $schema=schemas/config.schema.json
+# yaml-language-server: $schema=https://raw.githubusercontent.com/Second-Hand-Friends/kleinanzeigen-bot/main/schemas/config.schema.json
```
For ad files, use the ad schema instead:
```yaml
-# yaml-language-server: $schema=schemas/ad.schema.json
+# yaml-language-server: $schema=https://raw.githubusercontent.com/Second-Hand-Friends/kleinanzeigen-bot/main/schemas/ad.schema.json
```
+## Minimal Configuration Example
+
+Here's the smallest viable `config.yaml` to get started. Only the **login** section is required—everything else uses sensible defaults:
+
+```yaml
+# yaml-language-server: $schema=https://raw.githubusercontent.com/Second-Hand-Friends/kleinanzeigen-bot/main/schemas/config.schema.json
+
+# REQUIRED: Your kleinanzeigen.de credentials
+login:
+ username: "your_username"
+ password: "your_password"
+
+# OPTIONAL: Where to find your ad files (default pattern shown)
+# ad_files:
+# - "./**/ad_*.{json,yml,yaml}"
+
+# OPTIONAL: Default values for all ads
+# ad_defaults:
+# price_type: NEGOTIABLE
+# shipping_type: SHIPPING
+# republication_interval: 7
+```
+
+Run `kleinanzeigen-bot create-config` to generate a complete configuration with all available options and their default values.
+
+The `ad_files` setting controls where the bot looks for your ad YAML files (default pattern: `./**/ad_*.{json,yml,yaml}`). The `ad_defaults` section lets you set default values that apply to all ads—things like price type, shipping options, and republication interval.
+
+📖 **[Complete Ad Configuration Reference →](AD_CONFIGURATION.md)**
+
+Full documentation for ad YAML files including automatic price reduction, description prefix/suffix, shipping options, category IDs, and special attributes.
+
## File Location
The bot looks for `config.yaml` in the current directory by default. You can specify a different location using the `--config` command line option:
@@ -71,7 +102,7 @@ ad_defaults:
republication_interval: 7 # every X days ads should be re-published
```
-> **Tip:** For current defaults of all timeout and diagnostic settings, run `kleinanzeigen-bot create-config` or see the [JSON schema](../schemas/config.schema.json).
+> **Tip:** For current defaults of all timeout and diagnostic settings, run `kleinanzeigen-bot create-config` or see the [JSON schema](https://raw.githubusercontent.com/Second-Hand-Friends/kleinanzeigen-bot/main/schemas/config.schema.json).
### categories
@@ -309,4 +340,4 @@ kleinanzeigen-bot create-config
This generates a config file with `exclude_none=True`, giving you all the non-None defaults.
-For the complete machine-readable reference, see the [JSON schema](../schemas/config.schema.json).
+For the complete machine-readable reference, see the [JSON schema](https://raw.githubusercontent.com/Second-Hand-Friends/kleinanzeigen-bot/main/schemas/config.schema.json).
diff --git a/src/kleinanzeigen_bot/__init__.py b/src/kleinanzeigen_bot/__init__.py
index 618aeb2..2283a62 100644
--- a/src/kleinanzeigen_bot/__init__.py
+++ b/src/kleinanzeigen_bot/__init__.py
@@ -252,8 +252,7 @@ class KleinanzeigenBot(WebScrapingMixin): # noqa: PLR0904
# Log installation mode and config location (INFO level for user visibility)
mode_display = "portable (current directory)" if self.installation_mode == "portable" else "system-wide (XDG directories)"
- LOG.info("Installation mode: %s", mode_display)
- LOG.info("Config file: %s", self.config_file_path)
+ LOG.info("Installation mode: %s [%s]", mode_display, self.config_file_path)
async def run(self, args:list[str]) -> None:
self.parse_args(args)
@@ -585,7 +584,7 @@ class KleinanzeigenBot(WebScrapingMixin): # noqa: PLR0904
header=(
"# yaml-language-server: $schema="
"https://raw.githubusercontent.com/Second-Hand-Friends/kleinanzeigen-bot"
- "/refs/heads/main/schemas/config.schema.json"
+ "/main/schemas/config.schema.json"
),
exclude = {"ad_defaults": {"description"}},
)
@@ -599,12 +598,20 @@ class KleinanzeigenBot(WebScrapingMixin): # noqa: PLR0904
self.config = Config.model_validate(config_yaml, strict = True, context = self.config_file_path)
# load built-in category mappings
- self.categories = dicts.load_dict_from_module(resources, "categories.yaml", "categories")
- deprecated_categories = dicts.load_dict_from_module(resources, "categories_old.yaml", "categories")
+ self.categories = dicts.load_dict_from_module(resources, "categories.yaml", "")
+ LOG.debug("Loaded %s categories from categories.yaml", len(self.categories))
+ deprecated_categories = dicts.load_dict_from_module(resources, "categories_old.yaml", "")
+ LOG.debug("Loaded %s categories from categories_old.yaml", len(deprecated_categories))
self.categories.update(deprecated_categories)
+ custom_count = 0
if self.config.categories:
+ custom_count = len(self.config.categories)
self.categories.update(self.config.categories)
- LOG.info(" -> found %s", pluralize("category", self.categories))
+ LOG.debug("Loaded %s categories from config.yaml (custom)", custom_count)
+ total_count = len(self.categories)
+ if total_count == 0:
+ LOG.warning("No categories loaded - category files may be missing or empty")
+ LOG.debug("Loaded %s categories in total", total_count)
# populate browser_config object used by WebScrapingMixin
self.browser_config.arguments = self.config.browser.arguments
diff --git a/src/kleinanzeigen_bot/resources/translations.de.yaml b/src/kleinanzeigen_bot/resources/translations.de.yaml
index c284224..a7aa538 100644
--- a/src/kleinanzeigen_bot/resources/translations.de.yaml
+++ b/src/kleinanzeigen_bot/resources/translations.de.yaml
@@ -63,8 +63,11 @@ kleinanzeigen_bot/__init__.py:
load_config:
"config": "Konfiguration"
- " -> found %s": "-> %s gefunden"
- "category": "Kategorie"
+ "Loaded %s categories from categories.yaml": "%s Kategorien aus categories.yaml geladen"
+ "Loaded %s categories from categories_old.yaml": "%s Kategorien aus categories_old.yaml geladen"
+ "Loaded %s categories from config.yaml (custom)": "%s Kategorien aus config.yaml geladen (benutzerdefiniert)"
+ "Loaded %s categories in total": "%s Kategorien insgesamt geladen"
+ "No categories loaded - category files may be missing or empty": "Keine Kategorien geladen - Kategorie-Dateien fehlen oder sind leer"
check_and_wait_for_captcha:
"# Captcha present! Please solve the captcha.": "# Captcha vorhanden! Bitte lösen Sie das Captcha."
@@ -138,9 +141,8 @@ kleinanzeigen_bot/__init__.py:
"Found extend button on page %s": "'Verlängern'-Button auf Seite %s gefunden"
finalize_installation_mode:
- "Config file: %s": "Konfigurationsdatei: %s"
"First run detected, prompting user for installation mode": "Erster Start erkannt, frage Benutzer nach Installationsmodus"
- "Installation mode: %s": "Installationsmodus: %s"
+ "Installation mode: %s [%s]": "Installationsmodus: %s [%s]"
publish_ads:
"Processing %s/%s: '%s' from [%s]...": "Verarbeite %s/%s: '%s' von [%s]..."
@@ -686,7 +688,7 @@ kleinanzeigen_bot/utils/xdg_paths.py:
"Failed to create %s %s: %s": "Fehler beim Erstellen von %s %s: %s"
detect_installation_mode:
"Detected installation mode: %s": "Erkannter Installationsmodus: %s"
- "No existing installation found": "Keine bestehende Installation gefunden"
+ "No existing configuration (portable or system-wide) found": "Keine bestehende Konfiguration (portabel oder systemweit) gefunden"
prompt_installation_mode:
"Non-interactive mode detected, defaulting to portable installation": "Nicht-interaktiver Modus erkannt, Standard-Installation: portabel"
"Choose installation type:": "Installationstyp wählen:"
diff --git a/src/kleinanzeigen_bot/utils/dicts.py b/src/kleinanzeigen_bot/utils/dicts.py
index e6c895d..06e6bbe 100644
--- a/src/kleinanzeigen_bot/utils/dicts.py
+++ b/src/kleinanzeigen_bot/utils/dicts.py
@@ -79,7 +79,7 @@ def load_dict(filepath:str, content_label:str = "") -> dict[str, Any]:
def load_dict_if_exists(filepath:str, content_label:str = "") -> dict[str, Any] | None:
abs_filepath = files.abspath(filepath)
- LOG.info("Loading %s[%s]...", content_label and content_label + " from " or "", abs_filepath)
+ LOG.debug("Loading %s[%s]...", content_label and content_label + " from " or "", abs_filepath)
__, file_ext = os.path.splitext(filepath)
if file_ext not in {".json", ".yaml", ".yml"}:
diff --git a/src/kleinanzeigen_bot/utils/xdg_paths.py b/src/kleinanzeigen_bot/utils/xdg_paths.py
index 2fa6d9f..6f29a7f 100644
--- a/src/kleinanzeigen_bot/utils/xdg_paths.py
+++ b/src/kleinanzeigen_bot/utils/xdg_paths.py
@@ -89,7 +89,7 @@ def detect_installation_mode() -> InstallationMode | None:
LOG.debug("Checking for portable config at: %s", portable_config)
if portable_config.exists():
- LOG.info("Detected installation mode: %s", "portable")
+ LOG.debug("Detected installation mode: %s", "portable")
return "portable"
# Check for XDG installation
@@ -97,11 +97,11 @@ def detect_installation_mode() -> InstallationMode | None:
LOG.debug("Checking for XDG config at: %s", xdg_config)
if xdg_config.exists():
- LOG.info("Detected installation mode: %s", "xdg")
+ LOG.debug("Detected installation mode: %s", "xdg")
return "xdg"
# Neither exists - first run
- LOG.info("No existing installation found")
+ LOG.info("No existing configuration (portable or system-wide) found")
return None