├── bot ├── core │ ├── __init__.py │ ├── headers.py │ ├── registrator.py │ └── claimer.py ├── __init__.py ├── config │ ├── __init__.py │ ├── proxies.txt │ └── config.py ├── exceptions │ └── __init__.py └── utils │ ├── __init__.py │ ├── logger.py │ └── launcher.py ├── .env-example ├── .github └── images │ └── demo.png ├── START.bat ├── START.sh ├── main.py ├── Dockerfile ├── INSTALL.bat ├── docker-compose.yml ├── requirements.txt ├── INSTALL.sh ├── .gitignore ├── README.md └── README-RU.md /bot/core/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bot/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.0' 2 | -------------------------------------------------------------------------------- /bot/config/__init__.py: -------------------------------------------------------------------------------- 1 | from .config import settings 2 | -------------------------------------------------------------------------------- /bot/exceptions/__init__.py: -------------------------------------------------------------------------------- 1 | class InvalidSession(BaseException): 2 | ... 3 | -------------------------------------------------------------------------------- /.env-example: -------------------------------------------------------------------------------- 1 | API_ID= 2 | API_HASH= 3 | 4 | SLEEP_BETWEEN_START= 5 | USE_PROXY_FROM_FILE= 6 | -------------------------------------------------------------------------------- /.github/images/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexell/ClaytonBOT/HEAD/.github/images/demo.png -------------------------------------------------------------------------------- /bot/config/proxies.txt: -------------------------------------------------------------------------------- 1 | type://user:pass@ip:port 2 | type://user:pass:ip:port 3 | type://ip:port:user:pass 4 | type://ip:port@user:pass 5 | type://ip:port -------------------------------------------------------------------------------- /START.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo Activating virtual environment... 3 | call venv\Scripts\activate 4 | echo Starting the bot... 5 | python main.py 6 | pause 7 | -------------------------------------------------------------------------------- /bot/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .logger import logger 2 | from . import launcher 3 | 4 | 5 | import os 6 | 7 | if not os.path.exists(path='sessions'): 8 | os.mkdir(path='sessions') 9 | -------------------------------------------------------------------------------- /START.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Activating virtual environment..." 4 | source venv/bin/activate 5 | 6 | echo "Starting the bot..." 7 | python main.py 8 | 9 | echo "Press any key to continue..." 10 | read -n 1 -s 11 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from contextlib import suppress 3 | 4 | from bot.utils.launcher import process 5 | 6 | 7 | async def main(): 8 | await process() 9 | 10 | 11 | if __name__ == '__main__': 12 | with suppress(KeyboardInterrupt): 13 | asyncio.run(main()) 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10 2 | 3 | WORKDIR app/ 4 | 5 | COPY requirements.txt requirements.txt 6 | 7 | RUN pip3 install --upgrade pip setuptools wheel 8 | RUN pip3 install --no-warn-script-location --no-cache-dir -r requirements.txt 9 | 10 | COPY . . 11 | 12 | ENTRYPOINT ["python3", "main.py"] 13 | CMD ["-a", "2"] -------------------------------------------------------------------------------- /bot/utils/logger.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from loguru import logger 3 | 4 | 5 | logger.remove() 6 | logger.add(sink=sys.stdout, format="{time:YYYY-MM-DD HH:mm:ss}" 7 | " | {level: <8}" 8 | " | {line}" 9 | " - {message}") 10 | logger = logger.opt(colors=True) 11 | -------------------------------------------------------------------------------- /INSTALL.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo Creating virtual environment... 3 | python -m venv venv 4 | echo Activating virtual environment... 5 | call venv\Scripts\activate 6 | echo Installing dependencies... 7 | pip install -r requirements.txt 8 | echo Copying .env-example to .env... 9 | copy .env-example .env 10 | echo Please edit the .env file to add your API_ID and API_HASH. 11 | pause 12 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | bot: 4 | container_name: 'ClaytonBot' 5 | stop_signal: SIGINT 6 | build: 7 | context: . 8 | working_dir: /app 9 | volumes: 10 | - .:/app 11 | entrypoint: "python3 main.py" 12 | command: ["-a", "2"] 13 | restart: unless-stopped 14 | env_file: .env -------------------------------------------------------------------------------- /bot/config/config.py: -------------------------------------------------------------------------------- 1 | from pydantic_settings import BaseSettings, SettingsConfigDict 2 | from bot.utils import logger 3 | 4 | 5 | class Settings(BaseSettings): 6 | model_config = SettingsConfigDict(env_file=".env", env_ignore_empty=True) 7 | 8 | API_ID: int 9 | API_HASH: str 10 | 11 | SLEEP_BETWEEN_START: list[int] = [20, 360] 12 | USE_PROXY_FROM_FILE: bool = False 13 | 14 | 15 | try: 16 | settings = Settings() 17 | except Exception as error: 18 | logger.error(error) 19 | settings = False 20 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.9.3 2 | aiohttp-proxy==0.1.2 3 | aiosignal==1.3.1 4 | annotated-types==0.6.0 5 | async-timeout==4.0.3 6 | attrs==23.2.0 7 | better-proxy==1.1.5 8 | colorama==0.4.6 9 | frozenlist==1.4.1 10 | idna==3.6 11 | loguru==0.7.2 12 | multidict==6.0.5 13 | pyaes==1.6.1 14 | pydantic==2.6.4 15 | pydantic-settings==2.2.1 16 | pydantic_core==2.16.3 17 | Pyrogram==2.0.106 18 | PySocks==1.7.1 19 | python-dotenv==1.0.1 20 | python-dateutil==2.9.0 21 | TgCrypto==1.2.5 22 | typing_extensions==4.11.0 23 | win32-setctime==1.1.0 24 | yarl==1.9.4 25 | -------------------------------------------------------------------------------- /bot/core/headers.py: -------------------------------------------------------------------------------- 1 | headers = { 2 | 'Accept': '*/*', 3 | 'Accept-Language': 'en-US,en;q=0.9,uk-UA;q=0.8,uk;q=0.7,ru;q=0.6,zh-CN;q=0.5,zh;q=0.4', 4 | 'Content-Type': 'application/json', 5 | 'Priority': 'u=1, i', 6 | 'Origin': 'https://tonclayton.fun', 7 | 'Referer': 'https://tonclayton.fun/', 8 | 'Sec-Fetch-Dest': 'empty', 9 | 'Sec-Fetch-Mode': 'cors', 10 | 'Sec-Fetch-Site': 'same-origin', 11 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36', 12 | 'sec-ch-ua': '"Google Chrome";v="125", "Chromium";v="125", "Not.A/Brand";v="24"', 13 | 'sec-ch-ua-mobile': '?0', 14 | 'sec-ch-ua-platform': '"macOS"', 15 | } 16 | -------------------------------------------------------------------------------- /bot/core/registrator.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client 2 | 3 | from bot.config import settings 4 | from bot.utils import logger 5 | 6 | 7 | async def register_sessions() -> None: 8 | API_ID = settings.API_ID 9 | API_HASH = settings.API_HASH 10 | 11 | if not API_ID or not API_HASH: 12 | raise ValueError("API_ID and API_HASH not found in the .env file.") 13 | 14 | session_name = input('\nEnter the session name (press Enter to exit): ') 15 | 16 | if not session_name: 17 | return None 18 | 19 | session = Client( 20 | name=session_name, 21 | api_id=API_ID, 22 | api_hash=API_HASH, 23 | workdir="sessions/" 24 | ) 25 | 26 | async with session: 27 | user_data = await session.get_me() 28 | 29 | logger.success(f'Session added successfully @{user_data.username} | {user_data.first_name} {user_data.last_name}') 30 | -------------------------------------------------------------------------------- /INSTALL.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | install_python() { 4 | echo "Select the Python version to install:" 5 | echo "1) Python 3.10" 6 | echo "2) Python 3.11" 7 | echo "3) Python 3.12" 8 | read -p "Enter the number of your choice: " choice 9 | 10 | case $choice in 11 | 1) version="3.10" ;; 12 | 2) version="3.11" ;; 13 | 3) version="3.12" ;; 14 | *) echo "Invalid choice"; exit 1 ;; 15 | esac 16 | 17 | if command -v apt-get &> /dev/null; then 18 | sudo apt-get update 19 | sudo apt-get install -y python$version python$version-venv python$version-pip 20 | elif command -v yum &> /dev/null; then 21 | sudo yum install -y https://repo.ius.io/ius-release-el$(rpm -E %{rhel}).rpm 22 | sudo yum install -y python$version python$version-venv python$version-pip 23 | elif command -v dnf &> /dev/null; then 24 | sudo dnf install -y python$version python$version-venv python$version-pip 25 | else 26 | echo "Package manager not supported. Please install Python manually." 27 | exit 1 28 | fi 29 | } 30 | 31 | if ! command -v python3 &> /dev/null; then 32 | install_python 33 | fi 34 | 35 | echo "Creating virtual environment..." 36 | python3 -m venv venv 37 | 38 | echo "Activating virtual environment..." 39 | source venv/bin/activate 40 | 41 | echo "Installing dependencies..." 42 | pip install -r requirements.txt 43 | 44 | echo "Copying .env-example to .env..." 45 | cp .env-example .env 46 | 47 | echo "Please edit the .env file to add your API_ID and API_HASH." 48 | read -p "Press any key to continue..." -------------------------------------------------------------------------------- /bot/utils/launcher.py: -------------------------------------------------------------------------------- 1 | import os 2 | import glob 3 | import asyncio 4 | import argparse 5 | import random 6 | from itertools import cycle 7 | from pathlib import Path 8 | 9 | from pyrogram import Client 10 | from better_proxy import Proxy 11 | 12 | from bot.config import settings 13 | from bot.utils import logger 14 | from bot.core.claimer import run_claimer 15 | from bot.core.registrator import register_sessions 16 | 17 | 18 | start_text = """ 19 | 20 | 21 | ██████╗██╗ █████╗ ██╗ ██╗████████╗ ██████╗ ███╗ ██╗██████╗ ██████╗ ████████╗ 22 | ██╔════╝██║ ██╔══██╗╚██╗ ██╔╝╚══██╔══╝██╔═══██╗████╗ ██║██╔══██╗██╔═══██╗╚══██╔══╝ 23 | ██║ ██║ ███████║ ╚████╔╝ ██║ ██║ ██║██╔██╗ ██║██████╔╝██║ ██║ ██║ 24 | ██║ ██║ ██╔══██║ ╚██╔╝ ██║ ██║ ██║██║╚██╗██║██╔══██╗██║ ██║ ██║ 25 | ╚██████╗███████╗██║ ██║ ██║ ██║ ╚██████╔╝██║ ╚████║██████╔╝╚██████╔╝ ██║ 26 | ╚═════╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═════╝ ╚═════╝ ╚═╝ 27 | 28 | 29 | 30 | Select an action: 31 | 32 | 1. Create session 33 | 2. Run claimer 34 | """ 35 | 36 | 37 | def get_session_names() -> list[str]: 38 | session_path = Path('sessions') 39 | session_files = session_path.glob('*.session') 40 | session_names = sorted([file.stem for file in session_files]) 41 | return session_names 42 | 43 | 44 | def get_proxies() -> list[Proxy]: 45 | if settings.USE_PROXY_FROM_FILE: 46 | with open(file='bot/config/proxies.txt', encoding='utf-8-sig') as file: 47 | proxies = sorted([Proxy.from_str(proxy=row.strip()).as_url for row in file if row.strip()]) 48 | else: 49 | proxies = [] 50 | 51 | return proxies 52 | 53 | 54 | async def get_tg_clients() -> list[Client]: 55 | session_names = get_session_names() 56 | 57 | if not session_names: 58 | raise FileNotFoundError("Not found session files") 59 | 60 | tg_clients = [Client( 61 | name=session_name, 62 | api_id=settings.API_ID, 63 | api_hash=settings.API_HASH, 64 | workdir='sessions/', 65 | plugins=dict(root='bot/plugins') 66 | ) for session_name in session_names] 67 | 68 | return tg_clients 69 | 70 | async def run_bot_with_delay(tg_client, proxy, delay): 71 | if delay > 0: 72 | logger.info(f"{tg_client.name} | Wait {delay} seconds before start") 73 | await asyncio.sleep(delay) 74 | await run_claimer(tg_client=tg_client, proxy=proxy) 75 | 76 | async def run_clients(tg_clients: list[Client]): 77 | proxies = get_proxies() 78 | proxies_cycle = cycle(proxies) if proxies else cycle([None]) 79 | tasks = [] 80 | delay = 0 81 | for index, tg_client in enumerate(tg_clients): 82 | if index > 0: 83 | delay = random.randint(*settings.SLEEP_BETWEEN_START) 84 | proxy = next(proxies_cycle) 85 | task = asyncio.create_task(run_bot_with_delay(tg_client=tg_client, proxy=proxy, delay=delay)) 86 | tasks.append(task) 87 | await asyncio.gather(*tasks) 88 | 89 | async def process() -> None: 90 | if not settings: 91 | logger.warning(f"Please fix the above errors in the .env file") 92 | return 93 | parser = argparse.ArgumentParser() 94 | parser.add_argument('-a', '--action', type=int, help='Action to perform') 95 | 96 | logger.info(f"Detected {len(get_session_names())} sessions | {len(get_proxies())} proxies") 97 | 98 | action = parser.parse_args().action 99 | 100 | if not action: 101 | print(start_text) 102 | 103 | while True: 104 | action = input("> ") 105 | 106 | if not action.isdigit(): 107 | logger.warning("Action must be number") 108 | elif action not in ['1', '2']: 109 | logger.warning("Action must be 1 or 2") 110 | else: 111 | action = int(action) 112 | break 113 | 114 | if action == 1: 115 | await register_sessions() 116 | elif action == 2: 117 | tg_clients = await get_tg_clients() 118 | await run_clients(tg_clients=tg_clients) 119 | 120 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # DB 65 | sessions/ 66 | 67 | # Flask stuff: 68 | instance/ 69 | .webassets-cache 70 | 71 | # Scrapy stuff: 72 | .scrapy 73 | 74 | # Sphinx documentation 75 | docs/_build/ 76 | 77 | # PyBuilder 78 | .pybuilder/ 79 | target/ 80 | 81 | # Jupyter Notebook 82 | .ipynb_checkpoints 83 | 84 | # IPython 85 | profile_default/ 86 | ipython_config.py 87 | 88 | # pyenv 89 | # For a library or package, you might want to ignore these files since the code is 90 | # intended to run in multiple environments; otherwise, check them in: 91 | # .python-version 92 | 93 | # pipenv 94 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 95 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 96 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 97 | # install all needed dependencies. 98 | #Pipfile.lock 99 | 100 | # poetry 101 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 102 | # This is especially recommended for binary packages to ensure reproducibility, and is more 103 | # commonly ignored for libraries. 104 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 105 | #poetry.lock 106 | 107 | # pdm 108 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 109 | #pdm.lock 110 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 111 | # in version control. 112 | # https://pdm.fming.dev/#use-with-ide 113 | .pdm.toml 114 | 115 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 116 | __pypackages__/ 117 | 118 | # Celery stuff 119 | celerybeat-schedule 120 | celerybeat.pid 121 | 122 | # SageMath parsed files 123 | *.sage.py 124 | 125 | # Environments 126 | .env 127 | .venv 128 | env/ 129 | venv/ 130 | ENV/ 131 | env.bak/ 132 | venv.bak/ 133 | 134 | # Spyder project settings 135 | .spyderproject 136 | .spyproject 137 | 138 | # Rope project settings 139 | .ropeproject 140 | 141 | # mkdocs documentation 142 | /site 143 | 144 | # mypy 145 | .mypy_cache/ 146 | .dmypy.json 147 | dmypy.json 148 | 149 | # Pyre type checker 150 | .pyre/ 151 | 152 | # pytype static type analyzer 153 | .pytype/ 154 | 155 | # Cython debug symbols 156 | cython_debug/ 157 | 158 | # PyCharm 159 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 160 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 161 | # and can be added to the global gitignore or merged into this file. For a more nuclear 162 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 163 | .idea/ 164 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bot for [Clayton](https://alexell.pro/cc/clayton) 2 | 3 | ![img1](.github/images/demo.png) 4 | 5 | > 🇷🇺 README на русском доступен [здесь](README-RU.md) 6 | 7 | ## Functionality 8 | | Feature | Supported | 9 | |----------------------------------------------------------------|:----------:| 10 | | Multithreading | ✅ | 11 | | Binding a proxy to a session | ✅ | 12 | | Automatic farming | ✅ | 13 | | Automatic games (Stack and 1024) | ✅ | 14 | | Docker | ✅ | 15 | 16 | ## [Options](https://github.com/Alexell/ClaytonBOT/blob/main/.env-example) 17 | | Option | Description | 18 | |-------------------------|----------------------------------------------------------------------------| 19 | | **API_ID / API_HASH** | Platform data from which to launch a Telegram session (stock - Android) | 20 | | **ERRORS_BEFORE_STOP** | The number of failed requests after which the bot will stop | 21 | | **SLEEP_BETWEEN_START** | Sleep before start each session (e.g. [20, 360]) | 22 | | **USE_PROXY_FROM_FILE** | Whether to use proxy from the `bot/config/proxies.txt` file (True / False) | 23 | 24 | You can obtain the **API_ID** and **API_HASH** after creating an application at [my.telegram.org/apps](https://my.telegram.org/apps) 25 | 26 | ## Quick start 27 | ### Windows 28 | 1. Ensure you have **Python 3.10** or a newer version installed. 29 | 2. Use `INSTALL.bat` to install, then specify your API_ID and API_HASH in the .env file. 30 | 3. Use `START.bat` to launch the bot (or in the console: `python main.py`). 31 | 32 | ### Linux 33 | 1. Clone the repository: `git clone https://github.com/Alexell/ClaytonBOT.git && cd ClaytonBOT` 34 | 2. Run the installation: `chmod +x INSTALL.sh START.sh && ./INSTALL.sh`, then specify your API_ID and API_HASH in the .env file. 35 | 3. Use `./START.sh` to run the bot (or in the console: `python3 main.py`). 36 | 37 | ## Running in Docker 38 | ``` 39 | $ git clone https://github.com/Alexell/ClaytonBOT.git 40 | $ cd ClaytonBOT 41 | $ cp .env-example .env 42 | $ nano .env # specify your API_ID and API_HASH, the rest can be left as default 43 | ``` 44 | ### Docker Compose (recommended) 45 | ``` 46 | $ docker-compose run bot -a 1 # first run for authorization (override arguments) 47 | $ docker-compose start # start in background mode (default arguments: -a 2) 48 | ``` 49 | ### Docker 50 | ``` 51 | $ docker build -t clayton_bot . 52 | $ docker run --name ClaytonBOT -v .:/app -it clayton_bot -a 1 # first run for authorization 53 | $ docker rm ClaytonBOT # remove container to recreate with default arguments 54 | $ docker run -d --restart unless-stopped --name ClaytonBOT -v .:/app clayton_bot # start in background mode (default arguments: -a 2) 55 | ``` 56 | 57 | ## Manual installation 58 | You can download [**Repository**](https://github.com/Alexell/ClaytonBOT) by cloning it to your system and installing the necessary dependencies: 59 | ``` 60 | $ git clone https://github.com/Alexell/ClaytonBOT.git 61 | $ cd ClaytonBOT 62 | 63 | # Linux 64 | $ python3 -m venv venv 65 | $ source venv/bin/activate 66 | $ pip3 install -r requirements.txt 67 | $ cp .env-example .env 68 | $ nano .env # specify your API_ID and API_HASH, the rest can be left as default 69 | $ python3 main.py 70 | 71 | # Windows (first, install Python 3.10 or a newer version) 72 | > python -m venv venv 73 | > venv\Scripts\activate 74 | > pip install -r requirements.txt 75 | > copy .env-example .env 76 | > # specify your API_ID and API_HASH, the rest can be left as default 77 | > python main.py 78 | ``` 79 | 80 | Also for quick launch you can use arguments: 81 | ``` 82 | $ python3 main.py --action (1/2) 83 | # or 84 | $ python3 main.py -a (1/2) 85 | 86 | # 1 - Create session 87 | # 2 - Run claimer 88 | ``` 89 | 90 | ## Running a bot in the background (Linux) 91 | ``` 92 | $ cd ClaytonBOT 93 | 94 | # with logging 95 | $ setsid venv/bin/python3 main.py --action 2 >> app.log 2>&1 & 96 | 97 | # without logging 98 | $ setsid venv/bin/python3 main.py --action 2 > /dev/null 2>&1 & 99 | 100 | # Now you can close the console, and the bot will continue its work. 101 | ``` 102 | 103 | ### Find the bot process 104 | ``` 105 | $ ps aux | grep "python3 main.py" | grep -v grep 106 | ``` -------------------------------------------------------------------------------- /README-RU.md: -------------------------------------------------------------------------------- 1 | # Бот для [Clayton](https://alexell.pro/cc/clayton) 2 | 3 | ![img1](.github/images/demo.png) 4 | 5 | > 🇺🇸 README in english available [here](README.md) 6 | 7 | ## Функционал 8 | | Функция | Поддерживается | 9 | |----------------------------------------------------------------|:---------------:| 10 | | Многопоточность | ✅ | 11 | | Привязка прокси к сессии | ✅ | 12 | | Автоматический фарминг | ✅ | 13 | | Автоматические игры (Stack и 1024) | ✅ | 14 | | Docker | ✅ | 15 | 16 | ## [Настройки](https://github.com/Alexell/ClaytonBOT/blob/main/.env-example) 17 | | Опция | Описание | 18 | |-------------------------|----------------------------------------------------------------------------| 19 | | **API_ID / API_HASH** | Данные платформы, с которой запускать сессию Telegram (сток - Android) | 20 | | **ERRORS_BEFORE_STOP** | Количество неудачных запросов, по достижению которых, бот остановится | 21 | | **SLEEP_BETWEEN_START** | Задержка перед запуском каждой сессии (напр. [20, 360]) | 22 | | **USE_PROXY_FROM_FILE** | Использовать-ли прокси из файла `bot/config/proxies.txt` (True / False) | 23 | 24 | **API_ID** и **API_HASH** вы можете получить после создания приложения на [my.telegram.org/apps](https://my.telegram.org/apps) 25 | 26 | ## Быстрый старт 27 | ### Windows 28 | 1. Убедитесь, что у вас установлен **Python 3.10** или более новая версия. 29 | 2. Используйте `INSTALL.bat` для установки, затем укажите ваши API_ID и API_HASH в .env 30 | 3. Используйте `START.bat` для запуска бота (или в консоли: `python main.py`) 31 | 32 | ### Linux 33 | 1. Клонируйте репозиторий: `git clone https://github.com/Alexell/ClaytonBOT.git && cd ClaytonBOT` 34 | 2. Выполните установку: `chmod +x INSTALL.sh START.sh && ./INSTALL.sh`, затем укажите ваши API_ID и API_HASH в .env 35 | 3. Используйте `./START.sh` для запуска бота (или в консоли: `python3 main.py`) 36 | 37 | ## Запуск в Docker 38 | ``` 39 | $ git clone https://github.com/Alexell/ClaytonBOT.git 40 | $ cd ClaytonBOT 41 | $ cp .env-example .env 42 | $ nano .env # укажите ваши API_ID и API_HASH, остальное можно оставить по умолчанию 43 | ``` 44 | ### Docker Compose (рекомендуется) 45 | ``` 46 | $ docker-compose run bot -a 1 # первый запуск для авторизации (переопределяем аргументы) 47 | $ docker-compose start # запуск в фоновом режиме (аргументы по умолчанию: -a 2) 48 | ``` 49 | ### Docker 50 | ``` 51 | $ docker build -t clayton_bot . 52 | $ docker run --name ClaytonBOT -v .:/app -it clayton_bot -a 1 # первый запуск для авторизации 53 | $ docker rm ClaytonBOT # удаляем контейнер для пересоздания с аргументами по умолчанию 54 | $ docker run -d --restart unless-stopped --name ClaytonBOT -v .:/app clayton_bot # запуск в фоновом режиме (аргументы по умолчанию: -a 2) 55 | ``` 56 | 57 | ## Ручная установка 58 | Вы можете скачать [**Репозиторий**](https://github.com/Alexell/ClaytonBOT) клонированием на вашу систему и установкой необходимых зависимостей: 59 | ``` 60 | $ git clone https://github.com/Alexell/ClaytonBOT.git 61 | $ cd ClaytonBOT 62 | 63 | # Linux 64 | $ python3 -m venv venv 65 | $ source venv/bin/activate 66 | $ pip3 install -r requirements.txt 67 | $ cp .env-example .env 68 | $ nano .env # укажите ваши API_ID и API_HASH, остальное можно оставить по умолчанию 69 | $ python3 main.py 70 | 71 | # Windows (сначала установите Python 3.10 или более новую версию) 72 | > python -m venv venv 73 | > venv\Scripts\activate 74 | > pip install -r requirements.txt 75 | > copy .env-example .env 76 | > # укажите ваши API_ID и API_HASH, остальное можно оставить по умолчанию 77 | > python main.py 78 | ``` 79 | 80 | Также для быстрого запуска вы можете использовать аргументы: 81 | ``` 82 | $ python3 main.py --action (1/2) 83 | # или 84 | $ python3 main.py -a (1/2) 85 | 86 | # 1 - создать сессию 87 | # 2 - запустить бот 88 | ``` 89 | 90 | ## Запуск бота в фоновом режиме (Linux) 91 | ``` 92 | $ cd ClaytonBOT 93 | 94 | # с логированием 95 | $ setsid venv/bin/python3 main.py --action 2 >> app.log 2>&1 & 96 | 97 | # без логирования 98 | $ setsid venv/bin/python3 main.py --action 2 > /dev/null 2>&1 & 99 | 100 | # Теперь вы можете закрыть консоль и бот продолжит свою работу. 101 | ``` 102 | 103 | ### Найти процесс бота 104 | ``` 105 | $ ps aux | grep "python3 main.py" | grep -v grep 106 | ``` -------------------------------------------------------------------------------- /bot/core/claimer.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from time import time 3 | from dateutil import parser 4 | from datetime import datetime, timezone 5 | from urllib.parse import unquote 6 | 7 | import aiohttp 8 | from aiohttp_proxy import ProxyConnector 9 | from better_proxy import Proxy 10 | from pyrogram import Client 11 | from pyrogram.errors import Unauthorized, UserDeactivated, AuthKeyUnregistered 12 | from pyrogram.raw.functions.messages import RequestWebView 13 | 14 | from bot.config import settings 15 | from bot.utils import logger 16 | from bot.exceptions import InvalidSession 17 | from .headers import headers 18 | import random 19 | 20 | class Claimer: 21 | def __init__(self, tg_client: Client): 22 | self.session_name = tg_client.name 23 | self.tg_client = tg_client 24 | 25 | async def get_tg_web_data(self, proxy: str | None) -> str: 26 | proxy_dict = None 27 | self.tg_client.proxy = proxy_dict 28 | 29 | try: 30 | if not self.tg_client.is_connected: 31 | try: 32 | await self.tg_client.connect() 33 | except (Unauthorized, UserDeactivated, AuthKeyUnregistered): 34 | raise InvalidSession(self.session_name) 35 | web_view = await self.tg_client.invoke(RequestWebView( 36 | peer=await self.tg_client.resolve_peer('claytoncoinbot'), 37 | bot=await self.tg_client.resolve_peer('claytoncoinbot'), 38 | platform='android', 39 | from_bot_menu=False, 40 | url='https://tonclayton.fun/' 41 | )) 42 | auth_url = web_view.url 43 | tg_web_data = unquote( 44 | string=auth_url.split('tgWebAppData=', maxsplit=1)[1].split('&tgWebAppVersion', maxsplit=1)[0]) 45 | if self.tg_client.is_connected: 46 | await self.tg_client.disconnect() 47 | 48 | return tg_web_data 49 | 50 | except InvalidSession as error: 51 | raise error 52 | 53 | except Exception as error: 54 | logger.error(f"{self.session_name} | Unknown error during Authorization: {error}") 55 | await asyncio.sleep(delay=3) 56 | 57 | async def send_claim(self, http_client: aiohttp.ClientSession) -> bool: 58 | try: 59 | response = await http_client.post('https://tonclayton.fun/api/user/claim', json={}) 60 | response.raise_for_status() 61 | return True 62 | except Exception as error: 63 | logger.error(f"{self.session_name} | Unknown error when Claiming: {error}") 64 | await asyncio.sleep(delay=3) 65 | 66 | return False 67 | 68 | async def start_farming(self, http_client: aiohttp.ClientSession) -> bool: 69 | await asyncio.sleep(delay=6) 70 | try: 71 | response = await http_client.post('https://tonclayton.fun/api/user/start', json={}) 72 | response.raise_for_status() 73 | 74 | return True 75 | except Exception as error: 76 | logger.error(f"{self.session_name} | Unknown error when Start Farming: {error}") 77 | await asyncio.sleep(delay=3) 78 | 79 | return False 80 | 81 | async def check_proxy(self, http_client: aiohttp.ClientSession, proxy: Proxy) -> None: 82 | try: 83 | response = await http_client.get(url='https://httpbin.org/ip', timeout=aiohttp.ClientTimeout(5)) 84 | ip = (await response.json()).get('origin') 85 | logger.info(f"{self.session_name} | Proxy IP: {ip}") 86 | except Exception as error: 87 | logger.error(f"{self.session_name} | Proxy: {proxy} | Error: {error}") 88 | 89 | async def get_mining_data(self, http_client: aiohttp.ClientSession) -> dict[str, str]: 90 | try: 91 | response = await http_client.post('https://tonclayton.fun/api/user/auth') 92 | response.raise_for_status() 93 | 94 | response_json = await response.json() 95 | mining_data = response_json 96 | 97 | return mining_data 98 | except Exception as error: 99 | logger.error(f"{self.session_name} | Unknown error when getting Profile Data: {error}") 100 | await asyncio.sleep(delay=3) 101 | return {} 102 | 103 | async def perform_game(self, http_client: aiohttp.ClientSession, multiplier: int) -> None: 104 | # Start a new '1024' game 105 | try: 106 | response = await http_client.post('https://tonclayton.fun/api/game/start') 107 | response.raise_for_status() 108 | logger.info(f"{self.session_name} | Game '1024' started") 109 | except aiohttp.ClientResponseError as e: 110 | logger.error(f"{self.session_name} | Error starting game: {e}") 111 | return 112 | except Exception as error: 113 | logger.error(f"{self.session_name} | Unknown error when starting game: {error}") 114 | return 115 | 116 | await asyncio.sleep(random.randint(2, 3)) # Sleep 2-3 seconds 117 | 118 | # Progress through tile values 119 | base_url = 'https://tonclayton.fun/api/game/save-tile' 120 | tile_values = [4, 8, 16, 32, 64, 128, 256, 512, 1024] 121 | tiles_count = len(tile_values) 122 | start_time = asyncio.get_event_loop().time() 123 | end_time = start_time + 150 # 2:30 124 | interval = (150 - 5) / tiles_count # сalculate interval leaving 5 seconds to the end 125 | try: 126 | i = 0 127 | while i < tiles_count and (asyncio.get_event_loop().time() < end_time - 5): 128 | payload = {"maxTile": tile_values[i]} 129 | response = await http_client.post(base_url, json=payload) 130 | response.raise_for_status() 131 | logger.info(f"{self.session_name} | Successfully saved tile: {tile_values[i]}") 132 | i += 1 133 | if i < tiles_count: await asyncio.sleep(interval) 134 | 135 | # End the game after reaching 1024 136 | payload = {"multiplier": multiplier} 137 | response = await http_client.post('https://tonclayton.fun/api/game/over', json=payload) 138 | response.raise_for_status() 139 | response_json = await response.json() 140 | earn = response_json.get('earn', 0) 141 | if earn > 0: 142 | logger.success(f"{self.session_name} | Game '1024' finished (+{earn} tokens)") 143 | else: 144 | logger.warning(f"{self.session_name} | Game '1024' failed") 145 | except aiohttp.ClientResponseError as e: 146 | logger.error(f"{self.session_name} | Error during game play: {e}") 147 | return 148 | except Exception as error: 149 | logger.error(f"{self.session_name} | Error during game play: {error}") 150 | return 151 | 152 | async def perform_stack(self, http_client: aiohttp.ClientSession, multiplier: int) -> None: 153 | # Start a new 'Stack' game 154 | try: 155 | response = await http_client.post('https://tonclayton.fun/api/stack/start-game') 156 | response.raise_for_status() 157 | logger.info(f"{self.session_name} | Game 'Stack' started") 158 | except aiohttp.ClientResponseError as e: 159 | logger.error(f"{self.session_name} | Error starting game: {e}") 160 | return 161 | except Exception as error: 162 | logger.error(f"{self.session_name} | Unknown error when starting game: {error}") 163 | return 164 | 165 | start_time = asyncio.get_event_loop().time() 166 | end_time = start_time + 120 # 2:00 167 | max_score = 90 168 | score = 0 169 | update_count = max_score / 10 170 | interval = (120 - 5) / update_count # сalculate interval leaving 5 seconds to the end 171 | try: 172 | while score < max_score and (asyncio.get_event_loop().time() < end_time - 5): 173 | score += 10 174 | payload = {"score": score} 175 | response = await http_client.post('https://tonclayton.fun/api/stack/update-game', json=payload) 176 | response.raise_for_status() 177 | logger.info(f"{self.session_name} | Successfully saved score: {score}") 178 | await asyncio.sleep(interval) 179 | 180 | score += random.randint(2, 8) 181 | payload = {"score": score, "multiplier": multiplier} 182 | response = await http_client.post('https://tonclayton.fun/api/stack/end-game', json=payload) 183 | response.raise_for_status() 184 | response_json = await response.json() 185 | earn = response_json.get('earn', 0) 186 | if earn > 0: 187 | logger.success(f"{self.session_name} | Game 'Stack' finished (+{earn} tokens)") 188 | else: 189 | logger.warning(f"{self.session_name} | Game 'Stack' failed") 190 | except aiohttp.ClientResponseError as e: 191 | logger.error(f"{self.session_name} | Error during game play: {e}") 192 | return 193 | except Exception as error: 194 | logger.error(f"{self.session_name} | Error during game play: {error}") 195 | return 196 | 197 | async def daily_reward(self, http_client: aiohttp.ClientSession) -> None: 198 | try: 199 | response = await http_client.post('https://tonclayton.fun/api/user/daily-claim') 200 | response.raise_for_status() 201 | 202 | response_json = await response.json() 203 | tokens = response_json['tokens'] 204 | logger.success(f"{self.session_name} | Daily reward claimed (+{tokens} tokens)") 205 | except Exception as error: 206 | logger.error(f"{self.session_name} | Unknown error in daily reward: {error}") 207 | await asyncio.sleep(delay=3) 208 | 209 | async def perform_tasks(self, http_client: aiohttp.ClientSession) -> None: 210 | task_urls = ['https://tonclayton.fun/api/tasks/super-tasks', 'https://tonclayton.fun/api/tasks/partner-tasks', 'https://tonclayton.fun/api/user/okx/tickets', 'https://tonclayton.fun/api/tasks/daily-tasks', 'https://tonclayton.fun/api/tasks/default-tasks'] 211 | url_check = 'https://tonclayton.fun/api/tasks/check' 212 | url_complete = 'https://tonclayton.fun/api/tasks/complete' 213 | url_claim = 'https://tonclayton.fun/api/tasks/claim' 214 | try: 215 | for url in task_urls: 216 | response = await http_client.get(url) 217 | response.raise_for_status() 218 | response_json = await response.json() 219 | if 'okx/tickets' in url: continue 220 | for task in response_json: 221 | if task['is_claimed']: continue 222 | logger.info(f"{self.session_name} | Processing task {task['task_id']}...") 223 | if task['task']['requires_check']: 224 | payload = {"task_id": task['task_id']} 225 | response2 = await http_client.post(url_check, json=payload) 226 | response2.raise_for_status() 227 | response2_json = await response2.json() 228 | if response2_json['is_completed']: 229 | payload = {"task_id": task['task_id']} 230 | response2 = await http_client.post(url_claim, json=payload) 231 | response2.raise_for_status() 232 | response2_json = await response2.json() 233 | if response2_json['reward_tokens']: 234 | logger.success(f"{self.session_name} | Task {task['task_id']} completed (+{response2_json['reward_tokens']} tokens)") 235 | else: 236 | payload = {"task_id": task['task_id']} 237 | response2 = await http_client.post(url_complete, json=payload) 238 | response2.raise_for_status() 239 | response2_json = await response2.json() 240 | if response2_json.get('message', False) and 'completed' in response2_json['message']: 241 | payload = {"task_id": task['task_id']} 242 | response2 = await http_client.post(url_claim, json=payload) 243 | response2.raise_for_status() 244 | response2_json = await response2.json() 245 | if response2_json['reward_tokens']: 246 | logger.success(f"{self.session_name} | Task {task['task_id']} completed (+{response2_json['reward_tokens']} tokens)") 247 | await asyncio.sleep(random.randint(12, 24)) 248 | await asyncio.sleep(random.randint(24, 48)) 249 | except Exception as error: 250 | logger.error(f"{self.session_name} | Unknown error in daily reward: {error}") 251 | await asyncio.sleep(delay=3) 252 | 253 | async def run(self, proxy: str | None) -> None: 254 | claim_time = 0 255 | 256 | proxy_conn = ProxyConnector().from_url(proxy) if proxy else None 257 | 258 | async with aiohttp.ClientSession(headers=headers, connector=proxy_conn) as http_client: 259 | while True: 260 | try: 261 | tg_web_data = await self.get_tg_web_data(proxy=proxy) 262 | http_client.headers["Init-Data"] = tg_web_data 263 | headers["Init-Data"] = tg_web_data 264 | mining_data = await self.get_mining_data(http_client=http_client) 265 | tokens = int(mining_data['user']['tokens']) 266 | multiplier = mining_data['user']['multiplier'] 267 | daily_attempts = mining_data['user']['daily_attempts'] 268 | need_daily_claim = mining_data['dailyReward']['can_claim_today'] 269 | #active_farm = mining_data['active_farm'] 270 | #start_time = parser.parse(mining_data['start_time']) 271 | #start_time = start_time.astimezone(timezone.utc) 272 | #current_time = datetime.now(timezone.utc) 273 | 274 | # Log current status 275 | logger.info(f"{self.session_name} | Balance: {tokens} | " 276 | f"Multiplier: {multiplier}") 277 | 278 | await asyncio.sleep(random.randint(2, 4)) 279 | if need_daily_claim: 280 | logger.info(f"{self.session_name} | Daily reward available") 281 | await self.daily_reward(http_client=http_client) 282 | else: 283 | logger.info(f"{self.session_name} | Daily reward not available") 284 | 285 | await asyncio.sleep(random.randint(4, 8)) 286 | await self.perform_tasks(http_client=http_client) 287 | 288 | await asyncio.sleep(random.randint(4, 8)) 289 | if daily_attempts > 0: 290 | logger.info(f"{self.session_name} | Game attempts remaining: {daily_attempts}") 291 | games = ['1024', 'Stack'] 292 | while daily_attempts > 0: 293 | game = random.choice(games) 294 | if game == '1024': 295 | await self.perform_game(http_client=http_client, multiplier=multiplier) 296 | else: 297 | await self.perform_stack(http_client=http_client, multiplier=multiplier) 298 | daily_attempts -= 1 299 | await asyncio.sleep(random.randint(10, 15)) # Sleep between games 300 | continue 301 | 302 | '''await asyncio.sleep(random.randint(2, 4)) 303 | if not active_farm: 304 | logger.info(f"{self.session_name} | Farm not active. Claiming and starting farming.") 305 | if await self.send_claim(http_client=http_client): 306 | logger.success(f"{self.session_name} | Claim successful.") 307 | if await self.start_farming(http_client=http_client): 308 | logger.success(f"{self.session_name} | Farming started successfully.") 309 | else: 310 | time_elapsed = current_time - start_time 311 | time_to_wait = max(0, 6 * 3600 - time_elapsed.total_seconds()) 312 | 313 | if time_to_wait > 0: 314 | hours = int(time_to_wait // 3600) 315 | minutes = int((time_to_wait % 3600) // 60) 316 | logger.info(f"{self.session_name} | Farming active. Waiting for {hours} hours and {minutes} minutes before claiming and restarting.") 317 | await asyncio.sleep(time_to_wait) 318 | continue 319 | 320 | logger.info(f"{self.session_name} | Time to claim and restart farming.") 321 | if await self.send_claim(http_client=http_client): 322 | logger.success(f"{self.session_name} | Claim successful.") 323 | if await self.start_farming(http_client=http_client): 324 | logger.success(f"{self.session_name} | Farming restarted successfully.")''' 325 | 326 | except InvalidSession as error: 327 | raise error 328 | except Exception as error: 329 | logger.error(f"{self.session_name} | Unknown error: {error}") 330 | await asyncio.sleep(delay=3) 331 | else: 332 | 333 | sleep_time = random.randint(3600, 10800) 334 | hours, minutes = divmod(sleep_time, 3600) 335 | minutes //= 60 336 | logger.info(f"{self.session_name} | Sleep {int(hours)} hours {int(minutes)} minutes") 337 | await asyncio.sleep(delay=sleep_time) 338 | 339 | async def run_claimer(tg_client: Client, proxy: str | None): 340 | try: 341 | await Claimer(tg_client=tg_client).run(proxy=proxy) 342 | except InvalidSession: 343 | logger.error(f"{tg_client.name} | Invalid Session") 344 | --------------------------------------------------------------------------------