mirror of
https://github.com/Second-Hand-Friends/kleinanzeigen-bot.git
synced 2026-03-12 10:31:50 +01:00
fix: Handle email verification dialog (#782)
This commit is contained in:
@@ -317,6 +317,7 @@ timeouts:
|
|||||||
page_load: 15.0 # Timeout for web_open page loads
|
page_load: 15.0 # Timeout for web_open page loads
|
||||||
captcha_detection: 2.0 # Timeout for captcha iframe detection
|
captcha_detection: 2.0 # Timeout for captcha iframe detection
|
||||||
sms_verification: 4.0 # Timeout for SMS verification banners
|
sms_verification: 4.0 # Timeout for SMS verification banners
|
||||||
|
email_verification: 4.0 # Timeout for email verification banners
|
||||||
gdpr_prompt: 10.0 # Timeout when handling GDPR dialogs
|
gdpr_prompt: 10.0 # Timeout when handling GDPR dialogs
|
||||||
login_detection: 10.0 # Timeout for DOM-based login detection fallback (auth probe is tried first)
|
login_detection: 10.0 # Timeout for DOM-based login detection fallback (auth probe is tried first)
|
||||||
publishing_result: 300.0 # Timeout for publishing status checks
|
publishing_result: 300.0 # Timeout for publishing status checks
|
||||||
|
|||||||
@@ -518,6 +518,13 @@
|
|||||||
"title": "Sms Verification",
|
"title": "Sms Verification",
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
"email_verification": {
|
||||||
|
"default": 4.0,
|
||||||
|
"description": "Timeout for eMail verification prompts.",
|
||||||
|
"minimum": 0.1,
|
||||||
|
"title": "Email Verification",
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
"gdpr_prompt": {
|
"gdpr_prompt": {
|
||||||
"default": 10.0,
|
"default": 10.0,
|
||||||
"description": "Timeout for GDPR/consent dialogs.",
|
"description": "Timeout for GDPR/consent dialogs.",
|
||||||
|
|||||||
@@ -863,11 +863,22 @@ class KleinanzeigenBot(WebScrapingMixin): # noqa: PLR0904
|
|||||||
LOG.warning("############################################")
|
LOG.warning("############################################")
|
||||||
LOG.warning("# Device verification message detected. Please follow the instruction displayed in the Browser.")
|
LOG.warning("# Device verification message detected. Please follow the instruction displayed in the Browser.")
|
||||||
LOG.warning("############################################")
|
LOG.warning("############################################")
|
||||||
await ainput("Press ENTER when done...")
|
await ainput(_("Press ENTER when done..."))
|
||||||
except TimeoutError:
|
except TimeoutError:
|
||||||
# No SMS verification prompt detected.
|
# No SMS verification prompt detected.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
email_timeout = self._timeout("email_verification")
|
||||||
|
await self.web_find(By.TEXT, "Um dein Konto zu schützen haben wir dir eine E-Mail geschickt", timeout = email_timeout)
|
||||||
|
LOG.warning("############################################")
|
||||||
|
LOG.warning("# Device verification message detected. Please follow the instruction displayed in the Browser.")
|
||||||
|
LOG.warning("############################################")
|
||||||
|
await ainput(_("Press ENTER when done..."))
|
||||||
|
except TimeoutError:
|
||||||
|
# No email verification prompt detected.
|
||||||
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
LOG.info("Handling GDPR disclaimer...")
|
LOG.info("Handling GDPR disclaimer...")
|
||||||
gdpr_timeout = self._timeout("gdpr_prompt")
|
gdpr_timeout = self._timeout("gdpr_prompt")
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ class TimeoutConfig(ContextualModel):
|
|||||||
page_load:float = Field(default = 15.0, ge = 1.0, description = "Page load timeout for web_open.")
|
page_load:float = Field(default = 15.0, ge = 1.0, description = "Page load timeout for web_open.")
|
||||||
captcha_detection:float = Field(default = 2.0, ge = 0.1, description = "Timeout for captcha iframe detection.")
|
captcha_detection:float = Field(default = 2.0, ge = 0.1, description = "Timeout for captcha iframe detection.")
|
||||||
sms_verification:float = Field(default = 4.0, ge = 0.1, description = "Timeout for SMS verification prompts.")
|
sms_verification:float = Field(default = 4.0, ge = 0.1, description = "Timeout for SMS verification prompts.")
|
||||||
|
email_verification:float = Field(default = 4.0, ge = 0.1, description = "Timeout for email verification prompts.")
|
||||||
gdpr_prompt:float = Field(default = 10.0, ge = 1.0, description = "Timeout for GDPR/consent dialogs.")
|
gdpr_prompt:float = Field(default = 10.0, ge = 1.0, description = "Timeout for GDPR/consent dialogs.")
|
||||||
login_detection:float = Field(default = 10.0, ge = 1.0, description = "Timeout for detecting existing login session via DOM elements.")
|
login_detection:float = Field(default = 10.0, ge = 1.0, description = "Timeout for detecting existing login session via DOM elements.")
|
||||||
publishing_result:float = Field(default = 300.0, ge = 10.0, description = "Timeout for publishing result checks.")
|
publishing_result:float = Field(default = 300.0, ge = 10.0, description = "Timeout for publishing result checks.")
|
||||||
@@ -218,7 +219,7 @@ class DiagnosticsConfig(ContextualModel):
|
|||||||
|
|
||||||
def _validate_glob_pattern(v:str) -> str:
|
def _validate_glob_pattern(v:str) -> str:
|
||||||
if not v.strip():
|
if not v.strip():
|
||||||
raise ValueError("must be a non-empty, non-blank glob pattern")
|
raise ValueError(_("must be a non-empty, non-blank glob pattern"))
|
||||||
return v
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -603,6 +603,8 @@ kleinanzeigen_bot/model/config_model.py:
|
|||||||
"amount must be specified when auto_price_reduction is enabled": "amount muss angegeben werden, wenn auto_price_reduction aktiviert ist"
|
"amount must be specified when auto_price_reduction is enabled": "amount muss angegeben werden, wenn auto_price_reduction aktiviert ist"
|
||||||
"min_price must be specified when auto_price_reduction is enabled": "min_price muss angegeben werden, wenn auto_price_reduction aktiviert ist"
|
"min_price must be specified when auto_price_reduction is enabled": "min_price muss angegeben werden, wenn auto_price_reduction aktiviert ist"
|
||||||
"Percentage reduction amount must not exceed %s": "Prozentuale Reduktionsmenge darf %s nicht überschreiten"
|
"Percentage reduction amount must not exceed %s": "Prozentuale Reduktionsmenge darf %s nicht überschreiten"
|
||||||
|
_validate_glob_pattern:
|
||||||
|
"must be a non-empty, non-blank glob pattern": "muss ein nicht-leeres Glob-Muster sein"
|
||||||
_validate_pause_requires_capture:
|
_validate_pause_requires_capture:
|
||||||
"pause_on_login_detection_failure requires login_detection_capture to be enabled": "pause_on_login_detection_failure erfordert, dass login_detection_capture aktiviert ist"
|
"pause_on_login_detection_failure requires login_detection_capture to be enabled": "pause_on_login_detection_failure erfordert, dass login_detection_capture aktiviert ist"
|
||||||
|
|
||||||
|
|||||||
@@ -509,17 +509,21 @@ class TestKleinanzeigenBotAuthentication:
|
|||||||
# First login attempt:
|
# First login attempt:
|
||||||
# 1. Captcha iframe found (in check_and_wait_for_captcha)
|
# 1. Captcha iframe found (in check_and_wait_for_captcha)
|
||||||
# 2. Phone verification not found (in handle_after_login_logic)
|
# 2. Phone verification not found (in handle_after_login_logic)
|
||||||
# 3. GDPR banner not found (in handle_after_login_logic)
|
# 3. Email verification not found (in handle_after_login_logic)
|
||||||
|
# 4. GDPR banner not found (in handle_after_login_logic)
|
||||||
# Second login attempt:
|
# Second login attempt:
|
||||||
# 4. Captcha iframe found (in check_and_wait_for_captcha)
|
# 5. Captcha iframe found (in check_and_wait_for_captcha)
|
||||||
# 5. Phone verification not found (in handle_after_login_logic)
|
# 6. Phone verification not found (in handle_after_login_logic)
|
||||||
# 6. GDPR banner not found (in handle_after_login_logic)
|
# 7. Email verification not found (in handle_after_login_logic)
|
||||||
|
# 8. GDPR banner not found (in handle_after_login_logic)
|
||||||
mock_find.side_effect = [
|
mock_find.side_effect = [
|
||||||
AsyncMock(), # Captcha iframe (first login)
|
AsyncMock(), # Captcha iframe (first login)
|
||||||
TimeoutError(), # Phone verification (first login)
|
TimeoutError(), # Phone verification (first login)
|
||||||
|
TimeoutError(), # Email verification (first login)
|
||||||
TimeoutError(), # GDPR banner (first login)
|
TimeoutError(), # GDPR banner (first login)
|
||||||
AsyncMock(), # Captcha iframe (second login)
|
AsyncMock(), # Captcha iframe (second login)
|
||||||
TimeoutError(), # Phone verification (second login)
|
TimeoutError(), # Phone verification (second login)
|
||||||
|
TimeoutError(), # Email verification (second login)
|
||||||
TimeoutError(), # GDPR banner (second login)
|
TimeoutError(), # GDPR banner (second login)
|
||||||
]
|
]
|
||||||
mock_ainput.return_value = ""
|
mock_ainput.return_value = ""
|
||||||
@@ -529,7 +533,7 @@ class TestKleinanzeigenBotAuthentication:
|
|||||||
await test_bot.login()
|
await test_bot.login()
|
||||||
|
|
||||||
# Verify the complete flow
|
# Verify the complete flow
|
||||||
assert mock_find.call_count == 6 # Exactly 6 web_find calls
|
assert mock_find.call_count == 8 # Exactly 8 web_find calls
|
||||||
assert mock_ainput.call_count == 2 # Two captcha prompts
|
assert mock_ainput.call_count == 2 # Two captcha prompts
|
||||||
assert mock_input.call_count == 6 # Two login attempts with username, clear password, and set password
|
assert mock_input.call_count == 6 # Two login attempts with username, clear password, and set password
|
||||||
assert mock_click.call_count == 2 # Two submit button clicks
|
assert mock_click.call_count == 2 # Two submit button clicks
|
||||||
@@ -583,13 +587,13 @@ class TestKleinanzeigenBotAuthentication:
|
|||||||
patch("kleinanzeigen_bot.ainput", new_callable = AsyncMock) as mock_ainput,
|
patch("kleinanzeigen_bot.ainput", new_callable = AsyncMock) as mock_ainput,
|
||||||
):
|
):
|
||||||
# Test case 1: No special handling needed
|
# Test case 1: No special handling needed
|
||||||
mock_find.side_effect = [TimeoutError(), TimeoutError()] # No phone verification, no GDPR
|
mock_find.side_effect = [TimeoutError(), TimeoutError(), TimeoutError()] # No phone verification, no email verification, no GDPR
|
||||||
mock_click.return_value = AsyncMock()
|
mock_click.return_value = AsyncMock()
|
||||||
mock_ainput.return_value = ""
|
mock_ainput.return_value = ""
|
||||||
|
|
||||||
await test_bot.handle_after_login_logic()
|
await test_bot.handle_after_login_logic()
|
||||||
|
|
||||||
assert mock_find.call_count == 2
|
assert mock_find.call_count == 3
|
||||||
assert mock_click.call_count == 0
|
assert mock_click.call_count == 0
|
||||||
assert mock_ainput.call_count == 0
|
assert mock_ainput.call_count == 0
|
||||||
|
|
||||||
@@ -597,11 +601,11 @@ class TestKleinanzeigenBotAuthentication:
|
|||||||
mock_find.reset_mock()
|
mock_find.reset_mock()
|
||||||
mock_click.reset_mock()
|
mock_click.reset_mock()
|
||||||
mock_ainput.reset_mock()
|
mock_ainput.reset_mock()
|
||||||
mock_find.side_effect = [AsyncMock(), TimeoutError()] # Phone verification found, no GDPR
|
mock_find.side_effect = [AsyncMock(), TimeoutError(), TimeoutError()] # Phone verification found, no email verification, no GDPR
|
||||||
|
|
||||||
await test_bot.handle_after_login_logic()
|
await test_bot.handle_after_login_logic()
|
||||||
|
|
||||||
assert mock_find.call_count == 2
|
assert mock_find.call_count == 3
|
||||||
assert mock_click.call_count == 0 # No click needed, just wait for user
|
assert mock_click.call_count == 0 # No click needed, just wait for user
|
||||||
assert mock_ainput.call_count == 1 # Wait for user to complete verification
|
assert mock_ainput.call_count == 1 # Wait for user to complete verification
|
||||||
|
|
||||||
@@ -609,11 +613,11 @@ class TestKleinanzeigenBotAuthentication:
|
|||||||
mock_find.reset_mock()
|
mock_find.reset_mock()
|
||||||
mock_click.reset_mock()
|
mock_click.reset_mock()
|
||||||
mock_ainput.reset_mock()
|
mock_ainput.reset_mock()
|
||||||
mock_find.side_effect = [TimeoutError(), AsyncMock()] # No phone verification, GDPR found
|
mock_find.side_effect = [TimeoutError(), TimeoutError(), AsyncMock()] # No phone verification, no email verification, GDPR found
|
||||||
|
|
||||||
await test_bot.handle_after_login_logic()
|
await test_bot.handle_after_login_logic()
|
||||||
|
|
||||||
assert mock_find.call_count == 2
|
assert mock_find.call_count == 3
|
||||||
assert mock_click.call_count == 2 # Click to accept GDPR and continue
|
assert mock_click.call_count == 2 # Click to accept GDPR and continue
|
||||||
assert mock_ainput.call_count == 0
|
assert mock_ainput.call_count == 0
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user