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