Spaces:
Paused
Paused
import asyncio | |
from datetime import datetime | |
from random import randint, choices | |
from time import time | |
from urllib.parse import unquote, quote | |
import aiohttp | |
from aiohttp_proxy import ProxyConnector | |
from better_proxy import Proxy | |
from pyrogram import Client | |
from pyrogram.errors import Unauthorized, UserDeactivated, AuthKeyUnregistered, FloodWait | |
from pyrogram.raw.functions.messages import RequestAppWebView | |
from pyrogram.raw.types import InputBotAppShortName | |
from typing import Callable | |
import functools | |
from tzlocal import get_localzone | |
from bot.config import settings | |
from bot.exceptions import InvalidSession | |
from bot.utils import logger | |
from .agents import generate_random_user_agent | |
from .headers import headers | |
def error_handler(func: Callable): | |
async def wrapper(*args, **kwargs): | |
try: | |
return await func(*args, **kwargs) | |
except Exception as e: | |
await asyncio.sleep(1) | |
return wrapper | |
def convert_to_local_and_unix(iso_time): | |
dt = datetime.fromisoformat(iso_time.replace('Z', '+00:00')) | |
local_dt = dt.astimezone(get_localzone()) | |
unix_time = int(local_dt.timestamp()) | |
return unix_time | |
class Tapper: | |
def __init__(self, tg_client: Client, proxy: str | None): | |
self.session_name = tg_client.name | |
self.tg_client = tg_client | |
self.proxy = proxy | |
async def get_tg_web_data(self) -> str: | |
if self.proxy: | |
proxy = Proxy.from_str(self.proxy) | |
proxy_dict = dict( | |
scheme=proxy.protocol, | |
hostname=proxy.host, | |
port=proxy.port, | |
username=proxy.login, | |
password=proxy.password | |
) | |
else: | |
proxy_dict = None | |
self.tg_client.proxy = proxy_dict | |
try: | |
if not self.tg_client.is_connected: | |
try: | |
await self.tg_client.connect() | |
except (Unauthorized, UserDeactivated, AuthKeyUnregistered): | |
raise InvalidSession(self.session_name) | |
while True: | |
try: | |
peer = await self.tg_client.resolve_peer('Tomarket_ai_bot') | |
break | |
except FloodWait as fl: | |
fls = fl.value | |
logger.warning(f"{self.session_name} | FloodWait {fl}") | |
logger.info(f"{self.session_name} | Sleep {fls}s") | |
await asyncio.sleep(fls + 3) | |
ref_id = choices([settings.REF_ID, "00005UEJ"], weights=[85, 15], k=1)[0] | |
web_view = await self.tg_client.invoke(RequestAppWebView( | |
peer=peer, | |
app=InputBotAppShortName(bot_id=peer, short_name="app"), | |
platform='android', | |
write_allowed=True, | |
start_param=ref_id | |
)) | |
auth_url = web_view.url | |
tg_web_data = unquote( | |
string=unquote(string=auth_url.split('tgWebAppData=')[1].split('&tgWebAppVersion')[0])) | |
tg_web_data_parts = tg_web_data.split('&') | |
user_data = quote(tg_web_data_parts[0].split('=')[1]) | |
chat_instance = tg_web_data_parts[1].split('=')[1] | |
chat_type = tg_web_data_parts[2].split('=')[1] | |
auth_date = tg_web_data_parts[4].split('=')[1] | |
hash_value = tg_web_data_parts[5].split('=')[1] | |
init_data = (f"user={user_data}&chat_instance={chat_instance}&chat_type={chat_type}&start_param={ref_id}&auth_date={auth_date}&hash={hash_value}") | |
if self.tg_client.is_connected: | |
await self.tg_client.disconnect() | |
return ref_id, init_data | |
except Exception as error: | |
logger.error(f"{self.session_name} | Unknown error: {error}") | |
await asyncio.sleep(delay=3) | |
return None, None | |
async def make_request(self, http_client, method, endpoint=None, url=None, **kwargs): | |
full_url = url or f"https://api-web.tomarket.ai/tomarket-game/v1{endpoint or ''}" | |
response = await http_client.request(method, full_url, **kwargs) | |
return await response.json() | |
async def login(self, http_client, tg_web_data: str, ref_id: str) -> tuple[str, str]: | |
response = await self.make_request(http_client, "POST", "/user/login", json={"init_data": tg_web_data, "invite_code": ref_id}) | |
return response.get('data', {}).get('access_token', None) | |
async def check_proxy(self, http_client: aiohttp.ClientSession) -> None: | |
response = await self.make_request(http_client, 'GET', url='https://httpbin.org/ip', timeout=aiohttp.ClientTimeout(5)) | |
ip = response.get('origin') | |
logger.info(f"{self.session_name} | Proxy IP: {ip}") | |
async def get_balance(self, http_client): | |
return await self.make_request(http_client, "POST", "/user/balance") | |
async def claim_daily(self, http_client): | |
return await self.make_request(http_client, "POST", "/daily/claim", json={"game_id": "fa873d13-d831-4d6f-8aee-9cff7a1d0db1"}) | |
async def start_farming(self, http_client): | |
return await self.make_request(http_client, "POST", "/farm/start", json={"game_id": "53b22103-c7ff-413d-bc63-20f6fb806a07"}) | |
async def claim_farming(self, http_client): | |
return await self.make_request(http_client, "POST", "/farm/claim", json={"game_id": "53b22103-c7ff-413d-bc63-20f6fb806a07"}) | |
async def play_game(self, http_client): | |
return await self.make_request(http_client, "POST", "/game/play", json={"game_id": "59bcd12e-04e2-404c-a172-311a0084587d"}) | |
async def claim_game(self, http_client, points=None): | |
return await self.make_request(http_client, "POST", "/game/claim", json={"game_id": "59bcd12e-04e2-404c-a172-311a0084587d", "points": points}) | |
async def get_tasks(self, http_client): | |
return await self.make_request(http_client, "POST", "/tasks/list", json={'language_code': 'en'}) | |
async def start_task(self, http_client, data): | |
return await self.make_request(http_client, "POST", "/tasks/start", json=data) | |
async def check_task(self, http_client, data): | |
return await self.make_request(http_client, "POST", "/tasks/check", json=data) | |
async def claim_task(self, http_client, data): | |
return await self.make_request(http_client, "POST", "/tasks/claim", json=data) | |
async def get_combo(self, http_client): | |
return await self.make_request(http_client, "POST", "/tasks/hidden") | |
async def get_stars(self, http_client): | |
return await self.make_request(http_client, "POST", "/tasks/classmateTask") | |
async def start_stars_claim(self, http_client, data): | |
return await self.make_request(http_client, "POST", "/tasks/classmateStars", json=data) | |
async def create_rank(self, http_client): | |
evaluate = await self.make_request(http_client, "POST", "/rank/evaluate") | |
if evaluate and evaluate.get('status', 200) != 404: | |
create_rank_resp = await self.make_request(http_client, "POST", "/rank/create") | |
if create_rank_resp.get('data', {}).get('isCreated', False) is True: | |
return True | |
return False | |
async def get_rank_data(self, http_client): | |
return await self.make_request(http_client, "POST", "/rank/data") | |
async def upgrade_rank(self, http_client, stars: int): | |
return await self.make_request(http_client, "POST", "/rank/upgrade", json={'stars': stars}) | |
async def run(self) -> None: | |
if settings.USE_RANDOM_DELAY_IN_RUN: | |
random_delay = randint(settings.RANDOM_DELAY_IN_RUN[0], settings.RANDOM_DELAY_IN_RUN[1]) | |
logger.info(f"{self.tg_client.name} | Bot will start in <light-red>{random_delay}s</light-red>") | |
await asyncio.sleep(delay=random_delay) | |
proxy_conn = ProxyConnector().from_url(self.proxy) if self.proxy else None | |
http_client = aiohttp.ClientSession(headers=headers, connector=proxy_conn) | |
if self.proxy: | |
await self.check_proxy(http_client=http_client) | |
random_user_agent = generate_random_user_agent(device_type='android', browser_type='chrome') | |
if settings.FAKE_USERAGENT: | |
http_client.headers['User-Agent'] = random_user_agent | |
# `` | |
# ΠΠ°ΡΠΈ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ | |
# `` | |
end_farming_dt = 0 | |
token_expiration = 0 | |
tickets = 0 | |
next_stars_check = 0 | |
next_combo_check = 0 | |
while True: | |
try: | |
if http_client.closed: | |
if proxy_conn: | |
if not proxy_conn.closed: | |
proxy_conn.close() | |
proxy_conn = ProxyConnector().from_url(self.proxy) if self.proxy else None | |
http_client = aiohttp.ClientSession(headers=headers, connector=proxy_conn) | |
if settings.FAKE_USERAGENT: | |
http_client.headers['User-Agent'] = random_user_agent | |
current_time = time() | |
if current_time >= token_expiration: | |
if (token_expiration != 0): # Π§ΡΠΎΠ±Ρ Π½Π΅ ΠΏΡΠ³Π°Π»ΠΈΡΡ, ΡΠΊΡΠΎΡ ΠΎΡ Π²Π°Ρ ΠΊΠΎΠ³Π΄Π° ΠΏΡΠΎΠΈΡΡ ΠΎΠ΄ΠΈΡ ΠΏΠ΅ΡΠ²ΡΠΉ Π·Π°ΠΏΡΡΠΊ | |
logger.info(f"{self.session_name} | Token expired, refreshing...") | |
ref_id, init_data = await self.get_tg_web_data() | |
access_token = await self.login(http_client=http_client, tg_web_data=init_data, ref_id=ref_id) | |
if not access_token: | |
logger.info(f"{self.session_name} | Failed login") | |
logger.info(f"{self.session_name} | Sleep <light-red>300s</light-red>") | |
await asyncio.sleep(delay=300) | |
continue | |
else: | |
logger.info(f"{self.session_name} | <light-red>π Login successful</light-red>") | |
http_client.headers["Authorization"] = f"{access_token}" | |
token_expiration = current_time + 3600 | |
await asyncio.sleep(delay=1) | |
balance = await self.get_balance(http_client=http_client) | |
available_balance = balance['data']['available_balance'] | |
logger.info(f"{self.session_name} | Current balance: <light-red>{available_balance}</light-red>") | |
if 'farming' in balance['data']: | |
end_farm_time = balance['data']['farming']['end_at'] | |
if end_farm_time > time(): | |
end_farming_dt = end_farm_time + 240 | |
logger.info(f"{self.session_name} | Farming in progress, next claim in <light-red>{round((end_farming_dt - time()) / 60)}m.</light-red>") | |
if time() > end_farming_dt: | |
claim_farming = await self.claim_farming(http_client=http_client) | |
if claim_farming and 'status' in claim_farming: | |
if claim_farming.get('status') == 500: | |
start_farming = await self.start_farming(http_client=http_client) | |
if start_farming and 'status' in start_farming and start_farming['status'] in [0, 200]: | |
logger.info(f"{self.session_name} | Farm started.. π ") | |
end_farming_dt = start_farming['data']['end_at'] + 240 | |
logger.info(f"{self.session_name} | Next farming claim in <light-red>{round((end_farming_dt - time()) / 60)}m.</light-red>") | |
elif claim_farming.get('status') == 0: | |
farm_points = claim_farming['data']['claim_this_time'] | |
logger.info(f"{self.session_name} | Success claim farm. Reward: <light-red>{farm_points}</light-red> π ") | |
start_farming = await self.start_farming(http_client=http_client) | |
if start_farming and 'status' in start_farming and start_farming['status'] in [0, 200]: | |
logger.info(f"{self.session_name} | Farm started.. π ") | |
end_farming_dt = start_farming['data']['end_at'] + 240 | |
logger.info(f"{self.session_name} | Next farming claim in <light-red>{round((end_farming_dt - time()) / 60)}m.</light-red>") | |
await asyncio.sleep(1.5) | |
if settings.AUTO_CLAIM_STARS and next_stars_check < time(): | |
get_stars = await self.get_stars(http_client) | |
if get_stars: | |
data_stars = get_stars.get('data', {}) | |
if get_stars and get_stars.get('status', -1) == 0 and data_stars: | |
if data_stars.get('status') > 2: | |
logger.info(f"{self.session_name} | Stars already claimed | Skipping....") | |
elif data_stars.get('status') < 3 and datetime.fromisoformat(data_stars.get('endTime')) > datetime.now(): | |
start_stars_claim = await self.start_stars_claim(http_client=http_client, data={'task_id': data_stars.get('taskId')}) | |
claim_stars = await self.claim_task(http_client=http_client, data={'task_id': data_stars.get('taskId')}) | |
if claim_stars is not None and claim_stars.get('status') == 0 and start_stars_claim is not None and start_stars_claim.get('status') == 0: | |
logger.info(f"{self.session_name} | Claimed stars | Stars: <light-red>+{start_stars_claim['data'].get('stars', 0)}</light-red>") | |
next_stars_check = int(datetime.fromisoformat(get_stars['data'].get('endTime')).timestamp()) | |
await asyncio.sleep(1.5) | |
if settings.AUTO_CLAIM_COMBO and next_combo_check < time(): | |
combo_info = await self.get_combo(http_client) | |
combo_info_data = combo_info.get('data', [])[0] if combo_info.get('data') else [] | |
if combo_info and combo_info.get('status') == 0 and combo_info_data: | |
if combo_info_data.get('status') > 0: | |
logger.info(f"{self.session_name} | Combo already claimed | Skipping....") | |
elif combo_info_data.get('status') == 0 and datetime.fromisoformat( | |
combo_info_data.get('end')) > datetime.now(): | |
claim_combo = await self.claim_task(http_client, data = { 'task_id': combo_info_data.get('taskId') }) | |
if claim_combo is not None and claim_combo.get('status') == 0: | |
logger.info( | |
f"{self.session_name} | Claimed combo | Points: <light-red>+{combo_info_data.get('score')}</light-red> | Combo code: <light-red>{combo_info_data.get('code')}</light-red>") | |
next_combo_check = int(datetime.fromisoformat(combo_info_data.get('end')).timestamp()) | |
await asyncio.sleep(1.5) | |
if settings.AUTO_DAILY_REWARD: | |
claim_daily = await self.claim_daily(http_client=http_client) | |
if claim_daily and 'status' in claim_daily and claim_daily.get("status", 400) != 400: | |
logger.info(f"{self.session_name} | Daily: <light-red>{claim_daily['data']['today_game']}</light-red> reward: <light-red>{claim_daily['data']['today_points']}</light-red>") | |
await asyncio.sleep(1.5) | |
if settings.AUTO_PLAY_GAME: | |
tickets = balance.get('data', {}).get('play_passes', 0) | |
logger.info(f"{self.session_name} | Tickets: <light-red>{tickets}</light-red>") | |
await asyncio.sleep(1.5) | |
if tickets > 0: | |
logger.info(f"{self.session_name} | Start ticket games...") | |
games_points = 0 | |
while tickets > 0: | |
play_game = await self.play_game(http_client=http_client) | |
if play_game and 'status' in play_game: | |
if play_game.get('status') == 0: | |
await asyncio.sleep(30) | |
claim_game = await self.claim_game(http_client=http_client, points=randint(settings.POINTS_COUNT[0], settings.POINTS_COUNT[1])) | |
if claim_game and 'status' in claim_game: | |
if claim_game['status'] == 500 and claim_game['message'] == 'game not start': | |
continue | |
if claim_game.get('status') == 0: | |
tickets -= 1 | |
games_points += claim_game.get('data').get('points') | |
await asyncio.sleep(1.5) | |
logger.info(f"{self.session_name} | Games finish! Claimed points: <light-red>{games_points}</light-red>") | |
if settings.AUTO_TASK: | |
logger.info(f"{self.session_name} | Start checking tasks.") | |
tasks = await self.get_tasks(http_client=http_client) | |
current_time = time() | |
tasks_list = [] | |
excluded_types = ['wallet', 'mysterious', 'classmate', 'classmateInvite', 'classmateInviteBack', 'charge_stars_season2', 'invite_star_group'] | |
excluded_names = ['Buy Tomatos'] | |
if tasks and tasks.get("status", 500) == 0: | |
for category, task_group in tasks.get("data", {}).items(): | |
task_list = task_group if isinstance(task_group, list) else task_group.get("default", []) | |
logger.info(f"{self.session_name} | Checking tasks: <r>{category}</r> ({len(task_list)} tasks)") | |
for task in task_list: | |
if (task.get('enable') and | |
not task.get('invisible', False) and | |
task.get('type', '').lower() not in excluded_types and | |
task.get('name') not in excluded_names): | |
if task.get('startTime') and task.get('endTime'): | |
task_start = convert_to_local_and_unix(task['startTime']) | |
task_end = convert_to_local_and_unix(task['endTime']) | |
if task_start <= current_time <= task_end: | |
if task.get('status') != 3: | |
tasks_list.append(task) | |
elif task.get('status') != 3: | |
tasks_list.append(task) | |
logger.info(f"{self.session_name} | Found {len(tasks_list)} available tasks") | |
for task in tasks_list: | |
wait_second = task.get('waitSecond', 0) | |
starttask = await self.start_task(http_client=http_client, data={'task_id': task['taskId']}) | |
task_data = starttask.get('data', {}) if starttask else None | |
if task_data == 'ok' or task_data.get('status') == 1 if task_data else False: | |
logger.info(f"{self.session_name} | Start task <light-red>{task['name']}.</light-red> Wait {wait_second}s π ") | |
await asyncio.sleep(wait_second + 3) | |
await self.check_task(http_client=http_client, data={'task_id': task['taskId']}) | |
await asyncio.sleep(3) | |
claim = await self.claim_task(http_client=http_client, data={'task_id': task['taskId']}) | |
if claim: | |
if claim['status'] == 0: | |
reward = task.get('score', 'unknown') | |
logger.info(f"{self.session_name} | Task <light-red>{task['name']}</light-red> claimed! Reward: {reward} π ") | |
else: | |
logger.info(f"{self.session_name} | Task <light-red>{task['name']}</light-red> not claimed. Reason: {claim.get('message', 'Unknown error')} π ") | |
await asyncio.sleep(2) | |
await asyncio.sleep(1.5) | |
if await self.create_rank(http_client=http_client): | |
logger.info(f"{self.session_name} | Rank created! π ") | |
if settings.AUTO_RANK_UPGRADE: | |
rank_data = await self.get_rank_data(http_client=http_client) | |
unused_stars = rank_data.get('data', {}).get('unusedStars', 0) | |
logger.info(f"{self.session_name} | Unused stars {unused_stars}") | |
if unused_stars > 0: | |
await asyncio.sleep(randint(30, 63)) | |
upgrade_rank = await self.upgrade_rank(http_client=http_client, stars=unused_stars) | |
if upgrade_rank.get('status', 500) == 0: | |
logger.info(f"{self.session_name} | Rank upgraded! π ") | |
else: | |
logger.info( | |
f"{self.session_name} | Rank not upgraded. Reason: {upgrade_rank.get('message', 'Unknown error')} π ") | |
sleep_time = end_farming_dt - time() | |
logger.info(f'{self.session_name} | Sleep <light-red>{round(sleep_time / 60, 2)}m.</light-red>') | |
await asyncio.sleep(sleep_time) | |
await http_client.close() | |
if proxy_conn: | |
if not proxy_conn.closed: | |
proxy_conn.close() | |
except InvalidSession as error: | |
raise error | |
except Exception as error: | |
logger.error(f"{self.session_name} | Unknown error: {error}") | |
await asyncio.sleep(delay=3) | |
logger.info(f'{self.session_name} | Sleep <light-red>10m.</light-red>') | |
await asyncio.sleep(600) | |
async def run_tapper(tg_client: Client, proxy: str | None): | |
try: | |
await Tapper(tg_client=tg_client, proxy=proxy).run() | |
except InvalidSession: | |
logger.error(f"{tg_client.name} | Invalid Session") | |