upd structure && upd control class

This commit is contained in:
Andrei 2023-03-20 03:58:52 +03:00
parent ef352bae56
commit 36c8b1c851
20 changed files with 751 additions and 466 deletions

View File

@ -4,7 +4,7 @@ omit =
*/tests/*
include =
*/python3_anticaptcha/*
*/src/python3_anticaptcha/*
[report]
@ -13,4 +13,4 @@ omit =
*/tests/*
include =
*/python3_anticaptcha/*
*/src/python3_anticaptcha/*

View File

@ -20,9 +20,6 @@ jobs:
- name: Build docs requirements
run: pip install -r docs/requirements.txt
- name: Build library requirements
run: pip install -r src/requirements.txt
- name: Build docs
run: make doc

View File

@ -39,7 +39,6 @@ jobs:
run: |
pip install --upgrade pip
pip install -r requirements.test.txt
pip install -r src/requirements.txt
- name: Test
run: make tests

View File

@ -1,8 +1,7 @@
install:
cd src/ && pip install -e .
tests:
cd src/ && \
tests: install
coverage run --rcfile=.coveragerc -m pytest -s tests --disable-warnings && \
coverage report --precision=3 --sort=cover --skip-empty --show-missing && \
coverage html --precision=3 --skip-empty -d coverage/html/ && \
@ -12,31 +11,25 @@ refactor:
black docs/
isort docs/
cd src/ && \
autoflake --in-place \
--recursive \
--remove-unused-variables \
--remove-duplicate-keys \
--remove-all-unused-imports \
--ignore-init-module-imports \
python3_anticaptcha/ tests/ && \
black python3_anticaptcha/ tests/ && \
isort python3_anticaptcha/ tests/
src/ tests/ && \
black src/ tests/ && \
isort src/ tests/
lint:
cd src/ && \
autoflake --in-place --recursive python3_anticaptcha/ --check && \
black python3_anticaptcha/ --check && \
isort python3_anticaptcha/ --check-only
release:
pip install twine
python setup.py upload
autoflake --in-place --recursive src/ --check && \
black src/ --check && \
isort src/ --check-only
upload:
pip install twine
cd src/ && python setup.py upload
doc:
doc: install
cd docs/ && \
make html -e

View File

@ -11,7 +11,7 @@ sys.path.insert(0, os.path.abspath("src/"))
for x in os.walk("src/python3_anticaptcha/"):
sys.path.insert(0, x[0])
from python3_anticaptcha import core, turnstile
from python3_anticaptcha import core, control, turnstile
from python3_anticaptcha.__version__ import __version__
# -- Project information -----------------------------------------------------
@ -45,9 +45,7 @@ html_show_sourcelink = False
html_context = {
"project_links": [
ProjectLink("PyPI Releases", "https://pypi.org/project/python3-anticaptcha/"),
ProjectLink(
"Source Code", "https://github.com/AndreiDrang/python3-anticaptcha"
),
ProjectLink("Source Code", "https://github.com/AndreiDrang/python3-anticaptcha"),
ProjectLink(
"AntiCaptcha",
"http://getcaptchasolution.com/vchfpctqyz",

View File

@ -23,6 +23,7 @@ The library is intended for software developers and is used to work with the `An
:caption: Captcha examples:
modules/turnstile/example.rst
modules/control/example.rst
.. toctree::
:maxdepth: 2

View File

@ -0,0 +1,12 @@
Control
=======
To import this module:
.. code-block:: python
from python3_anticaptcha.control import Control
.. autoclass:: python3_anticaptcha.control.Control
:members:

View File

@ -24,6 +24,10 @@ To import this module:
:members:
:undoc-members:
.. autoclass:: core.enum.ProxyType
.. autoclass:: core.enum.ProxyTypeEnm
:members:
:undoc-members:
.. autoclass:: core.enum.ControlPostfixEnm
:members:
:undoc-members:

View File

@ -8,5 +8,5 @@ To import this module:
from python3_anticaptcha.turnstile import Turnstile
.. autoclass:: turnstile.Turnstile
.. autoclass:: python3_anticaptcha.turnstile.Turnstile
:members:

View File

@ -1,63 +0,0 @@
import aiohttp
import requests
from python3_anticaptcha import get_app_stats_url
class AntiCaptchaAppStats:
def __init__(self, anticaptcha_key: str):
"""
Синхронный метод работы с балансом и жалобами
:param anticaptcha_key: Ключ антикапчи
"""
self.ANTICAPTCHA_KEY = anticaptcha_key
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type:
return False
return True
def get_stats(self, softId: int, mode: str = "errors"):
"""
Получение баланса аккаунта
:return: Возвращает актуальный баланс
"""
answer = requests.post(
get_app_stats_url,
json={"clientKey": self.ANTICAPTCHA_KEY, "softId": softId, "mode": mode},
verify=False,
)
return answer.json()
class aioAntiCaptchaAppStats:
def __init__(self, anticaptcha_key: str):
"""
Асинхронный метод работы с балансом и жалобами
:param anticaptcha_key: Ключ антикапчи
"""
self.ANTICAPTCHA_KEY = anticaptcha_key
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type:
return False
return True
async def get_stats(self, softId: int, mode: str = "errors"):
"""
Получение баланса аккаунта
:return: Возвращает актуальный баланс
"""
async with aiohttp.ClientSession() as session:
async with session.post(
get_app_stats_url,
json={"clientKey": self.ANTICAPTCHA_KEY, "softId": softId, "mode": mode},
) as resp:
return await resp.json()

View File

@ -1,348 +0,0 @@
import aiohttp
import requests
# Адрес для получения баланса
get_balance_url = "https://api.anti-captcha.com/getBalance"
# Адрес для отправки жалобы на неверное решение капчи-изображения
incorrect_imagecaptcha_url = "https://api.anti-captcha.com/reportIncorrectImageCaptcha"
# Адрес для отправки жалобы на неверное решение ReCaptcha
incorrect_recaptcha_url = "https://api.anti-captcha.com/reportIncorrectRecaptcha"
# Адрес для получения информации о очереди
get_queue_status_url = "https://api.anti-captcha.com/getQueueStats"
# С помощью этого метода можно получить статистику трат за последние 24 часа.
get_spend_stats_url = "https://api.anti-captcha.com/getSpendingStats"
# Адрес для получения информации о приложении
get_app_stats_url = "https://api.anti-captcha.com/getAppStats"
# С помощью этого метода можно получить статистику трат за последние 24 часа.
send_funds_url = "https://api.anti-captcha.com/sendFunds"
# available app stats mods
mods = ("errors", "views", "downloads", "users", "money")
# available complaint captcha types
complaint_types = ("image", "recaptcha")
# availalbe queue ID's
queue_ids = (1, 2, 5, 6, 7, 10, 11, 12, 13, 18, 19, 20, 21, 22)
queues_names = (
"English ImageToText",
"Russian ImageToText",
"Recaptcha Proxy-on",
"Recaptcha Proxyless",
"FunCaptcha",
"Funcaptcha Proxyless",
"Square Net Task",
"GeeTest Proxy-on",
"GeeTest Proxyless",
)
class AntiCaptchaControl:
def __init__(self, anticaptcha_key: str):
"""
Синхронный метод работы с балансом и жалобами
:param anticaptcha_key: Ключ антикапчи
"""
self.ANTICAPTCHA_KEY = anticaptcha_key
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type:
return False
return True
def get_balance(self) -> dict:
"""
Получение баланса аккаунта
:return: Возвращает актуальный баланс
"""
answer = requests.post(get_balance_url, json={"clientKey": self.ANTICAPTCHA_KEY}, verify=False)
return answer.json()
def send_funds(self, accountLogin: str = None, accountEmail: str = None, amount: float = None) -> dict:
"""
Отправить средства другому пользователю
В вашем аккаунте должна быть включена опция отправки средств через API.
Включается через службу поддержки, нужно указать причину зачем вам это требуется.
:param accountLogin: Логин целевого аккаунта
:param accountEmail: Адрес почты целевого аккаунта
:param amount: Сумма
"""
payload = {
"clientKey": self.ANTICAPTCHA_KEY,
"accountLogin": accountLogin,
"accountEmail": accountEmail,
"amount": amount,
}
# get response
answer = requests.post(send_funds_url, json=payload, verify=False)
return answer.json()
def get_spend_stats(self, date: int = None, queue: str = None, softId: int = None, ip: str = None) -> dict:
f"""
С помощью этого метода можно получить статистику трат за последние 24 часа.
:param date: Unix timestamp начала периода 24-х часового отчета
:param queue: Имя очереди, может быть найдено в статистике Антикапчи.
Если не указано, то возвращается суммированная статистика по всем очередям.
:param softId: ID приложения из Developers Center
:param ip: IP с которого шли запросы к API
:return: Возвращает словарь с данными трат
"""
if queue and queue not in queues_names:
raise ValueError(f"\nWrong `queue` parameter. Valid params: {queues_names}." f"\n\tYour param - `{queue}`")
payload = {
"clientKey": self.ANTICAPTCHA_KEY,
"date": date,
"queue": queue,
"softId": softId,
"ip": ip,
}
# get response
answer = requests.post(get_spend_stats_url, json=payload, verify=False)
return answer.json()
def get_app_stats(self, softId: int, mode: str = "errors") -> dict:
"""
Получение статистики приложения
:return: Возвращает актуальный баланс
"""
if mode not in mods:
raise ValueError(f"\nWrong `mode` parameter. Valid params: {mods}." f"\n\tYour param - `{mode}`")
payload = {"clientKey": self.ANTICAPTCHA_KEY, "softId": softId, "mode": mode}
answer = requests.post(get_app_stats_url, json=payload, verify=False)
if answer.text:
return answer.json()
else:
return {"errorId": 1}
def complaint_on_result(self, reported_id: int, captcha_type: str = "image") -> dict:
f"""
Позволяет отправить жалобу на неправильно решённую капчу.
:param reported_id: Отправляете ID капчи на которую нужно пожаловаться
:param captcha_type: Тип капчи на который идёт жалоба. Возможные варианты:
{complaint_types}
:return: Возвращает True/False, в зависимости от результата
"""
if captcha_type not in complaint_types:
raise ValueError(
f"\nWrong `captcha_type` parameter. Valid params: {complaint_types}."
f"\n\tYour param - `{captcha_type}`"
)
payload = {"clientKey": self.ANTICAPTCHA_KEY, "taskId": reported_id}
# complaint on image captcha
if captcha_type == "image":
answer = requests.post(incorrect_imagecaptcha_url, json=payload, verify=False)
# complaint on re-captcha
elif captcha_type == "recaptcha":
answer = requests.post(incorrect_recaptcha_url, json=payload, verify=False)
return answer.json()
@staticmethod
def get_queue_status(queue_id: int) -> dict:
"""
Получение информации о загрузке очереди, в зависимости от ID очереди.
Метод позволяет определить, насколько в данный момент целесообразно загружать новое задание в очередь.
Данные в выдаче кешируются на 10 секунд.
Список ID очередей:
1 - стандартная ImageToText, язык английский
2 - стандартная ImageToText, язык русский
5 - Recaptcha NoCaptcha
6 - Recaptcha Proxyless
7 - Funcaptcha
10 - Funcaptcha Proxyless
11 - Square Net Task
12 - GeeTest Proxy-On
13 - GeeTest Proxyless
18 - Recaptcha V3 s0.3
19 - Recaptcha V3 s0.7
20 - Recaptcha V3 s0.9
Пример выдачи ответа:
{
"waiting":242,
"load":60.33,
"bid":"0.0008600982",
"speed":10.77,
"total": 610
}
:param queue_id: Номер очереди
:return: JSON-объект
"""
if queue_id not in queue_ids:
raise ValueError(f"\nWrong `mode` parameter. Valid params: {queue_ids}." f"\n\tYour param - `{queue_id}`")
payload = {"queueId": queue_id}
answer = requests.post(get_queue_status_url, json=payload, verify=False)
return answer.json()
class aioAntiCaptchaControl:
def __init__(self, anticaptcha_key: str):
"""
Асинхронный метод работы с балансом и жалобами
:param anticaptcha_key: Ключ антикапчи
"""
self.ANTICAPTCHA_KEY = anticaptcha_key
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type:
return False
return True
async def get_balance(self) -> dict:
"""
Получение баланса аккаунта
:return: Возвращает актуальный баланс
"""
async with aiohttp.ClientSession() as session:
async with session.post(get_balance_url, json={"clientKey": self.ANTICAPTCHA_KEY}) as resp:
if await resp.text():
return await resp.json()
else:
return {"errorId": 1}
async def send_funds(self, accountLogin: str = None, accountEmail: str = None, amount: float = None) -> dict:
"""
Отправить средства другому пользователю
В вашем аккаунте должна быть включена опция отправки средств через API.
Включается через службу поддержки, нужно указать причину зачем вам это требуется.
:param accountLogin: Логин целевого аккаунта
:param accountEmail: Адрес почты целевого аккаунта
:param amount: Сумма
"""
payload = {
"clientKey": self.ANTICAPTCHA_KEY,
"accountLogin": accountLogin,
"accountEmail": accountEmail,
"amount": amount,
}
# get response
async with aiohttp.ClientSession() as session:
async with session.post(send_funds_url, json=payload) as resp:
if await resp.text():
return await resp.json()
else:
return {"errorId": 1}
async def get_spend_stats(self, date: int = None, queue: str = None, softId: int = None, ip: str = None) -> dict:
f"""
С помощью этого метода можно получить статистику трат за последние 24 часа.
:param date: Unix timestamp начала периода 24-х часового отчета
:param queue: Имя очереди, может быть найдено в статистике Антикапчи.
Если не указано, то возвращается суммированная статистика по всем очередям.
:param softId: ID приложения из Developers Center
:param ip: IP с которого шли запросы к API
:return: Возвращает словарь с данными трат
"""
if queue and queue not in queues_names:
raise ValueError(f"\nWrong `queue` parameter. Valid params: {queues_names}." f"\n\tYour param - `{queue}`")
payload = {
"clientKey": self.ANTICAPTCHA_KEY,
"date": date,
"queue": queue,
"softId": softId,
"ip": ip,
}
# get response
async with aiohttp.ClientSession() as session:
async with session.post(get_spend_stats_url, json=payload) as resp:
if await resp.text():
return await resp.json()
else:
return {"errorId": 1}
async def get_app_stats(self, softId: int, mode: str = "errors") -> dict:
"""
Получение баланса аккаунта
:return: Возвращает актуальный баланс
"""
if mode not in mods:
raise ValueError(f"\nWrong `mode` parameter. Valid params: {mods}." f"\n\tYour param - `{mode}`")
payload = {"clientKey": self.ANTICAPTCHA_KEY, "softId": softId, "mode": mode}
async with aiohttp.ClientSession() as session:
async with session.post(get_app_stats_url, json=payload) as resp:
if await resp.text():
return await resp.json()
else:
return {"errorId": 1}
async def complaint_on_result(self, reported_id: int, captcha_type: str = "image") -> dict:
f"""
Позволяет отправить жалобу на неправильно решённую капчу.
:param reported_id: Отправляете ID капчи на которую нужно пожаловаться
:param captcha_type: Тип капчи на который идёт жалоба. Возможные варианты:
{complaint_types}
:return: Возвращает True/False, в зависимости от результата
"""
if captcha_type not in complaint_types:
raise ValueError(
f"\nWrong `captcha_type` parameter. Valid params: {complaint_types}."
f"\n\tYour param - `{captcha_type}`"
)
payload = {"clientKey": self.ANTICAPTCHA_KEY, "taskId": reported_id}
# complaint on image captcha
if captcha_type == "image":
async with aiohttp.ClientSession() as session:
async with session.post(incorrect_imagecaptcha_url, json=payload) as resp:
if await resp.text():
return await resp.json()
else:
return {"errorId": 1}
# complaint on re-captcha
elif captcha_type == "recaptcha":
async with aiohttp.ClientSession() as session:
async with session.post(incorrect_recaptcha_url, json=payload) as resp:
if await resp.text():
return await resp.json()
else:
return {"errorId": 1}
@staticmethod
async def get_queue_status(queue_id: int) -> dict:
"""
Получение информации о загрузке очереди, в зависимости от ID очереди.
Метод позволяет определить, насколько в данный момент целесообразно загружать новое задание в очередь.
Данные в выдаче кешируются на 10 секунд.
Список ID очередей:
1 - стандартная ImageToText, язык английский
2 - стандартная ImageToText, язык русский
5 - Recaptcha NoCaptcha
6 - Recaptcha Proxyless
7 - Funcaptcha
10 - Funcaptcha Proxyless
Пример выдачи ответа:
{
"waiting":242,
"load":60.33,
"bid":"0.0008600982",
"speed":10.77,
"total": 610
}
:param queue_id: Номер очереди
:return: JSON-объект
"""
if queue_id not in queue_ids:
raise ValueError(f"\nWrong `mode` parameter. Valid params: {queue_ids}." f"\n\tYour param - `{queue_id}`")
payload = {"queueId": queue_id}
async with aiohttp.ClientSession() as session:
async with session.post(get_queue_status_url, json=payload) as resp:
if await resp.text():
return await resp.json()
else:
return {"errorId": 1}

View File

@ -0,0 +1,627 @@
from typing import Optional
from .core.base import BaseCaptcha
from .core.enum import CaptchaTypeEnm, ControlPostfixEnm
class Control(BaseCaptcha):
def __init__(
self,
api_key: str,
*args,
**kwargs,
):
"""
The class is used to work with Turnstile.
Args:
api_key: Capsolver API key
captcha_type: Captcha type
websiteURL: Address of the webpage
websiteKey: Turnstile sitekey
proxyType: Type of the proxy
proxyAddress: Proxy IP address IPv4/IPv6. Not allowed to use:
host names instead of IPs,
transparent proxies (where client IP is visible),
proxies from local networks (192.., 10.., 127...)
proxyPort: Proxy port.
sleep_time: The waiting time between requests to get the result of the Captcha
kwargs: Additional not required params for main request body.
Like `callbackUrl`/`languagePool` and etc.
More info - https://anti-captcha.com/apidoc/methods/createTask
Examples:
>>> Control(api_key="99d7d111a0111dc11184111c8bb111da").get_balance()
{
"errorId": 0,
"balance": 14.12396
}
>>> Control.get_queue_status(queue_id=1)
{
"waiting": 234,
"load": 46.58,
"bid": 0.000576,
"speed": 8.43,
"total": 438
}
>>> Control(api_key="99d7d111a0111dc11184111c8bb111da").get_spending_stats(softId=867)
{
"errorId":0,
"data":[
{
"dateFrom":1679183850,
"dateTill":1679187449,
"volume":0,
"money":0
}
]
}
>>> Control(api_key="99d7d111a0111dc11184111c8bb111da").get_app_stats(softId=867, mode='views')
{
"errorId":0,
"chartData":[
{
......
}
],
"fromDate":"17 Feb 23:48",
"toDate":"19 Mar 23:48"
}
>>> Control(api_key="99d7d111a0111dc11184111c8bb111da").report_incorrect_image(taskId=425436541)
{
"errorId":0,
"status":"success"
}
>>> Control(api_key="99d7d111a0111dc11184111c8bb111da").report_incorrect_recaptcha(taskId=425436541)
{
"errorId":0,
"status":"success"
}
>>> Control(api_key="99d7d111a0111dc11184111c8bb111da").report_correct_recaptcha(taskId=425436541)
{
"errorId":0,
"status":"success"
}
>>> Control(api_key="99d7d111a0111dc11184111c8bb111da").report_incorrect_hcaptcha(taskId=425436541)
{
"errorId":0,
"status":"success"
}
Notes:
https://anti-captcha.com/apidoc/methods/getBalance
https://anti-captcha.com/apidoc/methods/getQueueStats
https://anti-captcha.com/apidoc/methods/getSpendingStats
https://anti-captcha.com/apidoc/methods/getAppStats
https://anti-captcha.com/apidoc/methods/reportIncorrectHcaptcha
https://anti-captcha.com/apidoc/methods/reportCorrectRecaptcha
https://anti-captcha.com/apidoc/methods/reportIncorrectRecaptcha
https://anti-captcha.com/apidoc/methods/reportIncorrectImageCaptcha
"""
super().__init__(api_key=api_key, captcha_type=CaptchaTypeEnm.Control, *args, **kwargs)
def get_balance(self) -> dict:
"""
Retrieve an account balance with its account key.
Examples:
>>> Control(api_key="99d7d111a0111dc11184111c8bb111da").get_balance()
{
"errorId": 0,
"balance": 14.12396
}
Returns:
Dict with full server response
Notes:
https://anti-captcha.com/apidoc/methods/getBalance
"""
return self._send_post_request(
session=self._session,
url_postfix=ControlPostfixEnm.GET_BALANCE,
payload={"clientKey": self._params.clientKey},
)
async def aio_get_balance(self) -> dict:
"""
Async retrieve an account balance with its account key.
Examples:
>>> await Control(api_key="99d7d111a0111dc11184111c8bb111da").aio_get_balance()
{
"errorId": 0,
"balance": 14.12396
}
Returns:
Dict with full server response
Notes:
https://anti-captcha.com/apidoc/methods/getBalance
"""
return await self._aio_send_post_request(
url_postfix=ControlPostfixEnm.GET_BALANCE, payload={"clientKey": self._params.clientKey}
)
@staticmethod
def get_queue_status(queue_id: int) -> dict:
"""
This method makes it possible to define a suitable time for uploading a new task
Args:
queue_id: Identifier of a queue
Examples:
>>> Control.get_queue_status(queue_id=1)
{
"waiting": 234,
"load": 46.58,
"bid": 0.000576,
"speed": 8.43,
"total": 438
}
>>> Control.get_queue_status(queue_id=20)
{
"waiting": 90,
"load": 38.36,
"bid": 0.002,
"speed": 7.38,
"total": 146
}
Returns:
Dict with full server response
Notes:
https://anti-captcha.com/apidoc/methods/getQueueStats
"""
return BaseCaptcha._send_post_request(
url_postfix=ControlPostfixEnm.GET_QUEUE_STATS, payload={"queueId": queue_id}
)
@staticmethod
async def aio_get_queue_status(queue_id: int) -> dict:
"""
Async method makes it possible to define a suitable time for uploading a new task
Args:
queue_id: Identifier of a queue
Examples:
>>> await Control.aio_get_queue_status(queue_id=1)
{
"waiting": 234,
"load": 46.58,
"bid": 0.000576,
"speed": 8.43,
"total": 438
}
>>> await Control.aio_get_queue_status(queue_id=20)
{
"waiting": 90,
"load": 38.36,
"bid": 0.002,
"speed": 7.38,
"total": 146
}
Returns:
Dict with full server response
Notes:
https://anti-captcha.com/apidoc/methods/getQueueStats
"""
return await BaseCaptcha._aio_send_post_request(
url_postfix=ControlPostfixEnm.GET_QUEUE_STATS, payload={"queueId": queue_id}
)
def get_spending_stats(self, **kwargs) -> dict:
"""
This method grabs account spendings and task volume statistics for a 24 hour period.
Examples:
>>> Control(api_key="99d7d111a0111dc11184111c8bb111da").get_spending_stats(softId=867)
{
"errorId":0,
"data":[
{
"dateFrom":1679183850,
"dateTill":1679187449,
"volume":0,
"money":0
}
]
}
>>> Control(api_key="99d7d111a0111dc11184111c8bb111da").get_spending_stats(softId=867,
... queue="English ImageToText")
{
"errorId":0,
"data":[
{
"dateFrom":1679183850,
"dateTill":1679187449,
"volume":0,
"money":0
}
]
}
>>> Control(api_key="99d7d111a0111dc11184111c8bb111da").get_spending_stats(queue="English ImageToText")
{
"errorId":0,
"data":[
{
"dateFrom":1679183850,
"dateTill":1679187449,
"volume":0,
"money":0
}
]
}
Returns:
Dict with full server response
Notes:
https://anti-captcha.com/apidoc/methods/getSpendingStats
"""
return self._send_post_request(
session=self._session,
url_postfix=ControlPostfixEnm.GET_SPENDING_STATS,
payload={"clientKey": self._params.clientKey, **kwargs},
)
async def aio_get_spending_stats(self, **kwargs) -> dict:
"""
Async method grabs account spendings and task volume statistics for a 24 hour period.
Examples:
>>> await Control(api_key="99d7d111a0111dc11184111c8bb111da").aio_get_spending_stats(softId=867)
{
"errorId":0,
"data":[
{
"dateFrom":1679183850,
"dateTill":1679187449,
"volume":0,
"money":0
}
]
}
>>> await Control(api_key="99d7d111a0111dc11184111c8bb111da").aio_get_spending_stats(softId=867,
... queue="English ImageToText")
{
"errorId":0,
"data":[
{
"dateFrom":1679183850,
"dateTill":1679187449,
"volume":0,
"money":0
}
]
}
>>> await Control(api_key="99d7d111a0111dc11184111c8bb111da").aio_get_spending_stats(queue="English ImageToText")
{
"errorId":0,
"data":[
{
"dateFrom":1679183850,
"dateTill":1679187449,
"volume":0,
"money":0
}
]
}
Returns:
Dict with full server response
Notes:
https://anti-captcha.com/apidoc/methods/getSpendingStats
"""
return await self._aio_send_post_request(
url_postfix=ControlPostfixEnm.GET_SPENDING_STATS, payload={"clientKey": self._params.clientKey, **kwargs}
)
def get_app_stats(self, softId: int, mode: Optional[str] = None) -> dict:
"""
This method retrieves daily statistics for your application, which you register in Developer Center.
Statistics are available only to the application owner. Improper access returns `ERROR_ACCESS_DENIED`.
Examples:
>>> Control(api_key="99d7d111a0111dc11184111c8bb111da").get_app_stats(softId=867, mode='views')
{
"errorId":0,
"chartData":[
{
......
}
],
"fromDate":"17 Feb 23:48",
"toDate":"19 Mar 23:48"
}
>>> Control(api_key="99d7d111a0111dc11184111c8bb111da").get_app_stats(softId=867, mode='errors')
{
"errorId":0,
"chartData":[
{
......
}
],
"fromDate":"17 Feb 23:48",
"toDate":"19 Mar 23:48"
}
Returns:
Dict with full server response
Notes:
https://anti-captcha.com/apidoc/methods/getAppStats
"""
return self._send_post_request(
session=self._session,
url_postfix=ControlPostfixEnm.GET_APP_STATS,
payload={"clientKey": self._params.clientKey, "softId": softId, "mode": mode},
)
async def aio_get_app_stats(self, softId: int, mode: Optional[str] = None) -> dict:
"""
Async method retrieves daily statistics for your application, which you register in Developer Center.
Statistics are available only to the application owner. Improper access returns `ERROR_ACCESS_DENIED`.
Examples:
>>> await Control(api_key="99d7d111a0111dc11184111c8bb111da").aio_get_app_stats(softId=867, mode='views')
{
"errorId":0,
"chartData":[
{
......
}
],
"fromDate":"17 Feb 23:48",
"toDate":"19 Mar 23:48"
}
>>> await Control(api_key="99d7d111a0111dc11184111c8bb111da").aio_get_app_stats(softId=867, mode='errors')
{
"errorId":0,
"chartData":[
{
......
}
],
"fromDate":"17 Feb 23:48",
"toDate":"19 Mar 23:48"
}
Returns:
Dict with full server response
Notes:
https://anti-captcha.com/apidoc/methods/getAppStats
"""
return await self._aio_send_post_request(
url_postfix=ControlPostfixEnm.GET_APP_STATS,
payload={"clientKey": self._params.clientKey, "softId": softId, "mode": mode},
)
def report_incorrect_image(self, taskId: int) -> dict:
"""
Complaints are accepted for image captchas only.
Examples:
>>> Control(api_key="99d7d111a0111dc11184111c8bb111da").report_incorrect_image(taskId=425436541)
{
"errorId":0,
"status":"success"
}
Returns:
Dict with full server response
Notes:
https://anti-captcha.com/apidoc/methods/reportIncorrectImageCaptcha
"""
return self._send_post_request(
session=self._session,
url_postfix=ControlPostfixEnm.REPORT_INCORRECT_IMAGE_CAPTCHA,
payload={"clientKey": self._params.clientKey, "taskId": taskId},
)
async def aio_report_incorrect_image(self, taskId: int) -> dict:
"""
Async complaints are accepted for image captchas only.
Examples:
>>> await Control(api_key="99d7d111a0111dc11184111c8bb111da").aio_report_incorrect_image(taskId=425436541)
{
"errorId":0,
"status":"success"
}
Returns:
Dict with full server response
Notes:
https://anti-captcha.com/apidoc/methods/reportIncorrectImageCaptcha
"""
return await self._aio_send_post_request(
url_postfix=ControlPostfixEnm.REPORT_INCORRECT_IMAGE_CAPTCHA,
payload={"clientKey": self._params.clientKey, "taskId": taskId},
)
def report_incorrect_recaptcha(self, taskId: int) -> dict:
"""
Complaints are accepted for V2 and V3 Recaptchas only, including Enterprise Recaptcha.
Examples:
>>> Control(api_key="99d7d111a0111dc11184111c8bb111da").report_incorrect_recaptcha(taskId=425436541)
{
"errorId":0,
"status":"success"
}
Returns:
Dict with full server response
Notes:
https://anti-captcha.com/apidoc/methods/reportIncorrectRecaptcha
"""
return self._send_post_request(
session=self._session,
url_postfix=ControlPostfixEnm.REPORT_INCORRECT_RECAPTCHA,
payload={"clientKey": self._params.clientKey, "taskId": taskId},
)
async def aio_report_incorrect_recaptcha(self, taskId: int) -> dict:
"""
Async complaints are accepted for V2 and V3 Recaptchas only, including Enterprise Recaptcha.
Examples:
>>> await Control(api_key="99d7d111a0111dc11184111c8bb111da").aio_report_incorrect_recaptcha(taskId=425436541)
{
"errorId":0,
"status":"success"
}
Returns:
Dict with full server response
Notes:
https://anti-captcha.com/apidoc/methods/reportIncorrectRecaptcha
"""
return await self._aio_send_post_request(
url_postfix=ControlPostfixEnm.REPORT_INCORRECT_RECAPTCHA,
payload={"clientKey": self._params.clientKey, "taskId": taskId},
)
def report_correct_recaptcha(self, taskId: int) -> dict:
"""
Reporting correctly solved ReCaptcha
Examples:
>>> Control(api_key="99d7d111a0111dc11184111c8bb111da").report_correct_recaptcha(taskId=425436541)
{
"errorId":0,
"status":"success"
}
Returns:
Dict with full server response
Notes:
https://anti-captcha.com/apidoc/methods/reportCorrectRecaptcha
"""
return self._send_post_request(
session=self._session,
url_postfix=ControlPostfixEnm.REPORT_CORRECT_RECAPTCHA,
payload={"clientKey": self._params.clientKey, "taskId": taskId},
)
async def aio_report_correct_recaptcha(self, taskId: int) -> dict:
"""
Async reporting correctly solved ReCaptcha
Examples:
>>> await Control(api_key="99d7d111a0111dc11184111c8bb111da").aio_report_correct_recaptcha(taskId=425436541)
{
"errorId":0,
"status":"success"
}
Returns:
Dict with full server response
Notes:
https://anti-captcha.com/apidoc/methods/reportCorrectRecaptcha
"""
return await self._aio_send_post_request(
url_postfix=ControlPostfixEnm.REPORT_CORRECT_RECAPTCHA,
payload={"clientKey": self._params.clientKey, "taskId": taskId},
)
def report_incorrect_hcaptcha(self, taskId: int) -> dict:
"""
Use this method to send us information about tokens which did not pass on target service
Examples:
>>> Control(api_key="99d7d111a0111dc11184111c8bb111da").report_incorrect_hcaptcha(taskId=425436541)
{
"errorId":0,
"status":"success"
}
Returns:
Dict with full server response
Notes:
https://anti-captcha.com/apidoc/methods/reportIncorrectHcaptcha
"""
return self._send_post_request(
session=self._session,
url_postfix=ControlPostfixEnm.REPORT_INCORRECT_HCAPTCHA,
payload={"clientKey": self._params.clientKey, "taskId": taskId},
)
async def aio_report_incorrect_hcaptcha(self, taskId: int) -> dict:
"""
Async method to send us information about tokens which did not pass on target service
Examples:
>>> await Control(api_key="99d7d111a0111dc11184111c8bb111da").aio_report_incorrect_hcaptcha(taskId=4256541)
{
"errorId":0,
"status":"success"
}
Returns:
Dict with full server response
Notes:
https://anti-captcha.com/apidoc/methods/reportIncorrectHcaptcha
"""
return await self._aio_send_post_request(
url_postfix=ControlPostfixEnm.REPORT_INCORRECT_HCAPTCHA,
payload={"clientKey": self._params.clientKey, "taskId": taskId},
)

View File

@ -1,7 +1,7 @@
import time
import asyncio
import logging
from typing import Union
from typing import Union, Optional
from urllib import parse
import aiohttp
@ -35,24 +35,30 @@ class BaseCaptcha:
request_url: API address for sending requests
"""
def __init__(self, api_key: str, captcha_type: Union[CaptchaTypeEnm, str], sleep_time: int, **kwargs):
def __init__(
self,
api_key: str,
captcha_type: Union[CaptchaTypeEnm, str],
sleep_time: int = 15,
**kwargs,
):
# validate captcha_type parameter
if captcha_type not in CaptchaTypeEnm.list_values():
raise ValueError(f"Invalid `captcha_type` parameter set, available - {CaptchaTypeEnm.list_values()}")
self.__sleep_time = sleep_time
# assign args to validator
self.__params = CreateTaskRequestSer(clientKey=api_key, **locals())
self._params = CreateTaskRequestSer(clientKey=api_key, **locals())
# `task` body for task creation payload
self.task_params = {}
# prepare `get task result` payload
self._get_result_params = GetTaskResultRequestSer(clientKey=api_key)
# prepare session
self.__session = requests.Session()
self.__session.mount("http://", HTTPAdapter(max_retries=RETRIES))
self.__session.mount("https://", HTTPAdapter(max_retries=RETRIES))
self.__session.verify = False
self._session = requests.Session()
self._session.mount("http://", HTTPAdapter(max_retries=RETRIES))
self._session.mount("https://", HTTPAdapter(max_retries=RETRIES))
self._session.verify = False
"""
Sync part
@ -60,7 +66,7 @@ class BaseCaptcha:
def _processing_captcha(self) -> dict:
# added task params to payload
self.__params.task = self.task_params
self._params.task = self.task_params
created_task = self._create_task()
@ -69,12 +75,12 @@ class BaseCaptcha:
return self._get_result().dict()
return created_task.dict()
def _create_task(self) -> CreateTaskResponseSer:
def _create_task(self, url_postfix: str = CREATE_TASK_POSTFIX) -> CreateTaskResponseSer:
"""
Function send SYNC request to service and wait for result
"""
try:
resp = self.__session.post(parse.urljoin(BASE_REQUEST_URL, CREATE_TASK_POSTFIX), json=self.__params.dict())
resp = self._session.post(parse.urljoin(BASE_REQUEST_URL, url_postfix), json=self._params.dict())
if resp.status_code == 200:
return CreateTaskResponseSer(**resp.json())
else:
@ -93,7 +99,7 @@ class BaseCaptcha:
attempts = attempts_generator()
for _ in attempts:
try:
task_result_response = self.__session.post(
task_result_response = self._session.post(
parse.urljoin(BASE_REQUEST_URL, GET_RESULT_POSTFIX), json=self._get_result_params.dict()
)
if task_result_response.status_code == 200:
@ -114,13 +120,32 @@ class BaseCaptcha:
logging.exception(error)
raise
@staticmethod
def _send_post_request(
payload: Optional[dict] = None,
session: requests.Session = requests.Session(),
url_postfix: str = CREATE_TASK_POSTFIX,
) -> dict:
"""
Function send SYNC request to service and wait for result
"""
try:
resp = session.post(parse.urljoin(BASE_REQUEST_URL, url_postfix), json=payload)
if resp.status_code == 200:
return resp.json()
else:
raise ValueError(resp.raise_for_status())
except Exception as error:
logging.exception(error)
raise
"""
Async part
"""
async def _aio_processing_captcha(self) -> dict:
# added task params to payload
self.__params.task = self.task_params
self._params.task = self.task_params
created_task = await self._aio_create_task()
@ -130,15 +155,13 @@ class BaseCaptcha:
return result.dict()
return created_task.dict()
async def _aio_create_task(self) -> CreateTaskResponseSer:
async def _aio_create_task(self, url_postfix: str = CREATE_TASK_POSTFIX) -> CreateTaskResponseSer:
"""
Function send SYNC request to service and wait for result
"""
async with aiohttp.ClientSession() as session:
try:
async with session.post(
parse.urljoin(BASE_REQUEST_URL, CREATE_TASK_POSTFIX), json=self.__params.dict()
) as resp:
async with session.post(parse.urljoin(BASE_REQUEST_URL, url_postfix), json=self._params.dict()) as resp:
if resp.status == 200:
return CreateTaskResponseSer(**await resp.json())
else:
@ -180,6 +203,25 @@ class BaseCaptcha:
logging.exception(error)
raise
@staticmethod
async def _aio_send_post_request(payload: Optional[dict] = None, url_postfix: str = CREATE_TASK_POSTFIX) -> dict:
"""
Function send ASYNC request to service and wait for result
"""
async with aiohttp.ClientSession() as session:
try:
async with session.post(parse.urljoin(BASE_REQUEST_URL, url_postfix), json=payload) as resp:
if resp.status == 200:
return await resp.json()
else:
raise ValueError(resp.reason)
except Exception as error:
logging.exception(error)
raise
# Context methods
def __enter__(self):
return self

View File

@ -78,7 +78,7 @@ class ResponseStatusEnm(str, MyEnum):
ready = "ready" # Task is complete; you'll find a solution in the solution property
class ProxyType(str, MyEnum):
class ProxyTypeEnm(str, MyEnum):
"""
Enum store proxy types
"""
@ -87,3 +87,20 @@ class ProxyType(str, MyEnum):
https = "https"
socks4 = "socks4"
socks5 = "socks5"
class ControlPostfixEnm(str, MyEnum):
"""
Enum store control methods URLs postfix
"""
# get account info
GET_BALANCE = "getBalance"
GET_QUEUE_STATS = "getQueueStats"
GET_APP_STATS = "getAppStats"
GET_SPENDING_STATS = "getSpendingStats"
# reports
REPORT_INCORRECT_IMAGE_CAPTCHA = "reportIncorrectImageCaptcha"
REPORT_INCORRECT_RECAPTCHA = "reportIncorrectRecaptcha"
REPORT_CORRECT_RECAPTCHA = "reportCorrectRecaptcha"
REPORT_INCORRECT_HCAPTCHA = "reportIncorrectHcaptcha"

View File

@ -2,7 +2,7 @@ from typing import Dict
from pydantic import Field, BaseModel, constr
from python3_anticaptcha.core.enum import ProxyType, CaptchaTypeEnm, ResponseStatusEnm
from python3_anticaptcha.core.enum import ProxyTypeEnm, CaptchaTypeEnm, ResponseStatusEnm
from python3_anticaptcha.core.config import APP_KEY
@ -17,7 +17,7 @@ class BaseAPIRequestSer(MyBaseModel):
class BaseAPIResponseSer(MyBaseModel):
errorId: int = Field(..., description="Error identifier.")
errorId: int = Field(None, description="Error identifier.")
errorCode: str = Field(None, description="An error code.")
errorDescription: str = Field(None, description="Short description of the error.")
@ -34,7 +34,7 @@ class CreateTaskRequestTaskSer(MyBaseModel):
class ProxyDataOptionsSer(MyBaseModel):
proxyType: ProxyType = Field(..., description="Type of proxy")
proxyType: ProxyTypeEnm = Field(..., description="Type of proxy")
proxyAddress: str = Field(
...,
description="Proxy IP address ipv4/ipv6. No host names or IP addresses from local networks",

View File

@ -1,8 +1,8 @@
from typing import Union, Optional
from python3_anticaptcha.core.base import BaseCaptcha
from python3_anticaptcha.core.enum import ProxyType, CaptchaTypeEnm
from python3_anticaptcha.core.serializer import TurnstileOptionsSer, TurnstileProxylessOptionsSer
from .core.base import BaseCaptcha
from .core.enum import ProxyTypeEnm, CaptchaTypeEnm
from .core.serializer import TurnstileOptionsSer, TurnstileProxylessOptionsSer
class Turnstile(BaseCaptcha):
@ -12,7 +12,7 @@ class Turnstile(BaseCaptcha):
captcha_type: Union[CaptchaTypeEnm, str],
websiteURL: str,
websiteKey: str,
proxyType: Optional[Union[ProxyType, str]] = None,
proxyType: Optional[Union[ProxyTypeEnm, str]] = None,
proxyAddress: Optional[str] = None,
proxyPort: Optional[int] = None,
sleep_time: Optional[int] = 10,

View File

@ -7,11 +7,17 @@ import pytest
@pytest.fixture(scope="function")
def delay():
time.sleep(1)
def delay_func():
time.sleep(5)
@pytest.mark.usefixtures("delay")
@pytest.fixture(scope="class")
def delay_class():
time.sleep(10)
@pytest.mark.usefixtures("delay_func")
@pytest.mark.usefixtures("delay_class")
class BaseTest:
API_KEY = os.getenv("API_KEY", "ad9053f3182ca81755768608fa75")
sleep_time = 5

View File

@ -1,7 +1,7 @@
from tenacity import AsyncRetrying
from urllib3.util.retry import Retry
from src.tests.conftest import BaseTest
from tests.conftest import BaseTest
from python3_anticaptcha.core.base import BaseCaptcha
from python3_anticaptcha.core.enum import CaptchaTypeEnm
from python3_anticaptcha.core.config import RETRIES, ASYNC_RETRIES, BASE_REQUEST_URL, attempts_generator