jerry há 3 semanas atrás
pai
commit
d0090cb73c
2 ficheiros alterados com 133 adições e 24 exclusões
  1. 15 1
      docker-compose.yml
  2. 118 23
      tls_registration_bot.py

+ 15 - 1
docker-compose.yml

@@ -64,4 +64,18 @@ services:
   #     resources:
   #       limits:
   #         cpus: '0.2'
-  #         memory: 256M
+  #         memory: 256M
+
+  registration-bot:
+    build: .
+    image: coordinator:latest
+    container_name: tls-bot-worker
+    environment:
+      - CAPSOLVER_KEY=CAP-5441DD341DD3CC2FAEF0BE6FE493EE9A
+    command: ["python3", "-u" ,"tls_registration_bot.py", "-n", "3", "-m", "10", "-p", "isp_all", "-u", "https://visas-fr.tlscontact.com/en-us/country/gb/vac/gbLON2fr"]
+    volumes:
+      - ./config:/app/config     # 挂载代理配置文件
+    deploy:
+      resources:
+        limits:
+          memory: 2G

+ 118 - 23
tls_registration_bot.py

@@ -7,6 +7,8 @@ import socket
 import shutil
 import random
 import requests
+import argparse
+import concurrent.futures
 from urllib.parse import urlencode
 from datetime import datetime, timedelta
 from typing import Optional, Dict
@@ -18,6 +20,7 @@ from vs_types import NotFoundError, PermissionDeniedError, RateLimiteddError, Se
 from utils.mouse import HumanMouse
 from utils.keyboard import HumanKeyboard
 from utils.scroll import HumanScroll
+from utils.fingerprint_utils import FingerprintGenerator
 
 def generate_random_account_detail() -> Dict:
     """基于 randomuser 生成随机账户信息,并保留指定固定字段。"""
@@ -110,6 +113,20 @@ def generate_random_account_detail() -> Dict:
         }
     except Exception:
         return default_payload
+    
+def load_proxies(pool_name):
+    """从 config/proxies.json 读取对应的代理池"""
+    config_path = os.path.join(os.path.dirname(__file__), 'config', 'proxies.json')
+    try:
+        with open(config_path, 'r', encoding='utf-8') as f:
+            data = json.load(f)
+            proxies = data.get(pool_name, [])
+            if not proxies:
+                raise ValueError(f"代理池 '{pool_name}' 为空或不存在!")
+            return proxies
+    except Exception as e:
+        print(f"读取代理配置文件失败: {e}")
+        exit(1)
 
 class TlsRegistrator:
     def __init__(self, tls_url, proxy_config: Optional[Dict]=None, capsolver_key: Optional[str]=None, account_detail: Optional[Dict]=None):
@@ -117,9 +134,9 @@ class TlsRegistrator:
         self.capsolver_key = capsolver_key
         self.account_detail = account_detail
         # 隔离的用户数据目录
-        #self.instance_id = uuid.uuid4().hex[:8]
+        self.instance_id = uuid.uuid4().hex[:8]
         self.tls_url = tls_url
-        self.instance_id = '18d389e9'
+        # self.instance_id = '18d389e9'
         self.workspace = os.path.abspath(os.path.join("data", f"reg_session_{self.instance_id}"))
         self.page = None
         self.mouse = None
@@ -155,13 +172,19 @@ class TlsRegistrator:
             self._log(f"Setting proxy: {p['ip']}:{p['port']}")
             co.set_proxy(proxy_str)
 
+        fingerprint_gen = FingerprintGenerator()
+        specific_fp = fingerprint_gen.generate(self.instance_id)
+        self._log(f'browser fingerprint={specific_fp}')
         # 3. 反爬及稳定性配置
         co.headless(False)
         co.set_argument('--no-sandbox')
-        co.set_argument('--disable-gpu')
+        # co.set_argument('--disable-gpu')
         co.set_argument('--disable-dev-shm-usage')
         co.set_argument('--window-size=1920,1080')
         co.set_argument('--disable-blink-features=AutomationControlled')
+        co.set_argument(f"--fingerprint={specific_fp.get('seed')}")
+        co.set_argument(f"--fingerprint-platform={specific_fp.get('platform')}")
+        co.set_argument(f"--fingerprint-brand={specific_fp.get('brand')}")
         self.page = ChromiumPage(co)
         self.page.get(self.tls_url)
         cf_bypasser = CloudflareBypasser(self.page, log=True)
@@ -615,32 +638,104 @@ class TlsRegistrator:
         if os.path.exists(self.workspace):
             time.sleep(1) # 等待文件锁释放
             shutil.rmtree(self.workspace, ignore_errors=True)
-
-
-if __name__ == "__main__":
-    # ================= 配置区域 =================
-    
-    PROXY_CONFIG = {
-        "scheme": "http",
-        "ip": "127.0.0.1",   
-        "port": "7890",      
-        "username": "",      
-        "password": ""
-    }
+            
+def register_worker(proxy_config, tls_url, capsolver_key):
+    """单个注册任务的工作线程函数"""
+    # account_detail = generate_random_account_detail() # 替换为你的实际调用
+    account_detail = {"email": f"test_{random.randint(1000,9999)}@test.com"} 
     
-    CAPSOLVER_KEY = "CAP-5441DD341DD3CC2FAEF0BE6FE493EE9A"
-    TLS_URL =  "https://visas-fr.tlscontact.com/en-us/country/gb/vac/gbLON2fr"
-    ACCOUNT_DETAIL = generate_random_account_detail()
-    # ============================================
+    bot = None
     try:
-        bot = TlsRegistrator(TLS_URL, proxy_config=PROXY_CONFIG, capsolver_key=CAPSOLVER_KEY, account_detail=ACCOUNT_DETAIL)
-        bot.init_browser()
+        bot = TlsRegistrator(
+            tls_url, 
+            proxy_config=proxy_config, 
+            capsolver_key=capsolver_key, 
+            account_detail=account_detail
+        )
+        bot.init_browser() # ⚠️ 记得在 Docker 中必须是 headless 无头模式
+        
         now_utc = datetime.utcnow()
         sent_at = now_utc.strftime("%Y-%m-%d %H:%M:%S")
+        
         bot.register() 
         bot.activate(sent_at=sent_at)
         bot.make_account_useful()
         bot.upload_account_to_server()
+        
+        print(f"[SUCCESS] 账号 {account_detail.get('email')} 注册成功! 使用代理: {proxy_config.get('ip')}")
+        return True
+        
     except Exception as e:
-        print(f'Exception Info={e}')
-        time.sleep(3600)
+        print(f"[ERROR] 注册失败 | 代理 IP: {proxy_config.get('ip')} | 异常信息: {e}")
+        return False
+        
+    finally:
+        # ⚠️ 极度重要:无论成功或失败,必须关闭浏览器释放内存!
+        if bot and hasattr(bot, 'close'):
+            try: bot.close()
+            except: pass
+        elif bot and hasattr(bot, 'quit'):
+            try: bot.quit()
+            except: pass
+
+
+def main():
+    # ================= 命令行参数解析 =================
+    parser = argparse.ArgumentParser(description="TLS 批量注册机")
+    parser.add_argument("-n", "--concurrency", type=int, default=5, help="最大并发数 (N)")
+    parser.add_argument("-m", "--target", type=int, default=10, help="最大成功注册数 (M)")
+    parser.add_argument("-p", "--pool", type=str, default="isp_all", help="代理池名称")
+    parser.add_argument("-u", "--url", type=str, default="https://visas-fr.tlscontact.com/en-us/country/gb/vac/gbLON2fr", help="TLS 目标网址")
+    args = parser.parse_args()
+
+    # ================= 环境变量读取 =================
+    capsolver_key = os.getenv("CAPSOLVER_KEY")
+    if not capsolver_key:
+        print("[FATAL] 未设置环境变量 CAPSOLVER_KEY,程序退出!")
+        exit(1)
+
+    print(f"[*] 启动注册任务 | 目标数: {args.target} | 并发数: {args.concurrency} | 代理池: {args.pool} | URL: {args.url}")
+    
+    proxies = load_proxies(args.pool)
+    print(f"[*] 成功加载代理数量: {len(proxies)} 个")
+
+    success_count = 0
+    active_tasks = 0
+
+    # 使用线程池维持并发
+    with concurrent.futures.ThreadPoolExecutor(max_workers=args.concurrency) as executor:
+        futures = {}
+
+        # 1. 初始填充任务队列
+        while active_tasks < args.concurrency and (success_count + active_tasks) < args.target:
+            proxy = random.choice(proxies)
+            fut = executor.submit(register_worker, proxy, args.url, capsolver_key)
+            futures[fut] = proxy
+            active_tasks += 1
+
+        # 2. 调度循环
+        while futures:
+            done, _ = concurrent.futures.wait(futures, return_when=concurrent.futures.FIRST_COMPLETED)
+            
+            for fut in done:
+                proxy = futures.pop(fut)
+                active_tasks -= 1
+                
+                try:
+                    if fut.result():
+                        success_count += 1
+                        print(f"[*] 进度更新: {success_count} / {args.target}")
+                except Exception as e:
+                    print(f"[FATAL] 线程未捕获异常: {e}")
+
+                # 如果还没达到目标,补入新任务
+                if (success_count + active_tasks) < args.target:
+                    new_proxy = random.choice(proxies)
+                    new_fut = executor.submit(register_worker, new_proxy, args.url, capsolver_key)
+                    futures[new_fut] = new_proxy
+                    active_tasks += 1
+
+    print(f"[*] 任务结束!共成功注册 {success_count} 个账号。")
+
+if __name__ == "__main__":
+    main()