From c653554ca8d130e0a2ccd5948c1404379f24e08f Mon Sep 17 00:00:00 2001 From: Xewdy444 Date: Fri, 31 May 2024 12:03:55 -0500 Subject: [PATCH 1/3] Fixed solve detection issue --- playwright_recaptcha/recaptchav2/async_solver.py | 15 +++++++++++++-- playwright_recaptcha/recaptchav2/sync_solver.py | 15 +++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/playwright_recaptcha/recaptchav2/async_solver.py b/playwright_recaptcha/recaptchav2/async_solver.py index f3205b3..1de34e6 100644 --- a/playwright_recaptcha/recaptchav2/async_solver.py +++ b/playwright_recaptcha/recaptchav2/async_solver.py @@ -611,13 +611,24 @@ async def solve_recaptcha( else: recaptcha_box = await AsyncRecaptchaBox.from_frames(self._page.frames) + if await recaptcha_box.rate_limit_is_visible(): + raise RecaptchaRateLimitError + if await recaptcha_box.checkbox.is_visible(): await self._click_checkbox(recaptcha_box) if self._token is not None: return self._token - elif await recaptcha_box.rate_limit_is_visible(): - raise RecaptchaRateLimitError + + if ( + recaptcha_box.frames_are_detached() + or not await recaptcha_box.challenge_is_visible() + or await recaptcha_box.challenge_is_solved() + ): + while self._token is None: + await self._page.wait_for_timeout(250) + + return self._token if image_challenge and await recaptcha_box.image_challenge_button.is_visible(): await recaptcha_box.image_challenge_button.click() diff --git a/playwright_recaptcha/recaptchav2/sync_solver.py b/playwright_recaptcha/recaptchav2/sync_solver.py index 4854364..9ef033c 100644 --- a/playwright_recaptcha/recaptchav2/sync_solver.py +++ b/playwright_recaptcha/recaptchav2/sync_solver.py @@ -548,13 +548,24 @@ def solve_recaptcha( else: recaptcha_box = SyncRecaptchaBox.from_frames(self._page.frames) + if recaptcha_box.rate_limit_is_visible(): + raise RecaptchaRateLimitError + if recaptcha_box.checkbox.is_visible(): self._click_checkbox(recaptcha_box) if self._token is not None: return self._token - elif recaptcha_box.rate_limit_is_visible(): - raise RecaptchaRateLimitError + + if ( + recaptcha_box.frames_are_detached() + or not recaptcha_box.challenge_is_visible() + or recaptcha_box.challenge_is_solved() + ): + while self._token is None: + self._page.wait_for_timeout(250) + + return self._token if image_challenge and recaptcha_box.image_challenge_button.is_visible(): recaptcha_box.image_challenge_button.click() From 3f622c2493661598af8c25c6318e4283f0cd96ff Mon Sep 17 00:00:00 2001 From: Xewdy444 Date: Sat, 1 Jun 2024 11:39:16 -0500 Subject: [PATCH 2/3] Added any_challenge_is_visible() method --- .../recaptchav2/async_solver.py | 14 ++- .../recaptchav2/recaptcha_box.py | 118 ++++++++++++------ .../recaptchav2/sync_solver.py | 14 ++- 3 files changed, 93 insertions(+), 53 deletions(-) diff --git a/playwright_recaptcha/recaptchav2/async_solver.py b/playwright_recaptcha/recaptchav2/async_solver.py index 1de34e6..d571d8e 100644 --- a/playwright_recaptcha/recaptchav2/async_solver.py +++ b/playwright_recaptcha/recaptchav2/async_solver.py @@ -327,7 +327,7 @@ async def _click_checkbox(self, recaptcha_box: AsyncRecaptchaBox) -> None: if await recaptcha_box.rate_limit_is_visible(): raise RecaptchaRateLimitError - if await recaptcha_box.challenge_is_visible(): + if await recaptcha_box.any_challenge_is_visible(): return await self._page.wait_for_timeout(250) @@ -392,7 +392,7 @@ async def _submit_audio_text( raise RecaptchaRateLimitError if ( - not await recaptcha_box.challenge_is_visible() + not await recaptcha_box.audio_challenge_is_visible() or await recaptcha_box.solve_failure_is_visible() or await recaptcha_box.challenge_is_solved() ): @@ -622,7 +622,7 @@ async def solve_recaptcha( if ( recaptcha_box.frames_are_detached() - or not await recaptcha_box.challenge_is_visible() + or not await recaptcha_box.any_challenge_is_visible() or await recaptcha_box.challenge_is_solved() ): while self._token is None: @@ -630,10 +630,12 @@ async def solve_recaptcha( return self._token + while not await recaptcha_box.any_challenge_is_visible(): + await self._page.wait_for_timeout(250) + if image_challenge and await recaptcha_box.image_challenge_button.is_visible(): await recaptcha_box.image_challenge_button.click() - - if ( + elif ( not image_challenge and await recaptcha_box.audio_challenge_button.is_visible() ): @@ -654,7 +656,7 @@ async def solve_recaptcha( if ( recaptcha_box.frames_are_detached() - or not await recaptcha_box.challenge_is_visible() + or not await recaptcha_box.any_challenge_is_visible() or await recaptcha_box.challenge_is_solved() ): while self._token is None: diff --git a/playwright_recaptcha/recaptchav2/recaptcha_box.py b/playwright_recaptcha/recaptchav2/recaptcha_box.py index 863f5ea..58d1b6c 100644 --- a/playwright_recaptcha/recaptchav2/recaptcha_box.py +++ b/playwright_recaptcha/recaptchav2/recaptcha_box.py @@ -249,6 +249,17 @@ def solve_failure_is_visible(self) -> bool: True if the reCAPTCHA solve failure message is visible, False otherwise. """ + @abstractmethod + def image_challenge_is_visible(self) -> bool: + """ + Check if the reCAPTCHA image challenge is visible. + + Returns + ------- + bool + True if the reCAPTCHA challenge is visible, False otherwise. + """ + @abstractmethod def audio_challenge_is_visible(self) -> bool: """ @@ -260,6 +271,17 @@ def audio_challenge_is_visible(self) -> bool: True if the reCAPTCHA audio challenge is visible, False otherwise. """ + @abstractmethod + def any_challenge_is_visible(self) -> bool: + """ + Check if any reCAPTCHA challenge is visible. + + Returns + ------- + bool + True if any reCAPTCHA challenge is visible, False otherwise. + """ + @abstractmethod def try_again_is_visible(self) -> bool: """ @@ -294,17 +316,6 @@ def select_all_matching_is_visible(self) -> bool: False otherwise. """ - @abstractmethod - def challenge_is_visible(self) -> bool: - """ - Check if the reCAPTCHA challenge is visible. - - Returns - ------- - bool - True if the reCAPTCHA challenge is visible, False otherwise. - """ - @abstractmethod def challenge_is_solved(self) -> bool: """ @@ -378,8 +389,7 @@ def from_frames(cls, frames: Iterable[SyncFrame]) -> SyncRecaptchaBox: if ( recaptcha_box.frames_are_attached() - and recaptcha_box.checkbox.is_visible() - and not recaptcha_box.checkbox.is_checked() + and not recaptcha_box.challenge_is_solved() or recaptcha_box.audio_challenge_button.is_visible() and recaptcha_box.audio_challenge_button.is_enabled() or recaptcha_box.image_challenge_button.is_visible() @@ -427,6 +437,19 @@ def solve_failure_is_visible(self) -> bool: re.compile("|".join(TRANSLATIONS["multiple_correct_solutions_required"])) ).is_visible() + @_check_if_attached + def image_challenge_is_visible(self) -> bool: + """ + Check if the reCAPTCHA image challenge is visible. + + Returns + ------- + bool + True if the reCAPTCHA challenge is visible, False otherwise. + """ + button = self.skip_button.or_(self.next_button).or_(self.verify_button) + return button.is_enabled() + @_check_if_attached def audio_challenge_is_visible(self) -> bool: """ @@ -441,6 +464,18 @@ def audio_challenge_is_visible(self) -> bool: re.compile("|".join(TRANSLATIONS["press_play_to_listen"])) ).is_visible() + @_check_if_attached + def any_challenge_is_visible(self) -> bool: + """ + Check if any reCAPTCHA challenge is visible. + + Returns + ------- + bool + True if any reCAPTCHA challenge is visible, False otherwise. + """ + return self.image_challenge_is_visible() or self.audio_challenge_is_visible() + @_check_if_attached def try_again_is_visible(self) -> bool: """ @@ -484,19 +519,6 @@ def select_all_matching_is_visible(self) -> bool: re.compile("|".join(TRANSLATIONS["please_select_all_matching_images"])) ).is_visible() - @_check_if_attached - def challenge_is_visible(self) -> bool: - """ - Check if the reCAPTCHA challenge is visible. - - Returns - ------- - bool - True if the reCAPTCHA challenge is visible, False otherwise. - """ - button = self.skip_button.or_(self.next_button).or_(self.verify_button) - return button.is_enabled() - @_check_if_attached def challenge_is_solved(self) -> bool: """ @@ -571,8 +593,7 @@ async def from_frames(cls, frames: Iterable[AsyncFrame]) -> AsyncRecaptchaBox: if ( recaptcha_box.frames_are_attached() - and await recaptcha_box.checkbox.is_visible() - and not await recaptcha_box.checkbox.is_checked() + and not await recaptcha_box.challenge_is_solved() or await recaptcha_box.audio_challenge_button.is_visible() and await recaptcha_box.audio_challenge_button.is_enabled() or await recaptcha_box.image_challenge_button.is_visible() @@ -620,6 +641,19 @@ async def solve_failure_is_visible(self) -> bool: re.compile("|".join(TRANSLATIONS["multiple_correct_solutions_required"])) ).is_visible() + @_check_if_attached + async def image_challenge_is_visible(self) -> bool: + """ + Check if the reCAPTCHA image challenge is visible. + + Returns + ------- + bool + True if the reCAPTCHA challenge is visible, False otherwise. + """ + button = self.skip_button.or_(self.next_button).or_(self.verify_button) + return await button.is_enabled() + @_check_if_attached async def audio_challenge_is_visible(self) -> bool: """ @@ -634,6 +668,21 @@ async def audio_challenge_is_visible(self) -> bool: re.compile("|".join(TRANSLATIONS["press_play_to_listen"])) ).is_visible() + @_check_if_attached + async def any_challenge_is_visible(self) -> bool: + """ + Check if any reCAPTCHA challenge is visible. + + Returns + ------- + bool + True if any reCAPTCHA challenge is visible, False otherwise. + """ + return ( + await self.image_challenge_is_visible() + or await self.audio_challenge_is_visible() + ) + @_check_if_attached async def try_again_is_visible(self) -> bool: """ @@ -677,19 +726,6 @@ async def select_all_matching_is_visible(self) -> bool: re.compile("|".join(TRANSLATIONS["please_select_all_matching_images"])) ).is_visible() - @_check_if_attached - async def challenge_is_visible(self) -> bool: - """ - Check if the reCAPTCHA challenge is visible. - - Returns - ------- - bool - True if the reCAPTCHA challenge is visible, False otherwise. - """ - button = self.skip_button.or_(self.next_button).or_(self.verify_button) - return await button.is_enabled() - @_check_if_attached async def challenge_is_solved(self) -> bool: """ diff --git a/playwright_recaptcha/recaptchav2/sync_solver.py b/playwright_recaptcha/recaptchav2/sync_solver.py index 9ef033c..669d5ae 100644 --- a/playwright_recaptcha/recaptchav2/sync_solver.py +++ b/playwright_recaptcha/recaptchav2/sync_solver.py @@ -276,7 +276,7 @@ def _click_checkbox(self, recaptcha_box: SyncRecaptchaBox) -> None: if recaptcha_box.rate_limit_is_visible(): raise RecaptchaRateLimitError - if recaptcha_box.challenge_is_visible(): + if recaptcha_box.any_challenge_is_visible(): return self._page.wait_for_timeout(250) @@ -337,7 +337,7 @@ def _submit_audio_text(self, recaptcha_box: SyncRecaptchaBox, text: str) -> None raise RecaptchaRateLimitError if ( - not recaptcha_box.challenge_is_visible() + not recaptcha_box.audio_challenge_is_visible() or recaptcha_box.solve_failure_is_visible() or recaptcha_box.challenge_is_solved() ): @@ -559,7 +559,7 @@ def solve_recaptcha( if ( recaptcha_box.frames_are_detached() - or not recaptcha_box.challenge_is_visible() + or not recaptcha_box.any_challenge_is_visible() or recaptcha_box.challenge_is_solved() ): while self._token is None: @@ -567,10 +567,12 @@ def solve_recaptcha( return self._token + while not recaptcha_box.any_challenge_is_visible(): + self._page.wait_for_timeout(250) + if image_challenge and recaptcha_box.image_challenge_button.is_visible(): recaptcha_box.image_challenge_button.click() - - if not image_challenge and recaptcha_box.audio_challenge_button.is_visible(): + elif not image_challenge and recaptcha_box.audio_challenge_button.is_visible(): recaptcha_box.audio_challenge_button.click() if image_challenge: @@ -588,7 +590,7 @@ def solve_recaptcha( if ( recaptcha_box.frames_are_detached() - or not recaptcha_box.challenge_is_visible() + or not recaptcha_box.any_challenge_is_visible() or recaptcha_box.challenge_is_solved() ): while self._token is None: From a8e3da4eea03dab87d4bedb3e02d670b454dd1e4 Mon Sep 17 00:00:00 2001 From: Xewdy444 Date: Mon, 3 Jun 2024 12:16:43 -0500 Subject: [PATCH 3/3] Small fixes --- playwright_recaptcha/recaptchav2/async_solver.py | 2 +- playwright_recaptcha/recaptchav2/recaptcha_box.py | 6 ++++-- playwright_recaptcha/recaptchav2/sync_solver.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/playwright_recaptcha/recaptchav2/async_solver.py b/playwright_recaptcha/recaptchav2/async_solver.py index 6d32ed9..97b36bc 100644 --- a/playwright_recaptcha/recaptchav2/async_solver.py +++ b/playwright_recaptcha/recaptchav2/async_solver.py @@ -334,7 +334,7 @@ async def _click_checkbox(self, recaptcha_box: AsyncRecaptchaBox) -> None: RecaptchaRateLimitError If the reCAPTCHA rate limit has been exceeded. """ - await recaptcha_box.checkbox.click(force=True) + await recaptcha_box.checkbox.click() while recaptcha_box.frames_are_attached() and self._token is None: if await recaptcha_box.rate_limit_is_visible(): diff --git a/playwright_recaptcha/recaptchav2/recaptcha_box.py b/playwright_recaptcha/recaptchav2/recaptcha_box.py index 1ef775d..3d7f61d 100644 --- a/playwright_recaptcha/recaptchav2/recaptcha_box.py +++ b/playwright_recaptcha/recaptchav2/recaptcha_box.py @@ -421,7 +421,8 @@ def from_frames(cls, frames: Iterable[SyncFrame]) -> SyncRecaptchaBox: if ( recaptcha_box.frames_are_attached() - and not recaptcha_box.challenge_is_solved() + and recaptcha_box.checkbox.is_visible() + and not recaptcha_box.checkbox.is_checked() or recaptcha_box.audio_challenge_button.is_visible() and recaptcha_box.audio_challenge_button.is_enabled() or recaptcha_box.image_challenge_button.is_visible() @@ -637,7 +638,8 @@ async def from_frames(cls, frames: Iterable[AsyncFrame]) -> AsyncRecaptchaBox: if ( recaptcha_box.frames_are_attached() - and not await recaptcha_box.challenge_is_solved() + and await recaptcha_box.checkbox.is_visible() + and not await recaptcha_box.checkbox.is_checked() or await recaptcha_box.audio_challenge_button.is_visible() and await recaptcha_box.audio_challenge_button.is_enabled() or await recaptcha_box.image_challenge_button.is_visible() diff --git a/playwright_recaptcha/recaptchav2/sync_solver.py b/playwright_recaptcha/recaptchav2/sync_solver.py index 69812ba..ac6eaa7 100644 --- a/playwright_recaptcha/recaptchav2/sync_solver.py +++ b/playwright_recaptcha/recaptchav2/sync_solver.py @@ -281,7 +281,7 @@ def _click_checkbox(self, recaptcha_box: SyncRecaptchaBox) -> None: RecaptchaRateLimitError If the reCAPTCHA rate limit has been exceeded. """ - recaptcha_box.checkbox.click(force=True) + recaptcha_box.checkbox.click() while recaptcha_box.frames_are_attached() and self._token is None: if recaptcha_box.rate_limit_is_visible():