simplex 1.2.36__tar.gz → 1.2.38__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of simplex might be problematic. Click here for more details.
- {simplex-1.2.36 → simplex-1.2.38}/PKG-INFO +1 -1
- {simplex-1.2.36 → simplex-1.2.38}/setup.py +1 -1
- {simplex-1.2.36 → simplex-1.2.38}/simplex/cli.py +7 -6
- {simplex-1.2.36 → simplex-1.2.38}/simplex/simplex.py +155 -26
- {simplex-1.2.36 → simplex-1.2.38}/simplex.egg-info/PKG-INFO +1 -1
- {simplex-1.2.36 → simplex-1.2.38}/LICENSE +0 -0
- {simplex-1.2.36 → simplex-1.2.38}/README.md +0 -0
- {simplex-1.2.36 → simplex-1.2.38}/pyproject.toml +0 -0
- {simplex-1.2.36 → simplex-1.2.38}/setup.cfg +0 -0
- {simplex-1.2.36 → simplex-1.2.38}/simplex/__init__.py +0 -0
- {simplex-1.2.36 → simplex-1.2.38}/simplex/deploy/__init__.py +0 -0
- {simplex-1.2.36 → simplex-1.2.38}/simplex/deploy/push.py +0 -0
- {simplex-1.2.36 → simplex-1.2.38}/simplex.egg-info/SOURCES.txt +0 -0
- {simplex-1.2.36 → simplex-1.2.38}/simplex.egg-info/dependency_links.txt +0 -0
- {simplex-1.2.36 → simplex-1.2.38}/simplex.egg-info/entry_points.txt +0 -0
- {simplex-1.2.36 → simplex-1.2.38}/simplex.egg-info/requires.txt +0 -0
- {simplex-1.2.36 → simplex-1.2.38}/simplex.egg-info/top_level.txt +0 -0
- {simplex-1.2.36 → simplex-1.2.38}/tests/test.py +0 -0
|
@@ -63,13 +63,14 @@ def login(website, proxies):
|
|
|
63
63
|
website = 'https://' + website
|
|
64
64
|
|
|
65
65
|
animated_print(f"[SIMPLEX] Creating login session for {website}")
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
|
|
67
|
+
fileName = simplex.create_login_session(website)
|
|
68
|
+
|
|
69
|
+
if fileName:
|
|
70
|
+
animated_print(f"[SIMPLEX] Login session created and saved to {fileName}")
|
|
71
71
|
else:
|
|
72
|
-
print(f"{Fore.YELLOW}[SIMPLEX] Warning:
|
|
72
|
+
print(f"{Fore.YELLOW}[SIMPLEX] Warning: Session data could not be saved{Style.RESET_ALL}")
|
|
73
|
+
|
|
73
74
|
except Exception as e:
|
|
74
75
|
raise click.ClickException(str(e))
|
|
75
76
|
|
|
@@ -1,9 +1,58 @@
|
|
|
1
1
|
import requests
|
|
2
2
|
import atexit
|
|
3
3
|
from typing import Optional
|
|
4
|
+
from playwright.sync_api import sync_playwright
|
|
5
|
+
import os
|
|
6
|
+
import json
|
|
7
|
+
|
|
4
8
|
BASE_URL = "https://api.simplex.sh"
|
|
5
9
|
|
|
6
|
-
|
|
10
|
+
class Playwright:
|
|
11
|
+
def __init__(self, simplex_instance):
|
|
12
|
+
self.simplex = simplex_instance
|
|
13
|
+
|
|
14
|
+
def click(self, locator: str, locator_type: str, exact: Optional[bool] = False,
|
|
15
|
+
element_index: Optional[str] = None, nth_index: Optional[int] = None,
|
|
16
|
+
locator_options: Optional[dict] = None):
|
|
17
|
+
|
|
18
|
+
if element_index and element_index not in ["first", "last", "nth"]:
|
|
19
|
+
raise ValueError("element_index must be 'first', 'last', or 'nth'")
|
|
20
|
+
|
|
21
|
+
if element_index=="nth" and not nth_index:
|
|
22
|
+
raise ValueError("nth_index is required when element_index is 'nth'")
|
|
23
|
+
|
|
24
|
+
data = {
|
|
25
|
+
'session_id': self.simplex.session_id,
|
|
26
|
+
'locator': locator,
|
|
27
|
+
'locator_type': locator_type,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if element_index:
|
|
31
|
+
data['element_index'] = element_index
|
|
32
|
+
data['nth_index'] = nth_index
|
|
33
|
+
|
|
34
|
+
if exact:
|
|
35
|
+
data['exact'] = exact
|
|
36
|
+
|
|
37
|
+
if locator_options:
|
|
38
|
+
data['locator_options'] = json.dumps(locator_options)
|
|
39
|
+
|
|
40
|
+
response = requests.post(
|
|
41
|
+
f"{BASE_URL}/playwright/click",
|
|
42
|
+
headers={
|
|
43
|
+
'x-api-key': self.simplex.api_key
|
|
44
|
+
},
|
|
45
|
+
data=data
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
if 'succeeded' not in response.json():
|
|
49
|
+
print(response.json())
|
|
50
|
+
raise ValueError(f"It looks like the click action with playwright failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
51
|
+
|
|
52
|
+
if response.json()["succeeded"]:
|
|
53
|
+
return
|
|
54
|
+
else:
|
|
55
|
+
raise ValueError(f"Failed to click element: {response.json()['error']}")
|
|
7
56
|
|
|
8
57
|
class Simplex:
|
|
9
58
|
def __init__(self, api_key: str):
|
|
@@ -11,6 +60,8 @@ class Simplex:
|
|
|
11
60
|
self.session_id = None
|
|
12
61
|
atexit.register(self.close_session)
|
|
13
62
|
|
|
63
|
+
self.playwright = Playwright(self)
|
|
64
|
+
|
|
14
65
|
def close_session(self):
|
|
15
66
|
if not self.session_id:
|
|
16
67
|
return
|
|
@@ -30,13 +81,33 @@ class Simplex:
|
|
|
30
81
|
raise ValueError(f"Failed to close session: {response.json()['error']}")
|
|
31
82
|
|
|
32
83
|
def create_session(self, show_in_console: Optional[bool] = True, proxies: Optional[bool] = True, session_data: Optional[str] = None):
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
84
|
+
if session_data:
|
|
85
|
+
try:
|
|
86
|
+
# Try to parse as JSON string first
|
|
87
|
+
session_data_dict = json.loads(session_data)
|
|
88
|
+
except json.JSONDecodeError:
|
|
89
|
+
# If parsing fails, treat as file path
|
|
90
|
+
try:
|
|
91
|
+
with open(session_data, 'r') as f:
|
|
92
|
+
session_data_dict = json.load(f)
|
|
93
|
+
except Exception as e:
|
|
94
|
+
raise ValueError(f"Failed to load session data. Input must be valid JSON string or path to JSON file. Error: {str(e)}")
|
|
95
|
+
print(session_data_dict)
|
|
96
|
+
response = requests.post(
|
|
97
|
+
f"{BASE_URL}/create_session",
|
|
98
|
+
headers={
|
|
99
|
+
'x-api-key': self.api_key
|
|
100
|
+
},
|
|
101
|
+
data={'proxies': proxies, 'session_data': json.dumps(session_data_dict)}
|
|
102
|
+
)
|
|
103
|
+
else:
|
|
104
|
+
response = requests.post(
|
|
105
|
+
f"{BASE_URL}/create_session",
|
|
106
|
+
headers={
|
|
107
|
+
'x-api-key': self.api_key
|
|
108
|
+
},
|
|
109
|
+
data={'proxies': proxies}
|
|
110
|
+
)
|
|
40
111
|
# Check for non-200 status code
|
|
41
112
|
if response.status_code != 200:
|
|
42
113
|
raise ValueError(f"Create session request failed with status code {response.status_code}: {response.text}")
|
|
@@ -50,7 +121,7 @@ class Simplex:
|
|
|
50
121
|
if show_in_console:
|
|
51
122
|
print(f"Livestream URL: {livestream_url}")
|
|
52
123
|
|
|
53
|
-
return livestream_url
|
|
124
|
+
return self.session_id, livestream_url
|
|
54
125
|
|
|
55
126
|
def goto(self, url: str, cdp_url: str = None):
|
|
56
127
|
if not cdp_url and not self.session_id:
|
|
@@ -240,12 +311,12 @@ class Simplex:
|
|
|
240
311
|
else:
|
|
241
312
|
data['session_id'] = self.session_id
|
|
242
313
|
|
|
243
|
-
response = requests.
|
|
314
|
+
response = requests.post(
|
|
244
315
|
f"{BASE_URL}/extract-text",
|
|
245
316
|
headers={
|
|
246
317
|
'x-api-key': self.api_key
|
|
247
318
|
},
|
|
248
|
-
|
|
319
|
+
data=data
|
|
249
320
|
)
|
|
250
321
|
if 'succeeded' not in response.json():
|
|
251
322
|
raise ValueError(f"It looks like the extract_text action failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
@@ -329,20 +400,78 @@ class Simplex:
|
|
|
329
400
|
else:
|
|
330
401
|
raise ValueError(f"Failed to wait: {response.json()['error']}")
|
|
331
402
|
|
|
332
|
-
def create_login_session(self, url: str,
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
403
|
+
def create_login_session(self, url: str, save_directory: Optional[str] = None):
|
|
404
|
+
def get_website_name(url: str) -> str:
|
|
405
|
+
"""Extract website name from URL"""
|
|
406
|
+
from urllib.parse import urlparse
|
|
407
|
+
netloc = urlparse(url).netloc
|
|
408
|
+
# Remove www. if present
|
|
409
|
+
if netloc.startswith('www.'):
|
|
410
|
+
netloc = netloc[4:]
|
|
411
|
+
return netloc.replace(".", "_")
|
|
412
|
+
|
|
413
|
+
with sync_playwright() as p:
|
|
414
|
+
browser = p.chromium.launch(
|
|
415
|
+
channel="chrome",
|
|
416
|
+
headless=False,
|
|
417
|
+
args=[
|
|
418
|
+
'--disable-blink-features=AutomationControlled',
|
|
419
|
+
'--disable-automation',
|
|
420
|
+
],
|
|
421
|
+
ignore_default_args=['--enable-automation']
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
# Create context and pages
|
|
425
|
+
context = browser.new_context()
|
|
426
|
+
main_page = context.new_page()
|
|
427
|
+
main_page.goto(url)
|
|
428
|
+
|
|
429
|
+
# Create control page in same context
|
|
430
|
+
control_page = context.new_page()
|
|
431
|
+
control_page.set_content("""
|
|
432
|
+
<html>
|
|
433
|
+
<body style="display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f5f5f5;">
|
|
434
|
+
<button id="capture-btn" style="
|
|
435
|
+
padding: 20px 40px;
|
|
436
|
+
font-size: 18px;
|
|
437
|
+
cursor: pointer;
|
|
438
|
+
background-color: #2196F3;
|
|
439
|
+
color: white;
|
|
440
|
+
border: none;
|
|
441
|
+
border-radius: 5px;
|
|
442
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
|
443
|
+
">Click here when logged in to capture session</button>
|
|
444
|
+
</body>
|
|
445
|
+
</html>
|
|
446
|
+
""")
|
|
447
|
+
|
|
448
|
+
# Wait for button click using Playwright's wait_for_event
|
|
449
|
+
control_page.wait_for_selector('#capture-btn')
|
|
450
|
+
control_page.evaluate("""
|
|
451
|
+
() => {
|
|
452
|
+
return new Promise((resolve) => {
|
|
453
|
+
document.getElementById('capture-btn').onclick = () => {
|
|
454
|
+
resolve(true);
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
""")
|
|
459
|
+
|
|
460
|
+
# Use context.storage_state() instead of browser.storage_state()
|
|
461
|
+
storage = context.storage_state()
|
|
462
|
+
if save_directory:
|
|
463
|
+
filename = os.path.join(save_directory, get_website_name(url) + "_session_data.json")
|
|
464
|
+
else:
|
|
465
|
+
filename = get_website_name(url) + "_session_data.json"
|
|
466
|
+
|
|
467
|
+
with open(filename, 'w') as f:
|
|
468
|
+
json.dump(storage, f, indent=2)
|
|
469
|
+
|
|
470
|
+
print(f"Session data saved to '{filename}'")
|
|
471
|
+
|
|
472
|
+
browser.close()
|
|
473
|
+
|
|
474
|
+
return filename
|
|
346
475
|
|
|
347
476
|
def restore_login_session(self, session_data: str, cdp_url: str = None):
|
|
348
477
|
"""
|
|
@@ -462,4 +591,4 @@ class Simplex:
|
|
|
462
591
|
if response_json['succeeded']:
|
|
463
592
|
return response_json['exists'], response_json['reasoning']
|
|
464
593
|
else:
|
|
465
|
-
raise ValueError(f"Failed to check if element exists: {response_json['error']}")
|
|
594
|
+
raise ValueError(f"Failed to check if element exists: {response_json['error']}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|