mirror of
https://github.com/Second-Hand-Friends/kleinanzeigen-bot.git
synced 2026-03-12 02:31:45 +01:00
feat: allow auto-restart on captcha (#481)
Co-authored-by: sebthom <sebthom@users.noreply.github.com>
This commit is contained in:
@@ -265,6 +265,14 @@ publishing:
|
|||||||
delete_old_ads: "AFTER_PUBLISH" # one of: AFTER_PUBLISH, BEFORE_PUBLISH, NEVER
|
delete_old_ads: "AFTER_PUBLISH" # one of: AFTER_PUBLISH, BEFORE_PUBLISH, NEVER
|
||||||
delete_old_ads_by_title: true # only works if delete_old_ads is set to BEFORE_PUBLISH
|
delete_old_ads_by_title: true # only works if delete_old_ads is set to BEFORE_PUBLISH
|
||||||
|
|
||||||
|
# captcha-Handling (optional)
|
||||||
|
# To ensure that the bot does not require manual confirmation after a captcha, but instead automatically pauses for a defined period and then restarts, you can enable the captcha section:
|
||||||
|
|
||||||
|
captcha:
|
||||||
|
auto_restart: true # If true, the bot aborts when a Captcha appears and retries publishing later
|
||||||
|
# If false (default), the Captcha must be solved manually to continue
|
||||||
|
restart_delay: 1h 30m # Time to wait before retrying after a Captcha was encountered (default: 6h)
|
||||||
|
|
||||||
# browser configuration
|
# browser configuration
|
||||||
browser:
|
browser:
|
||||||
# https://peter.sh/experiments/chromium-command-line-switches/
|
# https://peter.sh/experiments/chromium-command-line-switches/
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ from . import extract, resources
|
|||||||
from ._version import __version__
|
from ._version import __version__
|
||||||
from .ads import calculate_content_hash, get_description_affixes
|
from .ads import calculate_content_hash, get_description_affixes
|
||||||
from .utils import dicts, error_handlers, loggers, misc
|
from .utils import dicts, error_handlers, loggers, misc
|
||||||
|
from .utils.exceptions import CaptchaEncountered
|
||||||
from .utils.files import abspath
|
from .utils.files import abspath
|
||||||
from .utils.i18n import Locale, get_current_locale, pluralize, set_current_locale
|
from .utils.i18n import Locale, get_current_locale, pluralize, set_current_locale
|
||||||
from .utils.misc import ainput, ensure, is_frozen, parse_datetime, parse_decimal
|
from .utils.misc import ainput, ensure, is_frozen, parse_datetime, parse_decimal
|
||||||
@@ -773,7 +774,16 @@ class KleinanzeigenBot(WebScrapingMixin):
|
|||||||
# wait for captcha
|
# wait for captcha
|
||||||
#############################
|
#############################
|
||||||
try:
|
try:
|
||||||
await self.web_find(By.CSS_SELECTOR, "iframe[name^='a-'][src^='https://www.google.com/recaptcha/api2/anchor?']", timeout = 2)
|
await self.web_find(
|
||||||
|
By.CSS_SELECTOR,
|
||||||
|
"iframe[name^='a-'][src^='https://www.google.com/recaptcha/api2/anchor?']",
|
||||||
|
timeout = 2)
|
||||||
|
|
||||||
|
if self.config.get("captcha", {}).get("auto_restart", False):
|
||||||
|
LOG.warning("Captcha recognized - auto-restart enabled, abort run...")
|
||||||
|
raise CaptchaEncountered(misc.parse_duration(self.config.get("captcha", {}).get("restart_delay", "6h")))
|
||||||
|
|
||||||
|
# Fallback: manuell
|
||||||
LOG.warning("############################################")
|
LOG.warning("############################################")
|
||||||
LOG.warning("# Captcha present! Please solve the captcha.")
|
LOG.warning("# Captcha present! Please solve the captcha.")
|
||||||
LOG.warning("############################################")
|
LOG.warning("############################################")
|
||||||
@@ -1128,7 +1138,7 @@ class KleinanzeigenBot(WebScrapingMixin):
|
|||||||
else dicts.safe_get(ad_cfg, "description", "prefix")
|
else dicts.safe_get(ad_cfg, "description", "prefix")
|
||||||
if dicts.safe_get(ad_cfg, "description", "prefix") is not None
|
if dicts.safe_get(ad_cfg, "description", "prefix") is not None
|
||||||
# 3. Global prefix from config
|
# 3. Global prefix from config
|
||||||
else get_description_affixes(self.config, prefix=True)
|
else get_description_affixes(self.config, prefix = True)
|
||||||
or "" # Default to empty string if all sources are None
|
or "" # Default to empty string if all sources are None
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1140,7 +1150,7 @@ class KleinanzeigenBot(WebScrapingMixin):
|
|||||||
else dicts.safe_get(ad_cfg, "description", "suffix")
|
else dicts.safe_get(ad_cfg, "description", "suffix")
|
||||||
if dicts.safe_get(ad_cfg, "description", "suffix") is not None
|
if dicts.safe_get(ad_cfg, "description", "suffix") is not None
|
||||||
# 3. Global suffix from config
|
# 3. Global suffix from config
|
||||||
else get_description_affixes(self.config, prefix=False)
|
else get_description_affixes(self.config, prefix = False)
|
||||||
or "" # Default to empty string if all sources are None
|
or "" # Default to empty string if all sources are None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,23 @@ SPDX-FileCopyrightText: © Sebastian Thomschke and contributors
|
|||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
SPDX-ArtifactOfProjectHomePage: https://github.com/Second-Hand-Friends/kleinanzeigen-bot/
|
SPDX-ArtifactOfProjectHomePage: https://github.com/Second-Hand-Friends/kleinanzeigen-bot/
|
||||||
"""
|
"""
|
||||||
import sys
|
import sys, time
|
||||||
import kleinanzeigen_bot
|
from gettext import gettext as _
|
||||||
|
|
||||||
kleinanzeigen_bot.main(sys.argv)
|
import kleinanzeigen_bot
|
||||||
|
from kleinanzeigen_bot.utils.exceptions import CaptchaEncountered
|
||||||
|
from kleinanzeigen_bot.utils.misc import format_timedelta
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Main loop: run bot → if captcha → sleep → restart
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
kleinanzeigen_bot.main(sys.argv) # runs & returns when finished
|
||||||
|
sys.exit(0) # not using `break` to prevent process closing issues
|
||||||
|
|
||||||
|
except CaptchaEncountered as ex:
|
||||||
|
delay = ex.restart_delay
|
||||||
|
print(_("[INFO] Captcha detected. Sleeping %s before restart...") % format_timedelta(delay))
|
||||||
|
time.sleep(delay.total_seconds())
|
||||||
|
# loop continues and starts a fresh run
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ getopt.py:
|
|||||||
short_has_arg:
|
short_has_arg:
|
||||||
"option -%s not recognized": "Option -%s unbekannt"
|
"option -%s not recognized": "Option -%s unbekannt"
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
kleinanzeigen_bot/__main__.py:
|
||||||
|
#################################################
|
||||||
|
module:
|
||||||
|
"[INFO] Captcha detected. Sleeping %s before restart...": "[INFO] Captcha erkannt. Warte %s h bis zum Neustart..."
|
||||||
|
|
||||||
#################################################
|
#################################################
|
||||||
kleinanzeigen_bot/__init__.py:
|
kleinanzeigen_bot/__init__.py:
|
||||||
@@ -78,6 +83,7 @@ kleinanzeigen_bot/__init__.py:
|
|||||||
" -> SUCCESS: ad published with ID %s": " -> ERFOLG: Anzeige mit ID %s veröffentlicht"
|
" -> SUCCESS: ad published with ID %s": " -> ERFOLG: Anzeige mit ID %s veröffentlicht"
|
||||||
" -> effective ad meta:": " -> effektive Anzeigen-Metadaten:"
|
" -> effective ad meta:": " -> effektive Anzeigen-Metadaten:"
|
||||||
"Could not set city from location": "Stadt konnte nicht aus dem Standort gesetzt werden"
|
"Could not set city from location": "Stadt konnte nicht aus dem Standort gesetzt werden"
|
||||||
|
"Captcha recognized - auto-restart enabled, abort run...": "Captcha erkannt - Auto-Neustart aktiviert, Durchlauf wird beendet..."
|
||||||
|
|
||||||
__set_condition:
|
__set_condition:
|
||||||
"Unable to close condition dialog!": "Kann den Dialog für Artikelzustand nicht schließen!"
|
"Unable to close condition dialog!": "Kann den Dialog für Artikelzustand nicht schließen!"
|
||||||
|
|||||||
18
src/kleinanzeigen_bot/utils/exceptions.py
Normal file
18
src/kleinanzeigen_bot/utils/exceptions.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
"""
|
||||||
|
SPDX-FileCopyrightText: © Sebastian Thomschke and contributors
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
SPDX-ArtifactOfProjectHomePage: https://github.com/Second-Hand-Friends/kleinanzeigen-bot/
|
||||||
|
"""
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
|
||||||
|
class KleinanzeigenBotError(RuntimeError):
|
||||||
|
"""Base class for all custom bot-related exceptions."""
|
||||||
|
|
||||||
|
|
||||||
|
class CaptchaEncountered(KleinanzeigenBotError):
|
||||||
|
"""Raised when a Captcha was detected and auto-restart is enabled."""
|
||||||
|
|
||||||
|
def __init__(self, restart_delay: timedelta):
|
||||||
|
super().__init__()
|
||||||
|
self.restart_delay = restart_delay
|
||||||
@@ -28,6 +28,9 @@ LOG_ROOT:Final[logging.Logger] = logging.getLogger()
|
|||||||
|
|
||||||
|
|
||||||
def configure_console_logging() -> None:
|
def configure_console_logging() -> None:
|
||||||
|
# if a StreamHandler already exists, do not append it again
|
||||||
|
if any(isinstance(h, logging.StreamHandler) for h in LOG_ROOT.handlers):
|
||||||
|
return
|
||||||
|
|
||||||
class CustomFormatter(logging.Formatter):
|
class CustomFormatter(logging.Formatter):
|
||||||
LEVEL_COLORS = {
|
LEVEL_COLORS = {
|
||||||
|
|||||||
Reference in New Issue
Block a user