Skip to content

Commit

Permalink
Api V2 implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreiDrang committed Sep 25, 2023
1 parent 8133316 commit 32bfea1
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 140 deletions.
2 changes: 1 addition & 1 deletion src/python_rucaptcha/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "5.3.1"
__version__ = "6.0"
53 changes: 20 additions & 33 deletions src/python_rucaptcha/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import time
import uuid
import asyncio
from typing import Union
from pathlib import Path

import aiohttp
Expand All @@ -10,7 +11,7 @@

from . import enums
from .config import RETRIES, ASYNC_RETRIES
from .serializer import ResponseSer, GetRequestSer, PostRequestSer, CaptchaOptionsSer, ServicePostResponseSer
from .serializer import CaptchaOptionsSer, CreateTaskBaseSer, CreateTaskResponseSer, GetTaskResultRequestSer
from .result_handler import get_sync_result, get_async_result


Expand Down Expand Up @@ -38,57 +39,43 @@ def __init__(
# assign args to validator
self.params = CaptchaOptionsSer(**locals(), **kwargs)

# prepare POST payload
self.post_payload = PostRequestSer(key=self.params.rucaptcha_key, method=method).dict(by_alias=True)
# prepare GET payload
self.get_payload = GetRequestSer(key=self.params.rucaptcha_key, action=action).dict(
by_alias=True, exclude_none=True
# prepare create task payload
self.create_task_payload = CreateTaskBaseSer(clientKey=self.params.rucaptcha_key, **locals()).dict(
by_alias=True
)
# prepare result payload
self.result = ResponseSer()
# prepare get task result data payload
self.get_task_payload = GetTaskResultRequestSer(clientKey=self.params.rucaptcha_key)

for key in kwargs:
self.post_payload.update({key: kwargs[key]})
self.create_task_payload.update({key: kwargs[key]})

# prepare session
self.session = requests.Session()
self.session.mount("http://", HTTPAdapter(max_retries=RETRIES))
self.session.mount("https://", HTTPAdapter(max_retries=RETRIES))

def _processing_response(self, **kwargs: dict) -> dict:
def _processing_response(self, **kwargs: dict) -> Union[dict, Exception]:
"""
Method processing captcha solving task creation result
:param kwargs: additional params for Requests library
"""
try:
response = ServicePostResponseSer(
**self.session.post(self.params.url_request, data=self.post_payload, **kwargs).json()
response = CreateTaskResponseSer(
**self.session.post(self.params.url_request, json=self.create_task_payload, **kwargs).json()
)
# check response status
if response.status == 1:
self.result.taskId = response.request
if response.errorId == 0:
self.get_task_payload.taskId = response.taskId
else:
self.result.error = True
self.result.errorBody = response.request
return response.dict()
except Exception as error:
self.result.error = True
self.result.errorBody = str(error)

# check for errors while make request to server
if self.result.error:
return self.result.dict()

# if all is ok - send captcha to service and wait solution
# update payload - add captcha taskId
self.get_payload.update({"id": self.result.taskId})
return error

# wait captcha solving
time.sleep(self.params.sleep_time)

return get_sync_result(
get_payload=self.get_payload,
sleep_time=self.params.sleep_time,
url_response=self.params.url_response,
result=self.result,
get_payload=self.get_task_payload, sleep_time=self.params.sleep_time, url_response=self.params.url_response
)

def url_open(self, url: str, **kwargs):
Expand Down Expand Up @@ -130,7 +117,7 @@ async def _aio_processing_response(self) -> dict:

# if all is ok - send captcha to service and wait solution
# update payload - add captcha taskId
self.get_payload.update({"id": self.result.taskId})
self.get_payload.taskId = self.result.taskId

# wait captcha solving
await asyncio.sleep(self.params.sleep_time)
Expand All @@ -141,15 +128,15 @@ async def _aio_processing_response(self) -> dict:
result=self.result,
)

async def __aio_make_post_request(self) -> ServicePostResponseSer:
async def __aio_make_post_request(self):
async with aiohttp.ClientSession() as session:
async for attempt in ASYNC_RETRIES:
with attempt:
async with session.post(
self.params.url_request, data=self.post_payload, raise_for_status=True
) as resp:
response_json = await resp.json(content_type=None)
return ServicePostResponseSer(**response_json)
return GetTaskResultSer(**response_json)

# Working with images methods

Expand Down
3 changes: 3 additions & 0 deletions src/python_rucaptcha/core/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ def list_names(cls) -> List[str]:
class ServiceEnm(str, MyEnum):
TWOCAPTCHA = "2captcha"
RUCAPTCHA = "rucaptcha"
TWOCAPTCHA_V2 = "2captcha"
RUCAPTCHA_V2 = "rucaptcha"
DEATHBYCAPTCHA = "deathbycaptcha"


Expand Down Expand Up @@ -106,6 +108,7 @@ class AmazonWAFCaptchaEnm(str, MyEnum):

class TextCaptchaEnm(str, MyEnum):
TEXT = "text"
TextCaptchaTask = "TextCaptchaTask"


class AudioCaptchaEnm(str, MyEnum):
Expand Down
50 changes: 8 additions & 42 deletions src/python_rucaptcha/core/result_handler.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,15 @@
import time
import asyncio
from typing import Union

import aiohttp
import requests

from .config import attempts_generator
from .serializer import ResponseSer, ServiceGetResponseSer
from .serializer import GetTaskResultRequestSer, GetTaskResultResponseSer


def result_processing(captcha_response: ServiceGetResponseSer, result: ResponseSer) -> dict:
"""
Function processing service response status values
"""

# on error during solving
if captcha_response.status == 0:
result.error = True
result.errorBody = captcha_response.request

# if solving is success
elif captcha_response.status == 1:
result.error = False
result.errorBody = None
result.captchaSolve = captcha_response.request

# if this is ReCaptcha v3 then we get it from the server
if captcha_response.user_check and captcha_response.user_score:
result = result.dict()
result.update(
{
"user_check": captcha_response.user_check,
"user_score": captcha_response.user_score,
}
)
return result

return result.dict()


def get_sync_result(get_payload: dict, sleep_time: int, url_response: str, result: ResponseSer) -> dict:
def get_sync_result(get_payload: GetTaskResultRequestSer, sleep_time: int, url_response: str) -> Union[dict, Exception]:
"""
Function periodically send the SYNC request to service and wait for captcha solving result
"""
Expand All @@ -47,23 +18,18 @@ def get_sync_result(get_payload: dict, sleep_time: int, url_response: str, resul
for _ in attempts:
try:
# send a request for the result of solving the captcha
captcha_response = ServiceGetResponseSer(**requests.get(url_response, params=get_payload).json())
captcha_response = GetTaskResultResponseSer(**requests.post(url_response, json=get_payload.dict()).json())
# if the captcha has not been resolved yet, wait
if captcha_response.request == "CAPCHA_NOT_READY":
if captcha_response.status == "processing":
time.sleep(sleep_time)
result.error = True
result.errorBody = "ERROR_CAPTCHA_UNSOLVABLE"
else:
return result_processing(captcha_response, result)
return captcha_response.dict()

except Exception as error:
result.error = True
result.errorBody = str(error)

return result.dict()
return error


async def get_async_result(get_payload: dict, sleep_time: int, url_response: str, result: ResponseSer) -> dict:
async def get_async_result(get_payload: GetTaskResultRequestSer, sleep_time: int, url_response: str) -> dict:
"""
Function periodically send the ASYNC request to service and wait for captcha solving result
"""
Expand Down
63 changes: 41 additions & 22 deletions src/python_rucaptcha/core/serializer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging
from uuid import uuid4
from typing import Union, Optional
from typing import Optional

from pydantic import Field, BaseModel, conint, constr, validator, root_validator

Expand Down Expand Up @@ -84,14 +84,16 @@ class PostRequestSer(MyBaseModel):
field_json: int = Field(1, alias="json")


class GetRequestSer(BaseModel):
key: str
action: str = "get"
field_json: int = Field(1, alias="json")
class TaskSer(MyBaseModel):
type: str

# Control keys
ids: str = None
id: str = None

class CreateTaskBaseSer(MyBaseModel):
clientKey: str
task: TaskSer = None
languagePool: str = "en"
callbackUrl: str = None
softId: str = Field(APP_KEY, const=True)


class CaptchaOptionsSer(BaseModel):
Expand Down Expand Up @@ -135,6 +137,13 @@ def urls_set(cls, values):
"url_response": f"http://api.{service_type}.com/2captcha/res.php",
}
)
elif service_type in (enums.ServiceEnm.TWOCAPTCHA_V2, enums.ServiceEnm.RUCAPTCHA_V2):
values.update(
{
"url_request": f"https://api.{service_type}.com/createTask",
"url_response": f"https://api.{service_type}.com/getTaskResult",
}
)
else:
values.update(
{
Expand All @@ -150,22 +159,32 @@ def urls_set(cls, values):
"""


class ServicePostResponseSer(MyBaseModel):
status: int
request: str
class CreateTaskResponseSer(MyBaseModel):
errorId: int
taskId: int = None


class GetTaskResultRequestSer(BaseModel):
clientKey: str
taskId: int = None


class ServiceGetResponseSer(BaseModel):
status: int
request: Union[str, dict]
class GetTaskResultResponseSer(BaseModel):
errorId: int = 0
status: str = None
solution: dict = None
cost: float = None
ip: str = None
createTime: int = None
endTime: int = None
solveCount: int = None

# ReCaptcha V3 params
user_check: str = ""
user_score: str = ""

"""
Captcha tasks serializers
"""


class ResponseSer(MyBaseModel):
captchaSolve: Union[dict, str] = {}
taskId: Optional[int] = None
error: bool = False
errorBody: Optional[str] = None
class TextCaptchaTaskSer(TaskSer):
type: str = "TextCaptchaTask"
comment: str = "Если завтра суббота, то какой сегодня день?"
Loading

0 comments on commit 32bfea1

Please sign in to comment.