├── 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 | 
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 | 
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 |
--------------------------------------------------------------------------------