-
-
Notifications
You must be signed in to change notification settings - Fork 38
Email/password SSID malformation #79
Copy link
Copy link
Open
Labels
bugSomething isn't workingSomething isn't workingenhancementNew feature or requestNew feature or requestgood first issueGood for newcomersGood for newcomers
Description
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
- Copied/pasted all the code for email/password login
- Do the regular initialization with SSID provided by loginAndGetSSID function.
- 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.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't workingenhancementNew feature or requestNew feature or requestgood first issueGood for newcomersGood for newcomers