simplex 1.2.63__tar.gz → 1.2.65__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.63 → simplex-1.2.65}/PKG-INFO +1 -1
- {simplex-1.2.63 → simplex-1.2.65}/setup.py +1 -1
- {simplex-1.2.63 → simplex-1.2.65}/simplex/simplex.py +133 -85
- {simplex-1.2.63 → simplex-1.2.65}/simplex.egg-info/PKG-INFO +1 -1
- {simplex-1.2.63 → simplex-1.2.65}/LICENSE +0 -0
- {simplex-1.2.63 → simplex-1.2.65}/README.md +0 -0
- {simplex-1.2.63 → simplex-1.2.65}/setup.cfg +0 -0
- {simplex-1.2.63 → simplex-1.2.65}/simplex/__init__.py +0 -0
- {simplex-1.2.63 → simplex-1.2.65}/simplex/cli.py +0 -0
- {simplex-1.2.63 → simplex-1.2.65}/simplex/deploy/__init__.py +0 -0
- {simplex-1.2.63 → simplex-1.2.65}/simplex/deploy/push.py +0 -0
- {simplex-1.2.63 → simplex-1.2.65}/simplex.egg-info/SOURCES.txt +0 -0
- {simplex-1.2.63 → simplex-1.2.65}/simplex.egg-info/dependency_links.txt +0 -0
- {simplex-1.2.63 → simplex-1.2.65}/simplex.egg-info/entry_points.txt +0 -0
- {simplex-1.2.63 → simplex-1.2.65}/simplex.egg-info/requires.txt +0 -0
- {simplex-1.2.63 → simplex-1.2.65}/simplex.egg-info/top_level.txt +0 -0
|
@@ -4,67 +4,29 @@ from typing import Optional, Union
|
|
|
4
4
|
import os
|
|
5
5
|
import json
|
|
6
6
|
import warnings
|
|
7
|
-
|
|
8
|
-
# Try to import playwright, but don't fail if it's not available (lite version)
|
|
7
|
+
|
|
9
8
|
try:
|
|
10
9
|
from rebrowser_playwright.sync_api import sync_playwright
|
|
11
10
|
PLAYWRIGHT_AVAILABLE = True
|
|
12
11
|
except ImportError:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
element_index: Optional[str] = None, nth_index: Optional[int] = None,
|
|
28
|
-
locator_options: Optional[dict] = None):
|
|
29
|
-
|
|
30
|
-
if element_index and element_index not in ["first", "last", "nth"]:
|
|
31
|
-
raise ValueError("element_index must be 'first', 'last', or 'nth'")
|
|
32
|
-
|
|
33
|
-
if element_index=="nth" and not nth_index:
|
|
34
|
-
raise ValueError("nth_index is required when element_index is 'nth'")
|
|
35
|
-
|
|
36
|
-
data = {
|
|
37
|
-
'session_id': self.simplex.session_id,
|
|
38
|
-
'locator': locator,
|
|
39
|
-
'locator_type': locator_type,
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if element_index:
|
|
43
|
-
data['element_index'] = element_index
|
|
44
|
-
data['nth_index'] = nth_index
|
|
45
|
-
|
|
46
|
-
if exact:
|
|
47
|
-
data['exact'] = exact
|
|
48
|
-
|
|
49
|
-
if locator_options:
|
|
50
|
-
data['locator_options'] = json.dumps(locator_options)
|
|
51
|
-
|
|
52
|
-
response = requests.post(
|
|
53
|
-
f"{BASE_URL}/playwright/click",
|
|
54
|
-
headers={
|
|
55
|
-
'x-api-key': self.simplex.api_key
|
|
56
|
-
},
|
|
57
|
-
data=data
|
|
12
|
+
try:
|
|
13
|
+
from playwright.sync_api import sync_playwright
|
|
14
|
+
PLAYWRIGHT_AVAILABLE = True
|
|
15
|
+
warnings.warn(
|
|
16
|
+
"Using standard playwright instead of rebrowser-patches. Some anti-bot systems may detect automation. "
|
|
17
|
+
"To improve stealth, install with 'pip install rebrowser-playwright'",
|
|
18
|
+
ImportWarning
|
|
19
|
+
)
|
|
20
|
+
except ImportError:
|
|
21
|
+
PLAYWRIGHT_AVAILABLE = False
|
|
22
|
+
warnings.warn(
|
|
23
|
+
"Playwright is not available. Some features will be disabled. "
|
|
24
|
+
"To enable all features, install with 'pip install simplex[playwright]'",
|
|
25
|
+
ImportWarning
|
|
58
26
|
)
|
|
59
27
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
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?")
|
|
63
|
-
|
|
64
|
-
if "succeeded" in response.json():
|
|
65
|
-
return
|
|
66
|
-
else:
|
|
67
|
-
raise ValueError(f"Failed to click element: {response.json()['error']}")
|
|
28
|
+
BASE_URL = "https://simplex-dev--api-server-and-container-service-fastapi-app.modal.run"
|
|
29
|
+
# BASE_URL = "https://api.simplex.sh"
|
|
68
30
|
|
|
69
31
|
class Simplex:
|
|
70
32
|
def __init__(self, api_key: str):
|
|
@@ -72,11 +34,9 @@ class Simplex:
|
|
|
72
34
|
self.session_id = None
|
|
73
35
|
atexit.register(self.close_session)
|
|
74
36
|
if PLAYWRIGHT_AVAILABLE:
|
|
75
|
-
self.playwright = Playwright(self)
|
|
76
37
|
self.pw_browser = None
|
|
77
38
|
self.pw = None
|
|
78
39
|
else:
|
|
79
|
-
self.playwright = None
|
|
80
40
|
self.pw_browser = None
|
|
81
41
|
self.pw = None
|
|
82
42
|
|
|
@@ -105,7 +65,7 @@ class Simplex:
|
|
|
105
65
|
self.session_id = None
|
|
106
66
|
if 'succeeded' not in response.json():
|
|
107
67
|
raise ValueError(f"It looks like the close_session action failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
108
|
-
if
|
|
68
|
+
if response.json()['succeeded']:
|
|
109
69
|
return
|
|
110
70
|
else:
|
|
111
71
|
raise ValueError(f"Failed to close session: {response.json()['error']}")
|
|
@@ -187,10 +147,11 @@ class Simplex:
|
|
|
187
147
|
},
|
|
188
148
|
data=data
|
|
189
149
|
)
|
|
150
|
+
|
|
190
151
|
if 'succeeded' not in response.json():
|
|
191
152
|
raise ValueError(f"It looks like the goto action failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
192
153
|
|
|
193
|
-
if
|
|
154
|
+
if response.json()['succeeded']:
|
|
194
155
|
return
|
|
195
156
|
else:
|
|
196
157
|
raise ValueError(f"Failed to goto url: {response.json()['error']}")
|
|
@@ -222,6 +183,65 @@ class Simplex:
|
|
|
222
183
|
else:
|
|
223
184
|
raise ValueError(f"Failed to click element: {response.json()['error']}")
|
|
224
185
|
|
|
186
|
+
|
|
187
|
+
def scroll_to_element(self, element_description: str, cdp_url: str = None):
|
|
188
|
+
if not element_description or not element_description.strip():
|
|
189
|
+
raise ValueError("element_description cannot be empty")
|
|
190
|
+
if not cdp_url and not self.session_id:
|
|
191
|
+
raise ValueError(f"Must call create_session before calling action scroll_to_element with element_description='{element_description}'")
|
|
192
|
+
|
|
193
|
+
data = {'element_description': element_description}
|
|
194
|
+
|
|
195
|
+
if cdp_url:
|
|
196
|
+
data['cdp_url'] = cdp_url
|
|
197
|
+
else:
|
|
198
|
+
data['session_id'] = self.session_id
|
|
199
|
+
|
|
200
|
+
response = requests.post(
|
|
201
|
+
f"{BASE_URL}/scroll_to_element",
|
|
202
|
+
headers={
|
|
203
|
+
'x-api-key': self.api_key
|
|
204
|
+
},
|
|
205
|
+
data=data
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
if 'succeeded' not in response.json():
|
|
209
|
+
raise ValueError(f"It looks like the scroll_to_element action failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
210
|
+
if response.json()["succeeded"]:
|
|
211
|
+
return
|
|
212
|
+
else:
|
|
213
|
+
raise ValueError(f"Failed to scroll element into view: {response.json()['error']}")
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def hover(self, element_description: str, cdp_url: str = None):
|
|
218
|
+
if not element_description or not element_description.strip():
|
|
219
|
+
raise ValueError("element_description cannot be empty")
|
|
220
|
+
if not cdp_url and not self.session_id:
|
|
221
|
+
raise ValueError(f"Must call create_session before calling action hover with element_description='{element_description}'")
|
|
222
|
+
|
|
223
|
+
data = {'element_description': element_description}
|
|
224
|
+
|
|
225
|
+
if cdp_url:
|
|
226
|
+
data['cdp_url'] = cdp_url
|
|
227
|
+
else:
|
|
228
|
+
data['session_id'] = self.session_id
|
|
229
|
+
|
|
230
|
+
response = requests.post(
|
|
231
|
+
f"{BASE_URL}/hover",
|
|
232
|
+
headers={
|
|
233
|
+
'x-api-key': self.api_key
|
|
234
|
+
},
|
|
235
|
+
data=data
|
|
236
|
+
)
|
|
237
|
+
if 'succeeded' not in response.json():
|
|
238
|
+
raise ValueError(f"It looks like the hover action failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
239
|
+
|
|
240
|
+
if response.json()["succeeded"]:
|
|
241
|
+
return
|
|
242
|
+
else:
|
|
243
|
+
raise ValueError(f"Failed to hover: {response.json()['error']}")
|
|
244
|
+
|
|
225
245
|
def type(self, text: str, cdp_url: str = None):
|
|
226
246
|
if not text or not text.strip():
|
|
227
247
|
raise ValueError("text cannot be empty")
|
|
@@ -242,10 +262,9 @@ class Simplex:
|
|
|
242
262
|
},
|
|
243
263
|
data=data
|
|
244
264
|
)
|
|
245
|
-
if
|
|
265
|
+
if response.json()['succeeded']:
|
|
246
266
|
return
|
|
247
267
|
else:
|
|
248
|
-
print(response.json())
|
|
249
268
|
raise ValueError(f"Failed to type text: {response.json()['error']}")
|
|
250
269
|
|
|
251
270
|
|
|
@@ -269,7 +288,7 @@ class Simplex:
|
|
|
269
288
|
)
|
|
270
289
|
if 'succeeded' not in response.json():
|
|
271
290
|
raise ValueError(f"It looks like the reload action failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
272
|
-
if
|
|
291
|
+
if response.json()['succeeded']:
|
|
273
292
|
return
|
|
274
293
|
else:
|
|
275
294
|
raise ValueError(f"Failed to reload: {response.json()['error']}")
|
|
@@ -294,7 +313,7 @@ class Simplex:
|
|
|
294
313
|
)
|
|
295
314
|
if 'succeeded' not in response.json():
|
|
296
315
|
raise ValueError(f"It looks like the press_enter action failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
297
|
-
if
|
|
316
|
+
if response.json()['succeeded']:
|
|
298
317
|
return
|
|
299
318
|
else:
|
|
300
319
|
raise ValueError(f"Failed to press enter: {response.json()['error']}")
|
|
@@ -319,7 +338,7 @@ class Simplex:
|
|
|
319
338
|
)
|
|
320
339
|
if 'succeeded' not in response.json():
|
|
321
340
|
raise ValueError(f"It looks like the press_tab action failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
322
|
-
if
|
|
341
|
+
if response.json()['succeeded']:
|
|
323
342
|
return
|
|
324
343
|
else:
|
|
325
344
|
raise ValueError(f"Failed to press tab: {response.json()['error']}")
|
|
@@ -344,7 +363,7 @@ class Simplex:
|
|
|
344
363
|
)
|
|
345
364
|
if 'succeeded' not in response.json():
|
|
346
365
|
raise ValueError(f"It looks like the delete_text action failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
347
|
-
if
|
|
366
|
+
if response.json()['succeeded']:
|
|
348
367
|
return
|
|
349
368
|
else:
|
|
350
369
|
raise ValueError(f"Failed to delete text: {response.json()['error']}")
|
|
@@ -371,7 +390,7 @@ class Simplex:
|
|
|
371
390
|
)
|
|
372
391
|
if 'succeeded' not in response.json():
|
|
373
392
|
raise ValueError(f"It looks like the extract_bbox action failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
374
|
-
if
|
|
393
|
+
if response.json()['succeeded']:
|
|
375
394
|
return response.json()["bbox"]
|
|
376
395
|
else:
|
|
377
396
|
raise ValueError(f"Failed to extract bbox: {response.json()['error']}")
|
|
@@ -426,7 +445,7 @@ class Simplex:
|
|
|
426
445
|
)
|
|
427
446
|
if 'succeeded' not in response.json():
|
|
428
447
|
raise ValueError(f"It looks like the extract_image action failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
429
|
-
if
|
|
448
|
+
if response.json()['succeeded']:
|
|
430
449
|
return response.json()["image"]
|
|
431
450
|
else:
|
|
432
451
|
raise ValueError(f"Failed to extract image: {response.json()['error']}")
|
|
@@ -451,7 +470,7 @@ class Simplex:
|
|
|
451
470
|
)
|
|
452
471
|
if 'succeeded' not in response.json():
|
|
453
472
|
raise ValueError(f"It looks like the scroll action failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
454
|
-
if
|
|
473
|
+
if response.json()['succeeded']:
|
|
455
474
|
return
|
|
456
475
|
else:
|
|
457
476
|
raise ValueError(f"Failed to scroll: {response.json()['error']}")
|
|
@@ -476,7 +495,7 @@ class Simplex:
|
|
|
476
495
|
)
|
|
477
496
|
if 'succeeded' not in response.json():
|
|
478
497
|
raise ValueError(f"It looks like the wait action failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
479
|
-
if
|
|
498
|
+
if response.json()['succeeded']:
|
|
480
499
|
return
|
|
481
500
|
else:
|
|
482
501
|
raise ValueError(f"Failed to wait: {response.json()['error']}")
|
|
@@ -495,24 +514,23 @@ class Simplex:
|
|
|
495
514
|
return netloc.replace(".", "_")
|
|
496
515
|
|
|
497
516
|
with sync_playwright() as p:
|
|
498
|
-
|
|
517
|
+
browser = p.chromium.launch(
|
|
499
518
|
channel="chrome",
|
|
500
519
|
headless=False,
|
|
501
520
|
args=[
|
|
502
521
|
'--disable-blink-features=AutomationControlled',
|
|
503
522
|
'--disable-automation',
|
|
504
523
|
],
|
|
505
|
-
ignore_default_args=['--enable-automation']
|
|
506
|
-
user_data_dir="/home/simplex/.config/google-chrome/Profile 1/",
|
|
507
|
-
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
508
|
-
viewport={"width": 1243, "height": 681}
|
|
524
|
+
ignore_default_args=['--enable-automation']
|
|
509
525
|
)
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
526
|
+
# Create context and pages
|
|
527
|
+
context = browser.new_context(viewport=None)
|
|
528
|
+
main_page = context.new_page()
|
|
529
|
+
main_page.goto(url)
|
|
530
|
+
|
|
531
|
+
# Create control page in same context
|
|
514
532
|
control_page = context.new_page()
|
|
515
|
-
|
|
533
|
+
# Use a simple HTML file instead of set_content
|
|
516
534
|
html_content = """
|
|
517
535
|
<!DOCTYPE html>
|
|
518
536
|
<html>
|
|
@@ -560,6 +578,7 @@ class Simplex:
|
|
|
560
578
|
os.unlink(temp_path)
|
|
561
579
|
except:
|
|
562
580
|
pass
|
|
581
|
+
|
|
563
582
|
# Use context.storage_state() instead of browser.storage_state()
|
|
564
583
|
storage = context.storage_state()
|
|
565
584
|
if save_directory:
|
|
@@ -572,9 +591,37 @@ class Simplex:
|
|
|
572
591
|
|
|
573
592
|
print(f"Session data saved to '{filename}'")
|
|
574
593
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
594
|
+
browser.close()
|
|
595
|
+
|
|
596
|
+
return filename
|
|
597
|
+
|
|
598
|
+
def get_network_response(self, url: str, cdp_url: str = None):
|
|
599
|
+
print(f"Getting network response for {url}")
|
|
600
|
+
if not cdp_url and not self.session_id:
|
|
601
|
+
raise ValueError(f"Must call create_session before calling action get_network_response with url='{url}'")
|
|
602
|
+
|
|
603
|
+
data = {'url': url}
|
|
604
|
+
|
|
605
|
+
if cdp_url:
|
|
606
|
+
data['cdp_url'] = cdp_url
|
|
607
|
+
else:
|
|
608
|
+
data['session_id'] = self.session_id
|
|
609
|
+
|
|
610
|
+
response = requests.post(
|
|
611
|
+
f"{BASE_URL}/get_network_response",
|
|
612
|
+
headers={
|
|
613
|
+
'x-api-key': self.api_key
|
|
614
|
+
},
|
|
615
|
+
data=data
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
if 'status' not in response.json():
|
|
619
|
+
raise ValueError(f"It looks like the get_network_response action failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
620
|
+
if response.json()['status'] == 'success':
|
|
621
|
+
return response.json()['response']
|
|
622
|
+
else:
|
|
623
|
+
raise ValueError(f"Failed to get network response: {response.json()['error']}")
|
|
624
|
+
|
|
578
625
|
def restore_login_session(self, session_data: str, cdp_url: str = None):
|
|
579
626
|
"""
|
|
580
627
|
Restore a login session from either a file path or a JSON string.
|
|
@@ -611,7 +658,7 @@ class Simplex:
|
|
|
611
658
|
)
|
|
612
659
|
if 'succeeded' not in response.json():
|
|
613
660
|
raise ValueError(f"It looks like the restore_login_session action failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
614
|
-
if
|
|
661
|
+
if response.json()['succeeded']:
|
|
615
662
|
return
|
|
616
663
|
else:
|
|
617
664
|
raise ValueError(f"Failed to restore login session: {response.json()['error']}")
|
|
@@ -655,7 +702,7 @@ class Simplex:
|
|
|
655
702
|
)
|
|
656
703
|
if 'succeeded' not in response.json():
|
|
657
704
|
raise ValueError(f"It looks like the click_and_upload action failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
658
|
-
if
|
|
705
|
+
if response.json()['succeeded']:
|
|
659
706
|
return
|
|
660
707
|
else:
|
|
661
708
|
raise ValueError(f"Failed to click and upload: {response.json()['error']}")
|
|
@@ -726,7 +773,8 @@ class Simplex:
|
|
|
726
773
|
if 'succeeded' not in response.json():
|
|
727
774
|
raise ValueError(f"It looks like the exists action failed to return a response. Did you set your api_key when creating the Simplex class?")
|
|
728
775
|
response_json = response.json()
|
|
729
|
-
|
|
776
|
+
|
|
777
|
+
if response_json['succeeded']:
|
|
730
778
|
return response_json['exists'], response_json['reasoning']
|
|
731
779
|
else:
|
|
732
780
|
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
|