diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 0000000..e59bc73 --- /dev/null +++ b/tools/README.md @@ -0,0 +1,126 @@ +# PocketOption Tools + +This directory contains utility tools for working with the PocketOption API. + +## get_ssid.py - SSID Extraction Tool + +This tool automatically extracts your PocketOption session ID (SSID) by monitoring WebSocket traffic during login. + +### What is SSID? + +SSID (Session ID) is the authentication token required to use the PocketOption API. It's a string that looks like: + +``` +42["auth",{"session":"your-session-here","isDemo":1,"uid":12345,"platform":1}] +``` + +### Prerequisites + +Before running the tool, make sure you have the required dependencies installed: + +```bash +pip install -r requirements.txt +``` + +The tool requires: +- `selenium>=4.0.0` +- `webdriver-manager>=4.0.0` +- Chrome browser installed on your system + +### How to Use + +1. **Navigate to the tools directory:** + ```bash + cd tools + ``` + +2. **Run the script:** + ```bash + python get_ssid.py + ``` + +3. **Follow the on-screen instructions:** + - A Chrome browser window will open automatically + - Navigate to the PocketOption login page + - Log in with your credentials + - Wait for the automatic redirection to the trading cabinet + - The script will automatically extract and save your SSID + +4. **Find your SSID:** + - The SSID will be saved to a `.env` file in the current directory + - You can now use this SSID in your API scripts + +### Expected Output + +When successful, you'll see: + +``` +2025-12-25 10:30:15 - INFO - ================================================================================ +2025-12-25 10:30:15 - INFO - PocketOption SSID Extractor Tool +2025-12-25 10:30:15 - INFO - ================================================================================ +2025-12-25 10:30:15 - INFO - INSTRUCTIONS: +2025-12-25 10:30:15 - INFO - 1. A Chrome browser will open shortly +2025-12-25 10:30:15 - INFO - 2. Please log in to PocketOption with your credentials +... +2025-12-25 10:31:45 - INFO - Found valid SSID string in WebSocket payload +2025-12-25 10:31:45 - INFO - ================================================================================ +2025-12-25 10:31:45 - INFO - SUCCESS! SSID successfully extracted and saved to .env file. +2025-12-25 10:31:45 - INFO - You can now use this SSID in your PocketOption API scripts. +2025-12-25 10:31:45 - INFO - ================================================================================ +``` + +### Troubleshooting + +#### "SSID string pattern not found in WebSocket logs" + +If you see this warning, try the following: + +1. **Run the script again** - Sometimes the WebSocket connection timing can vary +2. **Manual extraction method:** + - Open PocketOption in Chrome + - Press F12 to open Developer Tools + - Go to the "Network" tab + - Filter by "WS" (WebSocket) + - Log in and navigate to a trading page + - Look for messages containing `42["auth"` + - Copy the complete message including the `42["auth",{...}]` format + - Save it to your `.env` file as: `SSID="your-copied-message"` + +#### Browser doesn't open + +- Make sure Chrome is installed on your system +- The script uses `webdriver-manager` which automatically downloads ChromeDriver +- Check your internet connection + +#### Other issues + +- Make sure all dependencies are installed: `pip install -r requirements.txt` +- Check the console output for specific error messages +- Try running the script with Python 3.8 or higher + +### Using Your SSID + +Once you have your SSID, you can use it in your API scripts: + +```python +from pocketoptionapi_async import AsyncPocketOptionClient +import asyncio + +async def main(): + # Load SSID from environment or paste it directly + SSID = "42[\"auth\",{\"session\":\"...\",\"isDemo\":1,\"uid\":12345,\"platform\":1}]" + + client = AsyncPocketOptionClient(SSID, is_demo=True) + await client.connect() + + balance = await client.get_balance() + print(f"Balance: {balance.balance} {balance.currency}") + + await client.disconnect() + +asyncio.run(main()) +``` + +### Security Note + +Your SSID is sensitive information that grants access to your PocketOption account. Keep it secure and never share it publicly. The `.env` file should be added to `.gitignore` to prevent accidental commits. diff --git a/tools/get_ssid.py b/tools/get_ssid.py index cf0efb5..dca6de3 100644 --- a/tools/get_ssid.py +++ b/tools/get_ssid.py @@ -9,11 +9,11 @@ from driver import get_driver # Configure logging for this script to provide clear, structured output. -# Logs will be directed to standard output, making them compatible with containerization -# and centralized log collection systems. +# Using a simpler format for better readability by end users. logging.basicConfig( level=logging.INFO, - format='{"timestamp": "%(asctime)s", "level": "%(levelname)s", "module": "%(name)s", "message": "%(message)s"}', + format='%(asctime)s - %(levelname)s - %(message)s', + datefmt='%Y-%m-%d %H:%M:%S' ) logger = logging.getLogger(__name__) @@ -57,19 +57,39 @@ def get_pocketoption_ssid(): Automates the process of logging into PocketOption, navigating to a specific cabinet page, and then scraping WebSocket traffic to extract the session ID (SSID). The extracted SSID is then saved to the .env file. + + Instructions: + 1. Run this script + 2. A Chrome browser will open and navigate to PocketOption login page + 3. Log in manually with your credentials + 4. Wait for the script to automatically extract your SSID + 5. The SSID will be saved to .env file in the current directory """ driver = None try: + logger.info("=" * 80) + logger.info("PocketOption SSID Extractor Tool") + logger.info("=" * 80) + logger.info("INSTRUCTIONS:") + logger.info("1. A Chrome browser will open shortly") + logger.info("2. Please log in to PocketOption with your credentials") + logger.info("3. Wait for automatic redirection to the trading cabinet") + logger.info("4. The script will extract your SSID automatically") + logger.info("=" * 80) + # Initialize the Selenium WebDriver using the helper function from driver.py. # This ensures the browser profile is persistent for easier logins. driver = get_driver("chrome") login_url = "https://pocketoption.com/en/login" cabinet_base_url = "https://pocketoption.com/en/cabinet" target_cabinet_url = "https://pocketoption.com/en/cabinet/demo-quick-high-low/" - # Regex to capture the entire "42[\"auth\",{...}]" string. - # This pattern is designed to be robust and capture the full authentication message, - # regardless of the specific content of the 'session' field (e.g., simple string or serialized PHP array). - ssid_pattern = r'(42\["auth",\{"session":"[^"]+","isDemo":\d+,"uid":\d+,"platform":\d+,"isFastHistory":(?:true|false)\}\])' + + # Flexible regex pattern to capture auth messages in various formats + # This pattern handles: + # - Optional isFastHistory field + # - Any field order in the JSON object + # - Various session string formats + ssid_pattern = r'42\["auth",(\{(?:[^{}]|\{[^}]*\})*\})\]' logger.info(f"Navigating to login page: {login_url}") driver.get(login_url) @@ -77,6 +97,7 @@ def get_pocketoption_ssid(): # Wait indefinitely for the user to manually log in and be redirected to the cabinet base page. # This uses an explicit wait condition to check if the current URL contains the cabinet_base_url. logger.info(f"Waiting for user to login and redirect to {cabinet_base_url}...") + logger.info("Please complete the login in the browser window...") WebDriverWait(driver, 9999).until(EC.url_contains(cabinet_base_url)) logger.info("Login successful. Redirected to cabinet base page.") @@ -89,9 +110,10 @@ def get_pocketoption_ssid(): WebDriverWait(driver, 60).until(EC.url_contains(target_cabinet_url)) logger.info("Successfully navigated to the target cabinet page.") - # Give the page some time to load all WebSocket connections and messages after redirection. - # This delay helps ensure that the relevant WebSocket frames are captured in the logs. - time.sleep(5) + # Give the page more time to establish WebSocket connections and capture auth messages + # Increased from 5 to 10 seconds to ensure auth messages are captured + logger.info("Waiting for WebSocket connections to establish...") + time.sleep(10) # Retrieve performance logs which include network requests and WebSocket frames. # These logs are crucial for capturing the raw WebSocket messages. @@ -104,35 +126,80 @@ def get_pocketoption_ssid(): logger.info(f"Collected {len(performance_logs)} performance log entries.") found_full_ssid_string = None + websocket_frames_found = 0 + auth_messages_found = 0 + # Iterate through the performance logs to find WebSocket frames. for entry in performance_logs: - message = json.loads(entry["message"]) - # Check if the log entry is a WebSocket frame (either sent or received) - # and contains the desired payload data. - if ( - message["message"]["method"] == "Network.webSocketFrameReceived" - or message["message"]["method"] == "Network.webSocketFrameSent" - ): - payload_data = message["message"]["params"]["response"]["payloadData"] - # Attempt to find the full SSID string using the defined regex pattern. - match = re.search(ssid_pattern, payload_data) - if match: - # Capture the entire matched group as the full SSID string. - found_full_ssid_string = match.group(1) - logger.info( - f"Found full SSID string in WebSocket payload: {found_full_ssid_string}" - ) - # Break after finding the first match as it's likely the correct one. - break + try: + message = json.loads(entry["message"]) + # Check if the log entry is a WebSocket frame (either sent or received) + # and contains the desired payload data. + if ( + message["message"]["method"] == "Network.webSocketFrameReceived" + or message["message"]["method"] == "Network.webSocketFrameSent" + ): + websocket_frames_found += 1 + payload_data = message["message"]["params"]["response"]["payloadData"] + + # Check if this is an auth-related message + if '"auth"' in payload_data: + auth_messages_found += 1 + logger.debug(f"Found auth message: {payload_data[:200]}...") + + # Attempt to find the full SSID string using the defined regex pattern. + match = re.search(ssid_pattern, payload_data) + if match: + # Reconstruct the full SSID string + json_part = match.group(1) + found_full_ssid_string = f'42["auth",{json_part}]' + + # Validate that it contains required fields + try: + data = json.loads(json_part) + if "session" in data and "uid" in data: + logger.info( + f"Found valid SSID string in WebSocket payload" + ) + logger.info(f"SSID preview: 42[\"auth\",{{\"session\":\"***\",\"uid\":{data.get('uid')},\"isDemo\":{data.get('isDemo', 'N/A')},...}}]") + # Break after finding the first valid match + break + else: + logger.warning(f"Found auth message but missing required fields: {list(data.keys())}") + found_full_ssid_string = None + except json.JSONDecodeError as e: + logger.warning(f"Found auth pattern but invalid JSON: {e}") + found_full_ssid_string = None + except Exception as e: + logger.debug(f"Error processing log entry: {e}") + continue + + logger.info(f"Statistics: Found {websocket_frames_found} WebSocket frames, {auth_messages_found} auth messages") if found_full_ssid_string: # Save the extracted full SSID string to the .env file. save_to_env("SSID", found_full_ssid_string) - logger.info("Full SSID string successfully extracted and saved to .env.") + logger.info("=" * 80) + logger.info("SUCCESS! SSID successfully extracted and saved to .env file.") + logger.info("You can now use this SSID in your PocketOption API scripts.") + logger.info("=" * 80) else: + logger.warning("=" * 80) logger.warning( - "Full SSID string pattern not found in WebSocket logs after login." + "SSID string pattern not found in WebSocket logs after login." ) + logger.warning("Possible reasons:") + logger.warning("1. WebSocket connection was not established yet (try running again)") + logger.warning("2. You may need to navigate to a trading page manually") + logger.warning("3. The auth message format has changed") + logger.warning("") + logger.warning("Alternative method to get SSID:") + logger.warning("1. Open PocketOption in Chrome") + logger.warning("2. Open Developer Tools (F12)") + logger.warning("3. Go to Network tab and filter by 'WS' (WebSocket)") + logger.warning("4. Look for messages containing '42[\"auth\"'") + logger.warning("5. Copy the complete message including the 42[\"auth\",{...}] format") + logger.warning("=" * 80) except Exception as e: logger.error(f"An error occurred: {e}", exc_info=True)