diff --git a/core/mexc_webclient/README.md b/core/mexc_webclient/README.md new file mode 100644 index 0000000..400c0bd --- /dev/null +++ b/core/mexc_webclient/README.md @@ -0,0 +1,9 @@ + # Run the automation +python run_mexc_browser.py + +# Browser opens with MEXC futures page +# Log in manually โ†’ Choose option 1 to verify login +# Choose option 5 for guided test trading +# Perform small trade โ†’ All requests captured +# Choose option 4 to save data +# Use captured cookies with MEXCFuturesWebClient \ No newline at end of file diff --git a/core/mexc_webclient/__init__.py b/core/mexc_webclient/__init__.py new file mode 100644 index 0000000..449bcac --- /dev/null +++ b/core/mexc_webclient/__init__.py @@ -0,0 +1,8 @@ +# MEXC Web Client Module +# +# This module provides web-based trading capabilities for MEXC futures trading +# which is not supported by their official API. + +from .mexc_futures_client import MEXCFuturesWebClient + +__all__ = ['MEXCFuturesWebClient'] \ No newline at end of file diff --git a/core/mexc_webclient/auto_browser.py b/core/mexc_webclient/auto_browser.py new file mode 100644 index 0000000..909932e --- /dev/null +++ b/core/mexc_webclient/auto_browser.py @@ -0,0 +1,502 @@ +#!/usr/bin/env python3 +""" +MEXC Auto Browser with Request Interception + +This script automatically spawns a ChromeDriver instance and captures +all MEXC futures trading requests in real-time, including full request +and response data needed for reverse engineering. +""" + +import logging +import time +import json +import sys +import os +from typing import Dict, List, Optional, Any +from datetime import datetime +import threading +import queue + +# Selenium imports +try: + from selenium import webdriver + from selenium.webdriver.chrome.options import Options + from selenium.webdriver.chrome.service import Service + from selenium.webdriver.common.by import By + from selenium.webdriver.support.ui import WebDriverWait + from selenium.webdriver.support import expected_conditions as EC + from selenium.common.exceptions import TimeoutException, WebDriverException + from webdriver_manager.chrome import ChromeDriverManager + from selenium.webdriver.common.desired_capabilities import DesiredCapabilities +except ImportError: + print("Please install selenium and webdriver-manager:") + print("pip install selenium webdriver-manager") + sys.exit(1) + +# Setup logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + +class MEXCRequestInterceptor: + """ + Automatically spawns ChromeDriver and intercepts all MEXC API requests + """ + + def __init__(self, headless: bool = False, save_to_file: bool = True): + """ + Initialize the request interceptor + + Args: + headless: Run browser in headless mode + save_to_file: Save captured requests to JSON file + """ + self.driver = None + self.headless = headless + self.save_to_file = save_to_file + self.captured_requests = [] + self.captured_responses = [] + self.session_cookies = {} + self.monitoring = False + self.request_queue = queue.Queue() + + # File paths for saving data + self.timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + self.requests_file = f"mexc_requests_{self.timestamp}.json" + self.cookies_file = f"mexc_cookies_{self.timestamp}.json" + + def setup_chrome_with_logging(self) -> webdriver.Chrome: + """Setup Chrome with performance logging enabled""" + logger.info("Setting up ChromeDriver with request interception...") + + # Chrome options + chrome_options = Options() + + if self.headless: + chrome_options.add_argument("--headless") + logger.info("Running in headless mode") + + # Essential options for automation + chrome_options.add_argument("--no-sandbox") + chrome_options.add_argument("--disable-dev-shm-usage") + chrome_options.add_argument("--disable-blink-features=AutomationControlled") + chrome_options.add_argument("--disable-web-security") + chrome_options.add_argument("--allow-running-insecure-content") + chrome_options.add_argument("--disable-features=VizDisplayCompositor") + + # User agent to avoid detection + user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36" + chrome_options.add_argument(f"--user-agent={user_agent}") + + # Disable automation flags + chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"]) + chrome_options.add_experimental_option('useAutomationExtension', False) + + # Enable performance logging for network requests + chrome_options.add_argument("--enable-logging") + chrome_options.add_argument("--log-level=0") + chrome_options.add_argument("--v=1") + + # Set capabilities for performance logging + caps = DesiredCapabilities.CHROME + caps['goog:loggingPrefs'] = { + 'performance': 'ALL', + 'browser': 'ALL' + } + + try: + # Automatically download and install ChromeDriver + logger.info("Downloading/updating ChromeDriver...") + service = Service(ChromeDriverManager().install()) + + # Create driver + driver = webdriver.Chrome( + service=service, + options=chrome_options, + desired_capabilities=caps + ) + + # Hide automation indicators + driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})") + driver.execute_cdp_cmd('Network.setUserAgentOverride', { + "userAgent": user_agent + }) + + # Enable network domain for CDP + driver.execute_cdp_cmd('Network.enable', {}) + driver.execute_cdp_cmd('Runtime.enable', {}) + + logger.info("ChromeDriver setup complete!") + return driver + + except Exception as e: + logger.error(f"Failed to setup ChromeDriver: {e}") + raise + + def start_monitoring(self): + """Start the browser and begin monitoring""" + logger.info("Starting MEXC Request Interceptor...") + + try: + # Setup ChromeDriver + self.driver = self.setup_chrome_with_logging() + + # Navigate to MEXC futures + mexc_url = "https://www.mexc.com/en-GB/futures/ETH_USDT?type=linear_swap" + logger.info(f"Navigating to: {mexc_url}") + self.driver.get(mexc_url) + + # Wait for page load + WebDriverWait(self.driver, 10).until( + EC.presence_of_element_located((By.TAG_NAME, "body")) + ) + + logger.info("โœ… MEXC page loaded successfully!") + logger.info("๐Ÿ“ Please log in manually in the browser window") + logger.info("๐Ÿ” Request monitoring is now active...") + + # Start monitoring in background thread + self.monitoring = True + monitor_thread = threading.Thread(target=self._monitor_requests, daemon=True) + monitor_thread.start() + + # Wait for manual login + self._wait_for_login() + + return True + + except Exception as e: + logger.error(f"Failed to start monitoring: {e}") + return False + + def _wait_for_login(self): + """Wait for user to log in and show interactive menu""" + logger.info("\n" + "="*60) + logger.info("MEXC REQUEST INTERCEPTOR - INTERACTIVE MODE") + logger.info("="*60) + + while True: + print("\nOptions:") + print("1. Check login status") + print("2. Extract current cookies") + print("3. Show captured requests summary") + print("4. Save captured data to files") + print("5. Perform test trade (manual)") + print("6. Monitor for 60 seconds") + print("0. Stop and exit") + + choice = input("\nEnter choice (0-6): ").strip() + + if choice == "1": + self._check_login_status() + elif choice == "2": + self._extract_cookies() + elif choice == "3": + self._show_requests_summary() + elif choice == "4": + self._save_all_data() + elif choice == "5": + self._guide_test_trade() + elif choice == "6": + self._monitor_for_duration(60) + elif choice == "0": + break + else: + print("Invalid choice. Please try again.") + + self.stop_monitoring() + + def _check_login_status(self): + """Check if user is logged into MEXC""" + try: + cookies = self.driver.get_cookies() + auth_cookies = ['uc_token', 'u_id', 'x-mxc-fingerprint'] + found_auth = [] + + for cookie in cookies: + if cookie['name'] in auth_cookies and cookie['value']: + found_auth.append(cookie['name']) + + if len(found_auth) >= 2: + print("โœ… LOGIN DETECTED - You appear to be logged in!") + print(f" Found auth cookies: {', '.join(found_auth)}") + return True + else: + print("โŒ NOT LOGGED IN - Please log in to MEXC in the browser") + print(" Missing required authentication cookies") + return False + + except Exception as e: + print(f"โŒ Error checking login: {e}") + return False + + def _extract_cookies(self): + """Extract and display current session cookies""" + try: + cookies = self.driver.get_cookies() + cookie_dict = {} + + for cookie in cookies: + cookie_dict[cookie['name']] = cookie['value'] + + self.session_cookies = cookie_dict + + print(f"\n๐Ÿ“Š Extracted {len(cookie_dict)} cookies:") + + # Show important cookies + important = ['uc_token', 'u_id', 'x-mxc-fingerprint', 'mexc_fingerprint_visitorId'] + for name in important: + if name in cookie_dict: + value = cookie_dict[name] + display_value = value[:20] + "..." if len(value) > 20 else value + print(f" โœ… {name}: {display_value}") + else: + print(f" โŒ {name}: Missing") + + # Save cookies to file + if self.save_to_file: + with open(self.cookies_file, 'w') as f: + json.dump(cookie_dict, f, indent=2) + print(f"\n๐Ÿ’พ Cookies saved to: {self.cookies_file}") + + except Exception as e: + print(f"โŒ Error extracting cookies: {e}") + + def _monitor_requests(self): + """Background thread to monitor network requests""" + last_log_count = 0 + + while self.monitoring: + try: + # Get performance logs + logs = self.driver.get_log('performance') + + for log in logs: + try: + message = json.loads(log['message']) + method = message.get('message', {}).get('method', '') + + # Capture network requests + if method == 'Network.requestWillBeSent': + self._process_request(message['message']['params']) + elif method == 'Network.responseReceived': + self._process_response(message['message']['params']) + + except (json.JSONDecodeError, KeyError) as e: + continue + + # Show progress every 10 new requests + if len(self.captured_requests) >= last_log_count + 10: + last_log_count = len(self.captured_requests) + logger.info(f"๐Ÿ“ˆ Captured {len(self.captured_requests)} requests, {len(self.captured_responses)} responses") + + except Exception as e: + if self.monitoring: # Only log if we're still supposed to be monitoring + logger.debug(f"Monitor error: {e}") + + time.sleep(0.5) # Check every 500ms + + def _process_request(self, request_data): + """Process a captured network request""" + try: + url = request_data.get('request', {}).get('url', '') + + # Filter for MEXC API requests + if self._is_mexc_request(url): + request_info = { + 'type': 'request', + 'timestamp': datetime.now().isoformat(), + 'url': url, + 'method': request_data.get('request', {}).get('method', ''), + 'headers': request_data.get('request', {}).get('headers', {}), + 'postData': request_data.get('request', {}).get('postData', ''), + 'requestId': request_data.get('requestId', '') + } + + self.captured_requests.append(request_info) + + # Show important requests immediately + if ('futures.mexc.com' in url or 'captcha' in url): + print(f"\n๐Ÿš€ CAPTURED REQUEST: {request_info['method']} {url}") + if request_info['postData']: + print(f" ๐Ÿ“„ POST Data: {request_info['postData'][:100]}...") + + except Exception as e: + logger.debug(f"Error processing request: {e}") + + def _process_response(self, response_data): + """Process a captured network response""" + try: + url = response_data.get('response', {}).get('url', '') + + # Filter for MEXC API responses + if self._is_mexc_request(url): + response_info = { + 'type': 'response', + 'timestamp': datetime.now().isoformat(), + 'url': url, + 'status': response_data.get('response', {}).get('status', 0), + 'headers': response_data.get('response', {}).get('headers', {}), + 'requestId': response_data.get('requestId', '') + } + + self.captured_responses.append(response_info) + + # Show important responses immediately + if ('futures.mexc.com' in url or 'captcha' in url): + status = response_info['status'] + status_emoji = "โœ…" if status == 200 else "โŒ" + print(f" {status_emoji} RESPONSE: {status} for {url}") + + except Exception as e: + logger.debug(f"Error processing response: {e}") + + def _is_mexc_request(self, url: str) -> bool: + """Check if URL is a relevant MEXC API request""" + mexc_indicators = [ + 'futures.mexc.com', + 'ucgateway/captcha_api', + 'api/v1/private', + 'api/v3/order', + 'mexc.com/api' + ] + + return any(indicator in url for indicator in mexc_indicators) + + def _show_requests_summary(self): + """Show summary of captured requests""" + print(f"\n๐Ÿ“Š CAPTURE SUMMARY:") + print(f" Total Requests: {len(self.captured_requests)}") + print(f" Total Responses: {len(self.captured_responses)}") + + # Group by URL pattern + url_counts = {} + for req in self.captured_requests: + base_url = req['url'].split('?')[0] # Remove query params + url_counts[base_url] = url_counts.get(base_url, 0) + 1 + + print("\n๐Ÿ”— Top URLs:") + for url, count in sorted(url_counts.items(), key=lambda x: x[1], reverse=True)[:5]: + print(f" {count}x {url}") + + # Show recent futures API calls + futures_requests = [r for r in self.captured_requests if 'futures.mexc.com' in r['url']] + if futures_requests: + print(f"\n๐Ÿš€ Futures API Calls: {len(futures_requests)}") + for req in futures_requests[-3:]: # Show last 3 + print(f" {req['method']} {req['url']}") + + def _save_all_data(self): + """Save all captured data to files""" + if not self.save_to_file: + print("File saving is disabled") + return + + try: + # Save requests + with open(self.requests_file, 'w') as f: + json.dump({ + 'requests': self.captured_requests, + 'responses': self.captured_responses, + 'summary': { + 'total_requests': len(self.captured_requests), + 'total_responses': len(self.captured_responses), + 'capture_session': self.timestamp + } + }, f, indent=2) + + # Save cookies if we have them + if self.session_cookies: + with open(self.cookies_file, 'w') as f: + json.dump(self.session_cookies, f, indent=2) + + print(f"\n๐Ÿ’พ Data saved to:") + print(f" ๐Ÿ“‹ Requests: {self.requests_file}") + if self.session_cookies: + print(f" ๐Ÿช Cookies: {self.cookies_file}") + + except Exception as e: + print(f"โŒ Error saving data: {e}") + + def _guide_test_trade(self): + """Guide user through performing a test trade""" + print("\n๐Ÿงช TEST TRADE GUIDE:") + print("1. Make sure you're logged into MEXC") + print("2. Go to the trading interface") + print("3. Try to place a SMALL test trade (it may fail, but we'll capture the requests)") + print("4. Watch the console for captured API calls") + print("\nโš ๏ธ IMPORTANT: Use very small amounts for testing!") + input("\nPress Enter when you're ready to start monitoring...") + + self._monitor_for_duration(120) # Monitor for 2 minutes + + def _monitor_for_duration(self, seconds: int): + """Monitor requests for a specific duration""" + print(f"\n๐Ÿ” Monitoring requests for {seconds} seconds...") + print("Perform your trading actions now!") + + start_time = time.time() + initial_count = len(self.captured_requests) + + while time.time() - start_time < seconds: + current_count = len(self.captured_requests) + new_requests = current_count - initial_count + + remaining = seconds - int(time.time() - start_time) + print(f"\rโฑ๏ธ Time remaining: {remaining}s | New requests: {new_requests}", end="", flush=True) + + time.sleep(1) + + final_count = len(self.captured_requests) + new_total = final_count - initial_count + print(f"\nโœ… Monitoring complete! Captured {new_total} new requests") + + def stop_monitoring(self): + """Stop monitoring and close browser""" + logger.info("Stopping request monitoring...") + self.monitoring = False + + if self.driver: + self.driver.quit() + logger.info("Browser closed") + + # Final save + if self.save_to_file and (self.captured_requests or self.captured_responses): + self._save_all_data() + logger.info("Final data save complete") + +def main(): + """Main function to run the interceptor""" + print("๐Ÿš€ MEXC Request Interceptor with ChromeDriver") + print("=" * 50) + print("This will automatically:") + print("โœ… Download/setup ChromeDriver") + print("โœ… Open MEXC futures page") + print("โœ… Capture all API requests/responses") + print("โœ… Extract session cookies") + print("โœ… Save data to JSON files") + print("\nPress Ctrl+C to stop at any time") + + # Ask for preferences + headless = input("\nRun in headless mode? (y/n): ").lower().strip() == 'y' + + interceptor = MEXCRequestInterceptor(headless=headless, save_to_file=True) + + try: + success = interceptor.start_monitoring() + if not success: + print("โŒ Failed to start monitoring") + return + + except KeyboardInterrupt: + print("\n\nโน๏ธ Stopping interceptor...") + except Exception as e: + print(f"\nโŒ Error: {e}") + finally: + interceptor.stop_monitoring() + print("\n๐Ÿ‘‹ Goodbye!") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/core/mexc_webclient/browser_automation.py b/core/mexc_webclient/browser_automation.py new file mode 100644 index 0000000..f2f6e43 --- /dev/null +++ b/core/mexc_webclient/browser_automation.py @@ -0,0 +1,358 @@ +""" +MEXC Browser Automation for Cookie Extraction and Request Monitoring + +This module uses Selenium to automate browser interactions and extract +session cookies and request data for MEXC futures trading. +""" + +import logging +import time +import json +from typing import Dict, List, Optional, Any +from selenium import webdriver +from selenium.webdriver.chrome.options import Options +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.common.exceptions import TimeoutException, WebDriverException +from selenium.webdriver.chrome.service import Service +from webdriver_manager.chrome import ChromeDriverManager + +logger = logging.getLogger(__name__) + +class MEXCBrowserAutomation: + """ + Browser automation for MEXC futures trading session management + """ + + def __init__(self, headless: bool = False, proxy: Optional[str] = None): + """ + Initialize browser automation + + Args: + headless: Run browser in headless mode + proxy: HTTP proxy to use (format: host:port) + """ + self.driver = None + self.headless = headless + self.proxy = proxy + self.logged_in = False + + def setup_chrome_driver(self) -> webdriver.Chrome: + """Setup Chrome driver with appropriate options""" + chrome_options = Options() + + if self.headless: + chrome_options.add_argument("--headless") + + # Basic Chrome options for automation + chrome_options.add_argument("--no-sandbox") + chrome_options.add_argument("--disable-dev-shm-usage") + chrome_options.add_argument("--disable-blink-features=AutomationControlled") + chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"]) + chrome_options.add_experimental_option('useAutomationExtension', False) + + # Set user agent to avoid detection + chrome_options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36") + + # Proxy setup if provided + if self.proxy: + chrome_options.add_argument(f"--proxy-server=http://{self.proxy}") + + # Enable network logging + chrome_options.add_argument("--enable-logging") + chrome_options.add_argument("--log-level=0") + chrome_options.set_capability("goog:loggingPrefs", {"performance": "ALL"}) + + # Automatically download and setup ChromeDriver + service = Service(ChromeDriverManager().install()) + + try: + driver = webdriver.Chrome(service=service, options=chrome_options) + + # Execute script to avoid detection + driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})") + + return driver + except WebDriverException as e: + logger.error(f"Failed to setup Chrome driver: {e}") + raise + + def start_browser(self): + """Start the browser session""" + if self.driver is None: + logger.info("Starting Chrome browser for MEXC automation") + self.driver = self.setup_chrome_driver() + logger.info("Browser started successfully") + + def stop_browser(self): + """Stop the browser session""" + if self.driver: + logger.info("Stopping browser") + self.driver.quit() + self.driver = None + + def navigate_to_mexc_futures(self, symbol: str = "ETH_USDT"): + """ + Navigate to MEXC futures trading page + + Args: + symbol: Trading symbol to navigate to + """ + if not self.driver: + self.start_browser() + + url = f"https://www.mexc.com/en-GB/futures/{symbol}?type=linear_swap" + logger.info(f"Navigating to MEXC futures: {url}") + + self.driver.get(url) + + # Wait for page to load + try: + WebDriverWait(self.driver, 10).until( + EC.presence_of_element_located((By.TAG_NAME, "body")) + ) + logger.info("MEXC futures page loaded") + except TimeoutException: + logger.error("Timeout waiting for MEXC page to load") + + def wait_for_login(self, timeout: int = 300) -> bool: + """ + Wait for user to manually log in to MEXC + + Args: + timeout: Maximum time to wait for login (seconds) + + Returns: + bool: True if login detected, False if timeout + """ + logger.info("Please log in to MEXC manually in the browser window") + logger.info("Waiting for login completion...") + + start_time = time.time() + + while time.time() - start_time < timeout: + # Check if we can find elements that indicate logged in state + try: + # Look for user-specific elements that appear after login + cookies = self.driver.get_cookies() + + # Check for authentication cookies + auth_cookies = ['uc_token', 'u_id'] + logged_in_indicators = 0 + + for cookie in cookies: + if cookie['name'] in auth_cookies and cookie['value']: + logged_in_indicators += 1 + + if logged_in_indicators >= 2: + logger.info("Login detected!") + self.logged_in = True + return True + + except Exception as e: + logger.debug(f"Error checking login status: {e}") + + time.sleep(2) # Check every 2 seconds + + logger.error(f"Login timeout after {timeout} seconds") + return False + + def extract_session_cookies(self) -> Dict[str, str]: + """ + Extract all cookies from current browser session + + Returns: + Dictionary of cookie name-value pairs + """ + if not self.driver: + logger.error("Browser not started") + return {} + + cookies = {} + + try: + browser_cookies = self.driver.get_cookies() + + for cookie in browser_cookies: + cookies[cookie['name']] = cookie['value'] + + logger.info(f"Extracted {len(cookies)} cookies from browser session") + + # Log important cookies (without values for security) + important_cookies = ['uc_token', 'u_id', 'x-mxc-fingerprint', 'mexc_fingerprint_visitorId'] + for cookie_name in important_cookies: + if cookie_name in cookies: + logger.info(f"Found important cookie: {cookie_name}") + else: + logger.warning(f"Missing important cookie: {cookie_name}") + + return cookies + + except Exception as e: + logger.error(f"Failed to extract cookies: {e}") + return {} + + def monitor_network_requests(self, duration: int = 60) -> List[Dict[str, Any]]: + """ + Monitor network requests for the specified duration + + Args: + duration: How long to monitor requests (seconds) + + Returns: + List of captured network requests + """ + if not self.driver: + logger.error("Browser not started") + return [] + + logger.info(f"Starting network monitoring for {duration} seconds") + logger.info("Please perform trading actions in the browser (open/close positions)") + + start_time = time.time() + captured_requests = [] + + while time.time() - start_time < duration: + try: + # Get performance logs (network requests) + logs = self.driver.get_log('performance') + + for log in logs: + message = json.loads(log['message']) + + # Filter for relevant MEXC API requests + if (message.get('message', {}).get('method') == 'Network.responseReceived'): + response = message['message']['params']['response'] + url = response.get('url', '') + + # Look for futures API calls + if ('futures.mexc.com' in url or + 'ucgateway/captcha_api' in url or + 'api/v1/private' in url): + + request_data = { + 'url': url, + 'method': response.get('mimeType', ''), + 'status': response.get('status'), + 'headers': response.get('headers', {}), + 'timestamp': log['timestamp'] + } + + captured_requests.append(request_data) + logger.info(f"Captured request: {url}") + + except Exception as e: + logger.debug(f"Error in network monitoring: {e}") + + time.sleep(1) + + logger.info(f"Network monitoring complete. Captured {len(captured_requests)} requests") + return captured_requests + + def perform_test_trade(self, symbol: str = "ETH_USDT", volume: float = 1.0, leverage: int = 200): + """ + Attempt to perform a test trade to capture the complete request flow + + Args: + symbol: Trading symbol + volume: Position size + leverage: Leverage multiplier + """ + if not self.logged_in: + logger.error("Not logged in - cannot perform test trade") + return + + logger.info(f"Attempting test trade: {symbol}, Volume: {volume}, Leverage: {leverage}x") + logger.info("This will attempt to click trading interface elements") + + try: + # This would need to be implemented based on MEXC's specific UI elements + # For now, just wait and let user perform manual actions + logger.info("Please manually place a small test trade while monitoring is active") + time.sleep(30) + + except Exception as e: + logger.error(f"Error during test trade: {e}") + + def full_session_capture(self, symbol: str = "ETH_USDT") -> Dict[str, Any]: + """ + Complete session capture workflow + + Args: + symbol: Trading symbol to use + + Returns: + Dictionary containing cookies and captured requests + """ + logger.info("Starting full MEXC session capture") + + try: + # Start browser and navigate to MEXC + self.navigate_to_mexc_futures(symbol) + + # Wait for manual login + if not self.wait_for_login(): + return {'success': False, 'error': 'Login timeout'} + + # Extract session cookies + cookies = self.extract_session_cookies() + + if not cookies: + return {'success': False, 'error': 'Failed to extract cookies'} + + # Monitor network requests while user performs actions + logger.info("Starting network monitoring - please perform trading actions now") + requests = self.monitor_network_requests(duration=120) # 2 minutes + + return { + 'success': True, + 'cookies': cookies, + 'network_requests': requests, + 'timestamp': int(time.time()) + } + + except Exception as e: + logger.error(f"Error in session capture: {e}") + return {'success': False, 'error': str(e)} + + finally: + self.stop_browser() + +def main(): + """Main function for standalone execution""" + logging.basicConfig(level=logging.INFO) + + print("MEXC Browser Automation - Session Capture") + print("This will open a browser window for you to log into MEXC") + print("Make sure you have Chrome browser installed") + + automation = MEXCBrowserAutomation(headless=False) + + try: + result = automation.full_session_capture() + + if result['success']: + print(f"\nSession capture successful!") + print(f"Extracted {len(result['cookies'])} cookies") + print(f"Captured {len(result['network_requests'])} network requests") + + # Save results to file + output_file = f"mexc_session_capture_{int(time.time())}.json" + with open(output_file, 'w') as f: + json.dump(result, f, indent=2) + + print(f"Results saved to: {output_file}") + + else: + print(f"Session capture failed: {result['error']}") + + except KeyboardInterrupt: + print("\nSession capture interrupted by user") + except Exception as e: + print(f"Error: {e}") + finally: + automation.stop_browser() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/core/mexc_webclient/mexc_futures_client.py b/core/mexc_webclient/mexc_futures_client.py new file mode 100644 index 0000000..872ddd9 --- /dev/null +++ b/core/mexc_webclient/mexc_futures_client.py @@ -0,0 +1,474 @@ +""" +MEXC Futures Web Client + +This module implements a web-based client for MEXC futures trading +since their official API doesn't support futures (leverage) trading. + +It mimics browser behavior by replicating the exact HTTP requests +that the web interface makes. +""" + +import logging +import requests +import time +import json +import hmac +import hashlib +import base64 +from typing import Dict, List, Optional, Any +from datetime import datetime +import uuid +from urllib.parse import urlencode + +logger = logging.getLogger(__name__) + +class MEXCFuturesWebClient: + """ + MEXC Futures Web Client that mimics browser behavior for futures trading. + + Since MEXC's official API doesn't support futures, this client replicates + the exact HTTP requests made by their web interface. + """ + + def __init__(self, session_cookies: Dict[str, str] = None): + """ + Initialize the MEXC Futures Web Client + + Args: + session_cookies: Dictionary of cookies from an authenticated browser session + """ + self.session = requests.Session() + + # Base URLs for different endpoints + self.base_url = "https://www.mexc.com" + self.futures_api_url = "https://futures.mexc.com/api/v1" + self.captcha_url = f"{self.base_url}/ucgateway/captcha_api/captcha/robot" + + # Session state + self.is_authenticated = False + self.user_id = None + self.auth_token = None + self.fingerprint = None + self.visitor_id = None + + # Load session cookies if provided + if session_cookies: + self.load_session_cookies(session_cookies) + + # Setup default headers that mimic a real browser + self.setup_browser_headers() + + def setup_browser_headers(self): + """Setup default headers that mimic Chrome browser""" + self.session.headers.update({ + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36', + 'Accept': '*/*', + 'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8', + 'Accept-Encoding': 'gzip, deflate, br', + 'sec-ch-ua': '"Chromium";v="136", "Google Chrome";v="136", "Not.A/Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + 'sec-fetch-dest': 'empty', + 'sec-fetch-mode': 'cors', + 'sec-fetch-site': 'same-origin', + 'Cache-Control': 'no-cache', + 'Pragma': 'no-cache' + }) + + def load_session_cookies(self, cookies: Dict[str, str]): + """ + Load session cookies from browser + + Args: + cookies: Dictionary of cookie name-value pairs + """ + for name, value in cookies.items(): + self.session.cookies.set(name, value) + + # Extract important session info from cookies + self.auth_token = cookies.get('uc_token') + self.user_id = cookies.get('u_id') + self.fingerprint = cookies.get('x-mxc-fingerprint') + self.visitor_id = cookies.get('mexc_fingerprint_visitorId') + + if self.auth_token and self.user_id: + self.is_authenticated = True + logger.info("MEXC: Loaded authenticated session") + else: + logger.warning("MEXC: Session cookies incomplete - authentication may fail") + + def extract_cookies_from_browser(self, cookie_string: str) -> Dict[str, str]: + """ + Extract cookies from a browser cookie string + + Args: + cookie_string: Raw cookie string from browser (copy from Network tab) + + Returns: + Dictionary of parsed cookies + """ + cookies = {} + cookie_pairs = cookie_string.split(';') + + for pair in cookie_pairs: + if '=' in pair: + name, value = pair.strip().split('=', 1) + cookies[name] = value + + return cookies + + def verify_captcha(self, symbol: str, side: str, leverage: str) -> bool: + """ + Verify captcha for robot trading protection + + Args: + symbol: Trading symbol (e.g., 'ETH_USDT') + side: 'openlong', 'closelong', 'openshort', 'closeshort' + leverage: Leverage string (e.g., '200X') + + Returns: + bool: True if captcha verification successful + """ + if not self.is_authenticated: + logger.error("MEXC: Cannot verify captcha - not authenticated") + return False + + # Build captcha endpoint URL + endpoint = f"robot.future.{side}.{symbol}.{leverage}" + url = f"{self.captcha_url}/{endpoint}" + + # Setup headers for captcha request + headers = { + 'Content-Type': 'application/json', + 'Language': 'en-GB', + 'Referer': f'{self.base_url}/en-GB/futures/{symbol}?type=linear_swap', + 'trochilus-uid': self.user_id, + 'trochilus-trace-id': f"{uuid.uuid4()}-{int(time.time() * 1000) % 10000:04d}" + } + + # Add captcha token if available (this would need to be extracted from browser) + # For now, we'll make the request without it and see what happens + + try: + response = self.session.get(url, headers=headers, timeout=10) + + if response.status_code == 200: + data = response.json() + if data.get('success') and data.get('code') == 0: + logger.info(f"MEXC: Captcha verification successful for {side} {symbol}") + return True + else: + logger.warning(f"MEXC: Captcha verification failed: {data}") + return False + else: + logger.error(f"MEXC: Captcha request failed with status {response.status_code}") + return False + + except Exception as e: + logger.error(f"MEXC: Captcha verification error: {e}") + return False + + def generate_signature(self, method: str, path: str, params: Dict[str, Any], + timestamp: int, nonce: int) -> str: + """ + Generate signature for MEXC futures API requests + + This is reverse-engineered from the browser requests + """ + # This is a placeholder - the actual signature generation would need + # to be reverse-engineered from the browser's JavaScript + # For now, return empty string and rely on cookie authentication + return "" + + def open_long_position(self, symbol: str, volume: float, leverage: int = 200, + price: Optional[float] = None) -> Dict[str, Any]: + """ + Open a long futures position + + Args: + symbol: Trading symbol (e.g., 'ETH_USDT') + volume: Position size (contracts) + leverage: Leverage multiplier (default 200) + price: Limit price (None for market order) + + Returns: + dict: Order response with order ID + """ + if not self.is_authenticated: + logger.error("MEXC: Cannot open position - not authenticated") + return {'success': False, 'error': 'Not authenticated'} + + # First verify captcha + if not self.verify_captcha(symbol, 'openlong', f'{leverage}X'): + logger.error("MEXC: Captcha verification failed for opening long position") + return {'success': False, 'error': 'Captcha verification failed'} + + # Prepare order parameters based on the request dump + timestamp = int(time.time() * 1000) + nonce = timestamp + + order_data = { + 'symbol': symbol, + 'side': 1, # 1 = long, 2 = short + 'openType': 2, # Open position + 'type': '5', # Market order (might be '1' for limit) + 'vol': volume, + 'leverage': leverage, + 'marketCeiling': False, + 'priceProtect': '0', + 'ts': timestamp, + 'mhash': self._generate_mhash(), # This needs to be implemented + 'mtoken': self.visitor_id + } + + # Add price for limit orders + if price is not None: + order_data['price'] = price + order_data['type'] = '1' # Limit order + + # Add encrypted parameters (these would need proper implementation) + order_data['p0'] = self._encrypt_p0(order_data) # Placeholder + order_data['k0'] = self._encrypt_k0(order_data) # Placeholder + order_data['chash'] = self._generate_chash(order_data) # Placeholder + + # Setup headers for the order request + headers = { + 'Authorization': self.auth_token, + 'Content-Type': 'application/json', + 'Language': 'English', + 'x-language': 'en-GB', + 'x-mxc-nonce': str(nonce), + 'x-mxc-sign': self.generate_signature('POST', '/private/order/create', order_data, timestamp, nonce), + 'trochilus-uid': self.user_id, + 'trochilus-trace-id': f"{uuid.uuid4()}-{int(time.time() * 1000) % 10000:04d}", + 'Referer': 'https://www.mexc.com/' + } + + # Make the order request + url = f"{self.futures_api_url}/private/order/create" + + try: + # First make OPTIONS request (preflight) + options_response = self.session.options(url, headers=headers, timeout=10) + + if options_response.status_code == 200: + # Now make the actual POST request + response = self.session.post(url, json=order_data, headers=headers, timeout=15) + + if response.status_code == 200: + data = response.json() + if data.get('success') and data.get('code') == 0: + order_id = data.get('data', {}).get('orderId') + logger.info(f"MEXC: Long position opened successfully - Order ID: {order_id}") + return { + 'success': True, + 'order_id': order_id, + 'timestamp': data.get('data', {}).get('ts'), + 'symbol': symbol, + 'side': 'long', + 'volume': volume, + 'leverage': leverage + } + else: + logger.error(f"MEXC: Order failed: {data}") + return {'success': False, 'error': data.get('msg', 'Unknown error')} + else: + logger.error(f"MEXC: Order request failed with status {response.status_code}") + return {'success': False, 'error': f'HTTP {response.status_code}'} + else: + logger.error(f"MEXC: OPTIONS preflight failed with status {options_response.status_code}") + return {'success': False, 'error': f'Preflight failed: HTTP {options_response.status_code}'} + + except Exception as e: + logger.error(f"MEXC: Order execution error: {e}") + return {'success': False, 'error': str(e)} + + def close_long_position(self, symbol: str, volume: float, leverage: int = 200, + price: Optional[float] = None) -> Dict[str, Any]: + """ + Close a long futures position + + Args: + symbol: Trading symbol (e.g., 'ETH_USDT') + volume: Position size to close (contracts) + leverage: Leverage multiplier + price: Limit price (None for market order) + + Returns: + dict: Order response + """ + if not self.is_authenticated: + logger.error("MEXC: Cannot close position - not authenticated") + return {'success': False, 'error': 'Not authenticated'} + + # First verify captcha + if not self.verify_captcha(symbol, 'closelong', f'{leverage}X'): + logger.error("MEXC: Captcha verification failed for closing long position") + return {'success': False, 'error': 'Captcha verification failed'} + + # Similar to open_long_position but with closeType instead of openType + timestamp = int(time.time() * 1000) + nonce = timestamp + + order_data = { + 'symbol': symbol, + 'side': 2, # Close side is opposite + 'closeType': 1, # Close position + 'type': '5', # Market order + 'vol': volume, + 'leverage': leverage, + 'marketCeiling': False, + 'priceProtect': '0', + 'ts': timestamp, + 'mhash': self._generate_mhash(), + 'mtoken': self.visitor_id + } + + if price is not None: + order_data['price'] = price + order_data['type'] = '1' + + order_data['p0'] = self._encrypt_p0(order_data) + order_data['k0'] = self._encrypt_k0(order_data) + order_data['chash'] = self._generate_chash(order_data) + + return self._execute_order(order_data, 'close_long') + + def open_short_position(self, symbol: str, volume: float, leverage: int = 200, + price: Optional[float] = None) -> Dict[str, Any]: + """Open a short futures position""" + if not self.verify_captcha(symbol, 'openshort', f'{leverage}X'): + return {'success': False, 'error': 'Captcha verification failed'} + + order_data = { + 'symbol': symbol, + 'side': 2, # 2 = short + 'openType': 2, + 'type': '5', + 'vol': volume, + 'leverage': leverage, + 'marketCeiling': False, + 'priceProtect': '0', + 'ts': int(time.time() * 1000), + 'mhash': self._generate_mhash(), + 'mtoken': self.visitor_id + } + + if price is not None: + order_data['price'] = price + order_data['type'] = '1' + + order_data['p0'] = self._encrypt_p0(order_data) + order_data['k0'] = self._encrypt_k0(order_data) + order_data['chash'] = self._generate_chash(order_data) + + return self._execute_order(order_data, 'open_short') + + def close_short_position(self, symbol: str, volume: float, leverage: int = 200, + price: Optional[float] = None) -> Dict[str, Any]: + """Close a short futures position""" + if not self.verify_captcha(symbol, 'closeshort', f'{leverage}X'): + return {'success': False, 'error': 'Captcha verification failed'} + + order_data = { + 'symbol': symbol, + 'side': 1, # Close side is opposite + 'closeType': 1, + 'type': '5', + 'vol': volume, + 'leverage': leverage, + 'marketCeiling': False, + 'priceProtect': '0', + 'ts': int(time.time() * 1000), + 'mhash': self._generate_mhash(), + 'mtoken': self.visitor_id + } + + if price is not None: + order_data['price'] = price + order_data['type'] = '1' + + order_data['p0'] = self._encrypt_p0(order_data) + order_data['k0'] = self._encrypt_k0(order_data) + order_data['chash'] = self._generate_chash(order_data) + + return self._execute_order(order_data, 'close_short') + + def _execute_order(self, order_data: Dict[str, Any], action: str) -> Dict[str, Any]: + """Common order execution logic""" + timestamp = order_data['ts'] + nonce = timestamp + + headers = { + 'Authorization': self.auth_token, + 'Content-Type': 'application/json', + 'Language': 'English', + 'x-language': 'en-GB', + 'x-mxc-nonce': str(nonce), + 'x-mxc-sign': self.generate_signature('POST', '/private/order/create', order_data, timestamp, nonce), + 'trochilus-uid': self.user_id, + 'trochilus-trace-id': f"{uuid.uuid4()}-{int(time.time() * 1000) % 10000:04d}", + 'Referer': 'https://www.mexc.com/' + } + + url = f"{self.futures_api_url}/private/order/create" + + try: + response = self.session.post(url, json=order_data, headers=headers, timeout=15) + + if response.status_code == 200: + data = response.json() + if data.get('success') and data.get('code') == 0: + order_id = data.get('data', {}).get('orderId') + logger.info(f"MEXC: {action} executed successfully - Order ID: {order_id}") + return { + 'success': True, + 'order_id': order_id, + 'timestamp': data.get('data', {}).get('ts'), + 'action': action + } + else: + logger.error(f"MEXC: {action} failed: {data}") + return {'success': False, 'error': data.get('msg', 'Unknown error')} + else: + logger.error(f"MEXC: {action} request failed with status {response.status_code}") + return {'success': False, 'error': f'HTTP {response.status_code}'} + + except Exception as e: + logger.error(f"MEXC: {action} execution error: {e}") + return {'success': False, 'error': str(e)} + + # Placeholder methods for encryption/hashing - these need proper implementation + def _generate_mhash(self) -> str: + """Generate mhash parameter (needs reverse engineering)""" + return "a0015441fd4c3b6ba427b894b76cb7dd" # Placeholder from request dump + + def _encrypt_p0(self, order_data: Dict[str, Any]) -> str: + """Encrypt p0 parameter (needs reverse engineering)""" + return "placeholder_p0_encryption" # This needs proper implementation + + def _encrypt_k0(self, order_data: Dict[str, Any]) -> str: + """Encrypt k0 parameter (needs reverse engineering)""" + return "placeholder_k0_encryption" # This needs proper implementation + + def _generate_chash(self, order_data: Dict[str, Any]) -> str: + """Generate chash parameter (needs reverse engineering)""" + return "d6c64d28e362f314071b3f9d78ff7494d9cd7177ae0465e772d1840e9f7905d8" # Placeholder + + def get_account_info(self) -> Dict[str, Any]: + """Get account information including positions and balances""" + if not self.is_authenticated: + return {'success': False, 'error': 'Not authenticated'} + + # This would need to be implemented by reverse engineering the account info endpoints + logger.info("MEXC: Account info endpoint not yet implemented") + return {'success': False, 'error': 'Not implemented'} + + def get_open_positions(self) -> List[Dict[str, Any]]: + """Get list of open futures positions""" + if not self.is_authenticated: + return [] + + # This would need to be implemented by reverse engineering the positions endpoint + logger.info("MEXC: Open positions endpoint not yet implemented") + return [] \ No newline at end of file diff --git a/core/mexc_webclient/req_dumps/close_part_1.js b/core/mexc_webclient/req_dumps/close_part_1.js new file mode 100644 index 0000000..6fc7bb1 --- /dev/null +++ b/core/mexc_webclient/req_dumps/close_part_1.js @@ -0,0 +1,49 @@ +fetch("https://www.mexc.com/ucgateway/captcha_api/captcha/robot/robot.future.closelong.ETH_USDT.200X", { + "headers": { + "accept": "*/*", + "accept-language": "en-GB,en-US;q=0.9,en;q=0.8", + "captcha-token": "geetest eyJsb3ROdW1iZXIiOiIwNzA0YTAyOWZhOTc0ZjI5YWVmOWU0NmEyNzQ3OTdiNiIsImNhcHRjaGFPdXRwdXQiOiJaVkwzS3FWaWxnbEZjQWdXOENIQVgxMUVBLVVPUnE1aURQSldzcmlubDFqelBhRTNiUGlEc0VrVTJUR0xuUzRHUkxEdlRkRXFMUHNhZUxxNDZsZ2lNVERUMy1VXzhWOG93T09xSlpfYVdBcFpXak5jYjFUc0JUQnF1WkIwMVVmUkJBZy1UM0NpTlhoQjBRNFJPOEs2VTI3QkVON1VtTy0xd3ItT3dGRDdNV19mNmNpRlRKOGNoNWF0Z3dhUGEtdW15NlZXYmp3dEhzelR6NWFDME42ZnNQRV9YZmtNM244bzItTG10M1QyWWhId3FPVDNrTHJhRlpYTnl4TndXNW96SjJpS0VFWWhTVjAtOUVuYUQ0VWhkTk1nSU9aUHBaanpyUjhHWkxJVUJTei0tS1hYeU1mTC1aSm1QZjBVRjZaY1RVajlyZVNwaVlPT3oxUmRab3M3WXFoUWdLY19IdHBnNG01eEJKbEtmRmlacDRiTk4zci1lU0RfZ3owdnREVFd1ZFJ2bWIwUlIwR05lUV9iMmxMS1NHZmdfbG41TVhFNXB4WWp3SGdSaV9NXzRkM0RsTGstTEloVEcxOGc0Nnd1aFVvODdvdHdISG9qcUNSQ2xRYW9QR3p4c01MWWY2SHJKLV9IeVFMTUdzLTZ6Q1dWX1ZZRVZlMUxpeDVMckFSbyIsInBhc3NUb2tlbiI6IjdiNjY3ZDUwNmE2YWMyZWM5ZDc2MzE2NWM0MWU4YWRkMzVhZjI3Zjk4NzlkMzEzYjZkYTQ0OWM4OTQ0ZGQ5M2QiLCJnZW5UaW1lIjoiMTc1MDY2ODg0MCJ9", + "content-type": "application/json", + "language": "en-GB", + "pragma": "akamai-x-cache-on", + "priority": "u=1, i", + "sec-ch-ua": "\"Chromium\";v=\"136\", \"Google Chrome\";v=\"136\", \"Not.A/Brand\";v=\"99\"", + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": "\"Windows\"", + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + "trochilus-trace-id": "eebf8fc8-f964-4546-8744-5e64006a8c0a-1927", + "trochilus-uid": "34224692", + "cookie": "mxc_theme_upcolor=upgreen; _ym_uid=1742809214182527467; _ym_d=1742809214; mxc_theme_main=dark; mxc_reset_tradingview_key=false; CLIENT_LANG=en-GB; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%2C%22first_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E5%BC%95%E8%8D%90%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_landing_page%22%3A%22https%3A%2F%2Fwww.mexc.com%2Fen-GB%2Ffutures%2FETH_USDT%3Ftype%3Dlinear_swap%22%2C%22%24latest_utm_source%22%3A%22mexc%22%2C%22%24latest_utm_medium%22%3A%22webhomecard1%22%2C%22%24latest_utm_campaign%22%3A%222025marfc%22%7D%2C%22identities%22%3A%22eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTk1Yzc4NjE4MWVkNWQtMDg3OWM4Y2JmNTBiMmItMjYwMTFkNTEtMzY4NjQwMC0xOTVjNzg2MTgxZjJhNTYiLCIkaWRlbnRpdHlfbG9naW5faWQiOiIyMWE4NzI4OTkwYjg0ZjRmYTNhZTY0YzgwMDRiNGFhYSJ9%22%2C%22history_login_id%22%3A%7B%22name%22%3A%22%24identity_login_id%22%2C%22value%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%7D%2C%22%24device_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%7D; NEXT_LOCALE=en-GB; _ga=GA1.1.2036400604.1747751743; _ga_L6XJCQTK75=GS2.1.s1747815810$o3$g1$t1747815810$j60$l0$h0$d6OTUgGouJIfBWxC4kzFOTuaONliYdjfnGg; mxc_has_render_addToHomeScreen=1; mxc_exchange_layout=BA; _vid_t=xs3Co2BWoUOrDfqVl756oZtAnvROGBTE4gOy9JIPJ/czZGzVvGqdsEiNQvK1Su3CL77WpuN3NKOIiz9ANjwE5E6TAK1ti5xk6GZHHzA=; mexc_fingerprint_requestId=; x-mxc-fingerprint=19eaf0dd6e54deececa0a7c9f6dade73; mexc_fingerprint_visitorId=d232fdcef017520be35fda8d34bc393a; uc_token=WEB33c5abf08f789c62b7c413d489120bdb318878c1c83e11f55b918165eff6f71c; u_id=WEB33c5abf08f789c62b7c413d489120bdb318878c1c83e11f55b918165eff6f71c; mexc_clearance_modal_show_date=2025-06-22-undefined; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%2C%22first_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E5%BC%95%E8%8D%90%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_landing_page%22%3A%22https%3A%2F%2Fwww.mexc.com%2Fen-GB%2Ffutures%2FETH_USDT%3Ftype%3Dlinear_swap%22%2C%22%24latest_utm_source%22%3A%22mexc%22%2C%22%24latest_utm_medium%22%3A%22webhomecard1%22%2C%22%24latest_utm_campaign%22%3A%222025marfc%22%7D%2C%22identities%22%3A%22eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTk1Yzc4NjE4MWVkNWQtMDg3OWM4Y2JmNTBiMmItMjYwMTFkNTEtMzY4NjQwMC0xOTVjNzg2MTgxZjJhNTYiLCIkaWRlbnRpdHlfbG9naW5faWQiOiIyMWE4NzI4OTkwYjg0ZjRmYTNhZTY0YzgwMDRiNGFhYSJ9%22%2C%22history_login_id%22%3A%7B%22name%22%3A%22%24identity_login_id%22%2C%22value%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%7D%2C%22%24device_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%7D; bm_mi=C5309AE91EFDA39C797321C377A94138~YAAQJKVf1AF2NGCXAQAAxtvcmxzZtwryPea7KkhrzHyPfCMBuVaCgTWi1W8+GifXFggTxal13O/zo+KMZ2FVu86iraAEsfdHrgtlyu90JDKDC8VojepkVPwCVUKwC8bnnj50rkCYulH3IWDy6TaQwurETeBzB7OUPsh9wb/AZrXwdRa7MzS0e/K+u7SDUr4Y0DBivzNiy7fN7WO20bea/ihWy+Oo2j8FMRAo/jOrzIQYQ8jr4XW9EcUwKtPhwH3AfL7bhHLScqn4F/p/emp4o6osjmDXaup2hJECU+bqNnnw2MGlmt6WtIW7yX9Hh++Opn+u9dABJMsd9omw/jKCKzXw~1; ak_bmsc=9732D538691707BD5D4CF11C2561F097~000000000000000000000000000000~YAAQJKVf1BN2NGCXAQAAR+rcmxzh9GTmCq4973R9kb3R+UEYsN50341yuzStB2+G/rIKySYOFZgqfyuUuK0+VwxfP5QOricVy3E12artAYkL5eJRRkuUajSezJY0scRP8eEOi61XgRdez/LGDFoh405MOGpH+IvYf5bhsk7E3EvKhX/qudhWfyk01GBA3Gx98wIlvcEZ+0SRi4L9AiLgVKXlzc9fv5UtuLslp9PyAo3TVQiCMi9L24aIk/OirKRUL/EuK66Okjz+LDavrgviHlJ51gthjwG1myhnK8cYfQN8luNu6kV4sFOXl8RN3QlvnKLnAGMfF5DtjULhNkeGYzu2x+QCQBuCzyLn/+hH/seezSV2Tu72apIM3mOlCPB/H+TCvQmQmW2RlDWWBluBz2jC9axsrjiSTNjXHJpIxj+ZTvRUi2PxoT5sVuZQRrb9lGX7SomF2FSjJVtrYhb6sUNrG+/fDS25LRc7MWN31zxqkp+vM3fi4oO5pPM8gdz6weQ=; bm_sz=2EB794902DBD124BAFF19709C21C7D75~YAAQJKVf1OqrNGCXAQAARq7+mxxZ5uDok2hwqlLLDIr3IYGkWGdrA1FpnUr+E6HAc8/pb//lC+dbtRkVTLP63uTRbIviRlG/MnpFhxatTml2s+ev6pRWjArtyVciBPclPAXO1tJhlD3B7GYmG8FTqp+86mj9GWpg0YdtgZfN58cNtF3sRqrdRLvJYiF+KlJseOoa2ck6Xf3Ph3FjxlS6qUFW00KV3r2FW6El6WQQERabm6Hi8svWE20Qv25oZO85Vt8pB1PFmZJkJC2aRdL0OVpfbgvzEkpAzUbGFoD+6DOfxnjexCc8zH8Gbwf1DU30uaf7r2stIwkDMnSu/IOBd9YZSNUoZZCCwocVFJTJCXg7QuHJz4rAsv/h55OLsnjOBfWe3iC2DXhfzWHnyEwTz3EzG7k1oPVThmDI8ldtUhwWupvkRcPtV4w0Pq55AvifSrLaAEhGQy8Qm/H9HWBVnitHRMeDqBOdJo7U0OR0KE3OI5Q5IF+S9DmJ5ddm4j68aOHfrdzxV2KXlnIoQeSKxWEoOx8IhfXK9RWfJiN0O08yvvTzysa6HA==~4274233~4273456; bm_sv=A5AA59DABB1BCE4705C9B60BD6D1AD5B~YAAQJKVf1OurNGCXAQAAGK/+mxyPTUlzoUi7duT/246TPEhldLQefO3XJCBzd6ROPQWsVe8gdTQh9WHUzndHBbAcuPgjFWLweNYShmsgjoXMhmyra/u6VE/0ODW4OA1jxrfLbehp4rln7GZ6JrgDx6RQr3meqq/NQmPEP1ktpXB6znuHNWDfpjCznOWxcXsM7EVZ2NezQtcDtS92Wvy6U1TlFmmu7EaZBp1m9igPYY2+5e/bMbX5OEgrn8mhO0GyR+Shkc0ufQ==~1; _abck=877BB01412D9E29C3F700519938D94E0~-1~YAAQJKVf1PirNGCXAQAAQLn+mw4UFnJDqav+1sZR6xCAjMcKk/UJuC2yW+2Hgmm1kmKVZqlCHpW9Y3fZScxGpnFZ2Smha6HRxaWyQXNS/rkMZddCSLWAcCZpWWTNPxqlg8IvE4Thau3b8zyMDy1GPhYokcYcXWCxs9fXvltd199teyTFTXYZW1ZkrW0IkGJYYB2CN5X05mOuC2nSJobF0YA9i2dmSa/KyMWgE86hBNwpzzA56NLw3n5yWNOXHmrz+UIy4B4hjewwir0Tskbab1nlCKsiH8zEmymKCL+QeITlH+VgGCvQbH+Xhuz5PeuQeDq4pSwCc98a8gUBAAmo+fFvHBzs6g0k7A2MVEDy8Pof9z4r6w90y3k4lDJ6lq7AP27Xa87GhBu/wYloBPtl7yVTVMBpvROi1axlQi0DrfYewMJErIa2UozzVWa4ys3PhkpK/465dRc2bv0KW5BtYiroC77O1iCBgHNfgjg0IavjcxTjuzsIva43Bd1oU2uMcuZYo93KweivgrU3E4oODicrCqfCXOjikR4QUmex598jA2o9pz8yy7gCTCs4I/Yy+9GnHz18MkLUq1W0dOt47OaVtSWnOxokXzwYPCS9YeTpQclJDTSZ7i7zKsCu81lA4NJDu1xJCVXMEB/NOrTaGryeIegXpAUGLjZc102X0AKZFwQMiWmWkC8QJgGQuOXcUlsI+UDQy/LK1qT713NdhvHDwUy5LGCVcTw23CNAr9O4qiQoifG3DhLfAzEQAXO/RmnVBT9RUhdagYSvc/LBo/3dGNMk1sJ+s2M5hRRbUgA7SsQDBBn0hu0s8R70VcWcssYgP8b95bryyp7TUSS1+S12H5OEyxhne/gv90PUViexloOl5du86rP4MABHfesr0nL0Q4sRzjDE1J25ekl5P291Ur/BEs8l1B7v2TZH8Y0uipYqmVgBSkZpoqicamsSARoNo/qC9h+ECjaEZrX4RCDlL5+qJCS1NYL/fyJFtvkPSGW5rRp92vXiyOQQfPo8Eif0jeVdoGPf~-1~||0||~-1", + "Referer": "https://www.mexc.com/en-GB/futures/ETH_USDT?type=linear_swap", + "Referrer-Policy": "strict-origin-when-cross-origin" + }, + "body": null, + "method": "GET" + }); + +fetch("https://www.mexc.com/ucgateway/captcha_api/captcha/robot/robot.future.closelong.ETH_USDT.200X", { + "headers": { + "accept": "*/*", + "accept-language": "en-GB,en-US;q=0.9,en;q=0.8", + "captcha-token": "geetest eyJsb3ROdW1iZXIiOiI2YTQ2NjFlYjAxOGY0MWJhYjMxMTJhZDgxMjc1OThlYyIsImNhcHRjaGFPdXRwdXQiOiJaVkwzS3FWaWxnbEZjQWdXOENIQVgxMUVBLVVPUnE1aURQSldzcmlubDFqelBhRTNiUGlEc0VrVTJUR0xuUzRHZlVuOFN4QkRjd2xUMzFPYTNFM2RXSzJhZ1pTb0VwQU5yVmhpQ2NSLVN6VlpfQ0FpelR5Ql80aENJVDNaNVAtRnBVdFZlZ0NxdTBFQmRYb3VQWDEtUTNCemE4Y3hkS2h2aHJIRi02RnBYY1pyb0huYlpZREVIQU5LeTM1SDI5YWVGa2VZcEhxZUhWQjlKTTZuQUlFSGpoV0FOcnBJb0FyRE5NaGJlVmd6RldXeWJHdEJRMkxBeUU5STFjblVfM3JuZjVZaXFYWHhKWmJ4QTdnWVF5RHlUTDZ5ZWN3UFFaM285WnpOOGk5VHRiUlFLeDY3ZkxPbS16b1ZUNWt1bnlHZE1EbENvY0ZBMW9tZ0ZJdjg0TzNwaTl2b2RDYnlDaDFudGRwRG1zc1FYVFIxa084WDFKVjZ3TUNkRndic0lpc1J5VEptNGhzemhiWDdzakl6ZHFSazNGNG5LUERFdnFJUHpSR0NhYTVrX2FPQnVnTFJvWGEzNEtmd3dpYTJYLVVZQjdJdXhRT1FJNS0yTlVlUUNkZkhvTW9BWGd1eDgzUVJaRjh4ZjNhSWpKazFyLTZYYl9LRVVmaUh5M0xWUlhOUSIsInBhc3NUb2tlbiI6IjNmZmI2YWE0Njc3ZDk1ODVkYzU5M2YyZjVkOTYzYjlmYTc3MjE1MjljZmZkOGFlODBmZTM3YzU4MjlkMzc1MWEiLCJnZW5UaW1lIjoiMTc1MDY2ODg1MSJ9", + "content-type": "application/json", + "language": "en-GB", + "pragma": "akamai-x-cache-on", + "priority": "u=1, i", + "sec-ch-ua": "\"Chromium\";v=\"136\", \"Google Chrome\";v=\"136\", \"Not.A/Brand\";v=\"99\"", + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": "\"Windows\"", + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + "trochilus-trace-id": "dcd2b2fd-130c-4546-b054-4b2fd14f9ca0-1950", + "trochilus-uid": "34224692", + "cookie": "mxc_theme_upcolor=upgreen; _ym_uid=1742809214182527467; _ym_d=1742809214; mxc_theme_main=dark; mxc_reset_tradingview_key=false; CLIENT_LANG=en-GB; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%2C%22first_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E5%BC%95%E8%8D%90%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_landing_page%22%3A%22https%3A%2F%2Fwww.mexc.com%2Fen-GB%2Ffutures%2FETH_USDT%3Ftype%3Dlinear_swap%22%2C%22%24latest_utm_source%22%3A%22mexc%22%2C%22%24latest_utm_medium%22%3A%22webhomecard1%22%2C%22%24latest_utm_campaign%22%3A%222025marfc%22%7D%2C%22identities%22%3A%22eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTk1Yzc4NjE4MWVkNWQtMDg3OWM4Y2JmNTBiMmItMjYwMTFkNTEtMzY4NjQwMC0xOTVjNzg2MTgxZjJhNTYiLCIkaWRlbnRpdHlfbG9naW5faWQiOiIyMWE4NzI4OTkwYjg0ZjRmYTNhZTY0YzgwMDRiNGFhYSJ9%22%2C%22history_login_id%22%3A%7B%22name%22%3A%22%24identity_login_id%22%2C%22value%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%7D%2C%22%24device_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%7D; NEXT_LOCALE=en-GB; _ga=GA1.1.2036400604.1747751743; _ga_L6XJCQTK75=GS2.1.s1747815810$o3$g1$t1747815810$j60$l0$h0$d6OTUgGouJIfBWxC4kzFOTuaONliYdjfnGg; mxc_has_render_addToHomeScreen=1; mxc_exchange_layout=BA; _vid_t=xs3Co2BWoUOrDfqVl756oZtAnvROGBTE4gOy9JIPJ/czZGzVvGqdsEiNQvK1Su3CL77WpuN3NKOIiz9ANjwE5E6TAK1ti5xk6GZHHzA=; mexc_fingerprint_requestId=; x-mxc-fingerprint=19eaf0dd6e54deececa0a7c9f6dade73; mexc_fingerprint_visitorId=d232fdcef017520be35fda8d34bc393a; uc_token=WEB33c5abf08f789c62b7c413d489120bdb318878c1c83e11f55b918165eff6f71c; u_id=WEB33c5abf08f789c62b7c413d489120bdb318878c1c83e11f55b918165eff6f71c; mexc_clearance_modal_show_date=2025-06-22-undefined; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%2C%22first_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E5%BC%95%E8%8D%90%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_landing_page%22%3A%22https%3A%2F%2Fwww.mexc.com%2Fen-GB%2Ffutures%2FETH_USDT%3Ftype%3Dlinear_swap%22%2C%22%24latest_utm_source%22%3A%22mexc%22%2C%22%24latest_utm_medium%22%3A%22webhomecard1%22%2C%22%24latest_utm_campaign%22%3A%222025marfc%22%7D%2C%22identities%22%3A%22eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTk1Yzc4NjE4MWVkNWQtMDg3OWM4Y2JmNTBiMmItMjYwMTFkNTEtMzY4NjQwMC0xOTVjNzg2MTgxZjJhNTYiLCIkaWRlbnRpdHlfbG9naW5faWQiOiIyMWE4NzI4OTkwYjg0ZjRmYTNhZTY0YzgwMDRiNGFhYSJ9%22%2C%22history_login_id%22%3A%7B%22name%22%3A%22%24identity_login_id%22%2C%22value%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%7D%2C%22%24device_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%7D; bm_mi=C5309AE91EFDA39C797321C377A94138~YAAQJKVf1AF2NGCXAQAAxtvcmxzZtwryPea7KkhrzHyPfCMBuVaCgTWi1W8+GifXFggTxal13O/zo+KMZ2FVu86iraAEsfdHrgtlyu90JDKDC8VojepkVPwCVUKwC8bnnj50rkCYulH3IWDy6TaQwurETeBzB7OUPsh9wb/AZrXwdRa7MzS0e/K+u7SDUr4Y0DBivzNiy7fN7WO20bea/ihWy+Oo2j8FMRAo/jOrzIQYQ8jr4XW9EcUwKtPhwH3AfL7bhHLScqn4F/p/emp4o6osjmDXaup2hJECU+bqNnnw2MGlmt6WtIW7yX9Hh++Opn+u9dABJMsd9omw/jKCKzXw~1; ak_bmsc=9732D538691707BD5D4CF11C2561F097~000000000000000000000000000000~YAAQJKVf1BN2NGCXAQAAR+rcmxzh9GTmCq4973R9kb3R+UEYsN50341yuzStB2+G/rIKySYOFZgqfyuUuK0+VwxfP5QOricVy3E12artAYkL5eJRRkuUajSezJY0scRP8eEOi61XgRdez/LGDFoh405MOGpH+IvYf5bhsk7E3EvKhX/qudhWfyk01GBA3Gx98wIlvcEZ+0SRi4L9AiLgVKXlzc9fv5UtuLslp9PyAo3TVQiCMi9L24aIk/OirKRUL/EuK66Okjz+LDavrgviHlJ51gthjwG1myhnK8cYfQN8luNu6kV4sFOXl8RN3QlvnKLnAGMfF5DtjULhNkeGYzu2x+QCQBuCzyLn/+hH/seezSV2Tu72apIM3mOlCPB/H+TCvQmQmW2RlDWWBluBz2jC9axsrjiSTNjXHJpIxj+ZTvRUi2PxoT5sVuZQRrb9lGX7SomF2FSjJVtrYhb6sUNrG+/fDS25LRc7MWN31zxqkp+vM3fi4oO5pPM8gdz6weQ=; bm_sz=2EB794902DBD124BAFF19709C21C7D75~YAAQJKVf1CqsNGCXAQAA3Nb+mxzdBlL2ywpwD8dcHJD9gU45uHez683v68hyGnPNQ9sN6NhSm+/0R0Z4db6SCJ4qkq3fKouM9+rQxXguiD45meck3xP6dEWJhqTFfUmOt4iO75FCXUV3gQxYyKrBOgVWBzySkFh88L71FZHl517R9J2LDVKrcB3Yne5j0upjLqzASoITcjenWmhCs29R/5XtViMFxPCk+u1OJJN2N4EogU6/V7Ed/MFzgtz8Bxq1zhsx238rvfy/3o9OTEFDaj8Z1HSO54l31Bi/9F0EHQhsvl6D3PJV9wTc+HPt7E8CZkGHtks5uQbQpOkS8gyp8fQij3E03gB7VFZV3juGxWKv4FztCq88Jlg4cFg6skUn2D15nUrwpQy/02Fiyu0qX1DEaJAqWNz5dfqZ6aD5zVLTorN0+WyKsc34F0TkbtBu5MYnVvtEt/M2FhxV4ZA0WlzoON0ejxI8a8D9Y4OgaR+Ck63llZak+Wtm5zgR8gsx602/Dgacz5Xb6o4c5/PM5YH5h18/syZW+mWpYqPR7VDfrq+5gig=~4274233~4273456; bm_sv=A5AA59DABB1BCE4705C9B60BD6D1AD5B~YAAQJKVf1CusNGCXAQAAI9j+mxxP/NwAKNnJQIpkicWXvWHhJqRTM/9izEWVhcQxz0cgANvWz05B7+kbM2QNRruUU95w9YzqgTo5WPdIZntXgRjE+/eZpwwxJehsqjiqEsDuAFvOmUJTXFn9FyQhvFy/cipwyszVUzqeyqifwfvviSAAFyIqpOtl1H9x9aDgdA9/fc2l3Z/bXoGlJWpu9339JVYouoiIstLMaOiT7X45K7WxqKHZusZS2TnqJ0ZjqBWQ0y0nSA==~1; _abck=877BB01412D9E29C3F700519938D94E0~-1~YAAQJKVf1DKsNGCXAQAA2N/+mw533PnQBntmRbc+Y4RI4xxPvPEIsyMB9n3vdJS1bSjeiY1pd+UcG8a4b0wosL9N1mriX6gO2bv+bT0M0cNMII58W7jydCPaM1s85i5nDGkdKK4fSVeY3JEPNgPOfout7VYSpJ1DAAnTGLqaMmyX9AMNJWDAgKvh1SUunEfI6YNhaLnToVLzsiPGcLHkZKHiP4yNoiWN64gv0Hx6GYznnFSMkPHI12ThYidz8hnhylcQRDKr6KWcB0uHjTmmoN71PgU/Yz16wT9Uk/ZsX0oo/adJ7oDD6TEjZzVhb4xGVYgLg9EE6HkB+j0qYRTszwCVW3thpPNOK0C8hBIv156vG8TAWHxX5XKF7bi0L+Xt5sovPXN8viXg5PhroySUdFEzV6PcmgkJIt0WmojujNXT3jHxXpNvw7CdFkf531bSerxLYXstJcaPdA5YYQAq6QeXLeQb4J2LnrTkxesT8NMIPWxN9td9H/dViZSsWZC5XDTtoDqwte7/J6sTy3AMtWtfMbIT1jn5ol+JfOZG5jr1e8kyXNEZm5rlBWpLhOhhqoP8pBUdQrkJg5nhev+TPHIK1fY0bah5Gvjlo87K8WgX/xaOCb6z9ZalRPaCJ3Gzqs3DkSs7IlFkXp0tSqPTZtkL8Jz0kWEMxHW0C3LqO8ESDMQ/m9ANVBkvR+5QooHyhs3kPArTEGD0SGguPPpZOIK0RxSAjX7Tq9JrzMlz62TpSzTc3SpsDIOFnKBHr/hE99wdKwQ8kfH8rOLsdTIhga1Cl3zWvDm1y0gxqWq9pPW7ERM3PiAkZZW62in2OIBXC0Y6tTCqex8vV9+mGvLBDIyjmP6C9jv58hy5lT26KnMnrQGRuz3yMkqvhQDJwAFvOS+jS2S7PKKR2/pOd5ZZwFa120fwy0CMLBDZNo0EDbBs42RYcz7PDK4ib0iBzJr2D3rNsM5CWbapmYJxiLwBp299TO77rBcSx02tECYozwTYYsZzPLQECZ1NkrEUTQRNi4yfB5jcyUZz~-1~||0||~-1", + "Referer": "https://www.mexc.com/en-GB/futures/ETH_USDT?type=linear_swap", + "Referrer-Policy": "strict-origin-when-cross-origin" + }, + "body": null, + "method": "GET" + }); \ No newline at end of file diff --git a/core/mexc_webclient/req_dumps/open.js b/core/mexc_webclient/req_dumps/open.js new file mode 100644 index 0000000..b05eac9 --- /dev/null +++ b/core/mexc_webclient/req_dumps/open.js @@ -0,0 +1,132 @@ +// try buy $10 - failed +fetch("https://www.mexc.com/ucgateway/captcha_api/captcha/robot/robot.future.openlong.ETH_USDT.200X", { + "headers": { + "accept": "*/*", + "accept-language": "en-GB,en-US;q=0.9,en;q=0.8", + "captcha-token": "geetest eyJsb3ROdW1iZXIiOiIyZTJlN2QyY2FlMmQ0MDlmYTA2NjExNjM2MzgyODFmZiIsImNhcHRjaGFPdXRwdXQiOiJaVkwzS3FWaWxnbEZjQWdXOENIQVgxMUVBLVVPUnE1aURQSldzcmlubDFqelBhRTNiUGlEc0VrVTJUR0xuUzRHUms1R2QydG01SVFQb1NPRTdHSmd5Yk56Z01Lb05idXVhWTVUX3ZlN1B0M1ZGRVg1OGlBUHpPUm9mRkYxUW1ZNTJnQm95V3dLSjBvajNES1dCVm1EMUFDZWh1UkNDeGZjcVZwZm9vY25rdkJ3b2piWmliT1VyVnJvdXBVSVczWGJqeWowWGQ1VkJSS3ROZXFJa19lcjJQQTVMbllzS081aWxHd2pDWW5YakUwMDYwTnZTa3VqQnFWZnNpVFZjaEE2ZURfZzBXc2pqN01ISlRrTDdRdzJFR3ZVRFRqSF9xRUlVMFo5VjVqQTdWMlVvcXVIY093Vzg2Z1lQVjV6U0p0VnlWbjh6c3dZT015bHpLUUpTNzk2MnJ0NWRfdnVmVVVjSHc0X2FwU0sxY0hvNU9Jck9pZzdyZHdxRzhlU2R6aG9maTY2MlBLaFpuaDZfUFp3R3R3bGg0TW5nZ3VNczM3YVBqWXdIZkxxVnE0MFdLLW54QXhUOGdYU3haMUxnMWh3bE9wZ2IyOC10azJIaW9YTmd1aTdLOVJ6MjJKdm83RmRscnZ6ay0xRWlJaGFOX0ZBYkpJOHctMUxXZGl2RndReiIsInBhc3NUb2tlbiI6Ijg5NzkyNTRkYTY4MzI2NmZkZmYyZmFkODg4ZDVjODIzZmM3NWUyYzAyZWE5Mjg3YTNlMTQ0ZjE4N2MwZDcxMWEiLCJnZW5UaW1lIjoiMTc1MDY2OTM3MCJ9", + "content-type": "application/json", + "language": "en-GB", + "pragma": "akamai-x-cache-on", + "priority": "u=1, i", + "sec-ch-ua": "\"Chromium\";v=\"136\", \"Google Chrome\";v=\"136\", \"Not.A/Brand\";v=\"99\"", + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": "\"Windows\"", + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + "trochilus-trace-id": "161b0cdd-28e7-4beb-a79e-b5c72a44a64f-0181", + "trochilus-uid": "34224692", + "cookie": "mxc_theme_upcolor=upgreen; _ym_uid=1742809214182527467; _ym_d=1742809214; mxc_theme_main=dark; mxc_reset_tradingview_key=false; CLIENT_LANG=en-GB; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%2C%22first_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E5%BC%95%E8%8D%90%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_landing_page%22%3A%22https%3A%2F%2Fwww.mexc.com%2Fen-GB%2Ffutures%2FETH_USDT%3Ftype%3Dlinear_swap%22%2C%22%24latest_utm_source%22%3A%22mexc%22%2C%22%24latest_utm_medium%22%3A%22webhomecard1%22%2C%22%24latest_utm_campaign%22%3A%222025marfc%22%7D%2C%22identities%22%3A%22eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTk1Yzc4NjE4MWVkNWQtMDg3OWM4Y2JmNTBiMmItMjYwMTFkNTEtMzY4NjQwMC0xOTVjNzg2MTgxZjJhNTYiLCIkaWRlbnRpdHlfbG9naW5faWQiOiIyMWE4NzI4OTkwYjg0ZjRmYTNhZTY0YzgwMDRiNGFhYSJ9%22%2C%22history_login_id%22%3A%7B%22name%22%3A%22%24identity_login_id%22%2C%22value%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%7D%2C%22%24device_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%7D; NEXT_LOCALE=en-GB; _ga=GA1.1.2036400604.1747751743; _ga_L6XJCQTK75=GS2.1.s1747815810$o3$g1$t1747815810$j60$l0$h0$d6OTUgGouJIfBWxC4kzFOTuaONliYdjfnGg; mxc_has_render_addToHomeScreen=1; mxc_exchange_layout=BA; _vid_t=xs3Co2BWoUOrDfqVl756oZtAnvROGBTE4gOy9JIPJ/czZGzVvGqdsEiNQvK1Su3CL77WpuN3NKOIiz9ANjwE5E6TAK1ti5xk6GZHHzA=; mexc_fingerprint_requestId=; x-mxc-fingerprint=19eaf0dd6e54deececa0a7c9f6dade73; mexc_fingerprint_visitorId=d232fdcef017520be35fda8d34bc393a; uc_token=WEB33c5abf08f789c62b7c413d489120bdb318878c1c83e11f55b918165eff6f71c; u_id=WEB33c5abf08f789c62b7c413d489120bdb318878c1c83e11f55b918165eff6f71c; mexc_clearance_modal_show_date=2025-06-22-undefined; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%2C%22first_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E5%BC%95%E8%8D%90%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_landing_page%22%3A%22https%3A%2F%2Fwww.mexc.com%2Fen-GB%2Ffutures%2FETH_USDT%3Ftype%3Dlinear_swap%22%2C%22%24latest_utm_source%22%3A%22mexc%22%2C%22%24latest_utm_medium%22%3A%22webhomecard1%22%2C%22%24latest_utm_campaign%22%3A%222025marfc%22%7D%2C%22identities%22%3A%22eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTk1Yzc4NjE4MWVkNWQtMDg3OWM4Y2JmNTBiMmItMjYwMTFkNTEtMzY4NjQwMC0xOTVjNzg2MTgxZjJhNTYiLCIkaWRlbnRpdHlfbG9naW5faWQiOiIyMWE4NzI4OTkwYjg0ZjRmYTNhZTY0YzgwMDRiNGFhYSJ9%22%2C%22history_login_id%22%3A%7B%22name%22%3A%22%24identity_login_id%22%2C%22value%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%7D%2C%22%24device_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%7D; bm_mi=C5309AE91EFDA39C797321C377A94138~YAAQJKVf1AF2NGCXAQAAxtvcmxzZtwryPea7KkhrzHyPfCMBuVaCgTWi1W8+GifXFggTxal13O/zo+KMZ2FVu86iraAEsfdHrgtlyu90JDKDC8VojepkVPwCVUKwC8bnnj50rkCYulH3IWDy6TaQwurETeBzB7OUPsh9wb/AZrXwdRa7MzS0e/K+u7SDUr4Y0DBivzNiy7fN7WO20bea/ihWy+Oo2j8FMRAo/jOrzIQYQ8jr4XW9EcUwKtPhwH3AfL7bhHLScqn4F/p/emp4o6osjmDXaup2hJECU+bqNnnw2MGlmt6WtIW7yX9Hh++Opn+u9dABJMsd9omw/jKCKzXw~1; ak_bmsc=9732D538691707BD5D4CF11C2561F097~000000000000000000000000000000~YAAQJKVf1BN2NGCXAQAAR+rcmxzh9GTmCq4973R9kb3R+UEYsN50341yuzStB2+G/rIKySYOFZgqfyuUuK0+VwxfP5QOricVy3E12artAYkL5eJRRkuUajSezJY0scRP8eEOi61XgRdez/LGDFoh405MOGpH+IvYf5bhsk7E3EvKhX/qudhWfyk01GBA3Gx98wIlvcEZ+0SRi4L9AiLgVKXlzc9fv5UtuLslp9PyAo3TVQiCMi9L24aIk/OirKRUL/EuK66Okjz+LDavrgviHlJ51gthjwG1myhnK8cYfQN8luNu6kV4sFOXl8RN3QlvnKLnAGMfF5DtjULhNkeGYzu2x+QCQBuCzyLn/+hH/seezSV2Tu72apIM3mOlCPB/H+TCvQmQmW2RlDWWBluBz2jC9axsrjiSTNjXHJpIxj+ZTvRUi2PxoT5sVuZQRrb9lGX7SomF2FSjJVtrYhb6sUNrG+/fDS25LRc7MWN31zxqkp+vM3fi4oO5pPM8gdz6weQ=; bm_sz=2EB794902DBD124BAFF19709C21C7D75~YAAQJKVf1Ni1NGCXAQAAgUgGnBzcmY3f6rixcHCyksQeLfq8sHUcAkzMDW6hzAacGT+eJTtiVRodtFYA89fbwriYvZMYlhFkzf+1aoy8XMzvk/BjpTZ9f68Z9gdAD37ZBXr13ORRrE2HePElRSXZamqWUYg2+aaTClrd4tRzgBT3vWjlFzN/bdroUn6iZW1cONG/OY78qbpALOgo7mvNG41yhI+GKE6JM9LxtgVHBx7uKyIqPjwyRrNuYkwdyu0G2xFJfnu0YE4Mxzpt6kyCg8w+dFBl390DNP2FZP8cgqNrhkms10yUGiJwAyuV5OQ4R/BvNl7xQDHdDshv9T7yUa8w5wERE4qaA/JV3BkrjYlqfutoCXnpYsXQXyDLzaQgy5zzyc1yRM0MOjqIo9InPNOCpwbSVLp3wGUOc6b87pxS4vv//OP6xtmvrBST/TCDVedgjO9oXzFj4Gw97MtWlJbGJjECJkUHSo0tKGGKO7MZQukO6P2M5MbNXhV9uBvcybANiCCRp2NR5EkT++LsVmI2w9z+C6Z1HcckY4umlZ09FHwu8zg=~4274233~4273456; _abck=877BB01412D9E29C3F700519938D94E0~0~YAAQJKVf1P61NGCXAQAA4WwGnA6ywE1+TZXL1dhzQ6znNRCYqkubxvO14xoyknfxTYvI/sO0EaumUswjut5BAgoBcXlEoRrAH7u8b+E1Ict/YotNMHWpJ46gLJZ65yHwTm6oULhMiQr/VNLEUcp+F+JWzYh/icU88bqveADAjjGDTvFVfFmnaYgZFLviyUyymuLqPPl/7TH88jLLOfW5DSxNIGQFMIoOKiuUuG3TLmwGS57kFxKlIHsIkFmeXBNK5rjUwc2639uU4hFOHSmKknIo/wvNafrAevQZAcTQOhQiy+O7in7Syam7YSO8l2L4jmExJR1eK65xV6KWfipHUnB5dKPo8wpB/jbYKcFGXGcSQeiHsTOj6hz/0yW3u82Ra3O0LoiSRT18yi9JvBBHRFErMzwLhlfxLxWNpEUlGMg4FHZNOd+B6VchE684ov2gbw8rUeSOxcIE0/d+9Ekqi9VSB8gfcpPUpFNTQ5KhJNaUQxRdHQH+rhXaMBmz05PwcJgm96B9+m7cZjlFm1aivO1FqZL5PF2VerYeJk7twzRMVeSY09VoepiHOLuKAaaSwKc45Ns36BGOCLHGbwGhJqlOQjNoIID9YfKCivoy0z24F/cE7nVXs+Q4ZcefkjlmUYFoxcrL0z8X9xVdY2wxX6IRb1neuBqq4KAhTmcBD6kJStwlJrWsoprG6UIbXrEPVcFmzsGlo/sxCDjNabM2jrd7UIQRNHK5Suu2HDrxDqOcG4uQx/XwA5lqQ5rgJNy+35YhhTlc0YP7PL9eqKuTRlhdqTOouw6svaqoejwEfEKxTStKiy2HsLzCdAGzt+CpNdi50yIp3Y9hFVYouiOBnYI92e8BznjMRbnBliVqeK0a0eKZ4mgVEVvuANWmEpacAgjeNnBHJT9QqDDTLaJr/MvfMmS2WsF62G6AftVxQIVXwVHLAK9EjapP9Sb16pRNunfGu8RrN/GMGbc42smIVjiLggWvieUyrpOjenOTAxyHimMo77rMZfiNX9slt3nwyeGNAy/BPSQ1wBnKVcHkFyXzlPbYCf9eSEk/rWvsnBJeiEH6Ccd9Dpx8d6yIFLQv3iZPUoarcksXjgj7mg00yvGuQC+7zPYanSQQkj0q+9eu7LK+knFks2WhEChTT//yFRM=~-1~||0||~-1; bm_sv=A5AA59DABB1BCE4705C9B60BD6D1AD5B~YAAQJKVf1Fy3NGCXAQAA9kgHnBz671gz2QGACYtoFa/tk1yD6jEhmmQk4SWx6TBwzKZTosp1diBj0eiVRqXjCfNsZCHsltTcvWf/7FEn1kPZGmA2JicYyAuJ4U4xFW8v4gFdT3BS6z9Ui+2nLkWx4hY7beqmv/wKg6XES6/GOXUdSRzt4HAia4RUCCdjmQEDduz5AMiRiKudBvDNE6kfPFhsnpZs0iSduJSJHYAzmNY6E2SUZ84igb6JJiUezRd3OnAl29wU7A==~1", + "Referer": "https://www.mexc.com/en-GB/futures/ETH_USDT?type=linear_swap", + "Referrer-Policy": "strict-origin-when-cross-origin" + }, + "body": null, + "method": "GET" + }); + +// response + {"success":true,"code":0,"msg":null,"data":null,"_extend":null} + + +// try buy $22 - failed +fetch("https://www.mexc.com/ucgateway/captcha_api/captcha/robot/robot.future.openlong.ETH_USDT.200X", { + "headers": { + "accept": "*/*", + "accept-language": "en-GB,en-US;q=0.9,en;q=0.8", + "captcha-token": "geetest eyJsb3ROdW1iZXIiOiJhZWZiZGNjOTJkMzU0MmM0Yjc0ODY5MDk4NzFiYWM2OCIsImNhcHRjaGFPdXRwdXQiOiJaVkwzS3FWaWxnbEZjQWdXOENIQVgxMUVBLVVPUnE1aURQSldzcmlubDFqelBhRTNiUGlEc0VrVTJUR0xuUzRHdUpzblFvcUZsaXhEQ1ZRRlhPVUhaY0hxRUZwOWE0S0ExQlNMcXBTX01HRXdxNmRSYkJCZkZkZTZiZVRQUFNuNnVTR3FJdlp3RUNTOVlvNzZuRkhEa1p5YlpQaUJKOFlneWQyS3ZFZktFQV83cjZNR2dIOUVjZHhMNloyN2ZqZ2s4U1ZGS3FZVlFKeTJDLXZZZDU3VDB3SVJqN3ZOUzUwdTVvUmoweG02QkxwQmdWTnF1QXRnaWtITktOQTJ0WFJyVTFTTXh0NkFlOEtNNGx5elRxTnpsdERJOWlzM09vaE0wa3Fkc05pRW0yNVA5a0psRGVuZGxqcWdTZzNpOGZaN2FYZldKUVRucy1tWWs2NmdPMDlIZmtoUFByM0o3cjE0MUl4SFB2aXBzQXBXTXFvOVR0Q2V3WFk5dFdmZGZTVTN6bkJsSjZyZXY0aDFoc1RxVTBpdWVPelNXWGJ2cTlLZDNfWEplejY5bk5xekE3MEp0Rnh3VFVJQi0tY3RjUWVrbGdsSER6Zy16QlpiLVE4VHEtdC1pUHpJRUkxb1Q1QUhCNWRsY25JYTFrQnRZaHpGV3NWbE1RNWwyazlyUmEzLSIsInBhc3NUb2tlbiI6IjhjZjE3MmJjM2UwYzA0ZTQwNTI2M2ZjYmViZGMwODZmNjZiYWU4NGE2N2JjZTNlZmZlMzM2MWM4MTRhMTkyOGMiLCJnZW5UaW1lIjoiMTc1MDY2OTQ5NyJ9", + "content-type": "application/json", + "language": "en-GB", + "pragma": "akamai-x-cache-on", + "priority": "u=1, i", + "sec-ch-ua": "\"Chromium\";v=\"136\", \"Google Chrome\";v=\"136\", \"Not.A/Brand\";v=\"99\"", + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": "\"Windows\"", + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + "trochilus-trace-id": "05f388d0-07aa-4e2a-b12a-2cc4ecd780e8-0174", + "trochilus-uid": "34224692", + "cookie": "mxc_theme_upcolor=upgreen; _ym_uid=1742809214182527467; _ym_d=1742809214; mxc_theme_main=dark; mxc_reset_tradingview_key=false; CLIENT_LANG=en-GB; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%2C%22first_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E5%BC%95%E8%8D%90%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_landing_page%22%3A%22https%3A%2F%2Fwww.mexc.com%2Fen-GB%2Ffutures%2FETH_USDT%3Ftype%3Dlinear_swap%22%2C%22%24latest_utm_source%22%3A%22mexc%22%2C%22%24latest_utm_medium%22%3A%22webhomecard1%22%2C%22%24latest_utm_campaign%22%3A%222025marfc%22%7D%2C%22identities%22%3A%22eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTk1Yzc4NjE4MWVkNWQtMDg3OWM4Y2JmNTBiMmItMjYwMTFkNTEtMzY4NjQwMC0xOTVjNzg2MTgxZjJhNTYiLCIkaWRlbnRpdHlfbG9naW5faWQiOiIyMWE4NzI4OTkwYjg0ZjRmYTNhZTY0YzgwMDRiNGFhYSJ9%22%2C%22history_login_id%22%3A%7B%22name%22%3A%22%24identity_login_id%22%2C%22value%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%7D%2C%22%24device_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%7D; NEXT_LOCALE=en-GB; _ga=GA1.1.2036400604.1747751743; _ga_L6XJCQTK75=GS2.1.s1747815810$o3$g1$t1747815810$j60$l0$h0$d6OTUgGouJIfBWxC4kzFOTuaONliYdjfnGg; mxc_has_render_addToHomeScreen=1; mxc_exchange_layout=BA; _vid_t=xs3Co2BWoUOrDfqVl756oZtAnvROGBTE4gOy9JIPJ/czZGzVvGqdsEiNQvK1Su3CL77WpuN3NKOIiz9ANjwE5E6TAK1ti5xk6GZHHzA=; mexc_fingerprint_requestId=; x-mxc-fingerprint=19eaf0dd6e54deececa0a7c9f6dade73; mexc_fingerprint_visitorId=d232fdcef017520be35fda8d34bc393a; uc_token=WEB33c5abf08f789c62b7c413d489120bdb318878c1c83e11f55b918165eff6f71c; u_id=WEB33c5abf08f789c62b7c413d489120bdb318878c1c83e11f55b918165eff6f71c; mexc_clearance_modal_show_date=2025-06-22-undefined; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%2C%22first_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E5%BC%95%E8%8D%90%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_landing_page%22%3A%22https%3A%2F%2Fwww.mexc.com%2Fen-GB%2Ffutures%2FETH_USDT%3Ftype%3Dlinear_swap%22%2C%22%24latest_utm_source%22%3A%22mexc%22%2C%22%24latest_utm_medium%22%3A%22webhomecard1%22%2C%22%24latest_utm_campaign%22%3A%222025marfc%22%7D%2C%22identities%22%3A%22eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTk1Yzc4NjE4MWVkNWQtMDg3OWM4Y2JmNTBiMmItMjYwMTFkNTEtMzY4NjQwMC0xOTVjNzg2MTgxZjJhNTYiLCIkaWRlbnRpdHlfbG9naW5faWQiOiIyMWE4NzI4OTkwYjg0ZjRmYTNhZTY0YzgwMDRiNGFhYSJ9%22%2C%22history_login_id%22%3A%7B%22name%22%3A%22%24identity_login_id%22%2C%22value%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%7D%2C%22%24device_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%7D; bm_mi=C5309AE91EFDA39C797321C377A94138~YAAQJKVf1AF2NGCXAQAAxtvcmxzZtwryPea7KkhrzHyPfCMBuVaCgTWi1W8+GifXFggTxal13O/zo+KMZ2FVu86iraAEsfdHrgtlyu90JDKDC8VojepkVPwCVUKwC8bnnj50rkCYulH3IWDy6TaQwurETeBzB7OUPsh9wb/AZrXwdRa7MzS0e/K+u7SDUr4Y0DBivzNiy7fN7WO20bea/ihWy+Oo2j8FMRAo/jOrzIQYQ8jr4XW9EcUwKtPhwH3AfL7bhHLScqn4F/p/emp4o6osjmDXaup2hJECU+bqNnnw2MGlmt6WtIW7yX9Hh++Opn+u9dABJMsd9omw/jKCKzXw~1; ak_bmsc=9732D538691707BD5D4CF11C2561F097~000000000000000000000000000000~YAAQJKVf1BN2NGCXAQAAR+rcmxzh9GTmCq4973R9kb3R+UEYsN50341yuzStB2+G/rIKySYOFZgqfyuUuK0+VwxfP5QOricVy3E12artAYkL5eJRRkuUajSezJY0scRP8eEOi61XgRdez/LGDFoh405MOGpH+IvYf5bhsk7E3EvKhX/qudhWfyk01GBA3Gx98wIlvcEZ+0SRi4L9AiLgVKXlzc9fv5UtuLslp9PyAo3TVQiCMi9L24aIk/OirKRUL/EuK66Okjz+LDavrgviHlJ51gthjwG1myhnK8cYfQN8luNu6kV4sFOXl8RN3QlvnKLnAGMfF5DtjULhNkeGYzu2x+QCQBuCzyLn/+hH/seezSV2Tu72apIM3mOlCPB/H+TCvQmQmW2RlDWWBluBz2jC9axsrjiSTNjXHJpIxj+ZTvRUi2PxoT5sVuZQRrb9lGX7SomF2FSjJVtrYhb6sUNrG+/fDS25LRc7MWN31zxqkp+vM3fi4oO5pPM8gdz6weQ=; bm_sz=2EB794902DBD124BAFF19709C21C7D75~YAAQJKVf1Iy4NGCXAQAAwk8InBxJr6xjVO1cJfxww3XYqZCUAJZBKKyiZwvm6/7kY5K34SkvzrF/mYFNf6S6IW+hYpzCrmyq4nuB24bihwVJ2zJPYBMZEtc7zvoZRhKBbzeKnWLPFX6/OeaXqasil6H3k4v6b9OiIWCRogGJZSHQqozjUrpdL8GKZBoK/x5erc6FiKap61AsVrX86nPfdRZdhbMNsWoWq/LtQvPa0fxPiCbTsSQpLDiRv4jfWjkc/v8GoZgzSIdKE7YIhQMaKJ8tkQsNPR6E+tmCn5BO9ernFs2737qZPXiaPO/s4gYizcj5V1tLi10YGJQGcYjt5hCEeFFolb3/G/cb6PamNtdDMkciLpkL1hCorzcbJLd0czPayNF5ruQEQmY1WaNo7zdpy0rKBmaVR8AJ176ZAQwh7CHB3jiXcfnJADPAMjF/x0X5TuphLIJNZy8Bw2wCRfu93CNltB4RBNxO3zO43/0izlhOtxpn7QYDnFDwt+GhQtJVCxLTEvHjlSdoJmFQVXEOCJcF2G0teffIQVbqMez6YDaR+gXr~4274233~4273456; _abck=877BB01412D9E29C3F700519938D94E0~0~YAAQJKVf1I+4NGCXAQAAOlEInA7y9M+CMWS2PTfbtsRL90+RsdcFAPUKSWLaXwYJk9CW1/Jn83q8BYJtu8FMwWRd3Uan1Q6ElrR7DDo5D2pxGNDk2fSpxOBpyi2O7+Xpubs2MJLVCz6+aC6yjE3lfN+H2Pw8Ls95Oq6YxJjNUUam0P0ji9SXrpBxa3n5RAJkl3Bk8EF5RNqVQsWyLWG950iOf5wTeUBnr6UNZ8LnGFZTWLKDM5G+ez5m8KoAhdMYQdaXu/m7thMc2wbTrVp0ALbgUN/GYgShNDdpoIYUnTiZS2xFZuNt1dZoGxtEDQyPuOgPF0FPL2LHYzzY7GLY4OByXUFwV70K4YnvEm7RCBNrVUCO4QYrb+C9P3Swj4ZfqS9jlKMHCsnxM9O82dCeMP5hbdEh57MuDQG698yQr5xn5G7yYA1i/oQIVdU2xH5Z7vKaQG9dQ1pCqSlW9VWpCBYoRp3JAgScPh7j5ywqH4rBIokNt8kuUoUG0Ub9LAMq8IR0E/Y/7FhzjHOY1ylkBqs0MiJoHeY/DFEHVM27uwXImDTRlljI9fD8YaF9bDhg/tuDBLGEa8VTu92VIfZDKbInuuRhmNUh0Mq8PFwhHTbkxKT0bfsW9pu3FcsQSrXPm+NrB1kqivm8Yg214VREEV15bjJHK4vNi1xD/YNsSPrKJ5glU7ydI2K7i3cA/DtNFB/b0poWC37Nfr+lEQx+hX0PjmcRU31jX0g0zYlBjptu7IjJ6RuRsi7MiCl6rTc9cO8EGbees3weXd85QFctuGpRo/D/Pxn9535TCx8dxBJ8t/F4gLCaPfpACf90dHarxcM7VLT0othZkbOPERhhrQZxezfdVfGrn33aYx6EPGGIxOc+GSFxntasLUQxavWKfaAfGBEfYJU5ArsmQTatKJY66sAPiH/XXUuR1G84RlBHlZ4SN4FAJWx3+4DEX+sYMN2cxBF4/sWExuoLMSc2UjM6MNPU6bzVpZog+++zFSvvcl0aHUhq00MqBOT7qicVHjtgrERqd4NmokHigqDASScnGLlef1XxBzy6I0jt5mTrDoC5n5fSHoE9tPrhOwGImEc00OR8KNc3w2mwOZATXS3y0XMnpfx+/zeG60HxqQ3ZJ5ocNK5CE0+5wuEipwy4QhY=~-1~-1~-1; bm_sv=A5AA59DABB1BCE4705C9B60BD6D1AD5B~YAAQJKVf1Fa6NGCXAQAAVSEJnByTDuNGj5hTXd6/YPeeipee4wEeYjz83qriUmIf2gMFaRAKjpzGB95HDl0+D1iwP9loR/G5BXk8//Z8aLqlJZnOw4J2TolGLcGbXvmp8m0swdbzFcNwpTzaSkPqIWmgYC4J3K42+TTRidjEUY2Hh43FxTChfoeQL9OmZ7Rf/7nBZZCktgB/9idtgEpyXQQhMZhvaREUgoyMUe1POlIi75MhgwzVz9Q46LgZvEgYXXW0Bwlb4Q==~1", + "Referer": "https://www.mexc.com/en-GB/futures/ETH_USDT?type=linear_swap", + "Referrer-Policy": "strict-origin-when-cross-origin" + }, + "body": null, + "method": "GET" + }); +//resp: +{"success":true,"code":0,"msg":null,"data":null,"_extend":null} + +// open #$30 - should pass +fetch("https://futures.mexc.com/api/v1/private/order/create?mhash=a0015441fd4c3b6ba427b894b76cb7dd", { + "headers": { + "accept": "*/*", + "accept-language": "en-GB,en-US;q=0.9,en;q=0.8", + "priority": "u=1, i", + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-site", + "Referer": "https://www.mexc.com/", + "Referrer-Policy": "strict-origin-when-cross-origin" + }, + "body": null, + "method": "OPTIONS" + }); + //resp headers + access-control-allow-credentials +true +access-control-allow-headers +ACCEPT, CONTENT-TYPE, X-MXC-SIGN, X-MXC-NONCE, LANGUAGE, VERSION, trochilus-trace-id, trochilus-uid, Authorization, NONCE, origin, timezone, device-id, platform, is-app, SIGNATURE, REQUEST-TIME, RECV-WINDOW, Pragma, Ucenter-Token, Ucenter-Via, x-web-sign, user-agent, timezone-login, captcha-token, siteSources, X-Device-Id, X-Client, x-language, expires, x-version, n-uuid, x-source, x-signature, n-timestamp, x-requested-with, responseType +access-control-allow-methods +POST, DELETE, PUT, GET, OPTIONS +access-control-allow-origin +https://www.mexc.com +access-control-expose-headers +x-cache, Content-Disposition +akamai-grn +0.1fa55fd4.1750669684.791dc04 +cache-control +max-age=0, no-cache, no-store +date +Mon, 23 Jun 2025 09:08:05 GMT +expires +Mon, 23 Jun 2025 09:08:05 GMT +nel +{"report_to": "nel", "max_age": 2592000, "response_headers":["Akami-Grn"] } +pragma +no-cache +report-to +{"group": "nel","max_age": 2592000, "endpoints": [{"url":"https://nel-cf.gotoda.co/nel/report"},{"url":"https://nel-akm.gotoda.co/nel/report"}]} +server-timing +ak_p; desc="1750669684876_3563037983_126999556_61383_13908_24_0_219";dur=1 +x-cache +NotCacheable from child +// follow up request +fetch("https://futures.mexc.com/api/v1/private/order/create?mhash=a0015441fd4c3b6ba427b894b76cb7dd", { + "headers": { + "accept": "*/*", + "accept-language": "en-GB,en-US;q=0.9,en;q=0.8", + "authorization": "WEB33c5abf08f789c62b7c413d489120bdb318878c1c83e11f55b918165eff6f71c", + "content-type": "application/json", + "language": "English", + "pragma": "akamai-x-cache-on", + "priority": "u=1, i", + "sec-ch-ua": "\"Chromium\";v=\"136\", \"Google Chrome\";v=\"136\", \"Not.A/Brand\";v=\"99\"", + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": "\"Windows\"", + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-site", + "trochilus-trace-id": "95eda516-c069-4305-898e-8e27f5d85e43-0229", + "trochilus-uid": "34224692", + "x-language": "en-GB", + "x-mxc-nonce": "1750669662851", + "x-mxc-sign": "56b7fb5c427bfeca7647c74f5776e5bc", + "cookie": "mxc_theme_upcolor=upgreen; _ym_uid=1742809214182527467; _ym_d=1742809214; mxc_theme_main=dark; mxc_reset_tradingview_key=false; CLIENT_LANG=en-GB; NEXT_LOCALE=en-GB; _ga=GA1.1.2036400604.1747751743; _ga_L6XJCQTK75=GS2.1.s1747815810$o3$g1$t1747815810$j60$l0$h0$d6OTUgGouJIfBWxC4kzFOTuaONliYdjfnGg; mxc_has_render_addToHomeScreen=1; mxc_exchange_layout=BA; _vid_t=xs3Co2BWoUOrDfqVl756oZtAnvROGBTE4gOy9JIPJ/czZGzVvGqdsEiNQvK1Su3CL77WpuN3NKOIiz9ANjwE5E6TAK1ti5xk6GZHHzA=; mexc_fingerprint_requestId=; x-mxc-fingerprint=19eaf0dd6e54deececa0a7c9f6dade73; mexc_fingerprint_visitorId=d232fdcef017520be35fda8d34bc393a; uc_token=WEB33c5abf08f789c62b7c413d489120bdb318878c1c83e11f55b918165eff6f71c; u_id=WEB33c5abf08f789c62b7c413d489120bdb318878c1c83e11f55b918165eff6f71c; mexc_clearance_modal_show_date=2025-06-22-undefined; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%2C%22first_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E5%BC%95%E8%8D%90%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_landing_page%22%3A%22https%3A%2F%2Fwww.mexc.com%2Fen-GB%2Ffutures%2FETH_USDT%3Ftype%3Dlinear_swap%22%2C%22%24latest_utm_source%22%3A%22mexc%22%2C%22%24latest_utm_medium%22%3A%22webhomecard1%22%2C%22%24latest_utm_campaign%22%3A%222025marfc%22%7D%2C%22identities%22%3A%22eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTk1Yzc4NjE4MWVkNWQtMDg3OWM4Y2JmNTBiMmItMjYwMTFkNTEtMzY4NjQwMC0xOTVjNzg2MTgxZjJhNTYiLCIkaWRlbnRpdHlfbG9naW5faWQiOiIyMWE4NzI4OTkwYjg0ZjRmYTNhZTY0YzgwMDRiNGFhYSJ9%22%2C%22history_login_id%22%3A%7B%22name%22%3A%22%24identity_login_id%22%2C%22value%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%7D%2C%22%24device_id%22%3A%22195c786181ed5d-0879c8cbf50b2b-26011d51-3686400-195c786181f2a56%22%7D; bm_mi=C5309AE91EFDA39C797321C377A94138~YAAQJKVf1AF2NGCXAQAAxtvcmxzZtwryPea7KkhrzHyPfCMBuVaCgTWi1W8+GifXFggTxal13O/zo+KMZ2FVu86iraAEsfdHrgtlyu90JDKDC8VojepkVPwCVUKwC8bnnj50rkCYulH3IWDy6TaQwurETeBzB7OUPsh9wb/AZrXwdRa7MzS0e/K+u7SDUr4Y0DBivzNiy7fN7WO20bea/ihWy+Oo2j8FMRAo/jOrzIQYQ8jr4XW9EcUwKtPhwH3AfL7bhHLScqn4F/p/emp4o6osjmDXaup2hJECU+bqNnnw2MGlmt6WtIW7yX9Hh++Opn+u9dABJMsd9omw/jKCKzXw~1; ak_bmsc=9732D538691707BD5D4CF11C2561F097~000000000000000000000000000000~YAAQJKVf1BN2NGCXAQAAR+rcmxzh9GTmCq4973R9kb3R+UEYsN50341yuzStB2+G/rIKySYOFZgqfyuUuK0+VwxfP5QOricVy3E12artAYkL5eJRRkuUajSezJY0scRP8eEOi61XgRdez/LGDFoh405MOGpH+IvYf5bhsk7E3EvKhX/qudhWfyk01GBA3Gx98wIlvcEZ+0SRi4L9AiLgVKXlzc9fv5UtuLslp9PyAo3TVQiCMi9L24aIk/OirKRUL/EuK66Okjz+LDavrgviHlJ51gthjwG1myhnK8cYfQN8luNu6kV4sFOXl8RN3QlvnKLnAGMfF5DtjULhNkeGYzu2x+QCQBuCzyLn/+hH/seezSV2Tu72apIM3mOlCPB/H+TCvQmQmW2RlDWWBluBz2jC9axsrjiSTNjXHJpIxj+ZTvRUi2PxoT5sVuZQRrb9lGX7SomF2FSjJVtrYhb6sUNrG+/fDS25LRc7MWN31zxqkp+vM3fi4oO5pPM8gdz6weQ=; bm_sz=2EB794902DBD124BAFF19709C21C7D75~YAAQJKVf1Iy4NGCXAQAAwk8InBxJr6xjVO1cJfxww3XYqZCUAJZBKKyiZwvm6/7kY5K34SkvzrF/mYFNf6S6IW+hYpzCrmyq4nuB24bihwVJ2zJPYBMZEtc7zvoZRhKBbzeKnWLPFX6/OeaXqasil6H3k4v6b9OiIWCRogGJZSHQqozjUrpdL8GKZBoK/x5erc6FiKap61AsVrX86nPfdRZdhbMNsWoWq/LtQvPa0fxPiCbTsSQpLDiRv4jfWjkc/v8GoZgzSIdKE7YIhQMaKJ8tkQsNPR6E+tmCn5BO9ernFs2737qZPXiaPO/s4gYizcj5V1tLi10YGJQGcYjt5hCEeFFolb3/G/cb6PamNtdDMkciLpkL1hCorzcbJLd0czPayNF5ruQEQmY1WaNo7zdpy0rKBmaVR8AJ176ZAQwh7CHB3jiXcfnJADPAMjF/x0X5TuphLIJNZy8Bw2wCRfu93CNltB4RBNxO3zO43/0izlhOtxpn7QYDnFDwt+GhQtJVCxLTEvHjlSdoJmFQVXEOCJcF2G0teffIQVbqMez6YDaR+gXr~4274233~4273456; _abck=877BB01412D9E29C3F700519938D94E0~0~YAAQJKVf1I+4NGCXAQAAOlEInA7y9M+CMWS2PTfbtsRL90+RsdcFAPUKSWLaXwYJk9CW1/Jn83q8BYJtu8FMwWRd3Uan1Q6ElrR7DDo5D2pxGNDk2fSpxOBpyi2O7+Xpubs2MJLVCz6+aC6yjE3lfN+H2Pw8Ls95Oq6YxJjNUUam0P0ji9SXrpBxa3n5RAJkl3Bk8EF5RNqVQsWyLWG950iOf5wTeUBnr6UNZ8LnGFZTWLKDM5G+ez5m8KoAhdMYQdaXu/m7thMc2wbTrVp0ALbgUN/GYgShNDdpoIYUnTiZS2xFZuNt1dZoGxtEDQyPuOgPF0FPL2LHYzzY7GLY4OByXUFwV70K4YnvEm7RCBNrVUCO4QYrb+C9P3Swj4ZfqS9jlKMHCsnxM9O82dCeMP5hbdEh57MuDQG698yQr5xn5G7yYA1i/oQIVdU2xH5Z7vKaQG9dQ1pCqSlW9VWpCBYoRp3JAgScPh7j5ywqH4rBIokNt8kuUoUG0Ub9LAMq8IR0E/Y/7FhzjHOY1ylkBqs0MiJoHeY/DFEHVM27uwXImDTRlljI9fD8YaF9bDhg/tuDBLGEa8VTu92VIfZDKbInuuRhmNUh0Mq8PFwhHTbkxKT0bfsW9pu3FcsQSrXPm+NrB1kqivm8Yg214VREEV15bjJHK4vNi1xD/YNsSPrKJ5glU7ydI2K7i3cA/DtNFB/b0poWC37Nfr+lEQx+hX0PjmcRU31jX0g0zYlBjptu7IjJ6RuRsi7MiCl6rTc9cO8EGbees3weXd85QFctuGpRo/D/Pxn9535TCx8dxBJ8t/F4gLCaPfpACf90dHarxcM7VLT0othZkbOPERhhrQZxezfdVfGrn33aYx6EPGGIxOc+GSFxntasLUQxavWKfaAfGBEfYJU5ArsmQTatKJY66sAPiH/XXUuR1G84RlBHlZ4SN4FAJWx3+4DEX+sYMN2cxBF4/sWExuoLMSc2UjM6MNPU6bzVpZog+++zFSvvcl0aHUhq00MqBOT7qicVHjtgrERqd4NmokHigqDASScnGLlef1XxBzy6I0jt5mTrDoC5n5fSHoE9tPrhOwGImEc00OR8KNc3w2mwOZATXS3y0XMnpfx+/zeG60HxqQ3ZJ5ocNK5CE0+5wuEipwy4QhY=~-1~-1~-1; bm_sv=A5AA59DABB1BCE4705C9B60BD6D1AD5B~YAAQJKVf1C+9NGCXAQAAmyoLnBzPhJ13RxeYEUC/FwqfAi6oUEm0ymigBsNk9XCmkRrKng4Ja9Rq0L4/pO1xvASF2KnZVlI4LxfugPshtfB6C4qWXppAOdivLoQR/a9tsGZiCercnmFOzucnmXP2p7rpaTtV0ulZ+nKI8gFuE2p7rzauFGEHyVZF9DoTgHzYlYd97ZCa0mFgTH+U6IVM/JjBasEoEzBzHj7U9pHEkv24879iQttqj+2+nqganxmdb78b17OPcw==~1", + "Referer": "https://www.mexc.com/", + "Referrer-Policy": "strict-origin-when-cross-origin" + }, + "body": "{\"symbol\":\"ETH_USDT\",\"side\":1,\"openType\":2,\"type\":\"5\",\"vol\":1,\"leverage\":200,\"marketCeiling\":false,\"priceProtect\":\"0\",\"p0\":\"q2jLy3l6VJbBGdIrVcLuE8vYvdIA5aGWwCx4OYoyTup6ewBjsVKj3XjRKU9O9HihvlD721ALZTbZ9+9grhXF9p8E97QF0SG+PnvWVYVs0JA2Ri/xfXvaZiJXnNNsxjNhvSElh1MD+I04By4dZjYFe+dztMbIWHNPVQoheWMimTg3+u6DM/oicqJ5bcDPawdCp2qZvaBza/OMOjQbIncWrni++R8VqpVnibTbcNd9Wx3GlS8qtIIXwyTdlFweu7wa5U7JXKU0ZoR+culZNAqdJuBfZoaQ8D5MH7CHuhzlejAcO0+OgerGuyJNC/XsU52/aAAnQ26I558/ME+2Obd8cRxbvko=\",\"k0\":\"i95ng7X1w8/emOdPRMB4Mh65BTI4NkLOKMjSW+2Ms+vK4WAHHVqZ8lIozVw1YDEAUErb4pupALhdhNFPlIROSSYxIyazMNtpThYj7bgpMcZUIxiYXLGeiWIe3a2mOvbqq/zzgLyxquUFevuRDFSMlQiGU41FS+GVn1QoEGWHt6p+zTN8LoiwBTiC4QKxfob8QXqrO2tqEHI0J6NlMpo8eaap2mwUZk9BR5E+CPcKbj1UDqeEsmC+EspZ0mxp5X5qhwRcQCaIvtt0E+zlnJelxDxrN0L7C1nGN5CZHrqktlDpGndJGEZGLwjuJGrSsB46vH481sis8+/1D3jGHSmSOQ==\",\"chash\":\"d6c64d28e362f314071b3f9d78ff7494d9cd7177ae0465e772d1840e9f7905d8\",\"mtoken\":\"d232fdcef017520be35fda8d34bc393a\",\"ts\":1750669684982,\"mhash\":\"a0015441fd4c3b6ba427b894b76cb7dd\"}", + "method": "POST" + }); + //resp + {"success":true,"code":0,"data":{"orderId":"692057166164617728","ts":1750669686219}} \ No newline at end of file diff --git a/core/mexc_webclient/session_manager.py b/core/mexc_webclient/session_manager.py new file mode 100644 index 0000000..9b3c412 --- /dev/null +++ b/core/mexc_webclient/session_manager.py @@ -0,0 +1,259 @@ +""" +MEXC Session Manager + +Helper utilities for managing MEXC web sessions and extracting cookies from browser. +""" + +import logging +import json +import re +from typing import Dict, Optional, Any +from pathlib import Path + +logger = logging.getLogger(__name__) + +class MEXCSessionManager: + """ + Helper class for managing MEXC web sessions and extracting browser cookies + """ + + def __init__(self): + self.session_file = Path("mexc_session.json") + + def extract_cookies_from_network_tab(self, cookie_header: str) -> Dict[str, str]: + """ + Extract cookies from browser Network tab cookie header + + Args: + cookie_header: Raw cookie string from browser (copy from Request Headers) + + Returns: + Dictionary of parsed cookies + """ + cookies = {} + + # Remove 'Cookie: ' prefix if present + if cookie_header.startswith('Cookie: '): + cookie_header = cookie_header[8:] + elif cookie_header.startswith('cookie: '): + cookie_header = cookie_header[8:] + + # Split by semicolon and parse each cookie + cookie_pairs = cookie_header.split(';') + + for pair in cookie_pairs: + pair = pair.strip() + if '=' in pair: + name, value = pair.split('=', 1) + cookies[name.strip()] = value.strip() + + logger.info(f"Extracted {len(cookies)} cookies from browser") + return cookies + + def validate_session_cookies(self, cookies: Dict[str, str]) -> bool: + """ + Validate that essential cookies are present for authentication + + Args: + cookies: Dictionary of cookie name-value pairs + + Returns: + bool: True if cookies appear valid for authentication + """ + required_cookies = [ + 'uc_token', # User authentication token + 'u_id', # User ID + 'x-mxc-fingerprint', # Browser fingerprint + 'mexc_fingerprint_visitorId' # Visitor ID + ] + + missing_cookies = [] + for cookie_name in required_cookies: + if cookie_name not in cookies or not cookies[cookie_name]: + missing_cookies.append(cookie_name) + + if missing_cookies: + logger.warning(f"Missing required cookies: {missing_cookies}") + return False + + logger.info("All required cookies are present") + return True + + def save_session(self, cookies: Dict[str, str], metadata: Optional[Dict[str, Any]] = None): + """ + Save session cookies to file for reuse + + Args: + cookies: Dictionary of cookies to save + metadata: Optional metadata about the session + """ + session_data = { + 'cookies': cookies, + 'metadata': metadata or {}, + 'timestamp': int(time.time()) + } + + try: + with open(self.session_file, 'w') as f: + json.dump(session_data, f, indent=2) + logger.info(f"Session saved to {self.session_file}") + except Exception as e: + logger.error(f"Failed to save session: {e}") + + def load_session(self) -> Optional[Dict[str, str]]: + """ + Load session cookies from file + + Returns: + Dictionary of cookies if successful, None otherwise + """ + if not self.session_file.exists(): + logger.info("No saved session found") + return None + + try: + with open(self.session_file, 'r') as f: + session_data = json.load(f) + + cookies = session_data.get('cookies', {}) + timestamp = session_data.get('timestamp', 0) + + # Check if session is too old (24 hours) + import time + if time.time() - timestamp > 24 * 3600: + logger.warning("Saved session is too old (>24h), may be expired") + + if self.validate_session_cookies(cookies): + logger.info("Loaded valid session from file") + return cookies + else: + logger.warning("Loaded session has invalid cookies") + return None + + except Exception as e: + logger.error(f"Failed to load session: {e}") + return None + + def extract_from_curl_command(self, curl_command: str) -> Dict[str, str]: + """ + Extract cookies from a curl command copied from browser + + Args: + curl_command: Complete curl command from browser "Copy as cURL" + + Returns: + Dictionary of extracted cookies + """ + cookies = {} + + # Find cookie header in curl command + cookie_match = re.search(r'-H [\'"]cookie: ([^\'"]+)[\'"]', curl_command, re.IGNORECASE) + if not cookie_match: + cookie_match = re.search(r'--header [\'"]cookie: ([^\'"]+)[\'"]', curl_command, re.IGNORECASE) + + if cookie_match: + cookie_header = cookie_match.group(1) + cookies = self.extract_cookies_from_network_tab(cookie_header) + logger.info(f"Extracted {len(cookies)} cookies from curl command") + else: + logger.warning("No cookie header found in curl command") + + return cookies + + def print_cookie_extraction_guide(self): + """Print instructions for extracting cookies from browser""" + print("\n" + "="*80) + print("MEXC COOKIE EXTRACTION GUIDE") + print("="*80) + print(""" +To extract cookies from your browser for MEXC futures trading: + +METHOD 1: Browser Network Tab +1. Open MEXC futures page and log in: https://www.mexc.com/en-GB/futures/ETH_USDT +2. Open browser Developer Tools (F12) +3. Go to Network tab +4. Try to place a small futures trade (it will fail, but we need the request) +5. Find the request to 'futures.mexc.com' in the Network tab +6. Right-click on the request -> Copy -> Copy request headers +7. Find the 'Cookie:' line and copy everything after 'Cookie: ' + +METHOD 2: Copy as cURL +1. Follow steps 1-5 above +2. Right-click on the futures API request -> Copy -> Copy as cURL +3. Paste the entire cURL command + +METHOD 3: Manual Cookie Extraction +1. While logged into MEXC, press F12 -> Application/Storage tab +2. On the left, expand 'Cookies' -> click on 'https://www.mexc.com' +3. Copy the values for these important cookies: + - uc_token + - u_id + - x-mxc-fingerprint + - mexc_fingerprint_visitorId + +IMPORTANT NOTES: +- Cookies expire after some time (usually 24 hours) +- You must be logged into MEXC futures (not just spot trading) +- Keep your cookies secure - they provide access to your account +- Test with small amounts first + +Example usage: + session_manager = MEXCSessionManager() + + # Method 1: From cookie header + cookie_header = "uc_token=ABC123; u_id=DEF456; ..." + cookies = session_manager.extract_cookies_from_network_tab(cookie_header) + + # Method 2: From cURL command + curl_cmd = "curl 'https://futures.mexc.com/...' -H 'cookie: uc_token=ABC123...'" + cookies = session_manager.extract_from_curl_command(curl_cmd) + + # Save session for reuse + session_manager.save_session(cookies) + """) + print("="*80) + +if __name__ == "__main__": + # When run directly, show the extraction guide + import time + + manager = MEXCSessionManager() + manager.print_cookie_extraction_guide() + + print("\nWould you like to:") + print("1. Load saved session") + print("2. Extract cookies from clipboard") + print("3. Exit") + + choice = input("\nEnter choice (1-3): ").strip() + + if choice == "1": + cookies = manager.load_session() + if cookies: + print(f"\nLoaded {len(cookies)} cookies from saved session") + if manager.validate_session_cookies(cookies): + print("Session appears valid for trading") + else: + print("Warning: Session may be incomplete or expired") + else: + print("No valid saved session found") + + elif choice == "2": + print("\nPaste your cookie header or cURL command:") + user_input = input().strip() + + if user_input.startswith('curl'): + cookies = manager.extract_from_curl_command(user_input) + else: + cookies = manager.extract_cookies_from_network_tab(user_input) + + if cookies and manager.validate_session_cookies(cookies): + print(f"\nSuccessfully extracted {len(cookies)} valid cookies") + save = input("Save session for reuse? (y/n): ").strip().lower() + if save == 'y': + manager.save_session(cookies) + else: + print("Failed to extract valid cookies") + + else: + print("Goodbye!") \ No newline at end of file diff --git a/run_mexc_browser.py b/run_mexc_browser.py new file mode 100644 index 0000000..76eb3dc --- /dev/null +++ b/run_mexc_browser.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +""" +One-Click MEXC Browser Launcher + +Simply run this script to start capturing MEXC futures trading requests. +""" + +import sys +import os + +# Add project root to path +project_root = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, project_root) + +def main(): + """Launch MEXC browser automation""" + print("๐Ÿš€ MEXC Futures Request Interceptor") + print("=" * 50) + print("This will automatically:") + print("โœ… Install ChromeDriver") + print("โœ… Open MEXC futures page") + print("โœ… Capture all API requests") + print("โœ… Extract session cookies") + print("โœ… Save data to JSON files") + print("\nRequirements will be installed automatically if missing.") + + try: + # First try to run the auto browser directly + from core.mexc_webclient.auto_browser import main as run_auto_browser + run_auto_browser() + + except ImportError as e: + print(f"\nโš ๏ธ Import error: {e}") + print("Installing requirements first...") + + # Try to install requirements and run setup + try: + from setup_mexc_browser import main as setup_main + setup_main() + except ImportError: + print("โŒ Could not find setup script") + print("Please run: pip install selenium webdriver-manager") + + except Exception as e: + print(f"โŒ Error: {e}") + print("\nTroubleshooting:") + print("1. Make sure you have Chrome browser installed") + print("2. Check your internet connection") + print("3. Try running: pip install selenium webdriver-manager") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/setup_mexc_browser.py b/setup_mexc_browser.py new file mode 100644 index 0000000..dc9b697 --- /dev/null +++ b/setup_mexc_browser.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +""" +MEXC Browser Setup & Runner + +This script automatically installs dependencies and runs the MEXC browser automation. +""" + +import subprocess +import sys +import os +import importlib + +def check_and_install_requirements(): + """Check and install required packages""" + required_packages = [ + 'selenium', + 'webdriver-manager', + 'requests' + ] + + print("๐Ÿ” Checking required packages...") + + missing_packages = [] + for package in required_packages: + try: + importlib.import_module(package.replace('-', '_')) + print(f"โœ… {package} - already installed") + except ImportError: + missing_packages.append(package) + print(f"โŒ {package} - missing") + + if missing_packages: + print(f"\n๐Ÿ“ฆ Installing missing packages: {', '.join(missing_packages)}") + + for package in missing_packages: + try: + subprocess.check_call([sys.executable, '-m', 'pip', 'install', package]) + print(f"โœ… Successfully installed {package}") + except subprocess.CalledProcessError as e: + print(f"โŒ Failed to install {package}: {e}") + return False + + print("โœ… All requirements satisfied!") + return True + +def run_browser_automation(): + """Run the MEXC browser automation""" + try: + # Import and run the auto browser + from core.mexc_webclient.auto_browser import main as auto_browser_main + auto_browser_main() + except ImportError: + print("โŒ Could not import auto browser module") + print("Make sure core/mexc_webclient/auto_browser.py exists") + except Exception as e: + print(f"โŒ Error running browser automation: {e}") + +def main(): + """Main setup and run function""" + print("๐Ÿš€ MEXC Browser Automation Setup") + print("=" * 40) + + # Check Python version + if sys.version_info < (3, 7): + print("โŒ Python 3.7+ required") + return + + print(f"โœ… Python {sys.version.split()[0]} detected") + + # Install requirements + if not check_and_install_requirements(): + print("โŒ Failed to install requirements") + return + + print("\n๐ŸŒ Starting browser automation...") + print("This will:") + print("โ€ข Download ChromeDriver automatically") + print("โ€ข Open MEXC futures page") + print("โ€ข Capture all trading requests") + print("โ€ข Extract session cookies") + + input("\nPress Enter to continue...") + + # Run the automation + run_browser_automation() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/test_mexc_futures_webclient.py b/test_mexc_futures_webclient.py new file mode 100644 index 0000000..2a1f647 --- /dev/null +++ b/test_mexc_futures_webclient.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python3 +""" +Test MEXC Futures Web Client + +This script demonstrates how to use the MEXC Futures Web Client +for futures trading that isn't supported by their official API. + +IMPORTANT: This requires extracting cookies from your browser session. +""" + +import logging +import sys +import os +import time + +# Add the project root to path +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from core.mexc_webclient import MEXCFuturesWebClient +from core.mexc_webclient.session_manager import MEXCSessionManager + +# Setup logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + +def test_basic_connection(): + """Test basic connection and authentication""" + logger.info("Testing MEXC Futures Web Client") + + # Initialize session manager + session_manager = MEXCSessionManager() + + # Try to load saved session first + cookies = session_manager.load_session() + + if not cookies: + print("\nNo saved session found. You need to extract cookies from your browser.") + session_manager.print_cookie_extraction_guide() + + print("\nPaste your cookie header or cURL command (or press Enter to exit):") + user_input = input().strip() + + if not user_input: + print("No input provided. Exiting.") + return False + + # Extract cookies from user input + if user_input.startswith('curl'): + cookies = session_manager.extract_from_curl_command(user_input) + else: + cookies = session_manager.extract_cookies_from_network_tab(user_input) + + if not cookies: + logger.error("Failed to extract cookies from input") + return False + + # Validate and save session + if session_manager.validate_session_cookies(cookies): + session_manager.save_session(cookies) + logger.info("Session saved for future use") + else: + logger.warning("Extracted cookies may be incomplete") + + # Initialize the web client + client = MEXCFuturesWebClient(cookies) + + if not client.is_authenticated: + logger.error("Failed to authenticate with extracted cookies") + return False + + logger.info("Successfully authenticated with MEXC") + logger.info(f"User ID: {client.user_id}") + logger.info(f"Auth Token: {client.auth_token[:20]}..." if client.auth_token else "No auth token") + + return True + +def test_captcha_verification(client: MEXCFuturesWebClient): + """Test captcha verification system""" + logger.info("Testing captcha verification...") + + # Test captcha for ETH_USDT long position with 200x leverage + success = client.verify_captcha('ETH_USDT', 'openlong', '200X') + + if success: + logger.info("Captcha verification successful") + else: + logger.warning("Captcha verification failed - this may be normal if no position is being opened") + + return success + +def test_position_opening(client: MEXCFuturesWebClient, dry_run: bool = True): + """Test opening a position (dry run by default)""" + if dry_run: + logger.info("DRY RUN: Testing position opening (no actual trade)") + else: + logger.warning("LIVE TRADING: Opening actual position!") + + symbol = 'ETH_USDT' + volume = 1 # Small test position + leverage = 200 + + logger.info(f"Attempting to open long position: {symbol}, Volume: {volume}, Leverage: {leverage}x") + + if not dry_run: + result = client.open_long_position(symbol, volume, leverage) + + if result['success']: + logger.info(f"Position opened successfully!") + logger.info(f"Order ID: {result['order_id']}") + logger.info(f"Timestamp: {result['timestamp']}") + return True + else: + logger.error(f"Failed to open position: {result['error']}") + return False + else: + logger.info("DRY RUN: Would attempt to open position here") + # Test just the captcha verification part + return client.verify_captcha(symbol, 'openlong', f'{leverage}X') + +def interactive_menu(client: MEXCFuturesWebClient): + """Interactive menu for testing different functions""" + while True: + print("\n" + "="*50) + print("MEXC Futures Web Client Test Menu") + print("="*50) + print("1. Test captcha verification") + print("2. Test position opening (DRY RUN)") + print("3. Test position opening (LIVE - BE CAREFUL!)") + print("4. Test position closing (DRY RUN)") + print("5. Show session info") + print("6. Refresh session") + print("0. Exit") + + choice = input("\nEnter choice (0-6): ").strip() + + if choice == "1": + test_captcha_verification(client) + + elif choice == "2": + test_position_opening(client, dry_run=True) + + elif choice == "3": + confirm = input("Are you sure you want to open a LIVE position? (type 'YES' to confirm): ") + if confirm == "YES": + test_position_opening(client, dry_run=False) + else: + print("Cancelled live trading") + + elif choice == "4": + logger.info("DRY RUN: Position closing test") + success = client.verify_captcha('ETH_USDT', 'closelong', '200X') + if success: + logger.info("DRY RUN: Would close position here") + else: + logger.warning("Captcha verification failed for position closing") + + elif choice == "5": + print(f"\nSession Information:") + print(f"Authenticated: {client.is_authenticated}") + print(f"User ID: {client.user_id}") + print(f"Auth Token: {client.auth_token[:20]}..." if client.auth_token else "None") + print(f"Fingerprint: {client.fingerprint}") + print(f"Visitor ID: {client.visitor_id}") + + elif choice == "6": + session_manager = MEXCSessionManager() + session_manager.print_cookie_extraction_guide() + + elif choice == "0": + print("Goodbye!") + break + + else: + print("Invalid choice. Please try again.") + +def main(): + """Main test function""" + print("MEXC Futures Web Client Test") + print("WARNING: This is experimental software for futures trading") + print("Use at your own risk and test with small amounts first!") + + # Test basic connection + if not test_basic_connection(): + logger.error("Failed to establish connection. Exiting.") + return + + # Create client with loaded session + session_manager = MEXCSessionManager() + cookies = session_manager.load_session() + + if not cookies: + logger.error("No valid session available") + return + + client = MEXCFuturesWebClient(cookies) + + if not client.is_authenticated: + logger.error("Authentication failed") + return + + # Show interactive menu + interactive_menu(client) + +if __name__ == "__main__": + main() \ No newline at end of file