Skip to content

Email/password SSID malformation #79

@potionsnotusedlol

Description

@potionsnotusedlol

Description

With the code provided in examples, the SSID that is being generated doesn't fit the new standards (no isOptimized field) and doesn't work with PocketOptionAsync() function.

Also (ik that doesn't refer to you) they added a captcha on their site so that it cannot be automated.

Reproduction

  1. Copied/pasted all the code for email/password login
  2. Do the regular initialization with SSID provided by loginAndGetSSID function.
  3. ValueError: PocketOptionError, Core error: Failed to parse ssid: JSON parsing error: invalid type: integer 42, expected struct Demo at line 1 column 2

Expected Behavior

The auth should be successful, no JSON errors.

Context

  • OS: macOS Tahoe 26.4
  • Python Version: 3.12
  • Library Version: Latest
  • Installation: python -m pip... from uv-operated venv

Evidence

Code Sample

import asyncio
import json
from colorama import Fore, init
# import pandas as pd
from BinaryOptionsToolsV2.pocketoption import PocketOptionAsync
from BinaryOptionsToolsV2.tracing import Logger
from Config import config
# from random import uniform
# Imports for Selenium login
try:
    import urllib.parse
    from selenium import webdriver
    from selenium.webdriver.chrome.service import Service
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions
    from selenium.webdriver.support.ui import WebDriverWait
    from webdriver_manager.chrome import ChromeDriverManager

    SELENIUM_AVAILABLE = True
except ImportError:
    SELENIUM_AVAILABLE = False

    print(Fore.RED + "Selenium libraries not found. Login with email/password will not be available.")
    print(Fore.YELLOW + "Please install them: python -m pip install selenium webdriver-manager")

init()

red = Fore.RED
green = Fore.GREEN
yellow = Fore.YELLOW

def formatSessionForPocketOptionAuth(
        input_session_string: str,
        is_demo: int = 0,
        uid: int = some_uid,
        platform: int = 2,
        is_fast_history: bool = False
) -> str:
    """
    Formats the raw session string into the specific auth structure required by PocketOption.
    Example target: 42["auth",{"session":"PHP_SERIALIZED_STRING_WITH_HASH","isDemo":0,"uid":126609617,"platform":2,"isFastHistory":false}]
    The input_session_string is the PHP_SERIALIZED_STRING_WITH_HASH part.
    """
    # The input_session_string is the raw PHP serialized string
    # json.dumps will handle escaping the double quotes within it correctly for JSON.

    auth_payload = {
        "session": input_session_string,
        "isDemo": is_demo,
        "uid": uid,
        "platform": platform,
        "isFastHistory": is_fast_history
    }

    # Convert the payload dictionary to a JSON string
    # json.dumps will correctly handle boolean to lowercase true/false
    # and ensure valid JSON syntax, including excaping quotes in input_session_string to \"
    auth_payload_json_string = json.dumps(auth_payload, separators=(",", ":"))
    # construct the final target string
    target_string = f'42["auth":{auth_payload_json_string}]'

    print(yellow + f"Formatted SSID: {target_string}")

    return target_string

async def loginAndGetSSID(email_str: str, password_str: str) -> str | None:
    """Handles Selenium login and returns the formatted SSID string or None on failure"""
    if not SELENIUM_AVAILABLE:
        print(red + "Selenium libraries are not available. Cannot login with email/password.")

        return None
    
    # this inner function contains the blocking Selenium code
    def getSSIDBlocking(email_val : str, password_val: str) -> str | None:
        print(yellow + "Initializing WebDriver for PocketOption login...")

        driver = None # Initialize driver to None for finally block

        try:
            chrome_options = webdriver.ChromeOptions()
            # Common options to try for stability, especially in automated/headless environments
            chrome_options.add_argument("--no-sandbox")
            chrome_options.add_argument("--disable-dev-shm-usage")
            chrome_options.add_argument("--disable-gpu") # often recommended for headless
            chrome_options.add_argument("start-maximized")
            chrome_options.add_argument("disable-infobars")
            chrome_options.add_argument("--disable-extensions")

            service = Service(ChromeDriverManager().install())
            driver = webdriver.Chrome(service=service, options=chrome_options)

            print(yellow + "Navigating to PocketOption login page (po.trage/login)...")
            driver.get("https://po.trade/login") # Official PocketOption URL for login

            # Wait for email field to be present and interactable
            email_field = WebDriverWait(driver, 30).until(expected_conditions.element_to_be_clickable((By.NAME, "email")))

            print(yellow + "Login page loaded. Entering credentials...")
            email_field.click()
            email_field.clear()
            email_field.send_keys(email_val)

            password_field = driver.find_element(By.NAME, "password")
            password_field.click()
            password_field.clear()
            password_field.send_keys(password_val)

            # Try to find the login button various robust selectors
            login_button_selectors = [
                "button[type='submit'].btn.btn-green-light", # From original user example
                "//button[normalize-space(translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'))='log in' and @type='submit']", # Case-insensitive XPath
                "//button[normalize-space(translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'))='login' and @type='submit']", # Case-insensitive XPath
                "form button[type='submit']" # General form submit button
            ]
            login_button = None

            for idx, selector in enumerate(login_button_selectors):
                try:
                    by_method = (By.XPATH if selector.startswith("//") else By.CSS_SELECTOR)
                    login_button = WebDriverWait(driver, 5).until(expected_conditions.element_to_be_clickable((by_method, selector)))

                    if login_button:
                        print(yellow + f"Login button found with selector #{idx + 1} ('{selector}').")

                        break
                except Exception:
                    if idx == len(login_button_selectors) - 1:
                        print(red + f"Selector '{selector}' not found or not clickable.")

            if not login_button:
                print(red + "Could not find a clickable login button.")
                driver.save_screenshot("debug_login_button_not_found.png")

                return None
            
            login_button.click()
            print(yellow + "Login submitted. Waiting for page load/redirection (up to 60 s)...")
            # Wait for a condition that indicates successful login
            # e.g., URL change from /login, or presence of a dashboard/cabinet element
            WebDriverWait(driver, 60).until(
                lambda d: (
                    d.current_url != "https://po.trade/login/"
                    and (
                        expected_conditions.url_contains("cabinet")(d)
                        or expected_conditions.presence_of_element_located((By.ID, "crm-widget-wrapper"))(d)
                        or expected_conditions.presence_of_element_located((By.CSS_SELECTOR, ".is_real"))(d)
                        or expected_conditions.presence_of_element_located((By.CSS_SELECTOR, ".is_demo"))(d)
                    )
                )
            )
            print(green + f"Login appears successful. Current URL: {driver.current_url}")
            print(yellow + "Retrieving session cookie(s)...")

            cookies = driver.get_cookies()
            session_token = None
            # Common session cookie names for PHP sites like PocketOption. PHPSESSID is very common.
            possible_session_cookie_names = [
                "PHPSESSID",
                "ci_session",
                "po_session",
                "SID",
                "ssid"
            ]

            for cookie in cookies:
                if cookie["name"] in possible_session_cookie_names:
                    session_token = cookie["value"]
                    
                    print(green + f"Found session cookie: {cookie['name']} = {session_token[:30]}...")

                    break
                    
            if not session_token:
                print(red + "Could not find a known session cookir (e.g., PHPSESSID, ci_session) after login.")
                print(yellow + "Available cookies:")

                for cookie_idx, cookie_data in enumerate(cookies):
                    print(f"    {cookie_idx + 1}.   {cookie_data['name']}:  {cookie_data['value'][:40]}...")
                    driver.save_screenshot("debug_no_session_cookie.png")

                    return None
                
            # URL decode the raw cookie value
            decoded_cookie_value = urllib.parse.unquote(session_token)
            
            print(green + f"Raw session cookie value (decoded): {decoded_cookie_value[:60]}...")

            # Format this decoded cookie value into the target SSID structure
            # !! IMPORTANT: The defaults for isDemo, uid in formatSessionForPocketOptionAuth are from user's example.
            # !! These might need to be dynamically determined (scraped) by Selenuim from the page if they vary.
            # Example: a basic dynamic check for demo account (might need refinement)
            is_demo_account = 0 # Default to real

            try:
                if driver.find_elements(By.CSS_SELECTOR, ".is_demo_balance") or "demo" in driver.current_url.lower():
                    is_demo_account = 1
                    print(yellow + "Detected DEMO account from page elements/URL.")
            except Exception:
                pass # Ignore if elements not found, keep default

            # UID is harder to get dynamically and is critical. Using hardcoded from example for now.
            # User MUST ensure the UID in formatSessionForPocketOptionAuth is correct for their account.
            formatted_ssid = formatSessionForPocketOptionAuth(decoded_cookie_value, is_demo=is_demo_account)

            print(green + f"Formatted SSID for PocketOptionAsync: {formatted_ssid[:80]}...")

            return formatted_ssid
        except Exception as e_selenium:
            print(red + f"Error during Selenium login operations: {e_selenium}")

            import traceback

            traceback.print_exc()

            if driver:
                try:
                    driver.save_screenshot("selenium_login_error.png")
                    print(yellow + "Screenshot saved as selenium_login_error.png")
                    print(yellow + f"Current URL at error: {driver.current_url if driver.current_url else 'N/A'}")
                except Exception as e_screenshot:
                    print(red + f"Could not save screenshot: {e_screenshot}")

            return None
        finally:
            if driver:
                print(yellow + "Closing WebDriver.")
                driver.quit()

    # Run the Selenium function in a separate thread to avoid blocking asyncio event loop
    return await asyncio.to_thread(getSSIDBlocking, email_str, password_str)

async def alternate_main():
    ssid = await loginAndGetSSID("some_address@some_domain.some_space", "potential_password")
    api = PocketOptionAsync(ssid)

    await asyncio.sleep(5)

    try:
        candles = await api.get_candles("AEDCNY_otc", 30, 480)

        print(candles)
    except Exception as e:
        print(f"Not handled, {e}")

Error Logs

<All the WebDriver operations successful>
Closing WebDriver.
Traceback (most recent call last):
  File "/Users/user/Documents/programming/working/stock_watcher/test.py", line 329, in <module>
    asyncio.run(alternate_main())
  File "/opt/homebrew/Cellar/python@3.12/3.12.12/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/runners.py", line 195, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.12/3.12.12/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.12/3.12.12/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/base_events.py", line 691, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/Users/user/Documents/programming/working/stock_watcher/test.py", line 267, in alternate_main
    api = PocketOptionAsync(ssid)
          ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/user/Documents/programming/working/stock_watcher/.venv/lib/python3.12/site-packages/BinaryOptionsToolsV2/pocketoption/asynchronous.py", line 237, in __init__
    self.client: "RawPocketOption" = RawPocketOption.new_with_config(ssid, self.config.pyconfig)
                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: PocketOptionError, Core error: Failed to parse ssid: JSON parsing error: invalid type: integer `42`, expected struct Demo at line 1 column 2

Additional Information

The method is not so useful for automation now bc they added a captcha on the login screen.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingenhancementNew feature or requestgood first issueGood for newcomers

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions