From ab58fd834c6630eb25c7eded6560afa44e2be46d Mon Sep 17 00:00:00 2001 From: Maxim S Date: Mon, 27 Oct 2025 08:13:12 +0100 Subject: [PATCH 1/4] Added vkimage --- examples/images/vk.jpg | Bin 0 -> 47549 bytes examples/vkimage.py | 26 ++++++++++++++++++++++ examples/vkimage_base64.py | 29 +++++++++++++++++++++++++ examples/vkimage_options.py | 27 +++++++++++++++++++++++ tests/test_vkimage.py | 42 ++++++++++++++++++++++++++++++++++++ 5 files changed, 124 insertions(+) create mode 100644 examples/images/vk.jpg create mode 100644 examples/vkimage.py create mode 100644 examples/vkimage_base64.py create mode 100644 examples/vkimage_options.py create mode 100755 tests/test_vkimage.py diff --git a/examples/images/vk.jpg b/examples/images/vk.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1d851982aaea3c63076af484ccdf86dbc67db508 GIT binary patch literal 47549 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 literal 0 HcmV?d00001 diff --git a/examples/vkimage.py b/examples/vkimage.py new file mode 100644 index 0000000..2aea352 --- /dev/null +++ b/examples/vkimage.py @@ -0,0 +1,26 @@ +import sys +import os +import requests +sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) + +from twocaptcha import TwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +solver = TwoCaptcha(api_key) + +try: + result = solver.geetest_v4(captcha_id='e392e1d7fd421dc63325744d5a2b9c73', + url='https://2captcha.com/demo/geetest-v4') + +except Exception as e: + sys.exit(e) + +else: + sys.exit('result: ' + str(result)) diff --git a/examples/vkimage_base64.py b/examples/vkimage_base64.py new file mode 100644 index 0000000..62e29e6 --- /dev/null +++ b/examples/vkimage_base64.py @@ -0,0 +1,29 @@ +import sys +import os +import requests +sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) + +from twocaptcha import TwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +solver = TwoCaptcha(api_key) + +try: + result = solver.vkimage(files='./images/vk.jpg', + steps='[5,4,7,7,14,22,8,3,2,7,23,22,2,8,24,5,9,20,2,5,0,6,22,4,5,11,12,12,9,6,18,3,21,18,17,' + '7,6,1,4,19,8,11,3,14,20,6,16,11,23,0,10,14,10,9,24,3,14,14,10,0,15,10,6,6,20,12,18,13,' + '20,7,13,9,22,14,24,14,17,22,0,4,6,11,10,15,18,20,0,3,6,4,23,12,15,14,18,4,2,9,5,2]', + ) + +except Exception as e: + sys.exit(e) + +else: + sys.exit('result: ' + str(result)) diff --git a/examples/vkimage_options.py b/examples/vkimage_options.py new file mode 100644 index 0000000..d48e7f9 --- /dev/null +++ b/examples/vkimage_options.py @@ -0,0 +1,27 @@ +import sys +import os +import requests +sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) + +from twocaptcha import TwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +solver = TwoCaptcha(api_key) + +try: + result = solver.vkimage(files='./images/vk.jpg', + steps='[5,4,7,7,14,22,8,3,2,7,23,22,2,8,24,5,9,20,2,5,0,6,22,4,5,11,12,12,9,6,18,3,21,18,17,7,6,1,4,19,8,11,3,14,20,6,16,11,23,0,10,14,10,9,24,3,14,14,10,0,15,10,6,6,20,12,18,13,20,7,13,9,22,14,24,14,17,22,0,4,6,11,10,15,18,20,0,3,6,4,23,12,15,14,18,4,2,9,5,2]', + ) + +except Exception as e: + sys.exit(e) + +else: + sys.exit('result: ' + str(result)) diff --git a/tests/test_vkimage.py b/tests/test_vkimage.py new file mode 100755 index 0000000..05abef2 --- /dev/null +++ b/tests/test_vkimage.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +import unittest + +try: + from .abstract import AbstractTest +except ImportError: + from abstract import AbstractTest + + + +class VkImage(AbstractTest): + + + def test_all_params(self): + + + params = { + 'files' : '../examples/images/vk.jpg', + 'steps' : '[5,4,7,7,14,22,8,3,2,7,23,22,2,8,24,5,9,20,2,5,0,6,22,4,5,11,12,12,9,6,18,3,21,18,17,7,6,' + '1,4,19,8,11,3,14,20,6,16,11,23,0,10,14,10,9,24,3,14,14,10,0,15,10,6,6,20,12,18,13,20,7,13,' + '9,22,14,24,14,17,22,0,4,6,11,10,15,18,20,0,3,6,4,23,12,15,14,18,4,2,9,5,2]', + } + + sends = { + 'method' : 'vkimage', + 'file' : '../examples/images/vk.jpg', + 'steps' : '[5,4,7,7,14,22,8,3,2,7,23,22,2,8,24,5,9,20,2,5,0,6,22,4,5,11,12,12,9,6,18,3,21,18,17,7,6,' + '1,4,19,8,11,3,14,20,6,16,11,23,0,10,14,10,9,24,3,14,14,10,0,15,10,6,6,20,12,18,13,20,7,13,' + '9,22,14,24,14,17,22,0,4,6,11,10,15,18,20,0,3,6,4,23,12,15,14,18,4,2,9,5,2]' + } + + return self.send_return(sends, self.solver.vkimage, **params) + + + + + +if __name__ == '__main__': + + unittest.main() + From d574c500e2ccceca47d7a4811017ff38b09ab4e0 Mon Sep 17 00:00:00 2001 From: Maxim S Date: Mon, 27 Oct 2025 08:18:18 +0100 Subject: [PATCH 2/4] added vkimage --- README.md | 11 ++++++++++ examples/vkimage.py | 7 +++++-- examples/vkimage_base64.py | 6 +++++- examples/vkimage_options.py | 15 ++++++++++++-- setup.py | 2 +- twocaptcha/solver.py | 41 +++++++++++++++++++++++++++++++++++++ 6 files changed, 76 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 658fe9c..120d4c0 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ Examples of API requests for different captcha types are available on the [Pytho - [Cutcaptcha](#cutcaptcha) - [Tencent](#tencent) - [DataDome](#datadome) + - [VKImage](#vkimage) - [CyberSiARA](#cybersiara) - [Other methods](#other-methods) - [send / get\_result](#send--get_result) @@ -439,6 +440,16 @@ result = solver.datadome(captcha_url="https://geo.captcha-delivery.com/captcha/? }, param1=..., ...) ``` +### VKImage + +[API method description.](https://2captcha.com/2captcha-api#vkcaptcha) + +This method can be used to solve VK captcha using graphical captcha. Returns the number of steps and solution value in the target site's API format. Or get an extended response by passing the `extendedResponse=True` attribute to the class instance + +```python +result = solver.vkimage('path/to/captcha.jpg', steps='[5,4,7,7,14,22,8,...]', ...) +``` + ### CyberSiARA diff --git a/examples/vkimage.py b/examples/vkimage.py index 2aea352..62e29e6 100644 --- a/examples/vkimage.py +++ b/examples/vkimage.py @@ -16,8 +16,11 @@ solver = TwoCaptcha(api_key) try: - result = solver.geetest_v4(captcha_id='e392e1d7fd421dc63325744d5a2b9c73', - url='https://2captcha.com/demo/geetest-v4') + result = solver.vkimage(files='./images/vk.jpg', + steps='[5,4,7,7,14,22,8,3,2,7,23,22,2,8,24,5,9,20,2,5,0,6,22,4,5,11,12,12,9,6,18,3,21,18,17,' + '7,6,1,4,19,8,11,3,14,20,6,16,11,23,0,10,14,10,9,24,3,14,14,10,0,15,10,6,6,20,12,18,13,' + '20,7,13,9,22,14,24,14,17,22,0,4,6,11,10,15,18,20,0,3,6,4,23,12,15,14,18,4,2,9,5,2]', + ) except Exception as e: sys.exit(e) diff --git a/examples/vkimage_base64.py b/examples/vkimage_base64.py index 62e29e6..65806f8 100644 --- a/examples/vkimage_base64.py +++ b/examples/vkimage_base64.py @@ -1,6 +1,7 @@ import sys import os import requests +from base64 import b64encode sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) from twocaptcha import TwoCaptcha @@ -15,8 +16,11 @@ solver = TwoCaptcha(api_key) +with open('./images/vk.jpg', 'rb') as f: + b64 = b64encode(f.read()).decode('utf-8') + try: - result = solver.vkimage(files='./images/vk.jpg', + result = solver.vkimage(files=b64, steps='[5,4,7,7,14,22,8,3,2,7,23,22,2,8,24,5,9,20,2,5,0,6,22,4,5,11,12,12,9,6,18,3,21,18,17,' '7,6,1,4,19,8,11,3,14,20,6,16,11,23,0,10,14,10,9,24,3,14,14,10,0,15,10,6,6,20,12,18,13,' '20,7,13,9,22,14,24,14,17,22,0,4,6,11,10,15,18,20,0,3,6,4,23,12,15,14,18,4,2,9,5,2]', diff --git a/examples/vkimage_options.py b/examples/vkimage_options.py index d48e7f9..be35dda 100644 --- a/examples/vkimage_options.py +++ b/examples/vkimage_options.py @@ -13,11 +13,22 @@ api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') -solver = TwoCaptcha(api_key) +config = { + 'server': '2captcha.com', # can be also set to 'rucaptcha.com' + 'apiKey': api_key, + 'softId': 123, + 'defaultTimeout': 120, + 'recaptchaTimeout': 600, + 'pollingInterval': 10, + } + +solver = TwoCaptcha(**config) try: result = solver.vkimage(files='./images/vk.jpg', - steps='[5,4,7,7,14,22,8,3,2,7,23,22,2,8,24,5,9,20,2,5,0,6,22,4,5,11,12,12,9,6,18,3,21,18,17,7,6,1,4,19,8,11,3,14,20,6,16,11,23,0,10,14,10,9,24,3,14,14,10,0,15,10,6,6,20,12,18,13,20,7,13,9,22,14,24,14,17,22,0,4,6,11,10,15,18,20,0,3,6,4,23,12,15,14,18,4,2,9,5,2]', + steps='[5,4,7,7,14,22,8,3,2,7,23,22,2,8,24,5,9,20,2,5,0,6,22,4,5,11,12,12,9,6,18,3,21,18,17,' + '7,6,1,4,19,8,11,3,14,20,6,16,11,23,0,10,14,10,9,24,3,14,14,10,0,15,10,6,6,20,12,18,13,' + '20,7,13,9,22,14,24,14,17,22,0,4,6,11,10,15,18,20,0,3,6,4,23,12,15,14,18,4,2,9,5,2]', ) except Exception as e: diff --git a/setup.py b/setup.py index 7ba1dff..6ebd2b3 100644 --- a/setup.py +++ b/setup.py @@ -36,6 +36,6 @@ def get_version(): '2captcha', 'captcha', 'api', 'captcha solver', 'reCAPTCHA', 'FunCaptcha', 'Geetest', 'image captcha', 'Coordinates', 'Click Captcha', 'Geetest V4', 'Lemin captcha', 'Amazon WAF', 'Cloudflare Turnstile', - 'Capy Puzzle', 'MTCaptcha', 'Friendly Captcha', 'Tencent', 'Cutcaptcha', 'DataDome', 'cybersiara'], + 'Capy Puzzle', 'MTCaptcha', 'Friendly Captcha', 'Tencent', 'Cutcaptcha', 'DataDome', 'VK Captcha', 'cybersiara'], python_requires='>=3.6', test_suite='tests') diff --git a/twocaptcha/solver.py b/twocaptcha/solver.py index f8e3a8e..42141bf 100755 --- a/twocaptcha/solver.py +++ b/twocaptcha/solver.py @@ -930,6 +930,47 @@ def cutcaptcha(self, misery_key, apikey, url, **kwargs): **kwargs) return result + def vkimage(self, files, steps, **kwargs): + '''Wrapper for solving vkimage captcha. + + Parameters + __________ + file : str + Captcha image as a file or base64. + steps: str + Array of steps. + proxy : dict, optional + {'type': 'HTTPS', 'uri': 'login:password@IP_address:PORT'}. + ''' + + if isinstance(files, str): + + payload = self.get_method(files) + payload.pop('method', None) + + result = self.solve(method='vkimage', steps=steps, **payload, **kwargs) + return result + + elif isinstance(files, dict): + files = list(files.values()) + + files = self.extract_files(files) + + result = self.solve(method='vkimage', + files=files, + steps=steps, + **kwargs) + return result + + def vkcaptcha(self, redirect_uri, userAgent, proxytype, proxy): + + result = self.solve(misery_key=misery_key, + api_key=apikey, + url=url, + method='cutcaptcha', + **kwargs) + return result + def datadome(self, captcha_url, pageurl, userAgent, proxy, **kwargs): """Wrapper for solving DataDome Captcha. From dfed1768b0d44850375367d9bd03dd759a3a70f1 Mon Sep 17 00:00:00 2001 From: Maxim S Date: Tue, 28 Oct 2025 08:44:15 +0100 Subject: [PATCH 3/4] added vkcaptcha --- README.md | 21 +++++++++++++++- examples/captchafox.py | 28 +++++++++++++++++++++ examples/vkcaptcha.py | 29 ++++++++++++++++++++++ examples/vkcaptcha_options.py | 39 +++++++++++++++++++++++++++++ tests/test_vkcaptcha.py | 42 ++++++++++++++++++++++++++++++++ twocaptcha/solver.py | 46 +++++++++++++++++++++++++++++++---- 6 files changed, 199 insertions(+), 6 deletions(-) create mode 100644 examples/captchafox.py create mode 100644 examples/vkcaptcha.py create mode 100644 examples/vkcaptcha_options.py create mode 100755 tests/test_vkcaptcha.py diff --git a/README.md b/README.md index 120d4c0..6253177 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Examples of API requests for different captcha types are available on the [Pytho - [Tencent](#tencent) - [DataDome](#datadome) - [VKImage](#vkimage) + - [VKCaptcha](#vkcaptcha) - [CyberSiARA](#cybersiara) - [Other methods](#other-methods) - [send / get\_result](#send--get_result) @@ -440,16 +441,34 @@ result = solver.datadome(captcha_url="https://geo.captcha-delivery.com/captcha/? }, param1=..., ...) ``` + ### VKImage [API method description.](https://2captcha.com/2captcha-api#vkcaptcha) -This method can be used to solve VK captcha using graphical captcha. Returns the number of steps and solution value in the target site's API format. Or get an extended response by passing the `extendedResponse=True` attribute to the class instance +This method can be used to solve VK captcha using graphical captcha. Returns the number of steps and solution value in the target site's API format. ```python result = solver.vkimage('path/to/captcha.jpg', steps='[5,4,7,7,14,22,8,...]', ...) ``` +### VKCaptcha + +[API method description.](https://2captcha.com/2captcha-api#vkcaptcha) + +This method can be used to solve VK Captcha using a token. + +> [!IMPORTANT] +> To solve the VK Captcha, you must use a proxy. It is recommended to use [residential proxies]. + +```python +result = solver.vkcaptcha(redirect_uri='https://id.vk.ru/...', + userAgent='Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36..', + proxy={ + 'type': 'HTTP', + 'uri': 'login:password@IP_address:PORT'} + ) +``` ### CyberSiARA diff --git a/examples/captchafox.py b/examples/captchafox.py new file mode 100644 index 0000000..d1aa862 --- /dev/null +++ b/examples/captchafox.py @@ -0,0 +1,28 @@ +import sys +import os + +sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) + +from twocaptcha import TwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +solver = TwoCaptcha(api_key) + +try: + result = solver.mtcaptcha( + sitekey='MTPublic-KzqLY1cKH', + url='https://2captcha.com/demo/mtcaptcha', + ) + +except Exception as e: + sys.exit(e) + +else: + sys.exit('result: ' + str(result)) \ No newline at end of file diff --git a/examples/vkcaptcha.py b/examples/vkcaptcha.py new file mode 100644 index 0000000..9607918 --- /dev/null +++ b/examples/vkcaptcha.py @@ -0,0 +1,29 @@ +import sys +import os +sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) + +from twocaptcha import TwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +solver = TwoCaptcha(api_key) + +try: + result = solver.vkcaptcha(redirect_uri='https://id.vk.ru/not_robot_captcha?domain=vk.com&session_token=eyJhbGciOiJBMjU2R0NN....', + userAgent='Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.4348.100 Yandex/23.6.1.1107 Yowser/2.5 Safari/537.36', + proxy={'type': 'HTTPS', + 'uri': 'login:password@IP_address:PORT'} + ) + + +except Exception as e: + sys.exit(e) + +else: + sys.exit('result: ' + str(result)) diff --git a/examples/vkcaptcha_options.py b/examples/vkcaptcha_options.py new file mode 100644 index 0000000..f3cf8f3 --- /dev/null +++ b/examples/vkcaptcha_options.py @@ -0,0 +1,39 @@ +import sys +import os +sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) + +from twocaptcha import TwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +config = { + 'server': '2captcha.com', # can be also set to 'rucaptcha.com' + 'apiKey': api_key, + 'softId': 123, + 'defaultTimeout': 120, + 'recaptchaTimeout': 600, + 'pollingInterval': 10, + 'extendedResponse': True + } + +solver = TwoCaptcha(**config) + +try: + result = solver.vkcaptcha(redirect_uri='https://id.vk.ru/not_robot_captcha?domain=vk.com&session_token=eyJhbGciOiJBMjU2R0NN....', + userAgent='Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.4348.100 Yandex/23.6.1.1107 Yowser/2.5 Safari/537.36', + proxy={'type': 'HTTPS', + 'uri': 'login:password@IP_address:PORT'} + ) + + +except Exception as e: + sys.exit(e) + +else: + sys.exit('result: ' + str(result)) diff --git a/tests/test_vkcaptcha.py b/tests/test_vkcaptcha.py new file mode 100755 index 0000000..d3deda1 --- /dev/null +++ b/tests/test_vkcaptcha.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +import unittest + +try: + from .abstract import AbstractTest +except ImportError: + from abstract import AbstractTest + + + +class VkCaptcha(AbstractTest): + + + def test_all_params(self): + + + params = { + 'redirect_uri': 'https://id.vk.ru/not_robot_captcha?domain=vk.com&session_token=eyJhbGciOiJBMjU2R0NN...', + 'userAgent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.4348.100 Yandex/23.6.1.1107 Yowser/2.5 Safari/537.36', + 'proxy': {'type': 'HTTPS', + 'uri': 'login:password@IP_address:PORT'} + } + + sends = { + 'method' : 'vkcaptcha', + 'redirect_uri': 'https://id.vk.ru/not_robot_captcha?domain=vk.com&session_token=eyJhbGciOiJBMjU2R0NN...', + 'useragent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.4348.100 Yandex/23.6.1.1107 Yowser/2.5 Safari/537.36', + 'proxytype': 'HTTPS', + 'proxy': 'login:password@IP_address:PORT' + } + + return self.send_return(sends, self.solver.vkcaptcha, **params) + + + + + +if __name__ == '__main__': + + unittest.main() + diff --git a/twocaptcha/solver.py b/twocaptcha/solver.py index 42141bf..61ea91f 100755 --- a/twocaptcha/solver.py +++ b/twocaptcha/solver.py @@ -962,12 +962,48 @@ def vkimage(self, files, steps, **kwargs): **kwargs) return result - def vkcaptcha(self, redirect_uri, userAgent, proxytype, proxy): + def vkcaptcha(self, redirect_uri, userAgent, proxy, **kwargs): + '''Wrapper for solving VK captcha using tokens. - result = self.solve(misery_key=misery_key, - api_key=apikey, - url=url, - method='cutcaptcha', + Parameters + __________ + redirect_uri : str + The URL that is returned for requests to the captchas API. + userAgent : str + User-Agent of the browser that will be used by the employee when loading the captcha. + proxy : dict + {'type': 'HTTPS', 'uri': 'login:password@IP_address:PORT'}. + ''' + + + result = self.solve(method='vkcaptcha', + redirect_uri=redirect_uri, + useragent=userAgent, + proxy=proxy, + **kwargs) + return result + + def captchafox(self, sitekey, pageurl, userAgent, proxy, **kwargs): + '''Wrapper for solving VK captcha using tokens. + + Parameters + __________ + sitekey : str + The sitekey parameter value found on the page or in network requests. + pageurl : str + Full URL of the page with captcha. + userAgent : str + User-Agent of the browser that will be used by the employee when loading the captcha. + proxy : dict + {'type': 'HTTPS', 'uri': 'login:password@IP_address:PORT'}. + ''' + + + result = self.solve(method='captchafox', + sitekey=sitekey, + pageurl=pageurl, + useragent=userAgent, + proxy=proxy, **kwargs) return result From 59609c7b1b83c89e2d02156fae4027322f0a5a03 Mon Sep 17 00:00:00 2001 From: Maxim S Date: Wed, 29 Oct 2025 15:00:27 +0100 Subject: [PATCH 4/4] Added methods vkimage, vkcaptcha, prosopo, captchafox to solver.py and async_solver.py, added a description of new captchas to README.md, added tests for new captchas in sync and async, corrected error output in all async examples, corrected the python version and added new captchas to the description in setup.py Signed-off-by: Maxim S --- README.md | 30 +++++- examples/async/async_amazon_waf.py | 3 +- examples/async/async_amazon_waf_options.py | 3 +- examples/async/async_atb_captcha.py | 3 +- examples/async/async_atb_captcha_options.py | 3 +- examples/async/async_audio.py | 3 +- examples/async/async_canvas.py | 3 +- examples/async/async_canvas_base64.py | 3 +- examples/async/async_canvas_options.py | 3 +- examples/async/async_captchafox.py | 33 +++++++ examples/async/async_captchafox_options.py | 42 ++++++++ examples/async/async_capy.py | 3 +- examples/async/async_capy_options.py | 3 +- examples/async/async_coordinates.py | 3 +- examples/async/async_coordinates_base64.py | 3 +- examples/async/async_coordinates_options.py | 3 +- examples/async/async_cutcaptcha.py | 3 +- examples/async/async_cutcaptcha_options.py | 3 +- examples/async/async_cybersiara.py | 3 +- examples/async/async_datadome.py | 3 +- examples/async/async_friendly_captcha.py | 3 +- .../async/async_friendly_captcha_options.py | 3 +- examples/async/async_funcaptcha.py | 3 +- examples/async/async_funcaptcha_options.py | 3 +- examples/async/async_geetest.py | 3 +- examples/async/async_geetest_options.py | 3 +- examples/async/async_geetest_v4.py | 3 +- examples/async/async_geetest_v4_options.py | 3 +- examples/async/async_grid.py | 3 +- examples/async/async_grid_base64.py | 3 +- examples/async/async_grid_options.py | 3 +- examples/async/async_keycaptcha.py | 3 +- examples/async/async_keycaptcha_options.py | 3 +- examples/async/async_lemin.py | 3 +- examples/async/async_mtcaptcha.py | 3 +- examples/async/async_mtcaptcha_options.py | 3 +- examples/async/async_normal.py | 3 +- examples/async/async_normal_base64.py | 3 +- examples/async/async_normal_options.py | 3 +- examples/async/async_prosopo.py | 31 ++++++ examples/async/async_prosopo_options.py | 39 ++++++++ examples/async/async_recaptcha_v2.py | 3 +- examples/async/async_recaptcha_v2_options.py | 3 +- examples/async/async_recaptcha_v3.py | 3 +- examples/async/async_recaptcha_v3_options.py | 3 +- examples/async/async_rotate.py | 3 +- examples/async/async_rotate_options.py | 3 +- examples/async/async_tencent.py | 3 +- examples/async/async_tencent_options.py | 3 +- examples/async/async_text.py | 3 +- examples/async/async_text_options.py | 3 +- examples/async/async_turnstile.py | 3 +- examples/async/async_turnstile_options.py | 3 +- examples/async/async_vkcaptcha.py | 32 ++++++ examples/async/async_vkcaptcha_options.py | 42 ++++++++ examples/async/async_vkimage.py | 32 ++++++ examples/async/async_vkimage_base64.py | 32 ++++++ examples/async/async_vkimage_options.py | 42 ++++++++ examples/async/async_yandex_smart.py | 3 +- examples/async/async_yandex_smart_options.py | 3 +- examples/sync/captchafox.py | 31 ++++++ examples/sync/captchafox_options.py | 40 ++++++++ examples/{captchafox.py => sync/prosopo.py} | 6 +- examples/sync/prosopo_options.py | 41 ++++++++ examples/{ => sync}/vkcaptcha.py | 0 examples/{ => sync}/vkcaptcha_options.py | 0 examples/{ => sync}/vkimage.py | 0 examples/{ => sync}/vkimage_base64.py | 2 +- examples/{ => sync}/vkimage_options.py | 0 setup.py | 4 +- tests/async/test_async_captchafox.py | 34 +++++++ tests/async/test_async_prosopo.py | 28 ++++++ tests/async/test_async_vkcaptcha.py | 32 ++++++ tests/async/test_async_vkimage.py | 54 +++++++++++ tests/sync/test_captchafox.py | 44 +++++++++ tests/sync/test_prosopo.py | 38 ++++++++ tests/{ => sync}/test_vkcaptcha.py | 4 +- tests/sync/test_vkimage.py | 55 +++++++++++ tests/test_vkimage.py | 42 -------- twocaptcha/async_solver.py | 97 +++++++++++++++++++ twocaptcha/solver.py | 22 ++++- 81 files changed, 927 insertions(+), 152 deletions(-) create mode 100644 examples/async/async_captchafox.py create mode 100644 examples/async/async_captchafox_options.py create mode 100644 examples/async/async_prosopo.py create mode 100644 examples/async/async_prosopo_options.py create mode 100644 examples/async/async_vkcaptcha.py create mode 100644 examples/async/async_vkcaptcha_options.py create mode 100644 examples/async/async_vkimage.py create mode 100644 examples/async/async_vkimage_base64.py create mode 100644 examples/async/async_vkimage_options.py create mode 100644 examples/sync/captchafox.py create mode 100644 examples/sync/captchafox_options.py rename examples/{captchafox.py => sync/prosopo.py} (81%) create mode 100644 examples/sync/prosopo_options.py rename examples/{ => sync}/vkcaptcha.py (100%) rename examples/{ => sync}/vkcaptcha_options.py (100%) rename examples/{ => sync}/vkimage.py (100%) rename examples/{ => sync}/vkimage_base64.py (96%) rename examples/{ => sync}/vkimage_options.py (100%) create mode 100644 tests/async/test_async_captchafox.py create mode 100644 tests/async/test_async_prosopo.py create mode 100644 tests/async/test_async_vkcaptcha.py create mode 100644 tests/async/test_async_vkimage.py create mode 100755 tests/sync/test_captchafox.py create mode 100755 tests/sync/test_prosopo.py rename tests/{ => sync}/test_vkcaptcha.py (97%) create mode 100755 tests/sync/test_vkimage.py delete mode 100755 tests/test_vkimage.py diff --git a/README.md b/README.md index 6253177..27f830b 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,8 @@ Examples of API requests for different captcha types are available on the [Pytho - [DataDome](#datadome) - [VKImage](#vkimage) - [VKCaptcha](#vkcaptcha) + - [CaptchaFox](#captchafox) + - [Prosopo](#prosopo) - [CyberSiARA](#cybersiara) - [Other methods](#other-methods) - [send / get\_result](#send--get_result) @@ -456,7 +458,7 @@ result = solver.vkimage('path/to/captcha.jpg', steps='[5,4,7,7,14,22,8,...]', .. [API method description.](https://2captcha.com/2captcha-api#vkcaptcha) -This method can be used to solve VK Captcha using a token. +This method can be used to solve VK Captcha using a token. Returns a token. > [!IMPORTANT] > To solve the VK Captcha, you must use a proxy. It is recommended to use [residential proxies]. @@ -470,6 +472,32 @@ result = solver.vkcaptcha(redirect_uri='https://id.vk.ru/...', ) ``` +### CaptchaFox + +[API method description.](https://2captcha.com/2captcha-api#captchafox) + +This method can be used to solve CaptchaFox using a token. Returns a token. + +```python +result = solver.captchafox(sitekey='sk_ILKWNruBBVKDOM7dZs59KHnDLEWiH', + pageurl='https://mysite.com/page/with/captchafox', + userAgent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36', + proxy={'type': 'HTTPS', + 'uri': 'login:password@IP_address:PORT'}) +``` + +### Prosopo + +[API method description.](https://2captcha.com/2captcha-api#prosopo-procaptcha) + +This method can be used to solve Prosopo captcha using a token. Returns a token. + +```python +result = solver.prosopo(sitekey='5EZVvsHMrKCFKp5NYNoTyDjTjetoVo1Z4UNNb1DkVLS0JbqR', + pageurl='https://mysite.com/page/with/prosopo' + ) +``` + ### CyberSiARA [API method description.](https://2captcha.com/2captcha-api#cybersiara) diff --git a/examples/async/async_amazon_waf.py b/examples/async/async_amazon_waf.py index c80e33b..1d07d45 100644 --- a/examples/async/async_amazon_waf.py +++ b/examples/async/async_amazon_waf.py @@ -31,8 +31,7 @@ async def solve_captcha(): url='https://efw47fpad9.execute-api.us-east-1.amazonaws.com/latest', ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_amazon_waf_options.py b/examples/async/async_amazon_waf_options.py index affff4e..78a76da 100644 --- a/examples/async/async_amazon_waf_options.py +++ b/examples/async/async_amazon_waf_options.py @@ -48,8 +48,7 @@ async def solve_amazon_waf(): # } ) except Exception as e: - print(e) - raise e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_atb_captcha.py b/examples/async/async_atb_captcha.py index c4b4432..8b44854 100644 --- a/examples/async/async_atb_captcha.py +++ b/examples/async/async_atb_captcha.py @@ -25,8 +25,7 @@ async def solve_captcha(): url='http://mysite.com/', ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_atb_captcha_options.py b/examples/async/async_atb_captcha_options.py index d9163cb..c210cef 100644 --- a/examples/async/async_atb_captcha_options.py +++ b/examples/async/async_atb_captcha_options.py @@ -39,8 +39,7 @@ async def solve_captcha(): # } ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_audio.py b/examples/async/async_audio.py index 65b8395..d27fd0c 100644 --- a/examples/async/async_audio.py +++ b/examples/async/async_audio.py @@ -21,8 +21,7 @@ async def solve_captcha(): try: return await solver.audio('../audio/example.mp3', lang='en') except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_canvas.py b/examples/async/async_canvas.py index 3d62b01..ee7daa7 100644 --- a/examples/async/async_canvas.py +++ b/examples/async/async_canvas.py @@ -20,8 +20,7 @@ async def solve_captcha(): try: return await solver.canvas('../images/canvas.jpg', hintText='Draw around apple') except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_canvas_base64.py b/examples/async/async_canvas_base64.py index fc9f647..72e2d07 100644 --- a/examples/async/async_canvas_base64.py +++ b/examples/async/async_canvas_base64.py @@ -26,8 +26,7 @@ async def solve_captcha(): try: return await solver.canvas(b64, hintText='Draw around apple') except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_canvas_options.py b/examples/async/async_canvas_options.py index e0f329a..508b722 100644 --- a/examples/async/async_canvas_options.py +++ b/examples/async/async_canvas_options.py @@ -28,8 +28,7 @@ async def solve_captcha(): hintText='Draw around apple', ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_captchafox.py b/examples/async/async_captchafox.py new file mode 100644 index 0000000..ccd6af5 --- /dev/null +++ b/examples/async/async_captchafox.py @@ -0,0 +1,33 @@ +import asyncio +import sys +import os + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))) + +from twocaptcha import AsyncTwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +solver = AsyncTwoCaptcha(api_key) + +async def solve_captcha(): + try: + return await solver.captchafox( + sitekey='sk_ILKWNruBBVKDOM7dZs59KHnDLEWiH', + pageurl='https://mysite.com/page/with/captchafox', + userAgent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36', + proxy={'type': 'HTTPS', + 'uri': 'login:password@IP_address:PORT'} + ) + except Exception as e: + sys.exit(e) + +if __name__ == '__main__': + result = asyncio.run(solve_captcha()) + sys.exit('result: ' + str(result)) \ No newline at end of file diff --git a/examples/async/async_captchafox_options.py b/examples/async/async_captchafox_options.py new file mode 100644 index 0000000..fd7ea79 --- /dev/null +++ b/examples/async/async_captchafox_options.py @@ -0,0 +1,42 @@ +import asyncio +import sys +import os + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))) + +from twocaptcha import AsyncTwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +config = { + 'server': '2captcha.com', # can be also set to 'rucaptcha.com' + 'apiKey': api_key, + 'softId': 123, + 'defaultTimeout': 120, + 'recaptchaTimeout': 600, + 'pollingInterval': 10, + } + +solver = AsyncTwoCaptcha(**config) + +async def solve_captcha(): + try: + return await solver.captchafox( + sitekey='sk_ILKWNruBBVKDOM7dZs59KHnDLEWiH', + pageurl='https://mysite.com/page/with/captchafox', + userAgent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36', + proxy={'type': 'HTTPS', + 'uri': 'login:password@IP_address:PORT'} + ) + except Exception as e: + sys.exit(e) + +if __name__ == '__main__': + result = asyncio.run(solve_captcha()) + sys.exit('result: ' + str(result)) \ No newline at end of file diff --git a/examples/async/async_capy.py b/examples/async/async_capy.py index 8232dc4..737928b 100644 --- a/examples/async/async_capy.py +++ b/examples/async/async_capy.py @@ -25,8 +25,7 @@ async def solve_captcha(): api_server="https://jp.api.capy.me/", ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_capy_options.py b/examples/async/async_capy_options.py index 87d1cdc..422c37b 100644 --- a/examples/async/async_capy_options.py +++ b/examples/async/async_capy_options.py @@ -24,8 +24,7 @@ async def solve_captcha(): api_server="https://jp.api.capy.me/", softId=33112) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_coordinates.py b/examples/async/async_coordinates.py index 0deb48f..a3ce91a 100644 --- a/examples/async/async_coordinates.py +++ b/examples/async/async_coordinates.py @@ -21,8 +21,7 @@ async def solve_captcha(): try: return await solver.coordinates('../images/grid.jpg') except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_coordinates_base64.py b/examples/async/async_coordinates_base64.py index 32c5be7..b04fb02 100644 --- a/examples/async/async_coordinates_base64.py +++ b/examples/async/async_coordinates_base64.py @@ -27,8 +27,7 @@ async def solve_captcha(): try: return await solver.coordinates(b64) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_coordinates_options.py b/examples/async/async_coordinates_options.py index 06f8131..80f74c9 100644 --- a/examples/async/async_coordinates_options.py +++ b/examples/async/async_coordinates_options.py @@ -26,8 +26,7 @@ async def solve_captcha(): min_clicks=2, max_clicks=3) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_cutcaptcha.py b/examples/async/async_cutcaptcha.py index 1b3351a..35a28cb 100644 --- a/examples/async/async_cutcaptcha.py +++ b/examples/async/async_cutcaptcha.py @@ -25,8 +25,7 @@ async def solve_captcha(): url='https://mysite.com/page/with/cutcaptcha', ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_cutcaptcha_options.py b/examples/async/async_cutcaptcha_options.py index edfcd72..0a83a20 100644 --- a/examples/async/async_cutcaptcha_options.py +++ b/examples/async/async_cutcaptcha_options.py @@ -38,8 +38,7 @@ async def solve_captcha(): # } ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_cybersiara.py b/examples/async/async_cybersiara.py index 93b4c7c..8a59669 100644 --- a/examples/async/async_cybersiara.py +++ b/examples/async/async_cybersiara.py @@ -25,8 +25,7 @@ async def solve_captcha(): userAgent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36', ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_datadome.py b/examples/async/async_datadome.py index c1ff46f..9cc9af2 100644 --- a/examples/async/async_datadome.py +++ b/examples/async/async_datadome.py @@ -29,8 +29,7 @@ async def solve_captcha(): } ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_friendly_captcha.py b/examples/async/async_friendly_captcha.py index 9987380..a80c619 100644 --- a/examples/async/async_friendly_captcha.py +++ b/examples/async/async_friendly_captcha.py @@ -24,8 +24,7 @@ async def solve_captcha(): url='https://friendlycaptcha.com/demo', ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_friendly_captcha_options.py b/examples/async/async_friendly_captcha_options.py index 38e24d1..60c9cd6 100644 --- a/examples/async/async_friendly_captcha_options.py +++ b/examples/async/async_friendly_captcha_options.py @@ -38,8 +38,7 @@ async def solve_captcha(): # } ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_funcaptcha.py b/examples/async/async_funcaptcha.py index 844999a..75a2ccf 100644 --- a/examples/async/async_funcaptcha.py +++ b/examples/async/async_funcaptcha.py @@ -25,8 +25,7 @@ async def solve_captcha(): surl='https://client-api.arkoselabs.com' ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_funcaptcha_options.py b/examples/async/async_funcaptcha_options.py index 2af81a2..dcf9409 100644 --- a/examples/async/async_funcaptcha_options.py +++ b/examples/async/async_funcaptcha_options.py @@ -31,8 +31,7 @@ async def solve_captcha(): 'uri': 'login:password@123.123.123.123:8080' }) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_geetest.py b/examples/async/async_geetest.py index dd7a214..c6f9265 100644 --- a/examples/async/async_geetest.py +++ b/examples/async/async_geetest.py @@ -36,8 +36,7 @@ async def solve_captcha(): url='https://2captcha.com/demo/geetest' ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_geetest_options.py b/examples/async/async_geetest_options.py index ffb0d41..2bddae4 100644 --- a/examples/async/async_geetest_options.py +++ b/examples/async/async_geetest_options.py @@ -40,8 +40,7 @@ async def solve_captcha(): # } ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_geetest_v4.py b/examples/async/async_geetest_v4.py index 6f5857f..956c14c 100644 --- a/examples/async/async_geetest_v4.py +++ b/examples/async/async_geetest_v4.py @@ -24,8 +24,7 @@ async def solve_captcha(): url='https://2captcha.com/demo/geetest-v4' ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_geetest_v4_options.py b/examples/async/async_geetest_v4_options.py index 7a07b3c..6f9dc2b 100644 --- a/examples/async/async_geetest_v4_options.py +++ b/examples/async/async_geetest_v4_options.py @@ -38,8 +38,7 @@ async def solve_captcha(): # } ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_grid.py b/examples/async/async_grid.py index e41a43f..6edbd4b 100644 --- a/examples/async/async_grid.py +++ b/examples/async/async_grid.py @@ -26,8 +26,7 @@ async def solve_captcha(): cols=3 ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_grid_base64.py b/examples/async/async_grid_base64.py index 76f7c47..452e609 100644 --- a/examples/async/async_grid_base64.py +++ b/examples/async/async_grid_base64.py @@ -33,8 +33,7 @@ async def solve_captcha(): cols=3 ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_grid_options.py b/examples/async/async_grid_options.py index 8f3cf74..5daec79 100644 --- a/examples/async/async_grid_options.py +++ b/examples/async/async_grid_options.py @@ -30,8 +30,7 @@ async def solve_captcha(): # hintText='Select all images with an Orange', ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_keycaptcha.py b/examples/async/async_keycaptcha.py index 3854f75..11c72b9 100644 --- a/examples/async/async_keycaptcha.py +++ b/examples/async/async_keycaptcha.py @@ -27,8 +27,7 @@ async def solve_captcha(): url='https://2captcha.com/demo/keycaptcha' ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_keycaptcha_options.py b/examples/async/async_keycaptcha_options.py index 746b749..cd6dc11 100644 --- a/examples/async/async_keycaptcha_options.py +++ b/examples/async/async_keycaptcha_options.py @@ -39,8 +39,7 @@ async def solve_captcha(): # 'uri': 'login:password@IP_address:PORT'} ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_lemin.py b/examples/async/async_lemin.py index e119664..b39e3f8 100644 --- a/examples/async/async_lemin.py +++ b/examples/async/async_lemin.py @@ -25,8 +25,7 @@ async def solve_captcha(): url='https://2captcha.com/demo/lemin' ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_mtcaptcha.py b/examples/async/async_mtcaptcha.py index a704ffe..2f0aeff 100644 --- a/examples/async/async_mtcaptcha.py +++ b/examples/async/async_mtcaptcha.py @@ -24,8 +24,7 @@ async def solve_captcha(): url='https://2captcha.com/demo/mtcaptcha', ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_mtcaptcha_options.py b/examples/async/async_mtcaptcha_options.py index 83dc416..f2b6b77 100644 --- a/examples/async/async_mtcaptcha_options.py +++ b/examples/async/async_mtcaptcha_options.py @@ -38,8 +38,7 @@ async def solve_captcha(): # } ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_normal.py b/examples/async/async_normal.py index 3089252..a5997d4 100644 --- a/examples/async/async_normal.py +++ b/examples/async/async_normal.py @@ -21,8 +21,7 @@ async def solve_captcha(): try: return await solver.normal('../images/normal.jpg') except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_normal_base64.py b/examples/async/async_normal_base64.py index 2a03e82..5305338 100644 --- a/examples/async/async_normal_base64.py +++ b/examples/async/async_normal_base64.py @@ -27,8 +27,7 @@ async def solve_captcha(): try: return await solver.normal(b64) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_normal_options.py b/examples/async/async_normal_options.py index 89855ec..f6c5dd9 100644 --- a/examples/async/async_normal_options.py +++ b/examples/async/async_normal_options.py @@ -32,8 +32,7 @@ async def solve_captcha(): # hintText='Type red symbols only', ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_prosopo.py b/examples/async/async_prosopo.py new file mode 100644 index 0000000..0dc1ed6 --- /dev/null +++ b/examples/async/async_prosopo.py @@ -0,0 +1,31 @@ +import asyncio +import sys +import os + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))) + +from twocaptcha import AsyncTwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +solver = AsyncTwoCaptcha(api_key) + +async def solve_captcha(): + try: + return await solver.prosopo( + sitekey='5EZVvsHMrKCFKp5NYNoTyDjTjetoVo1Z4UNNb1DkVLS0JbqR', + pageurl='https://mysite.com/page/with/prosopo', + ) + + except Exception as e: + sys.exit(e) + +if __name__ == '__main__': + result = asyncio.run(solve_captcha()) + sys.exit('result: ' + str(result)) \ No newline at end of file diff --git a/examples/async/async_prosopo_options.py b/examples/async/async_prosopo_options.py new file mode 100644 index 0000000..bc677fd --- /dev/null +++ b/examples/async/async_prosopo_options.py @@ -0,0 +1,39 @@ +import asyncio +import sys +import os + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))) + +from twocaptcha import AsyncTwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +config = { + 'server': '2captcha.com', # can be also set to 'rucaptcha.com' + 'apiKey': api_key, + 'softId': 123, + 'defaultTimeout': 120, + 'recaptchaTimeout': 600, + 'pollingInterval': 10, + } + +solver = AsyncTwoCaptcha(**config) + +async def solve_captcha(): + try: + return await solver.prosopo( + sitekey='5EZVvsHMrKCFKp5NYNoTyDjTjetoVo1Z4UNNb1DkVLS0JbqR', + pageurl='https://mysite.com/page/with/prosopo', + ) + except Exception as e: + sys.exit(e) + +if __name__ == '__main__': + result = asyncio.run(solve_captcha()) + sys.exit('result: ' + str(result)) \ No newline at end of file diff --git a/examples/async/async_recaptcha_v2.py b/examples/async/async_recaptcha_v2.py index 3da9566..28e3599 100644 --- a/examples/async/async_recaptcha_v2.py +++ b/examples/async/async_recaptcha_v2.py @@ -23,8 +23,7 @@ async def solve_captcha(): sitekey='6LdO5_IbAAAAAAeVBL9TClS19NUTt5wswEb3Q7C5', url='https://2captcha.com/demo/recaptcha-v2-invisible') except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_recaptcha_v2_options.py b/examples/async/async_recaptcha_v2_options.py index 88cfeaa..eb1bc56 100644 --- a/examples/async/async_recaptcha_v2_options.py +++ b/examples/async/async_recaptcha_v2_options.py @@ -40,8 +40,7 @@ async def solve_captcha(): # } ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_recaptcha_v3.py b/examples/async/async_recaptcha_v3.py index 1705127..c0bb590 100644 --- a/examples/async/async_recaptcha_v3.py +++ b/examples/async/async_recaptcha_v3.py @@ -25,8 +25,7 @@ async def solve_captcha(): action='login', version='v3') except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_recaptcha_v3_options.py b/examples/async/async_recaptcha_v3_options.py index 0d5ba6b..2ddd70c 100644 --- a/examples/async/async_recaptcha_v3_options.py +++ b/examples/async/async_recaptcha_v3_options.py @@ -42,8 +42,7 @@ async def solve_captcha(): # } ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_rotate.py b/examples/async/async_rotate.py index cf90851..ac3075b 100644 --- a/examples/async/async_rotate.py +++ b/examples/async/async_rotate.py @@ -21,8 +21,7 @@ async def solve_captcha(): try: return await solver.rotate('../images/rotate.jpg') except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_rotate_options.py b/examples/async/async_rotate_options.py index 930489c..37d9340 100644 --- a/examples/async/async_rotate_options.py +++ b/examples/async/async_rotate_options.py @@ -26,8 +26,7 @@ async def solve_captcha(): # hintImg = '../images/rotate_hint.jpg' hintText='Put the images in the correct way up') except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_tencent.py b/examples/async/async_tencent.py index 20e7b3c..980a127 100644 --- a/examples/async/async_tencent.py +++ b/examples/async/async_tencent.py @@ -24,8 +24,7 @@ async def solve_captcha(): url="https://mysite.com/page/with/tencent" ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_tencent_options.py b/examples/async/async_tencent_options.py index 4c26b50..29c264f 100644 --- a/examples/async/async_tencent_options.py +++ b/examples/async/async_tencent_options.py @@ -38,8 +38,7 @@ async def solve_captcha(): # } ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_text.py b/examples/async/async_text.py index c1fcdfb..dc17a1e 100644 --- a/examples/async/async_text.py +++ b/examples/async/async_text.py @@ -21,8 +21,7 @@ async def solve_captcha(): try: return await solver.text('If tomorrow is Saturday, what day is today?') except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_text_options.py b/examples/async/async_text_options.py index 2bf0e01..375166d 100644 --- a/examples/async/async_text_options.py +++ b/examples/async/async_text_options.py @@ -22,8 +22,7 @@ async def solve_captcha(): return await solver.text('If tomorrow is Saturday, what day is today?', lang='en') except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_turnstile.py b/examples/async/async_turnstile.py index f303d67..a5f2465 100644 --- a/examples/async/async_turnstile.py +++ b/examples/async/async_turnstile.py @@ -24,8 +24,7 @@ async def solve_captcha(): url='https://2captcha.com/demo/cloudflare-turnstile', ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_turnstile_options.py b/examples/async/async_turnstile_options.py index 54fcc8b..59c4cfc 100644 --- a/examples/async/async_turnstile_options.py +++ b/examples/async/async_turnstile_options.py @@ -42,8 +42,7 @@ async def solve_captcha(): # } ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_vkcaptcha.py b/examples/async/async_vkcaptcha.py new file mode 100644 index 0000000..135758c --- /dev/null +++ b/examples/async/async_vkcaptcha.py @@ -0,0 +1,32 @@ +import asyncio +import sys +import os + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))) + +from twocaptcha import AsyncTwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +solver = AsyncTwoCaptcha(api_key) + +async def solve_captcha(): + try: + return await solver.vkcaptcha( + redirect_uri='https://id.vk.ru/not_robot_captcha?domain=vk.com&session_token=eyJhbGciOiJBMjU2R0NN....', + userAgent='Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.4348.100 Yandex/23.6.1.1107 Yowser/2.5 Safari/537.36', + proxy={'type': 'HTTPS', + 'uri': 'login:password@IP_address:PORT'} + ) + except Exception as e: + sys.exit(e) + +if __name__ == '__main__': + result = asyncio.run(solve_captcha()) + sys.exit('result: ' + str(result)) \ No newline at end of file diff --git a/examples/async/async_vkcaptcha_options.py b/examples/async/async_vkcaptcha_options.py new file mode 100644 index 0000000..8503510 --- /dev/null +++ b/examples/async/async_vkcaptcha_options.py @@ -0,0 +1,42 @@ +import asyncio +import sys +import os + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))) + +from twocaptcha import AsyncTwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +config = { + 'server': '2captcha.com', # can be also set to 'rucaptcha.com' + 'apiKey': api_key, + 'softId': 123, + 'defaultTimeout': 120, + 'recaptchaTimeout': 600, + 'pollingInterval': 10, + 'extendedResponse':True + } + +solver = AsyncTwoCaptcha(**config) + +async def solve_captcha(): + try: + return await solver.vkcaptcha( + redirect_uri='https://id.vk.ru/not_robot_captcha?domain=vk.com&session_token=eyJhbGciOiJBMjU2R0NN....', + userAgent='Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.4348.100 Yandex/23.6.1.1107 Yowser/2.5 Safari/537.36', + proxy={'type': 'HTTPS', + 'uri': 'login:password@IP_address:PORT'} + ) + except Exception as e: + sys.exit(e) + +if __name__ == '__main__': + result = asyncio.run(solve_captcha()) + sys.exit('result: ' + str(result)) \ No newline at end of file diff --git a/examples/async/async_vkimage.py b/examples/async/async_vkimage.py new file mode 100644 index 0000000..710c30a --- /dev/null +++ b/examples/async/async_vkimage.py @@ -0,0 +1,32 @@ +import asyncio +import sys +import os + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))) + +from twocaptcha import AsyncTwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +solver = AsyncTwoCaptcha(api_key) + +async def solve_captcha(): + try: + return await solver.vkimage( + files='../images/vk.jpg', + steps='[5,4,7,7,14,22,8,3,2,7,23,22,2,8,24,5,9,20,2,5,0,6,22,4,5,11,12,12,9,6,18,3,21,18,17,' + '7,6,1,4,19,8,11,3,14,20,6,16,11,23,0,10,14,10,9,24,3,14,14,10,0,15,10,6,6,20,12,18,13,' + '20,7,13,9,22,14,24,14,17,22,0,4,6,11,10,15,18,20,0,3,6,4,23,12,15,14,18,4,2,9,5,2]', + ) + except Exception as e: + sys.exit(e) + +if __name__ == '__main__': + result = asyncio.run(solve_captcha()) + sys.exit('result: ' + str(result)) \ No newline at end of file diff --git a/examples/async/async_vkimage_base64.py b/examples/async/async_vkimage_base64.py new file mode 100644 index 0000000..710c30a --- /dev/null +++ b/examples/async/async_vkimage_base64.py @@ -0,0 +1,32 @@ +import asyncio +import sys +import os + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))) + +from twocaptcha import AsyncTwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +solver = AsyncTwoCaptcha(api_key) + +async def solve_captcha(): + try: + return await solver.vkimage( + files='../images/vk.jpg', + steps='[5,4,7,7,14,22,8,3,2,7,23,22,2,8,24,5,9,20,2,5,0,6,22,4,5,11,12,12,9,6,18,3,21,18,17,' + '7,6,1,4,19,8,11,3,14,20,6,16,11,23,0,10,14,10,9,24,3,14,14,10,0,15,10,6,6,20,12,18,13,' + '20,7,13,9,22,14,24,14,17,22,0,4,6,11,10,15,18,20,0,3,6,4,23,12,15,14,18,4,2,9,5,2]', + ) + except Exception as e: + sys.exit(e) + +if __name__ == '__main__': + result = asyncio.run(solve_captcha()) + sys.exit('result: ' + str(result)) \ No newline at end of file diff --git a/examples/async/async_vkimage_options.py b/examples/async/async_vkimage_options.py new file mode 100644 index 0000000..dab7778 --- /dev/null +++ b/examples/async/async_vkimage_options.py @@ -0,0 +1,42 @@ +import asyncio +import sys +import os + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))) + +from twocaptcha import AsyncTwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +config = { + 'server': '2captcha.com', # can be also set to 'rucaptcha.com' + 'apiKey': api_key, + 'softId': 123, + 'defaultTimeout': 120, + 'recaptchaTimeout': 600, + 'pollingInterval': 10, + 'extendedResponse': True + } + +solver = AsyncTwoCaptcha(**config) + +async def solve_captcha(): + try: + return await solver.vkimage( + files='../images/vk.jpg', + steps='[5,4,7,7,14,22,8,3,2,7,23,22,2,8,24,5,9,20,2,5,0,6,22,4,5,11,12,12,9,6,18,3,21,18,17,' + '7,6,1,4,19,8,11,3,14,20,6,16,11,23,0,10,14,10,9,24,3,14,14,10,0,15,10,6,6,20,12,18,13,' + '20,7,13,9,22,14,24,14,17,22,0,4,6,11,10,15,18,20,0,3,6,4,23,12,15,14,18,4,2,9,5,2]', + ) + except Exception as e: + sys.exit(e) + +if __name__ == '__main__': + result = asyncio.run(solve_captcha()) + sys.exit('result: ' + str(result)) \ No newline at end of file diff --git a/examples/async/async_yandex_smart.py b/examples/async/async_yandex_smart.py index c3b2198..a8d574b 100644 --- a/examples/async/async_yandex_smart.py +++ b/examples/async/async_yandex_smart.py @@ -24,8 +24,7 @@ async def solve_captcha(): url="https://www.site.com/page/" ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/async/async_yandex_smart_options.py b/examples/async/async_yandex_smart_options.py index 5305ada..4e12b69 100644 --- a/examples/async/async_yandex_smart_options.py +++ b/examples/async/async_yandex_smart_options.py @@ -38,8 +38,7 @@ async def solve_captcha(): # } ) except Exception as e: - print(e) - return e + sys.exit(e) if __name__ == '__main__': diff --git a/examples/sync/captchafox.py b/examples/sync/captchafox.py new file mode 100644 index 0000000..d50bbca --- /dev/null +++ b/examples/sync/captchafox.py @@ -0,0 +1,31 @@ +import sys +import os + +sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) + +from twocaptcha import TwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +solver = TwoCaptcha(api_key) + +try: + result = solver.captchafox( + sitekey='sk_ILKWNruBBVKDOM7dZs59KHnDLEWiH', + pageurl='https://mysite.com/page/with/captchafox', + userAgent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36', + proxy={'type': 'HTTPS', + 'uri': 'login:password@IP_address:PORT'} + ) + +except Exception as e: + sys.exit(e) + +else: + sys.exit('result: ' + str(result)) \ No newline at end of file diff --git a/examples/sync/captchafox_options.py b/examples/sync/captchafox_options.py new file mode 100644 index 0000000..e7339a1 --- /dev/null +++ b/examples/sync/captchafox_options.py @@ -0,0 +1,40 @@ +import sys +import os + +sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) + +from twocaptcha import TwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +config = { + 'server': '2captcha.com', # can be also set to 'rucaptcha.com' + 'apiKey': api_key, + 'softId': 123, + 'defaultTimeout': 120, + 'recaptchaTimeout': 600, + 'pollingInterval': 10, + } + +solver = TwoCaptcha(**config) + +try: + result = solver.captchafox( + sitekey='sk_ILKWNruBBVKDOM7dZs59KHnDLEWiH', + pageurl='https://mysite.com/page/with/captchafox', + userAgent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36', + proxy={'type': 'HTTPS', + 'uri': 'login:password@IP_address:PORT'} + ) + +except Exception as e: + sys.exit(e) + +else: + sys.exit('result: ' + str(result)) \ No newline at end of file diff --git a/examples/captchafox.py b/examples/sync/prosopo.py similarity index 81% rename from examples/captchafox.py rename to examples/sync/prosopo.py index d1aa862..688134a 100644 --- a/examples/captchafox.py +++ b/examples/sync/prosopo.py @@ -16,9 +16,9 @@ solver = TwoCaptcha(api_key) try: - result = solver.mtcaptcha( - sitekey='MTPublic-KzqLY1cKH', - url='https://2captcha.com/demo/mtcaptcha', + result = solver.prosopo( + sitekey='5EZVvsHMrKCFKp5NYNoTyDjTjetoVo1Z4UNNb1DkVLS0JbqR', + pageurl='https://mysite.com/page/with/prosopo', ) except Exception as e: diff --git a/examples/sync/prosopo_options.py b/examples/sync/prosopo_options.py new file mode 100644 index 0000000..402aa82 --- /dev/null +++ b/examples/sync/prosopo_options.py @@ -0,0 +1,41 @@ +import sys +import os + +sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) + +from twocaptcha import TwoCaptcha + +# in this example we store the API key inside environment variables that can be set like: +# export APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Linux or macOS +# set APIKEY_2CAPTCHA=1abc234de56fab7c89012d34e56fa7b8 on Windows +# you can just set the API key directly to it's value like: +# api_key="1abc234de56fab7c89012d34e56fa7b8" + +api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY') + +config = { + 'server': '2captcha.com', # can be also set to 'rucaptcha.com' + 'apiKey': api_key, + 'softId': 123, + 'defaultTimeout': 120, + 'recaptchaTimeout': 600, + 'pollingInterval': 10, + } + +solver = TwoCaptcha(**config) + +try: + result = solver.prosopo( + sitekey='5EZVvsHMrKCFKp5NYNoTyDjTjetoVo1Z4UNNb1DkVLS0JbqR', + pageurl='https://mysite.com/page/with/prosopo', + # proxy={ + # 'type': 'HTTPS', + # 'uri': 'login:password@IP_address:PORT' + # } + ) + +except Exception as e: + sys.exit(e) + +else: + sys.exit('result: ' + str(result)) \ No newline at end of file diff --git a/examples/vkcaptcha.py b/examples/sync/vkcaptcha.py similarity index 100% rename from examples/vkcaptcha.py rename to examples/sync/vkcaptcha.py diff --git a/examples/vkcaptcha_options.py b/examples/sync/vkcaptcha_options.py similarity index 100% rename from examples/vkcaptcha_options.py rename to examples/sync/vkcaptcha_options.py diff --git a/examples/vkimage.py b/examples/sync/vkimage.py similarity index 100% rename from examples/vkimage.py rename to examples/sync/vkimage.py diff --git a/examples/vkimage_base64.py b/examples/sync/vkimage_base64.py similarity index 96% rename from examples/vkimage_base64.py rename to examples/sync/vkimage_base64.py index 65806f8..5643302 100644 --- a/examples/vkimage_base64.py +++ b/examples/sync/vkimage_base64.py @@ -16,7 +16,7 @@ solver = TwoCaptcha(api_key) -with open('./images/vk.jpg', 'rb') as f: +with open('../images/vk.jpg', 'rb') as f: b64 = b64encode(f.read()).decode('utf-8') try: diff --git a/examples/vkimage_options.py b/examples/sync/vkimage_options.py similarity index 100% rename from examples/vkimage_options.py rename to examples/sync/vkimage_options.py diff --git a/setup.py b/setup.py index 6ebd2b3..57912f8 100644 --- a/setup.py +++ b/setup.py @@ -36,6 +36,6 @@ def get_version(): '2captcha', 'captcha', 'api', 'captcha solver', 'reCAPTCHA', 'FunCaptcha', 'Geetest', 'image captcha', 'Coordinates', 'Click Captcha', 'Geetest V4', 'Lemin captcha', 'Amazon WAF', 'Cloudflare Turnstile', - 'Capy Puzzle', 'MTCaptcha', 'Friendly Captcha', 'Tencent', 'Cutcaptcha', 'DataDome', 'VK Captcha', 'cybersiara'], - python_requires='>=3.6', + 'Capy Puzzle', 'MTCaptcha', 'Friendly Captcha', 'Tencent', 'Cutcaptcha', 'DataDome', 'VK Captcha', 'CaptchaFox', 'Prosopo', 'cybersiara'], + python_requires='>=3.8', test_suite='tests') diff --git a/tests/async/test_async_captchafox.py b/tests/async/test_async_captchafox.py new file mode 100644 index 0000000..cb6a708 --- /dev/null +++ b/tests/async/test_async_captchafox.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +import unittest + +try: + from .abstract_async import AsyncAbstractTest +except ImportError: + from abstract_async import AsyncAbstractTest + + +class AsyncCaptchaFox(AsyncAbstractTest): + def test_all_params(self): + params = { + 'sitekey': 'sk_ILKWNruBBVKDOM7dZs50WPNUuCUKR', + 'pageurl': 'https://mysite.com/page/with/captchafox', + 'userAgent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36', + 'proxy': {'type': 'HTTPS', + 'uri': 'login:password@IP_address:PORT'} + } + + sends = { + 'method' : 'captchafox', + 'sitekey': 'sk_ILKWNruBBVKDOM7dZs50WPNUuCUKR', + 'pageurl': 'https://mysite.com/page/with/captchafox', + 'useragent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36', + 'proxytype': 'HTTPS', + 'proxy': 'login:password@IP_address:PORT' + } + + self.send_return(sends, self.solver.captchafox, **params) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/async/test_async_prosopo.py b/tests/async/test_async_prosopo.py new file mode 100644 index 0000000..5d5aa2f --- /dev/null +++ b/tests/async/test_async_prosopo.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 + +import unittest + +try: + from .abstract_async import AsyncAbstractTest +except ImportError: + from abstract_async import AsyncAbstractTest + + +class AsyncProsopo(AsyncAbstractTest): + def test_all_params(self): + params = { + 'sitekey': '5EZVvsHMrKCFKp5NYNoTyDjTjetoVo1Z4UNNbTwJf1GfN6Xm', + 'pageurl': 'https://www.twickets.live/', + } + + sends = { + 'method' : 'prosopo', + 'sitekey': '5EZVvsHMrKCFKp5NYNoTyDjTjetoVo1Z4UNNbTwJf1GfN6Xm', + 'pageurl': 'https://www.twickets.live/', + } + + self.send_return(sends, self.solver.prosopo, **params) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/async/test_async_vkcaptcha.py b/tests/async/test_async_vkcaptcha.py new file mode 100644 index 0000000..24e48b2 --- /dev/null +++ b/tests/async/test_async_vkcaptcha.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 + +import unittest + +try: + from .abstract_async import AsyncAbstractTest +except ImportError: + from abstract_async import AsyncAbstractTest + + +class AsyncVkCaptcha(AsyncAbstractTest): + def test_all_params(self): + params = { + 'redirect_uri': 'https://id.vk.ru/not_robot_captcha?domain=vk.com&session_token=eyJhbGciOiJBMjU2R0NN...', + 'userAgent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.4348.100 Yandex/23.6.1.1107 Yowser/2.5 Safari/537.36', + 'proxy': {'type': 'HTTPS', + 'uri': 'login:password@IP_address:PORT'} + } + + sends = { + 'method' : 'vkcaptcha', + 'redirect_uri': 'https://id.vk.ru/not_robot_captcha?domain=vk.com&session_token=eyJhbGciOiJBMjU2R0NN...', + 'useragent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.4348.100 Yandex/23.6.1.1107 Yowser/2.5 Safari/537.36', + 'proxytype': 'HTTPS', + 'proxy': 'login:password@IP_address:PORT' + } + + self.send_return(sends, self.solver.vkcaptcha, **params) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/async/test_async_vkimage.py b/tests/async/test_async_vkimage.py new file mode 100644 index 0000000..6315c06 --- /dev/null +++ b/tests/async/test_async_vkimage.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 + +import unittest +from pathlib import Path +from base64 import b64encode + +images_path = Path(__file__).resolve().parents[2] / 'examples' / 'images' +files = [str(images_path / 'vk.jpg')] +files_dict = {f'file_{i + 1}': path for i, path in enumerate(files)} + +STEPS = ('[5,4,7,7,14,22,8,3,2,7,23,22,2,8,24,5,9,20,2,5,0,6,22,4,5,11,12,12,9,6,18,3,21,18,17,7,6,' + '1,4,19,8,11,3,14,20,6,16,11,23,0,10,14,10,9,24,3,14,14,10,0,15,10,6,6,20,12,18,13,20,7,13,' + '9,22,14,24,14,17,22,0,4,6,11,10,15,18,20,0,3,6,4,23,12,15,14,18,4,2,9,5,2]') + +try: + from .abstract_async import AsyncAbstractTest +except ImportError: + from abstract_async import AsyncAbstractTest + + +class AsyncVkImage(AsyncAbstractTest): + def test_single_file(self): + sends = {'method': 'vkimage', 'file': files[0], 'steps': STEPS} + self.send_return(sends, self.solver.vkimage, files=files[0], steps=STEPS) + + def test_files_list(self): + sends = {'method': 'vkimage', 'files': files_dict, 'steps': STEPS} + self.send_return(sends, self.solver.vkimage, files=files, steps=STEPS) + + def test_files_dict(self): + sends = {'method': 'vkimage', 'files': files_dict, 'steps': STEPS} + self.send_return(sends, self.solver.vkimage, files=files_dict, steps=STEPS) + + def test_all_params(self): + params = {'files': files[0], 'steps': STEPS} + sends = {'method': 'vkimage', 'file': files[0], 'steps': STEPS} + self.send_return(sends, self.solver.vkimage, **params) + + def test_base64_body(self): + with open(files[0], 'rb') as fh: + body = b64encode(fh.read()).decode('utf-8') + params = {'files': body, 'steps': STEPS} + sends = {'method': 'vkimage', 'body': body, 'steps': STEPS} + self.send_return(sends, self.solver.vkimage, **params) + + def test_not_found(self): + self.invalid_file(self.solver.vkimage, steps=STEPS) + + def test_too_many(self): + self.too_many_files(self.solver.vkimage, steps=STEPS) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/sync/test_captchafox.py b/tests/sync/test_captchafox.py new file mode 100755 index 0000000..06f8665 --- /dev/null +++ b/tests/sync/test_captchafox.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +import unittest + +try: + from .abstract import AbstractTest +except ImportError: + from abstract import AbstractTest + + + +class CaptchaFox(AbstractTest): + + + def test_all_params(self): + + + params = { + 'sitekey': 'sk_ILKWNruBBVKDOM7dZs50WPNUuCUKR', + 'pageurl': 'https://mysite.com/page/with/captchafox', + 'userAgent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36', + 'proxy': {'type': 'HTTPS', + 'uri': 'login:password@IP_address:PORT'} + } + + sends = { + 'method' : 'captchafox', + 'sitekey': 'sk_ILKWNruBBVKDOM7dZs50WPNUuCUKR', + 'pageurl': 'https://mysite.com/page/with/captchafox', + 'useragent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36', + 'proxytype': 'HTTPS', + 'proxy': 'login:password@IP_address:PORT' + } + + return self.send_return(sends, self.solver.captchafox, **params) + + + + + +if __name__ == '__main__': + + unittest.main() + diff --git a/tests/sync/test_prosopo.py b/tests/sync/test_prosopo.py new file mode 100755 index 0000000..bf0ec83 --- /dev/null +++ b/tests/sync/test_prosopo.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +import unittest + +try: + from .abstract import AbstractTest +except ImportError: + from abstract import AbstractTest + + + +class Prosopo(AbstractTest): + + + def test_all_params(self): + + + params = { + 'sitekey': '5EZVvsHMrKCFKp5NYNoTyDjTjetoVo1Z4UNNbTwJf1GfN6Xm', + 'pageurl': 'https://www.twickets.live/', + } + + sends = { + 'method' : 'prosopo', + 'sitekey': '5EZVvsHMrKCFKp5NYNoTyDjTjetoVo1Z4UNNbTwJf1GfN6Xm', + 'pageurl': 'https://www.twickets.live/', + } + + return self.send_return(sends, self.solver.prosopo, **params) + + + + + +if __name__ == '__main__': + + unittest.main() + diff --git a/tests/test_vkcaptcha.py b/tests/sync/test_vkcaptcha.py similarity index 97% rename from tests/test_vkcaptcha.py rename to tests/sync/test_vkcaptcha.py index d3deda1..74a8358 100755 --- a/tests/test_vkcaptcha.py +++ b/tests/sync/test_vkcaptcha.py @@ -20,7 +20,7 @@ def test_all_params(self): 'userAgent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.4348.100 Yandex/23.6.1.1107 Yowser/2.5 Safari/537.36', 'proxy': {'type': 'HTTPS', 'uri': 'login:password@IP_address:PORT'} - } + } sends = { 'method' : 'vkcaptcha', @@ -28,7 +28,7 @@ def test_all_params(self): 'useragent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.4348.100 Yandex/23.6.1.1107 Yowser/2.5 Safari/537.36', 'proxytype': 'HTTPS', 'proxy': 'login:password@IP_address:PORT' - } + } return self.send_return(sends, self.solver.vkcaptcha, **params) diff --git a/tests/sync/test_vkimage.py b/tests/sync/test_vkimage.py new file mode 100755 index 0000000..9e95040 --- /dev/null +++ b/tests/sync/test_vkimage.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +import unittest +from pathlib import Path +from base64 import b64encode + +images_path = Path(__file__).resolve().parents[2] / 'examples' / 'images' +files = [str(images_path / 'vk.jpg')] +files_dict = {f'file_{i + 1}': path for i, path in enumerate(files)} + +STEPS = ('[5,4,7,7,14,22,8,3,2,7,23,22,2,8,24,5,9,20,2,5,0,6,22,4,5,11,12,12,9,6,18,3,21,18,17,7,6,' + '1,4,19,8,11,3,14,20,6,16,11,23,0,10,14,10,9,24,3,14,14,10,0,15,10,6,6,20,12,18,13,20,7,13,' + '9,22,14,24,14,17,22,0,4,6,11,10,15,18,20,0,3,6,4,23,12,15,14,18,4,2,9,5,2]') + +try: + from .abstract import AbstractTest +except ImportError: + from abstract import AbstractTest + + +class VkImage(AbstractTest): + + def test_single_file(self): + sends = {'method': 'vkimage', 'file': files[0], 'steps': STEPS} + return self.send_return(sends, self.solver.vkimage, files=files[0], steps=STEPS) + + def test_files_list(self): + sends = {'method': 'vkimage', 'files': files_dict, 'steps': STEPS} + return self.send_return(sends, self.solver.vkimage, files=files, steps=STEPS) + + def test_files_dict(self): + sends = {'method': 'vkimage', 'files': files_dict, 'steps': STEPS} + return self.send_return(sends, self.solver.vkimage, files=files_dict, steps=STEPS) + + def test_all_params(self): + params = {'files': files[0], 'steps': STEPS} + sends = {'method': 'vkimage', 'file': files[0], 'steps': STEPS} + return self.send_return(sends, self.solver.vkimage, **params) + + def test_base64_body(self): + with open(files[0], 'rb') as fh: + body = b64encode(fh.read()).decode('utf-8') + params = {'files': body, 'steps': STEPS} + sends = {'method': 'vkimage', 'body': body, 'steps': STEPS} + return self.send_return(sends, self.solver.vkimage, **params) + + def test_not_found(self): + return self.invalid_file(self.solver.vkimage, steps=STEPS) + + def test_too_many(self): + return self.too_many_files(self.solver.vkimage, steps=STEPS) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_vkimage.py b/tests/test_vkimage.py deleted file mode 100755 index 05abef2..0000000 --- a/tests/test_vkimage.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python3 - -import unittest - -try: - from .abstract import AbstractTest -except ImportError: - from abstract import AbstractTest - - - -class VkImage(AbstractTest): - - - def test_all_params(self): - - - params = { - 'files' : '../examples/images/vk.jpg', - 'steps' : '[5,4,7,7,14,22,8,3,2,7,23,22,2,8,24,5,9,20,2,5,0,6,22,4,5,11,12,12,9,6,18,3,21,18,17,7,6,' - '1,4,19,8,11,3,14,20,6,16,11,23,0,10,14,10,9,24,3,14,14,10,0,15,10,6,6,20,12,18,13,20,7,13,' - '9,22,14,24,14,17,22,0,4,6,11,10,15,18,20,0,3,6,4,23,12,15,14,18,4,2,9,5,2]', - } - - sends = { - 'method' : 'vkimage', - 'file' : '../examples/images/vk.jpg', - 'steps' : '[5,4,7,7,14,22,8,3,2,7,23,22,2,8,24,5,9,20,2,5,0,6,22,4,5,11,12,12,9,6,18,3,21,18,17,7,6,' - '1,4,19,8,11,3,14,20,6,16,11,23,0,10,14,10,9,24,3,14,14,10,0,15,10,6,6,20,12,18,13,20,7,13,' - '9,22,14,24,14,17,22,0,4,6,11,10,15,18,20,0,3,6,4,23,12,15,14,18,4,2,9,5,2]' - } - - return self.send_return(sends, self.solver.vkimage, **params) - - - - - -if __name__ == '__main__': - - unittest.main() - diff --git a/twocaptcha/async_solver.py b/twocaptcha/async_solver.py index d063fcc..1806f6e 100644 --- a/twocaptcha/async_solver.py +++ b/twocaptcha/async_solver.py @@ -800,6 +800,103 @@ async def cutcaptcha(self, misery_key, apikey, url, **kwargs): **kwargs) return result + async def vkimage(self, files, steps, **kwargs): + '''Wrapper for solving vkimage captcha. + + Parameters + __________ + file : str + Captcha image as a file or base64. + steps: str + Array of steps. + proxy : dict, optional + {'type': 'HTTPS', 'uri': 'login:password@IP_address:PORT'}. + ''' + + if isinstance(files, str): + + payload = await self.get_method(files) + payload.pop('method', None) + + result = await self.solve(method='vkimage', steps=steps, **payload, **kwargs) + return result + + elif isinstance(files, dict): + files = list(files.values()) + + files = self.extract_files(files) + + result = await self.solve(method='vkimage', + files=files, + steps=steps, + **kwargs) + return result + + async def vkcaptcha(self, redirect_uri, userAgent, proxy, **kwargs): + '''Wrapper for solving VK captcha using tokens. + + Parameters + __________ + redirect_uri : str + The URL that is returned for requests to the captchas API. + userAgent : str + User-Agent of the browser that will be used by the employee when loading the captcha. + proxy : dict + {'type': 'HTTPS', 'uri': 'login:password@IP_address:PORT'}. + ''' + + + result = await self.solve(method='vkcaptcha', + redirect_uri=redirect_uri, + useragent=userAgent, + proxy=proxy, + **kwargs) + return result + + async def captchafox(self, sitekey, pageurl, userAgent, proxy, **kwargs): + '''Wrapper for solving CaptchaFox using tokens. + + Parameters + __________ + sitekey : str + The sitekey parameter value found on the page or in network requests. + pageurl : str + Full URL of the page with captcha. + userAgent : str + User-Agent of the browser that will be used by the employee when loading the captcha. + proxy : dict + {'type': 'HTTPS', 'uri': 'login:password@IP_address:PORT'}. + ''' + + + result = await self.solve(method='captchafox', + sitekey=sitekey, + pageurl=pageurl, + useragent=userAgent, + proxy=proxy, + **kwargs) + return result + + async def prosopo(self, sitekey, pageurl, **kwargs): + '''Wrapper for solving Prosopo captcha using tokens. + + Parameters + __________ + sitekey : str + The sitekey parameter value found on the page or in network requests. + pageurl : str + Full URL of the page with captcha. + proxy : dict, optional + {'type': 'HTTPS', 'uri': 'login:password@IP_address:PORT'}. + ''' + + + result = await self.solve(method='prosopo', + sitekey=sitekey, + pageurl=pageurl, + **kwargs) + return result + async def datadome(self, captcha_url, pageurl, userAgent, proxy, **kwargs): """Wrapper for solving DataDome Captcha. diff --git a/twocaptcha/solver.py b/twocaptcha/solver.py index 61ea91f..d186f9d 100755 --- a/twocaptcha/solver.py +++ b/twocaptcha/solver.py @@ -984,7 +984,7 @@ def vkcaptcha(self, redirect_uri, userAgent, proxy, **kwargs): return result def captchafox(self, sitekey, pageurl, userAgent, proxy, **kwargs): - '''Wrapper for solving VK captcha using tokens. + '''Wrapper for solving CaptchaFox using tokens. Parameters __________ @@ -1007,6 +1007,26 @@ def captchafox(self, sitekey, pageurl, userAgent, proxy, **kwargs): **kwargs) return result + def prosopo(self, sitekey, pageurl, **kwargs): + '''Wrapper for solving Prosopo captcha using tokens. + + Parameters + __________ + sitekey : str + The sitekey parameter value found on the page or in network requests. + pageurl : str + Full URL of the page with captcha. + proxy : dict, optional + {'type': 'HTTPS', 'uri': 'login:password@IP_address:PORT'}. + ''' + + + result = self.solve(method='prosopo', + sitekey=sitekey, + pageurl=pageurl, + **kwargs) + return result + def datadome(self, captcha_url, pageurl, userAgent, proxy, **kwargs): """Wrapper for solving DataDome Captcha.