├── database ├── __init__.py ├── sessions.db ├── on_startup.py └── actions.py ├── core ├── __init__.py ├── headers.py ├── create_sessions.py └── start_farming.py ├── utils ├── __init__.py ├── eval_js.py ├── monkeypatching.py └── read_session_json_file.py ├── exceptions └── __init__.py ├── data ├── proxies.txt └── config.py ├── requirements.txt ├── README.md └── main.py /database/__init__.py: -------------------------------------------------------------------------------- 1 | from .on_startup import on_startup_database 2 | -------------------------------------------------------------------------------- /database/sessions.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazavod777/notcoin_bot/HEAD/database/sessions.db -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- 1 | from .create_sessions import create_sessions 2 | from .start_farming import start_farming 3 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .eval_js import eval_js 2 | from .read_session_json_file import read_session_json_file 3 | -------------------------------------------------------------------------------- /exceptions/__init__.py: -------------------------------------------------------------------------------- 1 | class InvalidSession(BaseException): 2 | pass 3 | 4 | 5 | class TurboExpired(BaseException): 6 | pass 7 | -------------------------------------------------------------------------------- /data/proxies.txt: -------------------------------------------------------------------------------- 1 | type://user:pass@ip:port 2 | type://user:pass:ip:port 3 | type://ip:port:user:pass 4 | type://ip:port@user:pass 5 | type://ip:port 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp>=3.8.6 2 | TGConvertor>=0.0.7 3 | loguru>=0.7.2 4 | pyuseragents>=1.0.5 5 | Telethon>=1.32.1 6 | Pyrogram>=2.0.106 7 | aiosqlite>=0.17.0 8 | better-proxy>=0.2.2 9 | aiohttp-proxy>=0.1.2 10 | 11 | pythonmonkey>=0.2.3 12 | opentele>=1.15.1 13 | aiofiles>=23.2.1 14 | -------------------------------------------------------------------------------- /core/headers.py: -------------------------------------------------------------------------------- 1 | headers: dict = { 2 | 'accept': 'application/json', 3 | 'accept-language': 'ru,en-GB;q=0.9,en-US;q=0.8,en;q=0.7', 4 | 'auth': '1', 5 | 'content-type': 'application/json', 6 | 'origin': 'https://clicker.joincommunity.xyz', 7 | 'referer': 'https://clicker.joincommunity.xyz/' 8 | } 9 | -------------------------------------------------------------------------------- /database/on_startup.py: -------------------------------------------------------------------------------- 1 | import aiosqlite 2 | 3 | 4 | async def on_startup_database() -> None: 5 | async with aiosqlite.connect(database='database/sessions.db') as db: 6 | await db.execute(sql=""" 7 | CREATE TABLE IF NOT EXISTS sessions ( 8 | id INTEGER PRIMARY KEY, 9 | session_name TEXT, 10 | session_proxy TEXT 11 | ); 12 | """) 13 | await db.commit() 14 | -------------------------------------------------------------------------------- /utils/eval_js.py: -------------------------------------------------------------------------------- 1 | from pythonmonkey import eval as js_eval 2 | 3 | 4 | def eval_js(function: str) -> int | None: 5 | match function: 6 | case 'document.querySelectorAll(\'body\').length': 7 | return 1 8 | 9 | case 'window.location.host == \'clicker.joincommunity.xyz\' ? 129 : 578': 10 | return 129 11 | 12 | try: 13 | return int(js_eval(function)) 14 | 15 | except Exception: 16 | return None 17 | -------------------------------------------------------------------------------- /data/config.py: -------------------------------------------------------------------------------- 1 | API_ID: int = 6 2 | API_HASH: str = 'eb06d4abfb49dc3eeb1aeb98ae0f581e' 3 | MIN_CLICKS_COUNT: int = 1 4 | AUTO_BUY_ENERGY_BOOST: bool = False 5 | AUTO_BUY_SPEED_BOOST: bool = True 6 | AUTO_BUY_CLICK_BOOST: bool = True 7 | USE_PROXY_FROM_FILE: bool = True 8 | SLEEP_BETWEEN_CLICK: list[int] = [10, 15] 9 | SLEEP_BEFORE_BUY_MERGE: list[int] = [10, 15] 10 | SLEEP_BEFORE_ACTIVATE_FREE_BUFFS: list[int] = [10, 15] 11 | SLEEP_BEFORE_ACTIVATE_TURBO: list[int] = [10, 15] 12 | -------------------------------------------------------------------------------- /database/actions.py: -------------------------------------------------------------------------------- 1 | import aiosqlite 2 | 3 | 4 | async def add_session(session_name: str, 5 | session_proxy: str = '') -> None: 6 | async with aiosqlite.connect(database='database/sessions.db') as db: 7 | await db.execute(sql='INSERT INTO sessions (session_name, session_proxy) VALUES (?, ?)', 8 | parameters=(session_name, session_proxy)) 9 | await db.commit() 10 | 11 | 12 | async def get_session_proxy_by_name(session_name: str) -> str | None: 13 | async with aiosqlite.connect(database='database/sessions.db') as db: 14 | async with db.execute(sql='SELECT session_proxy FROM sessions WHERE session_name = ?', 15 | parameters=(session_name,)) as cursor: 16 | result = await cursor.fetchone() 17 | return result[0] if result else None 18 | -------------------------------------------------------------------------------- /utils/monkeypatching.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import aiosqlite 4 | from TGConvertor.manager.sessions.pyro import PyroSession 5 | 6 | 7 | async def new_validate(cls, path: Path) -> bool: 8 | try: 9 | async with aiosqlite.connect(path) as db: 10 | db.row_factory = aiosqlite.Row 11 | sql = "SELECT name FROM sqlite_master WHERE type='table'" 12 | async with db.execute(sql) as cursor: 13 | tables = {row["name"] for row in await cursor.fetchall()} 14 | 15 | if tables != set(cls.TABLES.keys()): 16 | return False 17 | 18 | for table, session_columns in cls.TABLES.items(): 19 | sql = f'pragma table_info("{table}")' 20 | async with db.execute(sql) as cur: 21 | columns = {row["name"] for row in await cur.fetchall()} 22 | if "api_id" in columns: 23 | columns.remove("api_id") 24 | if session_columns != columns: 25 | return False 26 | 27 | except aiosqlite.DatabaseError: 28 | return False 29 | 30 | return True 31 | 32 | 33 | PyroSession.validate = classmethod(new_validate) 34 | -------------------------------------------------------------------------------- /utils/read_session_json_file.py: -------------------------------------------------------------------------------- 1 | from json import loads 2 | 3 | import aiofiles 4 | from aiofiles.ospath import exists 5 | from loguru import logger 6 | 7 | 8 | def get_value(file_json: dict, 9 | *keys) -> str | None: 10 | for key in keys: 11 | if key in file_json: 12 | return file_json[key] 13 | return None 14 | 15 | 16 | async def read_session_json_file(session_name: str) -> dict: 17 | file_path: str = f'sessions/{session_name}.json' 18 | result_dict: dict = {} 19 | 20 | try: 21 | if not await exists(file_path): 22 | return result_dict 23 | 24 | async with aiofiles.open(file=file_path, 25 | mode='r', 26 | encoding='utf-8') as file: 27 | file_json: dict = loads(await file.read()) 28 | 29 | result_dict: dict = { 30 | 'api_id': get_value(file_json, 'api_id', 'app_id', 'apiId', 'appId'), 31 | 'api_hash': get_value(file_json, 'api_hash', 'app_hash', 'apiHash', 'appHash'), 32 | 'device_model': get_value(file_json, 'deviceModel', 'device'), 33 | 'system_version': get_value(file_json, 'systemVersion', 'system_version', 'appVersion', 'app_version'), 34 | 'app_version': get_value(file_json, 'appVersion', 'app_version'), 35 | 'lang_code': get_value(file_json, 'lang_pack', 'langCode', 'lang'), 36 | 'system_lang_code': get_value(file_json, 'system_lang_pack', 'systemLangCode', 'systemLangPack') 37 | } 38 | 39 | except Exception as error: 40 | logger.error(f'{session_name} | Ошибка при чтении .json файла: {error}') 41 | 42 | return result_dict 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Telegram channel](https://img.shields.io/endpoint?url=https://runkit.io/damiankrawczyk/telegram-badge/branches/master?url=https://t.me/n4z4v0d)](https://t.me/n4z4v0d) 2 | [![PyPI supported Python versions](https://img.shields.io/pypi/pyversions/better-automation.svg)](https://www.python.org/downloads/release/python-3116/) 3 | [![works badge](https://cdn.jsdelivr.net/gh/nikku/works-on-my-machine@v0.2.0/badge.svg)](https://github.com/nikku/works-on-my-machine) 4 | 5 | ![alt text](https://i.imgur.com/PDYwSJ9.png) 6 | 7 | ### Функционал 8 | + _Многопоточность_ 9 | + _Привязка прокси к сессии_ 10 | + _Авто-покупка предметов при наличии денег (enery boost, speed boost, click boost)_ 11 | + _Рандомное время сна между кликами_ 12 | + _Рандомное количество кликов за запрос_ 13 | + _Поддержка tdata / pyrogram .session / telethon .session_ 14 | 15 | ### data/config.py 16 | _**API_ID / API_HASH** - Данные платформы, с которой запускать сессию Telegram (сток - Android) 17 | **MIN_CLICKS_COUNT** - Минимальное количество кликов за один запрос (считается без множителя, т.е напр. при множителе x9: 1 клик будет равнятся 9 монетам, а не одной) 18 | **AUTO_BUY_ENERGY_BOOST** - Автоматическая покупка Energy Boost при достижении баланса (True / False) 19 | **AUTO_BUY_SPEED_BOOST** - Автоматическая покупка Speed Boost при достижении баланса (True / False) 20 | **AUTO_BUY_CLICK_BOOST** - Автоматическая покупка Click Boost при достижении баланса (True / False) 21 | **USE_PROXY_FROM_FILE** - Использовать-ли Proxy из файла data/proxies.txt для аккаунтов, к которм не привязаны Proxy (True / False) 22 | **SLEEP_BETWEEN_CLICK** - Диапазон задержки между кликами (в секундах) 23 | **SLEEP_BEFORE_BUY_MERGE** - Диапазон задержки перед покупкой бустов (в секундах) 24 | **SLEEP_BEFORE_ACTIVATE_FREE_BUFFS** - Диапазон задержки перед активацией ежедневных бустов (в секундах) 25 | **SLEEP_BEFORE_ACTIVATE_TURBO** - Диапазон задержки перед активацией Turbo (в секундах)_ 26 | 27 | # DONATE (_any evm_) - 0xDEADf12DE9A24b47Da0a43E1bA70B8972F5296F2 28 | # DONATE (_sol_) - 2Fw2wh1pN77ELg6sWnn5cZrTDCK5ibfnKymTuCXL8sPX 29 | -------------------------------------------------------------------------------- /core/create_sessions.py: -------------------------------------------------------------------------------- 1 | import pyrogram 2 | from better_proxy import Proxy 3 | from loguru import logger 4 | 5 | from data import config 6 | from database import actions as db_actions 7 | 8 | 9 | async def create_sessions() -> None: 10 | while True: 11 | session_name: str = input('\nВведите название сессии (для выхода нажмите Enter): ') 12 | 13 | if not session_name: 14 | return 15 | 16 | while True: 17 | proxy_str: str = input('Введите Proxy (type://user:pass@ip:port // type://ip:port, для использования без ' 18 | 'Proxy нажмите Enter): ').replace('https://', 'http://') 19 | 20 | if proxy_str: 21 | try: 22 | proxy: Proxy = Proxy.from_str( 23 | proxy=proxy_str 24 | ) 25 | 26 | proxy_dict: dict = { 27 | 'scheme': proxy.protocol, 28 | 'hostname': proxy.host, 29 | 'port': proxy.port, 30 | 'username': proxy.login, 31 | 'password': proxy.password 32 | } 33 | 34 | except ValueError: 35 | logger.error(f'Неверно указан Proxy, повторите попытку ввода') 36 | 37 | else: 38 | break 39 | 40 | else: 41 | proxy: None = None 42 | proxy_dict: None = None 43 | break 44 | 45 | session: pyrogram.Client = pyrogram.Client( 46 | api_id=config.API_ID, 47 | api_hash=config.API_HASH, 48 | name=session_name, 49 | workdir='sessions', 50 | proxy=proxy_dict 51 | ) 52 | 53 | async with session: 54 | user_data = await session.get_me() 55 | 56 | logger.success(f'Успешно добавлена сессия {user_data.username} | {user_data.first_name} {user_data.last_name}') 57 | 58 | await db_actions.add_session(session_name=session_name, 59 | session_proxy=proxy.as_url if proxy else '') 60 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from itertools import cycle 2 | from better_proxy import Proxy 3 | from os.path import isdir 4 | import asyncio 5 | from os import listdir 6 | from os import mkdir 7 | from os.path import exists 8 | from sys import stderr 9 | 10 | from loguru import logger 11 | 12 | from core import create_sessions, start_farming 13 | from database import on_startup_database 14 | from utils import monkeypatching 15 | 16 | logger.remove() 17 | logger.add(stderr, format='{time:HH:mm:ss}' 18 | ' | {level: <8}' 19 | ' | {line}' 20 | ' - {message}') 21 | 22 | 23 | async def main() -> None: 24 | await on_startup_database() 25 | 26 | match user_action: 27 | case 1: 28 | await create_sessions() 29 | 30 | logger.success('Сессии успешно добавлены') 31 | 32 | case 2: 33 | tasks: list = [ 34 | asyncio.create_task(coro=start_farming(session_name=current_session_name, 35 | proxy=next(proxies_cycled) if proxies_cycled else None)) 36 | for current_session_name in session_files 37 | ] 38 | 39 | await asyncio.gather(*tasks) 40 | 41 | case _: 42 | logger.error('Действие выбрано некорректно') 43 | 44 | 45 | if __name__ == '__main__': 46 | if not exists(path='sessions'): 47 | mkdir(path='sessions') 48 | 49 | with open(file='data/proxies.txt', 50 | mode='r', 51 | encoding='utf-8-sig') as file: 52 | proxies: list[str] = [Proxy.from_str(proxy=row.strip()).as_url for row in file] 53 | 54 | if proxies: 55 | proxies_cycled: cycle = cycle(proxies) 56 | 57 | else: 58 | proxies_cycled: None = None 59 | 60 | session_files: list[str] = [current_file[:-8] if current_file.endswith('.session') 61 | else current_file for current_file in listdir(path='sessions') 62 | if current_file.endswith('.session') or isdir(s=f'sessions/{current_file}')] 63 | 64 | logger.info(f'Обнаружено {len(session_files)} сессий / {len(proxies)} прокси') 65 | 66 | user_action: int = int(input('\n1. Создать сессию' 67 | '\n2. Запустить бота с существующих сессий' 68 | '\nВыберите ваше действие: ')) 69 | print() 70 | 71 | try: 72 | import uvloop 73 | 74 | uvloop.run(main()) 75 | 76 | except ModuleNotFoundError: 77 | asyncio.run(main()) 78 | 79 | input('\n\nPress Enter to Exit..') 80 | -------------------------------------------------------------------------------- /core/start_farming.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from base64 import b64decode 3 | from math import floor 4 | from pathlib import Path 5 | from random import randint 6 | from time import time 7 | from urllib.parse import unquote 8 | 9 | import aiohttp 10 | from TGConvertor.manager.exceptions import ValidationError 11 | from TGConvertor.manager.manager import SessionManager 12 | from aiohttp_proxy import ProxyConnector 13 | from better_proxy import Proxy 14 | from loguru import logger 15 | from opentele.exception import TFileNotFound, OpenTeleException 16 | from pyuseragents import random as random_useragent 17 | from telethon import TelegramClient 18 | from telethon import functions 19 | from telethon.sessions import StringSession 20 | 21 | from data import config 22 | from database import actions as db_actions 23 | from exceptions import InvalidSession, TurboExpired 24 | from utils import eval_js, read_session_json_file 25 | from .headers import headers 26 | 27 | 28 | class Farming: 29 | def __init__(self, 30 | session_name: str): 31 | self.session_name: str = session_name 32 | 33 | async def get_access_token(self, 34 | client: aiohttp.ClientSession, 35 | tg_web_data: str) -> str: 36 | r: None = None 37 | 38 | while True: 39 | try: 40 | r: aiohttp.ClientResponse = await client.post(url='https://clicker-api.joincommunity.xyz/auth/' 41 | 'webapp-session', 42 | json={ 43 | 'webAppData': tg_web_data 44 | }, 45 | verify_ssl=False) 46 | 47 | return (await r.json(content_type=None))['data']['accessToken'] 48 | 49 | except Exception as error: 50 | if r: 51 | logger.error(f'{self.session_name} | Неизвестная ошибка при получении Access Token: {error}, ' 52 | f'ответ: {await r.text()}') 53 | 54 | else: 55 | logger.error(f'{self.session_name} | Неизвестная ошибка при получении Access Token: {error}') 56 | 57 | async def get_tg_web_data(self, 58 | session_proxy: str | None) -> str | None: 59 | while True: 60 | try: 61 | if session_proxy: 62 | try: 63 | proxy: Proxy = Proxy.from_str( 64 | proxy=session_proxy 65 | ) 66 | 67 | proxy_dict: dict = { 68 | 'proxy_type': proxy.protocol, 69 | 'addr': proxy.host, 70 | 'port': proxy.port, 71 | 'username': proxy.login, 72 | 'password': proxy.password 73 | } 74 | 75 | except ValueError: 76 | proxy_dict: None = None 77 | 78 | else: 79 | proxy_dict: None = None 80 | 81 | session: any = None 82 | 83 | try: 84 | session = SessionManager.from_tdata_folder(folder=Path(f'sessions/{self.session_name}')) 85 | 86 | except (ValidationError, FileNotFoundError, TFileNotFound, OpenTeleException): 87 | pass 88 | 89 | if not session: 90 | for action in [SessionManager.from_pyrogram_file, SessionManager.from_telethon_file]: 91 | try: 92 | # noinspection PyArgumentList 93 | session = await action(file=Path(f'sessions/{self.session_name}.session')) 94 | 95 | except (ValidationError, FileNotFoundError, TFileNotFound, OpenTeleException): 96 | pass 97 | 98 | else: 99 | break 100 | 101 | if not session: 102 | raise InvalidSession(self.session_name) 103 | 104 | telethon_string: str = session.to_telethon_string() 105 | platform_data: dict = await read_session_json_file(session_name=self.session_name) 106 | 107 | client = TelegramClient(session=StringSession(string=telethon_string), 108 | api_id=platform_data.get('api_id', config.API_ID), 109 | api_hash=platform_data.get('api_hash', config.API_HASH), 110 | device_model=platform_data.get('device_model', None), 111 | system_version=platform_data.get('system_version', None), 112 | app_version=platform_data.get('app_version', None), 113 | lang_code=platform_data.get('lang_code', 'en'), 114 | system_lang_code=platform_data.get('system_lang_code', 'en'), 115 | proxy=proxy_dict) 116 | 117 | try: 118 | await client.connect() 119 | 120 | if not await client.is_user_authorized(): 121 | raise InvalidSession(self.session_name) 122 | 123 | except InvalidSession as error: 124 | raise error 125 | 126 | except Exception as error: 127 | raise error 128 | 129 | finally: 130 | await client.disconnect() 131 | 132 | async with TelegramClient(session=StringSession(string=telethon_string), 133 | api_id=platform_data.get('api_id', config.API_ID), 134 | api_hash=platform_data.get('api_hash', config.API_HASH), 135 | device_model=platform_data.get('device_model', None), 136 | system_version=platform_data.get('system_version', None), 137 | app_version=platform_data.get('app_version', None), 138 | lang_code=platform_data.get('lang_code', 'en'), 139 | system_lang_code=platform_data.get('system_lang_code', 'en'), 140 | proxy=proxy_dict) as client: 141 | await client.send_message(entity='notcoin_bot', 142 | message='/start r_577441_3319074') 143 | # noinspection PyTypeChecker 144 | result = await client(functions.messages.RequestWebViewRequest( 145 | peer='notcoin_bot', 146 | bot='notcoin_bot', 147 | platform='android', 148 | from_bot_menu=False, 149 | url='https://clicker.joincommunity.xyz/clicker', 150 | )) 151 | auth_url: str = result.url 152 | 153 | tg_web_data: str = unquote(string=unquote( 154 | string=auth_url.split(sep='tgWebAppData=', 155 | maxsplit=1)[1].split(sep='&tgWebAppVersion', 156 | maxsplit=1)[0] 157 | )) 158 | 159 | return tg_web_data 160 | 161 | except InvalidSession as error: 162 | raise error 163 | 164 | except Exception as error: 165 | logger.error(f'{self.session_name} | Неизвестная ошибка при авторизации: {error}') 166 | 167 | async def get_profile_data(self, 168 | client: aiohttp.ClientSession) -> dict: 169 | while True: 170 | try: 171 | r: aiohttp.ClientResponse = await client.get( 172 | url='https://clicker-api.joincommunity.xyz/clicker/profile', 173 | verify_ssl=False) 174 | 175 | if not (await r.json(content_type=None)).get('ok'): 176 | logger.error(f'{self.session_name} | Неизвестный ответ при получении данных профиля, ' 177 | f'ответ: {await r.text()}') 178 | continue 179 | 180 | return await r.json(content_type=None) 181 | 182 | except Exception as error: 183 | logger.error(f'{self.session_name} | Неизвестная ошибка при получении данных профиля: {error}') 184 | 185 | async def send_clicks(self, 186 | client: aiohttp.ClientSession, 187 | clicks_count: int, 188 | tg_web_data: str, 189 | balance: int, 190 | total_coins: str | int, 191 | click_hash: str | None = None, 192 | turbo: bool | None = None) -> tuple[int | None, str | None, bool | None]: 193 | while True: 194 | try: 195 | json_data: dict = { 196 | 'count': clicks_count, 197 | 'webAppData': tg_web_data 198 | } 199 | 200 | if click_hash: 201 | json_data['hash']: str = click_hash 202 | 203 | if turbo: 204 | json_data['turbo']: bool = True 205 | 206 | r: aiohttp.ClientResponse = await client.post( 207 | url='https://clicker-api.joincommunity.xyz/clicker/core/click', 208 | json=json_data, 209 | verify_ssl=False) 210 | 211 | if (await r.json(content_type=None)).get('data') \ 212 | and isinstance((await r.json(content_type=None))['data'], dict) \ 213 | and (await r.json(content_type=None))['data'].get('message', '') == 'Turbo mode is expired': 214 | raise TurboExpired() 215 | 216 | if (await r.json(content_type=None)).get('data') \ 217 | and isinstance((await r.json(content_type=None))['data'], dict) \ 218 | and (await r.json(content_type=None))['data'].get('message', '') == 'Try later': 219 | await asyncio.sleep(delay=1) 220 | continue 221 | 222 | if (await r.json(content_type=None)).get('ok'): 223 | logger.success(f'{self.session_name} | Успешно сделал Click | Balance: ' 224 | f'{balance + clicks_count} (+{clicks_count}) | Total Coins: {total_coins}') 225 | 226 | next_hash: str | None = eval_js( 227 | function=b64decode(s=(await r.json())['data'][0]['hash'][0]).decode()) 228 | 229 | return balance + clicks_count, next_hash, (await r.json())['data'][0]['turboTimes'] > 0 230 | 231 | logger.error(f'{self.session_name} | Не удалось сделать Click, ответ: {await r.text()}') 232 | return None, None, None 233 | 234 | except Exception as error: 235 | logger.error(f'{self.session_name} | Неизвестная ошибка при попытке сделать Click: {error}') 236 | 237 | async def get_merged_list(self, 238 | client: aiohttp.ClientSession) -> dict | None: 239 | r: None = None 240 | 241 | try: 242 | r: aiohttp.ClientResponse = await client.get( 243 | url='https://clicker-api.joincommunity.xyz/clicker/store/merged') 244 | 245 | if (await r.json(content_type=None)).get('ok'): 246 | return await r.json(content_type=None) 247 | 248 | logger.error(f'{self.session_name} | Не удалось получить список товаров, ответ: {await r.text()}') 249 | 250 | return 251 | 252 | except Exception as error: 253 | if r: 254 | logger.error(f'{self.session_name} | Неизвестная ошибка при получении списка товаров: {error}, ' 255 | f'ответ: {await r.text()}') 256 | 257 | else: 258 | logger.error(f'{self.session_name} | Неизвестная ошибка при получении списка товаров: {error}') 259 | 260 | async def buy_item(self, 261 | client: aiohttp.ClientSession, 262 | item_id: int | str) -> bool: 263 | r: None = None 264 | 265 | try: 266 | r: aiohttp.ClientResponse = await client.post(url=f'https://clicker-api.joincommunity.xyz/clicker/store/' 267 | f'buy/{item_id}', 268 | headers={ 269 | 'accept-language': 'ru-RU,ru;q=0.9', 270 | }, 271 | json=False) 272 | 273 | if (await r.json(content_type=None)).get('ok'): 274 | return True 275 | 276 | logger.error(f'{self.session_name} | Неизвестный ответ при покупке в магазине: {await r.text()}') 277 | 278 | return False 279 | 280 | except Exception as error: 281 | if r: 282 | logger.error(f'{self.session_name} | Неизвестная ошибка при покупке в магазине: {error}, ' 283 | f'ответ: {await r.text()}') 284 | 285 | else: 286 | logger.error(f'{self.session_name} | Неизвестная ошибка при покупке в магазине: {error}') 287 | 288 | return False 289 | 290 | async def activate_turbo(self, 291 | client: aiohttp.ClientSession) -> int | None: 292 | r: None = None 293 | 294 | try: 295 | r: aiohttp.ClientResponse = await client.post(url=f'https://clicker-api.joincommunity.xyz/clicker/core/' 296 | 'active-turbo', 297 | headers={ 298 | 'accept-language': 'ru-RU,ru;q=0.9', 299 | }, 300 | json=False) 301 | 302 | return (await r.json(content_type=None))['data'][0].get('multiple', 1) 303 | 304 | except Exception as error: 305 | if r: 306 | logger.error(f'{self.session_name} | Неизвестная ошибка при активации Turbo: {error}, ' 307 | f'ответ: {await r.text()}') 308 | 309 | else: 310 | logger.error(f'{self.session_name} | Неизвестная ошибка при активации Turbo: {error}') 311 | 312 | return 313 | 314 | async def activate_task(self, 315 | client: aiohttp.ClientSession, 316 | task_id: int | str) -> bool | None: 317 | r: None = None 318 | 319 | try: 320 | r: aiohttp.ClientResponse = await client.post(url=f'https://clicker-api.joincommunity.xyz/clicker/task/' 321 | f'{task_id}', 322 | headers={ 323 | 'accept-language': 'ru-RU,ru;q=0.9', 324 | }, 325 | json=False) 326 | 327 | if (await r.json(content_type=None)).get('ok'): 328 | return True 329 | 330 | logger.error(f'{self.session_name} | Неизвестный ответ при активации Task {task_id}: {await r.text()}') 331 | 332 | return False 333 | 334 | except Exception as error: 335 | if r: 336 | logger.error(f'{self.session_name} | Неизвестная ошибка при активации Task {task_id}: {error}, ' 337 | f'ответ: {await r.text()}') 338 | 339 | else: 340 | logger.error(f'{self.session_name} | Неизвестная ошибка при активации Task {task_id}: {error}') 341 | 342 | return False 343 | 344 | async def get_free_buffs_data(self, 345 | client: aiohttp.ClientSession) -> tuple[bool, bool]: 346 | r: None = None 347 | max_turbo_times: int = 3 348 | max_full_energy_times: int = 3 349 | 350 | turbo_times_count: int = 0 351 | full_energy_times_count: int = 0 352 | 353 | try: 354 | r: aiohttp.ClientResponse = await client.get(url=f'https://clicker-api.joincommunity.xyz/clicker/task/' 355 | 'combine-completed') 356 | 357 | for current_buff in (await r.json(content_type=None))['data']: 358 | match current_buff['taskId']: 359 | case 3: 360 | max_turbo_times: int = current_buff['task']['max'] 361 | 362 | if current_buff['task']['status'] == 'active': 363 | turbo_times_count += 1 364 | 365 | case 2: 366 | max_full_energy_times: int = current_buff['task']['max'] 367 | 368 | if current_buff['task']['status'] == 'active': 369 | full_energy_times_count += 1 370 | 371 | return max_turbo_times > turbo_times_count, max_full_energy_times > full_energy_times_count 372 | 373 | except Exception as error: 374 | if r: 375 | logger.error(f'{self.session_name} | Неизвестная ошибка при получении статуса бесплатных баффов: ' 376 | f'{error}, ответ: {await r.text()}') 377 | 378 | else: 379 | logger.error(f'{self.session_name} | Неизвестная ошибка при получении статуса бесплатных баффов: ' 380 | f'{error}') 381 | 382 | return False, False 383 | 384 | async def start_farming(self, 385 | proxy: str | None = None): 386 | session_proxy: str = await db_actions.get_session_proxy_by_name(session_name=self.session_name) 387 | 388 | if not session_proxy and config.USE_PROXY_FROM_FILE: 389 | session_proxy: str = proxy 390 | 391 | access_token_created_time: float = 0 392 | click_hash: None | str = None 393 | active_turbo: bool = False 394 | turbo_multiplier: int = 1 395 | 396 | while True: 397 | try: 398 | async with aiohttp.ClientSession( 399 | connector=ProxyConnector.from_url(url=session_proxy) if session_proxy else None, 400 | headers={ 401 | **headers, 402 | 'user-agent': random_useragent() 403 | }) as client: 404 | while True: 405 | try: 406 | if time() - access_token_created_time >= 1800: 407 | tg_web_data: str = await self.get_tg_web_data(session_proxy=session_proxy) 408 | 409 | access_token: str = await self.get_access_token(client=client, 410 | tg_web_data=tg_web_data) 411 | client.headers['Authorization']: str = f'Bearer {access_token}' 412 | access_token_created_time: float = time() 413 | 414 | profile_data: dict = await self.get_profile_data(client=client) 415 | 416 | if not active_turbo: 417 | if config.MIN_CLICKS_COUNT > floor(profile_data['data'][0]['availableCoins'] \ 418 | / profile_data['data'][0]['multipleClicks']): 419 | logger.info(f'{self.session_name} | Недостаточно монет для клика') 420 | continue 421 | 422 | if floor(profile_data['data'][0]['availableCoins'] \ 423 | / profile_data['data'][0]['multipleClicks']) < 160: 424 | max_clicks_count: int = floor(profile_data['data'][0]['availableCoins'] \ 425 | / profile_data['data'][0]['multipleClicks']) 426 | 427 | else: 428 | max_clicks_count: int = 160 429 | 430 | clicks_count: int = randint(a=config.MIN_CLICKS_COUNT, 431 | b=max_clicks_count) \ 432 | * profile_data['data'][0]['multipleClicks'] * turbo_multiplier 433 | 434 | try: 435 | new_balance, click_hash, have_turbo = await self.send_clicks(client=client, 436 | clicks_count=clicks_count, 437 | tg_web_data=tg_web_data, 438 | balance= 439 | profile_data['data'][0][ 440 | 'balanceCoins'], 441 | total_coins= 442 | profile_data['data'][0][ 443 | 'totalCoins'], 444 | click_hash=click_hash, 445 | turbo=active_turbo) 446 | 447 | except TurboExpired: 448 | active_turbo: bool = False 449 | turbo_multiplier: int = 1 450 | continue 451 | 452 | if have_turbo: 453 | random_sleep_time: int = randint(a=config.SLEEP_BEFORE_ACTIVATE_TURBO[0], 454 | b=config.SLEEP_BEFORE_ACTIVATE_TURBO[1]) 455 | 456 | logger.info(f'{self.session_name} | Сплю {random_sleep_time} перед активацией ' 457 | f'Turbo') 458 | 459 | await asyncio.sleep(delay=random_sleep_time) 460 | 461 | turbo_multiplier: int | None = await self.activate_turbo(client=client) 462 | 463 | if turbo_multiplier: 464 | logger.success(f'{self.session_name} | Успешно активировал Turbo: ' 465 | f'x{turbo_multiplier}') 466 | active_turbo: bool = True 467 | continue 468 | 469 | else: 470 | turbo_multiplier: int = 1 471 | 472 | if new_balance: 473 | merged_data: dict | None = await self.get_merged_list(client=client) 474 | 475 | if merged_data: 476 | for current_merge in merged_data['data']: 477 | match current_merge['id']: 478 | case 1: 479 | if not config.AUTO_BUY_ENERGY_BOOST: 480 | continue 481 | 482 | energy_price: int | None = current_merge['price'] 483 | 484 | if new_balance >= energy_price \ 485 | and current_merge['max'] > current_merge['count']: 486 | sleep_before_buy_merge: int = randint( 487 | a=config.SLEEP_BEFORE_BUY_MERGE[0], 488 | b=config.SLEEP_BEFORE_BUY_MERGE[1] 489 | ) 490 | logger.info(f'{self.session_name} | Сплю {sleep_before_buy_merge} ' 491 | f'сек. перед покупкой Energy Boost') 492 | 493 | await asyncio.sleep(delay=sleep_before_buy_merge) 494 | 495 | if await self.buy_item(client=client, 496 | item_id=1): 497 | logger.success(f'{self.session_name} | Успешно купил Energy ' 498 | 'Boost') 499 | continue 500 | 501 | case 2: 502 | if not config.AUTO_BUY_SPEED_BOOST: 503 | continue 504 | 505 | speed_price: int | None = current_merge['price'] 506 | 507 | if new_balance >= speed_price \ 508 | and current_merge['max'] > current_merge['count']: 509 | sleep_before_buy_merge: int = randint( 510 | a=config.SLEEP_BEFORE_BUY_MERGE[0], 511 | b=config.SLEEP_BEFORE_BUY_MERGE[1] 512 | ) 513 | logger.info(f'{self.session_name} | Сплю {sleep_before_buy_merge} ' 514 | 'сек. перед покупкой Speed Boost') 515 | 516 | await asyncio.sleep(delay=sleep_before_buy_merge) 517 | 518 | if await self.buy_item(client=client, 519 | item_id=2): 520 | logger.success( 521 | f'{self.session_name} | Успешно купил Speed Boost') 522 | continue 523 | 524 | case 3: 525 | if not config.AUTO_BUY_CLICK_BOOST: 526 | continue 527 | 528 | click_price: int | None = current_merge['price'] 529 | 530 | if new_balance >= click_price \ 531 | and current_merge['max'] > current_merge['count']: 532 | sleep_before_buy_merge: int = randint( 533 | a=config.SLEEP_BEFORE_BUY_MERGE[0], 534 | b=config.SLEEP_BEFORE_BUY_MERGE[1]) 535 | logger.info( 536 | f'{self.session_name} | Сплю {sleep_before_buy_merge} сек. ' 537 | f'перед покупкой Speed Boost') 538 | 539 | await asyncio.sleep(delay=sleep_before_buy_merge) 540 | 541 | if await self.buy_item(client=client, 542 | item_id=3): 543 | logger.success( 544 | f'{self.session_name} | Успешно купил Click Boost') 545 | continue 546 | 547 | case 4: 548 | pass 549 | 550 | free_daily_turbo, free_daily_full_energy = await self.get_free_buffs_data(client=client) 551 | 552 | if free_daily_turbo: 553 | random_sleep_time: int = randint(a=config.SLEEP_BEFORE_ACTIVATE_FREE_BUFFS[0], 554 | b=config.SLEEP_BEFORE_ACTIVATE_FREE_BUFFS[1]) 555 | 556 | logger.info(f'{self.session_name} | Сплю {random_sleep_time} перед запросом ' 557 | f'ежедневного Turbo') 558 | 559 | await asyncio.sleep(delay=random_sleep_time) 560 | 561 | if await self.activate_task(client=client, 562 | task_id=3): 563 | logger.success(f'{self.session_name} | Успешно запросил ежедневное Turbo') 564 | 565 | random_sleep_time: int = randint(a=config.SLEEP_BEFORE_ACTIVATE_TURBO[0], 566 | b=config.SLEEP_BEFORE_ACTIVATE_TURBO[1]) 567 | 568 | logger.info(f'{self.session_name} | Сплю {random_sleep_time} перед активацией ' 569 | f'Turbo') 570 | 571 | await asyncio.sleep(delay=random_sleep_time) 572 | 573 | turbo_multiplier: int | None = await self.activate_turbo(client=client) 574 | 575 | if turbo_multiplier: 576 | logger.success(f'{self.session_name} | Успешно активировал Turbo: ' 577 | f'x{turbo_multiplier}') 578 | active_turbo: bool = True 579 | continue 580 | 581 | else: 582 | turbo_multiplier: int = 1 583 | 584 | elif free_daily_full_energy: 585 | random_sleep_time: int = randint(a=config.SLEEP_BEFORE_ACTIVATE_FREE_BUFFS[0], 586 | b=config.SLEEP_BEFORE_ACTIVATE_FREE_BUFFS[1]) 587 | 588 | logger.info(f'{self.session_name} | Сплю {random_sleep_time} перед активацией ' 589 | f'ежедневного Full Energy') 590 | 591 | await asyncio.sleep(delay=random_sleep_time) 592 | 593 | if await self.activate_task(client=client, 594 | task_id=2): 595 | logger.success(f'{self.session_name} | Успешно запросил ежедневный Full Energy') 596 | 597 | except InvalidSession as error: 598 | raise error 599 | 600 | except Exception as error: 601 | logger.error(f'{self.session_name} | Неизвестная ошибка: {error}') 602 | 603 | random_sleep_time: int = randint(a=config.SLEEP_BETWEEN_CLICK[0], 604 | b=config.SLEEP_BETWEEN_CLICK[1]) 605 | 606 | logger.info(f'{self.session_name} | Сплю {random_sleep_time} сек.') 607 | await asyncio.sleep(delay=random_sleep_time) 608 | 609 | else: 610 | random_sleep_time: int = randint(a=config.SLEEP_BETWEEN_CLICK[0], 611 | b=config.SLEEP_BETWEEN_CLICK[1]) 612 | 613 | logger.info(f'{self.session_name} | Сплю {random_sleep_time} сек.') 614 | await asyncio.sleep(delay=random_sleep_time) 615 | 616 | except InvalidSession as error: 617 | raise error 618 | 619 | except Exception as error: 620 | logger.error(f'{self.session_name} | Неизвестная ошибка: {error}') 621 | 622 | 623 | async def start_farming(session_name: str, 624 | proxy: str | None = None) -> None: 625 | try: 626 | await Farming(session_name=session_name).start_farming(proxy=proxy) 627 | 628 | except InvalidSession: 629 | logger.error(f'{session_name} | Invalid Session') 630 | --------------------------------------------------------------------------------