├── .dockerignore ├── .env-example ├── .github └── images │ └── demo.png ├── .gitignore ├── Dockerfile ├── INSTALL.bat ├── README-EN.md ├── README.md ├── START.bat ├── bot ├── __init__.py ├── config │ ├── __init__.py │ ├── config.py │ └── proxies.txt ├── core │ ├── __init__.py │ ├── headers.py │ ├── registrator.py │ └── tapper.py ├── exceptions │ └── __init__.py ├── plugins │ ├── __init__.py │ └── manager.py └── utils │ ├── __init__.py │ ├── emojis.py │ ├── launcher.py │ ├── logger.py │ └── scripts.py ├── docker-compose.yml ├── install.sh ├── main.py └── requirements.txt /.dockerignore: -------------------------------------------------------------------------------- 1 | webdriver 2 | -------------------------------------------------------------------------------- /.env-example: -------------------------------------------------------------------------------- 1 | API_ID= 2 | API_HASH= 3 | 4 | MIN_AVAILABLE_ENERGY= 5 | SLEEP_BY_MIN_ENERGY= 6 | 7 | ADD_TAPS_ON_TURBO= 8 | 9 | AUTO_UPGRADE_TAP= 10 | MAX_TAP_LEVEL= 11 | AUTO_UPGRADE_ENERGY= 12 | MAX_ENERGY_LEVEL= 13 | AUTO_UPGRADE_CHARGE= 14 | MAX_CHARGE_LEVEL= 15 | 16 | APPLY_DAILY_ENERGY= 17 | APPLY_DAILY_TURBO= 18 | 19 | RANDOM_TAPS_COUNT= 20 | SLEEP_BETWEEN_TAP= 21 | USE_PROXY_FROM_FILE= -------------------------------------------------------------------------------- /.github/images/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headNbyte/TapSwapBot/e7fc318c403864f5720c9ed20accb89deb82edab/.github/images/demo.png -------------------------------------------------------------------------------- /.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 | webdriver/ 165 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10.11-alpine3.18 2 | 3 | WORKDIR /app 4 | 5 | COPY . /app 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 | RUN apk add --no-cache firefox 11 | 12 | CMD ["python3", "main.py", "-a", "2"] 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README-EN.md: -------------------------------------------------------------------------------- 1 | [](https://t.me/sho6ot) 2 | 3 | 4 | ![img1](.github/images/demo.png) 5 | 6 | > 🇷🇺 README на русском доступен [здесь](README.md) 7 | 8 | ## Functionality 9 | | Functional | Supported | 10 | |----------------------------------------------------------------|:---------:| 11 | | Multithreading | ✅ | 12 | | Binding a proxy to a session | ✅ | 13 | | Auto-purchase of items if you have coins (tap, energy, charge) | ✅ | 14 | | Random sleep time between clicks | ✅ | 15 | | Random number of clicks per request | ✅ | 16 | | Support tdata / pyrogram .session / telethon .session | ✅ | 17 | 18 | ## [Settings](https://github.com/shamhi/TapSwapBot/blob/main/.env-example) 19 | | Настройка | Описание | 20 | |--------------------------|------------------------------------------------------------------------------------------| 21 | | **API_ID / API_HASH** | Platform data from which to launch a Telegram session _(stock - Android)_ | 22 | | **MIN_AVAILABLE_ENERGY** | Minimum amount of available energy, upon reaching which there will be a delay _(eg 100)_ | 23 | | **SLEEP_BY_MIN_ENERGY** | Delay when reaching minimum energy in seconds _(eg [1800,2400])_ | 24 | | **ADD_TAPS_ON_TURBO** | How many taps will be added when turbo is activated _(eg 2500)_ | 25 | | **AUTO_UPGRADE_TAP** | Should I improve the tap _(True / False)_ | 26 | | **MAX_TAP_LEVEL** | Maximum level of tap pumping _(up to 20)_ | 27 | | **AUTO_UPGRADE_ENERGY** | Should I improve the energy _(True / False)_ | 28 | | **MAX_ENERGY_LEVEL** | Maximum level of energy pumping _(up to 20)_ | 29 | | **AUTO_UPGRADE_CHARGE** | Should I improve the charge _(True / False)_ | 30 | | **MAX_CHARGE_LEVEL** | Maximum level of charge pumping _(up to 5)_ | 31 | | **APPLY_DAILY_ENERGY** | Whether to use the daily free energy boost _(True / False)_ | 32 | | **APPLY_DAILY_TURBO** | Whether to use the daily free turbo boost (_True / False)_ | 33 | | **RANDOM_CLICKS_COUNT** | Random number of taps _(eg [50,200])_ | 34 | | **SLEEP_BETWEEN_TAP** | Random delay between taps in seconds _(eg [10,25])_ | 35 | | **USE_PROXY_FROM_FILE** | Whether to use proxy from the `bot/config/proxies.txt` file _(True / False)_ | 36 | 37 | ## Quick Start 📚 38 | 1. To install libraries on Windows click on `INSTALL.bat`. 39 | 2. To start the bot use `START.bat` (or in console: `python main.py`). 40 | 41 | ## Prerequisites 42 | Before you begin, ensure you have the following installed: 43 | - [Python](https://www.python.org/downloads/) version 3.10 or 3.11 44 | 45 | ## Obtaining API Keys 46 | 1. Go to [my.telegram.org](https://my.telegram.org) and log in using your phone number. 47 | 2. Select **"API development tools"** and fill out the form to register a new application. 48 | 3. Note down the `API_ID` and `API_HASH` in `.env` file provided after registering your application. 49 | 50 | ## Installation 51 | You can download [**Repository**](https://github.com/shamhi/TapSwapBot) by cloning it to your system and installing the necessary dependencies: 52 | ```shell 53 | ~ >>> git clone https://github.com/shamhi/TapSwapBot.git 54 | ~ >>> cd TapSwapBot 55 | 56 | # If you are using Telethon sessions, then clone the "converter" branch 57 | ~ >>> git clone https://github.com/shamhi/TapSwapBot.git -b converter 58 | ~ >>> cd TapSwapBot 59 | 60 | #Linux 61 | ~/TapSwapBot >>> sudo sh install.sh 62 | ~/TapSwapBot >>> python3 -m venv venv 63 | ~/TapSwapBot >>> source venv/bin/activate 64 | ~/TapSwapBot >>> pip3 install -r requirements.txt 65 | ~/TapSwapBot >>> cp .env-example .env 66 | ~/TapSwapBot >>> nano .env # Here you must specify your API_ID and API_HASH , the rest is taken by default 67 | ~/TapSwapBot >>> python3 main.py 68 | 69 | #Windows 70 | ~/TapSwapBot >>> python -m venv venv 71 | ~/TapSwapBot >>> venv\Scripts\activate 72 | ~/TapSwapBot >>> pip install -r requirements.txt 73 | ~/TapSwapBot >>> copy .env-example .env 74 | ~/TapSwapBot >>> # Specify your API_ID and API_HASH, the rest is taken by default 75 | ~/TapSwapBot >>> python main.py 76 | ``` 77 | 78 | Also for quick launch you can use arguments, for example: 79 | ```shell 80 | ~/TapSwapBot >>> python3 main.py --action (1/2/3) 81 | # Or 82 | ~/TapSwapBot >>> python3 main.py -a (1/2/3) 83 | 84 | #1 - Create session 85 | #2 - Run clicker 86 | #3 - Run via Telegram 87 | ``` 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [](https://t.me/sho6ot) 2 | 3 | 4 | ![img1](.github/images/demo.png) 5 | 6 | > 🇪🇳 README in english available [here](README-EN.md) 7 | 8 | ## Функционал 9 | | Функционал | Поддерживается | 10 | |----------------------------------------------------------------|:---------------:| 11 | | Многопоточность | ✅ | 12 | | Привязка прокси к сессии | ✅ | 13 | | Авто-покупка предметов при наличии монет (tap, energy, charge) | ✅ | 14 | | Рандомное время сна между кликами | ✅ | 15 | | Рандомное количество кликов за запрос | ✅ | 16 | | Поддержка tdata / pyrogram .session / telethon .session | ✅ | 17 | 18 | 19 | ## [Настройки](https://github.com/shamhi/TapSwapBot/blob/main/.env-example) 20 | | Настройка | Описание | 21 | |--------------------------|-----------------------------------------------------------------------------------------------| 22 | | **API_ID / API_HASH** | Данные платформы, с которой запускать сессию Telegram _(сток - Android)_ | 23 | | **MIN_AVAILABLE_ENERGY** | Минимальное количество доступной энергии, при достижении которой будет задержка _(напр. 100)_ | 24 | | **SLEEP_BY_MIN_ENERGY** | Задержка при достижении минимальной энергии в секундах _(напр. [1800,2400])_ | 25 | | **ADD_TAPS_ON_TURBO** | Сколько тапов будет добавлено при активации турбо _(напр. 2500)_ | 26 | | **AUTO_UPGRADE_TAP** | Улучшать ли тап _(True / False)_ | 27 | | **MAX_TAP_LEVEL** | Максимальный уровень прокачки тапа _(до 20)_ | 28 | | **AUTO_UPGRADE_ENERGY** | Улучшать ли энергию _(True / False)_ | 29 | | **MAX_ENERGY_LEVEL** | Максимальный уровень прокачки энергии _(до 20)_ | 30 | | **AUTO_UPGRADE_CHARGE** | Улучшать ли заряд энергии _(True / False)_ | 31 | | **MAX_CHARGE_LEVEL** | Максимальный уровень прокачки заряда энергии _(до 5)_ | 32 | | **APPLY_DAILY_ENERGY** | Использовать ли ежедневный бесплатный буст энергии _(True / False)_ | 33 | | **APPLY_DAILY_TURBO** | Использовать ли ежедневный бесплатный буст турбо _(True / False)_ | 34 | | **RANDOM_CLICKS_COUNT** | Рандомное количество тапов _(напр. [50,200])_ | 35 | | **SLEEP_BETWEEN_TAP** | Рандомная задержка между тапами в секундах _(напр. [10,25])_ | 36 | | **USE_PROXY_FROM_FILE** | Использовать-ли прокси из файла `bot/config/proxies.txt` _(True / False)_ | 37 | 38 | ## Быстрый старт 📚 39 | 1. Чтобы установить библиотеки в Windows, запустите INSTALL.bat. 40 | 2. Для запуска бота используйте `START.bat` (или в консоли: `python main.py`). 41 | 42 | ## Предварительные условия 43 | Прежде чем начать, убедитесь, что у вас установлено следующее: 44 | - [Python](https://www.python.org/downloads/) версии 3.10 или 3.11. 45 | 46 | ## Получение API ключей 47 | 1. Перейдите на сайт [my.telegram.org](https://my.telegram.org) и войдите в систему, используя свой номер телефона. 48 | 2. Выберите **"API development tools"** и заполните форму для регистрации нового приложения. 49 | 3. Запишите `API_ID` и `API_HASH` в файле `.env`, предоставленные после регистрации вашего приложения. 50 | 51 | ## Установка 52 | Вы можете скачать [**Репозиторий**](https://github.com/shamhi/TapSwapBot) клонированием на вашу систему и установкой необходимых зависимостей: 53 | ```shell 54 | ~ >>> git clone https://github.com/shamhi/TapSwapBot.git 55 | ~ >>> cd TapSwapBot 56 | 57 | # Linux 58 | ~/TapSwapBot >>> sudo sh install.sh 59 | ~/TapSwapBot >>> python3 -m venv venv 60 | ~/TapSwapBot >>> source venv/bin/activate 61 | ~/TapSwapBot >>> pip3 install -r requirements.txt 62 | ~/TapSwapBot >>> cp .env-example .env 63 | ~/TapSwapBot >>> nano .env # Здесь вы обязательно должны указать ваши API_ID и API_HASH , остальное берется по умолчанию 64 | ~/TapSwapBot >>> sh install.sh 65 | ~/TapSwapBot >>> python3 main.py 66 | 67 | # Windows 68 | ~/TapSwapBot >>> python -m venv venv 69 | ~/TapSwapBot >>> venv\Scripts\activate 70 | ~/TapSwapBot >>> pip install -r requirements.txt 71 | ~/TapSwapBot >>> copy .env-example .env 72 | ~/TapSwapBot >>> # Указываете ваши API_ID и API_HASH, остальное берется по умолчанию 73 | ~/TapSwapBot >>> python main.py 74 | ``` 75 | 76 | Также для быстрого запуска вы можете использовать аргументы, например: 77 | ```shell 78 | ~/TapSwapBot >>> python3 main.py --action (1/2/3) 79 | # Или 80 | ~/TapSwapBot >>> python3 main.py -a (1/2/3) 81 | 82 | # 1 - Создает сессию 83 | # 2 - Запускает кликер 84 | # 3 - Запуск через Telegram 85 | ``` 86 | -------------------------------------------------------------------------------- /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/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '2.0' 2 | -------------------------------------------------------------------------------- /bot/config/__init__.py: -------------------------------------------------------------------------------- 1 | from .config import settings 2 | -------------------------------------------------------------------------------- /bot/config/config.py: -------------------------------------------------------------------------------- 1 | from pydantic_settings import BaseSettings, SettingsConfigDict 2 | 3 | 4 | class Settings(BaseSettings): 5 | model_config = SettingsConfigDict(env_file=".env", env_ignore_empty=True) 6 | 7 | API_ID: int 8 | API_HASH: str 9 | 10 | MIN_AVAILABLE_ENERGY: int = 100 11 | SLEEP_BY_MIN_ENERGY: list[int] = [1800, 2400] 12 | 13 | ADD_TAPS_ON_TURBO: int = 2500 14 | 15 | AUTO_UPGRADE_TAP: bool = True 16 | MAX_TAP_LEVEL: int = 10 17 | AUTO_UPGRADE_ENERGY: bool = True 18 | MAX_ENERGY_LEVEL: int = 10 19 | AUTO_UPGRADE_CHARGE: bool = True 20 | MAX_CHARGE_LEVEL: int = 5 21 | 22 | APPLY_DAILY_ENERGY: bool = True 23 | APPLY_DAILY_TURBO: bool = True 24 | 25 | RANDOM_TAPS_COUNT: list[int] = [50, 200] 26 | SLEEP_BETWEEN_TAP: list[int] = [10, 25] 27 | 28 | USE_PROXY_FROM_FILE: bool = False 29 | 30 | 31 | settings = Settings() 32 | -------------------------------------------------------------------------------- /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 6 | -------------------------------------------------------------------------------- /bot/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headNbyte/TapSwapBot/e7fc318c403864f5720c9ed20accb89deb82edab/bot/core/__init__.py -------------------------------------------------------------------------------- /bot/core/headers.py: -------------------------------------------------------------------------------- 1 | headers = { 2 | 'Accept': '*/*', 3 | 'Accept-Language': 'ru-RU,ru;q=0.9', 4 | 'Connection': 'keep-alive', 5 | 'Content-Type': 'application/json', 6 | 'Origin': 'https://app.tapswap.club', 7 | 'Referer': 'https://app.tapswap.club/', 8 | 'Sec-Fetch-Dest': 'empty', 9 | 'Sec-Fetch-Mode': 'cors', 10 | 'Sec-Fetch-Site': 'cross-site', 11 | 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36', 12 | 'Sec-Ch-Ua': '"Google Chrome";v="125", "Chromium";v="125", "Not.A/Brand";v="24"', 13 | 'Sec-Ch-Ua-Mobile': '?1', 14 | 'Sec-Ch-Ua-Platform': '"Android"', 15 | 'X-App': 'tapswap_server', 16 | 'X-Bot': 'no', 17 | 'X-Cv': '624', 18 | } 19 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /bot/core/tapper.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from time import time 3 | from random import randint 4 | import traceback 5 | from urllib.parse import unquote 6 | 7 | import aiohttp 8 | from aiocfscrape import CloudflareScraper 9 | from aiohttp_proxy import ProxyConnector 10 | from better_proxy import Proxy 11 | from pyrogram import Client 12 | from pyrogram.errors import Unauthorized, UserDeactivated, AuthKeyUnregistered, FloodWait 13 | from pyrogram.raw.functions.messages import RequestWebView 14 | 15 | from bot.config import settings 16 | from bot.utils import logger 17 | from bot.utils.scripts import extract_chq, escape_html 18 | from bot.exceptions import InvalidSession 19 | from .headers import headers 20 | 21 | 22 | class Tapper: 23 | def __init__(self, tg_client: Client, lock: asyncio.Lock): 24 | self.session_name = tg_client.name 25 | self.tg_client = tg_client 26 | self.user_id = 0 27 | self.lock = lock 28 | 29 | async def get_tg_web_data(self, proxy: str | None) -> str: 30 | if proxy: 31 | proxy = Proxy.from_str(proxy) 32 | proxy_dict = dict( 33 | scheme=proxy.protocol, 34 | hostname=proxy.host, 35 | port=proxy.port, 36 | username=proxy.login, 37 | password=proxy.password 38 | ) 39 | else: 40 | proxy_dict = None 41 | 42 | self.tg_client.proxy = proxy_dict 43 | 44 | try: 45 | with_tg = True 46 | 47 | if not self.tg_client.is_connected: 48 | with_tg = False 49 | try: 50 | await self.tg_client.connect() 51 | except (Unauthorized, UserDeactivated, AuthKeyUnregistered): 52 | raise InvalidSession(self.session_name) 53 | 54 | while True: 55 | try: 56 | peer = await self.tg_client.resolve_peer('tapswap_bot') 57 | break 58 | except FloodWait as fl: 59 | fls = fl.value 60 | 61 | logger.warning(f"{self.session_name} | FloodWait {fl}") 62 | logger.info(f"{self.session_name} | Sleep {fls}s") 63 | 64 | await asyncio.sleep(fls + 3) 65 | 66 | web_view = await self.tg_client.invoke(RequestWebView( 67 | peer=peer, 68 | bot=peer, 69 | platform='android', 70 | from_bot_menu=False, 71 | url='https://app.tapswap.ai/' 72 | )) 73 | 74 | auth_url = web_view.url 75 | tg_web_data = unquote( 76 | string=unquote( 77 | string=auth_url.split('tgWebAppData=', maxsplit=1)[1].split('&tgWebAppVersion', maxsplit=1)[0])) 78 | 79 | self.user_id = (await self.tg_client.get_me()).id 80 | 81 | if with_tg is False: 82 | await self.tg_client.disconnect() 83 | 84 | return tg_web_data 85 | 86 | except InvalidSession as error: 87 | raise error 88 | 89 | except Exception as error: 90 | logger.error(f"{self.session_name} | Unknown error during Authorization: {error}") 91 | await asyncio.sleep(delay=3) 92 | 93 | async def login(self, http_client: aiohttp.ClientSession, tg_web_data: str) -> tuple[dict[str], str]: 94 | response_text = '' 95 | try: 96 | response = await http_client.post(url='https://api.tapswap.ai/api/account/login', 97 | json={"init_data": tg_web_data, "referrer": ""}) 98 | response_text = await response.text() 99 | response.raise_for_status() 100 | 101 | response_json = await response.json() 102 | wait_s = response_json.get('wait_s') 103 | if wait_s: 104 | logger.error(f"{self.session_name} | App overloaded, waiting for: {wait_s}") 105 | await asyncio.sleep(delay=wait_s) 106 | return self.login(http_client, tg_web_data) 107 | 108 | chq = response_json.get('chq') 109 | 110 | if chq: 111 | async with self.lock: 112 | chq_result = extract_chq(chq=chq) 113 | 114 | response = await http_client.post(url='https://api.tapswap.ai/api/account/login', 115 | json={"chr": chq_result, "init_data": tg_web_data, "referrer": ""}) 116 | response_text = await response.text() 117 | response.raise_for_status() 118 | 119 | response_json = await response.json() 120 | access_token = response_json['access_token'] 121 | profile_data = response_json 122 | 123 | return profile_data, access_token 124 | except Exception as error: 125 | traceback.print_exc() 126 | logger.error(f"{self.session_name} | Unknown error while getting Access Token: {error} | " 127 | f"Response text: {escape_html(response_text)}...") 128 | await asyncio.sleep(delay=3) 129 | 130 | async def apply_boost(self, http_client: aiohttp.ClientSession, boost_type: str) -> bool: 131 | response_text = '' 132 | try: 133 | response = await http_client.post(url='https://api.tapswap.ai/api/player/apply_boost', 134 | json={'type': boost_type}) 135 | response_text = await response.text() 136 | response.raise_for_status() 137 | 138 | return True 139 | except Exception as error: 140 | logger.error(f"{self.session_name} | Unknown error when Apply {boost_type} Boost: {error} | " 141 | f"Response text: {escape_html(response_text)[:128]}...") 142 | await asyncio.sleep(delay=3) 143 | 144 | return False 145 | 146 | async def upgrade_boost(self, http_client: aiohttp.ClientSession, boost_type: str) -> bool: 147 | response_text = '' 148 | try: 149 | response = await http_client.post(url='https://api.tapswap.ai/api/player/upgrade', 150 | json={'type': boost_type}) 151 | response_text = await response.text() 152 | response.raise_for_status() 153 | 154 | return True 155 | except Exception as error: 156 | logger.error(f"{self.session_name} | Unknown error when Upgrade {boost_type} Boost: {error} | " 157 | f"Response text: {escape_html(response_text)[:128]}...") 158 | await asyncio.sleep(delay=3) 159 | 160 | return False 161 | 162 | async def claim_reward(self, http_client: aiohttp.ClientSession, task_id: str) -> bool: 163 | response_text = '' 164 | try: 165 | response = await http_client.post(url='https://api.tapswap.ai/api/player/claim_reward', 166 | json={'task_id': task_id}) 167 | response_text = await response.text() 168 | response.raise_for_status() 169 | 170 | return True 171 | except Exception as error: 172 | logger.error(f"{self.session_name} | Unknown error when Claim {task_id} Reward: {error} | " 173 | f"Response text: {escape_html(response_text)[:128]}...") 174 | await asyncio.sleep(delay=3) 175 | 176 | return False 177 | 178 | async def send_taps(self, http_client: aiohttp.ClientSession, taps: int) -> dict[str]: 179 | response_text = '' 180 | try: 181 | timestamp = int(time() * 1000) 182 | content_id = int((timestamp * self.user_id * self.user_id / self.user_id) % self.user_id % self.user_id) 183 | 184 | http_client.headers['Content-Id'] = str(content_id) 185 | 186 | response = await http_client.post(url='https://api.tapswap.ai/api/player/submit_taps', 187 | json={'taps': taps, 'time': timestamp}) 188 | response_text = await response.text() 189 | response.raise_for_status() 190 | 191 | response_json = await response.json() 192 | player_data = response_json['player'] 193 | 194 | return player_data 195 | except Exception as error: 196 | logger.error(f"{self.session_name} | Unknown error when Tapping: {error} | " 197 | f"Response text: {escape_html(response_text)[:128]}...") 198 | await asyncio.sleep(delay=3) 199 | 200 | async def check_proxy(self, http_client: aiohttp.ClientSession, proxy: Proxy) -> None: 201 | try: 202 | response = await http_client.get(url='https://httpbin.org/ip', timeout=aiohttp.ClientTimeout(5)) 203 | ip = (await response.json()).get('origin') 204 | logger.info(f"{self.session_name} | Proxy IP: {ip}") 205 | except Exception as error: 206 | logger.error(f"{self.session_name} | Proxy: {proxy} | Error: {error}") 207 | 208 | async def run(self, proxy: str | None) -> None: 209 | access_token_created_time = 0 210 | turbo_time = 0 211 | active_turbo = False 212 | 213 | proxy_conn = ProxyConnector().from_url(proxy) if proxy else None 214 | 215 | http_client = CloudflareScraper(headers=headers, connector=proxy_conn) 216 | 217 | if proxy: 218 | await self.check_proxy(http_client=http_client, proxy=proxy) 219 | 220 | tg_web_data = await self.get_tg_web_data(proxy=proxy) 221 | 222 | while True: 223 | try: 224 | if http_client.closed: 225 | if proxy_conn: 226 | if not proxy_conn.closed: 227 | proxy_conn.close() 228 | 229 | proxy_conn = ProxyConnector().from_url(proxy) if proxy else None 230 | http_client = aiohttp.ClientSession(headers=headers, connector=proxy_conn) 231 | 232 | if time() - access_token_created_time >= 1800: 233 | profile_data, access_token = await self.login(http_client=http_client, tg_web_data=tg_web_data) 234 | 235 | if not access_token: 236 | continue 237 | 238 | http_client.headers["Authorization"] = f"Bearer {access_token}" 239 | 240 | access_token_created_time = time() 241 | 242 | tap_bot = profile_data['player']['tap_bot'] 243 | if tap_bot: 244 | bot_earned = profile_data['bot_shares'] 245 | 246 | logger.success(f"{self.session_name} | Tap bot earned +{bot_earned:,} coins!") 247 | 248 | balance = profile_data['player']['shares'] 249 | 250 | tap_prices = {index + 1: data['price'] for index, data in 251 | enumerate(profile_data['conf']['tap_levels'])} 252 | energy_prices = {index + 1: data['price'] for index, data in 253 | enumerate(profile_data['conf']['energy_levels'])} 254 | charge_prices = {index + 1: data['price'] for index, data in 255 | enumerate(profile_data['conf']['charge_levels'])} 256 | 257 | claims = profile_data['player']['claims'] 258 | if claims: 259 | for task_id in claims: 260 | logger.info(f"{self.session_name} | Sleep 5s before claim {task_id} reward") 261 | await asyncio.sleep(delay=5) 262 | 263 | status = await self.claim_reward(http_client=http_client, task_id=task_id) 264 | if status is True: 265 | logger.success(f"{self.session_name} | Successfully claim {task_id} reward") 266 | 267 | await asyncio.sleep(delay=1) 268 | 269 | taps = randint(a=settings.RANDOM_TAPS_COUNT[0], b=settings.RANDOM_TAPS_COUNT[1]) 270 | 271 | if active_turbo: 272 | taps += settings.ADD_TAPS_ON_TURBO 273 | if time() - turbo_time > 20: 274 | active_turbo = False 275 | turbo_time = 0 276 | 277 | player_data = await self.send_taps(http_client=http_client, taps=taps) 278 | 279 | if not player_data: 280 | continue 281 | 282 | available_energy = player_data['energy'] 283 | new_balance = player_data['shares'] 284 | calc_taps = abs(new_balance - balance) 285 | balance = new_balance 286 | total = player_data['stat']['earned'] 287 | 288 | turbo_boost_count = player_data['boost'][1]['cnt'] 289 | energy_boost_count = player_data['boost'][0]['cnt'] 290 | 291 | next_tap_level = player_data['tap_level'] + 1 292 | next_energy_level = player_data['energy_level'] + 1 293 | next_charge_level = player_data['charge_level'] + 1 294 | 295 | logger.success(f"{self.session_name} | Successful tapped! | " 296 | f"Balance: {balance:,} (+{calc_taps:,}) | Total: {total:,}") 297 | 298 | if active_turbo is False: 299 | if (energy_boost_count > 0 300 | and available_energy < settings.MIN_AVAILABLE_ENERGY 301 | and settings.APPLY_DAILY_ENERGY is True): 302 | logger.info(f"{self.session_name} | Sleep 5s before activating the daily energy boost") 303 | await asyncio.sleep(delay=5) 304 | 305 | status = await self.apply_boost(http_client=http_client, boost_type="energy") 306 | if status is True: 307 | logger.success(f"{self.session_name} | Energy boost applied") 308 | 309 | await asyncio.sleep(delay=1) 310 | 311 | continue 312 | 313 | if turbo_boost_count > 0 and settings.APPLY_DAILY_TURBO is True: 314 | logger.info(f"{self.session_name} | Sleep 5s before activating the daily turbo boost") 315 | await asyncio.sleep(delay=5) 316 | 317 | status = await self.apply_boost(http_client=http_client, boost_type="turbo") 318 | if status is True: 319 | logger.success(f"{self.session_name} | Turbo boost applied") 320 | 321 | await asyncio.sleep(delay=1) 322 | 323 | active_turbo = True 324 | turbo_time = time() 325 | 326 | continue 327 | 328 | if (settings.AUTO_UPGRADE_TAP is True 329 | and balance > tap_prices.get(next_tap_level, 0) 330 | and next_tap_level <= settings.MAX_TAP_LEVEL): 331 | logger.info(f"{self.session_name} | Sleep 5s before upgrade tap to {next_tap_level} lvl") 332 | await asyncio.sleep(delay=5) 333 | 334 | status = await self.upgrade_boost(http_client=http_client, boost_type="tap") 335 | if status is True: 336 | logger.success(f"{self.session_name} | Tap upgraded to {next_tap_level} lvl") 337 | 338 | await asyncio.sleep(delay=1) 339 | 340 | continue 341 | 342 | if (settings.AUTO_UPGRADE_ENERGY is True 343 | and balance > energy_prices.get(next_energy_level, 0) 344 | and next_energy_level <= settings.MAX_ENERGY_LEVEL): 345 | logger.info( 346 | f"{self.session_name} | Sleep 5s before upgrade energy to {next_energy_level} lvl") 347 | await asyncio.sleep(delay=5) 348 | 349 | status = await self.upgrade_boost(http_client=http_client, boost_type="energy") 350 | if status is True: 351 | logger.success(f"{self.session_name} | Energy upgraded to {next_energy_level} lvl") 352 | 353 | await asyncio.sleep(delay=1) 354 | 355 | continue 356 | 357 | if (settings.AUTO_UPGRADE_CHARGE is True 358 | and balance > charge_prices.get(next_charge_level, 0) 359 | and next_charge_level <= settings.MAX_CHARGE_LEVEL): 360 | logger.info( 361 | f"{self.session_name} | Sleep 5s before upgrade charge to {next_charge_level} lvl") 362 | await asyncio.sleep(delay=5) 363 | 364 | status = await self.upgrade_boost(http_client=http_client, boost_type="charge") 365 | if status is True: 366 | logger.success(f"{self.session_name} | Charge upgraded to {next_charge_level} lvl") 367 | 368 | await asyncio.sleep(delay=1) 369 | 370 | continue 371 | 372 | if available_energy < settings.MIN_AVAILABLE_ENERGY: 373 | await http_client.close() 374 | if proxy_conn: 375 | if not proxy_conn.closed: 376 | proxy_conn.close() 377 | 378 | random_sleep = randint(settings.SLEEP_BY_MIN_ENERGY[0], settings.SLEEP_BY_MIN_ENERGY[1]) 379 | 380 | logger.info(f"{self.session_name} | Minimum energy reached: {available_energy}") 381 | logger.info(f"{self.session_name} | Sleep {random_sleep:,}s") 382 | 383 | await asyncio.sleep(delay=random_sleep) 384 | 385 | access_token_created_time = 0 386 | 387 | except InvalidSession as error: 388 | raise error 389 | 390 | except Exception as error: 391 | logger.error(f"{self.session_name} | Unknown error: {error}") 392 | await asyncio.sleep(delay=3) 393 | 394 | else: 395 | sleep_between_clicks = randint(a=settings.SLEEP_BETWEEN_TAP[0], b=settings.SLEEP_BETWEEN_TAP[1]) 396 | 397 | if active_turbo is True: 398 | sleep_between_clicks = 4 399 | 400 | logger.info(f"Sleep {sleep_between_clicks}s") 401 | await asyncio.sleep(delay=sleep_between_clicks) 402 | 403 | 404 | async def run_tapper(tg_client: Client, proxy: str | None, lock: asyncio.Lock): 405 | try: 406 | await Tapper(tg_client=tg_client, lock=lock).run(proxy=proxy) 407 | except InvalidSession: 408 | logger.error(f"{tg_client.name} | Invalid Session") 409 | -------------------------------------------------------------------------------- /bot/exceptions/__init__.py: -------------------------------------------------------------------------------- 1 | class InvalidSession(BaseException): 2 | ... 3 | -------------------------------------------------------------------------------- /bot/plugins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headNbyte/TapSwapBot/e7fc318c403864f5720c9ed20accb89deb82edab/bot/plugins/__init__.py -------------------------------------------------------------------------------- /bot/plugins/manager.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters 2 | from pyrogram.types import Message 3 | 4 | from bot.utils import scripts 5 | from bot.utils.logger import logger 6 | from bot.utils.emojis import StaticEmoji 7 | from bot.utils.launcher import tg_clients, run_tasks 8 | 9 | 10 | @Client.on_message(filters.me & filters.chat("me") & filters.command("help", prefixes="/")) 11 | async def send_help(_: Client, message: Message): 12 | help_text = scripts.get_help_text() 13 | 14 | await message.edit(text=help_text) 15 | 16 | 17 | @Client.on_message(filters.me & filters.chat("me") & filters.command("tap", prefixes="/")) 18 | @scripts.with_args("This command does not work without arguments\n" 19 | "Type /tap on to start or /tap off to stop") 20 | async def launch_tapper(client: Client, message: Message): 21 | flag = scripts.get_command_args(message, "tap") 22 | 23 | flags_to_start = ["on", "start"] 24 | flags_to_stop = ["off", "stop"] 25 | 26 | if flag in flags_to_start: 27 | logger.info(f"The tapper is launched with the command /tap {flag}\n") 28 | 29 | await message.edit( 30 | text=f"{StaticEmoji.ACCEPT} Tapper launched! {StaticEmoji.START}") 31 | await run_tasks(tg_clients=tg_clients) 32 | elif flag in flags_to_stop: 33 | logger.info(f"Tapper stopped with /tap command {flag}\n") 34 | 35 | await scripts.stop_tasks(client=client) 36 | await message.edit( 37 | text=f"{StaticEmoji.ACCEPT} Tapper stopped! {StaticEmoji.STOP}") 38 | else: 39 | await message.edit( 40 | text=f"{StaticEmoji.DENY} This command only accepts the following arguments: on/off | start/stop") 41 | -------------------------------------------------------------------------------- /bot/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .logger import logger 2 | from . import launcher 3 | from . import scripts 4 | from . import emojis 5 | 6 | 7 | import os 8 | 9 | if not os.path.exists(path="sessions"): 10 | os.mkdir(path="sessions") 11 | -------------------------------------------------------------------------------- /bot/utils/emojis.py: -------------------------------------------------------------------------------- 1 | import random 2 | from enum import Enum 3 | 4 | 5 | class StaticEmoji(str, Enum): 6 | ACCEPT = '✔️' 7 | DENY = '' 8 | WARNING = '⚠️' 9 | LOUDSPEAKER = '📣' 10 | FLAG = '🔖' 11 | SCRAP = '📌' 12 | ARROW = '➡️' 13 | PLUS = '' 14 | DOLLAR = '💵' 15 | START = '🟢' 16 | STOP = '🔴' 17 | 18 | 19 | def get_random_reaction(): 20 | reactions = [ 21 | "👍", "👎", "❤️", "🔥", "🥰", "👏", "😁", "🤔", "🤯", "😱", "🤬", "😢", "🎉", "🤩", "🤮", "💩", "🙏", "👌", "🕊", "🤡", "🥱", "🥴", 22 | "😍", "🐳", "❤️‍🔥", "🌚", "🌭", "💯", "🤣", "⚡️", "🍌", "🏆", "💔", "🤨", "😐", "🍓", "🍾", "💋", "🖕", "😈", "😴", "😭", "🤓", 23 | "👻", "👨‍💻", "👀", "🎃", "🙈", "😇", "😨", "🤝", "✍️", "🤗", "🫡", "🎅", "🎄", "☃️", "💅", "🤪", "🗿", "🆒", "💘", "🙉", "🦄", 24 | "😘", "💊", "🙊", "😎", "👾", "🤷‍♂️", "🤷", "🤷‍♀️", "😡" 25 | ] 26 | 27 | return random.choice(reactions) 28 | 29 | 30 | accept = [ 31 | '🌟', 32 | '🌟', 33 | '🌟', 34 | '🌟', 35 | '🌟', 36 | '🌟', 37 | '🌟', 38 | '🌟', 39 | '🌟', 40 | '🌟', 41 | '🌟', 42 | '🌟', 43 | '🌟', 44 | '🌟', 45 | '🌟', 46 | '🌟', 47 | '', 48 | '', 49 | '', 50 | '', 51 | '', 52 | '', 53 | '', 54 | '', 55 | '', 56 | '', 57 | '', 58 | '', 59 | '', 60 | '', 61 | '', 62 | '', 63 | '', 64 | '', 65 | '', 66 | '', 67 | '', 68 | '', 69 | ] 70 | 71 | deny = [ 72 | "", 73 | "", 74 | "", 75 | "", 76 | "", 77 | "", 78 | ] 79 | 80 | warnings = [ 81 | "⚠️", 82 | "⚠️", 83 | "⚠️", 84 | "⚠️", 85 | "⚠️", 86 | "⚠️", 87 | ] 88 | 89 | nums = { 90 | 0: '🔠', 91 | 1: '🔠', 92 | 2: '🔠', 93 | 3: '🔠', 94 | 4: '🔠', 95 | 5: '🔠', 96 | 6: '🔠', 97 | 7: '🔠', 98 | 8: '🔠', 99 | 9: '🔠', 100 | } 101 | 102 | loads = [ 103 | "🫥", 104 | "🫥", 105 | "🫥", 106 | "🫥", 107 | "🫥", 108 | "🫥", 109 | "🫥", 110 | "🫥", 111 | "🫥", 112 | "🫠", 113 | "🫥", 114 | "🫥", 115 | "🫥", 116 | "🫥", 117 | "🫥", 118 | "🫥", 119 | "🫥", 120 | "🫥", 121 | "🫥", 122 | "🫥", 123 | "😵", 124 | "", 125 | "💫", 126 | "🫥", 127 | ] 128 | 129 | 130 | def rload(): 131 | return random.choice(loads) 132 | 133 | 134 | def rcheck(): 135 | return random.choice(accept) 136 | 137 | 138 | def rdeny(): 139 | return random.choice(deny) 140 | 141 | 142 | def rwarning(): 143 | return random.choice(warnings) 144 | 145 | 146 | def num(n): 147 | return nums[int(n)] 148 | -------------------------------------------------------------------------------- /bot/utils/launcher.py: -------------------------------------------------------------------------------- 1 | import os 2 | import glob 3 | import asyncio 4 | import argparse 5 | from itertools import cycle 6 | 7 | from pyrogram import Client, compose 8 | from better_proxy import Proxy 9 | 10 | from bot.config import settings 11 | from bot.utils import logger 12 | from bot.core.tapper import run_tapper 13 | from bot.core.registrator import register_sessions 14 | 15 | 16 | start_text = """ 17 | 18 | ▀▀█▀▀ █▀▀█ █▀▀█ ░█▀▀▀█ █ █ █▀▀█ █▀▀█ ░█▀▀█ █▀▀█ ▀▀█▀▀ 19 | ░█ █▄▄█ █ █ ▀▀▀▄▄ █▄█▄█ █▄▄█ █ █ ░█▀▀▄ █ █ █ 20 | ░█ ▀ ▀ █▀▀▀ ░█▄▄▄█ ▀ ▀ ▀ ▀ █▀▀▀ ░█▄▄█ ▀▀▀▀ ▀ 21 | 22 | Select an action: 23 | 24 | 1. Create session 25 | 2. Run clicker 26 | 3. Run via Telegram (Beta) 27 | """ 28 | 29 | global tg_clients 30 | 31 | def get_session_names() -> list[str]: 32 | session_names = glob.glob("sessions/*.session") 33 | session_names = [ 34 | os.path.splitext(os.path.basename(file))[0] for file in session_names 35 | ] 36 | 37 | return session_names 38 | 39 | 40 | def get_proxies() -> list[Proxy]: 41 | if settings.USE_PROXY_FROM_FILE: 42 | with open(file="bot/config/proxies.txt", encoding="utf-8-sig") as file: 43 | proxies = [Proxy.from_str(proxy=row.strip()).as_url for row in file] 44 | else: 45 | proxies = [] 46 | 47 | return proxies 48 | 49 | 50 | async def get_tg_clients() -> list[Client]: 51 | global tg_clients 52 | 53 | session_names = get_session_names() 54 | 55 | if not session_names: 56 | raise FileNotFoundError("Not found session files") 57 | 58 | if not settings.API_ID or not settings.API_HASH: 59 | raise ValueError("API_ID and API_HASH not found in the .env file.") 60 | 61 | tg_clients = [ 62 | Client( 63 | name=session_name, 64 | api_id=settings.API_ID, 65 | api_hash=settings.API_HASH, 66 | workdir="sessions/", 67 | plugins=dict(root="bot/plugins"), 68 | ) 69 | for session_name in session_names 70 | ] 71 | 72 | return tg_clients 73 | 74 | 75 | async def process() -> None: 76 | parser = argparse.ArgumentParser() 77 | parser.add_argument("-a", "--action", type=int, help="Action to perform") 78 | 79 | logger.info(f"Detected {len(get_session_names())} sessions | {len(get_proxies())} proxies") 80 | 81 | action = parser.parse_args().action 82 | 83 | if not action: 84 | print(start_text) 85 | 86 | while True: 87 | action = input("> ") 88 | 89 | if not action.isdigit(): 90 | logger.warning("Action must be number") 91 | elif action not in ["1", "2", "3"]: 92 | logger.warning("Action must be 1, 2 or 3") 93 | else: 94 | action = int(action) 95 | break 96 | 97 | if action == 1: 98 | await register_sessions() 99 | elif action == 2: 100 | tg_clients = await get_tg_clients() 101 | 102 | await run_tasks(tg_clients=tg_clients) 103 | elif action == 3: 104 | tg_clients = await get_tg_clients() 105 | 106 | logger.info("Send /help command in Saved Messages\n") 107 | 108 | await compose(tg_clients) 109 | 110 | 111 | async def run_tasks(tg_clients: list[Client]): 112 | proxies = get_proxies() 113 | proxies_cycle = cycle(proxies) if proxies else None 114 | lock = asyncio.Lock() 115 | 116 | tasks = [ 117 | asyncio.create_task( 118 | run_tapper( 119 | tg_client=tg_client, 120 | proxy=next(proxies_cycle) if proxies_cycle else None, 121 | lock=lock, 122 | ) 123 | ) 124 | for tg_client in tg_clients 125 | ] 126 | 127 | await asyncio.gather(*tasks) 128 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /bot/utils/scripts.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os 3 | import asyncio 4 | from typing import Union 5 | 6 | from pyrogram import Client 7 | from pyrogram.types import Message 8 | 9 | from bot.utils.emojis import num, StaticEmoji 10 | from bot.utils import logger 11 | from bs4 import BeautifulSoup 12 | 13 | import pathlib 14 | import shutil 15 | from selenium import webdriver 16 | from multiprocessing import Queue 17 | 18 | 19 | 20 | def get_session_names() -> list[str]: 21 | session_names = glob.glob("sessions/*.session") 22 | session_names = [ 23 | os.path.splitext(os.path.basename(file))[0] for file in session_names 24 | ] 25 | 26 | return session_names 27 | 28 | 29 | 30 | 31 | if os.name == "posix": 32 | from selenium.webdriver.firefox.service import Service as FirefoxService 33 | from selenium.webdriver.firefox.options import Options as FirefoxOptions 34 | from webdriver_manager.firefox import GeckoDriverManager 35 | 36 | web_options = FirefoxOptions 37 | web_service = FirefoxService 38 | web_manager = GeckoDriverManager 39 | web_driver = webdriver.Firefox 40 | else: 41 | from selenium.webdriver.chrome.service import Service as ChromeService 42 | from selenium.webdriver.chrome.options import Options as ChromeOptions 43 | from webdriver_manager.chrome import ChromeDriverManager 44 | 45 | web_options = ChromeOptions 46 | web_service = ChromeService 47 | web_manager = ChromeDriverManager 48 | web_driver = webdriver.Chrome 49 | 50 | if not pathlib.Path("webdriver").exists() or len(list(pathlib.Path("webdriver").iterdir())) == 0: 51 | logger.info("Downloading webdriver. It may take some time...") 52 | pathlib.Path("webdriver").mkdir(parents=True, exist_ok=True) 53 | webdriver_path = pathlib.Path(web_manager().install()) 54 | shutil.move(webdriver_path, f"webdriver/{webdriver_path.name}") 55 | logger.info("Webdriver downloaded successfully") 56 | 57 | webdriver_path = next(pathlib.Path("webdriver").iterdir()).as_posix() 58 | 59 | options = web_options() 60 | options.add_argument("--headless") 61 | driver = None 62 | 63 | session_queue = Queue() 64 | 65 | 66 | def get_command_args( 67 | message: Union[Message, str], 68 | command: Union[str, list[str]] = None, 69 | prefixes: str = "/", 70 | ) -> str: 71 | if isinstance(message, str): 72 | return message.split(f"{prefixes}{command}", maxsplit=1)[-1].strip() 73 | if isinstance(command, str): 74 | args = message.text.split(f"{prefixes}{command}", maxsplit=1)[-1].strip() 75 | return args 76 | elif isinstance(command, list): 77 | for cmd in command: 78 | args = message.text.split(f"{prefixes}{cmd}", maxsplit=1)[-1] 79 | if args != message.text: 80 | return args.strip() 81 | return "" 82 | 83 | 84 | def with_args(text: str): 85 | def decorator(func): 86 | async def wrapped(client: Client, message: Message): 87 | if message.text and len(message.text.split()) == 1: 88 | await message.edit(f"{text}") 89 | else: 90 | return await func(client, message) 91 | 92 | return wrapped 93 | 94 | return decorator 95 | 96 | 97 | def get_help_text(): 98 | return f""" 99 | {StaticEmoji.FLAG} [Demo version] 100 | 101 | {num(1)} /help - Displays all available commands 102 | {num(2)} /tap [on|start, off|stop] - Starts or stops the tapper 103 | 104 | """ 105 | 106 | 107 | 108 | async def stop_tasks(client: Client = None) -> None: 109 | if client: 110 | all_tasks = asyncio.all_tasks(loop=client.loop) 111 | else: 112 | loop = asyncio.get_event_loop() 113 | all_tasks = asyncio.all_tasks(loop=loop) 114 | 115 | clicker_tasks = [task for task in all_tasks 116 | if isinstance(task, asyncio.Task) and task._coro.__name__ == 'run_tapper'] 117 | 118 | for task in clicker_tasks: 119 | try: 120 | task.cancel() 121 | except: 122 | ... 123 | 124 | def escape_html(text: str) -> str: 125 | return text.replace('<', '\\<').replace('>', '\\>') 126 | 127 | 128 | def extract_chq(chq: str) -> int: 129 | global driver 130 | 131 | if driver is None: 132 | driver = web_driver(service=web_service(webdriver_path), options=options) 133 | 134 | chq_length = len(chq) 135 | 136 | bytes_array = bytearray(chq_length // 2) 137 | xor_key = 157 138 | 139 | for i in range(0, chq_length, 2): 140 | bytes_array[i // 2] = int(chq[i:i + 2], 16) 141 | 142 | xor_bytes = bytearray(t ^ xor_key for t in bytes_array) 143 | decoded_xor = xor_bytes.decode('utf-8') 144 | 145 | driver.execute_script(""" 146 | var chrStub = document.createElement("div"); 147 | chrStub.id = "_chr_"; 148 | document.body.appendChild(chrStub); 149 | """) 150 | 151 | fixed_xor = repr(decoded_xor).replace("`", "\\`") 152 | 153 | k = driver.execute_script(f""" 154 | try {{ 155 | return eval(`{fixed_xor[1:-1]}`); 156 | }} catch (e) {{ 157 | return e; 158 | }} 159 | """) 160 | 161 | session_queue.put(1) 162 | 163 | if len(get_session_names()) == session_queue.qsize(): 164 | logger.info("All sessions are closed. Quitting driver...") 165 | driver.quit() 166 | driver = None 167 | 168 | while session_queue.qsize() > 0: 169 | session_queue.get() 170 | 171 | return k 172 | 173 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | tapswap-bot: 4 | container_name: 'TapSwapBot' 5 | build: 6 | context: . 7 | stop_signal: SIGINT 8 | restart: unless-stopped 9 | command: "python3 main.py -a 2" 10 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Update package list and install prerequisites 4 | echo "Updating package list and installing prerequisites..." 5 | sudo apt update 6 | sudo apt install -y software-properties-common 7 | 8 | # Add Mozilla Team PPA 9 | echo "Adding Mozilla Team PPA..." 10 | sudo add-apt-repository -y ppa:mozillateam/ppa 11 | 12 | # Set package pinning preferences 13 | echo "Setting package pinning preferences..." 14 | sudo tee /etc/apt/preferences.d/mozilla-firefox <