Skip to content

Commit

Permalink
Merge branch 'main' into base-solvers
Browse files Browse the repository at this point in the history
  • Loading branch information
Xewdy444 authored Mar 13, 2024
2 parents ff90952 + 443dc91 commit 0892149
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 62 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
---

<div align="center">
<a href="https://www.capsolver.com/">
<img src="https://cdn.discordapp.com/attachments/1105172394655625306/1105180101802471575/20221207-160749.gif"/>
<a href="https://dashboard.capsolver.com/passport/register?inviteCode=m4C4_LnVN_re">
<img src="https://i.imgur.com/YaRmSt4.gif"/>
</a>
<br> At the lowest price on the market, you may receive a variety of solutions, including reCAPTCHA v2, reCAPTCHA v3, hCaptcha, hCaptcha Click, FunCaptcha, picture-to-text, AWS Amazon CAPTCHA, and more. With this service, 0.1s is the slowest speed ever measured.
</div>
Expand Down
38 changes: 19 additions & 19 deletions playwright_recaptcha/recaptchav2/async_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
)
from .base_solver import BaseSolver
from .recaptcha_box import AsyncRecaptchaBox
from .translations import TRANSLATIONS


class AsyncAudioFile(speech_recognition.AudioFile):
Expand Down Expand Up @@ -97,29 +98,28 @@ async def _get_task_object(recaptcha_box: AsyncRecaptchaBox) -> Optional[str]:
The object ID. Returns None if the task object is not recognized.
"""
object_dict = {
"taxis": "/m/0pg52",
"bus": "/m/01bjv",
"school bus": "/m/02yvhj",
"motorcycles": "/m/04_sv",
"tractors": "/m/013xlm",
"chimneys": "/m/01jk_4",
"crosswalks": "/m/014xcs",
"traffic lights": "/m/015qff",
"bicycles": "/m/0199g",
"parking meters": "/m/015qbp",
"cars": "/m/0k4j",
"bridges": "/m/015kr",
"boats": "/m/019jd",
"palm trees": "/m/0cdl1",
"mountains or hills": "/m/09d_r",
"fire hydrant": "/m/01pns0",
"stairs": "/m/01lynh",
"/m/0pg52": TRANSLATIONS["taxis"],
"/m/01bjv": TRANSLATIONS["bus"],
"/m/04_sv": TRANSLATIONS["motorcycles"],
"/m/013xlm": TRANSLATIONS["tractors"],
"/m/01jk_4": TRANSLATIONS["chimneys"],
"/m/014xcs": TRANSLATIONS["crosswalks"],
"/m/015qff": TRANSLATIONS["traffic_lights"],
"/m/0199g": TRANSLATIONS["bicycles"],
"/m/015qbp": TRANSLATIONS["parking_meters"],
"/m/0k4j": TRANSLATIONS["cars"],
"/m/015kr": TRANSLATIONS["bridges"],
"/m/019jd": TRANSLATIONS["boats"],
"/m/0cdl1": TRANSLATIONS["palm_trees"],
"/m/09d_r": TRANSLATIONS["mountains_or_hills"],
"/m/01pns0": TRANSLATIONS["fire_hydrant"],
"/m/01lynh": TRANSLATIONS["stairs"],
}

task = await recaptcha_box.bframe_frame.locator("div").all_inner_texts()

for object_name, object_id in object_dict.items():
if object_name in task[0]:
for object_id, translations in object_dict.items():
if any(translation in task[0] for translation in translations):
return object_id

return None
Expand Down
70 changes: 49 additions & 21 deletions playwright_recaptcha/recaptchav2/recaptcha_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from playwright.sync_api import Locator as SyncLocator

from ..errors import RecaptchaNotFoundError
from .translations import TRANSLATIONS

FrameT = TypeVar("FrameT", AsyncFrame, SyncFrame)
Locator = Union[AsyncLocator, SyncLocator]
Expand Down Expand Up @@ -91,49 +92,68 @@ def _get_recaptcha_frame_pairs(
@property
def checkbox(self) -> Locator:
"""The reCAPTCHA checkbox locator."""
return self.anchor_frame.get_by_role("checkbox", name="I'm not a robot")
return self.anchor_frame.get_by_role(
"checkbox", name=re.compile("|".join(TRANSLATIONS["im_not_a_robot"]))
)

@property
def audio_challenge_button(self) -> Locator:
"""The reCAPTCHA audio challenge button locator."""
return self.bframe_frame.get_by_role("button", name="Get an audio challenge")
return self.bframe_frame.get_by_role(
"button", name=re.compile("|".join(TRANSLATIONS["get_an_audio_challenge"]))
)

@property
def image_challenge_button(self) -> Locator:
"""The reCAPTCHA image challenge button locator."""
return self.bframe_frame.get_by_role("button", name="Get a visual challenge")
return self.bframe_frame.get_by_role(
"button", name=re.compile("|".join(TRANSLATIONS["get_a_visual_challenge"]))
)

@property
def new_challenge_button(self) -> Locator:
"""The reCAPTCHA new challenge button locator."""
return self.bframe_frame.get_by_role("button", name="Get a new challenge")
return self.bframe_frame.get_by_role(
"button", name=re.compile("|".join(TRANSLATIONS["get_a_new_challenge"]))
)

@property
def audio_download_button(self) -> Locator:
"""The reCAPTCHA audio download button locator."""
return self.bframe_frame.get_by_role(
"link", name="Alternatively, download audio as MP3"
"link",
name=re.compile(
"|".join(TRANSLATIONS["alternatively_download_audio_as_mp3"])
),
)

@property
def audio_challenge_textbox(self) -> Locator:
"""The reCAPTCHA audio challenge textbox locator."""
return self.bframe_frame.get_by_role("textbox", name="Enter what you hear")
return self.bframe_frame.get_by_role(
"textbox", name=re.compile("|".join(TRANSLATIONS["enter_what_you_hear"]))
)

@property
def skip_button(self) -> Locator:
"""The reCAPTCHA skip button locator."""
return self.bframe_frame.get_by_role("button", name="Skip")
return self.bframe_frame.get_by_role(
"button", name=re.compile("|".join(TRANSLATIONS["skip"]))
)

@property
def next_button(self) -> Locator:
"""The reCAPTCHA next button locator."""
return self.bframe_frame.get_by_role("button", name="Next")
return self.bframe_frame.get_by_role(
"button", name=re.compile("|".join(TRANSLATIONS["next"]))
)

@property
def verify_button(self) -> Locator:
"""The reCAPTCHA verify button locator."""
return self.bframe_frame.get_by_role("button", name="Verify")
return self.bframe_frame.get_by_role(
"button", name=re.compile("|".join(TRANSLATIONS["verify"]))
)

@property
def tile_selector(self) -> Locator:
Expand Down Expand Up @@ -389,7 +409,9 @@ def rate_limit_is_visible(self) -> bool:
bool
True if the reCAPTCHA rate limit message is visible, False otherwise.
"""
return self.bframe_frame.get_by_text("Try again later").is_visible()
return self.bframe_frame.get_by_text(
re.compile("|".join(TRANSLATIONS["try_again_later"]))
).is_visible()

@_check_if_attached
def solve_failure_is_visible(self) -> bool:
Expand All @@ -402,7 +424,7 @@ def solve_failure_is_visible(self) -> bool:
True if the reCAPTCHA solve failure message is visible, False otherwise.
"""
return self.bframe_frame.get_by_text(
"Multiple correct solutions required - please solve more."
re.compile("|".join(TRANSLATIONS["multiple_correct_solutions_required"]))
).is_visible()

@_check_if_attached
Expand All @@ -415,7 +437,9 @@ def audio_challenge_is_visible(self) -> bool:
bool
True if the reCAPTCHA audio challenge is visible, False otherwise.
"""
return self.bframe_frame.get_by_text("Press PLAY to listen").is_visible()
return self.bframe_frame.get_by_text(
re.compile("|".join(TRANSLATIONS["press_play_to_listen"]))
).is_visible()

@_check_if_attached
def try_again_is_visible(self) -> bool:
Expand All @@ -428,7 +452,7 @@ def try_again_is_visible(self) -> bool:
True if the reCAPTCHA try again message is visible, False otherwise.
"""
return self.bframe_frame.get_by_text(
re.compile("Please try again")
re.compile("|".join(TRANSLATIONS["please_try_again"]))
).is_visible()

@_check_if_attached
Expand All @@ -442,7 +466,7 @@ def check_new_images_is_visible(self) -> bool:
True if the reCAPTCHA check new images message is visible, False otherwise.
"""
return self.bframe_frame.get_by_text(
re.compile("Please also check the new images")
re.compile("|".join(TRANSLATIONS["please_also_check_the_new_images"]))
).is_visible()

@_check_if_attached
Expand All @@ -457,7 +481,7 @@ def select_all_matching_is_visible(self) -> bool:
False otherwise.
"""
return self.bframe_frame.get_by_text(
re.compile("Please select all matching images")
re.compile("|".join(TRANSLATIONS["please_select_all_matching_images"]))
).is_visible()

@_check_if_attached
Expand Down Expand Up @@ -578,7 +602,9 @@ async def rate_limit_is_visible(self) -> bool:
bool
True if the reCAPTCHA rate limit message is visible, False otherwise.
"""
return await self.bframe_frame.get_by_text("Try again later").is_visible()
return await self.bframe_frame.get_by_text(
re.compile("|".join(TRANSLATIONS["try_again_later"]))
).is_visible()

@_check_if_attached
async def solve_failure_is_visible(self) -> bool:
Expand All @@ -591,7 +617,7 @@ async def solve_failure_is_visible(self) -> bool:
True if the reCAPTCHA solve failure message is visible, False otherwise.
"""
return await self.bframe_frame.get_by_text(
"Multiple correct solutions required - please solve more."
re.compile("|".join(TRANSLATIONS["multiple_correct_solutions_required"]))
).is_visible()

@_check_if_attached
Expand All @@ -604,7 +630,9 @@ async def audio_challenge_is_visible(self) -> bool:
bool
True if the reCAPTCHA audio challenge is visible, False otherwise.
"""
return await self.bframe_frame.get_by_text("Press PLAY to listen").is_visible()
return await self.bframe_frame.get_by_text(
re.compile("|".join(TRANSLATIONS["press_play_to_listen"]))
).is_visible()

@_check_if_attached
async def try_again_is_visible(self) -> bool:
Expand All @@ -617,7 +645,7 @@ async def try_again_is_visible(self) -> bool:
True if the reCAPTCHA try again message is visible, False otherwise.
"""
return await self.bframe_frame.get_by_text(
re.compile("Please try again")
re.compile("|".join(TRANSLATIONS["please_try_again"]))
).is_visible()

@_check_if_attached
Expand All @@ -631,7 +659,7 @@ async def check_new_images_is_visible(self) -> bool:
True if the reCAPTCHA check new images message is visible, False otherwise.
"""
return await self.bframe_frame.get_by_text(
re.compile("Please also check the new images")
re.compile("|".join(TRANSLATIONS["please_also_check_the_new_images"]))
).is_visible()

@_check_if_attached
Expand All @@ -646,7 +674,7 @@ async def select_all_matching_is_visible(self) -> bool:
False otherwise.
"""
return await self.bframe_frame.get_by_text(
re.compile("Please select all matching images")
re.compile("|".join(TRANSLATIONS["please_select_all_matching_images"]))
).is_visible()

@_check_if_attached
Expand Down
38 changes: 19 additions & 19 deletions playwright_recaptcha/recaptchav2/sync_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
)
from .base_solver import BaseSolver
from .recaptcha_box import SyncRecaptchaBox
from .translations import TRANSLATIONS


class SyncSolver(BaseSolver[Page]):
Expand Down Expand Up @@ -59,29 +60,28 @@ def _get_task_object(recaptcha_box: SyncRecaptchaBox) -> Optional[str]:
The object ID. Returns None if the task object is not recognized.
"""
object_dict = {
"taxis": "/m/0pg52",
"bus": "/m/01bjv",
"school bus": "/m/02yvhj",
"motorcycles": "/m/04_sv",
"tractors": "/m/013xlm",
"chimneys": "/m/01jk_4",
"crosswalks": "/m/014xcs",
"traffic lights": "/m/015qff",
"bicycles": "/m/0199g",
"parking meters": "/m/015qbp",
"cars": "/m/0k4j",
"bridges": "/m/015kr",
"boats": "/m/019jd",
"palm trees": "/m/0cdl1",
"mountains or hills": "/m/09d_r",
"fire hydrant": "/m/01pns0",
"stairs": "/m/01lynh",
"/m/0pg52": TRANSLATIONS["taxis"],
"/m/01bjv": TRANSLATIONS["bus"],
"/m/04_sv": TRANSLATIONS["motorcycles"],
"/m/013xlm": TRANSLATIONS["tractors"],
"/m/01jk_4": TRANSLATIONS["chimneys"],
"/m/014xcs": TRANSLATIONS["crosswalks"],
"/m/015qff": TRANSLATIONS["traffic_lights"],
"/m/0199g": TRANSLATIONS["bicycles"],
"/m/015qbp": TRANSLATIONS["parking_meters"],
"/m/0k4j": TRANSLATIONS["cars"],
"/m/015kr": TRANSLATIONS["bridges"],
"/m/019jd": TRANSLATIONS["boats"],
"/m/0cdl1": TRANSLATIONS["palm_trees"],
"/m/09d_r": TRANSLATIONS["mountains_or_hills"],
"/m/01pns0": TRANSLATIONS["fire_hydrant"],
"/m/01lynh": TRANSLATIONS["stairs"],
}

task = recaptcha_box.bframe_frame.locator("div").all_inner_texts()

for object_name, object_id in object_dict.items():
if object_name in task[0]:
for object_id, translations in object_dict.items():
if any(translation in task[0] for translation in translations):
return object_id

return None
Expand Down
48 changes: 48 additions & 0 deletions playwright_recaptcha/recaptchav2/translations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
TRANSLATIONS = {
"im_not_a_robot": ("I'm not a robot", "Я не робот"),
"get_an_audio_challenge": ("Get an audio challenge", "Пройти аудиотест"),
"get_a_visual_challenge": ("Get a visual challenge", "Пройти визуальный тест"),
"get_a_new_challenge": ("Get a new challenge", "Обновить"),
"alternatively_download_audio_as_mp3": (
"Alternatively, download audio as MP3",
"Скачать MP3-файл",
),
"enter_what_you_hear": ("Enter what you hear", "Введите прозвучавшие слова"),
"skip": ("Skip", "Пропустить"),
"next": ("Next", "Далее"),
"verify": ("Verify", "Подтвердить"),
"try_again_later": ("Try again later", "Повторите попытку позже"),
"multiple_correct_solutions_required": (
"Multiple correct solutions required - please solve more",
"Вы должны выполнить несколько заданий",
),
"press_play_to_listen": (
"Press PLAY to listen",
'Чтобы прослушать, нажмите "Воспроизвести"',
),
"please_try_again": ("Please try again", "Повторите попытку"),
"please_also_check_the_new_images": (
"Please also check the new images",
"Просмотрите также новые изображение",
),
"please_select_all_matching_images": (
"Please select all matching images",
"Выберите все совпадающие изображения",
),
"taxis": ("taxis", "такси"),
"bus": ("bus", "автобус"),
"motorcycles": ("motorcycles", "мотоциклы"),
"tractors": ("tractors", "трактора"),
"chimneys": ("chimneys", "дымовые трубы"),
"crosswalks": ("crosswalks", "пешеходные переходы"),
"traffic_lights": ("traffic lights", "светофоры"),
"bicycles": ("bicycles", "велосипеды"),
"parking_meters": ("parking meters", "парковочные автоматы"),
"cars": ("cars", "автомобили"),
"bridges": ("bridges", "мостами"),
"boats": ("boats", "лодки"),
"palm_trees": ("palm trees", "пальмы"),
"mountains_or_hills": ("mountains or hills", "горы или холмы"),
"fire_hydrant": ("fire hydrant", "гидрантами", "пожарные гидранты"),
"stairs": ("stairs", "лестницы"),
}
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
playwright>=1.33.0
pydub==0.25.1
pytest-asyncio==0.23.5
pytest-asyncio==0.23.5.post1
setuptools==69.1.1
SpeechRecognition==3.10.1
tenacity==8.2.3

0 comments on commit 0892149

Please sign in to comment.