tls_registration_bot.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. import time
  2. import json
  3. import os
  4. import re
  5. import uuid
  6. import socket
  7. import shutil
  8. import random
  9. import requests
  10. from urllib.parse import urlencode
  11. from datetime import datetime, timedelta
  12. from typing import Optional, Dict
  13. from DrissionPage.common import Keys
  14. from DrissionPage import ChromiumPage, ChromiumOptions
  15. from utils.cloudflare_bypass_for_scraping import CloudflareBypasser
  16. from toolkit.vs_cloud_api import VSCloudApi
  17. from vs_types import NotFoundError, PermissionDeniedError, RateLimiteddError, SessionExpiredOrInvalidError, BizLogicError
  18. from utils.mouse import HumanMouse
  19. from utils.keyboard import HumanKeyboard
  20. from utils.scroll import HumanScroll
  21. def generate_random_account_detail() -> Dict:
  22. """基于 randomuser 生成随机账户信息,并保留指定固定字段。"""
  23. base_date = datetime.utcnow().date() + timedelta(days=random.randint(20, 90))
  24. arrival_schengen_area_date = base_date.strftime("%Y-%m-%d")
  25. departure_origin_date = (base_date - timedelta(days=random.randint(0, 2))).strftime("%Y-%m-%d")
  26. departure_schengen_area_date = (base_date + timedelta(days=random.randint(2, 15))).strftime("%Y-%m-%d")
  27. default_payload = {
  28. "email": f"user{random.randint(100000, 999999)}@gmail-app.com",
  29. "pwd": "Visafly@111",
  30. "location": "London",
  31. "visa_type": "Short stay (<90 days) - Tourism",
  32. "travel_purpose": "Tourism / Private visit",
  33. "application_form_id": "FRA" + "".join(str(random.randint(0, 9)) for _ in range(14)),
  34. "last_name": "Smith",
  35. "first_name": "James",
  36. "gender": "Male",
  37. "birthday": "1998-11-20",
  38. "nationality": "United Kingdom",
  39. "province_residence": "London",
  40. "passport_type": "Ordinary passport",
  41. "passport_no": "".join(random.choices("ABCDEFGHIJKLMNOPQRSTUVWXYZ", k=2)) + "".join(random.choices("0123456789", k=7)),
  42. "phone_country_code": "44",
  43. "phone_number": "7400000000",
  44. "departure_origin_date": departure_origin_date,
  45. "arrival_schengen_area_date": arrival_schengen_area_date,
  46. "departure_schengen_area_date": departure_schengen_area_date,
  47. }
  48. try:
  49. # 每次请求不传 seed,randomuser 会返回不同用户
  50. resp = requests.get("https://randomuser.me/api/?nat=gb", timeout=15)
  51. resp.raise_for_status()
  52. raw = resp.json()
  53. result = (raw.get("results") or [None])[0]
  54. if not result:
  55. return default_payload
  56. first_name = result.get("name", {}).get("first") or default_payload["first_name"]
  57. last_name = result.get("name", {}).get("last") or default_payload["last_name"]
  58. gender_raw = (result.get("gender") or "").strip().lower()
  59. gender = "Male" if gender_raw == "male" else "Female"
  60. birthday_raw = result.get("dob", {}).get("date", "")
  61. birthday = birthday_raw[:10] if birthday_raw else default_payload["birthday"]
  62. city = result.get("location", {}).get("city") or default_payload["location"]
  63. state = result.get("location", {}).get("state") or default_payload["province_residence"]
  64. country = result.get("location", {}).get("country") or default_payload["nationality"]
  65. phone_raw = result.get("cell") or result.get("phone") or default_payload["phone_number"]
  66. phone_number = re.sub(r"\D", "", phone_raw) or default_payload["phone_number"]
  67. email_prefix = re.sub(r"[^a-z0-9]", "", f"{first_name}{last_name}".lower())
  68. if not email_prefix:
  69. email_prefix = f"user{random.randint(100000, 999999)}"
  70. email = f"{email_prefix}{random.randint(1000, 9999)}@gmail-app.com"
  71. return {
  72. "email": email,
  73. "pwd": "Visafly@111",
  74. "location": city,
  75. "visa_type": "Short stay (<90 days) - Tourism",
  76. "travel_purpose": "Tourism / Private visit",
  77. "application_form_id": "FRA" + "".join(str(random.randint(0, 9)) for _ in range(14)),
  78. "last_name": last_name,
  79. "first_name": first_name,
  80. "gender": gender,
  81. "birthday": birthday,
  82. "nationality": country,
  83. "province_residence": state,
  84. "passport_type": "Ordinary passport",
  85. "passport_no": "".join(random.choices("ABCDEFGHIJKLMNOPQRSTUVWXYZ", k=2)) + "".join(random.choices("0123456789", k=7)),
  86. "phone_country_code": "44",
  87. "phone_number": phone_number,
  88. "departure_origin_date": departure_origin_date,
  89. "arrival_schengen_area_date": arrival_schengen_area_date,
  90. "departure_schengen_area_date": departure_schengen_area_date,
  91. }
  92. except Exception:
  93. return default_payload
  94. class TlsRegistrator:
  95. def __init__(self, tls_url, proxy_config: Optional[Dict]=None, capsolver_key: Optional[str]=None, account_detail: Optional[Dict]=None):
  96. self.proxy_config = proxy_config
  97. self.capsolver_key = capsolver_key
  98. self.account_detail = account_detail
  99. # 隔离的用户数据目录
  100. #self.instance_id = uuid.uuid4().hex[:8]
  101. self.tls_url = tls_url
  102. self.instance_id = '18d389e9'
  103. self.workspace = os.path.abspath(os.path.join("data", f"reg_session_{self.instance_id}"))
  104. self.page = None
  105. self.mouse = None
  106. self.keyboard = None
  107. def _log(self, msg):
  108. print(f"[TLS-Reg-{self.instance_id}] {msg}")
  109. def _get_free_port(self):
  110. """获取可用端口,防止 DrissionPage 解析日志报错"""
  111. with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
  112. s.bind(('', 0))
  113. return s.getsockname()[1]
  114. def init_browser(self):
  115. """初始化独立、配置好代理的浏览器环境"""
  116. self._log("Initializing browser...")
  117. co = ChromiumOptions()
  118. # 1. 端口与路径隔离
  119. port = self._get_free_port()
  120. co.set_local_port(port)
  121. co.set_user_data_path(self.workspace)
  122. # 2. 代理配置 (支持账号密码)
  123. if self.proxy_config and self.proxy_config.get("ip"):
  124. p = self.proxy_config
  125. if p.get("username") and p.get("password"):
  126. proxy_str = f"{p['username']}:{p['password']}@{p['ip']}:{p['port']}"
  127. else:
  128. proxy_str = f"{p.get('scheme', 'http')}://{p['ip']}:{p['port']}"
  129. self._log(f"Setting proxy: {p['ip']}:{p['port']}")
  130. co.set_proxy(proxy_str)
  131. # 3. 反爬及稳定性配置
  132. co.headless(False)
  133. co.set_argument('--no-sandbox')
  134. co.set_argument('--disable-gpu')
  135. co.set_argument('--disable-dev-shm-usage')
  136. co.set_argument('--window-size=1920,1080')
  137. co.set_argument('--disable-blink-features=AutomationControlled')
  138. self.page = ChromiumPage(co)
  139. self.page.get(self.tls_url)
  140. cf_bypasser = CloudflareBypasser(self.page, log=True)
  141. cf_bypasser.bypass()
  142. time.sleep(3)
  143. cf_bypasser.handle_waiting_room()
  144. self._log("正在初始化拟人化工具...")
  145. self.mouse = HumanMouse(self.page, debug=True)
  146. self.keyboard = HumanKeyboard(self.page)
  147. self._log("随机化鼠标开始位置...")
  148. viewport_width = self.page.rect.viewport_size[0]
  149. viewport_height = self.page.rect.viewport_size[1]
  150. init_x = random.randint(10, viewport_width - 10)
  151. init_y = random.randint(10, viewport_height - 10)
  152. self.mouse.move(init_x, init_y)
  153. def solve_captcha(self, page_url: str, task_type: str, site_key: str, use_proxy = False, action: str = None, api_domain: str = None) -> str:
  154. """通用解决验证码 (同步 User-Agent 防止被盾识别为高风险)"""
  155. if not self.capsolver_key:
  156. raise ValueError("Capsolver API key missing")
  157. task = {
  158. "type": task_type,
  159. "websiteURL": page_url,
  160. "websiteKey": site_key,
  161. }
  162. if api_domain:
  163. task["apiDomain"] = api_domain
  164. if use_proxy:
  165. proxy = self.proxy_config
  166. task["proxyType"] = proxy.get('scheme', 'http')
  167. task["proxyAddress"] = proxy.get('ip')
  168. task["proxyPort"] = int(proxy.get('port'))
  169. if proxy.get('username'):
  170. task["proxyLogin"] = proxy.get('username')
  171. task["proxyPassword"] = proxy.get('password')
  172. if action:
  173. task["pageAction"] = action
  174. payload = {"clientKey": self.capsolver_key, "task": task}
  175. res = requests.post("https://api.capsolver.com/createTask", json=payload, timeout=20)
  176. if res.status_code != 200 or res.json().get("errorId") != 0:
  177. raise Exception(f"Failed to create capsolver task: {res.text}")
  178. task_id = res.json().get("taskId")
  179. self._log(f"Task created: {task_id}. Waiting for solution...")
  180. for _ in range(30):
  181. r = requests.post(
  182. "https://api.capsolver.com/getTaskResult",
  183. json={"clientKey": self.capsolver_key, "taskId": task_id},
  184. timeout=20
  185. )
  186. data = r.json()
  187. if data.get("status") == "ready":
  188. self._log("Captcha solved successfully!")
  189. return data["solution"].get("gRecaptchaResponse") or data["solution"].get("token")
  190. time.sleep(3)
  191. raise Exception("Capsolver task timeout")
  192. def register(self):
  193. """执行自动注册"""
  194. email = self.account_detail.get('email')
  195. password = self.account_detail.get('pwd')
  196. btn_selector = '#submit'
  197. if not self.page.wait.ele_displayed(btn_selector, timeout=3):
  198. register_btn = self.page.ele("tag:a@@href:registration")
  199. self.mouse.human_click_ele(register_btn)
  200. time.sleep(3)
  201. if not self.page.wait.ele_displayed(btn_selector, timeout=10):
  202. raise BizLogicError(message=f"Can't find selector={btn_selector}")
  203. time.sleep(random.uniform(0.5, 1))
  204. self._log("正在填写邮箱和密码...")
  205. email_input_e = self.page.ele('#email')
  206. self.mouse.human_click_ele(email_input_e)
  207. self.keyboard.type_text(email, humanize=True)
  208. time.sleep(random.uniform(0.2, 0.5))
  209. password_e = self.page.ele('#password')
  210. self.mouse.human_click_ele(password_e)
  211. self.keyboard.type_text(password, humanize=True)
  212. time.sleep(random.uniform(0.2, 0.5))
  213. confirm_password_e = self.page.ele('#confirm-password')
  214. self.mouse.human_click_ele(confirm_password_e)
  215. self.keyboard.type_text(password, humanize=True)
  216. time.sleep(random.uniform(0.2, 0.5))
  217. self._log("正在勾选必选条款...")
  218. for checkbox_id in ['#terms-and-conditions', '#biometric-data', '#privacy-notice']:
  219. check_box_e = self.page.ele(checkbox_id).next()
  220. self.mouse.human_click_ele(check_box_e)
  221. time.sleep(random.uniform(0.3, 0.6))
  222. self._log("提交注册...")
  223. btn_e = self.page.ele(btn_selector)
  224. btn_e.scroll.to_see(center=True)
  225. time.sleep(random.uniform(0.3, 0.6))
  226. btn_mx, btn_my = int(btn_e.rect.midpoint[0]), int(btn_e.rect.midpoint[1])
  227. self.mouse.move(btn_mx, btn_my, humanize=True)
  228. time.sleep(0.5)
  229. btn_e.click(by_js=True)
  230. self._log("正在等待验证结果 (最多10秒)...")
  231. success_dialog = self.page.wait.ele_displayed('tag:h1@text():Check your email inbox', timeout=10)
  232. if not success_dialog:
  233. self.page.get_screenshot("failed_submit.png")
  234. raise BizLogicError(message='Failed to submit account registration')
  235. self._log("✅ 操作成功!已弹出提示:Check your email inbox")
  236. return True
  237. def activate(self, sent_at=None):
  238. email = self.account_detail.get('email')
  239. email_box = 'visafly666@gmail.com'
  240. sender = 'TLSContact'
  241. recipient = email
  242. subject_keywords = 'TLSContact'
  243. body_keywords = ''
  244. if not sent_at:
  245. now_utc = datetime.utcnow()
  246. sent_at = now_utc.strftime("%Y-%m-%d %H:%M:%S")
  247. content_out = VSCloudApi.Instance().fetch_mail_content(
  248. email=email_box,
  249. sender=sender,
  250. recipient=recipient,
  251. subject_keywords=subject_keywords,
  252. body_keywords=body_keywords,
  253. sent_date=sent_at,
  254. expiry=600
  255. )
  256. self._log(f'activate email content={content_out}')
  257. match = re.search(r'https://\S+', content_out)
  258. activate_link = match.group(0) if match else None
  259. self.page.get(activate_link)
  260. btn_selector = "#activation-pending-button"
  261. if not self.page.wait.ele_displayed(btn_selector, timeout=10):
  262. raise BizLogicError(message=f"Wait ele={btn_selector} timeout")
  263. self.page.ele(btn_selector).click()
  264. time.sleep(3)
  265. def make_account_useful(self):
  266. def fill_date_field(page, selector, date_str):
  267. if not date_str:
  268. return
  269. ele = page.ele(selector)
  270. ele.scroll.to_see(center=True)
  271. ele.click()
  272. time.sleep(0.2)
  273. year, month, day = date_str.split('-')
  274. page.actions.type(year)
  275. page.actions.type(Keys.TAB)
  276. time.sleep(0.1)
  277. page.actions.type(month)
  278. time.sleep(0.1)
  279. page.actions.type(day)
  280. email = self.account_detail.get('email')
  281. password = self.account_detail.get('pwd')
  282. location = self.account_detail.get('location')
  283. btn_selector = 'tag:button@@text():Login'
  284. if not self.page.wait.ele_displayed(btn_selector, timeout=3):
  285. login_btn = self.page.ele("tag:a@@href:login")
  286. self.mouse.human_click_ele(login_btn)
  287. time.sleep(3)
  288. if not self.page.wait.ele_displayed(btn_selector, timeout=10):
  289. raise BizLogicError(message=f"Can't find selector={btn_selector}")
  290. # recpatchav2_token = ""
  291. # if self.page.ele('.g-recaptcha') or self.page.ele('xpath://iframe[contains(@src, "recaptcha")]'):
  292. # self._log("Solving ReCaptcha...")
  293. # recpatchav2_token = self.solve_captcha(self.page.url, "ReCaptchaV2TaskProxyLess", "6LcDpXcfAAAAAM7wOEsF_38DNsL20tTvPTKxpyn0")
  294. input_ele = self.page.ele('tag:label@@text():Email').next()
  295. self.mouse.human_click_ele(input_ele)
  296. time.sleep(random.uniform(0.2, 0.6))
  297. self.keyboard.type_text(email, humanize=True)
  298. time.sleep(random.uniform(0.5, 1.2))
  299. input_ele = self.page.ele('tag:label@@text():Password').next()
  300. self.mouse.human_click_ele(input_ele)
  301. time.sleep(random.uniform(0.2, 0.6))
  302. self.keyboard.type_text(password, humanize=True)
  303. # if recpatchav2_token:
  304. # inject_recpatchav2_token_js = f"""
  305. # var g = document.getElementById('g-recaptcha-response');
  306. # if(g) {{ g.value = "{recpatchav2_token}"; }}
  307. # """
  308. # self._log("Inject ReCaptchaV2 Token via JS...")
  309. # self.page.run_js(inject_recpatchav2_token_js)
  310. # time.sleep(random.uniform(0.5, 1.0))
  311. self._log("Submitting Login...")
  312. time.sleep(random.uniform(0.3, 0.8))
  313. login_btn = self.page.ele('tag:button@@text():Login')
  314. self.mouse.human_click_ele(login_btn)
  315. self._log("Waiting for dashboard redirect...")
  316. self.page.wait.url_change('login-actions', exclude=True, timeout=45)
  317. time.sleep(4)
  318. if "login-actions" in self.page.url or "auth" in self.page.url:
  319. raise BizLogicError(message="Login Failed! Invalid credentials or Captcha rejected.")
  320. self._log("Waiting for dashboard...")
  321. self.page.wait.load_start()
  322. time.sleep(5)
  323. # 解析 Dashboard 提取 Group ID
  324. # self._log("Parsing Dashboard for Travel Group...")
  325. # html = self.page.html
  326. # js_pattern = r'\\"travelGroups\\":\s*(\[.*?\]),\\"availableCountriesToCreateGroups'
  327. # js_match = re.search(js_pattern, html, re.DOTALL)
  328. # groups = []
  329. # if js_match:
  330. # json_str = js_match.group(1).replace(r'\"', '"')
  331. # groups = json.loads(json_str)
  332. # travel_group = None
  333. # for g in groups:
  334. # if g.get('vacName', '').lower() == location.lower():
  335. # travel_group = g
  336. # break
  337. # if not travel_group:
  338. # raise BizLogicError(message=f"Travel Group not found for {location}")
  339. # formgroup_id = travel_group.get('formGroupId')
  340. # self._log(f"Waiting for group button to render: {formgroup_id}")
  341. # btn_selector = f'tag:a@@data-testid=btn-select-group'
  342. # self._log(f"Select group_id={formgroup_id}...")
  343. # self.mouse.human_click_ele(self.page.ele(btn_selector))
  344. # self._log("Waiting for url redirect...")
  345. # self.page.wait.url_change('travel-groups', exclude=True, timeout=45)
  346. # time.sleep(2)
  347. # if "travel-groups" in self.page.url or "auth" in self.page.url:
  348. # raise BizLogicError(message="Redirect to service-level Failed!")
  349. btn_selector = 'tag:button@@data-testid=btn-add-applicant'
  350. if not self.page.wait.ele_displayed(btn_selector, timeout=10):
  351. btn_selector = 'tag:button@@data-testid=btn-max-number-of-applicants'
  352. if not self.page.ele(btn_selector):
  353. raise BizLogicError(message=f"Can't find selector={btn_selector}")
  354. add_btn = self.page.ele(btn_selector)
  355. add_btn.scroll.to_see(center=True)
  356. time.sleep(random.uniform(0.6, 0.8))
  357. add_btn.click()
  358. time.sleep(6)
  359. visa_type = self.account_detail.get("visa_type")
  360. if visa_type:
  361. btn = self.page.ele('tag:button@@data-testid=input-visa-type')
  362. btn.scroll.to_see(center=True)
  363. btn.click()
  364. time.sleep(0.5)
  365. self.page.ele(f'tag:span@@text():{visa_type}').click(by_js=True)
  366. time.sleep(0.5)
  367. tavel_purpose = self.account_detail.get("travel_purpose")
  368. if tavel_purpose:
  369. btn = self.page.ele('tag:button@@data-testid=input-travel-purpose')
  370. btn.scroll.to_see(center=True)
  371. btn.click()
  372. time.sleep(0.5)
  373. self.page.ele(f'tag:span@@text():{tavel_purpose}').click(by_js=True)
  374. time.sleep(0.5)
  375. application_form_id = self.account_detail.get('application_form_id')
  376. if application_form_id:
  377. ele = self.page.ele('tag:input@@data-testid=f_cai')
  378. ele.scroll.to_see(center=True)
  379. ele.input(application_form_id)
  380. last_name = self.account_detail.get('last_name')
  381. if last_name:
  382. ele = self.page.ele('tag:input@@data-testid=f_pers_surnames')
  383. ele.scroll.to_see(center=True)
  384. ele.input(last_name.upper())
  385. first_name = self.account_detail.get('first_name')
  386. if first_name:
  387. ele = self.page.ele('tag:input@@data-testid=f_pers_givennames')
  388. ele.scroll.to_see(center=True)
  389. ele.input(first_name.upper())
  390. gender = self.account_detail.get('gender')
  391. if gender:
  392. gender = gender.capitalize()
  393. ele = self.page.ele(f'tag:label@@text():{gender}')
  394. ele.scroll.to_see(center=True)
  395. ele.click()
  396. fill_date_field(self.page, 'tag:input@@data-testid=f_pers_birth_date', self.account_detail.get('birthday'))
  397. nationality = self.account_detail.get('nationality')
  398. if nationality:
  399. nationality = nationality.title()
  400. btn = self.page.ele('tag:label@@for=f_pers_nationality').next()
  401. btn.scroll.to_see(center=True)
  402. btn.click()
  403. time.sleep(0.5)
  404. self.page.ele(f'tag:li@@role=option@@text():{nationality}').click(by_js=True)
  405. time.sleep(0.5)
  406. province_residence = self.account_detail.get('province_residence')
  407. if province_residence:
  408. try:
  409. province_residence = province_residence.title()
  410. btn = self.page.ele('tag:label@@for=f_pers_province').next()
  411. btn.scroll.to_see(center=True)
  412. btn.click()
  413. time.sleep(0.5)
  414. self.page.ele(f'tag:li@@role=option@@text():{province_residence}').click(by_js=True)
  415. time.sleep(0.5)
  416. except Exception as e:
  417. self._log(e)
  418. passport_type = self.account_detail.get('passport_type')
  419. if passport_type:
  420. btn = self.page.ele('tag:label@@for=f_identity_type').next()
  421. btn.scroll.to_see(center=True)
  422. btn.click()
  423. time.sleep(0.5)
  424. self.page.ele(f'tag:li@@role=option@@text():{passport_type}').click(by_js=True)
  425. time.sleep(0.5)
  426. passport_no = self.account_detail.get('passport_no')
  427. if passport_no:
  428. ele = self.page.ele('tag:input@@data-testid=f_pass_num')
  429. ele.scroll.to_see(center=True)
  430. ele.input(passport_no.upper())
  431. phone_country_code = self.account_detail.get('phone_country_code')
  432. phone_number = self.account_detail.get('phone_number')
  433. if phone_country_code:
  434. div = self.page.ele('tag:label@@for=f_pers_mobile_phone').next()
  435. btn = div.ele('tag:button')
  436. btn.scroll.to_see(center=True)
  437. btn.click()
  438. time.sleep(0.5)
  439. self.page.ele(f'tag:li@@role=option@@text():+{phone_country_code}').click(by_js=True)
  440. time.sleep(0.5)
  441. div.ele('tag:input@@type:tel').input(phone_number)
  442. # 使用封装好的日期输入函数
  443. fill_date_field(self.page, 'tag:input@@data-testid=fi_trav_origin_departure_date', self.account_detail.get('departure_origin_date'))
  444. fill_date_field(self.page, 'tag:input@@data-testid=f_trav_departure_date', self.account_detail.get('arrival_schengen_area_date'))
  445. fill_date_field(self.page, 'tag:input@@data-testid=f_trav_arrival_date', self.account_detail.get('departure_schengen_area_date'))
  446. submit_btn = self.page.ele('tag:button@@data-testid=btn-submit')
  447. submit_btn.scroll.to_see(center=True)
  448. time.sleep(1)
  449. submit_btn.click()
  450. time.sleep(6)
  451. submit_btn = self.page.ele('tag:button@@text():Confirm')
  452. submit_btn.scroll.to_see(center=True)
  453. submit_btn.click()
  454. def cleanup(self):
  455. """清理浏览器进程和缓存文件夹"""
  456. self._log("Cleaning up resources...")
  457. if self.page:
  458. try: self.page.quit()
  459. except: pass
  460. if os.path.exists(self.workspace):
  461. time.sleep(1) # 等待文件锁释放
  462. shutil.rmtree(self.workspace, ignore_errors=True)
  463. if __name__ == "__main__":
  464. # ================= 配置区域 =================
  465. PROXY_CONFIG = {
  466. "scheme": "http",
  467. "ip": "127.0.0.1",
  468. "port": "7890",
  469. "username": "",
  470. "password": ""
  471. }
  472. CAPSOLVER_KEY = "CAP-5441DD341DD3CC2FAEF0BE6FE493EE9A"
  473. TLS_URL = "https://visas-fr.tlscontact.com/en-us/country/cn/vac/cnCNG2fr"
  474. ACCOUNT_DETAIL = generate_random_account_detail()
  475. # ============================================
  476. try:
  477. bot = TlsRegistrator(TLS_URL, proxy_config=PROXY_CONFIG, capsolver_key=CAPSOLVER_KEY, account_detail=ACCOUNT_DETAIL)
  478. bot.init_browser()
  479. now_utc = datetime.utcnow()
  480. sent_at = now_utc.strftime("%Y-%m-%d %H:%M:%S")
  481. bot.register()
  482. bot.activate(sent_at=sent_at)
  483. bot.make_account_useful()
  484. except Exception as e:
  485. print(f'Exception Info={e}')
  486. time.sleep(3600)