Repository: AndreiDrang/python3-anticaptcha (language: python)

Issue:
2.0



Discussion / hints:

Fix patch:
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..570c103
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+  - package-ecosystem: "pip" # See documentation for possible values
+    directory: "/" # Location of package manifests
+    schedule:
+      interval: "weekly"
diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml
new file mode 100644
index 0000000..98f464c
--- /dev/null
+++ b/.github/workflows/install.yml
@@ -0,0 +1,36 @@
+name: Installation
+
+on:
+  push:
+    branches: [ "main", "release"]
+    paths:
+      - '.github/workflows/install.yml'
+      - 'src/**'
+      - 'Makefile'
+  pull_request:
+    branches: [ "main", "release"]
+    paths:
+      - '.github/workflows/install.yml'
+      - 'src/**'
+      - 'Makefile'
+
+jobs:
+  install:
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        python-version: ["3.7", "3.8", "3.9", "3.10"]
+
+    steps:
+    - uses: actions/checkout@v3
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v3
+      with:
+        python-version: ${{ matrix.python-version }}
+
+    - name: Local install checking
+      run: make install
+
+    - name: PYPI install checking
+      run: pip install python3-anticaptcha
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 0000000..3b5d019
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,42 @@
+name: Lint
+
+on:
+  push:
+    branches: [ "main", "release"]
+    paths:
+      - '.github/workflows/lint.yml'
+      - 'src/**'
+      - 'Makefile'
+      - 'requirements.style.txt'
+  pull_request:
+    branches: [ "main", "release"]
+    paths:
+      - '.github/workflows/lint.yml'
+      - 'src/**'
+      - 'Makefile'
+      - 'requirements.style.txt'
+
+jobs:
+  lint:
+    runs-on: ubuntu-latest
+    env:
+      API_KEY: ${{ secrets.API_KEY }}
+    strategy:
+      fail-fast: false
+      matrix:
+        python-version: ["3.7", "3.8", "3.9", "3.10"]
+
+    steps:
+    - uses: actions/checkout@v3
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v3
+      with:
+        python-version: ${{ matrix.python-version }}
+
+    - name: Install dependencies
+      run: |
+        pip install --upgrade pip
+        pip install -r requirements.style.txt
+        
+    - name: Lint
+      run: make lint
diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml
new file mode 100644
index 0000000..9672b73
--- /dev/null
+++ b/.github/workflows/sphinx.yml
@@ -0,0 +1,32 @@
+name: Sphinx build
+
+on:
+  push:
+    branches: [ "release" ]
+    paths:
+      - '.github/workflows/sphinx.yml'
+      - 'src/**'
+      - 'docs/**'
+      - 'files/**'
+
+jobs:
+  docs:
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v3
+      - uses: actions/setup-python@v3
+
+      - name: Build library requirements
+        run: pip install -r src/requirements.txt
+
+      - name: Build docs
+        run: make doc
+
+      - name: Deploy
+        uses: peaceiris/actions-gh-pages@v3
+        with:
+          publish_branch: gh-pages
+          github_token: ${{ secrets.GITHUB_TOKEN }}
+          publish_dir: docs/_build/html/
+          force_orphan: true
diff --git a/.gitignore b/.gitignore
index a410834..c1c6f37 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
 # Created by .ignore support plugin (hsz.mobi)
 .idea/.name
 .idea/PythonAntiCaptcha.iml
-python3_anticaptcha/__pycache__/
+src/python3_anticaptcha/__pycache__/
 /.idea/
 /python3_anticaptcha.egg-info/
 
@@ -11,11 +11,19 @@ build/
 
 __pycache__/
 
-\.coverage
+src/.coverage
 
 *.xml
 
 dist/
 
+venv/
 env/
-venv/
\ No newline at end of file
+/.coverage
+/src/python3_captchaai.egg-info/
+/src/.coverage
+/src/coverage/lcov.info
+/src/htmlcov/
+/src/coverage/
+/docs/_build/
+/src/python3_anticaptcha.egg-info/
diff --git a/.isort.cfg b/.isort.cfg
deleted file mode 100644
index 14af6a2..0000000
--- a/.isort.cfg
+++ /dev/null
@@ -1,8 +0,0 @@
-[settings]
-length_sort = True
-multi_line_output = 3
-include_trailing_comma = True
-force_grid_wrap = 0
-use_parentheses = True
-skip_glob = anticaptcha_examples/**, tests/**
-line_length=99
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 1f41c34..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-# multiple operating systems
-os:
-  - linux
-# clone depth
-git:
-  depth: 3
-
-  # Set the build language to Python
-language: python
-
-# Set the python version to 3.(6,7,8)
-python: 
-  - "3.6"
-  - "3.7"
-  - "3.8"
-
-# Install the codecov pip dependency
-install:
-  - python setup.py install
-
-# Run the unit test
-script:
-  - make test
diff --git a/Makefile b/Makefile
index 39ad4e8..52f8ae2 100644
--- a/Makefile
+++ b/Makefile
@@ -1,19 +1,42 @@
 install:
-	python setup.py install
+	cd src/ && pip install -e .
 
-remove:
-	pip uninstall python3-anticaptcha -y
-
-test:
-	pip install pytest coverage pytest-asyncio requests_mock
-	coverage run -m pytest tests -v --disable-warnings
-	coverage report -m python3_anticaptcha/*.py
+tests:
+	cd src/ && \
+	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/ && \
+	coverage xml -o coverage/coverage.xml
 
 refactor:
-	pip install black isort
-	black python3_anticaptcha/
-	isort -rc python3_anticaptcha/
+	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/
+
+lint:
+	cd src/ && \
+	autoflake --in-place --recursive python3_anticaptcha/ --check && \
+	black python3_anticaptcha/ --check && \
+	isort python3_anticaptcha/ --check-only
 
 release:
-	pip install wheel twine
+	pip install twine
 	python setup.py upload
+
+upload:
+	pip install twine
+	cd src/ && python setup.py upload
+
+doc:
+	cd docs/ && \
+	make html -e
diff --git a/README.md b/README.md
index 7269cbc..e547d1d 100644
--- a/README.md
+++ b/README.md
@@ -8,229 +8,33 @@
 
 [![Code Climate](https://codeclimate.com/github/AndreiDrang/python3-anticaptcha/badges/gpa.svg)](https://codeclimate.com/github/AndreiDrang/python3-anticaptcha)
 [![Codacy Badge](https://api.codacy.com/project/badge/Grade/2daabf7ff7974f01b9348fe64483c7af)](https://app.codacy.com/app/drang.andray/python3-anticaptcha?utm_source=github.com&utm_medium=referral&utm_content=AndreiDrang/python3-anticaptcha&utm_campaign=Badge_Grade_Settings)
+[![codecov](https://codecov.io/gh/AndreiDrang/python3-anticaptcha/branch/master/graph/badge.svg?token=W92nfZY6Tz)](https://codecov.io/gh/AndreiDrang/python3-anticaptcha)
 
+[![Build check](https://github.com/AndreiDrang/python3-anticaptcha/actions/workflows/test_build.yml/badge.svg?branch=main)](https://github.com/AndreiDrang/python3-anticaptcha/actions/workflows/test_build.yml)
+[![Installation check](https://github.com/AndreiDrang/python3-anticaptcha/actions/workflows/install.yml/badge.svg?branch=main)](https://github.com/AndreiDrang/python3-anticaptcha/actions/workflows/install.yml)
+[![Test](https://github.com/AndreiDrang/python3-anticaptcha/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/AndreiDrang/python3-anticaptcha/actions/workflows/test.yml)
+[![Lint](https://github.com/AndreiDrang/python3-anticaptcha/actions/workflows/lint.yml/badge.svg?branch=main)](https://github.com/AndreiDrang/python3-anticaptcha/actions/workflows/lint.yml)
 
-Python3 library for [Anti-Captcha](https://anti-captcha.com/mainpage) service.
+
+Python 3 library for [AntiCaptcha](https://anti-captcha.com/) service API.
 
 Tested on UNIX based OS.
 
-The library is intended for software developers and is used to work with the [Anti-Captcha](https://anti-captcha.com/mainpage) service API.
+The library is intended for software developers and is used to work with the [AntiCaptcha](https://anti-captcha.com/) service API.
 
 ***
-Application in [AppCenter](https://anti-captcha.com/clients/tools/appcenter/app/867).
 
 If you have any questions, please send a message to the [Telegram](https://t.me/pythoncaptcha) chat room.
 
 Or email python-captcha@pm.me
-***
-
-## How to install? Как установить?
-
-### pip
-
-```bash
-pip install python3-anticaptcha
-```
-
-
-### Source
-```bash
-git clone https://github.com/AndreiDrang/python3-anticaptcha.git
-cd python3-anticaptcha
-python setup.py install
-```
-Присутствуют [примеры работы с библиотекой](./anticaptcha_examples).
-
-Full examples you can find [here](./anticaptcha_examples).
 
 ***
-### At the moment the following methods are implemented:
-### На данный момент реализованы следующие методы:
-
-##### 0.[Manual result handler.](./anticaptcha_examples/custom_result_handler_example.py)
-
- 
-```python
-from python3_anticaptcha import CustomResultHandler
-# Enter the key to the AntiCaptcha service from your account. Anticaptcha service key.
-ANTICAPTCHA_KEY = "your_key"
-# Task ID to get result
-TASK_ID = 123456
-# This module is used to obtain the result of solving the task in "manual" mode
-custom_result = CustomResultHandler.CustomResultHandler(
-    anticaptcha_key=ANTICAPTCHA_KEY
-)
-
-user_answer = custom_result.task_handler(task_id=TASK_ID)
-print(user_answer)
-```
-
-##### 1. [Image to text captcha.](./anticaptcha_examples/anticaptcah_image_to_text_example.py)
-
- 
-```python
-from python3_anticaptcha import ImageToTextTask
-# Enter the key to the AntiCaptcha service from your account. Anticaptcha service key.
-ANTICAPTCHA_KEY = ""
-# Link to captcha image.
-image_link = "https://pythoncaptcha.tech/static/image/common_image_example/800070.png"
-# Get string for solve captcha, and some other info.
-user_answer = ImageToTextTask.ImageToTextTask(anticaptcha_key = ANTICAPTCHA_KEY).\
-                captcha_handler(captcha_link=image_link)
-
-print(user_answer)
-```
-
-##### 2. [ReCaptcha v2.](./anticaptcha_examples/anticaptcha_nocaptcha_example.py)
-
-
-##### 3. [ReCaptcha v2 Proxyless. ](./anticaptcha_examples/anticaptcha_nocaptcha_example.py) + [Selenium example](./anticaptcha_examples/selenium_recaptcha_v2.py)
-
-```python
-from python3_anticaptcha import NoCaptchaTaskProxyless
-# Enter the key to the AntiCaptcha service from your account. Anticaptcha service key.
-ANTICAPTCHA_KEY = ""
-# G-ReCaptcha ключ сайта. Website google key.
-SITE_KEY = '6LeuMjIUAAAAAODtAglF13UiJys0y05EjZugej6b'
-# Page url.
-PAGE_URL = 'https://www.google.com/recaptcha/intro/android.html'
-# Get string for solve captcha, and other info.
-user_answer = NoCaptchaTaskProxyless.NoCaptchaTaskProxyless(anticaptcha_key = ANTICAPTCHA_KEY)\
-                .captcha_handler(websiteURL=PAGE_URL,
-                                 websiteKey=SITE_KEY)
-
-print(user_answer)
-```
-
-##### 4. [ReCaptcha v3 Proxyless. ](./anticaptcha_examples/anticaptcha_nocaptcha_example.py)
 
-```python
-from python3_anticaptcha import ReCaptchaV3TaskProxyless
-# Enter the key to the AntiCaptcha service from your account. Anticaptcha service key.
-ANTICAPTCHA_KEY = ""
-# G-ReCaptcha - website google key.
-SITE_KEY = '6LeuMjIUAAAAAODtAglF13UiJys0y05EjZugej6b'
-# Page url.
-PAGE_URL = 'https://some_link'
-# The filter by which the employee with the required minimum score is selected.
-# possible options - 0.3, 0.5, 0.7
-MIN_SCORE=0.3
-# The value of the `action` parameter, which is passed by the recaptcha widget to google.
-PAGE_ACTION='login'
-# Get string for solve captcha, and other info.
-user_answer = ReCaptchaV3TaskProxyless.ReCaptchaV3TaskProxyless(anticaptcha_key = ANTICAPTCHA_KEY)\
-                .captcha_handler(websiteURL=PAGE_URL,
-                                 websiteKey=SITE_KEY,
-                                 minScore=MIN_SCORE,
-                                 pageAction=PAGE_ACTION
-                                )
+### Additional info
+1. [Library usage examples && Docs](https://andreidrang.github.io/python3-captchaai/)
+2. [Capsolver errors list](https://captchaai.atlassian.net/wiki/spaces/CAPTCHAAI/pages/426145/)
 
-print(user_answer)
-```
 
-##### 5. [Fun Captcha.](./anticaptcha_examples/anticaptcha_fun_example.py)
-
-##### 6. [Fun Captcha Proxyless.](./anticaptcha_examples/anticaptcha_fun_example.py)
-
-```python
-from python3_anticaptcha import FunCaptchaTask
-# Enter the key to the AntiCaptcha service from your account. Anticaptcha service key.
-ANTICAPTCHA_KEY = ""
-# G-ReCaptcha site key
-SITE_KEY = ''
-# Link to the page with captcha
-PAGE_URL = ''
-# Get full data for solve captcha.
-user_answer = FunCaptchaTask.FunCaptchaTask(anticaptcha_key=ANTICAPTCHA_KEY,
-                                            proxyType="http",
-                                            proxyAddress="8.8.8.8",
-                                            proxyPort=8080)\
-                .captcha_handler(websiteURL=PAGE_URL,
-                                 websitePublicKey=SITE_KEY)
-
-print(user_answer)
-```
-
-##### 7. [Account management module.](./anticaptcha_examples/anticaptcha_control_example.py)
-
-```python
-from python3_anticaptcha import AntiCaptchaControl
-# Enter the key to the AntiCaptcha service from your account. Anticaptcha service key.
-ANTICAPTCHA_KEY = ""
-# Balance info
-result = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key = ANTICAPTCHA_KEY).get_balance()
-# Submitting a complaint about incorrectly resolved captcha images
-result = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key = ANTICAPTCHA_KEY).complaint_on_result(
-    reported_id=543212, captcha_type="image"
-)
-# Submitting a complaint about incorrectly resolved ReCaptcha
-result = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key = ANTICAPTCHA_KEY).complaint_on_result(
-    reported_id=5432134, captcha_type="recaptcha"
-)
-# Giving information about loading the queue, depending on the queue ID
-result = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key = ANTICAPTCHA_KEY).get_queue_status(queue_id=1)
-```
-
-##### 8. [Custom Captcha.](./anticaptcha_examples/anticaptcha_customcaptcha_example.py)
-
-```python
-from python3_anticaptcha import CustomCaptchaTask
-# Enter the key to the AntiCaptcha service from your account. Anticaptcha service key.
-ANTICAPTCHA_KEY = ""
-# ссылка на изображение
-imageUrl = "https://files.anti-captcha.com/26/41f/c23/7c50ff19.jpg"
-# минимальный пример использования модуля
-my_custom_task = CustomCaptchaTask.CustomCaptchaTask(anticaptcha_key=ANTICAPTCHA_KEY).\
-                    captcha_handler(imageUrl=imageUrl)
-print(my_custom_task)
-```
-
-##### 9. [Gee Test.](./anticaptcha_examples/gee_example.py)
-
-##### 10. [Gee Test Proxyless.](./anticaptcha_examples/gee_example.py)
-
-```python
-from python3_anticaptcha import GeeTestTaskProxyless
-# Enter the key to the AntiCaptcha service from your account. Anticaptcha service key.
-ANTICAPTCHA_KEY = ""
-# обязательные параметры
-websiteURL = "http:\/\/mywebsite.com\/geetest\/test.php"
-gt = "874703612e5cac182812a00e273aad0d"
-challenge = "a559b82bca2c500101a1c8a4f4204742"
-# пример работы с GeeTestTask без прокси
-result = GeeTestTaskProxyless.GeeTestTaskProxyless(anticaptcha_key=ANTICAPTCHA_KEY,
-                                                   websiteURL=websiteURL,
-                                                   gt=gt).\
-            captcha_handler(challenge=challenge)
-
-print(result)
-```
-
-##### 11. [HCaptcha.](./anticaptcha_examples/anticaptcha_hcaptcha_example.py)
-
-##### 12. [HCaptcha Proxyless.](./anticaptcha_examples/anticaptcha_hcaptcha_example.py)
-
-```python
-from python3_anticaptcha import HCaptchaTaskProxyless
-# Enter the key to the AntiCaptcha service from your account. Anticaptcha service key.
-ANTICAPTCHA_KEY = ""
-WEB_URL = "https://dashboard.hcaptcha.com/signup"
-SITE_KEY = "00000000-0000-0000-0000-000000000000"
-
-result = HCaptchaTaskProxyless.HCaptchaTaskProxyless(anticaptcha_key=ANTICAPTCHA_KEY).\
-            captcha_handler(websiteURL=WEB_URL, websiteKey=SITE_KEY)
-
-print(result)
-```
-***
-Кроме того, для тестирования различных типов капчи предоставляется [специальный сайт](https://pythoncaptcha.tech/), на котором собраны все имеющиеся типы капчи, с удобной системой тестирования ваших скриптов.
-
-Some examples you can test with our [web-site](https://pythoncaptcha.tech/).
-
-***
-#### For tests:
-1. Clone repository
-2. ```bash
-    export anticaptcha_key=SERVICE_KEY
-    make test
-    ```
+### Get API Key to work with the library
+1. On the page - https://dashboard.capsolver.com/overview/user-center
+2. Find it: ![img.png](files/img.png)
diff --git a/_config.yml b/_config.yml
deleted file mode 100644
index b849713..0000000
--- a/_config.yml
+++ /dev/null
@@ -1,1 +0,0 @@
-theme: jekyll-theme-leap-day
\ No newline at end of file
diff --git a/anticaptcha_examples/088636.png b/anticaptcha_examples/088636.png
deleted file mode 100644
index 870367f..0000000
Binary files a/anticaptcha_examples/088636.png and /dev/null differ
diff --git a/anticaptcha_examples/anticaptcah_image_to_text_example.py b/anticaptcha_examples/anticaptcah_image_to_text_example.py
deleted file mode 100644
index 118118a..0000000
--- a/anticaptcha_examples/anticaptcah_image_to_text_example.py
+++ /dev/null
@@ -1,167 +0,0 @@
-import asyncio
-import base64
-import requests
-
-from python3_anticaptcha import ImageToTextTask, CallbackClient
-from python3_anticaptcha import errors
-
-ANTICAPTCHA_KEY = ""
-"""
-Данная библиотека реализует два различных метода для сохранения изображений(для последующей их передачи сервису):
-1. В качестве временного файла, параметр задаётся по умолчанию: 
-save_format = 'temp' .
-2. В качестве обычного файла, для этого нужно передать:
-save_format = 'const' . 
-========================================================================================================================
-This library implements two different methods for saving images (for their subsequent transfer to the service):
-1. As a temporary file, the parameter is set by default: 
-save_format = 'temp'.
-2. As a normal file, you need to pass this:
-save_format = 'const' .
-"""
-# Пример который показывает работу антикапчи при решении капчи-изображением и сохранением её в качестве обычного файла в
-# папку.
-# Example for working with captcha-image link, and save it like a usual file in system.
-
-result = ImageToTextTask.ImageToTextTask(
-    anticaptcha_key=ANTICAPTCHA_KEY, save_format="const"
-).captcha_handler(captcha_link="https://pythoncaptcha.tech/static/image/common_image_example/800070.png")
-print(result)
-
-# Пример который показывает работу антикапчи при решении капчи-изображением и сохранением её в качестве ВРЕМЕННОГО файла
-# Протестировано на Линуксах. Не используйте данный вариант на Windows! Возможно починим, но потом.
-# Example for working with captcha-image like a temporary file. Tested on UNIX-based systems. Don`t use it on Windows!
-result = ImageToTextTask.ImageToTextTask(anticaptcha_key=ANTICAPTCHA_KEY).captcha_handler(
-    captcha_link="https://pythoncaptcha.tech/static/image/common_image_example/800070.png"
-)
-print(result)
-
-"""
-========================================================================================================================
-Base64 files
-Пример работы с декодированием в base64 файла-капчи "налету"
-An example of working with decoding in base64 captcha-file after download. On-the-fly-encoding
-"""
-base_64_link = base64.b64encode(
-    requests.get("https://pythoncaptcha.tech/static/image/common_image_example/862963.png").content
-).decode("utf-8")
-
-user_answer_base64 = ImageToTextTask.ImageToTextTask(anticaptcha_key=ANTICAPTCHA_KEY).captcha_handler(
-    captcha_base64=base_64_link
-)
-
-"""
-Пример работы со скачанными файлами изображений капчи.
-Example for working with downloaded file.
-"""
-# Синхронный
-# папка в которой находится изображение, один из вариантов написания
-# The path to the file must be transferred in this format to the method
-captcha_file = "088636.png"
-# так же есть возможность передать так:
-# or in this format:
-# captcha_file = 'D:\/Python\/933588.png'
-# captcha_file = r'D:\Python\933588.png'
-
-result = ImageToTextTask.ImageToTextTask(anticaptcha_key=ANTICAPTCHA_KEY).captcha_handler(
-    captcha_file=captcha_file
-)
-print(result)
-"""
-Пример для работы с локальными файлами
-"""
-# папка в которой находится изображение, один из вариантов написания
-
-# так же есть возможность передать так:
-# captcha_file = r'D:\Python\933588.png'
-# captcha_file = 'D:\/Python\/933588.png'
-try:
-    user_answer_local = ImageToTextTask.ImageToTextTask(anticaptcha_key=ANTICAPTCHA_KEY).captcha_handler(
-        captcha_file=captcha_file
-    )
-    if user_answer_local["errorId"] == 0:
-        # решение капчи
-        print(user_answer_local["solution"]["text"])
-    elif user_answer_local["errorId"] == 1:
-        # Тело ошибки, если есть
-        print(user_answer_local["errorBody"])
-
-# отлов ошибки при проблемах чтения файла-изображения
-except errors.ReadError as err:
-    print(err)
-
-"""
-========================================================================================================================
-Asynchronous examples
-Пример асинхронного запуска решения капчи
-# AsyncIO example. Work with constant-saved file.
-"""
-
-
-async def run():
-    try:
-        resolve = await ImageToTextTask.aioImageToTextTask(
-            anticaptcha_key=ANTICAPTCHA_KEY, save_format="const"
-        ).captcha_handler(
-            captcha_link="https://pythoncaptcha.tech/static/image/common_image_example/800070.png"
-        )
-
-        print(resolve)
-
-        resolve = await ImageToTextTask.aioImageToTextTask(anticaptcha_key=ANTICAPTCHA_KEY).captcha_handler(
-            captcha_file=captcha_file
-        )
-
-        print(resolve)
-
-        resolve = await ImageToTextTask.aioImageToTextTask(anticaptcha_key=ANTICAPTCHA_KEY).captcha_handler(
-            captcha_base64=base_64_link
-        )
-
-        print(resolve)
-
-    except Exception as err:
-        print(err)
-
-
-if __name__ == "__main__":
-    loop = asyncio.get_event_loop()
-    loop.run_until_complete(run())
-    loop.close()
-
-"""
-Callback example
-"""
-QUEUE_KEY = "wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ_anticaptcha_queue"
-
-"""
-Перед тем как начать пользоваться сервисом нужно создать для своей задачи отдельную очередь
-Очередь можно создать один раз и пользоваться постоянно
-
-Для создания очереди нужно передать два параметра:
-1. key - название очереди, чем оно сложнее тем лучше
-2. vhost - название виртуального хоста(в данном случаи - `anticaptcha_vhost`)
-"""
-
-answer = requests.post(
-    "https://pythoncaptcha.tech:8001/register_key",
-    json={"key": QUEUE_KEY, "vhost": "anticaptcha_vhost"},
-)
-# если очередь успешно создана:
-if answer == "OK":
-
-    # создаём задание с callbackURL параметром
-    result = ImageToTextTask.ImageToTextTask(
-        anticaptcha_key=ANTICAPTCHA_KEY,
-        callbackUrl=f"https://pythoncaptcha.tech:8001/anticaptcha/image_to_text/{QUEUE_KEY}",
-    ).captcha_handler(captcha_link="https://pythoncaptcha.tech/static/image/common_image_example/800070.png")
-    print(result)
-
-    # получение результата из кеша
-    print(CallbackClient.CallbackClient(task_id=result["taskId"]).captcha_handler())
-    # получение результата из RabbitMQ очереди
-    print(
-        CallbackClient.CallbackClient(
-            task_id=result["taskId"], queue_name=QUEUE_KEY, call_type="queue"
-        ).captcha_handler()
-    )
diff --git a/anticaptcha_examples/anticaptcha_control_example.py b/anticaptcha_examples/anticaptcha_control_example.py
deleted file mode 100644
index e1a2679..0000000
--- a/anticaptcha_examples/anticaptcha_control_example.py
+++ /dev/null
@@ -1,60 +0,0 @@
-import asyncio
-
-from python3_anticaptcha import AntiCaptchaControl
-
-
-ANTICAPTCHA_KEY = ""
-# Пример метода, отправляющего жалобу на неправильно решённую капчу-изображение.
-# В качестве параметра, принимает ключ антикапчи и ID неправильно решённой капчи + тип капчи
-# Возвращает логические True(жалоба прошла)/False(ошибка при жалобе)
-result = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=ANTICAPTCHA_KEY).complaint_on_result(
-    reported_id=-5, captcha_type="image"
-)
-print(result)
-# Пример метода, отправляющего жалобу на неправильно решённую ReCaptcha.
-# В качестве параметра, принимает ключ антикапчи и ID неправильно решённой ReCaptcha + тип капчи
-# Возвращает логические True(жалоба прошла)/False(ошибка при жалобе)
-result = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=ANTICAPTCHA_KEY).complaint_on_result(
-    reported_id=-5, captcha_type="recaptcha"
-)
-print(result)
-# Пример метода, принимающего ключ аккаунта и возвращающего актуальный баланс
-result = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=ANTICAPTCHA_KEY).get_balance()
-print(result)
-# Пример метода, выдающий информацию о загрузке очереди, в зависимости от ID очереди
-# В данном случае queue_id = 1, то есть получаем информацию по загрузке очереди ImageToText (язык английский)
-result = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=ANTICAPTCHA_KEY).get_queue_status(queue_id=1)
-print(result)
-
-# Асинхронный метод работы
-async def run():
-    try:
-        # io.IOBase
-        resolved = await AntiCaptchaControl.aioAntiCaptchaControl(
-            anticaptcha_key=ANTICAPTCHA_KEY
-        ).get_balance()
-        print(resolved)
-
-        resolved = await AntiCaptchaControl.aioAntiCaptchaControl(
-            anticaptcha_key=ANTICAPTCHA_KEY
-        ).complaint_on_result(reported_id=-8, captcha_type="image")
-        print(resolved)
-
-        resolved = await AntiCaptchaControl.aioAntiCaptchaControl(
-            anticaptcha_key=ANTICAPTCHA_KEY
-        ).complaint_on_result(reported_id=-8, captcha_type="recaptcha")
-        print(resolved)
-
-        resolved = await AntiCaptchaControl.aioAntiCaptchaControl(
-            anticaptcha_key=ANTICAPTCHA_KEY
-        ).get_queue_status(queue_id=1)
-        print(resolved)
-
-    except Exception as err:
-        print(err)
-
-
-if __name__ == "__main__":
-    loop = asyncio.get_event_loop()
-    loop.run_until_complete(run())
-    loop.close()
diff --git a/anticaptcha_examples/anticaptcha_customcaptcha_example.py b/anticaptcha_examples/anticaptcha_customcaptcha_example.py
deleted file mode 100644
index 287a63e..0000000
--- a/anticaptcha_examples/anticaptcha_customcaptcha_example.py
+++ /dev/null
@@ -1,82 +0,0 @@
-import asyncio
-
-from python3_anticaptcha import CustomCaptchaTask
-
-
-ANTICAPTCHA_KEY = ""
-
-# Пример с созданием CustomCaptcha
-
-# подробней - https://anticaptcha.atlassian.net/wiki/spaces/API/pages/241827850/CustomCaptchaTask
-# ссылка на изображение
-imageUrl = "https://files.anti-captcha.com/26/41f/c23/7c50ff19.jpg"
-# кастомная форма для ответов пользователей
-custom_form = """[
-                {
-                    "label": "Number",
-                    "labelHint": false,
-                    "contentType": false,
-                    "name": "license_plate",
-                    "inputType": "text",
-                    "inputOptions": {
-                        "width": "100",
-                        "placeHolder": "Enter a letters and number without spaces"
-                    }
-                },{
-                    "label": "Car color",
-                    "labelHint": "Select color of the car",
-                    "name": "color",
-                    "inputType": "select",
-                    "inputOptions": [
-                        {
-                            "value": "white",
-                            "caption": "White color"
-                        },
-                        {
-                            "value": "black",
-                            "caption": "Black color"
-                        },
-                        {
-                            "value": "grey",
-                            "caption": "Grey color"
-                        },
-                        {
-                            "value": "blue",
-                            "caption": "Blue color"
-                        },
-                        {
-                            "value": "red",
-                            "caption": "Red color"
-                        }
-                    ]
-                }
-            ]"""
-
-my_custom_task = CustomCaptchaTask.CustomCaptchaTask(
-    anticaptcha_key=ANTICAPTCHA_KEY, assignment="Enter license plate number", forms=custom_form
-).captcha_handler(imageUrl=imageUrl)
-
-
-print(my_custom_task)
-
-
-# Асинхронный пример работы
-async def run():
-    try:
-        # Пример работы антикапчи с кастомной капчёй, асинхронно
-        result = CustomCaptchaTask.aioCustomCaptchaTask(
-            anticaptcha_key=ANTICAPTCHA_KEY,
-            assignment="Enter license plate number",
-            forms=custom_form,
-        ).captcha_handler(imageUrl=imageUrl)
-
-        print(result)
-
-    except Exception as err:
-        print(err)
-
-
-if __name__ == "__main__":
-    loop = asyncio.get_event_loop()
-    loop.run_until_complete(run())
-    loop.close()
diff --git a/anticaptcha_examples/anticaptcha_fun_example.py b/anticaptcha_examples/anticaptcha_fun_example.py
deleted file mode 100644
index ffb7482..0000000
--- a/anticaptcha_examples/anticaptcha_fun_example.py
+++ /dev/null
@@ -1,114 +0,0 @@
-import asyncio
-import requests
-
-from python3_anticaptcha import FunCaptchaTask, FunCaptchaTaskProxyless, CallbackClient
-
-
-ANTICAPTCHA_KEY = ""
-WEB_URL = ""
-SITE_KEY = ""
-# Пример работы антикапчи с фанкапчёй и с использованием прокси при этом
-result = FunCaptchaTask.FunCaptchaTask(
-    anticaptcha_key=ANTICAPTCHA_KEY,
-    proxyType="http",
-    proxyAddress="8.8.8.8",
-    proxyPort=8080,
-    proxyLogin="proxyLoginHere",
-    proxyPassword="proxyPasswordHere",
-).captcha_handler(websiteURL=WEB_URL, websitePublicKey=SITE_KEY)
-
-print(result)
-
-# Пример работы антикапчи с фанкапчёй и с БЕЗ использования прокси при этом
-result = FunCaptchaTaskProxyless.FunCaptchaTaskProxyless(anticaptcha_key=ANTICAPTCHA_KEY).captcha_handler(
-    websiteURL=WEB_URL, websitePublicKey=SITE_KEY
-)
-
-print(result)
-
-# Асинхронный пример работы
-async def run():
-    try:
-        # Пример работы антикапчи с фанкапчёй и с использованием прокси при этом
-        result = FunCaptchaTask.aioFunCaptchaTask(
-            anticaptcha_key=ANTICAPTCHA_KEY,
-            proxyType="http",
-            proxyAddress="8.8.8.8",
-            proxyPort=8080,
-            proxyLogin="proxyLoginHere",
-            proxyPassword="proxyPasswordHere",
-        ).captcha_handler(websiteURL=WEB_URL, websitePublicKey=SITE_KEY)
-
-        print(result)
-        # Пример работы антикапчи с фанкапчёй и БЕЗ использования прокси при этом
-        result = FunCaptchaTaskProxyless.aioFunCaptchaTaskProxyless(
-            anticaptcha_key=ANTICAPTCHA_KEY
-        ).captcha_handler(websiteURL=WEB_URL, websitePublicKey=SITE_KEY)
-
-        print(result)
-    except Exception as err:
-        print(err)
-
-
-if __name__ == "__main__":
-    loop = asyncio.get_event_loop()
-    loop.run_until_complete(run())
-    loop.close()
-
-"""
-Callback example
-"""
-QUEUE_KEY = "wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ_anticaptcha_queue"
-
-"""
-Перед тем как начать пользоваться сервисом нужно создать для своей задачи отдельную очередь
-Очередь можно создать один раз и пользоваться постоянно
-
-Для создания очереди нужно передать два параметра:
-1. key - название очереди, чем оно сложнее тем лучше
-2. vhost - название виртуального хоста(в данном случаи - `anticaptcha_vhost`)
-"""
-
-answer = requests.post(
-    "https://pythoncaptcha.tech:8001/register_key",
-    json={"key": QUEUE_KEY, "vhost": "anticaptcha_vhost"},
-)
-# если очередь успешно создана:
-if answer == "OK":
-
-    # создаём задание с callbackURL параметром
-    result = FunCaptchaTask.FunCaptchaTask(
-        anticaptcha_key=ANTICAPTCHA_KEY,
-        proxyType="http",
-        proxyAddress="8.8.8.8",
-        proxyPort=8080,
-        proxyLogin="proxyLoginHere",
-        proxyPassword="proxyPasswordHere",
-        callbackUrl=f"https://pythoncaptcha.tech:8001/anticaptcha/fun_captcha/{QUEUE_KEY}",
-    ).captcha_handler(websiteURL=WEB_URL, websitePublicKey=SITE_KEY)
-    print(result)
-
-    # получение результата из кеша
-    print(CallbackClient.CallbackClient(task_id=result["taskId"]).captcha_handler())
-    # получение результата из RabbitMQ очереди
-    print(
-        CallbackClient.CallbackClient(
-            task_id=result["taskId"], queue_name=QUEUE_KEY, call_type="queue"
-        ).captcha_handler()
-    )
-
-    # создаём задание с callbackURL параметром
-    result = FunCaptchaTaskProxyless.FunCaptchaTaskProxyless(
-        anticaptcha_key=ANTICAPTCHA_KEY,
-        callbackUrl=f"https://pythoncaptcha.tech:8001/anticaptcha/fun_captcha/{QUEUE_KEY}",
-    ).captcha_handler(websiteURL=WEB_URL, websitePublicKey=SITE_KEY)
-    print(result)
-
-    # получение результата из кеша
-    print(CallbackClient.CallbackClient(task_id=result["taskId"]).captcha_handler())
-    # получение результата из RabbitMQ очереди
-    print(
-        CallbackClient.CallbackClient(
-            task_id=result["taskId"], queue_name=QUEUE_KEY, call_type="queue"
-        ).captcha_handler()
-    )
diff --git a/anticaptcha_examples/anticaptcha_hcaptcha_example.py b/anticaptcha_examples/anticaptcha_hcaptcha_example.py
deleted file mode 100644
index 4b4288c..0000000
--- a/anticaptcha_examples/anticaptcha_hcaptcha_example.py
+++ /dev/null
@@ -1,117 +0,0 @@
-import asyncio
-import requests
-
-from python3_anticaptcha import HCaptchaTaskProxyless, HCaptchaTask, CallbackClient
-
-
-ANTICAPTCHA_KEY = "57929db084241b66dbc5e4eb80b117cf"
-WEB_URL = "https://dashboard.hcaptcha.com/signup"
-SITE_KEY = "00000000-0000-0000-0000-000000000000"
-# Пример работы антикапчи с фанкапчёй и с использованием прокси при этом
-result = HCaptchaTask.HCaptchaTask(
-    anticaptcha_key=ANTICAPTCHA_KEY,
-    proxyType="http",
-    proxyAddress="8.8.8.8",
-    proxyPort=8080,
-    proxyLogin="proxyLoginHere",
-    proxyPassword="proxyPasswordHere",
-    userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
-).captcha_handler(websiteURL=WEB_URL, websiteKey=SITE_KEY)
-
-print(result)
-
-# Пример работы антикапчи с фанкапчёй и с БЕЗ использования прокси при этом
-result = HCaptchaTaskProxyless.HCaptchaTaskProxyless(anticaptcha_key=ANTICAPTCHA_KEY).captcha_handler(
-    websiteURL=WEB_URL, websiteKey=SITE_KEY
-)
-
-print(result)
-
-# Асинхронный пример работы
-async def run():
-    try:
-        # Пример работы антикапчи с фанкапчёй и с использованием прокси при этом
-        result = await HCaptchaTask.aioHCaptchaTask(
-            anticaptcha_key=ANTICAPTCHA_KEY,
-            proxyType="http",
-            proxyAddress="8.8.8.8",
-            proxyPort=8080,
-            proxyLogin="proxyLoginHere",
-            proxyPassword="proxyPasswordHere",
-            userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
-        ).captcha_handler(websiteURL=WEB_URL, websiteKey=SITE_KEY)
-
-        print(result)
-        # Пример работы антикапчи с фанкапчёй и БЕЗ использования прокси при этом
-        result = await HCaptchaTaskProxyless.aioHCaptchaTaskProxyless(
-            anticaptcha_key=ANTICAPTCHA_KEY
-        ).captcha_handler(websiteURL=WEB_URL, websiteKey=SITE_KEY)
-
-        print(result)
-    except Exception as err:
-        print(err)
-
-
-if __name__ == "__main__":
-    loop = asyncio.get_event_loop()
-    loop.run_until_complete(run())
-    loop.close()
-
-"""
-Callback example
-"""
-QUEUE_KEY = "wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ_anticaptcha_queue"
-
-"""
-Перед тем как начать пользоваться сервисом нужно создать для своей задачи отдельную очередь
-Очередь можно создать один раз и пользоваться постоянно
-
-Для создания очереди нужно передать два параметра:
-1. key - название очереди, чем оно сложнее тем лучше
-2. vhost - название виртуального хоста(в данном случаи - `anticaptcha_vhost`)
-"""
-
-answer = requests.post(
-    "https://pythoncaptcha.tech:8001/register_key",
-    json={"key": QUEUE_KEY, "vhost": "anticaptcha_vhost"},
-)
-# если очередь успешно создана:
-if answer == "OK":
-
-    # создаём задание с callbackURL параметром
-    result = HCaptchaTask.HCaptchaTask(
-        anticaptcha_key=ANTICAPTCHA_KEY,
-        proxyType="http",
-        proxyAddress="8.8.8.8",
-        proxyPort=8080,
-        proxyLogin="proxyLoginHere",
-        proxyPassword="proxyPasswordHere",
-        userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
-        callbackUrl=f"https://pythoncaptcha.tech:8001/anticaptcha/fun_captcha/{QUEUE_KEY}",
-    ).captcha_handler(websiteURL=WEB_URL, websiteKey=SITE_KEY)
-    print(result)
-
-    # получение результата из кеша
-    print(CallbackClient.CallbackClient(task_id=result["taskId"]).captcha_handler())
-    # получение результата из RabbitMQ очереди
-    print(
-        CallbackClient.CallbackClient(
-            task_id=result["taskId"], queue_name=QUEUE_KEY, call_type="queue"
-        ).captcha_handler()
-    )
-
-    # создаём задание с callbackURL параметром
-    result = HCaptchaTaskProxyless.HCaptchaTaskProxyless(
-        anticaptcha_key=ANTICAPTCHA_KEY,
-        callbackUrl=f"https://pythoncaptcha.tech:8001/anticaptcha/fun_captcha/{QUEUE_KEY}",
-    ).captcha_handler(websiteURL=WEB_URL, websiteKey=SITE_KEY)
-    print(result)
-
-    # получение результата из кеша
-    print(CallbackClient.CallbackClient(task_id=result["taskId"]).captcha_handler())
-    # получение результата из RabbitMQ очереди
-    print(
-        CallbackClient.CallbackClient(
-            task_id=result["taskId"], queue_name=QUEUE_KEY, call_type="queue"
-        ).captcha_handler()
-    )
diff --git a/anticaptcha_examples/anticaptcha_nocaptcha_example.py b/anticaptcha_examples/anticaptcha_nocaptcha_example.py
deleted file mode 100644
index 5e57f84..0000000
--- a/anticaptcha_examples/anticaptcha_nocaptcha_example.py
+++ /dev/null
@@ -1,70 +0,0 @@
-import asyncio
-import requests
-
-from python3_anticaptcha import NoCaptchaTaskProxyless, NoCaptchaTask
-
-ANTICAPTCHA_KEY = "ae23fffcfaa29b170e3843e3a486ef19"
-
-
-"""
-Синхронный пример без прокси
-Sync example without proxy
-"""
-result = NoCaptchaTaskProxyless.NoCaptchaTaskProxyless(anticaptcha_key=ANTICAPTCHA_KEY).captcha_handler(
-    websiteURL="https://www.google.com/recaptcha/api2/demo",
-    websiteKey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
-)
-print(result)
-
-"""
-Синхронный пример с контекстным менеджером и без прокси
-Sync example with contextmanager and without proxy
-"""
-with NoCaptchaTaskProxyless.NoCaptchaTaskProxyless(anticaptcha_key=ANTICAPTCHA_KEY) as nocaptcha:
-    response = nocaptcha.captcha_handler(
-        websiteURL="https://www.google.com/recaptcha/api2/demo",
-        websiteKey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
-    )
-print(response)
-
-"""
-Синхронный пример с прокси
-Sync example with proxy
-"""
-result = NoCaptchaTask.NoCaptchaTask(
-    anticaptcha_key=ANTICAPTCHA_KEY,
-    proxyType="http",
-    proxyAddress="8.8.8.8",
-    proxyPort=8080,
-    proxyLogin="proxyLoginHere",
-    proxyPassword="proxyPasswordHere",
-).captcha_handler(
-    websiteURL="https://www.google.com/recaptcha/api2/demo",
-    websiteKey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
-)
-
-print(result)
-
-"""
-Асинхронный пример без прокси
-Async example without proxy
-"""
-
-
-async def run():
-    try:
-        result = await NoCaptchaTaskProxyless.aioNoCaptchaTaskProxyless(
-            anticaptcha_key=ANTICAPTCHA_KEY
-        ).captcha_handler(
-            websiteURL="https://www.google.com/recaptcha/api2/demo",
-            websiteKey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
-        )
-        print(result)
-    except Exception as err:
-        print(err)
-
-
-if __name__ == "__main__":
-    loop = asyncio.get_event_loop()
-    loop.run_until_complete(run())
-    loop.close()
diff --git a/anticaptcha_examples/anticaptcha_recaptcha_v3_example.py b/anticaptcha_examples/anticaptcha_recaptcha_v3_example.py
deleted file mode 100644
index c6b0d53..0000000
--- a/anticaptcha_examples/anticaptcha_recaptcha_v3_example.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import asyncio
-
-from python3_anticaptcha import ReCaptchaV3TaskProxyless
-
-ANTICAPTCHA_KEY = "ae23fffcfaa29b170e3843e3a486ef19"
-
-"""
-WARNING:
-
-`minScore` param can be only in [0.3, 0.5, 0.7]
-"""
-
-# Пример показывает работу антикапчи с ReCaptcha v3.
-# Это метод для работы без прокси
-result = ReCaptchaV3TaskProxyless.ReCaptchaV3TaskProxyless(anticaptcha_key=ANTICAPTCHA_KEY).captcha_handler(
-    websiteURL="https://some_page_link",
-    websiteKey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
-    minScore=0.3,
-    pageAction="login",
-)
-print(result)
-
-# contextmanager
-with ReCaptchaV3TaskProxyless.ReCaptchaV3TaskProxyless(anticaptcha_key=ANTICAPTCHA_KEY) as recaptcha:
-    response = recaptcha.captcha_handler(
-        websiteURL="https://some_page_link",
-        websiteKey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
-        minScore=0.3,
-        pageAction="login",
-    )
-print(response)
-
-# Асинхронный пример
-async def run():
-    try:
-        result = await ReCaptchaV3TaskProxyless.aioReCaptchaV3TaskProxyless(
-            anticaptcha_key=ANTICAPTCHA_KEY
-        ).captcha_handler(
-            websiteURL="https://some_page_link",
-            websiteKey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
-            minScore=0.3,
-            pageAction="login",
-        )
-        print(result)
-    except Exception as err:
-        print(err)
-
-
-if __name__ == "__main__":
-    loop = asyncio.get_event_loop()
-    loop.run_until_complete(run())
-    loop.close()
diff --git a/anticaptcha_examples/anticaptcha_square_net_task.py b/anticaptcha_examples/anticaptcha_square_net_task.py
deleted file mode 100644
index 5968dbe..0000000
--- a/anticaptcha_examples/anticaptcha_square_net_task.py
+++ /dev/null
@@ -1,82 +0,0 @@
-import asyncio
-
-import requests
-
-from python3_anticaptcha import SquareNetTextTask, CallbackClient
-
-"""
-Пример работы с данным типом капчи
-
-SquareNetTextTask : выбрать нужный объект на картинке с сеткой изображений
-________________________
-SquareNetTextTask : select objects on image with an overlay grid
-"""
-# вводим ключ от сервиса
-ANTICAPTCHA_KEY = ""
-
-# Простой пример работы / Base example
-result = SquareNetTextTask.SquareNetTextTask(anticaptcha_key=ANTICAPTCHA_KEY).captcha_handler(
-    objectName="captcha numbers",
-    rowsCount=2,
-    columnsCount=3,
-    image_link="https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/master/examples/088636.png",
-)
-print(result)
-
-# Асинхронный метод работы / Asyncio exaple
-async def run():
-
-    result = await SquareNetTextTask.aioSquareNetTextTask(anticaptcha_key=ANTICAPTCHA_KEY).captcha_handler(
-        objectName="captcha numbers",
-        rowsCount=2,
-        columnsCount=3,
-        image_link="https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/master/examples/088636.png",
-    )
-    print(result)
-
-
-if __name__ == "__main__":
-    loop = asyncio.get_event_loop()
-    loop.run_until_complete(run())
-    loop.close()
-
-"""
-Callback example
-"""
-QUEUE_KEY = "wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ_anticaptcha_queue"
-
-"""
-Перед тем как начать пользоваться сервисом нужно создать для своей задачи отдельную очередь
-Очередь можно создать один раз и пользоваться постоянно
-
-Для создания очереди нужно передать два параметра:
-1. key - название очереди, чем оно сложнее тем лучше
-2. vhost - название виртуального хоста(в данном случаи - `anticaptcha_vhost`)
-"""
-
-answer = requests.post(
-    "https://pythoncaptcha.tech:8001/register_key",
-    json={"key": QUEUE_KEY, "vhost": "anticaptcha_vhost"},
-)
-# если очередь успешно создана:
-if answer == "OK":
-    # создаём задание с callbackURL параметром
-    result = SquareNetTextTask.SquareNetTextTask(
-        anticaptcha_key=ANTICAPTCHA_KEY,
-        callbackUrl=f"https://pythoncaptcha.tech:8001/anticaptcha/fun_captcha/{QUEUE_KEY}",
-    ).captcha_handler(
-        objectName="captcha numbers",
-        rowsCount=2,
-        columnsCount=3,
-        image_link="https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/master/examples/088636.png",
-    )
-    print(result)
-
-    # получение результата из кеша
-    print(CallbackClient.CallbackClient(task_id=result["taskId"]).captcha_handler())
-    # получение результата из RabbitMQ очереди
-    print(
-        CallbackClient.CallbackClient(
-            task_id=result["taskId"], queue_name=QUEUE_KEY, call_type="queue"
-        ).captcha_handler()
-    )
diff --git a/anticaptcha_examples/chromefile/chromedriver b/anticaptcha_examples/chromefile/chromedriver
deleted file mode 100755
index d164e1a..0000000
Binary files a/anticaptcha_examples/chromefile/chromedriver and /dev/null differ
diff --git a/anticaptcha_examples/custom_result_handler_example.py b/anticaptcha_examples/custom_result_handler_example.py
deleted file mode 100644
index f60e27d..0000000
--- a/anticaptcha_examples/custom_result_handler_example.py
+++ /dev/null
@@ -1,33 +0,0 @@
-import asyncio
-
-from python3_anticaptcha import CustomResultHandler
-
-
-ANTICAPTCHA_KEY = ""
-"""
-This module is used to obtain the result of solving the task in "manual" mode
-"""
-TASK_ID = 123456
-
-# prepare client
-custom_result = CustomResultHandler.CustomResultHandler(anticaptcha_key=ANTICAPTCHA_KEY)
-
-response = custom_result.task_handler(task_id=TASK_ID)
-print(response)
-
-
-# async example
-async def run():
-    try:
-        # io.IOBase
-        custom_result = CustomResultHandler.aioCustomResultHandler(anticaptcha_key=ANTICAPTCHA_KEY)
-        response = await custom_result.task_handler(task_id=TASK_ID)
-        print(response)
-    except Exception as err:
-        print(err)
-
-
-if __name__ == "__main__":
-    loop = asyncio.get_event_loop()
-    loop.run_until_complete(run())
-    loop.close()
diff --git a/anticaptcha_examples/gee_example.py b/anticaptcha_examples/gee_example.py
deleted file mode 100644
index f9e06b9..0000000
--- a/anticaptcha_examples/gee_example.py
+++ /dev/null
@@ -1,68 +0,0 @@
-import asyncio
-
-from python3_anticaptcha import GeeTestTask, GeeTestTaskProxyless
-
-
-# пример решения GeeTestTask - скользящая капча от geetest.com
-
-ANTICAPTCHA_KEY = ""
-
-# обязательные параметры
-websiteURL = "http:\/\/mywebsite.com\/geetest\/test.php"
-gt = "874703612e5cac182812a00e273aad0d"
-challenge = "a559b82bca2c500101a1c8a4f4204742"
-
-# пример работы с GeeTestTask без прокси
-result = GeeTestTaskProxyless.GeeTestTaskProxyless(
-    anticaptcha_key=ANTICAPTCHA_KEY, websiteURL=websiteURL, gt=gt
-).captcha_handler(challenge=challenge)
-
-print(result)
-
-# пример работы с GeeTestTask c прокси
-result = GeeTestTask.GeeTestTask(
-    anticaptcha_key=ANTICAPTCHA_KEY,
-    websiteURL=websiteURL,
-    gt=gt,
-    proxyType="http",
-    proxyAddress="8.8.8.8",
-    proxyPort=8080,
-    proxyLogin="proxyLoginHere",
-    proxyPassword="proxyPasswordHere",
-    userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
-    cookies="test=value",
-).captcha_handler(challenge=challenge)
-
-print(result)
-
-
-# Асинхронный пример работы
-async def run():
-    try:
-        # пример работы с GeeTestTask c прокси
-        result = GeeTestTask.aioGeeTestTask(
-            anticaptcha_key=ANTICAPTCHA_KEY,
-            websiteURL=websiteURL,
-            gt=gt,
-            proxyType="http",
-            proxyAddress="8.8.8.8",
-            proxyPort=8080,
-            proxyLogin="proxyLoginHere",
-            proxyPassword="proxyPasswordHere",
-            userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
-            cookies="test=value",
-        ).captcha_handler(challenge=challenge)
-
-        print(result)
-        # пример работы с GeeTestTask без прокси
-        result = GeeTestTaskProxyless.aioGeeTestTaskProxyless(
-            anticaptcha_key=ANTICAPTCHA_KEY, websiteURL=websiteURL, gt=gt
-        ).captcha_handler(challenge=challenge)
-    except Exception as err:
-        print(err)
-
-
-if __name__ == "__main__":
-    loop = asyncio.get_event_loop()
-    loop.run_until_complete(run())
-    loop.close()
diff --git a/anticaptcha_examples/selenium_recaptcha_v2.py b/anticaptcha_examples/selenium_recaptcha_v2.py
deleted file mode 100644
index 7fdd32f..0000000
--- a/anticaptcha_examples/selenium_recaptcha_v2.py
+++ /dev/null
@@ -1,47 +0,0 @@
-import time
-
-from selenium import webdriver
-
-from python3_anticaptcha.NoCaptchaTaskProxyless import NoCaptchaTaskProxyless
-
-ANTICAPTCHA_KEY = ""
-WEB_URL = "https://www.google.com/recaptcha/api2/demo"
-SITE_KEY = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
-
-post_xpath = '//*[@id="recaptcha-demo-submit"]'
-recaptcha_xpath = '//*[@id="g-recaptcha-response"]'
-
-# setup selenium
-browser_options = webdriver.ChromeOptions()
-browser_options.headless = True
-browser_options.add_argument("--disable-gpu")
-browser_options.add_argument("--no-sandbox")
-browser_options.add_argument("disable-infobars")
-browser_options.add_argument("--disable-extensions")
-# run browser
-browser = webdriver.Chrome(executable_path="./chromefile/chromedriver", options=browser_options)
-# open page
-browser.get(WEB_URL)
-# prepare captcha solver
-captcha_obj = NoCaptchaTaskProxyless(anticaptcha_key=ANTICAPTCHA_KEY)
-# solve recaptcha
-browser.execute_script("document.getElementById('g-recaptcha-response').style.display = 'block';")
-# wait script finish
-time.sleep(1)
-recaptcha_element = browser.find_element_by_xpath(recaptcha_xpath)
-recaptcha_element.click()
-recaptcha_element.clear()
-# get captcha solution
-captcha_result = captcha_obj.captcha_handler(websiteURL=WEB_URL, websiteKey=SITE_KEY)
-# insert captcha solution
-recaptcha_element.send_keys(captcha_result["solution"]["gRecaptchaResponse"])
-# wait success insertion
-time.sleep(2)
-# push post button
-post_button = browser.find_element_by_xpath(post_xpath)
-browser.execute_script("arguments[0].click();", post_button)
-# OR
-# post_button.click()
-
-time.sleep(5)
-browser.quit()
diff --git a/codecov.yml b/codecov.yml
deleted file mode 100644
index 9449f72..0000000
--- a/codecov.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-codecov:
-  token:
-    561eb7be-fc37-4b09-b599-45172e77ddc4
-  notify:
-    require_ci_to_pass: yes
-
-coverage:
-  precision: 2
-  round: down
-  range: "50...100"
-
-  status:
-    project: yes
-    patch: yes
-    changes: no
-
-parsers:
-  gcov:
-    branch_detection:
-      conditional: yes
-      loop: yes
-      method: no
-      macro: no
-
-comment:
-  layout: "header, diff"
-  behavior: default
-  require_changes: no
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..d4bb2cb
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
+SOURCEDIR     = .
+BUILDDIR      = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/_static/AntiCaptcha.png b/docs/_static/AntiCaptcha.png
new file mode 100644
index 0000000..b6219aa
Binary files /dev/null and b/docs/_static/AntiCaptcha.png differ
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..6c5522f
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,87 @@
+# -- Path setup --------------------------------------------------------------
+
+import os
+import sys
+
+from pallets_sphinx_themes import ProjectLink
+
+os.chdir("../")
+sys.path.insert(0, os.path.abspath("src/"))
+
+for x in os.walk("src/python3_captchaai/"):
+    sys.path.insert(0, x[0])
+
+from python3_anticaptcha import core, turnstile
+from python3_anticaptcha.__version__ import __version__
+
+# -- Project information -----------------------------------------------------
+
+project = "python3-anticaptcha"
+copyright = "2022, AndreiDrang"
+author = "AndreiDrang"
+
+# -- General configuration ---------------------------------------------------
+extensions = (
+    "myst_parser",
+    "sphinx.ext.napoleon",
+    "pallets_sphinx_themes",
+    "sphinxcontrib.autodoc_pydantic",
+)
+myst_enable_extensions = ["deflist"]
+intersphinx_mapping = {"python": ("https://docs.python.org/3.10/", None)}
+templates_path = ["_templates"]
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
+
+# -- Options for HTML output -------------------------------------------------
+# Theme config
+html_theme = "jinja"
+html_theme_options = {"index_sidebar_logo": False}
+html_static_path = ["_static"]
+html_favicon = "_static/AntiCaptcha.png"
+html_logo = "_static/AntiCaptcha.png"
+html_title = f"python3-anticaptcha ({__version__})"
+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(
+            "AntiCaptcha",
+            "http://getcaptchasolution.com/vchfpctqyz",
+        ),
+    ]
+}
+html_sidebars = {
+    "index": ["project.html", "localtoc.html", "searchbox.html", "ethicalads.html"],
+    "**": ["localtoc.html", "relations.html", "searchbox.html", "ethicalads.html"],
+}
+
+# Typehints config
+autodoc_typehints = "both"
+autodoc_typehints_description_target = "documented"
+autodoc_typehints_format = "short"
+
+# Napoleon settings
+napoleon_google_docstring = True
+napoleon_numpy_docstring = False
+napoleon_include_init_with_doc = False
+napoleon_include_private_with_doc = False
+napoleon_include_special_with_doc = False
+napoleon_use_admonition_for_examples = True
+napoleon_use_admonition_for_notes = True
+napoleon_use_admonition_for_references = True
+napoleon_use_ivar = True
+napoleon_use_param = True
+napoleon_use_rtype = True
+napoleon_preprocess_types = True
+napoleon_type_aliases = True
+napoleon_attr_annotations = True
+
+autodoc_preserve_defaults = False
+autodoc_member_order = "bysource"
+autodoc_class_signature = "mixed"
+
+# Pydantic
+autodoc_pydantic_model_show_json = True
+autodoc_pydantic_settings_show_json = False
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..5d8c646
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,19 @@
+.. rst-class:: hide-header
+
+python3-anticaptcha
+===================
+.. image:: _static/AntiCaptcha.png
+    :align: center
+
+Python3 library for `AntiCaptcha API <http://getcaptchasolution.com/vchfpctqyz>`_.
+
+The library is intended for software developers and is used to work with the `AntiCaptcha API <http://getcaptchasolution.com/vchfpctqyz>`_.
+
+.. toctree::
+   :maxdepth: 1
+   :caption: Start here:
+
+   modules/main/info.md
+   modules/other-libs/info.md
+   modules/license/info.md
+   modules/contacts/info.md
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000..48b9584
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,36 @@
+
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+	set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=.
+set BUILDDIR=_build
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+	echo.
+	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+	echo.installed, then set the SPHINXBUILD environment variable to point
+	echo.to the full path of the 'sphinx-build' executable. Alternatively you
+	echo.may add the Sphinx directory to PATH.
+	echo.
+	echo.If you don't have Sphinx installed, grab it from
+	echo.http://sphinx-doc.org/
+	exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+
+:end
+popd
\ No newline at end of file
diff --git a/docs/modules/contacts/info.md b/docs/modules/contacts/info.md
new file mode 100644
index 0000000..a896f45
--- /dev/null
+++ b/docs/modules/contacts/info.md
@@ -0,0 +1,5 @@
+# Contacts
+
+If you have any questions, please send a message to the [Telegram](https://t.me/pythoncaptcha) chat room.
+
+Or email: [python-captcha@pm.me](mailto:python-captcha@pm.me)
\ No newline at end of file
diff --git a/docs/modules/license/info.md b/docs/modules/license/info.md
new file mode 100644
index 0000000..e452cbe
--- /dev/null
+++ b/docs/modules/license/info.md
@@ -0,0 +1,23 @@
+# License
+
+MIT License
+
+Copyright 2022 Andrei
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/docs/modules/main/info.md b/docs/modules/main/info.md
new file mode 100644
index 0000000..81228fb
--- /dev/null
+++ b/docs/modules/main/info.md
@@ -0,0 +1,48 @@
+# Introduction
+
+## Welcome to python3-anticaptcha
+
+![](../../_static/AntiCaptcha.png)
+
+[![PyPI version](https://badge.fury.io/py/python3-captchaai.svg)](https://badge.fury.io/py/python3-captchaai)
+[![Python versions](https://img.shields.io/pypi/pyversions/python3-captchaai.svg?logo=python&logoColor=FBE072)](https://badge.fury.io/py/python3-captchaai)
+[![Downloads](https://pepy.tech/badge/python3-captchaai/month)](https://pepy.tech/project/python3-captchaai)
+
+[![Maintainability](https://api.codeclimate.com/v1/badges/3431fd3fe71baf7eb9da/maintainability)](https://codeclimate.com/github/AndreiDrang/python3-captchaai/maintainability)
+[![Codacy Badge](https://app.codacy.com/project/badge/Grade/323d4eda0fe1477bbea8fe8902b9e97e)](https://www.codacy.com/gh/AndreiDrang/python3-captchaai/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=AndreiDrang/python3-captchaai&amp;utm_campaign=Badge_Grade)
+[![codecov](https://codecov.io/gh/AndreiDrang/python3-captchaai/branch/main/graph/badge.svg?token=2L4VVIF4G8)](https://codecov.io/gh/AndreiDrang/python3-captchaai)
+
+[![Build check](https://github.com/AndreiDrang/python3-captchaai/actions/workflows/test_build.yml/badge.svg?branch=main)](https://github.com/AndreiDrang/python3-captchaai/actions/workflows/test_build.yml)
+[![Installation check](https://github.com/AndreiDrang/python3-captchaai/actions/workflows/install.yml/badge.svg?branch=main)](https://github.com/AndreiDrang/python3-captchaai/actions/workflows/install.yml)
+[![Test](https://github.com/AndreiDrang/python3-captchaai/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/AndreiDrang/python3-captchaai/actions/workflows/test.yml)
+[![Lint](https://github.com/AndreiDrang/python3-captchaai/actions/workflows/lint.yml/badge.svg?branch=main)](https://github.com/AndreiDrang/python3-captchaai/actions/workflows/lint.yml)
+
+
+Python3 library for [Capsolver](https://capsolver.com/) service API.
+
+Tested on UNIX based OS.
+
+The library is intended for software developers and is used to work with the [Capsolver](https://capsolver.com/) service API.
+
+## How to install?
+
+We recommend using the latest version of Python. `python3-captchaai` supports Python
+3.7+.
+
+### pip
+
+```bash
+pip install python3-captchaai
+```
+
+### Source
+```bash
+git clone https://github.com/AndreiDrang/python3-captchaai.git
+cd python3-captchaai
+python setup.py install
+```
+
+## How to test?
+
+1. You need set ``API_KEY`` in your environment(get this value from you account).
+2. Run command ``make tests``, from root directory.
diff --git a/docs/modules/other-libs/info.md b/docs/modules/other-libs/info.md
new file mode 100644
index 0000000..4ee6070
--- /dev/null
+++ b/docs/modules/other-libs/info.md
@@ -0,0 +1,6 @@
+# Other captcha-solving services libs
+
+## Other libraries for Captcha-solving services
+1. [RuCaptcha / 2Captcha](https://github.com/AndreiDrang/python-rucaptcha)
+2. [AntiCaptcha](https://github.com/AndreiDrang/python3-anticaptcha)
+3. [Capsolver](https://github.com/AndreiDrang/python3-captchaai)
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 0000000..170893f
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,3 @@
+pallets_sphinx_themes==2.*
+myst-parser==0.18.*
+autodoc_pydantic==1.8.*
diff --git a/pyproject.toml b/pyproject.toml
deleted file mode 100644
index 07103cd..0000000
--- a/pyproject.toml
+++ /dev/null
@@ -1,16 +0,0 @@
-[tool.black]
-line-length = 110
-target-version = ['py36']
-exclude = '''
-/(
-    \.git
-  | \.hg
-  | \.mypy_cache
-  | \.tox
-  | \.venv
-  | _build
-  | buck-out
-  | build
-  | dist
-)/
-'''
\ No newline at end of file
diff --git a/requirements.style.txt b/requirements.style.txt
new file mode 100644
index 0000000..b2f2f39
--- /dev/null
+++ b/requirements.style.txt
@@ -0,0 +1,4 @@
+# codestyle
+isort==5.*
+black==22.*
+autoflake==2.*
diff --git a/src/.coveragerc b/src/.coveragerc
new file mode 100644
index 0000000..2146cbc
--- /dev/null
+++ b/src/.coveragerc
@@ -0,0 +1,16 @@
+[run]
+omit =
+        *__init__*
+        */tests/*
+
+include =
+        */python3_anticaptcha/*
+
+
+[report]
+omit =
+        *__init__*
+        */tests/*
+
+include =
+        */python3_anticaptcha/*
diff --git a/python3_anticaptcha/__init__.py b/src/__init__.py
similarity index 100%
rename from python3_anticaptcha/__init__.py
rename to src/__init__.py
diff --git a/src/pyproject.toml b/src/pyproject.toml
new file mode 100644
index 0000000..6c5d122
--- /dev/null
+++ b/src/pyproject.toml
@@ -0,0 +1,29 @@
+[tool.black]
+line-length = 120
+target-version = ['py310']
+exclude = '''
+/(
+    \.git
+  | \.hg
+  | \.mypy_cache
+  | \.tox
+  | \.venv
+  | _build
+  | buck-out
+  | build
+  | dist
+)/
+'''
+
+[tool.isort]
+profile = "black"
+line_length = 120
+length_sort = true
+
+[tool.pytest.ini_options]
+minversion = "7.0"
+asyncio_mode = "auto"
+testpaths = [
+    "tests",
+]
+addopts = "-vv --tb=short --durations=5"
diff --git a/python3_anticaptcha/AntiCaptchaAppStats.py b/src/python3_anticaptcha/AntiCaptchaAppStats.py
similarity index 100%
rename from python3_anticaptcha/AntiCaptchaAppStats.py
rename to src/python3_anticaptcha/AntiCaptchaAppStats.py
diff --git a/python3_anticaptcha/AntiCaptchaControl.py b/src/python3_anticaptcha/AntiCaptchaControl.py
similarity index 94%
rename from python3_anticaptcha/AntiCaptchaControl.py
rename to src/python3_anticaptcha/AntiCaptchaControl.py
index 31d7353..ab2d270 100644
--- a/python3_anticaptcha/AntiCaptchaControl.py
+++ b/src/python3_anticaptcha/AntiCaptchaControl.py
@@ -81,9 +81,7 @@ def send_funds(self, accountLogin: str = None, accountEmail: str = None, amount:
         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:
+    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-х часового отчета
@@ -94,9 +92,7 @@ def get_spend_stats(
         :return: Возвращает словарь с данными трат
         """
         if queue and queue not in queues_names:
-            raise ValueError(
-                f"\nWrong `queue` parameter. Valid params: {queues_names}." f"\n\tYour param - `{queue}`"
-            )
+            raise ValueError(f"\nWrong `queue` parameter. Valid params: {queues_names}." f"\n\tYour param - `{queue}`")
         payload = {
             "clientKey": self.ANTICAPTCHA_KEY,
             "date": date,
@@ -180,9 +176,7 @@ def get_queue_status(queue_id: int) -> dict:
         """
 
         if queue_id not in queue_ids:
-            raise ValueError(
-                f"\nWrong `mode` parameter. Valid params: {queue_ids}." f"\n\tYour param - `{queue_id}`"
-            )
+            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)
@@ -218,9 +212,7 @@ async def get_balance(self) -> dict:
                 else:
                     return {"errorId": 1}
 
-    async def send_funds(
-        self, accountLogin: str = None, accountEmail: str = None, amount: float = None
-    ) -> dict:
+    async def send_funds(self, accountLogin: str = None, accountEmail: str = None, amount: float = None) -> dict:
         """
         Отправить средства другому пользователю
         В вашем аккаунте должна быть включена опция отправки средств через API.
@@ -244,9 +236,7 @@ async def send_funds(
                 else:
                     return {"errorId": 1}
 
-    async def get_spend_stats(
-        self, date: int = None, queue: str = None, softId: int = None, ip: str = None
-    ) -> dict:
+    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-х часового отчета
@@ -257,9 +247,7 @@ async def get_spend_stats(
         :return: Возвращает словарь с данными трат
         """
         if queue and queue not in queues_names:
-            raise ValueError(
-                f"\nWrong `queue` parameter. Valid params: {queues_names}." f"\n\tYour param - `{queue}`"
-            )
+            raise ValueError(f"\nWrong `queue` parameter. Valid params: {queues_names}." f"\n\tYour param - `{queue}`")
         payload = {
             "clientKey": self.ANTICAPTCHA_KEY,
             "date": date,
@@ -349,9 +337,7 @@ async def get_queue_status(queue_id: int) -> dict:
         :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}`"
-            )
+            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:
diff --git a/python3_anticaptcha/CustomCaptchaTask.py b/src/python3_anticaptcha/CustomCaptchaTask.py
similarity index 100%
rename from python3_anticaptcha/CustomCaptchaTask.py
rename to src/python3_anticaptcha/CustomCaptchaTask.py
diff --git a/python3_anticaptcha/CustomResultHandler.py b/src/python3_anticaptcha/CustomResultHandler.py
similarity index 100%
rename from python3_anticaptcha/CustomResultHandler.py
rename to src/python3_anticaptcha/CustomResultHandler.py
diff --git a/python3_anticaptcha/FunCaptchaTask.py b/src/python3_anticaptcha/FunCaptchaTask.py
similarity index 100%
rename from python3_anticaptcha/FunCaptchaTask.py
rename to src/python3_anticaptcha/FunCaptchaTask.py
diff --git a/python3_anticaptcha/FunCaptchaTaskProxyless.py b/src/python3_anticaptcha/FunCaptchaTaskProxyless.py
similarity index 96%
rename from python3_anticaptcha/FunCaptchaTaskProxyless.py
rename to src/python3_anticaptcha/FunCaptchaTaskProxyless.py
index c90d892..b190c6e 100644
--- a/python3_anticaptcha/FunCaptchaTaskProxyless.py
+++ b/src/python3_anticaptcha/FunCaptchaTaskProxyless.py
@@ -51,9 +51,7 @@ def captcha_handler(self, websiteURL: str, data: str, websitePublicKey: str, **k
         :param kwargs: Дополнительные параметры для `requests.post(....)`.
                 :return: Возвращает ответ сервера в виде JSON(ответ так же можно глянуть в документации антикапчи)
         """
-        self.task_payload["task"].update(
-            {"websiteURL": websiteURL, "data": data, "websitePublicKey": websitePublicKey}
-        )
+        self.task_payload["task"].update({"websiteURL": websiteURL, "data": data, "websitePublicKey": websitePublicKey})
         # Отправляем на антикапча параметры фанкапич,
         # в результате получаем JSON ответ содержащий номер решаемой капчи
         captcha_id = requests.post(create_task_url, json=self.task_payload, verify=False, **kwargs).json()
@@ -110,9 +108,7 @@ async def captcha_handler(self, websiteURL: str, data: str, websitePublicKey: st
         :param websitePublicKey: Ключ капчи(как его получить - описано в документаии на сайте антикапчи)
         :return: Возвращает ответ сервера в виде JSON(ответ так же можно глянуть в документации антикапчи)
         """
-        self.task_payload["task"].update(
-            {"websiteURL": websiteURL, "data": data, "websitePublicKey": websitePublicKey}
-        )
+        self.task_payload["task"].update({"websiteURL": websiteURL, "data": data, "websitePublicKey": websitePublicKey})
         # Отправляем на антикапча параметры фанкапич,
         # в результате получаем JSON ответ содержащий номер решаемой капчи
         async with aiohttp.ClientSession() as session:
diff --git a/python3_anticaptcha/GeeTestTask.py b/src/python3_anticaptcha/GeeTestTask.py
similarity index 100%
rename from python3_anticaptcha/GeeTestTask.py
rename to src/python3_anticaptcha/GeeTestTask.py
diff --git a/python3_anticaptcha/GeeTestTaskProxyless.py b/src/python3_anticaptcha/GeeTestTaskProxyless.py
similarity index 100%
rename from python3_anticaptcha/GeeTestTaskProxyless.py
rename to src/python3_anticaptcha/GeeTestTaskProxyless.py
diff --git a/python3_anticaptcha/HCaptchaTask.py b/src/python3_anticaptcha/HCaptchaTask.py
similarity index 100%
rename from python3_anticaptcha/HCaptchaTask.py
rename to src/python3_anticaptcha/HCaptchaTask.py
diff --git a/python3_anticaptcha/HCaptchaTaskProxyless.py b/src/python3_anticaptcha/HCaptchaTaskProxyless.py
similarity index 100%
rename from python3_anticaptcha/HCaptchaTaskProxyless.py
rename to src/python3_anticaptcha/HCaptchaTaskProxyless.py
diff --git a/python3_anticaptcha/ImageToTextTask.py b/src/python3_anticaptcha/ImageToTextTask.py
similarity index 96%
rename from python3_anticaptcha/ImageToTextTask.py
rename to src/python3_anticaptcha/ImageToTextTask.py
index 87537ed..601ab4e 100644
--- a/python3_anticaptcha/ImageToTextTask.py
+++ b/src/python3_anticaptcha/ImageToTextTask.py
@@ -8,14 +8,7 @@
 import requests
 from requests.adapters import HTTPAdapter
 
-from python3_anticaptcha import (
-    ReadError,
-    ParamError,
-    app_key,
-    create_task_url,
-    get_sync_result,
-    get_async_result,
-)
+from python3_anticaptcha import ReadError, ParamError, app_key, create_task_url, get_sync_result, get_async_result
 
 SAVE_FORMATS = ("const", "temp")
 
@@ -52,8 +45,7 @@ def __init__(
             self.save_format = save_format
         else:
             raise ValueError(
-                f"\nWrong `save_format` parameter. Valid params: {SAVE_FORMATS}."
-                f"\n\tYour param - `{save_format}`"
+                f"\nWrong `save_format` parameter. Valid params: {SAVE_FORMATS}." f"\n\tYour param - `{save_format}`"
             )
         # Пайлоад для создания задачи
         self.task_payload = {
@@ -141,9 +133,7 @@ def __read_captcha_image_file(self, content: bytes, content_type: str = "file"):
             if content_type == "file":
                 with open(content, "rb") as captcha_image:
                     # Добавляем в пайлоад картинку и отправляем
-                    self.task_payload["task"].update(
-                        {"body": base64.b64encode(captcha_image.read()).decode("utf-8")}
-                    )
+                    self.task_payload["task"].update({"body": base64.b64encode(captcha_image.read()).decode("utf-8")})
             elif content_type == "base64":
                 self.task_payload["task"].update({"body": content})
             else:
@@ -227,9 +217,7 @@ def captcha_handler(
         else:
             # Ожидаем решения капчи
             time.sleep(self.sleep_time)
-            return get_sync_result(
-                result_payload=self.result_payload, sleep_time=self.sleep_time, **session_params
-            )
+            return get_sync_result(result_payload=self.result_payload, sleep_time=self.sleep_time, **session_params)
 
 
 class aioImageToTextTask:
@@ -264,8 +252,7 @@ def __init__(
             self.save_format = save_format
         else:
             raise ValueError(
-                f"\nWrong `save_format` parameter. Valid params: {SAVE_FORMATS}."
-                f"\n\tYour param - `{save_format}`"
+                f"\nWrong `save_format` parameter. Valid params: {SAVE_FORMATS}." f"\n\tYour param - `{save_format}`"
             )
 
         # Пайлоад для создания задачи
@@ -359,9 +346,7 @@ async def __read_captcha_image_file(self, content: bytes, content_type: str = "f
             if content_type == "file":
                 with open(content, "rb") as captcha_image:
                     # Добавляем в пайлоад картинку и отправляем
-                    self.task_payload["task"].update(
-                        {"body": base64.b64encode(captcha_image.read()).decode("utf-8")}
-                    )
+                    self.task_payload["task"].update({"body": base64.b64encode(captcha_image.read()).decode("utf-8")})
             elif content_type == "base64":
                 self.task_payload["task"].update({"body": content})
             else:
diff --git a/python3_anticaptcha/NoCaptchaTask.py b/src/python3_anticaptcha/NoCaptchaTask.py
similarity index 98%
rename from python3_anticaptcha/NoCaptchaTask.py
rename to src/python3_anticaptcha/NoCaptchaTask.py
index 1707630..feb51a7 100644
--- a/python3_anticaptcha/NoCaptchaTask.py
+++ b/src/python3_anticaptcha/NoCaptchaTask.py
@@ -48,9 +48,7 @@ def __exit__(self, exc_type, exc_value, traceback):
         return True
 
     # Работа с капчей
-    def captcha_handler(
-        self, websiteURL: str, websiteKey: str, recaptchaDataSValue: str = "", **kwargs
-    ) -> dict:
+    def captcha_handler(self, websiteURL: str, websiteKey: str, recaptchaDataSValue: str = "", **kwargs) -> dict:
         """
         Метод получает ссылку на страницу, где расположена капча, и ключ капчи
         :param: websiteURL: Ссылка на страницу с капчёй
diff --git a/python3_anticaptcha/NoCaptchaTaskProxyless.py b/src/python3_anticaptcha/NoCaptchaTaskProxyless.py
similarity index 98%
rename from python3_anticaptcha/NoCaptchaTaskProxyless.py
rename to src/python3_anticaptcha/NoCaptchaTaskProxyless.py
index 4f18cfb..368e67b 100644
--- a/python3_anticaptcha/NoCaptchaTaskProxyless.py
+++ b/src/python3_anticaptcha/NoCaptchaTaskProxyless.py
@@ -47,9 +47,7 @@ def __exit__(self, exc_type, exc_value, traceback):
         return True
 
     # Работа с капчёй
-    def captcha_handler(
-        self, websiteURL: str, websiteKey: str, recaptchaDataSValue: str = "", **kwargs
-    ) -> dict:
+    def captcha_handler(self, websiteURL: str, websiteKey: str, recaptchaDataSValue: str = "", **kwargs) -> dict:
         """
                 Метод решения ReCaptcha
                 :param websiteURL: Ссылка на страницу с капчёй
diff --git a/python3_anticaptcha/ReCaptchaV3TaskProxyless.py b/src/python3_anticaptcha/ReCaptchaV3TaskProxyless.py
similarity index 98%
rename from python3_anticaptcha/ReCaptchaV3TaskProxyless.py
rename to src/python3_anticaptcha/ReCaptchaV3TaskProxyless.py
index 34f8d80..e21d9a0 100644
--- a/python3_anticaptcha/ReCaptchaV3TaskProxyless.py
+++ b/src/python3_anticaptcha/ReCaptchaV3TaskProxyless.py
@@ -135,9 +135,7 @@ def __exit__(self, exc_type, exc_value, traceback):
         return True
 
     # Работа с капчёй
-    async def captcha_handler(
-        self, websiteURL: str, websiteKey: str, minScore: float, pageAction: str = None
-    ) -> dict:
+    async def captcha_handler(self, websiteURL: str, websiteKey: str, minScore: float, pageAction: str = None) -> dict:
         """
         Метод решения ReCaptcha V3
         :param websiteURL: Ссылка на страницу с капчёй
diff --git a/python3_anticaptcha/SquareNetTextTask.py b/src/python3_anticaptcha/SquareNetTextTask.py
similarity index 97%
rename from python3_anticaptcha/SquareNetTextTask.py
rename to src/python3_anticaptcha/SquareNetTextTask.py
index 16ed25c..fd243b1 100644
--- a/python3_anticaptcha/SquareNetTextTask.py
+++ b/src/python3_anticaptcha/SquareNetTextTask.py
@@ -8,14 +8,7 @@
 import requests
 from requests.adapters import HTTPAdapter
 
-from python3_anticaptcha import (
-    ReadError,
-    ParamError,
-    app_key,
-    create_task_url,
-    get_sync_result,
-    get_async_result,
-)
+from python3_anticaptcha import ReadError, ParamError, app_key, create_task_url, get_sync_result, get_async_result
 
 
 class SquareNetTextTask:
@@ -134,9 +127,7 @@ def __read_captcha_image_file(self, content: bytes, content_type: str = "file"):
             if content_type == "file":
                 with open(content, "rb") as captcha_image:
                     # Добавляем в пайлоад картинку и отправляем
-                    self.task_payload["task"].update(
-                        {"body": base64.b64encode(captcha_image.read()).decode("utf-8")}
-                    )
+                    self.task_payload["task"].update({"body": base64.b64encode(captcha_image.read()).decode("utf-8")})
             elif content_type == "base64":
                 self.task_payload["task"].update({"body": content})
             else:
@@ -260,8 +251,7 @@ def __init__(
             self.save_format = save_format
         else:
             raise ValueError(
-                "\nWrong `save_format` parameter. Valid params: `const` or `temp`."
-                f"\n\tYour param - `{save_format}`"
+                "\nWrong `save_format` parameter. Valid params: `const` or `temp`." f"\n\tYour param - `{save_format}`"
             )
         # Пайлоад для создания задачи
         self.task_payload = {
@@ -348,9 +338,7 @@ async def __read_captcha_image_file(self, content: bytes, content_type: str = "f
             if content_type == "file":
                 with open(content, "rb") as captcha_image:
                     # Добавляем в пайлоад картинку и отправляем
-                    self.task_payload["task"].update(
-                        {"body": base64.b64encode(captcha_image.read()).decode("utf-8")}
-                    )
+                    self.task_payload["task"].update({"body": base64.b64encode(captcha_image.read()).decode("utf-8")})
             elif content_type == "base64":
                 self.task_payload["task"].update({"body": content})
             else:
diff --git a/tests/__init__.py b/src/python3_anticaptcha/__init__.py
similarity index 100%
rename from tests/__init__.py
rename to src/python3_anticaptcha/__init__.py
diff --git a/src/python3_anticaptcha/__version__.py b/src/python3_anticaptcha/__version__.py
new file mode 100644
index 0000000..911cb08
--- /dev/null
+++ b/src/python3_anticaptcha/__version__.py
@@ -0,0 +1,1 @@
+__version__ = "2.0.1a"
diff --git a/python3_anticaptcha/config.py b/src/python3_anticaptcha/config.py
similarity index 99%
rename from python3_anticaptcha/config.py
rename to src/python3_anticaptcha/config.py
index c2d839c..2014df2 100644
--- a/python3_anticaptcha/config.py
+++ b/src/python3_anticaptcha/config.py
@@ -1,6 +1,7 @@
-import urllib3
 from typing import Generator
 
+import urllib3
+
 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
 
 # Адрес для создания задачи
diff --git a/src/python3_anticaptcha/core/__init__.py b/src/python3_anticaptcha/core/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/python3_anticaptcha/core/base.py b/src/python3_anticaptcha/core/base.py
new file mode 100644
index 0000000..8ac8650
--- /dev/null
+++ b/src/python3_anticaptcha/core/base.py
@@ -0,0 +1,205 @@
+import time
+import asyncio
+import logging
+from typing import Union
+from urllib import parse
+
+import aiohttp
+import requests
+from requests.adapters import HTTPAdapter
+
+from python3_anticaptcha.core.enum import CaptchaTypeEnm, ResponseStatusEnm
+from python3_anticaptcha.core.config import (
+    RETRIES,
+    BASE_REQUEST_URL,
+    GET_RESULT_POSTFIX,
+    CREATE_TASK_POSTFIX,
+    attempts_generator,
+)
+from python3_anticaptcha.core.serializer import (
+    CreateTaskRequestSer,
+    CreateTaskResponseSer,
+    GetTaskResultRequestSer,
+    GetTaskResultResponseSer,
+)
+
+
+class BaseCaptcha:
+    """
+    Basic Captcha solving class
+
+    Args:
+        api_key: Capsolver API key
+        captcha_type: Captcha type name, like `ReCaptchaV2Task` and etc.
+        sleep_time: The waiting time between requests to get the result of the Captcha
+        request_url: API address for sending requests
+    """
+
+    def __init__(self, api_key: str, captcha_type: Union[CaptchaTypeEnm, str], sleep_time: int, **kwargs):
+        # validate captcha_type parameter
+        if captcha_type in CaptchaTypeEnm.list_values():
+            self.captcha_type = captcha_type
+        else:
+            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())
+        # `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
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, exc_type, exc_value, traceback):
+        if exc_type:
+            return False
+        return True
+
+    async def __aenter__(self):
+        return self
+
+    async def __aexit__(self, exc_type, exc_value, traceback):
+        if exc_type:
+            return False
+        return True
+
+    """
+    Sync part
+    """
+
+    def _processing_captcha(self) -> dict:
+
+        # added task params to payload
+        self.__params.task = self.task_params
+
+        created_task = self._create_task()
+
+        if created_task.errorId == 0:
+            self._get_result_params.taskId = created_task.taskId
+            return self._get_result().dict()
+        return created_task.dict()
+
+    def _create_task(self) -> 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())
+            if resp.status_code == 200:
+                return CreateTaskResponseSer(**resp.json())
+            else:
+                raise ValueError(resp.raise_for_status())
+        except Exception as error:
+            logging.exception(error)
+            raise
+
+    def _get_result(self) -> GetTaskResultResponseSer:
+        """
+        Method send SYNC `getTaskResult` request to service and wait for result
+        """
+        # initial waiting
+        time.sleep(self.__sleep_time)
+
+        attempts = attempts_generator()
+        for i in attempts:
+            logging.info(f"Attempt #{i}")
+            try:
+                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:
+                    task_result_data = GetTaskResultResponseSer(**task_result_response.json())
+
+                    if task_result_data.errorId == 0:
+
+                        if task_result_data.status == ResponseStatusEnm.processing:
+                            time.sleep(self.__sleep_time)
+                        else:
+                            task_result_data.taskId = self._get_result_params.taskId
+                            return task_result_data
+                    else:
+                        task_result_data.taskId = self._get_result_params.taskId
+                        return task_result_data
+                else:
+                    raise ValueError(task_result_response.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
+
+        created_task = await self._aio_create_task()
+
+        if created_task.errorId == 0:
+            self._get_result_params.taskId = created_task.taskId
+            result = await self._aio_get_result()
+            return result.dict()
+        return created_task.dict()
+
+    async def _aio_create_task(self) -> 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:
+                    if resp.status == 200:
+                        return CreateTaskResponseSer(**await resp.json())
+                    else:
+                        raise ValueError(resp.reason)
+            except Exception as error:
+                logging.exception(error)
+                raise
+
+    async def _aio_get_result(self) -> GetTaskResultResponseSer:
+        """
+        Method send SYNC `getTaskResult` request to service and wait for result
+        """
+        # initial waiting
+        await asyncio.sleep(self.__sleep_time)
+
+        attempts = attempts_generator()
+        async with aiohttp.ClientSession() as session:
+            for i in attempts:
+                logging.info(f"Attempt #{i}")
+                try:
+                    async with session.post(
+                        parse.urljoin(BASE_REQUEST_URL, GET_RESULT_POSTFIX),
+                        json=self._get_result_params.dict(),
+                    ) as task_result_response:
+                        if task_result_response.status == 200:
+                            task_result_data = GetTaskResultResponseSer(**await task_result_response.json())
+
+                            if task_result_data.errorId == 0:
+
+                                if task_result_data.status == ResponseStatusEnm.processing:
+                                    time.sleep(self.__sleep_time)
+                                else:
+                                    task_result_data.taskId = self._get_result_params.taskId
+                                    return task_result_data
+                            else:
+                                task_result_data.taskId = self._get_result_params.taskId
+                                return task_result_data
+                        else:
+                            raise ValueError(task_result_response.raise_for_status())
+                except Exception as error:
+                    logging.exception(error)
+                    raise
diff --git a/src/python3_anticaptcha/core/config.py b/src/python3_anticaptcha/core/config.py
new file mode 100644
index 0000000..270d82c
--- /dev/null
+++ b/src/python3_anticaptcha/core/config.py
@@ -0,0 +1,39 @@
+from typing import Generator
+
+import urllib3
+from tenacity import AsyncRetrying, wait_fixed, stop_after_attempt
+from requests.adapters import Retry
+
+urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
+
+RETRIES = Retry(total=5, backoff_factor=0.9, status_forcelist=[500, 502, 503, 504])
+ASYNC_RETRIES = AsyncRetrying(wait=wait_fixed(5), stop=stop_after_attempt(5), reraise=True)
+
+BASE_REQUEST_URL = "https://api.anti-captcha.com/"
+CREATE_TASK_POSTFIX = "/createTask"
+GET_RESULT_POSTFIX = "/getTaskResult"
+APP_KEY = "867"
+
+
+# Connection retry generator
+def attempts_generator(amount: int = 30) -> Generator:
+    """
+    Function generates a generator of length equal to `amount`
+
+    Args:
+        amount: number of attempts generated
+
+    Yields:
+        int: The next number in the range of 1 to ``amount`` - 1.
+
+    Examples:
+        Examples should be written in doctest format, and should illustrate how
+        to use the function.
+
+        >>> print([i for i in attempts_generator(5)])
+        [1, 2, 3, 4]
+
+    Returns:
+        Attempt number
+    """
+    yield from range(1, amount)
diff --git a/src/python3_anticaptcha/core/enum.py b/src/python3_anticaptcha/core/enum.py
new file mode 100644
index 0000000..6590659
--- /dev/null
+++ b/src/python3_anticaptcha/core/enum.py
@@ -0,0 +1,89 @@
+from enum import Enum
+from types import DynamicClassAttribute
+from typing import List
+
+
+class MyEnum(Enum):
+    @classmethod
+    def list(cls) -> List[Enum]:
+        return list(map(lambda c: c, cls))
+
+    @classmethod
+    def list_values(cls) -> List[str]:
+        return list(map(lambda c: c.value, cls))
+
+    @classmethod
+    def list_names(cls) -> List[str]:
+        return list(map(lambda c: c.name, cls))
+
+    @DynamicClassAttribute
+    def name(self) -> str:
+        """
+        The name of the Enum member
+        """
+        return self._name_
+
+    @DynamicClassAttribute
+    def value(self) -> str:
+        """
+        The name of the Enum member
+        """
+        return self._value_
+
+
+class EndpointPostfixEnm(str, MyEnum):
+    """
+    Enum stored URL postfixes for API endpoints
+    """
+
+    CREATE_TASK = "createTask"
+    GET_TASK_RESULT = "getTaskResult"
+
+
+class CaptchaTypeEnm(str, MyEnum):
+    """
+    Enum with all available captcha types
+    """
+
+    Control = "Control"  # custom captcha type
+    ImageToTextTask = "ImageToTextTask"
+    # Recaptcha
+    RecaptchaV2Task = "RecaptchaV2Task"
+    RecaptchaV2TaskProxyless = "RecaptchaV2TaskProxyless"
+    RecaptchaV3TaskProxyless = "RecaptchaV3TaskProxyless"
+    RecaptchaV2EnterpriseTask = "RecaptchaV2EnterpriseTask"
+    RecaptchaV2EnterpriseTaskProxyless = "RecaptchaV2EnterpriseTaskProxyless"
+    # FunCaptcha
+    FunCaptchaTask = "FunCaptchaTask"
+    FunCaptchaTaskProxyless = "FunCaptchaTaskProxyless"
+    # GeeTest
+    GeeTestTask = "GeeTestTask"
+    GeeTestTaskProxyless = "GeeTestTaskProxyless"
+    # HCaptcha
+    HCaptchaTask = "HCaptchaTask"
+    HCaptchaTaskProxyless = "HCaptchaTaskProxyless"
+    # Turnstile
+    TurnstileTask = "TurnstileTask"
+    TurnstileTaskProxyless = "TurnstileTaskProxyless"
+    # Custom
+    AntiGateTask = "AntiGateTask"
+
+
+class ResponseStatusEnm(str, MyEnum):
+    """
+    Enum store results `status` field variants
+    """
+
+    processing = "processing"  # Task is not ready yet
+    ready = "ready"  # Task is complete; you'll find a solution in the solution property
+
+
+class ProxyType(str, MyEnum):
+    """
+    Enum store proxy types
+    """
+
+    http = "http"  # usual http / https
+    https = "https"
+    socks4 = "socks4"
+    socks5 = "socks5"
diff --git a/python3_anticaptcha/errors.py b/src/python3_anticaptcha/core/errors.py
similarity index 91%
rename from python3_anticaptcha/errors.py
rename to src/python3_anticaptcha/core/errors.py
index dd1bf49..374a6bd 100644
--- a/python3_anticaptcha/errors.py
+++ b/src/python3_anticaptcha/core/errors.py
@@ -22,9 +22,7 @@ class ParamError(AntiCaptchaApiException):
     def __init__(self, additional_info=None):
         AntiCaptchaApiException.__init__(
             self,
-            """\nПораждается, при передаче неверного параметра.""" + additional_info
-            if additional_info
-            else "\n",
+            """\nПораждается, при передаче неверного параметра.""" + additional_info if additional_info else "\n",
         )
 
 
diff --git a/src/python3_anticaptcha/core/serializer.py b/src/python3_anticaptcha/core/serializer.py
new file mode 100644
index 0000000..b43ca9e
--- /dev/null
+++ b/src/python3_anticaptcha/core/serializer.py
@@ -0,0 +1,69 @@
+from typing import Dict
+
+from pydantic import Field, BaseModel, constr
+
+from python3_anticaptcha.core.enum import ProxyType, CaptchaTypeEnm, ResponseStatusEnm
+from python3_anticaptcha.core.config import APP_KEY
+
+
+class BaseAPIRequestSer(BaseModel):
+    clientKey: constr(min_length=32, max_length=32)
+
+
+class BaseAPIResponseSer(BaseModel):
+    errorId: int = Field(..., description="Error identifier.")
+    errorCode: str = Field(None, description="An error code.")
+    errorDescription: str = Field(None, description="Short description of the error.")
+
+
+class CreateTaskRequestSer(BaseAPIRequestSer):
+    task: Dict = Field(None, description="Task object.")
+    languagePool: str = Field("en", description="Sets workers' pool language. Only applies to image captchas.")
+    callbackUrl: str = Field(None, description="Web address where we can send the results of captcha task processing.")
+    softId: int = Field(APP_KEY, const=True)
+
+
+class CreateTaskRequestTaskSer(BaseModel):
+    type: CaptchaTypeEnm = Field(..., description="Captcha task type name")
+
+
+class ProxyDataOptionsSer(BaseModel):
+    proxyType: ProxyType = Field(..., description="Type of proxy")
+    proxyAddress: str = Field(
+        ...,
+        description="Proxy IP address ipv4/ipv6. No host names or IP addresses from local networks",
+    )
+    proxyPort: int = Field(..., description="Proxy port")
+
+
+class CreateTaskResponseSer(BaseAPIResponseSer):
+    taskId: int = Field(None, description="Task ID that you should later use in the `getTaskResult` method.")
+
+
+class GetTaskResultRequestSer(BaseAPIRequestSer):
+    taskId: int = Field(None, description="An identifier obtained in the createTask method.")
+
+
+class GetTaskResultResponseSer(BaseAPIResponseSer):
+    status: ResponseStatusEnm = Field(None, description="Captcha solving process status.")
+    solution: Dict = Field(None, description="Task result data. Different for each type of task.")
+    cost: float = Field(None, description="Cost of the task in USD.")
+    ip: str = Field(None, description="IP from which the task was created.")
+    createTime: int = Field(None, description="UNIX timestamp date of task creation.")
+    endTime: int = Field(None, description="UNIX timestamp date of task completion.")
+    solveCount: int = Field(None, description="Number of workers who tried to complete your task.")
+    taskId: int = Field(None, description="Task ID.")
+
+
+"""
+Captcha tasks serializers
+"""
+
+
+class TurnstileProxylessOptionsSer(CreateTaskRequestTaskSer):
+    websiteURL: str = Field(..., description="Address of a target web page. Can be located anywhere on the web site.")
+    websiteKey: str = Field(..., description="Website key")
+
+
+class TurnstileOptionsSer(TurnstileProxylessOptionsSer, ProxyDataOptionsSer):
+    pass
diff --git a/src/python3_anticaptcha/errors.py b/src/python3_anticaptcha/errors.py
new file mode 100644
index 0000000..374a6bd
--- /dev/null
+++ b/src/python3_anticaptcha/errors.py
@@ -0,0 +1,37 @@
+class AntiCaptchaApiException(Exception):
+    pass
+
+
+class DownloadError(AntiCaptchaApiException):
+    def __init__(self):
+        AntiCaptchaApiException.__init__(self, """\nОшибка при скачивании файла""")
+
+
+class ReadError(AntiCaptchaApiException):
+    def __init__(self, error):
+        AntiCaptchaApiException.__init__(
+            self,
+            """\nПораждается, при проблеме во время чтения сохранённого файла.
+													\n\t{0}""".format(
+                error
+            ),
+        )
+
+
+class ParamError(AntiCaptchaApiException):
+    def __init__(self, additional_info=None):
+        AntiCaptchaApiException.__init__(
+            self,
+            """\nПораждается, при передаче неверного параметра.""" + additional_info if additional_info else "\n",
+        )
+
+
+class IdGetError(AntiCaptchaApiException):
+    def __init__(self, server_answer):
+        AntiCaptchaApiException.__init__(
+            self,
+            """\n Пораждается при ошибке получения ID капчи от сервиса. Ответ сервера:\n
+											   {0}""".format(
+                server_answer
+            ),
+        )
diff --git a/python3_anticaptcha/get_answer.py b/src/python3_anticaptcha/get_answer.py
similarity index 93%
rename from python3_anticaptcha/get_answer.py
rename to src/python3_anticaptcha/get_answer.py
index 1755a14..8ad17d8 100644
--- a/python3_anticaptcha/get_answer.py
+++ b/src/python3_anticaptcha/get_answer.py
@@ -5,7 +5,7 @@
 import requests
 from requests.adapters import HTTPAdapter
 
-from python3_anticaptcha.config import attempts_generator, get_result_url
+from python3_anticaptcha.config import get_result_url, attempts_generator
 
 
 def get_sync_result(result_payload: dict, sleep_time: int, **kwargs) -> dict:
@@ -17,7 +17,7 @@ def get_sync_result(result_payload: dict, sleep_time: int, **kwargs) -> dict:
     session.verify = False
 
     attempts = attempts_generator()
-    for attempt in attempts:
+    for _ in attempts:
         captcha_response = session.post(get_result_url, json=result_payload, **kwargs).json()
 
         if captcha_response["errorId"] == 0:
@@ -37,7 +37,7 @@ async def get_async_result(result_payload: dict, sleep_time: int) -> dict:
     attempts = attempts_generator()
     # Send request for status of captcha solution.
     async with aiohttp.ClientSession() as session:
-        for attempt in attempts:
+        for _ in attempts:
             async with session.post(get_result_url, json=result_payload) as resp:
                 json_result = await resp.json()
                 # if there is no error, check CAPTCHA status
diff --git a/src/python3_anticaptcha/turnstile.py b/src/python3_anticaptcha/turnstile.py
new file mode 100644
index 0000000..b4c9582
--- /dev/null
+++ b/src/python3_anticaptcha/turnstile.py
@@ -0,0 +1,124 @@
+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
+
+
+class BaseTurnstile(BaseCaptcha):
+    """
+    The class is used to work with AntiCaptcha Turnstile method.
+
+    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:
+        >>> Turnstile(api_key="XXXXXXX...",
+        ...         captcha_type="TurnstileTaskProxyless",
+        ...         websiteURL="https://react-turnstile.vercel.app/",
+        ...         websiteKey="0xAAAAAAAABBBBBBBCCCCCC"
+        ...        ).captcha_handler()
+
+        >>> Turnstile(api_key="XXXXXXX...",
+        ...         captcha_type="TurnstileTask",
+        ...         websiteURL="https://react-turnstile.vercel.app/",
+        ...         websiteKey="0xAAAAAAAABBBBBBBCCCCCC",
+        ...         proxyType="http",
+        ...         proxyAddress="0.0.0.0",
+        ...         proxyPort=9988,
+        ...        ).captcha_handler()
+
+        >>> Turnstile(api_key="XXXXXXX...",
+        ...         captcha_type="TurnstileTask",
+        ...         websiteURL="https://react-turnstile.vercel.app/",
+        ...         websiteKey="0xAAAAAAAABBBBBBBCCCCCC",
+        ...         proxyType="http",
+        ...         proxyAddress="0.0.0.0",
+        ...         proxyPort=9988
+        ...        ).captcha_handler(proxyLogin="some_login",
+        ...                         proxyPassword="some_strong_password")
+
+    Notes:
+        https://anti-captcha.com/apidoc/task-types/TurnstileTask
+        https://anti-captcha.com/apidoc/task-types/TurnstileTaskProxyless
+    """
+
+    def __init__(
+        self,
+        api_key: str,
+        captcha_type: Union[CaptchaTypeEnm, str],
+        websiteURL: str,
+        websiteKey: str,
+        proxyType: Optional[Union[ProxyType, str]] = None,
+        proxyAddress: Optional[str] = None,
+        proxyPort: Optional[int] = None,
+        sleep_time: Optional[int] = 10,
+        **kwargs,
+    ):
+        super().__init__(api_key=api_key, captcha_type=captcha_type, sleep_time=sleep_time, **kwargs)
+
+        # validation of the received parameters
+        if self.captcha_type == CaptchaTypeEnm.TurnstileTask:
+            self.task_params = TurnstileOptionsSer(type=captcha_type, **locals()).dict()
+        elif self.captcha_type == CaptchaTypeEnm.TurnstileTaskProxyless:
+            self.task_params = TurnstileProxylessOptionsSer(type=captcha_type, **locals()).dict()
+        else:
+            raise ValueError(
+                f"""Invalid `captcha_type` parameter set for `{self.__class__.__name__}`,
+                available - {CaptchaTypeEnm.TurnstileTask.value, CaptchaTypeEnm.TurnstileTaskProxyless.value}"""
+            )
+
+
+class Turnstile(BaseTurnstile):
+    __doc__ = BaseTurnstile.__doc__
+
+    def captcha_handler(self, **additional_params) -> dict:
+        """
+        Synchronous method for captcha solving
+
+        Args:
+            additional_params: Some additional parameters that will be used in creating the task
+                                and will be passed to the payload under ``task`` key.
+                                Like ``proxyLogin``, ``proxyPassword`` and etc. - more info in service docs
+
+        Returns:
+            Full service response
+
+        Notes:
+            Check class docstirng for more info
+        """
+
+        self.task_params.update({**additional_params})
+        return self._processing_captcha()
+
+    async def aio_captcha_handler(self, **additional_params) -> dict:
+        """
+        Asynchronous method for captcha solving
+
+        Args:
+            additional_params: Some additional parameters that will be used in creating the task
+                                and will be passed to the payload under ``task`` key.
+                                Like ``proxyLogin``, ``proxyPassword`` and etc. - more info in service docs
+
+        Returns:
+            Full service response
+
+        Notes:
+            Check class docstirng for more info
+        """
+
+        self.task_params.update({**additional_params})
+        return await self._aio_processing_captcha()
diff --git a/src/requirements.txt b/src/requirements.txt
new file mode 100644
index 0000000..232cfd6
--- /dev/null
+++ b/src/requirements.txt
@@ -0,0 +1,4 @@
+requests>=2.21.0
+aiohttp==3.*
+tenacity==8.*
+pydantic==1.*
diff --git a/setup.py b/src/setup.py
similarity index 70%
rename from setup.py
rename to src/setup.py
index 605339c..bc4905a 100644
--- a/setup.py
+++ b/src/setup.py
@@ -1,9 +1,13 @@
 import io
 import os
 import sys
-from shutil import rmtree
+import shutil
+import logging
 
-from setuptools import setup, Command
+from setuptools import Command, setup
+from pkg_resources import parse_requirements
+
+from python3_anticaptcha.__version__ import __version__
 
 # Package meta-data.
 NAME = "python3-anticaptcha"
@@ -11,16 +15,17 @@
 URL = "https://github.com/AndreiDrang/python3-anticaptcha"
 EMAIL = "python-captcha@pm.me"
 AUTHOR = "AndreiDrang, redV0ID"
-REQUIRES_PYTHON = ">=3.6.0"
-VERSION = "1.7.2a"
-REQUIRED = ["requests>=2.21.0", "aiohttp==3.*", "pika==1.*"]
+REQUIRES_PYTHON = ">=3.7.0"
+VERSION = __version__
+with open("requirements.txt", "rt") as requirements_txt:
+    REQUIRED = [str(requirement) for requirement in parse_requirements(requirements_txt)]
 
 here = os.path.abspath(os.path.dirname(__file__))
 
 # Import the README and use it as the long-description.
 # Note: this will only work if 'README.md' is present in your MANIFEST.in file!
 try:
-    with io.open(os.path.join(here, "README.md"), encoding="utf-8") as f:
+    with io.open(os.path.join(here, "../README.md"), encoding="utf-8") as f:
         long_description = "\n" + f.read()
 except FileNotFoundError:
     long_description = DESCRIPTION
@@ -44,17 +49,19 @@ def finalize_options(self):
         pass
 
     def run(self):
-        try:
-            self.status("Removing previous builds…")
-            rmtree(os.path.join(here, "dist"))
-        except OSError:
-            pass
+        logging.info("Clean builds . . .")
+        shutil.rmtree("./dist/", ignore_errors=True)
+
+        logging.info("Building Source and Wheel distribution . . .")
+        os.system("python setup.py bdist_wheel")
+
+        logging.info("Uploading the package to PyPI via Twin . . .")
+        os.system("twine upload dist/* --verbose")
 
-        self.status("Building Source and Wheel distribution…")
-        os.system("{0} setup.py sdist bdist_wheel".format(sys.executable))
+        logging.info("🤖 Uploaded . . .")
 
-        self.status("Uploading the package to PyPI via Twine…")
-        os.system("twine upload dist/*")
+        logging.info("Clean builds . . .")
+        shutil.rmtree("./dist/")
 
         sys.exit()
 
@@ -91,13 +98,14 @@ def run(self):
         # Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers
         "License :: OSI Approved :: MIT License",
         "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
+        "Development Status :: 5 - Production/Stable",
         "Programming Language :: Python",
-        "Programming Language :: Python :: 3.6",
+        "Programming Language :: Python :: 3",
+        "Programming Language :: Python :: 3 :: Only",
         "Programming Language :: Python :: 3.7",
         "Programming Language :: Python :: 3.8",
         "Programming Language :: Python :: 3.9",
         "Programming Language :: Python :: 3.10",
-        "Development Status :: 5 - Production/Stable",
         "Framework :: AsyncIO",
         "Operating System :: Unix",
         "Operating System :: Microsoft :: Windows",
@@ -106,3 +114,4 @@ def run(self):
     # Build and upload package: python3 setup.py upload
     cmdclass={"upload": UploadCommand},
 )
+print("🤖 Success install ...")


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..d8e4390
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,53 @@
+name: Tests
+
+on:
+  push:
+    branches: [ "main", "release"]
+    paths:
+      - '.github/workflows/test.yml'
+      - 'src/**'
+      - 'Makefile'
+      - 'requirements.test.txt'
+  pull_request:
+    branches: [ "main", "release"]
+    paths:
+      - '.github/workflows/test.yml'
+      - 'src/**'
+      - 'Makefile'
+      - 'requirements.test.txt'
+  schedule:
+    - cron: "5 0 * * 1"
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    env:
+      API_KEY: ${{ secrets.API_KEY }}
+    strategy:
+      fail-fast: false
+      matrix:
+        python-version: ["3.7", "3.8", "3.9", "3.10"]
+
+    steps:
+      - uses: actions/checkout@v3
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v3
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - name: Install dependencies
+        run: |
+          pip install --upgrade pip
+          pip install -r requirements.test.txt
+          pip install -r src/requirements.txt
+
+      - name: Test
+        run: make tests
+
+      - name: Upload coverage to Codecov
+        uses: codecov/codecov-action@v3
+        with:
+          token: ${{ secrets.CODECOV_TOKEN }}
+          files: /home/runner/work/python3-anticaptcha/python3-anticaptcha/src/coverage/coverage.xml
+          fail_ci_if_error: true
+          verbose: true
diff --git a/.github/workflows/test_build.yml b/.github/workflows/test_build.yml
new file mode 100644
index 0000000..8fbd706
--- /dev/null
+++ b/.github/workflows/test_build.yml
@@ -0,0 +1,35 @@
+name: Test Build
+
+on:
+  push:
+    branches: [ "main", "release"]
+    paths:
+      - '.github/workflows/test_build.yml'
+      - 'src/**'
+      - 'Makefile'
+  pull_request:
+    branches: [ "main", "release"]
+    paths:
+      - '.github/workflows/test_build.yml'
+      - 'src/**'
+      - 'Makefile'
+
+jobs:
+  test_build:
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        python-version: ["3.7", "3.8", "3.9", "3.10"]
+
+    steps:
+    - uses: actions/checkout@v3
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v3
+      with:
+        python-version: ${{ matrix.python-version }}
+
+    - name: Local build checking
+      run: |
+        pip install twine wheel
+        cd src/ && python setup.py sdist bdist_wheel
diff --git a/requirements.test.txt b/requirements.test.txt
new file mode 100644
index 0000000..c4ec205
--- /dev/null
+++ b/requirements.test.txt
@@ -0,0 +1,3 @@
+pytest==7.*
+coverage==6.*
+pytest-asyncio==0.*
diff --git a/src/tests/__init__.py b/src/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/tests/conftest.py b/src/tests/conftest.py
new file mode 100755
index 0000000..e850201
--- /dev/null
+++ b/src/tests/conftest.py
@@ -0,0 +1,36 @@
+import os
+import time
+import random
+import string
+
+import pytest
+
+
+@pytest.fixture(scope="function")
+def delay():
+    time.sleep(1)
+
+
+@pytest.mark.usefixtures("delay")
+class BaseTest:
+    API_KEY = os.getenv("API_KEY", "ad9053f3182ca81755768608fa75")
+    sleep_time = 5
+
+    proxyAddress = "0.0.0.0"
+    proxyPort = 9999
+
+    @staticmethod
+    def get_random_string(length: int) -> str:
+        """
+        Method generate random string with set length
+
+        Args:
+            length: Len of generated string
+
+        Returns:
+            Random letter string
+        """
+        # choose from all lowercase letter
+        letters = string.ascii_lowercase
+        result_str = "".join(random.choice(letters) for _ in range(length))
+        return result_str
diff --git a/src/tests/test_core.py b/src/tests/test_core.py
new file mode 100644
index 0000000..90504c9
--- /dev/null
+++ b/src/tests/test_core.py
@@ -0,0 +1,62 @@
+from tenacity import AsyncRetrying
+from urllib3.util.retry import Retry
+
+from src.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
+
+
+class TestCore(BaseTest):
+    """
+    Success tests
+    """
+
+    def test_reties(self):
+        assert isinstance(RETRIES, Retry)
+
+    def test_async_reties(self):
+        assert isinstance(ASYNC_RETRIES, AsyncRetrying)
+
+    def test_create_base(self):
+        BaseCaptcha(
+            api_key=self.get_random_string(32),
+            captcha_type=CaptchaTypeEnm.Control,
+            request_url=BASE_REQUEST_URL,
+            sleep_time=self.sleep_time,
+        )
+
+    def test_aio_create_base(self):
+        BaseCaptcha(
+            api_key=self.get_random_string(32),
+            captcha_type=CaptchaTypeEnm.Control,
+            request_url=BASE_REQUEST_URL,
+            sleep_time=self.sleep_time,
+        )
+
+    def test_create_base_context(self):
+        with BaseCaptcha(
+            api_key=self.get_random_string(32),
+            captcha_type=CaptchaTypeEnm.Control,
+            request_url=BASE_REQUEST_URL,
+            sleep_time=self.sleep_time,
+        ) as instance:
+            pass
+
+    async def test_aio_create_base_context(self):
+        async with BaseCaptcha(
+            api_key=self.get_random_string(32),
+            captcha_type=CaptchaTypeEnm.Control,
+            request_url=BASE_REQUEST_URL,
+            sleep_time=self.sleep_time,
+        ) as instance:
+            pass
+
+
+class TestConfig(BaseTest):
+    def test_attempts_generator(self):
+        attempt = None
+        attempts = attempts_generator(amount=5)
+        for attempt in attempts:
+            assert isinstance(attempt, int)
+        assert attempt == 4
diff --git a/tests/main.py b/tests/main.py
deleted file mode 100644
index 108bb7d..0000000
--- a/tests/main.py
+++ /dev/null
@@ -1,27 +0,0 @@
-import os
-
-# 1. `export anticaptcha_key=274832f8168a36019895a1e1174777c0`
-
-
-class MainAntiCaptcha(object):
-    WRONG_QUEUE_ID = WRONG_TASK_ID = -1
-
-    def setup_class(self):
-        self.anticaptcha_key_fail = os.getenv("anticaptcha_key")[:5]
-        self.anticaptcha_key_true = os.getenv("anticaptcha_key")
-        self.server_ip = "85.255.8.26"
-        self.host = "https://pythoncaptcha.tech"
-        self.image_url = self.host + "/static/image/common_image_example/620626.png"
-
-        self.ERROR_RESPONSE_JSON = {
-            "errorId": 1,
-            "errorCode": 123,
-            "errorDescription": "String with data",
-        }
-        self.VALID_RESPONSE_JSON = {
-            "errorId": 0,
-            "errorCode": 0,
-            "taskId": 456,
-            "errorDescription": "String with data",
-        }
-        self.VALID_RESPONSE_RESULT_JSON = {"errorId": 0, "status": "ready"}
diff --git a/tests/test_Control.py b/tests/test_Control.py
deleted file mode 100644
index d127317..0000000
--- a/tests/test_Control.py
+++ /dev/null
@@ -1,507 +0,0 @@
-import random
-import inspect
-
-import pytest
-import requests_mock
-
-from tests.main import MainAntiCaptcha
-from python3_anticaptcha import AntiCaptchaControl, config
-
-
-class TestControl(MainAntiCaptcha):
-    WRONG_MODE = WRONG_CAPTCHA_TYPE = WRONG_QUEUE = "qwerty"
-    WRONG_SOFT_ID = "-1" + config.app_key
-    REPORT_ID = WRONG_QUEUE_ID = -1
-    QUEUE_STATUS_KEYS = ("waiting", "load", "bid", "speed", "total")
-    """
-    Params check
-    """
-
-    def test_params(self):
-        default_init_params = ["self", "anticaptcha_key"]
-        default_get_balance_params = ["self"]
-        default_app_stats_params = ["self", "softId", "mode"]
-        default_complaint_params = ["self", "reported_id", "captcha_type"]
-        default_queue_status_params = ["queue_id"]
-        # get aiocaptchacontrol init and other params
-        aio_init_params = inspect.getfullargspec(AntiCaptchaControl.aioAntiCaptchaControl.__init__)
-        aio_balance_params = inspect.getfullargspec(AntiCaptchaControl.aioAntiCaptchaControl.get_balance)
-        aio_app_stats_params = inspect.getfullargspec(AntiCaptchaControl.aioAntiCaptchaControl.get_app_stats)
-        aio_complaint_params = inspect.getfullargspec(
-            AntiCaptchaControl.aioAntiCaptchaControl.complaint_on_result
-        )
-        aio_queue_status_params = inspect.getfullargspec(
-            AntiCaptchaControl.aioAntiCaptchaControl.get_queue_status
-        )
-
-        # get captchacontrol init and other params
-        init_params = inspect.getfullargspec(AntiCaptchaControl.AntiCaptchaControl.__init__)
-        balance_params = inspect.getfullargspec(AntiCaptchaControl.AntiCaptchaControl.get_balance)
-        app_stats_params = inspect.getfullargspec(AntiCaptchaControl.AntiCaptchaControl.get_app_stats)
-        complaint_params = inspect.getfullargspec(AntiCaptchaControl.AntiCaptchaControl.complaint_on_result)
-        queue_status_params = inspect.getfullargspec(AntiCaptchaControl.AntiCaptchaControl.get_queue_status)
-        # check aio module params
-        assert default_init_params == aio_init_params[0]
-        assert default_get_balance_params == aio_balance_params[0]
-        assert default_app_stats_params == aio_app_stats_params[0]
-        assert default_complaint_params == aio_complaint_params[0]
-        assert default_queue_status_params == aio_queue_status_params[0]
-        # check sync module params
-        assert default_init_params == init_params[0]
-        assert default_get_balance_params == balance_params[0]
-        assert default_app_stats_params == app_stats_params[0]
-        assert default_complaint_params == complaint_params[0]
-        assert default_queue_status_params == queue_status_params[0]
-
-    """
-    Request payload test MOCK
-    """
-
-    def test_balance_payload(self):
-        control = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_true)
-        # check response type
-        assert isinstance(control, AntiCaptchaControl.AntiCaptchaControl)
-
-        with requests_mock.Mocker() as req_mock:
-            req_mock.post(AntiCaptchaControl.get_balance_url, json=self.ERROR_RESPONSE_JSON)
-            control.get_balance()
-
-        history = req_mock.request_history
-
-        assert len(history) == 1
-
-        request_payload = history[0].json()
-
-        # check all dict keys
-        assert [
-            "clientKey",
-        ] == list(request_payload.keys())
-        assert request_payload["clientKey"] == self.anticaptcha_key_true
-
-    def test_stats_payload(self):
-        control = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_true)
-        # check response type
-        assert isinstance(control, AntiCaptchaControl.AntiCaptchaControl)
-        mode = random.choice(AntiCaptchaControl.mods)
-
-        with requests_mock.Mocker() as req_mock:
-            req_mock.post(AntiCaptchaControl.get_app_stats_url, json=self.ERROR_RESPONSE_JSON)
-            control.get_app_stats(softId=config.app_key, mode=mode)
-
-        history = req_mock.request_history
-
-        assert len(history) == 1
-
-        request_payload = history[0].json()
-
-        # check all dict keys
-        assert ["clientKey", "softId", "mode"] == list(request_payload.keys())
-        assert request_payload["clientKey"] == self.anticaptcha_key_true
-        assert request_payload["softId"] == config.app_key
-        assert request_payload["mode"] == mode
-
-    def test_complaint_image_payload(self):
-        control = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_true)
-        # check response type
-        assert isinstance(control, AntiCaptchaControl.AntiCaptchaControl)
-        task_id = 123456
-
-        with requests_mock.Mocker() as req_mock:
-            req_mock.post(AntiCaptchaControl.incorrect_imagecaptcha_url, json=self.ERROR_RESPONSE_JSON)
-            control.complaint_on_result(
-                reported_id=task_id, captcha_type=AntiCaptchaControl.complaint_types[0]
-            )
-
-        history = req_mock.request_history
-
-        assert len(history) == 1
-
-        request_payload = history[0].json()
-
-        # check all dict keys
-        assert ["clientKey", "taskId"] == list(request_payload.keys())
-        assert request_payload["clientKey"] == self.anticaptcha_key_true
-        assert request_payload["taskId"] == task_id
-
-    def test_complaint_re_payload(self):
-        control = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_true)
-        # check response type
-        assert isinstance(control, AntiCaptchaControl.AntiCaptchaControl)
-        task_id = 123456
-
-        with requests_mock.Mocker() as req_mock:
-            req_mock.post(AntiCaptchaControl.incorrect_recaptcha_url, json=self.ERROR_RESPONSE_JSON)
-            control.complaint_on_result(
-                reported_id=task_id, captcha_type=AntiCaptchaControl.complaint_types[1]
-            )
-
-        history = req_mock.request_history
-
-        assert len(history) == 1
-
-        request_payload = history[0].json()
-
-        # check all dict keys
-        assert ["clientKey", "taskId"] == list(request_payload.keys())
-        assert request_payload["clientKey"] == self.anticaptcha_key_true
-        assert request_payload["taskId"] == task_id
-
-    def test_queue_payload(self):
-        queue_id = random.choice(AntiCaptchaControl.queue_ids)
-        with requests_mock.Mocker() as req_mock:
-            req_mock.post(AntiCaptchaControl.get_queue_status_url, json=self.ERROR_RESPONSE_JSON)
-            AntiCaptchaControl.AntiCaptchaControl.get_queue_status(queue_id)
-
-        history = req_mock.request_history
-
-        assert len(history) == 1
-
-        request_payload = history[0].json()
-
-        # check all dict keys
-        assert [
-            "queueId",
-        ] == list(request_payload.keys())
-        assert request_payload["queueId"] == queue_id
-
-    """
-    Response checking
-    """
-
-    def test_response_control(self):
-        control_captcha = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_fail)
-        # check response type
-        assert isinstance(control_captcha, AntiCaptchaControl.AntiCaptchaControl)
-
-    @pytest.mark.asyncio
-    async def test_response_aiocontrol(self):
-        control_captcha = AntiCaptchaControl.aioAntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_fail)
-        # check response type
-        assert isinstance(control_captcha, AntiCaptchaControl.aioAntiCaptchaControl)
-
-    """
-    Fail tests
-    """
-
-    def test_fail_balance(self):
-        captcha_control = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_fail)
-
-        response = captcha_control.get_balance()
-
-        assert isinstance(response, dict)
-
-        assert 1 == response["errorId"]
-
-    def test_fail_balance_context(self):
-        with AntiCaptchaControl.AntiCaptchaControl(
-            anticaptcha_key=self.anticaptcha_key_fail
-        ) as captcha_control:
-
-            response = captcha_control.get_balance()
-
-            assert isinstance(response, dict)
-
-            assert 1 == response["errorId"]
-
-    def test_fail_key_app_stats(self):
-        captcha_control = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_fail)
-
-        response = captcha_control.get_app_stats(softId=self.WRONG_SOFT_ID)
-
-        assert isinstance(response, dict)
-
-        assert 1 == response["errorId"]
-
-    def test_fail_app_stats_context(self):
-        with AntiCaptchaControl.AntiCaptchaControl(
-            anticaptcha_key=self.anticaptcha_key_fail
-        ) as captcha_control:
-
-            response = captcha_control.get_app_stats(softId=config.app_key)
-
-            assert isinstance(response, dict)
-
-            assert 1 == response["errorId"]
-
-    def test_fail_id_app_stats(self):
-        captcha_control = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_true)
-
-        response = captcha_control.get_app_stats(softId=self.WRONG_SOFT_ID)
-
-        assert isinstance(response, dict)
-
-        assert 1 == response["errorId"]
-
-    def test_fail_mode_app_stats(self):
-        captcha_control = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_true)
-        with pytest.raises(ValueError):
-            assert captcha_control.get_app_stats(softId=config.app_key, mode=self.WRONG_MODE)
-
-    def test_fail_key_complaint(self):
-        captcha_control = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_fail)
-
-        response = captcha_control.complaint_on_result(
-            reported_id=self.REPORT_ID, captcha_type=AntiCaptchaControl.complaint_types[0]
-        )
-
-        assert isinstance(response, dict)
-
-        assert 1 == response["errorId"]
-
-    def test_fail_id_complaint(self):
-        captcha_control = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_true)
-
-        response = captcha_control.complaint_on_result(
-            reported_id=self.REPORT_ID, captcha_type=AntiCaptchaControl.complaint_types[0]
-        )
-
-        assert isinstance(response, dict)
-
-        assert 16 == response["errorId"]
-
-    def test_fail_type_complaint(self):
-        captcha_control = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_fail)
-
-        with pytest.raises(ValueError):
-            assert captcha_control.complaint_on_result(
-                reported_id=self.REPORT_ID, captcha_type=self.WRONG_CAPTCHA_TYPE
-            )
-
-    def test_fail_id_queue_status(self):
-        captcha_control = AntiCaptchaControl.AntiCaptchaControl
-
-        with pytest.raises(ValueError):
-            assert captcha_control.get_queue_status(queue_id=self.WRONG_QUEUE_ID)
-
-    @pytest.mark.asyncio
-    async def test_fail_aiobalance(self):
-        captcha_control = AntiCaptchaControl.aioAntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_fail)
-
-        response = await captcha_control.get_balance()
-
-        assert isinstance(response, dict)
-
-        assert 1 == response["errorId"]
-
-    @pytest.mark.asyncio
-    async def test_fail_aiobalance_context(self):
-        with AntiCaptchaControl.aioAntiCaptchaControl(
-            anticaptcha_key=self.anticaptcha_key_fail
-        ) as captcha_control:
-
-            response = await captcha_control.get_balance()
-
-            assert isinstance(response, dict)
-
-            assert 1 == response["errorId"]
-
-    @pytest.mark.asyncio
-    async def test_fail_aioapp_stats_context(self):
-        with AntiCaptchaControl.aioAntiCaptchaControl(
-            anticaptcha_key=self.anticaptcha_key_fail
-        ) as captcha_control:
-
-            response = await captcha_control.get_app_stats(softId=config.app_key)
-
-            assert isinstance(response, dict)
-
-            assert 1 == response["errorId"]
-
-    @pytest.mark.asyncio
-    async def test_fail_aiokey_app_stats(self):
-        captcha_control = AntiCaptchaControl.aioAntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_fail)
-
-        response = await captcha_control.get_app_stats(softId=self.WRONG_SOFT_ID)
-
-        assert isinstance(response, dict)
-
-        assert 1 == response["errorId"]
-
-    @pytest.mark.asyncio
-    async def test_fail_aioid_app_stats(self):
-        captcha_control = AntiCaptchaControl.aioAntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_true)
-
-        response = await captcha_control.get_app_stats(softId=self.WRONG_SOFT_ID)
-
-        assert isinstance(response, dict)
-
-        assert 1 == response["errorId"]
-
-    @pytest.mark.asyncio
-    async def test_fail_aiomode_app_stats(self):
-        with AntiCaptchaControl.aioAntiCaptchaControl(
-            anticaptcha_key=self.anticaptcha_key_fail
-        ) as captcha_control:
-
-            response = await captcha_control.get_balance()
-
-            assert isinstance(response, dict)
-
-            assert 1 == response["errorId"]
-
-    @pytest.mark.asyncio
-    async def test_fail_aiokey_complaint(self):
-        captcha_control = AntiCaptchaControl.aioAntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_fail)
-
-        response = await captcha_control.complaint_on_result(
-            reported_id=self.REPORT_ID, captcha_type=AntiCaptchaControl.complaint_types[0]
-        )
-
-        assert isinstance(response, dict)
-
-        assert 1 == response["errorId"]
-
-    @pytest.mark.asyncio
-    async def test_fail_aioid_complaint(self):
-        captcha_control = AntiCaptchaControl.aioAntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_true)
-
-        response = await captcha_control.complaint_on_result(
-            reported_id=self.REPORT_ID, captcha_type=AntiCaptchaControl.complaint_types[0]
-        )
-
-        assert isinstance(response, dict)
-
-        assert 16 == response["errorId"]
-
-    @pytest.mark.asyncio
-    async def test_fail_aiotype_complaint(self):
-        captcha_control = AntiCaptchaControl.aioAntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_fail)
-
-        with pytest.raises(ValueError):
-            assert await captcha_control.complaint_on_result(
-                reported_id=self.REPORT_ID, captcha_type=self.WRONG_CAPTCHA_TYPE
-            )
-
-    @pytest.mark.asyncio
-    async def test_fail_aioid_queue_status(self):
-        captcha_control = AntiCaptchaControl.aioAntiCaptchaControl
-
-        with pytest.raises(ValueError):
-            assert await captcha_control.get_queue_status(queue_id=self.WRONG_QUEUE_ID)
-
-    def test_fail_spend_stats_queue(self):
-        captcha_control = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_true)
-        with pytest.raises(ValueError):
-            captcha_control.get_spend_stats(queue=self.WRONG_QUEUE)
-
-    @pytest.mark.asyncio
-    async def test_fail_aiospend_stats_queue(self):
-        captcha_control = AntiCaptchaControl.aioAntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_true)
-        with pytest.raises(ValueError):
-            await captcha_control.get_spend_stats(queue=self.WRONG_QUEUE)
-
-    """
-    True tests
-    """
-
-    def test_true_mode_app_stats(self):
-        captcha_control = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_true)
-
-        for mode in AntiCaptchaControl.mods:
-            response = captcha_control.get_app_stats(softId=config.app_key, mode=mode)
-
-            assert isinstance(response, dict)
-
-            assert 0 == response["errorId"]
-
-    def test_true_mode_app_stats_context(self):
-        with AntiCaptchaControl.AntiCaptchaControl(
-            anticaptcha_key=self.anticaptcha_key_true
-        ) as captcha_control:
-
-            for mode in AntiCaptchaControl.mods:
-                response = captcha_control.get_app_stats(softId=config.app_key, mode=mode)
-
-                assert isinstance(response, dict)
-
-                assert 0 == response["errorId"]
-
-    def test_true_balance(self):
-        captcha_control = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_true)
-
-        response = captcha_control.get_balance()
-
-        assert isinstance(response, dict)
-
-        assert 0 == response["errorId"]
-
-    def test_true_balance_context(self):
-        with AntiCaptchaControl.AntiCaptchaControl(
-            anticaptcha_key=self.anticaptcha_key_true
-        ) as captcha_control:
-
-            response = captcha_control.get_balance()
-
-            assert isinstance(response, dict)
-
-            assert 0 == response["errorId"]
-
-    def test_true_queue_status(self):
-        captcha_control = AntiCaptchaControl.AntiCaptchaControl
-        for queue_id in AntiCaptchaControl.queue_ids:
-            response = captcha_control.get_queue_status(queue_id=queue_id)
-
-            assert isinstance(response, dict)
-
-            assert self.QUEUE_STATUS_KEYS == tuple(response.keys())
-
-    def test_true_spend_stats(self):
-        captcha_control = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_true)
-        response = captcha_control.get_spend_stats()
-
-        assert isinstance(response, dict)
-
-        assert ("errorId", "data") == tuple(response.keys())
-
-    def test_true_spend_stats_queues(self):
-        queues = AntiCaptchaControl.queues_names
-
-        captcha_control = AntiCaptchaControl.AntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_true)
-        for queue in queues:
-            response = captcha_control.get_spend_stats(queue=queue)
-
-            assert isinstance(response, dict)
-
-            assert ("errorId", "data") == tuple(response.keys())
-
-    def test_true_spend_stats_context(self):
-        with AntiCaptchaControl.AntiCaptchaControl(
-            anticaptcha_key=self.anticaptcha_key_true
-        ) as captcha_control:
-            response = captcha_control.get_spend_stats()
-
-        assert isinstance(response, dict)
-
-        assert ("errorId", "data") == tuple(response.keys())
-
-    @pytest.mark.asyncio
-    async def test_true_aiospend_stats(self):
-        captcha_control = AntiCaptchaControl.aioAntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_true)
-        response = await captcha_control.get_spend_stats()
-
-        assert isinstance(response, dict)
-
-        assert ("errorId", "data") == tuple(response.keys())
-
-    @pytest.mark.asyncio
-    async def test_true_aiospend_stats_context(self):
-        with AntiCaptchaControl.aioAntiCaptchaControl(
-            anticaptcha_key=self.anticaptcha_key_true
-        ) as captcha_control:
-            response = await captcha_control.get_spend_stats()
-
-        assert isinstance(response, dict)
-
-        assert ("errorId", "data") == tuple(response.keys())
-
-    @pytest.mark.asyncio
-    async def test_true_aiospend_stats_queues(self):
-        queues = AntiCaptchaControl.queues_names
-
-        captcha_control = AntiCaptchaControl.aioAntiCaptchaControl(anticaptcha_key=self.anticaptcha_key_true)
-        for queue in queues:
-            response = await captcha_control.get_spend_stats(queue=queue)
-
-            assert isinstance(response, dict)
-
-            assert ("errorId", "data") == tuple(response.keys())
diff --git a/tests/test_CustomResultHandler.py b/tests/test_CustomResultHandler.py
deleted file mode 100644
index f15364c..0000000
--- a/tests/test_CustomResultHandler.py
+++ /dev/null
@@ -1,142 +0,0 @@
-import inspect
-
-import pytest
-
-from tests.main import MainAntiCaptcha
-from python3_anticaptcha import CustomResultHandler
-
-
-class TestAntiCaptcha(MainAntiCaptcha):
-    """
-    Params check
-    """
-
-    def test_result_handler_params(self):
-        default_init_params = ["self", "anticaptcha_key", "sleep_time"]
-        default_handler_params = ["self", "task_id"]
-        # get customcaptcha init and task_handler params
-        aioinit_params = inspect.getfullargspec(CustomResultHandler.aioCustomResultHandler.__init__)
-        aiohandler_params = inspect.getfullargspec(CustomResultHandler.aioCustomResultHandler.task_handler)
-
-        # get customcaptcha init and task_handler params
-        init_params = inspect.getfullargspec(CustomResultHandler.CustomResultHandler.__init__)
-        handler_params = inspect.getfullargspec(CustomResultHandler.CustomResultHandler.task_handler)
-        # check aio module params
-        assert default_init_params == aioinit_params[0]
-        assert default_handler_params == aiohandler_params[0]
-        # check sync module params
-        assert default_init_params == init_params[0]
-        assert default_handler_params == handler_params[0]
-
-    """
-    Response checking
-    """
-
-    def test_response_result_handler(self):
-        # prepare client
-        custom_result = CustomResultHandler.CustomResultHandler(anticaptcha_key=self.anticaptcha_key_true)
-        # check response type
-        assert isinstance(custom_result, CustomResultHandler.CustomResultHandler)
-
-        response = custom_result.task_handler(task_id=self.WRONG_TASK_ID)
-        # check response type
-        assert isinstance(response, dict)
-        # check all dict keys
-        assert ["errorId", "errorCode", "errorDescription", "taskId"] == list(response.keys())
-
-    @pytest.mark.asyncio
-    async def test_response_aioresult_handler(self):
-        # prepare client
-        custom_result = CustomResultHandler.aioCustomResultHandler(anticaptcha_key=self.anticaptcha_key_true)
-        # check response type
-        assert isinstance(custom_result, CustomResultHandler.aioCustomResultHandler)
-
-        response = await custom_result.task_handler(task_id=self.WRONG_TASK_ID)
-        # check response type
-        assert isinstance(response, dict)
-        # check all dict keys
-        assert ["errorId", "errorCode", "errorDescription", "taskId"] == list(response.keys())
-
-    """
-    Fail tests
-    """
-
-    def test_fail_result_handler(self):
-        # prepare client
-        custom_result = CustomResultHandler.CustomResultHandler(anticaptcha_key=self.anticaptcha_key_fail)
-
-        response = custom_result.task_handler(task_id=self.WRONG_TASK_ID)
-        # check error code
-        assert 1 == response["errorId"]
-
-    def test_fail_result_handler_context(self):
-        # prepare client
-        with CustomResultHandler.CustomResultHandler(
-            anticaptcha_key=self.anticaptcha_key_fail
-        ) as custom_result:
-
-            response = custom_result.task_handler(task_id=self.WRONG_TASK_ID)
-            # check error code
-            assert 1 == response["errorId"]
-
-    @pytest.mark.asyncio
-    async def test_fail_aioresult_handler(self):
-        # prepare client
-        custom_result = CustomResultHandler.aioCustomResultHandler(anticaptcha_key=self.anticaptcha_key_fail)
-
-        response = await custom_result.task_handler(task_id=self.WRONG_TASK_ID)
-        # check error code
-        assert 1 == response["errorId"]
-
-    @pytest.mark.asyncio
-    async def test_fail_aioresult_handler_context(self):
-        # prepare client
-        with CustomResultHandler.aioCustomResultHandler(
-            anticaptcha_key=self.anticaptcha_key_fail
-        ) as custom_result:
-
-            response = await custom_result.task_handler(task_id=self.WRONG_TASK_ID)
-            # check error code
-            assert 1 == response["errorId"]
-
-    """
-    True tests
-    """
-
-    def test_true_result_handler(self):
-        # prepare client
-        custom_result = CustomResultHandler.CustomResultHandler(anticaptcha_key=self.anticaptcha_key_true)
-
-        response = custom_result.task_handler(task_id=self.WRONG_TASK_ID)
-        # check error code
-        assert 16 == response["errorId"]
-
-    def test_true_result_handler_context(self):
-        # prepare client
-        with CustomResultHandler.CustomResultHandler(
-            anticaptcha_key=self.anticaptcha_key_true
-        ) as custom_result:
-
-            response = custom_result.task_handler(task_id=self.WRONG_TASK_ID)
-            # check error code
-            assert 16 == response["errorId"]
-
-    @pytest.mark.asyncio
-    async def test_true_aioresult_handler(self):
-        # prepare client
-        custom_result = CustomResultHandler.aioCustomResultHandler(anticaptcha_key=self.anticaptcha_key_true)
-
-        response = await custom_result.task_handler(task_id=self.WRONG_TASK_ID)
-        # check error code
-        assert 16 == response["errorId"]
-
-    @pytest.mark.asyncio
-    async def test_true_aioresult_handler_context(self):
-        # prepare client
-        with CustomResultHandler.aioCustomResultHandler(
-            anticaptcha_key=self.anticaptcha_key_true
-        ) as custom_result:
-
-            response = await custom_result.task_handler(task_id=self.WRONG_TASK_ID)
-            # check error code
-            assert 16 == response["errorId"]
diff --git a/tests/test_ImageToText.py b/tests/test_ImageToText.py
deleted file mode 100644
index 7166530..0000000
--- a/tests/test_ImageToText.py
+++ /dev/null
@@ -1,198 +0,0 @@
-import inspect
-
-import pytest
-import requests_mock
-
-from tests.main import MainAntiCaptcha
-from python3_anticaptcha import ImageToTextTask, config
-
-
-class TestImageToTextCaptcha(MainAntiCaptcha):
-    WRONG_SAVE_FORMAT = "qwerty"
-
-    """
-    Params check
-    """
-
-    def test_customcatpcha_params(self):
-        default_init_params = [
-            "self",
-            "anticaptcha_key",
-            "sleep_time",
-            "save_format",
-            "callbackUrl",
-        ]
-        default_handler_params = ["self", "captcha_link", "captcha_file", "captcha_base64"]
-        # get customcaptcha init and captcha_handler params
-        aioinit_params = inspect.getfullargspec(ImageToTextTask.aioImageToTextTask.__init__)
-        aiohandler_params = inspect.getfullargspec(ImageToTextTask.aioImageToTextTask.captcha_handler)
-
-        # get customcaptcha init and captcha_handler params
-        init_params = inspect.getfullargspec(ImageToTextTask.ImageToTextTask.__init__)
-        handler_params = inspect.getfullargspec(ImageToTextTask.ImageToTextTask.captcha_handler)
-        # check aio module params
-        assert default_init_params == aioinit_params[0]
-        assert default_handler_params == aiohandler_params[0]
-        # check sync module params
-        assert default_init_params == init_params[0]
-        assert default_handler_params == handler_params[0]
-
-    def test_create_task_payload(self):
-        customcaptcha = ImageToTextTask.ImageToTextTask(anticaptcha_key=self.anticaptcha_key_fail)
-        # check response type
-        assert isinstance(customcaptcha, ImageToTextTask.ImageToTextTask)
-
-        with requests_mock.Mocker() as req_mock:
-            req_mock.register_uri("GET", self.image_url, json=self.VALID_RESPONSE_JSON)
-            req_mock.register_uri("POST", config.create_task_url, json=self.ERROR_RESPONSE_JSON)
-            customcaptcha.captcha_handler(captcha_link=self.image_url)
-
-        history = req_mock.request_history
-
-        assert len(history) == 2
-
-        request_payload = history[1].json()
-
-        # check all dict keys
-        assert ["clientKey", "task", "softId"] == list(request_payload.keys())
-        assert request_payload["softId"] == config.app_key
-        assert ["type", "body"] == list(request_payload["task"].keys())
-        assert request_payload["task"]["type"] == "ImageToTextTask"
-
-    def test_get_result_payload(self):
-        customcaptcha = ImageToTextTask.ImageToTextTask(anticaptcha_key=self.anticaptcha_key_fail)
-        # check response type
-        assert isinstance(customcaptcha, ImageToTextTask.ImageToTextTask)
-
-        with requests_mock.Mocker() as req_mock:
-            req_mock.register_uri("GET", self.image_url, json=self.VALID_RESPONSE_JSON)
-            req_mock.register_uri("POST", config.create_task_url, json=self.VALID_RESPONSE_JSON)
-            req_mock.register_uri("POST", config.get_result_url, json=self.VALID_RESPONSE_RESULT_JSON)
-            customcaptcha.captcha_handler(captcha_link=self.image_url)
-
-        history = req_mock.request_history
-
-        assert len(history) == 3
-
-        request_payload = history[2].json()
-
-        # check all dict keys
-        assert ["clientKey", "taskId"] == list(request_payload.keys())
-        assert request_payload["taskId"] == self.VALID_RESPONSE_JSON["taskId"]
-
-    """
-    Response checking
-    """
-
-    def test_response_imagecaptcha(self):
-        imagecaptcha = ImageToTextTask.ImageToTextTask(anticaptcha_key=self.anticaptcha_key_fail)
-        # check response type
-        assert isinstance(imagecaptcha, ImageToTextTask.ImageToTextTask)
-
-        response = imagecaptcha.captcha_handler(captcha_link=self.image_url)
-        # check response type
-        assert isinstance(response, dict)
-        # check all dict keys
-        assert ["errorId", "errorCode", "errorDescription"] == list(response.keys())
-
-    @pytest.mark.asyncio
-    async def test_response_aioimagecaptcha(self):
-        imagecaptcha = ImageToTextTask.aioImageToTextTask(anticaptcha_key=self.anticaptcha_key_fail)
-        # check response type
-        assert isinstance(imagecaptcha, ImageToTextTask.aioImageToTextTask)
-
-        response = await imagecaptcha.captcha_handler(captcha_link=self.image_url)
-
-        # check response type
-        assert isinstance(response, dict)
-        # check all dict keys
-        assert ["errorId", "errorCode", "errorDescription"] == list(response.keys())
-
-    """
-    Fail tests
-    """
-
-    def test_fail_imagecaptcha_value(self):
-        with pytest.raises(ValueError):
-            assert ImageToTextTask.ImageToTextTask(
-                anticaptcha_key=self.anticaptcha_key_fail, save_format=self.WRONG_SAVE_FORMAT
-            )
-
-    def test_fail_imagecaptcha_const(self):
-        imagecaptcha = ImageToTextTask.ImageToTextTask(
-            anticaptcha_key=self.anticaptcha_key_fail, save_format=ImageToTextTask.SAVE_FORMATS[0]
-        )
-
-        response = imagecaptcha.captcha_handler(captcha_link=self.image_url)
-
-        assert 1 == response["errorId"]
-
-    def test_fail_imagecaptcha_const_context(self):
-        with ImageToTextTask.ImageToTextTask(
-            anticaptcha_key=self.anticaptcha_key_fail, save_format=ImageToTextTask.SAVE_FORMATS[0]
-        ) as imagecaptcha:
-
-            response = imagecaptcha.captcha_handler(captcha_link=self.image_url)
-
-            assert 1 == response["errorId"]
-
-    def test_fail_imagecaptcha_temp(self):
-        imagecaptcha = ImageToTextTask.ImageToTextTask(
-            anticaptcha_key=self.anticaptcha_key_fail, save_format=ImageToTextTask.SAVE_FORMATS[1]
-        )
-
-        response = imagecaptcha.captcha_handler(captcha_link=self.image_url)
-
-        assert 1 == response["errorId"]
-
-    def test_fail_imagecaptcha_temp_context(self):
-        with ImageToTextTask.ImageToTextTask(
-            anticaptcha_key=self.anticaptcha_key_fail, save_format=ImageToTextTask.SAVE_FORMATS[1]
-        ) as imagecaptcha:
-
-            response = imagecaptcha.captcha_handler(captcha_link=self.image_url)
-
-            assert 1 == response["errorId"]
-
-    @pytest.mark.asyncio
-    async def test_fail_aioimagecaptcha_value(self):
-        with pytest.raises(ValueError):
-            assert await ImageToTextTask.ImageToTextTask(
-                anticaptcha_key=self.anticaptcha_key_fail, save_format=self.WRONG_SAVE_FORMAT
-            )
-
-    @pytest.mark.asyncio
-    async def test_fail_aioimagecaptcha_temp(self):
-        imagecaptcha = ImageToTextTask.aioImageToTextTask(
-            anticaptcha_key=self.anticaptcha_key_fail, save_format=ImageToTextTask.SAVE_FORMATS[1]
-        )
-        response = await imagecaptcha.captcha_handler(captcha_link=self.image_url)
-        assert 1 == response["errorId"]
-
-    @pytest.mark.asyncio
-    async def test_fail_aioimagecaptcha_temp_context(self):
-        with ImageToTextTask.aioImageToTextTask(
-            anticaptcha_key=self.anticaptcha_key_fail, save_format=ImageToTextTask.SAVE_FORMATS[1]
-        ) as imagecaptcha:
-            response = await imagecaptcha.captcha_handler(captcha_link=self.image_url)
-            assert 1 == response["errorId"]
-
-    @pytest.mark.asyncio
-    async def test_fail_aioimagecaptcha_const(self):
-        imagecaptcha = ImageToTextTask.aioImageToTextTask(
-            anticaptcha_key=self.anticaptcha_key_fail, save_format=ImageToTextTask.SAVE_FORMATS[0]
-        )
-        response = await imagecaptcha.captcha_handler(captcha_link=self.image_url)
-        assert 1 == response["errorId"]
-
-    @pytest.mark.asyncio
-    async def test_fail_aioimagecaptcha_const_context(self):
-        with ImageToTextTask.aioImageToTextTask(
-            anticaptcha_key=self.anticaptcha_key_fail, save_format=ImageToTextTask.SAVE_FORMATS[0]
-        ) as imagecaptcha:
-            response = await imagecaptcha.captcha_handler(captcha_link=self.image_url)
-            assert 1 == response["errorId"]
-
-    """
-    True tests
-    """
diff --git a/tests/test_NoCaptcha.py b/tests/test_NoCaptcha.py
deleted file mode 100644
index d38d202..0000000
--- a/tests/test_NoCaptcha.py
+++ /dev/null
@@ -1,60 +0,0 @@
-import inspect
-
-import pytest
-import requests_mock
-
-from tests.main import MainAntiCaptcha
-from python3_anticaptcha import NoCaptchaTask, config
-
-
-class TestNoCaptcha(MainAntiCaptcha):
-    WEBSITE_URL = "https://www.google.com/recaptcha/api2/demo"
-    WEBSITE_KEY = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
-
-    """
-    Params check
-    """
-
-    def test_nocaptcha_params(self):
-        default_init_params = ["self", "anticaptcha_key", "sleep_time", "callbackUrl"]
-        default_handler_params = ["self", "websiteURL", "websiteKey", "recaptchaDataSValue"]
-        # get customcaptcha init and captcha_handler params
-        aioinit_params = inspect.getfullargspec(NoCaptchaTask.aioNoCaptchaTask.__init__)
-        aiohandler_params = inspect.getfullargspec(NoCaptchaTask.aioNoCaptchaTask.captcha_handler)
-
-        # get customcaptcha init and captcha_handler params
-        init_params = inspect.getfullargspec(NoCaptchaTask.NoCaptchaTask.__init__)
-        handler_params = inspect.getfullargspec(NoCaptchaTask.NoCaptchaTask.captcha_handler)
-        # check aio module params
-        assert default_init_params == aioinit_params[0]
-        assert default_handler_params == aiohandler_params[0]
-        # check sync module params
-        assert default_init_params == init_params[0]
-        assert default_handler_params == handler_params[0]
-
-    """
-    Request payload test MOCK
-    """
-
-    def test_create_task_payload(self):
-        no_captcha = NoCaptchaTask.NoCaptchaTask(anticaptcha_key=self.anticaptcha_key_fail)
-        # check response type
-        assert isinstance(no_captcha, NoCaptchaTask.NoCaptchaTask)
-
-        with requests_mock.Mocker() as req_mock:
-            req_mock.post(config.create_task_url, json=self.ERROR_RESPONSE_JSON)
-            no_captcha.captcha_handler(websiteURL=self.WEBSITE_URL, websiteKey=self.WEBSITE_KEY)
-
-        history = req_mock.request_history
-
-        assert len(history) == 1
-
-        request_payload = history[0].json()
-
-        # check all dict keys
-        assert ["clientKey", "task", "softId"] == list(request_payload.keys())
-        assert request_payload["softId"] == config.app_key
-        assert ["type", "websiteURL", "websiteKey", "recaptchaDataSValue"] == list(
-            request_payload["task"].keys()
-        )
-        assert request_payload["task"]["type"] == "NoCaptchaTask"
diff --git a/tests/test_Recaptcha3Proxyless.py b/tests/test_Recaptcha3Proxyless.py
deleted file mode 100644
index ecf3923..0000000
--- a/tests/test_Recaptcha3Proxyless.py
+++ /dev/null
@@ -1,133 +0,0 @@
-import inspect
-
-import pytest
-
-from tests.main import MainAntiCaptcha
-from python3_anticaptcha import ReCaptchaV3TaskProxyless
-
-
-class TestAntiCaptcha(MainAntiCaptcha):
-    WRONG_MIN_SCORE = 0.1
-    """
-    Params check
-    """
-
-    def test_recaptcha3_params(self):
-        default_init_params = ["self", "anticaptcha_key", "sleep_time", "callbackUrl"]
-        default_handler_params = ["self", "websiteURL", "websiteKey", "minScore", "pageAction"]
-        # get customcaptcha init and captcha_handler params
-        aioinit_params = inspect.getfullargspec(ReCaptchaV3TaskProxyless.aioReCaptchaV3TaskProxyless.__init__)
-        aiohandler_params = inspect.getfullargspec(
-            ReCaptchaV3TaskProxyless.aioReCaptchaV3TaskProxyless.captcha_handler
-        )
-
-        # get customcaptcha init and captcha_handler params
-        init_params = inspect.getfullargspec(ReCaptchaV3TaskProxyless.ReCaptchaV3TaskProxyless.__init__)
-        handler_params = inspect.getfullargspec(
-            ReCaptchaV3TaskProxyless.ReCaptchaV3TaskProxyless.captcha_handler
-        )
-        # check aio module params
-        assert default_init_params == aioinit_params[0]
-        assert default_handler_params == aiohandler_params[0]
-        # check sync module params
-        assert default_init_params == init_params[0]
-        assert default_handler_params == handler_params[0]
-
-    """
-    Response checking
-    """
-
-    def test_response_recaptcha3(self):
-        recaptcha = ReCaptchaV3TaskProxyless.ReCaptchaV3TaskProxyless(
-            anticaptcha_key=self.anticaptcha_key_fail
-        )
-        # check response type
-        assert isinstance(recaptcha, ReCaptchaV3TaskProxyless.ReCaptchaV3TaskProxyless)
-
-        response = recaptcha.captcha_handler(
-            websiteURL="https://www.google.com/recaptcha/api2/demo",
-            websiteKey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
-            minScore=ReCaptchaV3TaskProxyless.MIN_SCORES[0],
-            pageAction="login_test",
-        )
-        # check response type
-        assert isinstance(response, dict)
-        # check all dict keys
-        assert ["errorId", "errorCode", "errorDescription"] == list(response.keys())
-
-    @pytest.mark.asyncio
-    async def test_response_aiorecaptcha3(self):
-        recaptcha = ReCaptchaV3TaskProxyless.aioReCaptchaV3TaskProxyless(
-            anticaptcha_key=self.anticaptcha_key_fail
-        )
-        # check response type
-        assert isinstance(recaptcha, ReCaptchaV3TaskProxyless.aioReCaptchaV3TaskProxyless)
-
-        response = await recaptcha.captcha_handler(
-            websiteURL="https://www.google.com/recaptcha/api2/demo",
-            websiteKey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
-            minScore=ReCaptchaV3TaskProxyless.MIN_SCORES[0],
-            pageAction="login_test",
-        )
-        # check response type
-        assert isinstance(response, dict)
-        # check all dict keys
-        assert ["errorId", "errorCode", "errorDescription"] == list(response.keys())
-
-    """
-    Fail tests
-    """
-
-    def test_fail_recaptcha3_value(self):
-        recaptcha = ReCaptchaV3TaskProxyless.ReCaptchaV3TaskProxyless(
-            anticaptcha_key=self.anticaptcha_key_fail
-        )
-        with pytest.raises(ValueError):
-            assert recaptcha.captcha_handler(
-                websiteURL="https://www.google.com/recaptcha/api2/demo",
-                websiteKey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
-                minScore=0.1,
-                pageAction="login_test",
-            )
-
-    def test_fail_recaptcha3_value_context(self):
-        with ReCaptchaV3TaskProxyless.ReCaptchaV3TaskProxyless(
-            anticaptcha_key=self.anticaptcha_key_fail
-        ) as recaptcha:
-            with pytest.raises(ValueError):
-                assert recaptcha.captcha_handler(
-                    websiteURL="https://www.google.com/recaptcha/api2/demo",
-                    websiteKey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
-                    minScore=0.1,
-                    pageAction="login_test",
-                )
-
-    @pytest.mark.asyncio
-    async def test_fail_aiorecaptcha3_value(self):
-        recaptcha = ReCaptchaV3TaskProxyless.aioReCaptchaV3TaskProxyless(
-            anticaptcha_key=self.anticaptcha_key_fail
-        )
-        with pytest.raises(ValueError):
-            assert await recaptcha.captcha_handler(
-                websiteURL="https://www.google.com/recaptcha/api2/demo",
-                websiteKey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
-                minScore=0.1,
-                pageAction="login_test",
-            )
-
-    @pytest.mark.asyncio
-    async def test_fail_aiorecaptcha3_value_context(self):
-        with ReCaptchaV3TaskProxyless.aioReCaptchaV3TaskProxyless(
-            anticaptcha_key=self.anticaptcha_key_fail
-        ) as recaptcha:
-            with pytest.raises(ValueError):
-                assert await recaptcha.captcha_handler(
-                    websiteURL="https://www.google.com/recaptcha/api2/demo",
-                    websiteKey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
-                    minScore=0.1,
-                    pageAction="login_test",
-                )
-
-    """
-    True tests
-    """