asterocr 1.0.0__py3-none-any.whl

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.
@@ -0,0 +1,380 @@
1
+ import os
2
+ import sys
3
+ import time
4
+ import json
5
+ from playwright.sync_api import sync_playwright
6
+
7
+ PROFILE_DIR = os.path.join(os.environ.get("LOCALAPPDATA", ""), "Google", "Chrome", "User Data")
8
+
9
+ import re
10
+
11
+ def check_quota_exceeded(page):
12
+ try:
13
+ import re
14
+ # Broadly search for quota limits anywhere in the DOM (including shadow DOMs)
15
+ loc = page.locator("text=/Cannot connect to (TPU|GPU) backend|usage limits in Colab|Usage limit exceeded/i")
16
+ if loc.count() > 0 and loc.first.is_visible():
17
+ print("[!] TAI KHOAN NAY DA HET HAN MUC GPU/TPU (Quota Exceeded)!")
18
+ print("[*] Chuan bi dong trinh duyet va chuyen sang Account tiep theo...")
19
+ return True
20
+ except Exception as e:
21
+ pass
22
+ return False
23
+
24
+ def check_and_clear_sessions(page):
25
+ try:
26
+ # Bơm Script JS trực tiếp vào trình duyệt để tìm và click nút xuyên qua mọi tầng mây Shadow DOM!
27
+ js_clicker = """
28
+ () => {
29
+ function findAndClick(text) {
30
+ let els = document.querySelectorAll('*');
31
+ for (let el of els) {
32
+ if (el.shadowRoot) {
33
+ let btn = el.shadowRoot.querySelector('mwc-button, paper-button, button');
34
+ if (btn && btn.textContent && btn.textContent.toLowerCase().includes(text.toLowerCase())) {
35
+ btn.click();
36
+ return true;
37
+ }
38
+ }
39
+ if (el.textContent && el.textContent.toLowerCase() === text.toLowerCase()) {
40
+ let btn = el.closest('mwc-button, paper-button, button') || el;
41
+ btn.click();
42
+ return true;
43
+ }
44
+ }
45
+ return false;
46
+ }
47
+ return findAndClick('Manage sessions');
48
+ }
49
+ """
50
+
51
+ # Thử click bằng JS trước
52
+ clicked_by_js = page.evaluate(js_clicker)
53
+
54
+ # Nếu JS thất bại thì dùng Playwright
55
+ manage_btn = page.locator("text=Manage sessions")
56
+ if clicked_by_js or (manage_btn.count() > 0 and manage_btn.first.is_visible()):
57
+ if not clicked_by_js:
58
+ manage_btn.first.click(force=True)
59
+
60
+ print("[*] Phat hien loi 'Too many sessions'! Tu dong dọn dẹp...")
61
+ time.sleep(4)
62
+
63
+ # Nhấn nút "Terminate other sessions"
64
+ term_other = page.locator("text=Terminate other sessions")
65
+ if term_other.count() > 0 and term_other.first.is_visible():
66
+ term_other.first.click(force=True)
67
+ time.sleep(2)
68
+
69
+ # Click thêm vào tất cả các biểu tượng thùng rác (nếu có)
70
+ trash_icons = page.locator("[title*='Terminate'], [aria-label*='Terminate'], [icon='delete']")
71
+ for i in range(trash_icons.count()):
72
+ try:
73
+ if trash_icons.nth(i).is_visible():
74
+ trash_icons.nth(i).click(force=True)
75
+ time.sleep(1)
76
+ except:
77
+ pass
78
+
79
+ # Đóng hộp thoại
80
+ close_btn = page.locator("text=Close")
81
+ if close_btn.count() > 0 and close_btn.first.is_visible():
82
+ close_btn.first.click(force=True)
83
+
84
+ try:
85
+ page.keyboard.press("Escape")
86
+ except:
87
+ pass
88
+ time.sleep(2)
89
+ print("[+] Da don dep xong session cu! Tiep tuc chay code tren account hien tai...")
90
+ return True
91
+ except Exception as e:
92
+ print(f"[DEBUG] Loi khi xoa session: {e}")
93
+ return False
94
+
95
+ def main():
96
+ print("=" * 65)
97
+ print(" AsterOCR Google Colab Zero-Touch Engine")
98
+ print("=" * 65)
99
+
100
+ def run_with_profile(profile_dir, is_tpu, COLAB_NOTEBOOK_CODE):
101
+ print(f"[*] Su dung Bot Profile tai: {profile_dir}")
102
+
103
+ with sync_playwright() as p:
104
+ browser = p.chromium.launch_persistent_context(
105
+ user_data_dir=profile_dir,
106
+ channel="chrome",
107
+ headless=False,
108
+ ignore_default_args=["--enable-automation"],
109
+ args=[
110
+ '--disable-blink-features=AutomationControlled',
111
+ '--disable-infobars',
112
+ '--no-sandbox',
113
+ '--disable-dev-shm-usage',
114
+ '--window-size=1920,1080'
115
+ ],
116
+ user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
117
+ no_viewport=True
118
+ )
119
+
120
+ page = browser.pages[0] if browser.pages else browser.new_page()
121
+
122
+ # Bơm Script tàng hình (Stealth Evasion) vào mỗi khi load trang
123
+ page.add_init_script("""
124
+ Object.defineProperty(navigator, 'webdriver', {
125
+ get: () => undefined
126
+ });
127
+ window.navigator.chrome = {
128
+ runtime: {}
129
+ };
130
+ Object.defineProperty(navigator, 'languages', {
131
+ get: () => ['en-US', 'en']
132
+ });
133
+ Object.defineProperty(navigator, 'plugins', {
134
+ get: () => [1, 2, 3]
135
+ });
136
+ """)
137
+
138
+ print("[*] Truc tiep truy cap vao Google Colab...")
139
+ page.goto("https://colab.research.google.com/#create=true")
140
+
141
+ # Check login
142
+ try:
143
+ page.wait_for_selector('.monaco-editor', timeout=15000)
144
+ print("[+] Da dang nhap Google Colab (Co Monaco Editor).")
145
+ except:
146
+ print("[!] Colab yeu cau dang nhap hoac load qua cham! Vui long tu dang nhap tren cua so trinh duyet.")
147
+ try:
148
+ page.wait_for_selector('.monaco-editor', timeout=60000)
149
+ print("[+] Da dang nhap thanh cong!")
150
+ except:
151
+ print("[!] Qua gio dang nhap hoac loi mang. Chuyen sang Profile tiep theo...")
152
+ return "ERROR"
153
+
154
+
155
+ print("[*] Cau hinh phan cung (TPU/GPU)...")
156
+ time.sleep(2)
157
+ try:
158
+ # Click Runtime -> Change runtime type (force=True de xuyen qua cac bang popup huong dan cua Google)
159
+ try:
160
+ page.locator('#runtime-menu-button').click(force=True, timeout=5000)
161
+ except:
162
+ page.get_by_text("Runtime", exact=True).click(force=True, timeout=5000)
163
+
164
+ time.sleep(1)
165
+ try:
166
+ page.locator('#ok').click(force=True, timeout=1000) # clear any dialogs
167
+ except:
168
+ pass
169
+
170
+ page.get_by_text("Change runtime type").click(force=True, timeout=5000)
171
+ time.sleep(2)
172
+
173
+ # Select hardware based on new Colab UI
174
+ if is_tpu:
175
+ # Try new TPU names first, fallback to old generic TPU
176
+ try:
177
+ page.get_by_text("v5e-1 TPU").first.click(force=True, timeout=3000)
178
+ except:
179
+ page.get_by_text("TPU").first.click(force=True, timeout=3000)
180
+ else:
181
+ try:
182
+ page.get_by_text("T4 GPU").first.click(force=True, timeout=3000)
183
+ except:
184
+ page.get_by_text("GPU").first.click(force=True, timeout=3000)
185
+ time.sleep(1)
186
+
187
+ # Click Save button
188
+ try:
189
+ page.get_by_role("button", name="Save").click(timeout=3000)
190
+ except:
191
+ try:
192
+ page.locator('button:has-text("Save")').last.click(force=True, timeout=3000)
193
+ except:
194
+ pass
195
+
196
+ # Ultimate fallback: Press Enter to submit the dialog
197
+ time.sleep(1)
198
+ page.keyboard.press("Enter")
199
+ time.sleep(3)
200
+ check_and_clear_sessions(page)
201
+ if check_quota_exceeded(page):
202
+ return "QUOTA_EXCEEDED"
203
+ print("[+] Cau hinh phan cung thanh cong!")
204
+ except Exception as e:
205
+ print(f"[!] Khong the cau hinh tu dong phan cung: {e}")
206
+
207
+ print("[*] Tiem code va bat dau chay...")
208
+ try:
209
+ # Click into the first cell
210
+ page.click('.monaco-editor')
211
+ time.sleep(1)
212
+ page.keyboard.press("Control+A")
213
+ page.keyboard.press("Backspace")
214
+ time.sleep(1)
215
+ # Type the code using a synthetic paste event to bypass auto-indent and clipboard locks!
216
+ page.evaluate('''
217
+ (code) => {
218
+ const dataTransfer = new DataTransfer();
219
+ dataTransfer.setData('text/plain', code);
220
+ const event = new ClipboardEvent('paste', {
221
+ clipboardData: dataTransfer,
222
+ bubbles: true,
223
+ cancelable: true
224
+ });
225
+ document.activeElement.dispatchEvent(event);
226
+ }
227
+ ''', COLAB_NOTEBOOK_CODE)
228
+ print("[*] Dang cho Colab cap phat GPU (Co the mat 10-15s)...")
229
+ time.sleep(2)
230
+ page.keyboard.press("Control+Enter")
231
+
232
+ # Vòng lặp chờ 15s để bắt lỗi "Too many sessions" hoặc "Quota Exceeded"
233
+ # Vì Colab mất một lúc để Allocating, nếu check quá nhanh sẽ bị hụt!
234
+ is_quota_exceeded = False
235
+ for _ in range(8):
236
+ time.sleep(2)
237
+ if check_and_clear_sessions(page):
238
+ time.sleep(2)
239
+ page.keyboard.press("Control+Enter")
240
+ if check_quota_exceeded(page):
241
+ is_quota_exceeded = True
242
+ break
243
+
244
+ if is_quota_exceeded:
245
+ return "QUOTA_EXCEEDED"
246
+
247
+ print("[+] Da tiem code va vuot qua buoc kiem tra GPU!")
248
+ except Exception as e:
249
+ print(f"[!] Loi khi paste code: {e}")
250
+
251
+ print("[*] Bat che do Auto-Watcher vinh vien...")
252
+ while True:
253
+ try:
254
+ time.sleep(10)
255
+ # Kiem tra nut Reconnect
256
+ reconnect_btn = page.query_selector('colab-connect-button[connected="false"], #connect, colab-connect-button:has-text("Reconnect"), colab-connect-button:has-text("Connect")')
257
+ if reconnect_btn:
258
+ print("[*] Mat ket noi! Tu dong Reconnect...")
259
+ reconnect_btn.click()
260
+ time.sleep(5)
261
+ page.keyboard.press("Control+F9")
262
+
263
+ # Dialog "Are you still there" hoac error
264
+ dialog_ok = page.query_selector('md-dialog mwc-button[dialogaction="ok"], paper-button:has-text("OK")')
265
+ if dialog_ok:
266
+ print("[*] Dong hop thoai Canh bao...")
267
+ dialog_ok.click()
268
+ time.sleep(3)
269
+ page.keyboard.press("Control+F9")
270
+
271
+ # Kiem tra trang thai chay cua Notebook (Idle Detection)
272
+ # Kiem tra xem co cell nao dang "Running" khong (Bang cach tim nut Stop)
273
+ is_running = page.evaluate('''
274
+ () => {
275
+ let btns = document.querySelectorAll('colab-run-button');
276
+ if (btns.length === 0) return true; // Chua the xac dinh, gia su dang chay de an toan
277
+ for (let b of btns) {
278
+ if (b.title && b.title.toLowerCase().includes('stop')) return true;
279
+ if (b.getAttribute('aria-label') && b.getAttribute('aria-label').toLowerCase().includes('stop')) return true;
280
+ let icon = b.querySelector('iron-icon');
281
+ if (icon && icon.getAttribute('icon') === 'colab:stop') return true;
282
+ }
283
+ return false; // Khong co cell nao dang chay
284
+ }
285
+ ''')
286
+
287
+ if not is_running:
288
+ print("[!] Phat hien Notebook bi DUNG chay (Idle/Crashed). Tu dong kich hoat chay lai...")
289
+ page.keyboard.press("Control+Enter")
290
+ time.sleep(5)
291
+
292
+ # Dialog "Too many sessions"
293
+ if check_and_clear_sessions(page):
294
+ page.keyboard.press("Control+Enter")
295
+
296
+ # Kiem tra Quota Limit Exceeded
297
+ if check_quota_exceeded(page):
298
+ return "QUOTA_EXCEEDED"
299
+
300
+ # Keep-alive
301
+ page.evaluate('() => { if (window.colab && window.colab.global) { window.colab.global.notebook.kernel.keepAlive(); } }')
302
+ except Exception:
303
+ time.sleep(10)
304
+
305
+ def main():
306
+ print("=" * 65)
307
+ print(" AsterOCR Google Colab Zero-Touch Engine")
308
+ print("=" * 65)
309
+ tunnel_url = sys.argv[1] if len(sys.argv) > 1 else "https://asterai.sglocalhub.com"
310
+ print(f"[*] Engine URL: {tunnel_url}")
311
+
312
+ # Read config to know if TPU or GPU
313
+ is_tpu = False
314
+ config_path = os.path.join(os.path.dirname(__file__), "..", "..", "scratch", "colab_run_config.json")
315
+ if os.path.exists(config_path):
316
+ import json
317
+ with open(config_path, "r", encoding="utf-8") as f:
318
+ cfg = json.load(f)
319
+ if cfg.get("device") == "colab_tpu":
320
+ is_tpu = True
321
+
322
+ COLAB_NOTEBOOK_CODE = f'''# --- ASTEROCR COLAB WORKER (ZERO-TOUCH) ---
323
+ import requests, os, time, subprocess, sys
324
+ server_url = "{tunnel_url}"
325
+
326
+ while True:
327
+ try:
328
+ print(f"[+] Xin code tu {{server_url}}...")
329
+ res = requests.get(f"{{server_url}}/api/colab/worker.py", timeout=20)
330
+ if res.status_code == 200 and not res.text.startswith("<!DOCTYPE"):
331
+ with open("worker.py", "w", encoding="utf-8") as f:
332
+ f.write(res.text)
333
+ print("[🚀] Bat dau Huan luyen...")
334
+ p = subprocess.Popen([sys.executable, "-u", "worker.py"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)
335
+ for line in p.stdout:
336
+ print(line, end='', flush=True)
337
+ p.wait()
338
+ print(f"[!] Tien trinh ket thuc voi ma loi: {{p.returncode}}")
339
+ else:
340
+ print(f"[ERROR] Loi tai code. Status: {{res.status_code}}")
341
+ if res.text.startswith("<!DOCTYPE"):
342
+ print("[ERROR] Nhan duoc HTML thay vi Code Python! Cloudflare Tunnel dang bi chan hoac chua san sang.")
343
+ print("[!] Phien ket thuc. Tu dong khoi dong lai sau 5s...")
344
+ time.sleep(5)
345
+ except Exception as e:
346
+ print(f"[!] Ket noi gian doan: {{e}}. Thu lai sau 10s...")
347
+ time.sleep(10)
348
+ '''
349
+
350
+ profiles = [
351
+ os.path.join(os.environ.get("LOCALAPPDATA", ""), "Google", "Chrome", "AsterOCR_Profile"),
352
+ os.path.join(os.environ.get("LOCALAPPDATA", ""), "Google", "Chrome", "AsterOCR_Profile_2"),
353
+ os.path.join(os.environ.get("LOCALAPPDATA", ""), "Google", "Chrome", "AsterOCR_Profile_3")
354
+ ]
355
+
356
+ print("[*] Bat dau chu trinh Multi-Profile Fallback...")
357
+
358
+ for i, profile_dir in enumerate(profiles, start=1):
359
+ print(f"\\n{'='*50}")
360
+ print(f"[*] DANG SU DUNG TAI KHOAN {i}/3")
361
+ print(f"{'='*50}")
362
+
363
+ status = run_with_profile(profile_dir, is_tpu, COLAB_NOTEBOOK_CODE)
364
+
365
+ if status == "QUOTA_EXCEEDED":
366
+ print(f"[!] Tai khoan {i} da het Quota. Tu dong chuyen sang tai khoan tiep theo...")
367
+ time.sleep(5)
368
+ continue
369
+ elif status == "ERROR":
370
+ print(f"[!] Tai khoan {i} gap loi ket noi. Thu tai khoan tiep theo...")
371
+ time.sleep(5)
372
+ continue
373
+ else:
374
+ print("[+] Tien trinh dung dot ngot hoac bi huy.")
375
+ break
376
+
377
+ print("[!] TAT CA TAI KHOAN DEU DA HET QUOTA HOAC GAP LOI!")
378
+
379
+ if __name__ == "__main__":
380
+ main()