Spaces:
Paused
Paused
Upload folder using huggingface_hub (#1)
Browse files- Upload folder using huggingface_hub (8cdbae16faff85fffba37495fe3b42c5176e2b69)
- .env +15 -0
- .env-example +13 -0
- Dockerfile +13 -0
- INSTALL.bat +11 -0
- INSTALL.sh +48 -0
- START.bat +9 -0
- START.sh +10 -0
- bot/__init__.py +0 -0
- bot/__pycache__/__init__.cpython-310.pyc +0 -0
- bot/__pycache__/launcher.cpython-310.pyc +0 -0
- bot/__pycache__/utils.cpython-310.pyc +0 -0
- bot/config/__init__.py +0 -0
- bot/config/__pycache__/__init__.cpython-310.pyc +0 -0
- bot/config/__pycache__/headers.cpython-310.pyc +0 -0
- bot/config/__pycache__/logger.cpython-310.pyc +0 -0
- bot/config/__pycache__/settings.cpython-310.pyc +0 -0
- bot/config/headers.py +12 -0
- bot/config/logger.py +15 -0
- bot/config/settings.py +79 -0
- bot/core/__init__.py +0 -0
- bot/core/__pycache__/__init__.cpython-310.pyc +0 -0
- bot/core/__pycache__/api.cpython-310.pyc +0 -0
- bot/core/__pycache__/bot.cpython-310.pyc +0 -0
- bot/core/__pycache__/errors.cpython-310.pyc +0 -0
- bot/core/__pycache__/models.cpython-310.pyc +0 -0
- bot/core/__pycache__/utils.cpython-310.pyc +0 -0
- bot/core/api.py +320 -0
- bot/core/api_js_helpers/__init__.py +0 -0
- bot/core/api_js_helpers/__pycache__/__init__.cpython-310.pyc +0 -0
- bot/core/api_js_helpers/__pycache__/bet_counter.cpython-310.pyc +0 -0
- bot/core/api_js_helpers/__pycache__/upgrader.cpython-310.pyc +0 -0
- bot/core/api_js_helpers/bet_counter.py +48 -0
- bot/core/api_js_helpers/upgrader.py +90 -0
- bot/core/bot.py +427 -0
- bot/core/errors.py +3 -0
- bot/core/models.py +219 -0
- bot/core/utils.py +24 -0
- bot/helper/__pycache__/utils.cpython-310.pyc +0 -0
- bot/helper/utils.py +89 -0
- bot/launcher.py +129 -0
- bot/utils.py +34 -0
- data.json +0 -0
- docker-compose.yml +14 -0
- main.py +15 -0
- proxies.txt +5 -0
- requirements.txt +11 -0
- sessions/yasir.session +0 -0
- youtube.json +25 -0
.env
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
API_ID=25514329
|
3 |
+
API_HASH=2b15eb2dd52e935d6b091432b71cfc78
|
4 |
+
|
5 |
+
TAPS_ENABLED=True
|
6 |
+
TAPS_PER_SECOND=[20, 30]
|
7 |
+
PVP_ENABLED=False
|
8 |
+
PVP_LEAGUE=
|
9 |
+
PVP_STRATEGY=aggressive
|
10 |
+
PVP_COUNT=10
|
11 |
+
SLEEP_BETWEEN_START=
|
12 |
+
ERRORS_BEFORE_STOP=
|
13 |
+
USE_PROXY_FROM_FILE=
|
14 |
+
REF_ID=hero5413217144
|
15 |
+
MONEY_TO_SAVE=100
|
.env-example
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
API_ID=
|
2 |
+
API_HASH=
|
3 |
+
|
4 |
+
TAPS_ENABLED=
|
5 |
+
TAPS_PER_SECOND=
|
6 |
+
PVP_ENABLED=
|
7 |
+
PVP_LEAGUE=
|
8 |
+
PVP_STRATEGY=
|
9 |
+
PVP_COUNT=
|
10 |
+
SLEEP_BETWEEN_START=
|
11 |
+
ERRORS_BEFORE_STOP=
|
12 |
+
USE_PROXY_FROM_FILE=
|
13 |
+
REF_ID=
|
Dockerfile
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.10
|
2 |
+
|
3 |
+
WORKDIR /app
|
4 |
+
|
5 |
+
COPY requirements.txt requirements.txt
|
6 |
+
|
7 |
+
RUN pip3 install --upgrade pip setuptools wheel
|
8 |
+
RUN pip3 install --no-warn-script-location --no-cache-dir -r requirements.txt
|
9 |
+
|
10 |
+
COPY . .
|
11 |
+
|
12 |
+
ENTRYPOINT ["python3", "main.py"]
|
13 |
+
CMD ["-a", "2"]
|
INSTALL.bat
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
INSTALL.sh
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
|
3 |
+
install_python() {
|
4 |
+
echo "Select the Python version to install:"
|
5 |
+
echo "1) Python 3.10"
|
6 |
+
echo "2) Python 3.11"
|
7 |
+
echo "3) Python 3.12"
|
8 |
+
read -p "Enter the number of your choice: " choice
|
9 |
+
|
10 |
+
case $choice in
|
11 |
+
1) version="3.10" ;;
|
12 |
+
2) version="3.11" ;;
|
13 |
+
3) version="3.12" ;;
|
14 |
+
*) echo "Invalid choice"; exit 1 ;;
|
15 |
+
esac
|
16 |
+
|
17 |
+
if command -v apt-get &> /dev/null; then
|
18 |
+
sudo apt-get update
|
19 |
+
sudo apt-get install -y python$version python$version-venv python$version-pip
|
20 |
+
elif command -v yum &> /dev/null; then
|
21 |
+
sudo yum install -y https://repo.ius.io/ius-release-el$(rpm -E %{rhel}).rpm
|
22 |
+
sudo yum install -y python$version python$version-venv python$version-pip
|
23 |
+
elif command -v dnf &> /dev/null; then
|
24 |
+
sudo dnf install -y python$version python$version-venv python$version-pip
|
25 |
+
else
|
26 |
+
echo "Package manager not supported. Please install Python manually."
|
27 |
+
exit 1
|
28 |
+
fi
|
29 |
+
}
|
30 |
+
|
31 |
+
if ! command -v python3 &> /dev/null; then
|
32 |
+
install_python
|
33 |
+
fi
|
34 |
+
|
35 |
+
echo "Creating virtual environment..."
|
36 |
+
python3 -m venv venv
|
37 |
+
|
38 |
+
echo "Activating virtual environment..."
|
39 |
+
source venv/bin/activate
|
40 |
+
|
41 |
+
echo "Installing dependencies..."
|
42 |
+
pip install -r requirements.txt
|
43 |
+
|
44 |
+
echo "Copying .env-example to .env..."
|
45 |
+
cp .env-example .env
|
46 |
+
|
47 |
+
echo "Please edit the .env file to add your API_ID and API_HASH."
|
48 |
+
read -p "Press any key to continue..."
|
START.bat
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@echo off
|
2 |
+
echo Activating virtual environment...
|
3 |
+
title MuskEmpire
|
4 |
+
call venv\Scripts\activate
|
5 |
+
echo Starting git pull
|
6 |
+
git pull
|
7 |
+
echo Starting the bot...
|
8 |
+
python main.py -a 2
|
9 |
+
pause
|
START.sh
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
|
3 |
+
echo "Activating virtual environment..."
|
4 |
+
source venv/bin/activate
|
5 |
+
|
6 |
+
echo "Starting the bot..."
|
7 |
+
python main.py
|
8 |
+
|
9 |
+
echo "Press any key to continue..."
|
10 |
+
read -n 1 -s
|
bot/__init__.py
ADDED
File without changes
|
bot/__pycache__/__init__.cpython-310.pyc
ADDED
Binary file (131 Bytes). View file
|
|
bot/__pycache__/launcher.cpython-310.pyc
ADDED
Binary file (4.55 kB). View file
|
|
bot/__pycache__/utils.cpython-310.pyc
ADDED
Binary file (1.4 kB). View file
|
|
bot/config/__init__.py
ADDED
File without changes
|
bot/config/__pycache__/__init__.cpython-310.pyc
ADDED
Binary file (138 Bytes). View file
|
|
bot/config/__pycache__/headers.cpython-310.pyc
ADDED
Binary file (647 Bytes). View file
|
|
bot/config/__pycache__/logger.cpython-310.pyc
ADDED
Binary file (539 Bytes). View file
|
|
bot/config/__pycache__/settings.cpython-310.pyc
ADDED
Binary file (3.54 kB). View file
|
|
bot/config/headers.py
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
headers = {
|
2 |
+
"Accept": "*/*",
|
3 |
+
"Accept-Language": "ru,ru-RU;q=0.9,en-US;q=0.8,en;q=0.7",
|
4 |
+
"Content-Type": "application/json",
|
5 |
+
"Origin": "https://game.xempire.io",
|
6 |
+
"Referer": "https://game.xempire.io/",
|
7 |
+
"Sec-Fetch-Dest": "empty",
|
8 |
+
"Sec-Fetch-Mode": "cors",
|
9 |
+
"Sec-Fetch-Site": "same-site",
|
10 |
+
"User-Agent": "Mozilla/5.0 (Linux; Android 9; SM-N971N Build/PQ3B.190801.07101020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/124.0.6367.82 Safari/537.36",
|
11 |
+
"X-Requested-With": "org.telegram.messenger.web",
|
12 |
+
}
|
bot/config/logger.py
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import sys
|
2 |
+
|
3 |
+
from loguru import logger
|
4 |
+
|
5 |
+
logger.remove()
|
6 |
+
|
7 |
+
logger_str_format = (
|
8 |
+
"<white>{time:YYYY-MM-DD HH:mm:ss}</white> | "
|
9 |
+
"<level>{level: <8}</level> | "
|
10 |
+
"<c><b>{line: <5}</b></c>| "
|
11 |
+
"<c><b>{extra[session_name]: <7}</b></c> | "
|
12 |
+
"<white><b>{message}</b></white>"
|
13 |
+
)
|
14 |
+
logger.add(sink=sys.stdout, format=logger_str_format, colorize=True)
|
15 |
+
log = logger.bind(session_name="GLOBAL").opt(colors=True)
|
bot/config/settings.py
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from enum import Enum
|
2 |
+
|
3 |
+
from pydantic import Field
|
4 |
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
5 |
+
|
6 |
+
logo = """
|
7 |
+
|
8 |
+
███ ███ ██ ██ ███████ ██ ██ ███████ ███ ███ ██████ ██ ██████ ███████
|
9 |
+
████ ████ ██ ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ██ ██ ██
|
10 |
+
██ ████ ██ ██ ██ ███████ █████ █████ ██ ████ ██ ██████ ██ ██████ █████
|
11 |
+
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
12 |
+
██ ██ ██████ ███████ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ███████
|
13 |
+
|
14 |
+
"""
|
15 |
+
|
16 |
+
|
17 |
+
class Strategy(str, Enum):
|
18 |
+
flexible = "flexible"
|
19 |
+
protective = "protective"
|
20 |
+
aggressive = "aggressive"
|
21 |
+
random = "random"
|
22 |
+
|
23 |
+
|
24 |
+
class League(str, Enum):
|
25 |
+
bronze = "bronze"
|
26 |
+
silver = "silver"
|
27 |
+
gold = "gold"
|
28 |
+
platina = "platina"
|
29 |
+
diamond = "diamond"
|
30 |
+
|
31 |
+
|
32 |
+
class Settings(BaseSettings):
|
33 |
+
model_config = SettingsConfigDict(env_file=".env", env_ignore_empty=True, extra="allow")
|
34 |
+
|
35 |
+
API_ID: int
|
36 |
+
API_HASH: str
|
37 |
+
|
38 |
+
LOGIN_TIMEOUT: int = 3600
|
39 |
+
|
40 |
+
TAPS_ENABLED: bool = True
|
41 |
+
TAPS_PER_SECOND: list[int] = [20, 30]
|
42 |
+
AUTO_UPGRADE_HERO: bool = True
|
43 |
+
PVP_ENABLED: bool = True
|
44 |
+
PVP_LEAGUE: League = League.bronze
|
45 |
+
PVP_STRATEGY: Strategy = Strategy.random
|
46 |
+
PVP_COUNT: int = 5
|
47 |
+
|
48 |
+
SLEEP_BETWEEN_START: list[int] = [10, 20]
|
49 |
+
SESSION_AC_DELAY: int = 10
|
50 |
+
ERRORS_BEFORE_STOP: int = 5
|
51 |
+
USE_PROXY_FROM_FILE: bool = False
|
52 |
+
ADD_LOCAL_MACHINE_AS_IP: bool = False
|
53 |
+
|
54 |
+
RANDOM_SLEEP_TIME: int = 8
|
55 |
+
SKILL_WEIGHT: float = 0
|
56 |
+
MAX_SKILL_UPGRADE_COSTS: int = 5e9
|
57 |
+
|
58 |
+
MONEY_TO_SAVE: int = 1_000_000
|
59 |
+
|
60 |
+
AUTO_UPGRADE_MINING: bool = True
|
61 |
+
MAX_MINING_UPGRADE_LEVEL: int = 30
|
62 |
+
MAX_MINING_ENERGY_RECOVERY_UPGRADE_LEVEL: int = 60
|
63 |
+
MINING_ENERGY_SKILLS: list[str] = ["energy_capacity", "energy_recovery", "profit_per_tap_power"]
|
64 |
+
MAX_MINING_UPGRADE_COSTS: int = 5_000_000
|
65 |
+
|
66 |
+
SKIP_IMPROVE_DISCIPLINE_BUG: bool = Field(
|
67 |
+
default=False,
|
68 |
+
description="Skip improve discipline bug for eror "
|
69 |
+
"{'success': False, 'error': 'invalid key improve_discipline'}",
|
70 |
+
)
|
71 |
+
SKIP_TO_UPGRADE_SKILLS: list = Field([], description='Skip upgrade skills. For example: ["Уборщик", "Рекрутер,HR"]')
|
72 |
+
|
73 |
+
BOT_SLEEP_TIME: list[int] = [3000, 3500]
|
74 |
+
REF_ID: str = "hero1092379081"
|
75 |
+
base_url: str = "https://game.muskempire.io/"
|
76 |
+
bot_name: str = "empirebot"
|
77 |
+
|
78 |
+
|
79 |
+
config = Settings()
|
bot/core/__init__.py
ADDED
File without changes
|
bot/core/__pycache__/__init__.cpython-310.pyc
ADDED
Binary file (136 Bytes). View file
|
|
bot/core/__pycache__/api.cpython-310.pyc
ADDED
Binary file (11.5 kB). View file
|
|
bot/core/__pycache__/bot.cpython-310.pyc
ADDED
Binary file (16.4 kB). View file
|
|
bot/core/__pycache__/errors.cpython-310.pyc
ADDED
Binary file (464 Bytes). View file
|
|
bot/core/__pycache__/models.cpython-310.pyc
ADDED
Binary file (6.96 kB). View file
|
|
bot/core/__pycache__/utils.cpython-310.pyc
ADDED
Binary file (900 Bytes). View file
|
|
bot/core/api.py
ADDED
@@ -0,0 +1,320 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import asyncio
|
2 |
+
import json
|
3 |
+
import random
|
4 |
+
from datetime import datetime
|
5 |
+
from typing import NamedTuple
|
6 |
+
from urllib.parse import parse_qs
|
7 |
+
|
8 |
+
import aiohttp
|
9 |
+
from aiocache import Cache, cached
|
10 |
+
from better_proxy import Proxy
|
11 |
+
from pyrogram import Client, errors
|
12 |
+
from pyrogram.errors import FloodWait, RPCError, UserAlreadyParticipant
|
13 |
+
from pyrogram.raw.functions import account
|
14 |
+
from pyrogram.raw.functions.messages import RequestAppWebView
|
15 |
+
from pyrogram.raw.types import InputBotAppShortName, InputNotifyPeer, InputPeerNotifySettings
|
16 |
+
from pytz import UTC
|
17 |
+
|
18 |
+
from bot.config.logger import log
|
19 |
+
from bot.config.settings import config
|
20 |
+
from bot.helper.utils import error_handler, handle_request
|
21 |
+
|
22 |
+
from .errors import TapsError
|
23 |
+
from .models import FundHelper, Profile, PvpData, UserDataAfter
|
24 |
+
from .utils import num_prettier
|
25 |
+
|
26 |
+
|
27 |
+
class TgWebData(NamedTuple):
|
28 |
+
hash: str
|
29 |
+
request_data: dict
|
30 |
+
|
31 |
+
|
32 |
+
class CryptoBotApi:
|
33 |
+
def __init__(self, tg_client: Client):
|
34 |
+
self.session_name = tg_client.name
|
35 |
+
self.tg_client = tg_client
|
36 |
+
self.user_id = None
|
37 |
+
self.api_url = "https://api.xempire.io"
|
38 |
+
self.need_quiz = False
|
39 |
+
self.need_rebus = False
|
40 |
+
self.rebus_key = ""
|
41 |
+
self.errors = 0
|
42 |
+
self.logger = log.bind(session_name=self.session_name)
|
43 |
+
self._peer = None
|
44 |
+
|
45 |
+
async def get_tg_web_data(self, proxy: str | None) -> TgWebData:
|
46 |
+
if proxy:
|
47 |
+
proxy = Proxy.from_str(proxy)
|
48 |
+
proxy_dict = {
|
49 |
+
"scheme": proxy.protocol,
|
50 |
+
"hostname": proxy.host,
|
51 |
+
"port": proxy.port,
|
52 |
+
"username": proxy.login,
|
53 |
+
"password": proxy.password,
|
54 |
+
}
|
55 |
+
else:
|
56 |
+
proxy_dict = None
|
57 |
+
|
58 |
+
self.tg_client.proxy = proxy_dict
|
59 |
+
|
60 |
+
try:
|
61 |
+
async with self.tg_client:
|
62 |
+
if not self._peer:
|
63 |
+
try:
|
64 |
+
self._peer = await self.tg_client.resolve_peer(config.bot_name)
|
65 |
+
except FloodWait as error:
|
66 |
+
self.logger.warning(f"FloodWait error: {error} | Retry in {error.value} seconds")
|
67 |
+
await asyncio.sleep(delay=error.value)
|
68 |
+
# update in session db peer ids to fix this errors˚
|
69 |
+
async for dialog in self.tg_client.get_dialogs():
|
70 |
+
if dialog.chat and dialog.chat.username and dialog.chat.username == config.bot_name:
|
71 |
+
break
|
72 |
+
self._peer = await self.tg_client.resolve_peer(config.bot_name)
|
73 |
+
|
74 |
+
web_view = await self.tg_client.invoke(
|
75 |
+
RequestAppWebView(
|
76 |
+
peer=self._peer,
|
77 |
+
app=InputBotAppShortName(bot_id=self._peer, short_name="game"),
|
78 |
+
platform="android",
|
79 |
+
write_allowed=True,
|
80 |
+
start_param=config.REF_ID,
|
81 |
+
)
|
82 |
+
)
|
83 |
+
tg_web_data = parse_qs(web_view.url.split("#")[1]).get("tgWebAppData")[0]
|
84 |
+
query_params = parse_qs(tg_web_data)
|
85 |
+
return TgWebData(
|
86 |
+
request_data={
|
87 |
+
"data": {
|
88 |
+
"chatId": "",
|
89 |
+
"chatInstance": tg_web_data,
|
90 |
+
"chatType": query_params.get("chat_type")[0],
|
91 |
+
"initData": tg_web_data,
|
92 |
+
"platform": "android",
|
93 |
+
"startParam": config.REF_ID,
|
94 |
+
},
|
95 |
+
},
|
96 |
+
hash=query_params.get("hash")[0],
|
97 |
+
)
|
98 |
+
|
99 |
+
except RuntimeError as error:
|
100 |
+
raise error from error
|
101 |
+
except FloodWait as error:
|
102 |
+
log.warning(f"{self.session_name} | FloodWait error: {error} | Retry in {error.value} seconds")
|
103 |
+
await asyncio.sleep(delay=error.value)
|
104 |
+
raise
|
105 |
+
except Exception as error:
|
106 |
+
log.error(f"{self.session_name} | Authorization error: {error}")
|
107 |
+
await asyncio.sleep(delay=3)
|
108 |
+
raise
|
109 |
+
|
110 |
+
async def join_and_archive_channel(self, channel_name: str) -> None:
|
111 |
+
try:
|
112 |
+
async with self.tg_client:
|
113 |
+
try:
|
114 |
+
chat = await self.tg_client.join_chat(channel_name)
|
115 |
+
self.logger.info(f"Successfully joined to <g>{chat.title}</g>")
|
116 |
+
except UserAlreadyParticipant:
|
117 |
+
self.logger.info(f"Chat <y>{channel_name}</y> already joined")
|
118 |
+
chat = await self.tg_client.get_chat(channel_name)
|
119 |
+
except RPCError:
|
120 |
+
self.logger.error(f"Channel <y>{channel_name}</y> not found")
|
121 |
+
raise
|
122 |
+
|
123 |
+
await self.sleeper()
|
124 |
+
peer = await self.tg_client.resolve_peer(chat.id)
|
125 |
+
|
126 |
+
await self.tg_client.invoke(
|
127 |
+
account.UpdateNotifySettings(
|
128 |
+
peer=InputNotifyPeer(peer=peer), settings=InputPeerNotifySettings(mute_until=2147483647)
|
129 |
+
)
|
130 |
+
)
|
131 |
+
self.logger.info(f"Successfully muted chat <g>{chat.title}</g> for channel <y>{channel_name}</y>")
|
132 |
+
await self.sleeper()
|
133 |
+
await self.tg_client.archive_chats(chat_ids=[chat.id])
|
134 |
+
self.logger.info(f"Channel <g>{chat.title}</g> successfully archived for channel <y>{channel_name}</y>")
|
135 |
+
|
136 |
+
except errors.FloodWait as e:
|
137 |
+
self.logger.error(f"Waiting {e.value} seconds before the next attempt.")
|
138 |
+
await asyncio.sleep(e.value)
|
139 |
+
raise
|
140 |
+
|
141 |
+
async def sleeper(self, delay: int = config.RANDOM_SLEEP_TIME, additional_delay: int = 4) -> None:
|
142 |
+
await asyncio.sleep(random.random() * delay + additional_delay)
|
143 |
+
|
144 |
+
@error_handler()
|
145 |
+
@handle_request("/telegram/auth")
|
146 |
+
async def login(self, *, response_json: dict, json_body: dict) -> bool:
|
147 |
+
if response_json.get("success", False):
|
148 |
+
self.logger.success("Login successful")
|
149 |
+
return True
|
150 |
+
return False
|
151 |
+
|
152 |
+
@error_handler()
|
153 |
+
@handle_request("/dbs", json_body={"data": {"dbs": ["all"]}})
|
154 |
+
async def get_dbs(self, *, response_json: dict) -> dict:
|
155 |
+
return response_json["data"]
|
156 |
+
|
157 |
+
@error_handler()
|
158 |
+
@handle_request("/hero/balance/sync", json_body={"data": {}})
|
159 |
+
async def syn_hero_balance(self, *, response_json: dict) -> Profile:
|
160 |
+
self._update_money_balance(response_json)
|
161 |
+
self.logger.info(
|
162 |
+
f"Level: <blue>{self.level}</blue> | "
|
163 |
+
f"Balance: <y>{num_prettier(self.balance)}</y> | "
|
164 |
+
f"Money per hour: <g>{num_prettier(self.mph)}</g>"
|
165 |
+
)
|
166 |
+
return Profile(**response_json["data"])
|
167 |
+
|
168 |
+
@error_handler()
|
169 |
+
@handle_request("/user/data/all", json_body={"data": {}})
|
170 |
+
async def get_profile_full(self, *, response_json: dict) -> dict:
|
171 |
+
return dict(**response_json["data"])
|
172 |
+
|
173 |
+
@error_handler()
|
174 |
+
@handle_request("/user/data/after", json_body={"data": {"lang": "en"}})
|
175 |
+
async def user_data_after(self, *, response_json: dict) -> UserDataAfter:
|
176 |
+
return UserDataAfter(**response_json["data"])
|
177 |
+
|
178 |
+
@error_handler()
|
179 |
+
@handle_request("/hero/bonus/offline/claim")
|
180 |
+
async def get_offline_bonus(self, *, response_json: dict) -> None:
|
181 |
+
self._update_money_balance(response_json)
|
182 |
+
self.logger.success(f"Offline bonus claimed: <y>+{num_prettier(self.user_profile.offline_bonus)}</y>")
|
183 |
+
|
184 |
+
@error_handler()
|
185 |
+
@handle_request("/quests/daily/claim")
|
186 |
+
async def daily_reward(self, *, response_json: dict, json_body: dict) -> None:
|
187 |
+
self._update_money_balance(response_json)
|
188 |
+
|
189 |
+
@error_handler()
|
190 |
+
@handle_request("/quests/claim")
|
191 |
+
async def quest_reward_claim(self, *, response_json: dict, json_body: dict) -> bool:
|
192 |
+
self._update_money_balance(response_json)
|
193 |
+
return True
|
194 |
+
|
195 |
+
@error_handler()
|
196 |
+
@handle_request("/quests/daily/progress/claim")
|
197 |
+
async def daily_quest_reward(self, *, response_json: dict, json_body: dict) -> None:
|
198 |
+
self._update_money_balance(response_json)
|
199 |
+
|
200 |
+
@error_handler()
|
201 |
+
@handle_request("/quests/daily/progress/all")
|
202 |
+
async def all_daily_quests(self, *, response_json: dict) -> dict:
|
203 |
+
return response_json["data"]
|
204 |
+
|
205 |
+
@error_handler()
|
206 |
+
@handle_request("/quests/check")
|
207 |
+
async def quest_check(self, *, response_json: dict, json_body: dict) -> bool:
|
208 |
+
await self.sleeper()
|
209 |
+
await self.quest_reward_claim(json_body=json_body)
|
210 |
+
|
211 |
+
@error_handler()
|
212 |
+
@handle_request("/friends/claim")
|
213 |
+
async def friend_reward(self, *, response_json: dict, json_body: dict) -> None:
|
214 |
+
self._update_money_balance(response_json)
|
215 |
+
|
216 |
+
@error_handler()
|
217 |
+
@handle_request("/hero/action/tap")
|
218 |
+
async def api_perform_taps(self, *, response_json: dict, json_body: dict) -> int:
|
219 |
+
if (error_msg := response_json.get("error")) and "take some rest" in error_msg:
|
220 |
+
raise TapsError(error_msg)
|
221 |
+
data = self._update_money_balance(response_json)
|
222 |
+
self.tapped_today = data.get("tappedToday", 0)
|
223 |
+
return int(data["hero"]["earns"]["task"]["energy"])
|
224 |
+
|
225 |
+
@cached(ttl=2 * 60 * 60, cache=Cache.MEMORY)
|
226 |
+
@error_handler()
|
227 |
+
@handle_request(
|
228 |
+
"https://raw.githubusercontent.com/testingstrategy/musk_daily/main/daily.json",
|
229 |
+
method="GET",
|
230 |
+
full_url=True,
|
231 |
+
)
|
232 |
+
async def get_helper(self, *, response_json: str) -> FundHelper | dict:
|
233 |
+
response_json = json.loads(response_json)
|
234 |
+
return FundHelper(
|
235 |
+
funds=response_json.get(str(datetime.now(UTC).date()), {}).get("funds", set()),
|
236 |
+
**response_json,
|
237 |
+
)
|
238 |
+
|
239 |
+
@error_handler()
|
240 |
+
@handle_request("/fund/info")
|
241 |
+
async def get_funds_info(self, *, response_json: dict) -> dict:
|
242 |
+
return response_json["data"]
|
243 |
+
|
244 |
+
@error_handler()
|
245 |
+
@handle_request("/box/list", json_body={})
|
246 |
+
async def get_box_list(self, *, response_json: dict) -> dict:
|
247 |
+
return response_json["data"] or {}
|
248 |
+
|
249 |
+
@error_handler()
|
250 |
+
@handle_request("/box/open")
|
251 |
+
async def box_open(self, *, response_json: dict, json_body: dict) -> list:
|
252 |
+
return response_json["data"]
|
253 |
+
|
254 |
+
@error_handler()
|
255 |
+
@handle_request("/pvp/info")
|
256 |
+
async def get_pvp_info(self, *, response_json: dict) -> dict:
|
257 |
+
return response_json["data"]
|
258 |
+
|
259 |
+
@error_handler()
|
260 |
+
@handle_request("/pvp/fight/start")
|
261 |
+
async def get_pvp_fight(self, *, response_json: dict, json_body: dict) -> PvpData | None:
|
262 |
+
if response_json["data"].get("opponent"):
|
263 |
+
return PvpData(**response_json["data"])
|
264 |
+
return None
|
265 |
+
|
266 |
+
@error_handler()
|
267 |
+
@handle_request("/pvp/claim")
|
268 |
+
async def get_pvp_claim(self, *, response_json: dict) -> None:
|
269 |
+
if response_json.get("success"):
|
270 |
+
self._update_money_balance(response_json)
|
271 |
+
|
272 |
+
@error_handler()
|
273 |
+
@handle_request(
|
274 |
+
"/settings/save",
|
275 |
+
json_body={
|
276 |
+
"data": {
|
277 |
+
"id": None,
|
278 |
+
"music": False,
|
279 |
+
"sound": True,
|
280 |
+
"vibrate": True,
|
281 |
+
"animations": True,
|
282 |
+
"darkTheme": True,
|
283 |
+
"lang": "en",
|
284 |
+
}
|
285 |
+
},
|
286 |
+
)
|
287 |
+
async def sent_eng_settings(self, *, response_json: dict) -> None: ...
|
288 |
+
|
289 |
+
@error_handler()
|
290 |
+
@handle_request("/fund/invest")
|
291 |
+
async def invest(self, *, response_json: dict, json_body: dict) -> None:
|
292 |
+
data = self._update_money_balance(response_json)
|
293 |
+
for fnd in data["funds"]:
|
294 |
+
if fnd["fundKey"] == json_body["data"]["fund"]:
|
295 |
+
money = fnd["moneyProfit"]
|
296 |
+
money_str = (
|
297 |
+
f"Win: <y>+{num_prettier(money)}</y>" if money > 0 else f"Loss: <red>{num_prettier(money)}</red>"
|
298 |
+
)
|
299 |
+
self.logger.success(f"Invest completed: {money_str}")
|
300 |
+
break
|
301 |
+
|
302 |
+
@error_handler()
|
303 |
+
@handle_request("/skills/improve")
|
304 |
+
async def skills_improve(self, *, response_json: dict, json_body: dict) -> None:
|
305 |
+
self._update_money_balance(response_json)
|
306 |
+
|
307 |
+
async def check_proxy(self, proxy: Proxy) -> None:
|
308 |
+
try:
|
309 |
+
response = await self.http_client.get(url="https://httpbin.org/ip", timeout=aiohttp.ClientTimeout(10))
|
310 |
+
ip = (await response.json()).get("origin")
|
311 |
+
self.logger.info(f"Proxy IP: {ip}")
|
312 |
+
except Exception:
|
313 |
+
self.logger.exception(f"Proxy: {proxy}")
|
314 |
+
|
315 |
+
def _update_money_balance(self, response_json: dict) -> dict:
|
316 |
+
response_json = response_json["data"]
|
317 |
+
self.balance = int(response_json["hero"]["money"])
|
318 |
+
self.level = int(response_json["hero"]["level"])
|
319 |
+
self.mph = int(response_json["hero"]["moneyPerHour"])
|
320 |
+
return response_json
|
bot/core/api_js_helpers/__init__.py
ADDED
File without changes
|
bot/core/api_js_helpers/__pycache__/__init__.cpython-310.pyc
ADDED
Binary file (151 Bytes). View file
|
|
bot/core/api_js_helpers/__pycache__/bet_counter.cpython-310.pyc
ADDED
Binary file (1.72 kB). View file
|
|
bot/core/api_js_helpers/__pycache__/upgrader.cpython-310.pyc
ADDED
Binary file (3.24 kB). View file
|
|
bot/core/api_js_helpers/bet_counter.py
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
class BetCounter:
|
2 |
+
bet_steps_count = 7
|
3 |
+
|
4 |
+
def __init__(self, obj):
|
5 |
+
self.obj = obj
|
6 |
+
|
7 |
+
def min_bet(self):
|
8 |
+
multiplier = 2
|
9 |
+
if self.obj.level < 3:
|
10 |
+
multiplier = 5
|
11 |
+
elif self.obj.level < 6:
|
12 |
+
multiplier = 4
|
13 |
+
elif self.obj.level < 10:
|
14 |
+
multiplier = 3
|
15 |
+
|
16 |
+
calculated_bet = self.smart_zero_round(self.obj.mph * multiplier / (self.bet_steps_count * 3))
|
17 |
+
return calculated_bet or 100
|
18 |
+
|
19 |
+
def max_bet(self):
|
20 |
+
return self.min_bet() * self.bet_steps_count
|
21 |
+
|
22 |
+
def smart_zero_round(self, amount: int):
|
23 |
+
def round_to_nearest(value, base=100):
|
24 |
+
return round(value / base) * base
|
25 |
+
|
26 |
+
if amount < 100:
|
27 |
+
return round_to_nearest(amount, 50)
|
28 |
+
elif amount < 1000:
|
29 |
+
return round_to_nearest(amount, 100)
|
30 |
+
elif amount < 10000:
|
31 |
+
return round_to_nearest(amount, 1000)
|
32 |
+
elif amount < 100000:
|
33 |
+
return round_to_nearest(amount, 10000)
|
34 |
+
elif amount < 1000000:
|
35 |
+
return round_to_nearest(amount, 100000)
|
36 |
+
elif amount < 10000000:
|
37 |
+
return round_to_nearest(amount, 1000000)
|
38 |
+
elif amount < 100000000:
|
39 |
+
return round_to_nearest(amount, 10000000)
|
40 |
+
else:
|
41 |
+
return round_to_nearest(amount, 1000)
|
42 |
+
|
43 |
+
def calculate_bet(self) -> int:
|
44 |
+
max_bet_ = self.max_bet()
|
45 |
+
min_bet_ = self.min_bet()
|
46 |
+
while max_bet_ > self.obj.balance:
|
47 |
+
max_bet_ -= min_bet_
|
48 |
+
return max_bet_
|
bot/core/api_js_helpers/upgrader.py
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import math
|
2 |
+
|
3 |
+
|
4 |
+
class Calculator:
|
5 |
+
def get_price(self, data, level):
|
6 |
+
return self.calculate_formula(data.priceFormula, level, data.priceBasic, data.priceFormulaK) if level else 0
|
7 |
+
|
8 |
+
def get_profit(self, data, level):
|
9 |
+
return (
|
10 |
+
self.calculate_formula(data.profitFormula, level, data.profitBasic, data.priceFormulaK, data)
|
11 |
+
if level
|
12 |
+
else 0
|
13 |
+
)
|
14 |
+
|
15 |
+
def calculate_formula(self, formula, level, base_value, formula_coefficient, data=None):
|
16 |
+
result = base_value
|
17 |
+
if formula == "fnCompound":
|
18 |
+
result = self.fn_compound(level, base_value, formula_coefficient)
|
19 |
+
elif formula == "fnLogarithmic":
|
20 |
+
result = self.fn_logarithmic(level, base_value)
|
21 |
+
elif formula == "fnLinear":
|
22 |
+
result = self.fn_linear(level, base_value)
|
23 |
+
elif formula == "fnQuadratic":
|
24 |
+
result = self.fn_quadratic(level, base_value)
|
25 |
+
elif formula == "fnCubic":
|
26 |
+
result = self.fn_cubic(level, base_value)
|
27 |
+
elif formula == "fnExponential":
|
28 |
+
result = self.fn_exponential(level, base_value, formula_coefficient)
|
29 |
+
elif formula == "fnPayback":
|
30 |
+
result = self.fn_payback(level, data)
|
31 |
+
|
32 |
+
return self.smart_round(result)
|
33 |
+
|
34 |
+
def smart_round(self, value):
|
35 |
+
def round_to(value, factor=100):
|
36 |
+
return round(value / factor) * factor
|
37 |
+
|
38 |
+
if value < 50:
|
39 |
+
return round(value)
|
40 |
+
elif value < 100:
|
41 |
+
return round_to(value, 5)
|
42 |
+
elif value < 500:
|
43 |
+
return round_to(value, 25)
|
44 |
+
elif value < 1000:
|
45 |
+
return round_to(value, 50)
|
46 |
+
elif value < 5000:
|
47 |
+
return round_to(value, 100)
|
48 |
+
elif value < 10000:
|
49 |
+
return round_to(value, 200)
|
50 |
+
elif value < 100000:
|
51 |
+
return round_to(value, 500)
|
52 |
+
elif value < 500000:
|
53 |
+
return round_to(value, 1000)
|
54 |
+
elif value < 1000000:
|
55 |
+
return round_to(value, 5000)
|
56 |
+
elif value < 50000000:
|
57 |
+
return round_to(value, 10000)
|
58 |
+
elif value < 100000000:
|
59 |
+
return round_to(value, 50000)
|
60 |
+
else:
|
61 |
+
return round_to(value, 100000)
|
62 |
+
|
63 |
+
def fn_linear(self, level, base_value):
|
64 |
+
return base_value * level
|
65 |
+
|
66 |
+
def fn_quadratic(self, level, base_value):
|
67 |
+
return base_value * level * level
|
68 |
+
|
69 |
+
def fn_cubic(self, level, base_value):
|
70 |
+
return base_value * level * level * level
|
71 |
+
|
72 |
+
def fn_exponential(self, level, base_value, coefficient):
|
73 |
+
return base_value * math.pow(coefficient / 10, level)
|
74 |
+
|
75 |
+
def fn_logarithmic(self, level, base_value):
|
76 |
+
return base_value * math.log2(level + 1)
|
77 |
+
|
78 |
+
def fn_compound(self, level, base_value, coefficient):
|
79 |
+
compound_rate = coefficient / 100
|
80 |
+
return base_value * math.pow(1 + compound_rate, level - 1)
|
81 |
+
|
82 |
+
def fn_payback(self, level, data):
|
83 |
+
accumulated = [0]
|
84 |
+
for current_level in range(1, level + 1):
|
85 |
+
previous_accumulated = accumulated[current_level - 1]
|
86 |
+
current_price = self.get_price(data, current_level)
|
87 |
+
current_profit = data.profitBasic + data.profitFormulaK * (current_level - 1)
|
88 |
+
smart_rounded_value = self.smart_round(previous_accumulated + current_price / current_profit)
|
89 |
+
accumulated.append(smart_rounded_value)
|
90 |
+
return accumulated[level]
|
bot/core/bot.py
ADDED
@@ -0,0 +1,427 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import asyncio
|
2 |
+
import math
|
3 |
+
import random
|
4 |
+
import time
|
5 |
+
from collections.abc import Generator
|
6 |
+
from datetime import datetime
|
7 |
+
from enum import Enum
|
8 |
+
|
9 |
+
import aiohttp
|
10 |
+
from aiohttp_proxy import ProxyConnector
|
11 |
+
from aiohttp_socks import ProxyConnector as SocksProxyConnector
|
12 |
+
from pyrogram import Client
|
13 |
+
from pytz import UTC
|
14 |
+
|
15 |
+
from bot.config.headers import headers
|
16 |
+
from bot.config.logger import log
|
17 |
+
from bot.config.settings import Strategy, config
|
18 |
+
from bot.core.api_js_helpers.bet_counter import BetCounter
|
19 |
+
|
20 |
+
from .api import CryptoBotApi
|
21 |
+
from .errors import TapsError
|
22 |
+
from .models import DbSkill, DbSkills, Profile, ProfileData, SessionData, SkillLevel
|
23 |
+
from .utils import load_codes_from_files, num_prettier
|
24 |
+
|
25 |
+
|
26 |
+
class CryptoBot(CryptoBotApi):
|
27 |
+
def __init__(self, tg_client: Client, additional_data: dict) -> None:
|
28 |
+
super().__init__(tg_client)
|
29 |
+
self.temporary_stop_taps_time = 0
|
30 |
+
self.bet_calculator = BetCounter(self)
|
31 |
+
self.pvp_count = config.PVP_COUNT
|
32 |
+
self.authorized = False
|
33 |
+
self.settings_was_set = False
|
34 |
+
self.sleep_time = config.BOT_SLEEP_TIME
|
35 |
+
self.additional_data: SessionData = SessionData.model_validate(
|
36 |
+
{k: v for d in additional_data for k, v in d.items()}
|
37 |
+
)
|
38 |
+
|
39 |
+
async def claim_daily_reward(self) -> None:
|
40 |
+
for day, status in self.data_after.daily_rewards.items():
|
41 |
+
if status == "canTake":
|
42 |
+
await self.daily_reward(json_body={"data": str(day)})
|
43 |
+
self.logger.success("Daily reward claimed")
|
44 |
+
return
|
45 |
+
|
46 |
+
async def perform_taps(self, profile: Profile) -> None:
|
47 |
+
self.logger.info("Taps started")
|
48 |
+
energy = profile.energy
|
49 |
+
while True:
|
50 |
+
taps_per_second = random.randint(*config.TAPS_PER_SECOND)
|
51 |
+
seconds = random.randint(5, 8)
|
52 |
+
earned_money = profile.money_per_tap * taps_per_second * seconds
|
53 |
+
energy_spent = math.ceil(earned_money / 2)
|
54 |
+
energy -= energy_spent
|
55 |
+
if energy < 0:
|
56 |
+
self.logger.info("Taps stopped (not enough energy)")
|
57 |
+
break
|
58 |
+
await asyncio.sleep(delay=seconds)
|
59 |
+
try:
|
60 |
+
json_data = {
|
61 |
+
"data": {
|
62 |
+
"data": {"task": {"amount": earned_money, "currentEnergy": energy}},
|
63 |
+
"seconds": seconds,
|
64 |
+
}
|
65 |
+
}
|
66 |
+
energy = await self.api_perform_taps(json_body=json_data)
|
67 |
+
self.logger.success(
|
68 |
+
f"Earned money: <y>+{num_prettier(earned_money)}</y> | Energy left: <blue>{num_prettier(energy)}</blue>"
|
69 |
+
)
|
70 |
+
except TapsError as e:
|
71 |
+
self.logger.warning(f"Taps stopped (<red>{e.message}</red>)")
|
72 |
+
self.temporary_stop_taps_time = time.monotonic() + 60 * 60 * 3
|
73 |
+
break
|
74 |
+
|
75 |
+
async def execute_and_claim_daily_quest(self) -> None:
|
76 |
+
helper_data = await self.get_helper()
|
77 |
+
helper_data.youtube.update(load_codes_from_files())
|
78 |
+
all_daily_quests = await self.all_daily_quests()
|
79 |
+
for key, value in all_daily_quests.items():
|
80 |
+
desc = value["description"]
|
81 |
+
if (
|
82 |
+
value["type"] == "youtube"
|
83 |
+
and not value["isRewarded"]
|
84 |
+
and (code := helper_data.youtube.get(value["description"])) is not None
|
85 |
+
):
|
86 |
+
await self.daily_quest_reward(json_body={"data": {"quest": key, "code": str(code)}})
|
87 |
+
self.logger.info(f"Quest <g>{desc}</g> claimed")
|
88 |
+
elif desc:
|
89 |
+
self.logger.info(f"Quest not executed: \n<r>{desc}</r>")
|
90 |
+
if not value["isRewarded"] and value["isComplete"] and not value["url"]:
|
91 |
+
await self.daily_quest_reward(json_body={"data": {"quest": key, "code": None}})
|
92 |
+
self.logger.info(f"Quest <g>{key}</g> claimed")
|
93 |
+
|
94 |
+
async def claim_all_executed_quest(self) -> None:
|
95 |
+
for i in self.data_after.quests:
|
96 |
+
if not i["isRewarded"]:
|
97 |
+
if config.SKIP_IMPROVE_DISCIPLINE_BUG and i["key"] == "improve_discipline":
|
98 |
+
continue
|
99 |
+
await self.quest_reward_claim(json_body={"data": [i["key"], None]})
|
100 |
+
self.logger.info(f'Quest <g>{i["key"]}</g> claimed ')
|
101 |
+
|
102 |
+
def random_pvp_count(self) -> int:
|
103 |
+
return random.randint(config.PVP_COUNT, config.PVP_COUNT * 2)
|
104 |
+
|
105 |
+
async def _perform_pvp(self, league: dict, strategy: str) -> None:
|
106 |
+
self.pvp_count = self.random_pvp_count()
|
107 |
+
self.logger.info(
|
108 |
+
f"PvP negotiations started | League: <blue>{league['key']}</blue> | Strategy: <g>{strategy}</g>"
|
109 |
+
)
|
110 |
+
res = await self.get_pvp_info()
|
111 |
+
await self.sleeper()
|
112 |
+
if res.get("fight"):
|
113 |
+
await self.get_pvp_claim()
|
114 |
+
await self.sleeper()
|
115 |
+
current_strategy = strategy
|
116 |
+
money = 0
|
117 |
+
while self.pvp_count > 0:
|
118 |
+
if self.balance < int(league["maxContract"]):
|
119 |
+
money_str = (
|
120 |
+
f"Profit: <y>+{num_prettier(money)}</y>"
|
121 |
+
if money >= 0
|
122 |
+
else f"Loss: <red>-{num_prettier(money)}</red>"
|
123 |
+
)
|
124 |
+
self.logger.info(f"PvP negotiations stopped (<red>not enough money</red>). Pvp profit: {money_str}")
|
125 |
+
break
|
126 |
+
|
127 |
+
if strategy == "random":
|
128 |
+
current_strategy = random.choice(self.strategies)
|
129 |
+
self.logger.info("Searching opponent...")
|
130 |
+
current_strategy = current_strategy.value if isinstance(current_strategy, Enum) else current_strategy
|
131 |
+
json_data = {"data": {"league": league["key"], "strategy": current_strategy}}
|
132 |
+
response_json = await self.get_pvp_fight(json_body=json_data)
|
133 |
+
if response_json is None:
|
134 |
+
await self.sleeper(delay=10, additional_delay=5)
|
135 |
+
continue
|
136 |
+
|
137 |
+
fight = response_json.fight
|
138 |
+
opponent_strategy = (
|
139 |
+
fight.player2Strategy if fight.player1 == self.user_profile.user_id else fight.player1Strategy
|
140 |
+
)
|
141 |
+
if fight.winner == self.user_profile.user_id:
|
142 |
+
money += fight.moneyProfit
|
143 |
+
log_part = f"You <g>WIN</g> (<y>+{num_prettier(fight.moneyProfit)})</y>"
|
144 |
+
else:
|
145 |
+
money -= fight.moneyContract
|
146 |
+
log_part = f"You <red>LOSE</red> (<y>-{num_prettier(fight.moneyProfit)}</y>)"
|
147 |
+
self.logger.success(
|
148 |
+
f"Contract sum: <y>{num_prettier(fight.moneyContract)}</y> | "
|
149 |
+
f"Your strategy: <c>{current_strategy}</c> | "
|
150 |
+
f"Opponent strategy: <blue>{opponent_strategy}</blue> | "
|
151 |
+
f"{log_part}"
|
152 |
+
)
|
153 |
+
await self.sleeper(additional_delay=10)
|
154 |
+
await self.get_pvp_claim()
|
155 |
+
self.pvp_count -= 1
|
156 |
+
await self.sleeper()
|
157 |
+
|
158 |
+
self.logger.info(
|
159 |
+
"Total money after all pvp:"
|
160 |
+
+ (f"<i><g>+{num_prettier(money)}</g></i>" if money >= 0 else f"<i><red>{num_prettier(money)}</red></i>")
|
161 |
+
)
|
162 |
+
self.pvp_count = config.PVP_COUNT
|
163 |
+
|
164 |
+
async def get_friend_reward(self) -> None:
|
165 |
+
for friend in [friend for friend in self.data_after.friends if friend["bonusToTake"] > 0]:
|
166 |
+
await self.friend_reward(json_body={"data": friend["id"]})
|
167 |
+
self.logger.info(
|
168 |
+
f"Friend <g>{friend['name']}</g> claimed money <y>{num_prettier(friend['bonusToTake'])}</y>"
|
169 |
+
)
|
170 |
+
await self.sleeper()
|
171 |
+
|
172 |
+
async def solve_quiz_and_rebus(self) -> None:
|
173 |
+
for quest in self.dbs["dbQuests"]:
|
174 |
+
quest_key = quest["key"]
|
175 |
+
if quest["requiredLevel"] > self.user_profile.level:
|
176 |
+
continue
|
177 |
+
if "t.me" in (link := quest.get("actionUrl")) and not self._is_event_solved(quest_key):
|
178 |
+
if len(link.split("/")) > 4 or "muskempire" in link:
|
179 |
+
continue
|
180 |
+
if quest["checkType"] != "fakeCheck":
|
181 |
+
link = link if "/+" in link else link.split("/")[-1]
|
182 |
+
await self.join_and_archive_channel(link)
|
183 |
+
await self.quest_check(json_body={"data": [quest_key]})
|
184 |
+
self.logger.info(
|
185 |
+
f'Claimed <g>{quest["title"]}</g> Reward: <y>+{num_prettier(quest["rewardMoney"])}</y>quest'
|
186 |
+
)
|
187 |
+
if any(i in quest_key for i in ("riddle", "rebus", "tg_story")) and not self._is_event_solved(quest_key):
|
188 |
+
await self.quest_check(json_body={"data": [quest_key, quest["checkData"]]})
|
189 |
+
self.logger.info(f"Was solved <g>{quest['title']}</g>")
|
190 |
+
|
191 |
+
def _is_event_solved(self, quest_key: str) -> bool:
|
192 |
+
return self.data_after.quests and any(i["key"] == quest_key for i in self.data_after.quests)
|
193 |
+
|
194 |
+
async def set_funds(self) -> None:
|
195 |
+
helper_data = await self.get_helper()
|
196 |
+
if helper_data.funds:
|
197 |
+
current_invest = await self.get_funds_info()
|
198 |
+
already_funded = {i["fundKey"] for i in current_invest["funds"]}
|
199 |
+
for fund in list(helper_data.funds - already_funded)[: 3 - len(already_funded)]:
|
200 |
+
if self.balance > (amount := self.bet_calculator.calculate_bet()):
|
201 |
+
self.logger.info(f"Investing <y>{num_prettier(amount)}</y> to fund <blue>{fund}</blue>")
|
202 |
+
await self.invest(json_body={"data": {"fund": fund, "money": amount}})
|
203 |
+
else:
|
204 |
+
self.logger.info("Not enough money for invest")
|
205 |
+
|
206 |
+
async def starting_pvp(self) -> None:
|
207 |
+
if self.dbs:
|
208 |
+
league_data = None
|
209 |
+
for league in self.dbs["dbNegotiationsLeague"]:
|
210 |
+
if league["key"] == config.PVP_LEAGUE:
|
211 |
+
league_data = league
|
212 |
+
break
|
213 |
+
|
214 |
+
if league_data is not None:
|
215 |
+
if self.level >= int(league_data["requiredLevel"]):
|
216 |
+
self.strategies = [strategy["key"] for strategy in self.dbs["dbNegotiationsStrategy"]]
|
217 |
+
if Strategy.random == config.PVP_STRATEGY or config.PVP_STRATEGY in self.strategies:
|
218 |
+
await self._perform_pvp(
|
219 |
+
league=league_data,
|
220 |
+
strategy=config.PVP_STRATEGY.value,
|
221 |
+
)
|
222 |
+
else:
|
223 |
+
config.PVP_ENABLED = False
|
224 |
+
self.logger.warning("PVP_STRATEGY param is invalid. PvP negotiations disabled.")
|
225 |
+
else:
|
226 |
+
config.PVP_ENABLED = False
|
227 |
+
self.logger.warning(
|
228 |
+
f"Your level is too low for the {config.PVP_LEAGUE} league. PvP negotiations disabled."
|
229 |
+
)
|
230 |
+
else:
|
231 |
+
config.PVP_ENABLED = False
|
232 |
+
self.logger.warning("PVP_LEAGUE param is invalid. PvP negotiations disabled.")
|
233 |
+
else:
|
234 |
+
self.logger.warning("Database is missing. PvP negotiations will be skipped this time.")
|
235 |
+
|
236 |
+
async def upgrade_hero(self) -> None:
|
237 |
+
available_skill = list(self._get_available_skills())
|
238 |
+
if config.AUTO_UPGRADE_HERO:
|
239 |
+
await self._upgrade_hero_skill(available_skill)
|
240 |
+
if config.AUTO_UPGRADE_MINING:
|
241 |
+
await self._upgrade_mining_skill(available_skill)
|
242 |
+
|
243 |
+
async def get_box_rewards(self) -> None:
|
244 |
+
boxes = await self.get_box_list()
|
245 |
+
for key, box_count in boxes.items():
|
246 |
+
for _ in range(box_count):
|
247 |
+
res = await self.box_open(json_body={"data": key})
|
248 |
+
self.logger.info(f"Box <g>{key}</g> Was looted: <y>{res['loot']}</y>")
|
249 |
+
|
250 |
+
async def _upgrade_mining_skill(self, available_skill: list[DbSkill]) -> None:
|
251 |
+
for skill in [skill for skill in available_skill if skill.category == "mining"]:
|
252 |
+
if (
|
253 |
+
skill.key in config.MINING_ENERGY_SKILLS
|
254 |
+
and skill.next_level <= config.MAX_MINING_ENERGY_RECOVERY_UPGRADE_LEVEL
|
255 |
+
or (
|
256 |
+
skill.next_level <= config.MAX_MINING_UPGRADE_LEVEL
|
257 |
+
or skill.skill_price <= config.MAX_MINING_UPGRADE_COSTS
|
258 |
+
)
|
259 |
+
):
|
260 |
+
await self._upgrade_skill(skill)
|
261 |
+
|
262 |
+
def _is_enough_money_for_upgrade(self, skill: DbSkill) -> bool:
|
263 |
+
return (self.balance - skill.skill_price) >= config.MONEY_TO_SAVE
|
264 |
+
|
265 |
+
async def _upgrade_hero_skill(self, available_skill: list[DbSkill]) -> None:
|
266 |
+
for skill in sorted(
|
267 |
+
[skill for skill in available_skill if skill.weight],
|
268 |
+
key=lambda x: x.weight,
|
269 |
+
reverse=True,
|
270 |
+
):
|
271 |
+
if skill.title in config.SKIP_TO_UPGRADE_SKILLS:
|
272 |
+
continue
|
273 |
+
# if skill.weight >= config.SKILL_WEIGHT or skill.skill_price <= config.MAX_SKILL_UPGRADE_COSTS:
|
274 |
+
if skill.weight >= config.SKILL_WEIGHT:
|
275 |
+
await self._upgrade_skill(skill)
|
276 |
+
|
277 |
+
async def _upgrade_skill(self, skill: DbSkill) -> None:
|
278 |
+
if self._is_enough_money_for_upgrade(skill):
|
279 |
+
try:
|
280 |
+
await self.skills_improve(json_body={"data": skill.key})
|
281 |
+
self.logger.info(
|
282 |
+
f"Skill: <blue>{skill.title}</blue> upgraded to level: <c>{skill.next_level}</c> "
|
283 |
+
f"Profit: <y>{num_prettier(skill.skill_profit)}</y> "
|
284 |
+
f"Costs: <blue>{num_prettier(skill.skill_price)}</blue> "
|
285 |
+
f"Money stay: <y>{num_prettier(self.balance)}</y> "
|
286 |
+
f"Skill weight <magenta>{skill.weight:.5f}</magenta>"
|
287 |
+
)
|
288 |
+
await self.sleeper()
|
289 |
+
except ValueError:
|
290 |
+
self.logger.exception(f"Failed to upgrade skill: {skill}")
|
291 |
+
raise
|
292 |
+
|
293 |
+
def _get_available_skills(self) -> Generator[DbSkill, None, None]:
|
294 |
+
for skill in DbSkills(**self.dbs).dbSkills:
|
295 |
+
self._calkulate_skill_requirements(skill)
|
296 |
+
if self._is_available_to_upgrade_skills(skill):
|
297 |
+
yield skill
|
298 |
+
|
299 |
+
def _calkulate_skill_requirements(self, skill: DbSkill) -> None:
|
300 |
+
skill.next_level = (
|
301 |
+
self.data_after.skills[skill.key]["level"] + 1 if self.data_after.skills.get(skill.key) else 1
|
302 |
+
)
|
303 |
+
skill.skill_profit = skill.calculate_profit(skill.next_level)
|
304 |
+
skill.skill_price = skill.price_for_level(skill.next_level)
|
305 |
+
skill.weight = skill.skill_profit / skill.skill_price
|
306 |
+
skill.progress_time = skill.get_skill_time(self.data_after)
|
307 |
+
|
308 |
+
def _is_available_to_upgrade_skills(self, skill: DbSkill) -> bool:
|
309 |
+
# check the current skill is still in the process of improvement
|
310 |
+
if skill.progress_time and skill.progress_time.timestamp() + 60 > datetime.now(UTC).timestamp():
|
311 |
+
return False
|
312 |
+
if skill.next_level > skill.maxLevel:
|
313 |
+
return False
|
314 |
+
skill_requirements = skill.get_level_by_skill_level(skill.next_level)
|
315 |
+
if not skill_requirements:
|
316 |
+
return True
|
317 |
+
return (
|
318 |
+
len(self.data_after.friends) >= skill_requirements.requiredFriends
|
319 |
+
and self.user_profile.level >= skill_requirements.requiredHeroLevel
|
320 |
+
and self._is_can_learn_skill(skill_requirements)
|
321 |
+
)
|
322 |
+
|
323 |
+
def _is_can_learn_skill(self, level: SkillLevel) -> bool:
|
324 |
+
if not level.requiredSkills:
|
325 |
+
return True
|
326 |
+
for skill, level in level.requiredSkills.items():
|
327 |
+
if skill not in self.data_after.skills:
|
328 |
+
return False
|
329 |
+
if self.data_after.skills[skill]["level"] >= level:
|
330 |
+
return True
|
331 |
+
return False
|
332 |
+
|
333 |
+
async def login_to_app(self, proxy: str | None) -> bool:
|
334 |
+
if self.authorized:
|
335 |
+
return True
|
336 |
+
tg_web_data = await self.get_tg_web_data(proxy=proxy)
|
337 |
+
self.http_client.headers["Api-Key"] = tg_web_data.hash
|
338 |
+
if await self.login(json_body=tg_web_data.request_data):
|
339 |
+
self.authorized = True
|
340 |
+
return True
|
341 |
+
return False
|
342 |
+
|
343 |
+
async def run(self, proxy: str | None) -> None:
|
344 |
+
proxy = proxy or self.additional_data.proxy
|
345 |
+
if proxy and "socks" in proxy:
|
346 |
+
proxy_conn = SocksProxyConnector.from_url(proxy)
|
347 |
+
elif proxy:
|
348 |
+
proxy_conn = ProxyConnector.from_url(proxy)
|
349 |
+
else:
|
350 |
+
proxy_conn = None
|
351 |
+
|
352 |
+
async with aiohttp.ClientSession(
|
353 |
+
headers=headers,
|
354 |
+
connector=proxy_conn,
|
355 |
+
timeout=aiohttp.ClientTimeout(total=60),
|
356 |
+
) as http_client:
|
357 |
+
self.http_client = http_client
|
358 |
+
if proxy:
|
359 |
+
await self.check_proxy(proxy=proxy)
|
360 |
+
|
361 |
+
while True:
|
362 |
+
if self.errors >= config.ERRORS_BEFORE_STOP:
|
363 |
+
self.logger.error("Bot stopped (too many errors)")
|
364 |
+
break
|
365 |
+
try:
|
366 |
+
if await self.login_to_app(proxy):
|
367 |
+
# if not self.settings_was_set:
|
368 |
+
# await self.sent_eng_settings()
|
369 |
+
data = await self.get_profile_full()
|
370 |
+
self.dbs = data["dbData"]
|
371 |
+
await self.get_box_rewards()
|
372 |
+
|
373 |
+
self.user_profile: ProfileData = ProfileData(**data)
|
374 |
+
if self.user_profile.offline_bonus > 0:
|
375 |
+
await self.get_offline_bonus()
|
376 |
+
|
377 |
+
profile = await self.syn_hero_balance()
|
378 |
+
|
379 |
+
config.MONEY_TO_SAVE = self.bet_calculator.max_bet()
|
380 |
+
self.logger.info(f"Max bet for funds saved: <y>{num_prettier(config.MONEY_TO_SAVE)}</y>")
|
381 |
+
|
382 |
+
self.data_after = await self.user_data_after()
|
383 |
+
|
384 |
+
await self.claim_daily_reward()
|
385 |
+
|
386 |
+
await self.execute_and_claim_daily_quest()
|
387 |
+
|
388 |
+
await self.syn_hero_balance()
|
389 |
+
|
390 |
+
await self.get_friend_reward()
|
391 |
+
|
392 |
+
if config.TAPS_ENABLED and profile.energy and time.monotonic() > self.temporary_stop_taps_time:
|
393 |
+
await self.perform_taps(profile)
|
394 |
+
|
395 |
+
await self.set_funds()
|
396 |
+
await self.solve_quiz_and_rebus()
|
397 |
+
|
398 |
+
await self.claim_all_executed_quest()
|
399 |
+
|
400 |
+
await self.syn_hero_balance()
|
401 |
+
|
402 |
+
await self.upgrade_hero()
|
403 |
+
|
404 |
+
if config.PVP_ENABLED:
|
405 |
+
await self.starting_pvp()
|
406 |
+
await self.syn_hero_balance()
|
407 |
+
sleep_time = random.randint(*config.BOT_SLEEP_TIME)
|
408 |
+
self.logger.info(f"Sleep minutes {sleep_time // 60} minutes")
|
409 |
+
await asyncio.sleep(sleep_time)
|
410 |
+
|
411 |
+
except RuntimeError as error:
|
412 |
+
raise error from error
|
413 |
+
except Exception:
|
414 |
+
self.errors += 1
|
415 |
+
self.authorized = False
|
416 |
+
self.logger.exception("Unknown error")
|
417 |
+
await self.sleeper(additional_delay=self.errors * 8)
|
418 |
+
else:
|
419 |
+
self.errors = 0
|
420 |
+
self.authorized = False
|
421 |
+
|
422 |
+
|
423 |
+
async def run_bot(tg_client: Client, proxy: str | None, additional_data: dict) -> None:
|
424 |
+
try:
|
425 |
+
await CryptoBot(tg_client=tg_client, additional_data=additional_data).run(proxy=proxy)
|
426 |
+
except RuntimeError:
|
427 |
+
log.bind(session_name=tg_client.name).exception("Session error")
|
bot/core/errors.py
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
class TapsError(Exception):
|
2 |
+
def __init__(self, message: str) -> None:
|
3 |
+
self.message = message
|
bot/core/models.py
ADDED
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from datetime import datetime
|
2 |
+
from typing import Any
|
3 |
+
|
4 |
+
from pydantic import AliasPath, BaseModel, Field, field_validator
|
5 |
+
from pytz import UTC
|
6 |
+
|
7 |
+
from bot.core.api_js_helpers.upgrader import Calculator
|
8 |
+
|
9 |
+
|
10 |
+
class SkillLevel(BaseModel):
|
11 |
+
level: int
|
12 |
+
title: str
|
13 |
+
requiredSkills: dict | list
|
14 |
+
requiredHeroLevel: int
|
15 |
+
requiredFriends: int
|
16 |
+
desc: str
|
17 |
+
|
18 |
+
|
19 |
+
class DbSkill(BaseModel):
|
20 |
+
key: str
|
21 |
+
title: str
|
22 |
+
category: str
|
23 |
+
subCategory: str
|
24 |
+
priceBasic: int
|
25 |
+
priceFormula: str
|
26 |
+
priceFormulaK: int
|
27 |
+
profitBasic: int
|
28 |
+
profitFormula: str
|
29 |
+
profitFormulaK: int
|
30 |
+
maxLevel: int
|
31 |
+
timeBasic: str
|
32 |
+
timeFormula: str
|
33 |
+
timeFormulaK: str
|
34 |
+
desc: str
|
35 |
+
special: str
|
36 |
+
levels: list[SkillLevel]
|
37 |
+
next_level: int = 1
|
38 |
+
skill_profit: int = 0
|
39 |
+
skill_price: int = 0
|
40 |
+
weight: int = 0
|
41 |
+
progress_time: datetime | None = None
|
42 |
+
|
43 |
+
def __init__(self, /, **data: Any) -> None:
|
44 |
+
super().__init__(**data)
|
45 |
+
self._calculator = Calculator()
|
46 |
+
|
47 |
+
def get_level_by_skill_level(self, level: int) -> SkillLevel | None:
|
48 |
+
if not self.levels or self.levels[0].level > level:
|
49 |
+
return None
|
50 |
+
|
51 |
+
for index, skill_level in enumerate(self.levels):
|
52 |
+
if skill_level.level <= level:
|
53 |
+
if index + 1 == len(self.levels):
|
54 |
+
return skill_level
|
55 |
+
if self.levels[index + 1].level > level:
|
56 |
+
return skill_level
|
57 |
+
|
58 |
+
return None
|
59 |
+
|
60 |
+
def calculate_profit(self, level: int) -> int:
|
61 |
+
return self._calculator.get_profit(self, level)
|
62 |
+
|
63 |
+
def price_for_level(self, level: int) -> int:
|
64 |
+
return self._calculator.get_price(self, level)
|
65 |
+
|
66 |
+
def get_skill_time(self, data_after: "UserDataAfter") -> None | datetime:
|
67 |
+
if finish_time := data_after.skills.get(self.key, {}).get("finishUpgradeDate"):
|
68 |
+
return datetime.strptime(finish_time, "%Y-%m-%d %H:%M:%S").replace(tzinfo=UTC)
|
69 |
+
return None
|
70 |
+
|
71 |
+
|
72 |
+
class DbSkills(BaseModel):
|
73 |
+
dbSkills: list[DbSkill]
|
74 |
+
|
75 |
+
|
76 |
+
class ProfileData(BaseModel):
|
77 |
+
user_id: int = Field(validation_alias=AliasPath("profile", "id"))
|
78 |
+
money: int = Field(validation_alias=AliasPath("hero", "money"))
|
79 |
+
level: int = Field(validation_alias=AliasPath("hero", "level"))
|
80 |
+
money_per_hour: int = Field(validation_alias=AliasPath("hero", "moneyPerHour"))
|
81 |
+
offline_bonus: int = Field(validation_alias=AliasPath("hero", "offlineBonus"))
|
82 |
+
|
83 |
+
|
84 |
+
class UserDataAfter(BaseModel):
|
85 |
+
daily_rewards: dict = Field(validation_alias=AliasPath("dailyRewards"))
|
86 |
+
quests: list = Field(validation_alias=AliasPath("quests"))
|
87 |
+
friends: list = Field(validation_alias=AliasPath("friends"))
|
88 |
+
skills: dict | list = Field(
|
89 |
+
description="all user learned skills",
|
90 |
+
examples=[
|
91 |
+
{"desks": {"level": 6, "lastUpgradeDate": "2024-07-30 19:20:32", "finishUpgradeDate": None}},
|
92 |
+
{
|
93 |
+
"empathy": {
|
94 |
+
"level": 6,
|
95 |
+
"lastUpgradeDate": "2024-07-30 19:21:36",
|
96 |
+
"finishUpgradeDate": "2024-07-30 19:22:13",
|
97 |
+
}
|
98 |
+
},
|
99 |
+
],
|
100 |
+
)
|
101 |
+
|
102 |
+
@field_validator("skills")
|
103 |
+
@classmethod
|
104 |
+
def check_skills(cls, v: Any) -> dict:
|
105 |
+
return v or {}
|
106 |
+
|
107 |
+
|
108 |
+
class Profile(BaseModel):
|
109 |
+
money_per_tap: int = Field(validation_alias=AliasPath("hero", "earns", "task", "moneyPerTap"))
|
110 |
+
limit: int = Field(validation_alias=AliasPath("hero", "earns", "task", "limit"))
|
111 |
+
energy: int = Field(validation_alias=AliasPath("hero", "earns", "task", "energy"))
|
112 |
+
energy_recovery: int = Field(validation_alias=AliasPath("hero", "earns", "task", "recoveryPerSecond"))
|
113 |
+
|
114 |
+
money: int = Field(validation_alias=AliasPath("hero", "money"))
|
115 |
+
level: int = Field(validation_alias=AliasPath("hero", "level"))
|
116 |
+
money_per_hour: int = Field(validation_alias=AliasPath("hero", "moneyPerHour"))
|
117 |
+
|
118 |
+
|
119 |
+
class Fight(BaseModel):
|
120 |
+
league: str
|
121 |
+
moneyProfit: int
|
122 |
+
player1: int
|
123 |
+
moneyContract: int
|
124 |
+
player1Strategy: str
|
125 |
+
player1Level: int
|
126 |
+
player1Rewarded: bool
|
127 |
+
player2: int
|
128 |
+
player2Strategy: str
|
129 |
+
player2Rewarded: bool
|
130 |
+
winner: int
|
131 |
+
|
132 |
+
|
133 |
+
class PvpData(BaseModel):
|
134 |
+
opponent: dict | None
|
135 |
+
fight: Fight | None
|
136 |
+
|
137 |
+
|
138 |
+
class FundHelper(BaseModel):
|
139 |
+
funds: set = Field(default_factory=set)
|
140 |
+
youtube: dict
|
141 |
+
|
142 |
+
|
143 |
+
class Skills(BaseModel):
|
144 |
+
skills: dict
|
145 |
+
|
146 |
+
|
147 |
+
if __name__ == "__main__":
|
148 |
+
data = {
|
149 |
+
"hero": {
|
150 |
+
"id": 1092379081,
|
151 |
+
"level": 12,
|
152 |
+
"exp": 207156950,
|
153 |
+
"money": 8154952,
|
154 |
+
"moneyUpdateDate": "2024-08-05 07:02:28",
|
155 |
+
"lastOfflineBonusDate": "2024-08-05 07:02:28",
|
156 |
+
"moneyPerHour": 7943050,
|
157 |
+
"energyUpdateDate": "2024-08-05 07:02:28",
|
158 |
+
"tax": 20,
|
159 |
+
"pvpMatch": 1143,
|
160 |
+
"pvpWin": 646,
|
161 |
+
"pvpLose": 497,
|
162 |
+
"pvpMatchesDaily": 123,
|
163 |
+
"pvpMatchesDailyDay": "2024-08-04",
|
164 |
+
"earns": {
|
165 |
+
"task": {"moneyPerTap": 21, "limit": 9500, "energy": 9500, "recoveryPerSecond": 14},
|
166 |
+
"sell": {"moneyPerTap": 20, "limit": 6600, "energy": 6600, "recoveryPerSecond": 14},
|
167 |
+
},
|
168 |
+
"dailyRewardLastDate": "2024-08-04 11:15:56",
|
169 |
+
"dailyRewardLastIndex": 7,
|
170 |
+
"onboarding": [
|
171 |
+
"9040",
|
172 |
+
"41",
|
173 |
+
"30",
|
174 |
+
"40",
|
175 |
+
"9000",
|
176 |
+
"90",
|
177 |
+
"70",
|
178 |
+
"10720",
|
179 |
+
"9020",
|
180 |
+
"10015",
|
181 |
+
"9010",
|
182 |
+
"80",
|
183 |
+
"50",
|
184 |
+
"10700",
|
185 |
+
"60",
|
186 |
+
"9030",
|
187 |
+
"20",
|
188 |
+
"51",
|
189 |
+
"1",
|
190 |
+
],
|
191 |
+
"updateDate": "2024-07-19 16:39:56.91573",
|
192 |
+
"userId": 1092379081,
|
193 |
+
},
|
194 |
+
"fight": {
|
195 |
+
"id": "08999015-6835-44ed-b00e-3ac529b62285",
|
196 |
+
"league": "bronze",
|
197 |
+
"moneyContract": 6600,
|
198 |
+
"moneyProfit": None,
|
199 |
+
"searchTime": None,
|
200 |
+
"player1": 1092379081,
|
201 |
+
"player1Strategy": "aggressive",
|
202 |
+
"player1Level": 12,
|
203 |
+
"player1Rewarded": False,
|
204 |
+
"player2": None,
|
205 |
+
"player2Strategy": None,
|
206 |
+
"player2Rewarded": False,
|
207 |
+
"winner": None,
|
208 |
+
"draw": [],
|
209 |
+
"updateDate": None,
|
210 |
+
"creationDate": "2024-08-05 07:02:35",
|
211 |
+
},
|
212 |
+
"opponent": None,
|
213 |
+
}
|
214 |
+
PvpData(**data)
|
215 |
+
|
216 |
+
|
217 |
+
class SessionData(BaseModel):
|
218 |
+
user_agent: str = Field(validation_alias="User-Agent")
|
219 |
+
proxy: str | None = None
|
bot/core/utils.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
from functools import lru_cache
|
3 |
+
from pathlib import Path
|
4 |
+
|
5 |
+
|
6 |
+
@lru_cache
|
7 |
+
def load_codes_from_files() -> dict:
|
8 |
+
with Path("youtube.json").open("r", encoding="utf-8") as file:
|
9 |
+
return json.load(file)
|
10 |
+
|
11 |
+
|
12 |
+
def num_prettier(num: int) -> str:
|
13 |
+
number = abs(num)
|
14 |
+
if number >= (comparer := 1e12):
|
15 |
+
prettier_num = f"{number / comparer:.1f}T"
|
16 |
+
elif number >= (comparer := 1e9):
|
17 |
+
prettier_num = f"{number / comparer:.1f}B"
|
18 |
+
elif number >= (comparer := 1e6):
|
19 |
+
prettier_num = f"{number / comparer:.1f}M"
|
20 |
+
elif number >= (comparer := 1e3):
|
21 |
+
prettier_num = f"{number / comparer:.1f}k"
|
22 |
+
else:
|
23 |
+
prettier_num = str(number)
|
24 |
+
return f"-{prettier_num}" if num < 0 else prettier_num
|
bot/helper/__pycache__/utils.cpython-310.pyc
ADDED
Binary file (3.03 kB). View file
|
|
bot/helper/utils.py
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import asyncio
|
2 |
+
import hashlib
|
3 |
+
import json
|
4 |
+
import random
|
5 |
+
from collections.abc import Callable
|
6 |
+
from functools import wraps
|
7 |
+
from time import time
|
8 |
+
|
9 |
+
import aiohttp
|
10 |
+
from loguru import logger
|
11 |
+
|
12 |
+
|
13 |
+
def error_handler(delay=3):
|
14 |
+
def decorator(func):
|
15 |
+
@wraps(func)
|
16 |
+
async def wrapper(*args, **kwargs):
|
17 |
+
try:
|
18 |
+
return await func(*args, **kwargs)
|
19 |
+
except Exception as error:
|
20 |
+
logger.error(f"Error in {func.__name__}: {error}")
|
21 |
+
await asyncio.sleep(random.randint(delay, delay * 2))
|
22 |
+
raise
|
23 |
+
|
24 |
+
return wrapper
|
25 |
+
|
26 |
+
return decorator
|
27 |
+
|
28 |
+
|
29 |
+
def handle_request(
|
30 |
+
endpoint: str,
|
31 |
+
full_url: bool = False,
|
32 |
+
method: str = "POST",
|
33 |
+
raise_for_status: bool = True,
|
34 |
+
json_body: dict | None = None,
|
35 |
+
):
|
36 |
+
def decorator(func: Callable) -> Callable:
|
37 |
+
@wraps(func)
|
38 |
+
async def wrapper(self, *args, **kwargs):
|
39 |
+
url = endpoint if full_url else self.api_url + endpoint
|
40 |
+
if method.upper() == "POST":
|
41 |
+
_json_body = kwargs.get("json_body") or json_body or {}
|
42 |
+
set_sign_headers(http_client=self.http_client, data=_json_body)
|
43 |
+
response = await self.http_client.post(url, json=_json_body)
|
44 |
+
elif method.upper() == "GET":
|
45 |
+
response = await self.http_client.get(url)
|
46 |
+
else:
|
47 |
+
msg = "Unsupported HTTP method"
|
48 |
+
raise ValueError(msg)
|
49 |
+
if raise_for_status:
|
50 |
+
response.raise_for_status()
|
51 |
+
|
52 |
+
content_type = response.headers.get("Content-Type", "")
|
53 |
+
if "application/json" in content_type:
|
54 |
+
response_data = await response.json()
|
55 |
+
elif "text/" in content_type:
|
56 |
+
response_data = await response.text()
|
57 |
+
else:
|
58 |
+
response_data = await response.read()
|
59 |
+
return await func(self, response_json=response_data, **kwargs)
|
60 |
+
|
61 |
+
return wrapper
|
62 |
+
|
63 |
+
return decorator
|
64 |
+
|
65 |
+
|
66 |
+
def set_sign_headers(http_client: aiohttp.ClientSession, data: dict) -> None:
|
67 |
+
time_string = str(int(time()))
|
68 |
+
json_string = json.dumps(data)
|
69 |
+
hash_object = hashlib.md5()
|
70 |
+
hash_object.update(f"{time_string}_{json_string}".encode())
|
71 |
+
hash_string = hash_object.hexdigest()
|
72 |
+
http_client.headers["Api-Time"] = time_string
|
73 |
+
http_client.headers["Api-Hash"] = hash_string
|
74 |
+
|
75 |
+
|
76 |
+
def error_handler(delay=3):
|
77 |
+
def decorator(func):
|
78 |
+
@wraps(func)
|
79 |
+
async def wrapper(self, *args, **kwargs):
|
80 |
+
try:
|
81 |
+
return await func(self, *args, **kwargs)
|
82 |
+
except Exception as error:
|
83 |
+
self.logger.error(f"Error in {func.__name__}: {error}")
|
84 |
+
await asyncio.sleep(random.randint(delay, delay * 2))
|
85 |
+
raise
|
86 |
+
|
87 |
+
return wrapper
|
88 |
+
|
89 |
+
return decorator
|
bot/launcher.py
ADDED
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import asyncio
|
2 |
+
import random
|
3 |
+
from argparse import ArgumentParser
|
4 |
+
from itertools import cycle
|
5 |
+
from pathlib import Path
|
6 |
+
from typing import NamedTuple
|
7 |
+
|
8 |
+
from better_proxy import Proxy
|
9 |
+
from pyrogram import Client
|
10 |
+
|
11 |
+
from bot.config.logger import log
|
12 |
+
from bot.config.settings import config, logo
|
13 |
+
from bot.core.bot import run_bot
|
14 |
+
from bot.utils import get_session_profiles
|
15 |
+
|
16 |
+
start_text = """
|
17 |
+
Select an action:
|
18 |
+
1. Create session
|
19 |
+
2. Run bot
|
20 |
+
"""
|
21 |
+
|
22 |
+
|
23 |
+
class SessionData(NamedTuple):
|
24 |
+
tg_client: Client
|
25 |
+
session_data: dict
|
26 |
+
|
27 |
+
|
28 |
+
def get_session_names() -> list[str]:
|
29 |
+
return [file.stem for file in sorted(Path("sessions").glob("*.session"))]
|
30 |
+
|
31 |
+
|
32 |
+
async def register_sessions() -> None:
|
33 |
+
session_name = input("\nEnter the session name (press Enter to exit): ")
|
34 |
+
if not session_name:
|
35 |
+
return
|
36 |
+
|
37 |
+
sessions_path = Path("sessions")
|
38 |
+
if not sessions_path.exists():
|
39 |
+
sessions_path.mkdir()
|
40 |
+
|
41 |
+
session = Client(
|
42 |
+
name=session_name,
|
43 |
+
api_id=config.API_ID,
|
44 |
+
api_hash=config.API_HASH,
|
45 |
+
workdir="sessions/",
|
46 |
+
)
|
47 |
+
|
48 |
+
async with session:
|
49 |
+
user_data = await session.get_me()
|
50 |
+
log.success(
|
51 |
+
f"Session added successfully: {user_data.username or user_data.id} | "
|
52 |
+
f"{user_data.first_name or ''} {user_data.last_name or ''}"
|
53 |
+
)
|
54 |
+
|
55 |
+
|
56 |
+
def get_proxies() -> [str | None]:
|
57 |
+
if config.USE_PROXY_FROM_FILE:
|
58 |
+
with Path("proxies.txt").open(encoding="utf-8") as file:
|
59 |
+
return [Proxy.from_str(proxy=row.strip()).as_url for row in file if row.strip()]
|
60 |
+
return None
|
61 |
+
|
62 |
+
|
63 |
+
async def get_tg_clients() -> list[SessionData]:
|
64 |
+
session_names = get_session_names()
|
65 |
+
|
66 |
+
if not session_names:
|
67 |
+
msg = "Not found session files"
|
68 |
+
raise FileNotFoundError(msg)
|
69 |
+
session_profiles = get_session_profiles(session_names)
|
70 |
+
return [
|
71 |
+
SessionData(
|
72 |
+
tg_client=Client(
|
73 |
+
name=session_name,
|
74 |
+
api_id=config.API_ID,
|
75 |
+
api_hash=config.API_HASH,
|
76 |
+
workdir="sessions/",
|
77 |
+
),
|
78 |
+
session_data=session_profiles[session_name],
|
79 |
+
)
|
80 |
+
for session_name in session_names
|
81 |
+
]
|
82 |
+
|
83 |
+
|
84 |
+
async def run_bot_with_delay(tg_client: Client, proxy: str | None, additional_data: dict, session_index: int) -> None:
|
85 |
+
delay = session_index * config.SESSION_AC_DELAY + random.randint(*config.SLEEP_BETWEEN_START)
|
86 |
+
log.bind(session_name=tg_client.name).info(f"Wait {delay} seconds before start")
|
87 |
+
await asyncio.sleep(delay)
|
88 |
+
await run_bot(tg_client=tg_client, proxy=proxy, additional_data=additional_data)
|
89 |
+
|
90 |
+
|
91 |
+
async def run_clients(session_data: list[SessionData]) -> None:
|
92 |
+
proxies = get_proxies() or [None]
|
93 |
+
if config.ADD_LOCAL_MACHINE_AS_IP:
|
94 |
+
proxies.append(None)
|
95 |
+
proxy_cycle = cycle(proxies)
|
96 |
+
await asyncio.gather(
|
97 |
+
*[
|
98 |
+
run_bot_with_delay(
|
99 |
+
tg_client=s_data.tg_client,
|
100 |
+
proxy=next(proxy_cycle),
|
101 |
+
additional_data=s_data.session_data,
|
102 |
+
session_index=index,
|
103 |
+
)
|
104 |
+
for index, s_data in enumerate(session_data)
|
105 |
+
]
|
106 |
+
)
|
107 |
+
|
108 |
+
|
109 |
+
async def start() -> None:
|
110 |
+
print(logo)
|
111 |
+
parser = ArgumentParser()
|
112 |
+
parser.add_argument("-a", "--action", type=int, choices=[1, 2], help="Action to perform (1 or 2)")
|
113 |
+
log.info(f"Detected {len(get_session_names())} sessions | {len(proxy) if (proxy := get_proxies()) else 0} proxies")
|
114 |
+
action = parser.parse_args().action
|
115 |
+
|
116 |
+
if not action:
|
117 |
+
print(start_text)
|
118 |
+
while True:
|
119 |
+
action = input("> ").strip()
|
120 |
+
if action.isdigit() and action in ["1", "2"]:
|
121 |
+
action = int(action)
|
122 |
+
break
|
123 |
+
log.warning("Action must be a number (1 or 2)")
|
124 |
+
|
125 |
+
if action == 1:
|
126 |
+
await register_sessions()
|
127 |
+
elif action == 2:
|
128 |
+
session_data = await get_tg_clients()
|
129 |
+
await run_clients(session_data=session_data)
|
bot/utils.py
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
from pathlib import Path
|
3 |
+
|
4 |
+
from fake_useragent import UserAgent
|
5 |
+
|
6 |
+
|
7 |
+
def read_session_profiles(sessions: list[str]) -> dict | None:
|
8 |
+
file_path = Path("session_profile.json")
|
9 |
+
if not file_path.exists():
|
10 |
+
return None
|
11 |
+
|
12 |
+
try:
|
13 |
+
with file_path.open(encoding="utf-8") as file:
|
14 |
+
data = json.load(file)
|
15 |
+
return data if all(session in data and len(data[session]) >= 2 for session in sessions) else None
|
16 |
+
except (OSError, json.JSONDecodeError):
|
17 |
+
return None
|
18 |
+
|
19 |
+
|
20 |
+
def get_session_profiles(sessions: list[str]) -> dict:
|
21 |
+
session_profiles = read_session_profiles(sessions)
|
22 |
+
if session_profiles is None:
|
23 |
+
session_profiles = {}
|
24 |
+
ua_generator = UserAgent(browsers=["safari"], os=["ios"], platforms=["mobile", "tablet"])
|
25 |
+
|
26 |
+
for session in sessions:
|
27 |
+
inner = session_profiles.setdefault(session, [])
|
28 |
+
inner.append({"User-Agent": ua_generator.random})
|
29 |
+
inner.append({"proxy": None})
|
30 |
+
|
31 |
+
with Path("session_profile.json").open("w", encoding="utf-8") as file:
|
32 |
+
json.dump(session_profiles, file, ensure_ascii=False, indent=4)
|
33 |
+
|
34 |
+
return session_profiles
|
data.json
ADDED
File without changes
|
docker-compose.yml
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
version: '3'
|
2 |
+
services:
|
3 |
+
bot:
|
4 |
+
container_name: 'MuskEmpireBot'
|
5 |
+
stop_signal: SIGINT
|
6 |
+
build:
|
7 |
+
context: .
|
8 |
+
working_dir: /app
|
9 |
+
volumes:
|
10 |
+
- .:/app
|
11 |
+
entrypoint: "python3 main.py"
|
12 |
+
command: ["-a", "2"]
|
13 |
+
restart: unless-stopped
|
14 |
+
env_file: .env
|
main.py
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import asyncio
|
2 |
+
|
3 |
+
from bot import launcher
|
4 |
+
from bot.config.logger import log
|
5 |
+
|
6 |
+
|
7 |
+
async def main() -> None:
|
8 |
+
await launcher.start()
|
9 |
+
|
10 |
+
|
11 |
+
if __name__ == "__main__":
|
12 |
+
try:
|
13 |
+
asyncio.run(main())
|
14 |
+
except KeyboardInterrupt:
|
15 |
+
log.info("Bot stopped by user")
|
proxies.txt
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
requirements.txt
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
pydantic-settings==2.3.4
|
2 |
+
aiohttp==3.10.2
|
3 |
+
better-proxy==1.2.0
|
4 |
+
loguru==0.7.2
|
5 |
+
Pyrogram==2.0.106
|
6 |
+
TgCrypto==1.2.5
|
7 |
+
aiocache==0.12.2
|
8 |
+
pytz==2024.1
|
9 |
+
fake-useragent==1.5.1
|
10 |
+
aiohttp-socks==0.9.0
|
11 |
+
aiohttp-proxy==0.1.2
|
sessions/yasir.session
ADDED
Binary file (28.7 kB). View file
|
|
youtube.json
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"From Queens to billionaire: the real story of Donald Trump. Controversy, power, wealth, and success": 2007,
|
3 |
+
"How сloud storage works? Episode 28": 75928,
|
4 |
+
"How biomimicry is changing the World. Episode 27": 35527,
|
5 |
+
"The Bezos phenomenon. The billionaire's path from the garage to space.": 0,
|
6 |
+
"How will AI change the world? Episode 29": 34329,
|
7 |
+
"What does a CEO really do? Episode 32": 12932,
|
8 |
+
"Marketing magic: turning Ordinary into Extraordinary. Episode 35": 15235,
|
9 |
+
"The secrets of multicurrency transfers. Episode 38": 22938,
|
10 |
+
"Who’s behind your crypto transfers? Episode 39": 44139,
|
11 |
+
"Check out the guide on how to connect your wallet and complete a test transaction. Once you've reviewed it, connect your wallet to participate in the Airdrop!": 3,
|
12 |
+
"How Sam Altman Is changing the AI world and what’s behind his popularity?": 2023,
|
13 |
+
"Bioengineering: medical miracles of tomorrow. Episode 37": 72837,
|
14 |
+
"Turning Creativity Into Cash: Intellectual Property Valuation. Episode 40": 65340,
|
15 |
+
"Corporate Anthropology: the art of Improving team work. Episode 41": 10941,
|
16 |
+
"The truth about Inflation. Episode 42": 83242,
|
17 |
+
"Mining secrets: How digital currency Is really made? Episode 43": 87843,
|
18 |
+
"Broker secrets: how they make your money work? Episode 44": 62944,
|
19 |
+
"From dorm room to tech Icon: Zuckerberg's story!": 2011,
|
20 |
+
"Why personal branding is the key to success? Episode 47": 24147,
|
21 |
+
"The power of strategic thinking: Think long-term, win big! Episode 50": 89350,
|
22 |
+
"Steve Jobs: Innovator, leader, enigma. The truth about the man behind Apple!": 1989,
|
23 |
+
"The Hidden Power and Harm of Monopolies. Episode 51": 37251,
|
24 |
+
"Reshoring: why Global businesses are coming home? Episode 49": 52649
|
25 |
+
}
|