tls_registration_bot.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. import time
  2. import json
  3. import os
  4. import re
  5. import uuid
  6. import socket
  7. import shutil
  8. import requests
  9. from datetime import datetime, timedelta
  10. from DrissionPage import ChromiumPage, ChromiumOptions
  11. from utils.cloudflare_bypass_for_scraping import CloudflareBypasser
  12. from toolkit.vs_cloud_api import VSCloudApi
  13. class TlsRegistrator:
  14. def __init__(self, proxy_config: dict, capsolver_key: str):
  15. self.proxy_config = proxy_config
  16. self.capsolver_key = capsolver_key
  17. # 隔离的用户数据目录
  18. self.instance_id = uuid.uuid4().hex[:8]
  19. self.workspace = os.path.abspath(os.path.join("data", f"reg_session_{self.instance_id}"))
  20. self.page = None
  21. def _log(self, msg):
  22. print(f"[TLS-Reg-{self.instance_id}] {msg}")
  23. def _get_free_port(self):
  24. """获取可用端口,防止 DrissionPage 解析日志报错"""
  25. with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
  26. s.bind(('', 0))
  27. return s.getsockname()[1]
  28. def init_browser(self):
  29. """初始化独立、配置好代理的浏览器环境"""
  30. self._log("Initializing browser...")
  31. co = ChromiumOptions()
  32. # 1. 端口与路径隔离
  33. port = self._get_free_port()
  34. co.set_local_port(port)
  35. co.set_user_data_path(self.workspace)
  36. # 2. 代理配置 (支持账号密码)
  37. if self.proxy_config and self.proxy_config.get("ip"):
  38. p = self.proxy_config
  39. if p.get("username") and p.get("password"):
  40. proxy_str = f"{p['username']}:{p['password']}@{p['ip']}:{p['port']}"
  41. else:
  42. proxy_str = f"{p.get('scheme', 'http')}://{p['ip']}:{p['port']}"
  43. self._log(f"Setting proxy: {p['ip']}:{p['port']}")
  44. co.set_proxy(proxy_str)
  45. # 3. 反爬及稳定性配置
  46. co.headless(False)
  47. co.set_argument('--no-sandbox')
  48. co.set_argument('--disable-gpu')
  49. co.set_argument('--disable-dev-shm-usage')
  50. co.set_argument('--window-size=1920,1080')
  51. co.set_argument('--disable-blink-features=AutomationControlled')
  52. self.page = ChromiumPage(co)
  53. def solve_captcha(self, page_url: str, site_key: str) -> str:
  54. """调用 Capsolver 解决验证码 (无依赖版本)"""
  55. if not self.capsolver_key:
  56. raise ValueError("Capsolver API key is missing!")
  57. self._log("Submitting captcha task to Capsolver...")
  58. task = {
  59. "type": "ReCaptchaV3TaskProxyLess",
  60. "websiteURL": page_url,
  61. "websiteKey": site_key,
  62. "pageAction": "register"
  63. }
  64. payload = {
  65. "clientKey": self.capsolver_key,
  66. "task": task
  67. }
  68. print(f'createTask payload={payload}')
  69. create_res = requests.post("https://api.capsolver.com/createTask", json=payload, timeout=20)
  70. if create_res.status_code != 200 or create_res.json().get("errorId") != 0:
  71. raise Exception(f"Failed to create capsolver task: {create_res.text}")
  72. task_id = create_res.json().get("taskId")
  73. self._log(f"Captcha Task created: {task_id}. Waiting for solution...")
  74. for _ in range(30):
  75. res = requests.post(
  76. "https://api.capsolver.com/getTaskResult",
  77. json={"clientKey": self.capsolver_key, "taskId": task_id},
  78. timeout=20
  79. )
  80. data = res.json()
  81. self._log(f"data={data}")
  82. if data.get("status") == "ready":
  83. self._log("Captcha solved successfully!")
  84. return data.get("solution", {}).get("gRecaptchaResponse") or data.get("solution", {}).get("token")
  85. time.sleep(3)
  86. raise Exception("Capsolver task timeout")
  87. def register(self, email, password, issuer_id="cnCNG2fr"):
  88. """执行自动注册"""
  89. try:
  90. self.init_browser()
  91. # 1. 访问注册页面并过盾
  92. reg_url = f"https://visas-fr.tlscontact.com/en-us/registration?issuerId={issuer_id}"
  93. self._log(f"Navigating to {reg_url}")
  94. self.page.get(reg_url)
  95. cf_bypasser = CloudflareBypasser(self.page, log=True)
  96. cf_bypasser.bypass()
  97. time.sleep(3)
  98. cf_bypasser.handle_waiting_room()
  99. site_key = "6LcTpXcfAAAAAM3VojNhyV-F1z92ADJIvcSZ39Y9"
  100. captcha_token = self.solve_captcha(reg_url, site_key)
  101. self._log("Constructing Next.js fetch request...")
  102. action_id = "608511a5a6e58da30dc13b591255b358caa2193367"
  103. router_state = '%5B%22%22%2C%7B%22children%22%3A%5B%5B%22lang%22%2C%22en-us%22%2C%22d%22%5D%2C%7B%22children%22%3A%5B%22(auth)%22%2C%7B%22children%22%3A%5B%22registration%22%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%2Cnull%2Cnull%5D%7D%2Cnull%2Cnull%5D%7D%2Cnull%2Cnull%5D%7D%2Cnull%2Cnull%2Ctrue%5D%7D%2Cnull%2Cnull%5D'
  104. payload =[
  105. {
  106. "email": email,
  107. "password": password,
  108. "locale": "en",
  109. "issuerId": issuer_id,
  110. "consentList": ["SURVEY"]
  111. },
  112. captcha_token
  113. ]
  114. body_str = json.dumps(payload).replace("'", "\\'")
  115. js_script = f"""
  116. const url = "{reg_url}";
  117. const headers = {{
  118. 'next-action': '{action_id}',
  119. 'next-router-state-tree': '{router_state}',
  120. 'accept': 'text/x-component',
  121. 'content-type': 'text/plain;charset=UTF-8'
  122. }};
  123. const bodyData = '{body_str}';
  124. return fetch(url, {{ method: 'POST', headers: headers, body: bodyData }})
  125. .then(async response => {{
  126. const text = await response.text();
  127. const headers = {{}};
  128. response.headers.forEach((value, key) => headers[key] = value);
  129. return {{ status: response.status, body: text, headers: headers, url: response.url }};
  130. }}).catch(err => {{
  131. return {{ status: 0, body: err.toString(), headers: {{}} }};
  132. }});
  133. """
  134. self._log("Submitting registration...")
  135. res_dict = self.page.run_js(js_script)
  136. # 4. 解析结果 (处理 Next.js Server Action 响应)
  137. status = res_dict.get('status')
  138. body_text = res_dict.get('body', '')
  139. resp_headers = {str(k).lower(): v for k, v in res_dict.get('headers', {}).items()}
  140. action_redirect = resp_headers.get('x-action-redirect', '')
  141. # 【核心修改点】:适配 Next.js 响应中的 "status":"OK"
  142. is_success = (
  143. status == 303 or
  144. (status == 200 and ("login" in action_redirect or '"status":"OK"' in body_text))
  145. )
  146. if is_success:
  147. self._log("✅ Registration SUCCESS!")
  148. self._log(f"Response snippet: {body_text[:100]}...")
  149. return True
  150. else:
  151. self._log(f"❌ Registration FAILED! Status: {status}")
  152. self._log(f"Response Body: {body_text[:500]}")
  153. return False
  154. except Exception as e:
  155. self._log(f"Error during registration: {e}")
  156. return False
  157. def activate(self, email, sent_at=None):
  158. email_box = 'visafly666@gmail.com'
  159. sender = 'TLSContact'
  160. recipient = email
  161. subject_keywords = 'Activate'
  162. body_keywords = ''
  163. content_out = VSCloudApi.Instance().fetch_mail_content(
  164. email=email_box,
  165. sender=sender,
  166. recipient=recipient,
  167. subject_keywords=subject_keywords,
  168. body_keywords=body_keywords,
  169. sent_date=sent_at,
  170. expiry=600
  171. )
  172. print(f'activate email content={content_out}')
  173. match = re.search(r'https://\S+', content_out)
  174. activate_link = match.group(0) if match else None
  175. tab = self.page.new_tab(activate_link)
  176. btn_selector = "Activate"
  177. if not tab.wait.ele_displayed(btn_selector, timeout=10):
  178. return False
  179. tab.ele(btn_selector).click()
  180. def cleanup(self):
  181. """清理浏览器进程和缓存文件夹"""
  182. self._log("Cleaning up resources...")
  183. if self.page:
  184. try: self.page.quit()
  185. except: pass
  186. if os.path.exists(self.workspace):
  187. time.sleep(1) # 等待文件锁释放
  188. shutil.rmtree(self.workspace, ignore_errors=True)
  189. if __name__ == "__main__":
  190. # ================= 配置区域 =================
  191. PROXY_CONFIG = {
  192. "scheme": "http",
  193. "ip": "127.0.0.1",
  194. "port": "7890",
  195. "username": "",
  196. "password": ""
  197. }
  198. CAPSOLVER_KEY = "CAP-5441DD341DD3CC2FAEF0BE6FE493EE9A"
  199. TARGET_EMAIL = "lisi39@gmail-app.com"
  200. TARGET_PWD = "Visafly@111"
  201. TARGET_ISSUER = "cnCNG2fr"
  202. # ============================================
  203. bot = TlsRegistrator(proxy_config=PROXY_CONFIG, capsolver_key=CAPSOLVER_KEY)
  204. now_utc = datetime.utcnow()
  205. formatted_utc_time = (now_utc - timedelta(minutes=4)).strftime("%Y-%m-%d %H:%M:%S")
  206. # success = bot.register(email=TARGET_EMAIL, password=TARGET_PWD, issuer_id=TARGET_ISSUER)
  207. # if not success:
  208. # print("\n--> 流程完成:账号注册失败,请检查日志。")
  209. bot.activate(email=TARGET_EMAIL, sent_at=formatted_utc_time)