diff --git a/.env-example b/.env-example
new file mode 100644
index 0000000..cf20a9d
--- /dev/null
+++ b/.env-example
@@ -0,0 +1,15 @@
+API_ID=
+API_HASH=
+
+AUTO_TAP=
+AUTO_MISSION=
+AUTO_LVL_UP=
+PLAY_WALK_GAME=
+PLAY_SHOOT_GAME=
+PLAY_RPG_GAME=
+PLAY_DIRTY_JOB_GAME=
+AUTO_BUY_PASS=
+
+REF_ID=
+
+USE_PROXY_FROM_FILE=
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ae5755f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,171 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# DB
+sessions/
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+.idea/
+
+.env
+
+sessions/
+
+*.session
+
+user_agents.json
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..7134498
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,12 @@
+FROM python:3.10.11-alpine3.18
+
+WORKDIR app/
+
+COPY requirements.txt requirements.txt
+
+RUN pip3 install --upgrade pip setuptools wheel
+RUN pip3 install --no-warn-script-location --no-cache-dir -r requirements.txt
+
+COPY . .
+
+CMD ["python3", "main.py", "-a", "1"]
diff --git a/README-RU.md b/README-RU.md
new file mode 100644
index 0000000..8f58ef8
--- /dev/null
+++ b/README-RU.md
@@ -0,0 +1,121 @@
+[![Static Badge](https://img.shields.io/badge/Телеграм-Наш_канал-Link?style=for-the-badge&logo=Telegram&logoColor=white&logoSize=auto&color=blue)](https://t.me/hidden_coding)
+
+[![Static Badge](https://img.shields.io/badge/Телеграм-Наш_чат-Link?style=for-the-badge&logo=Telegram&logoColor=white&logoSize=auto&color=blue)](https://t.me/hidden_codding_chat)
+
+[![Static Badge](https://img.shields.io/badge/Телеграм-Ссылка_на_бота-Link?style=for-the-badge&logo=Telegram&logoColor=white&logoSize=auto&color=blue)](https://t.me/HexacoinBot/wallet?startapp=737844465)
+
+## Рекомендация перед использованием
+
+# 🔥🔥 Используйте PYTHON версии 3.10 🔥🔥
+
+> 🇪🇳 README in english available [here](README-EN)
+
+## Функционал
+| Функционал | Поддерживается |
+|:-------------------------------------------------------:|:--------------:|
+| Многопоточность | ✅ |
+| Привязка прокси к сессии | ✅ |
+| Авто тапанье куба в главном меню | ✅ |
+| Авто игра в игры бота | ✅ |
+| Авто выполнение миссий | ✅ |
+| Авто реферальство ваших твинков | ✅ |
+| Поддержка tdata / pyrogram .session / telethon .session | ✅ |
+
+
+## [Настройки](https://github.com/HiddenCodeDevs/HEXACOREbot/blob/main/.env-example/)
+| Настройки | Описание |
+|:-----------------------:|:--------------------------------------------------------------------------------------------:|
+| **API_ID / API_HASH** | Данные платформы, с которой будет запущена сессия Telegram (по умолчанию - android) |
+| **AUTO_TAP** | Авто тапанье куба в главном меню (по умолчанию - True) |
+| **AUTO_MISSION** | Авто выполнение доступных миссий (по умолчанию - True) |
+| **AUTO_LVL_UP** | Авто улучшение уровня в боте (по умолчанию - True) |
+| **PLAY_WALK_GAME** | Авто получение награды в Hexacore Gaming Universe (по умолчанию - True) |
+| **PLAY_SHOOT_GAME** | Авто получение награды в Pin Bullet (по умолчанию - True) |
+| **PLAY_RPG_GAME** | Авто получение награды в Pals (по умолчанию - True) |
+| **PLAY_DIRTY_JOB_GAME** | Авто получение награды в Dirty Job (по умолчанию - True) |
+| **AUTO_BUY_PASS** | Авто покупка прибыльной подписки на клики (по умолчанию - True) |
+| **REF_ID** | Автоматически рефералит ваших твинков (по умолчанию - Нету, введите сюда ваш телеграмм айди) |
+| **USE_PROXY_FROM_FILE** | Использовать ли прокси из файла `bot/config/proxies.txt` (True / False) |
+
+## Быстрый старт 📚
+
+Для быстрой установки и последующего запуска - запустите файл run.bat на Windows или run.sh на Линукс
+
+## Предварительные условия
+Прежде чем начать, убедитесь, что у вас установлено следующее:
+- [Python](https://www.python.org/downloads/) **версии 3.10**
+
+## Получение API ключей
+1. Перейдите на сайт [my.telegram.org](https://my.telegram.org) и войдите в систему, используя свой номер телефона.
+2. Выберите **"API development tools"** и заполните форму для регистрации нового приложения.
+3. Запишите `API_ID` и `API_HASH` в файле `.env`, предоставленные после регистрации вашего приложения.
+
+## Установка
+Вы можете скачать [**Репозиторий**](https://github.com/HiddenCodeDevs/HEXACOREbot) клонированием на вашу систему и установкой необходимых зависимостей:
+```shell
+git clone https://github.com/HiddenCodeDevs/HEXACOREbot.git
+cd HEXACOREbot
+```
+
+Затем для автоматической установки введите:
+
+Windows:
+```shell
+run.bat
+```
+
+Linux:
+```shell
+run.sh
+```
+
+# Linux ручная установка
+```shell
+sudo sh install.sh
+python3 -m venv venv
+source venv/bin/activate
+pip3 install -r requirements.txt
+cp .env-example .env
+nano .env # Здесь вы обязательно должны указать ваши API_ID и API_HASH , остальное берется по умолчанию
+python3 main.py
+```
+
+Также для быстрого запуска вы можете использовать аргументы, например:
+```shell
+~/HEXACOREbot >>> python3 main.py --action (1/2)
+# Or
+~/HEXACOREbot >>> python3 main.py -a (1/2)
+
+# 1 - Запускает кликер
+# 2 - Создает сессию
+```
+
+
+# Windows ручная установка
+```shell
+python -m venv venv
+venv\Scripts\activate
+pip install -r requirements.txt
+copy .env-example .env
+# Указываете ваши API_ID и API_HASH, остальное берется по умолчанию
+python main.py
+```
+
+Также для быстрого запуска вы можете использовать аргументы, например:
+```shell
+~/HEXACOREbot >>> python main.py --action (1/2)
+# Или
+~/HEXACOREbot >>> python main.py -a (1/2)
+
+# 1 - Запускает кликер
+# 2 - Создает сессию
+```
+
+
+
+
+### Контакты
+
+Для поддержки или вопросов, свяжитесь со мной в Telegram:
+
+[![Static Badge](https://img.shields.io/badge/Телеграм-автор_бота-link?style=for-the-badge&logo=telegram&logoColor=white&logoSize=auto&color=blue)](https://t.me/ВАШЮЗЕРНЕЙМВТГ)
diff --git a/README.md b/README.md
index e913bc1..56e22d6 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,119 @@
-# HEXACOREbot
-Python bot for telegram HEXACORE bot
+[![Static Badge](https://img.shields.io/badge/Telegram-Channel-Link?style=for-the-badge&logo=Telegram&logoColor=white&logoSize=auto&color=blue)](https://t.me/hidden_coding)
+
+[![Static Badge](https://img.shields.io/badge/Telegram-Chat-yes?style=for-the-badge&logo=Telegram&logoColor=white&logoSize=auto&color=blue)](https://t.me/hidden_codding_chat)
+
+[![Static Badge](https://img.shields.io/badge/Telegram-Bot%20Link-Link?style=for-the-badge&logo=Telegram&logoColor=white&logoSize=auto&color=blue)](https://t.me/HexacoinBot/wallet?startapp=737844465)
+
+## Recommendation before use
+
+# 🔥🔥 PYTHON version must be 3.10 🔥🔥
+
+> 🇷 🇺 README in russian available [here](README-RU.md)
+
+## Features
+| Feature | Supported |
+|:---------------------------------------------------------:|:---------:|
+| Multithreading | ✅ |
+| Proxy binding to session | ✅ |
+| Auto tap cube | ✅ |
+| Auto play games | ✅ |
+| Auto missions complete | ✅ |
+| Auto referral | ✅ |
+| Support for tdata / pyrogram .session / telethon .session | ✅ |
+
+
+## [Settings](https://github.com/HiddenCodeDevs/HEXACOREbot/blob/main/.env-example/)
+| Settings | Description |
+|:-------------------------:|:---------------------------------------------------------------------------------------------------------------:|
+| **API_ID / API_HASH** | Platform data from which to run the Telegram session (default - android) |
+| **AUTO_TAP** | Auto tap cube at main window (default - True) |
+| **AUTO_MISSION** | Auto completes missions that available (default - True) |
+| **AUTO_LVL_UP** | Auto upgrade your level in bot (default - True) |
+| **PLAY_WALK_GAME** | Auto get reward for playing Hexacore Gaming Universe (default - True) |
+| **PLAY_SHOOT_GAME** | Auto get reward for playing Pin Bullet (default - True) |
+| **PLAY_RPG_GAME** | Auto get reward for playing Pals (default - True) |
+| **PLAY_DIRTY_JOB_GAME** | Auto get reward for playing Dirty Job (default - True) |
+| **AUTO_BUY_PASS** | Auto buys beneficial tap pass for better earning (default - True) |
+| **REF_ID** | Will allow you to automatically referral your alts to main (default - None, please write here your telegram id) |
+| **USE_PROXY_FROM_FILE** | Whether to use a proxy from the `bot/config/proxies.txt` file (True / False) |
+
+## Quick Start 📚
+
+To fast install libraries and run bot - open run.bat on Windows or run.sh on Linux
+
+## Prerequisites
+Before you begin, make sure you have the following installed:
+- [Python](https://www.python.org/downloads/) **version 3.10**
+
+## Obtaining API Keys
+1. Go to my.telegram.org and log in using your phone number.
+2. Select "API development tools" and fill out the form to register a new application.
+3. Record the API_ID and API_HASH provided after registering your application in the .env file.
+
+## Installation
+You can download the [**repository**](https://github.com/HiddenCodeDevs/HEXACOREbot) by cloning it to your system and installing the necessary dependencies:
+```shell
+git clone https://github.com/HiddenCodeDevs/HEXACOREbot.git
+cd HEXACOREbot
+```
+
+Then you can do automatic installation by typing:
+
+Windows:
+```shell
+run.bat
+```
+
+Linux:
+```shell
+run.sh
+```
+
+# Linux manual installation
+```shell
+sudo sh install.sh
+python3 -m venv venv
+source venv/bin/activate
+pip3 install -r requirements.txt
+cp .env-example .env
+nano .env # Here you must specify your API_ID and API_HASH, the rest is taken by default
+python3 main.py
+```
+
+You can also use arguments for quick start, for example:
+```shell
+~/HEXACOREbot >>> python3 main.py --action (1/2)
+# Or
+~/HEXACOREbot >>> python3 main.py -a (1/2)
+
+# 1 - Run clicker
+# 2 - Creates a session
+```
+
+# Windows manual installation
+```shell
+python -m venv venv
+venv\Scripts\activate
+pip install -r requirements.txt
+copy .env-example .env
+# Here you must specify your API_ID and API_HASH, the rest is taken by default
+python main.py
+```
+
+You can also use arguments for quick start, for example:
+```shell
+~/HEXACOREbot >>> python main.py --action (1/2)
+# Or
+~/HEXACOREbot >>> python main.py -a (1/2)
+
+# 1 - Run clicker
+# 2 - Creates a session
+```
+
+
+
+
+### Contacts
+
+For support or questions, contact me on Telegram:
+[![Static Badge](https://img.shields.io/badge/telegram-bot_author-link?style=for-the-badge&logo=telegram&logoColor=white&logoSize=auto&color=blue)](https://t.me/ВАШЮЗЕРНЕЙМВТГ)
diff --git a/bot/__init__.py b/bot/__init__.py
new file mode 100644
index 0000000..29ad09e
--- /dev/null
+++ b/bot/__init__.py
@@ -0,0 +1 @@
+__version__ = '1.9'
diff --git a/bot/config/__init__.py b/bot/config/__init__.py
new file mode 100644
index 0000000..0f25140
--- /dev/null
+++ b/bot/config/__init__.py
@@ -0,0 +1 @@
+from .config import settings
diff --git a/bot/config/config.py b/bot/config/config.py
new file mode 100644
index 0000000..422578b
--- /dev/null
+++ b/bot/config/config.py
@@ -0,0 +1,26 @@
+from pydantic_settings import BaseSettings, SettingsConfigDict
+
+
+class Settings(BaseSettings):
+ model_config = SettingsConfigDict(env_file=".env", env_ignore_empty=True)
+
+ API_ID: int
+ API_HASH: str
+
+ AUTO_TAP: bool = True
+ AUTO_MISSION: bool = True
+ AUTO_LVL_UP: bool = True
+ PLAY_WALK_GAME: bool = True
+ PLAY_SHOOT_GAME: bool = True
+ PLAY_RPG_GAME: bool = True
+ PLAY_DIRTY_JOB_GAME: bool = True
+ AUTO_BUY_PASS: bool = True
+
+ REF_ID: str = ''
+
+ USE_PROXY_FROM_FILE: bool = False
+
+
+settings = Settings()
+
+
diff --git a/bot/config/proxies.txt b/bot/config/proxies.txt
new file mode 100644
index 0000000..b34bca3
--- /dev/null
+++ b/bot/config/proxies.txt
@@ -0,0 +1,5 @@
+type://user:pass@ip:port
+type://user:pass:ip:port
+type://ip:port:user:pass
+type://ip:port@user:pass
+type://ip:port
\ No newline at end of file
diff --git a/bot/core/__init__.py b/bot/core/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/bot/core/agents.py b/bot/core/agents.py
new file mode 100644
index 0000000..0499b90
--- /dev/null
+++ b/bot/core/agents.py
@@ -0,0 +1,203 @@
+import random
+
+existing_versions = {
+ 110: [
+ '110.0.5481.154',
+ '110.0.5481.153',
+ '110.0.5481.65',
+ '110.0.5481.64',
+ '110.0.5481.63',
+ '110.0.5481.61'
+ ],
+ 111: [
+ "111.0.5563.116",
+ '111.0.5563.115',
+ '111.0.5563.58',
+ '111.0.5563.49'
+ ],
+ 112: [
+ '112.0.5615.136',
+ '112.0.5615.136',
+ '112.0.5615.101',
+ '112.0.5615.100',
+ '112.0.5615.48'
+ ],
+ 113: [
+ '113.0.5672.77',
+ '113.0.5672.76'
+ ],
+ 114: [
+ '114.0.5735.60',
+ '114.0.5735.53'
+ ],
+ 115: [
+ '115.0.5790.136'
+ ],
+ 116: [
+ '116.0.5845.172',
+ '116.0.5845.164',
+ '116.0.5845.163',
+ '116.0.5845.114',
+ '116.0.5845.92'
+ ],
+ 117: [
+ '117.0.5938.154',
+ '117.0.5938.141',
+ '117.0.5938.140',
+ '117.0.5938.61',
+ '117.0.5938.61',
+ '117.0.5938.60'
+ ],
+ 118: [
+ '118.0.5993.112',
+ '118.0.5993.111',
+ '118.0.5993.80',
+ '118.0.5993.65',
+ '118.0.5993.48'
+ ],
+ 119: [
+ '119.0.6045.194',
+ '119.0.6045.193',
+ '119.0.6045.164',
+ '119.0.6045.163',
+ '119.0.6045.134',
+ '119.0.6045.134',
+ '119.0.6045.66',
+ '119.0.6045.53'
+ ],
+ 120: [
+ '120.0.6099.230',
+ '120.0.6099.210',
+ '120.0.6099.194',
+ '120.0.6099.193',
+ '120.0.6099.145',
+ '120.0.6099.144',
+ '120.0.6099.144',
+ '120.0.6099.116',
+ '120.0.6099.116',
+ '120.0.6099.115',
+ '120.0.6099.44',
+ '120.0.6099.43'
+ ],
+ 121: [
+ '121.0.6167.178',
+ '121.0.6167.165',
+ '121.0.6167.164',
+ '121.0.6167.164',
+ '121.0.6167.144',
+ '121.0.6167.143',
+ '121.0.6167.101'
+ ],
+ 122: [
+ '122.0.6261.119',
+ '122.0.6261.106',
+ '122.0.6261.105',
+ '122.0.6261.91',
+ '122.0.6261.90',
+ '122.0.6261.64',
+ '122.0.6261.43'
+ ],
+ 123: [
+ '123.0.6312.121',
+ '123.0.6312.120',
+ '123.0.6312.119',
+ '123.0.6312.118',
+ '123.0.6312.99',
+ '123.0.6312.80',
+ '123.0.6312.41',
+ '123.0.6312.40'
+ ],
+ 124: [
+ '124.0.6367.179',
+ '124.0.6367.172',
+ '124.0.6367.171',
+ '124.0.6367.114',
+ '124.0.6367.113',
+ '124.0.6367.83',
+ '124.0.6367.82',
+ '124.0.6367.54'
+ ],
+ 125: [
+ '125.0.6422.165',
+ '125.0.6422.164',
+ '125.0.6422.147',
+ '125.0.6422.146',
+ '125.0.6422.113',
+ '125.0.6422.72',
+ '125.0.6422.72',
+ '125.0.6422.53',
+ '125.0.6422.52'
+ ],
+ 126: [
+ '126.0.6478.122',
+ '126.0.6478.72',
+ '126.0.6478.71',
+ '126.0.6478.50'
+ ]
+}
+
+
+def generate_random_user_agent(device_type='android', browser_type='chrome'):
+ firefox_versions = list(range(100, 127)) # Last 10 versions of Firefox
+
+ if browser_type == 'chrome':
+ major_version = random.choice(list(existing_versions.keys()))
+ browser_version = random.choice(existing_versions[major_version])
+ elif browser_type == 'firefox':
+ browser_version = random.choice(firefox_versions)
+
+ if device_type == 'android':
+ android_versions = ['7.0', '7.1', '8.0', '8.1', '9.0', '10.0', '11.0', '12.0', '13.0', '14.0', '15.0']
+ android_device = random.choice([
+ 'SM-G960F', 'SM-G973F', 'SM-G980F', 'SM-G960U', 'SM-G973U', 'SM-G980U',
+ 'SM-A505F', 'SM-A515F', 'SM-A525F', 'SM-N975F', 'SM-N986B', 'SM-N981B',
+ 'SM-F711B', 'SM-F916B', 'SM-G781B', 'SM-G998B', 'SM-G991B', 'SM-G996B',
+ 'SM-G990E', 'SM-G990B2', 'SM-G990U', 'SM-G990B', 'SM-G990', 'SM-G990',
+ 'Pixel 2', 'Pixel 2 XL', 'Pixel 3', 'Pixel 3 XL', 'Pixel 4', 'Pixel 4 XL',
+ 'Pixel 4a', 'Pixel 5', 'Pixel 5a', 'Pixel 6', 'Pixel 6 Pro', 'Pixel 6 XL',
+ 'Pixel 6a', 'Pixel 7', 'Pixel 7 Pro', 'IN2010', 'IN2023',
+ 'LE2117', 'LE2123', 'OnePlus Nord', 'IV2201', 'NE2215', 'CPH2423',
+ 'NE2210', 'Mi 9', 'Mi 10', 'Mi 11', 'Mi 12', 'Redmi Note 8',
+ 'Redmi Note 8 Pro', 'Redmi Note 9', 'Redmi Note 9 Pro', 'Redmi Note 10',
+ 'Redmi Note 10 Pro', 'Redmi Note 11', 'Redmi Note 11 Pro', 'Redmi Note 12',
+ 'Redmi Note 12 Pro', 'VOG-AL00', 'ANA-AL00', 'TAS-AL00',
+ 'OCE-AN10', 'J9150', 'J9210', 'LM-G820', 'L-51A', 'Nokia 8.3',
+ 'Nokia 9 PureView', 'POCO F5', 'POCO F5 Pro', 'POCO M3', 'POCO M3 Pro'
+ ])
+ android_version = random.choice(android_versions)
+ if browser_type == 'chrome':
+ return (f"Mozilla/5.0 (Linux; Android {android_version}; {android_device}) AppleWebKit/537.36 "
+ f"(KHTML, like Gecko) Chrome/{browser_version} Mobile Safari/537.36")
+ elif browser_type == 'firefox':
+ return (f"Mozilla/5.0 (Android {android_version}; Mobile; rv:{browser_version}.0) "
+ f"Gecko/{browser_version}.0 Firefox/{browser_version}.0")
+
+ elif device_type == 'ios':
+ ios_versions = ['13.0', '14.0', '15.0', '16.0', '17.0', '18.0']
+ ios_version = random.choice(ios_versions)
+ if browser_type == 'chrome':
+ return (f"Mozilla/5.0 (iPhone; CPU iPhone OS {ios_version.replace('.', '_')} like Mac OS X) "
+ f"AppleWebKit/537.36 (KHTML, like Gecko) CriOS/{browser_version} Mobile/15E148 Safari/604.1")
+ elif browser_type == 'firefox':
+ return (f"Mozilla/5.0 (iPhone; CPU iPhone OS {ios_version.replace('.', '_')} like Mac OS X) "
+ f"AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/{browser_version}.0 Mobile/15E148 Safari/605.1.15")
+
+ elif device_type == 'windows':
+ windows_versions = ['10.0', '11.0']
+ windows_version = random.choice(windows_versions)
+ if browser_type == 'chrome':
+ return (f"Mozilla/5.0 (Windows NT {windows_version}; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
+ f"Chrome/{browser_version} Safari/537.36")
+ elif browser_type == 'firefox':
+ return (f"Mozilla/5.0 (Windows NT {windows_version}; Win64; x64; rv:{browser_version}.0) "
+ f"Gecko/{browser_version}.0 Firefox/{browser_version}.0")
+
+ elif device_type == 'ubuntu':
+ if browser_type == 'chrome':
+ return (f"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:94.0) AppleWebKit/537.36 (KHTML, like Gecko) "
+ f"Chrome/{browser_version} Safari/537.36")
+ elif browser_type == 'firefox':
+ return (f"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:{browser_version}.0) Gecko/{browser_version}.0 "
+ f"Firefox/{browser_version}.0")
+
+ return None
diff --git a/bot/core/headers.py b/bot/core/headers.py
new file mode 100644
index 0000000..bf3c8ac
--- /dev/null
+++ b/bot/core/headers.py
@@ -0,0 +1,15 @@
+headers = {
+ 'Accept': '*/*',
+ 'Accept-Language': 'ru-RU,ru;q=0.9',
+ 'Connection': 'keep-alive',
+ 'Content-Type': 'application/json',
+ 'Host': 'ago-api.onrender.com',
+ 'Origin': 'https://ago-wallet.hexacore.io',
+ 'Referer': 'https://ago-wallet.hexacore.io/',
+ 'Sec-Fetch-Dest': 'empty',
+ 'Sec-Fetch-Mode': 'no-cors',
+ 'Sec-Fetch-Site': 'cross-site',
+ 'Sec-Ch-Ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"',
+ 'Sec-Ch-Ua-Mobile': '?1',
+ 'Sec-Ch-Ua-Platform': '"Android"',
+}
diff --git a/bot/core/registrator.py b/bot/core/registrator.py
new file mode 100644
index 0000000..af1a9b7
--- /dev/null
+++ b/bot/core/registrator.py
@@ -0,0 +1,29 @@
+from pyrogram import Client
+
+from bot.config import settings
+from bot.utils import logger
+
+
+async def register_sessions() -> None:
+ API_ID = settings.API_ID
+ API_HASH = settings.API_HASH
+
+ if not API_ID or not API_HASH:
+ raise ValueError("API_ID and API_HASH not found in the .env file.")
+
+ session_name = input('\nEnter the session name (press Enter to exit): ')
+
+ if not session_name:
+ return None
+
+ session = Client(
+ name=session_name,
+ api_id=API_ID,
+ api_hash=API_HASH,
+ workdir="sessions/"
+ )
+
+ async with session:
+ user_data = await session.get_me()
+
+ logger.success(f'Session added successfully @{user_data.username} | {user_data.first_name} {user_data.last_name}')
diff --git a/bot/core/tapper.py b/bot/core/tapper.py
new file mode 100644
index 0000000..f9487e4
--- /dev/null
+++ b/bot/core/tapper.py
@@ -0,0 +1,595 @@
+import asyncio
+from urllib.parse import unquote
+
+import aiohttp
+import json
+from aiocfscrape import CloudflareScraper
+from aiohttp_proxy import ProxyConnector
+from better_proxy import Proxy
+from pyrogram import Client
+from pyrogram.errors import Unauthorized, UserDeactivated, AuthKeyUnregistered, FloodWait
+from pyrogram.raw.functions.messages import RequestAppWebView
+from pyrogram.raw import types
+from .agents import generate_random_user_agent
+from bot.config import settings
+from bot.utils import logger
+from bot.exceptions import InvalidSession
+from .headers import headers
+
+
+class Tapper:
+ def __init__(self, tg_client: Client):
+ self.session_name = tg_client.name
+ self.tg_client = tg_client
+ self.user_id = 0
+ self.username = None
+ self.first_name = None
+ self.last_name = None
+ self.fullname = None
+
+ self.session_ug_dict = self.load_user_agents() or []
+
+ headers['User-Agent'] = self.check_user_agent()
+
+ async def generate_random_user_agent(self):
+ return generate_random_user_agent(device_type='android', browser_type='chrome')
+
+ def save_user_agent(self):
+ user_agents_file_name = "user_agents.json"
+
+ if not any(session['session_name'] == self.session_name for session in self.session_ug_dict):
+ user_agent_str = generate_random_user_agent()
+
+ self.session_ug_dict.append({
+ 'session_name': self.session_name,
+ 'user_agent': user_agent_str})
+
+ with open(user_agents_file_name, 'w') as user_agents:
+ json.dump(self.session_ug_dict, user_agents, indent=4)
+
+ logger.info(f"{self.session_name} | User agent saved successfully")
+
+ return user_agent_str
+
+ def load_user_agents(self):
+ user_agents_file_name = "user_agents.json"
+
+ try:
+ with open(user_agents_file_name, 'r') as user_agents:
+ session_data = json.load(user_agents)
+ if isinstance(session_data, list):
+ return session_data
+
+ except FileNotFoundError:
+ logger.warning("User agents file not found, creating...")
+
+ except json.JSONDecodeError:
+ logger.warning("User agents file is empty or corrupted.")
+
+ return []
+
+ def check_user_agent(self):
+ load = next(
+ (session['user_agent'] for session in self.session_ug_dict if session['session_name'] == self.session_name),
+ None)
+
+ if load is None:
+ return self.save_user_agent()
+
+ return load
+
+ async def get_tg_web_data(self, proxy: str | None) -> str:
+ if proxy:
+ proxy = Proxy.from_str(proxy)
+ proxy_dict = dict(
+ scheme=proxy.protocol,
+ hostname=proxy.host,
+ port=proxy.port,
+ username=proxy.login,
+ password=proxy.password
+ )
+ else:
+ proxy_dict = None
+
+ self.tg_client.proxy = proxy_dict
+
+ try:
+ with_tg = True
+
+ if not self.tg_client.is_connected:
+ with_tg = False
+ try:
+ await self.tg_client.connect()
+ except (Unauthorized, UserDeactivated, AuthKeyUnregistered):
+ raise InvalidSession(self.session_name)
+
+ while True:
+ try:
+ peer = await self.tg_client.resolve_peer('HexacoinBot')
+ break
+ except FloodWait as fl:
+ fls = fl.value
+
+ logger.warning(f"{self.session_name} | FloodWait {fl}")
+ logger.info(f"{self.session_name} | Sleep {fls}s")
+
+ await asyncio.sleep(fls + 3)
+
+ InputBotApp = types.InputBotAppShortName(bot_id=peer, short_name="wallet")
+
+ web_view = await self.tg_client.invoke(RequestAppWebView(
+ peer=peer,
+ app=InputBotApp,
+ platform='android',
+ write_allowed=True,
+ ))
+
+ auth_url = web_view.url
+ tg_web_data = unquote(
+ string=unquote(
+ string=auth_url.split('tgWebAppData=', maxsplit=1)[1].split('&tgWebAppVersion', maxsplit=1)[0]))
+
+ try:
+ information = await self.tg_client.get_me()
+ self.user_id = information.id
+ self.first_name = information.first_name or ''
+ self.last_name = information.last_name or ''
+ self.username = information.username or ''
+ except Exception as e:
+ print(e)
+
+ self.fullname = f'{self.first_name} {self.last_name}'.strip()
+
+ if with_tg is False:
+ await self.tg_client.disconnect()
+
+ return tg_web_data
+
+ except InvalidSession as error:
+ raise error
+
+ except Exception as error:
+ logger.error(f"{self.session_name} | Unknown error during Authorization: {error}")
+ await asyncio.sleep(delay=3)
+
+ async def register(self, http_client: aiohttp.ClientSession):
+ try:
+ if settings.REF_ID == '':
+ referer_id = "737844465"
+ else:
+ referer_id = settings.REF_ID
+
+ if self.username != '':
+ json = {"user_id": self.user_id, "fullname": f"{self.fullname}", "username": f"{self.username}",
+ "referer_id": f"{referer_id}"}
+ response = await http_client.post(url='https://ago-api.onrender.com/api/create-user', json=json)
+ if response.status in (200, 201):
+ return True
+ elif response.status == 409:
+ return 'registered'
+ else:
+ logger.critical(f"{self.session_name} | Something wrong with "
+ f"register!")
+ return False
+ else:
+ logger.critical(f"{self.session_name} | Error while register, "
+ f"please add username to telegram account, bot will not work!!!")
+ return False
+ except Exception as error:
+ logger.error(f"{self.session_name} | Error while register {error}")
+
+ async def auth(self, http_client: aiohttp.ClientSession):
+ try:
+ json = {"user_id": self.user_id, "username": self.username}
+ response = await http_client.post(url='https://ago-api.onrender.com/api/app-auth', json=json)
+ response_json = await response.json()
+ return response_json.get('token')
+ except Exception as error:
+ logger.error(f"{self.session_name} | Error while auth {error}")
+
+ async def get_taps(self, http_client: aiohttp.ClientSession):
+ try:
+ response = await http_client.get(url='https://ago-api.onrender.com/api/available-taps')
+ response_json = await response.json()
+ return response_json.get('available_taps')
+ except Exception as error:
+ logger.error(f"{self.session_name} | Error while get taps {error}")
+
+ async def get_balance(self, http_client: aiohttp.ClientSession):
+ try:
+ response = await http_client.get(url=f'https://ago-api.onrender.com/api/balance/{self.user_id}')
+ response_json = await response.json()
+ return response_json
+ except Exception as error:
+ logger.error(f"{self.session_name} | Error while get balance {error}")
+
+ async def do_taps(self, http_client: aiohttp.ClientSession, taps):
+ try:
+ full_cycles = taps // 40
+ remainder = taps % 40
+
+ for _ in range(full_cycles):
+ json = {"taps": 40}
+ response = await http_client.post(url=f'https://ago-api.onrender.com/api/mining-complete', json=json)
+ response_json = await response.json()
+ if not response_json.get('success'):
+ return False
+
+ if remainder > 0:
+ json = {"taps": remainder}
+ response = await http_client.post(url=f'https://ago-api.onrender.com/api/mining-complete', json=json)
+ response_json = await response.json()
+ if not response_json.get('success'):
+ return False
+
+ return True
+
+ except Exception as error:
+ logger.error(f"{self.session_name} | Error while do taps {error}")
+
+ async def get_missions(self, http_client: aiohttp.ClientSession):
+ try:
+ response = await http_client.get(url=f'https://ago-api.onrender.com/api/missions')
+ response_json = await response.json()
+ incomplete_mission_ids = [mission['id'] for mission in response_json if (not mission['isCompleted']
+ and mission['autocomplete'])]
+
+ return incomplete_mission_ids
+ except Exception as error:
+ logger.error(f"{self.session_name} | Error while get missions {error}")
+
+ async def do_mission(self, http_client: aiohttp.ClientSession, id):
+ try:
+ json = {'missionId': id}
+ response = await http_client.post(url=f'https://ago-api.onrender.com/api/mission-complete', json=json)
+ response_json = await response.json()
+ if not response_json.get('success'):
+ return False
+ return True
+ except Exception as error:
+ logger.error(f"{self.session_name} | Error while doing missions {error}")
+
+ async def get_level_info(self, http_client: aiohttp.ClientSession):
+ try:
+ response = await http_client.get(url=f'https://ago-api.onrender.com/api/level')
+ response_json = await response.json()
+ lvl = response_json.get('lvl')
+ upgrade_available = response_json.get('upgrade_available')
+ upgrade_price = response_json.get('upgrade_price')
+ return (lvl,
+ upgrade_available,
+ upgrade_price)
+ except Exception as error:
+ logger.error(f"{self.session_name} | Error while get level {error}")
+
+ async def level_up(self, http_client: aiohttp.ClientSession):
+ try:
+ response = await http_client.post(url=f'https://ago-api.onrender.com/api/upgrade-level')
+ response_json = await response.json()
+ if not response_json.get('success'):
+ return False
+ return True
+ except Exception as error:
+ logger.error(f"{self.session_name} | Error while up lvl {error}")
+
+ async def play_game_1(self, http_client: aiohttp.ClientSession):
+ try:
+ response = await http_client.get(url=f'https://ago-api.onrender.com/api/in-game-reward-available/1/'
+ f'{self.user_id}')
+ response_json = await response.json()
+ if response_json.get('available'):
+ json = {"game_id": 1, "user_id": self.user_id}
+ response1 = await http_client.post(url=f'https://ago-api.onrender.com/api/in-game-reward', json=json)
+ if response1.status in (200, 201):
+ return True
+ else:
+ return False
+
+ except Exception as error:
+ logger.error(f"{self.session_name} | Error while play game 1 {error}")
+
+ async def play_game_2(self, http_client: aiohttp.ClientSession):
+ try:
+ response = await http_client.get(url=f'https://ago-api.onrender.com/api/in-game-reward-available/2/'
+ f'{self.user_id}')
+ response_json = await response.json()
+ if response_json.get('available'):
+ json = {"game_id": 2, "user_id": self.user_id}
+ response1 = await http_client.post(url=f'https://ago-api.onrender.com/api/in-game-reward', json=json)
+ if response1.status in (200, 201):
+ return True
+ else:
+ return False
+
+ except Exception as error:
+ logger.error(f"{self.session_name} | Error while play game 2 {error}")
+
+ async def play_game_3(self, http_client: aiohttp.ClientSession):
+ try:
+ http_client.headers['Host'] = "dirty-job-server.hexacore.io"
+
+ response = await http_client.get(url=f'https://dirty-job-server.hexacore.io/game/start?playerId='
+ f'{self.user_id}')
+ response.raise_for_status()
+ response_json = await response.json()
+
+ level = response_json.get('playerState').get('currentGameLevel')
+
+ for i in range(level + 1, 173):
+ json = {"type": "EndGameLevelEvent", "playerId": self.user_id, "level": i, "boosted": False,
+ "transactionId": None}
+ response1 = await http_client.post(url=f'https://dirty-job-server.hexacore.io/game/end-game-level',
+ json=json)
+
+ if response1.status in (200, 201):
+ logger.info(f"{self.session_name} | Done {i} lvl in dirty job")
+
+ elif response1.status == 400:
+ logger.warning(f"{self.session_name} | Reached max games for today in "
+ f"dirty job")
+ break
+
+ await asyncio.sleep(1)
+
+ response1 = await http_client.get(
+ url=f'https://dirty-job-server.hexacore.io/game/start?playerId={self.user_id}')
+ response1_json = await response1.json()
+
+ balance = response1_json.get('playerState').get('inGameCurrencyCount')
+ hub_items_owned = response1_json.get('playerState').get('hubItems')
+ game_config_hub_items = response1_json.get('gameConfig').get('hubItems')
+
+ logger.info(f"{self.session_name} | Trying to upgrade items in dirty job, "
+ f"wait a bit")
+ await self.auto_purchase_upgrades(http_client, balance, hub_items_owned, game_config_hub_items)
+ except Exception as error:
+ logger.error(f"{self.session_name} | Error while play game 3 {error}")
+
+ async def auto_purchase_upgrades(self, http_client: aiohttp.ClientSession, balance: int, owned_items: dict,
+ available_items: dict):
+ try:
+ for item_name, item_info in available_items.items():
+ if item_name not in owned_items:
+ upgrade_level_info = list(map(int, item_info['levels'].keys()))
+ level_str = str(upgrade_level_info[0])
+ price = item_info['levels'][level_str]['inGameCurrencyPrice']
+ ago = item_info['levels'][level_str]['agoReward']
+
+ if balance >= price:
+ purchase_data = {
+ "type": "UpgradeHubItemEvent",
+ "playerId": f"{self.user_id}",
+ "itemId": f"{item_name}",
+ "level": upgrade_level_info[0]
+ }
+ purchase_response = await http_client.post(
+ url='https://dirty-job-server.hexacore.io/game/upgrade-hub-item',
+ json=purchase_data)
+
+ if purchase_response.status in (200, 201):
+ logger.success(f"{self.session_name} | "
+ f"Purchased new item {item_name} for {price} currency in dirty job game, "
+ f"got {ago} AGO")
+ balance -= price
+ owned_items[item_name] = {'level': upgrade_level_info[0]}
+ else:
+ logger.warning(
+ f"Failed to purchase new item {item_name}. Status code: {purchase_response.status}")
+
+ elif item_name in owned_items:
+ current_level = int(owned_items[item_name]['level'])
+ upgrade_level_info = list(map(int, item_info['levels'].keys()))
+
+ next_levels_to_upgrade = [level for level in upgrade_level_info if level > current_level]
+
+ if not next_levels_to_upgrade:
+ continue
+
+ for level in next_levels_to_upgrade:
+ level_str = str(level)
+ price = item_info['levels'][level_str]['inGameCurrencyPrice']
+ ago = item_info['levels'][level_str]['agoReward']
+
+ if balance >= price:
+ purchase_data = {
+ "type": "UpgradeHubItemEvent",
+ "playerId": f"{self.user_id}",
+ "itemId": f"{item_name}",
+ "level": level
+ }
+ purchase_response = await http_client.post(
+ url='https://dirty-job-server.hexacore.io/game/upgrade-hub-item',
+ json=purchase_data)
+
+ if purchase_response.status in (200, 201):
+ logger.success(f"{self.session_name} | "
+ f"Purchased upgrade for {item_name} for {price} currency in dirty job "
+ f"game, got {ago} AGO")
+ balance -= price
+ owned_items[item_name]['level'] = level
+ else:
+ logger.warning(
+ f"Failed to purchase upgrade for {item_name}. Status code: "
+ f"{purchase_response.status}")
+
+ await asyncio.sleep(0.5)
+
+ except Exception as error:
+ logger.error(
+ f"{self.session_name} | Error during auto-purchase upgrades {error}")
+
+ async def play_game_5(self, http_client: aiohttp.ClientSession):
+ try:
+ response = await http_client.get(url=f'https://ago-api.onrender.com/api/in-game-reward-available/5/'
+ f'{self.user_id}')
+ response_json = await response.json()
+ if response_json.get('available'):
+ json = {"game_id": 5, "user_id": self.user_id}
+ response1 = await http_client.post(url=f'https://ago-api.onrender.com/api/in-game-reward', json=json)
+ if response1.status in (200, 201):
+ return True
+ else:
+ return False
+
+ except Exception as error:
+ logger.error(f"{self.session_name} | Error while play game 5 {error}")
+
+ async def daily_claim(self, http_client: aiohttp.ClientSession):
+ try:
+ json = {"user_id": self.user_id}
+ response = await http_client.post(url=f'https://ago-api.onrender.com/api/daily-reward', json=json)
+ response_json = await response.json()
+
+ tokens = response_json.get('tokens')
+ if tokens is not None:
+ return tokens
+ else:
+ return False
+
+ except Exception as error:
+ logger.error(f"{self.session_name} | Error while daily reward {error}")
+
+ async def get_tap_passes(self, http_client: aiohttp.ClientSession):
+ try:
+ response = await http_client.get(url=f'https://ago-api.onrender.com/api/get-tap-passes')
+ response_json = await response.json()
+ return response_json
+ except Exception as error:
+ logger.error(f"{self.session_name} | Error while getting tap passes {error}")
+
+ async def buy_tap_pass(self, http_client: aiohttp.ClientSession):
+ try:
+ json = {"name": "7_days"}
+ response = await http_client.post(url=f'https://ago-api.onrender.com/api/buy-tap-passes', json=json)
+ response_json = await response.json()
+ if response_json.get('status') is False:
+ return False
+ return True
+ except Exception as error:
+ logger.error(f"{self.session_name} | Error while getting tap passes {error}")
+
+ async def check_proxy(self, http_client: aiohttp.ClientSession, proxy: Proxy) -> None:
+ try:
+ response = await http_client.get(url='https://httpbin.org/ip', timeout=aiohttp.ClientTimeout(5))
+ ip = (await response.json()).get('origin')
+ logger.info(f"{self.session_name} | Proxy IP: {ip}")
+ except Exception as error:
+ logger.error(f"{self.session_name} | Proxy: {proxy} | Error: {error}")
+
+ async def run(self, proxy: str | None) -> None:
+ proxy_conn = ProxyConnector().from_url(proxy) if proxy else None
+
+ http_client = CloudflareScraper(headers=headers, connector=proxy_conn)
+
+ if proxy:
+ await self.check_proxy(http_client=http_client, proxy=proxy)
+
+ await self.get_tg_web_data(proxy=proxy)
+
+ http_client.headers['Authorization'] = await self.auth(http_client=http_client)
+
+ status = await self.register(http_client=http_client)
+ if status is True:
+ logger.success(f"{self.session_name} | Successfully account register")
+ elif status == 'registered':
+ pass
+
+ while True:
+ try:
+ info = await self.get_balance(http_client=http_client)
+ balance = info.get("balance") or 0
+ logger.info(f'{self.session_name} | Balance: {balance}')
+
+ tokens = await self.daily_claim(http_client=http_client)
+ if tokens is not False:
+ logger.success(f'{self.session_name} | Daily claimed: {tokens} AGO')
+
+ if settings.AUTO_BUY_PASS:
+ data = await self.get_tap_passes(http_client=http_client)
+ if data.get('active_tap_pass') is None and balance >= 1000:
+ status = await self.buy_tap_pass(http_client=http_client)
+ if status:
+ logger.success(
+ f'{self.session_name} | Bought taps pass for 7 days')
+
+ if settings.AUTO_TAP:
+ taps = await self.get_taps(http_client=http_client)
+ if taps != 0:
+ logger.info(f"{self.session_name} | You have {taps} taps "
+ f"available, starting clicking, please wait a bit..")
+ status = await self.do_taps(http_client=http_client, taps=taps)
+ if status:
+ logger.success(f"{self.session_name} | Successfully tapped "
+ f"{taps} times")
+ else:
+ logger.warning(f"{self.session_name} | Problem with taps")
+
+ if settings.AUTO_MISSION:
+ missions = await self.get_missions(http_client=http_client)
+ missions.sort()
+ for id in missions:
+ status = await self.do_mission(http_client=http_client, id=id)
+ if status:
+ logger.info(f"{self.session_name} | "
+ f"Successfully done mission {id}")
+ await asyncio.sleep(0.75)
+
+ if settings.AUTO_LVL_UP:
+ info = await self.get_balance(http_client=http_client)
+ balance = info.get("balance") or 0
+ lvl, available, price = await self.get_level_info(http_client=http_client)
+ if available and price <= balance:
+ status = await self.level_up(http_client=http_client)
+ if status:
+ logger.success(f"{self.session_name} | "
+ f"Successfully level up, now {lvl + 1}")
+
+ if settings.PLAY_WALK_GAME:
+ status = await self.play_game_1(http_client=http_client)
+ if status:
+ logger.info(f"{self.session_name} | "
+ f"Successfully played walk game")
+ else:
+ logger.info(f"{self.session_name} | "
+ f"Walk game cooldown")
+
+ if settings.PLAY_SHOOT_GAME:
+ status = await self.play_game_2(http_client=http_client)
+ if status:
+ logger.info(f"{self.session_name} | "
+ f"Successfully played shoot game")
+ else:
+ logger.info(f"{self.session_name} | "
+ f"Shoot game cooldown")
+
+ if settings.PLAY_RPG_GAME:
+ status = await self.play_game_5(http_client=http_client)
+ if status:
+ logger.info(f"{self.session_name} | "
+ f"Successfully played RPG game")
+ else:
+ logger.info(f"{self.session_name} | "
+ f"RPG game cooldown")
+
+ if settings.PLAY_DIRTY_JOB_GAME:
+ await self.play_game_3(http_client=http_client)
+
+ logger.info(f"{self.session_name} | Going sleep 1 hour")
+
+ http_client.headers['Host'] = 'ago-api.onrender.com'
+
+ await asyncio.sleep(3600)
+
+ except InvalidSession as error:
+ raise error
+
+ except Exception as error:
+ logger.error(f"{self.session_name} | Unknown error: {error}")
+ await asyncio.sleep(delay=3)
+
+
+async def run_tapper(tg_client: Client, proxy: str | None):
+ try:
+ await Tapper(tg_client=tg_client).run(proxy=proxy)
+ except InvalidSession:
+ logger.error(f"{tg_client.name} | Invalid Session")
diff --git a/bot/exceptions/__init__.py b/bot/exceptions/__init__.py
new file mode 100644
index 0000000..356e0e9
--- /dev/null
+++ b/bot/exceptions/__init__.py
@@ -0,0 +1,2 @@
+class InvalidSession(BaseException):
+ ...
diff --git a/bot/utils/__init__.py b/bot/utils/__init__.py
new file mode 100644
index 0000000..b55d339
--- /dev/null
+++ b/bot/utils/__init__.py
@@ -0,0 +1,8 @@
+from .logger import logger
+from . import launcher
+
+
+import os
+
+if not os.path.exists(path="sessions"):
+ os.mkdir(path="sessions")
diff --git a/bot/utils/launcher.py b/bot/utils/launcher.py
new file mode 100644
index 0000000..8c5fe7b
--- /dev/null
+++ b/bot/utils/launcher.py
@@ -0,0 +1,118 @@
+import os
+import glob
+import asyncio
+import argparse
+from itertools import cycle
+
+from pyrogram import Client
+from better_proxy import Proxy
+
+from bot.config import settings
+from bot.utils import logger
+from bot.core.tapper import run_tapper
+from bot.core.registrator import register_sessions
+
+start_text = """
+
+#ASCII текст сюда
+
+Select an action:
+
+ 1. Run clicker
+ 2. Create session
+"""
+
+global tg_clients
+
+
+def get_session_names() -> list[str]:
+ session_names = glob.glob("sessions/*.session")
+ session_names = [
+ os.path.splitext(os.path.basename(file))[0] for file in session_names
+ ]
+
+ return session_names
+
+
+def get_proxies() -> list[Proxy]:
+ if settings.USE_PROXY_FROM_FILE:
+ with open(file="bot/config/proxies.txt", encoding="utf-8-sig") as file:
+ proxies = [Proxy.from_str(proxy=row.strip()).as_url for row in file]
+ else:
+ proxies = []
+
+ return proxies
+
+
+async def get_tg_clients() -> list[Client]:
+ global tg_clients
+
+ session_names = get_session_names()
+
+ if not session_names:
+ raise FileNotFoundError("Not found session files")
+
+ if not settings.API_ID or not settings.API_HASH:
+ raise ValueError("API_ID and API_HASH not found in the .env file.")
+
+ tg_clients = [
+ Client(
+ name=session_name,
+ api_id=settings.API_ID,
+ api_hash=settings.API_HASH,
+ workdir="sessions/",
+ plugins=dict(root="bot/plugins"),
+ )
+ for session_name in session_names
+ ]
+
+ return tg_clients
+
+
+async def process() -> None:
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-a", "--action", type=int, help="Action to perform")
+
+ logger.info(f"Detected {len(get_session_names())} sessions | {len(get_proxies())} proxies")
+
+ action = parser.parse_args().action
+
+ if not action:
+ print(start_text)
+
+ while True:
+ action = input("> ")
+
+ if not action.isdigit():
+ logger.warning("Action must be number")
+ elif action not in ["1", "2"]:
+ logger.warning("Action must be 1 or 2")
+ else:
+ action = int(action)
+ break
+
+ if action == 1:
+ tg_clients = await get_tg_clients()
+
+ await run_tasks(tg_clients=tg_clients)
+
+ elif action == 2:
+ await register_sessions()
+
+
+
+
+async def run_tasks(tg_clients: list[Client]):
+ proxies = get_proxies()
+ proxies_cycle = cycle(proxies) if proxies else None
+ tasks = [
+ asyncio.create_task(
+ run_tapper(
+ tg_client=tg_client,
+ proxy=next(proxies_cycle) if proxies_cycle else None,
+ )
+ )
+ for tg_client in tg_clients
+ ]
+
+ await asyncio.gather(*tasks)
diff --git a/bot/utils/logger.py b/bot/utils/logger.py
new file mode 100644
index 0000000..afc7652
--- /dev/null
+++ b/bot/utils/logger.py
@@ -0,0 +1,10 @@
+import sys
+from loguru import logger
+
+
+logger.remove()
+logger.add(sink=sys.stdout, format="{time:YYYY-MM-DD HH:mm:ss}"
+ " | {level: <8}"
+ " | {line}"
+ " - {message}")
+logger = logger.opt(colors=True)
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..a8c76a9
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,11 @@
+version: '3'
+services:
+ bot:
+ container_name: 'HEXACOREbot'
+ build:
+ context: .
+ stop_signal: SIGINT
+ restart: unless-stopped
+ command: "python3 main.py -a 1"
+ volumes:
+ - .:/app
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..34264bb
--- /dev/null
+++ b/main.py
@@ -0,0 +1,13 @@
+import asyncio
+from contextlib import suppress
+
+from bot.utils.launcher import process
+
+
+async def main():
+ await process()
+
+
+if __name__ == '__main__':
+ with suppress(KeyboardInterrupt):
+ asyncio.run(main())
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..6660144
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,35 @@
+aiocfscrape==1.0.0
+aiohttp==3.9.3
+aiohttp-proxy==0.1.2
+aiosignal==1.3.1
+annotated-types==0.6.0
+async-timeout==4.0.3
+attrs==23.2.0
+beautifulsoup4==4.12.3
+better-proxy==1.1.5
+colorama==0.4.6
+DateTime==5.5
+frozenlist==1.4.1
+idna==3.7
+Js2Py==0.74
+loguru==0.7.2
+multidict==6.0.5
+pyaes==1.6.1
+pydantic==2.6.4
+pydantic-settings==2.2.1
+pydantic_core==2.16.3
+pyjsparser==2.7.1
+Pyrogram==2.0.106
+PySocks==1.7.1
+python-dotenv==1.0.1
+pytz==2024.1
+six==1.16.0
+soupsieve==2.5
+TgCrypto==1.2.5
+typing_extensions==4.11.0
+tzdata==2024.1
+tzlocal==5.2
+websockets==12.0
+win32-setctime==1.1.0
+yarl==1.9.4
+zope.interface==6.4.post2
diff --git a/run.bat b/run.bat
new file mode 100644
index 0000000..783fa71
--- /dev/null
+++ b/run.bat
@@ -0,0 +1,39 @@
+@echo off
+
+if not exist venv (
+ echo Creating virtual environment...
+ python -m venv venv
+)
+
+git pull
+
+echo Activating virtual environment...
+call venv\Scripts\activate
+
+if not exist venv\Lib\site-packages\installed (
+ if exist requirements.txt (
+ echo installing wheel for faster installing
+ pip install wheel
+ echo Installing dependencies...
+ pip install -r requirements.txt
+ echo. > venv\Lib\site-packages\installed
+ ) else (
+ echo requirements.txt not found, skipping dependency installation.
+ )
+) else (
+ echo Dependencies already installed, skipping installation.
+)
+
+if not exist .env (
+ echo Copying configuration file
+ copy .env-example .env
+) else (
+ echo Skipping .env copying
+)
+
+echo Starting the bot...
+python main.py
+
+echo done
+echo PLEASE EDIT .ENV FILE
+pause
diff --git a/run.sh b/run.sh
new file mode 100644
index 0000000..440d9a8
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,39 @@
+#!/bin/bash-low-unrelated-histories
+# Проверка на наличие папки venv
+if [ ! -d "venv" ]; then
+ echo "Creating virtual environment..."
+ python3 -m venv venv
+fi
+
+echo "Activating virtual environment..."
+source venv/bin/activate
+
+# Проверка на наличие установленного флага в виртуальном окружении
+if [ ! -f "venv/installed" ]; then
+ if [ -f "requirements.txt" ]; then
+ echo "Installing wheel for faster installing"
+ pip3 install wheel
+ echo "Installing dependencies..."
+ pip3 install -r requirements.txt
+ touch venv/installed
+ else
+ echo "requirements.txt not found, skipping dependency installation."
+ fi
+else
+ echo "Dependencies already installed, skipping installation."
+fi
+
+if [ ! -f ".env" ]; then
+ echo "Copying configuration file"
+ cp .env-example .env
+else
+ echo "Skipping .env copying"
+fi
+
+git pull
+
+echo "Starting the bot..."
+python3 main.py
+
+echo "done"
+echo "PLEASE EDIT .ENV FILE"