root 3 nedēļas atpakaļ
vecāks
revīzija
4bda276400
3 mainītis faili ar 127 papildinājumiem un 77 dzēšanām
  1. 11 11
      config/config.json
  2. 43 41
      docker-compose.yml
  3. 73 25
      tls_registration_bot.py

+ 11 - 11
config/config.json

@@ -9,7 +9,7 @@
         {
             "identifier": "vfs.ie.nl",
             "debug": false,
-            "enable": false,
+            "enable": true,
             "need_account": true,
             "need_proxy": true,
             "proxy_pool": "isp_all",
@@ -27,7 +27,7 @@
                 "account_pool_id": "ie.nl.booker",
                 "target_instances": 1,
                 "account_cd": 180,
-                "booking_cooldown": 10,
+                "booking_cooldown": 300,
                 "max_bookings_per_account": 8
             },
             "query_wait": {
@@ -453,7 +453,7 @@
         {
             "identifier": "vfs.ie.at",
             "debug": false,
-            "enable": false,
+            "enable": true,
             "need_account": true,
             "need_proxy": true,
             "proxy_pool": "isp_all",
@@ -471,7 +471,7 @@
                 "account_pool_id": "ie.at.booker",
                 "target_instances": 1,
                 "account_cd": 180,
-                "booking_cooldown": 10,
+                "booking_cooldown": 300,
                 "max_bookings_per_account": 8
             },
             "query_wait": {
@@ -651,7 +651,7 @@
         {
             "identifier": "vfs.ie.hu",
             "debug": false,
-            "enable": false,
+            "enable": true,
             "need_account": true,
             "need_proxy": true,
             "proxy_pool": "isp_all",
@@ -669,7 +669,7 @@
                 "account_pool_id": "ie.hu.booker",
                 "target_instances": 1,
                 "account_cd": 180,
-                "booking_cooldown": 10,
+                "booking_cooldown": 180,
                 "max_bookings_per_account": 8
             },
             "query_wait": {
@@ -1190,7 +1190,7 @@
         {
             "identifier": "visaonweb.ie.be",
             "debug": false,
-            "enable": true,
+            "enable": false,
             "need_account": true,
             "need_proxy": true,
             "proxy_pool": "local",
@@ -1339,12 +1339,12 @@
         {
             "identifier": "greekemba.ie.gr",
             "debug": false,
-            "enable": false,
+            "enable": true,
             "need_account": true,
             "need_proxy": true,
             "proxy_pool": "isp_all",
             "proxy_cd": 5,
-            "session_max_life": 600,
+            "session_max_life": 1440,
             "sentinel": {
                 "account_source": "built-in",
                 "account_pool_id": "ie.gr.sentinel",
@@ -1354,9 +1354,9 @@
             },
             "booker": {
                 "account_source": "order",
-                "target_instances": 1,
+                "target_instances": 10,
                 "account_cd": 180,
-                "booking_cooldown": 10,
+                "booking_cooldown": 30,
                 "max_bookings_per_account": 1
             },
             "query_wait": {

+ 43 - 41
docker-compose.yml

@@ -25,46 +25,47 @@ services:
           cpus: '2.0'
           memory: 4G
 
-  # visa-booker:
-  #   build: .
-  #   image: coordinator:latest
-  #   container_name: coordinator-booker
-  #   command: ["python3", "main_booker.py"]
-  #   restart: unless-stopped
-  #   shm_size: '2gb'
-  #   volumes:
-  #     - ./config:/app/config
-  #     - ./logs:/app/logs
-  #     - ./data:/app/data
-  #     - ./plugins:/app/plugins
-  #   environment:
-  #     - TZ=Asia/Shanghai
-  #     - DISPLAY=:99
-  #     - CHROME_BIN=/opt/ungoogled-chromium/chrome
-  #   # 资源限制
-  #   deploy:
-  #     resources:
-  #       limits:
-  #         cpus: '2.0'
-  #         memory: 4G
+  visa-booker:
+    build: .
+    image: coordinator:latest
+    container_name: coordinator-booker
+    command: ["python3", "main_booker.py"]
+    restart: unless-stopped
+    shm_size: '2gb'
+    volumes:
+      - ./config:/app/config
+      - ./logs:/app/logs
+      - ./data:/app/data
+      - ./plugins:/app/plugins
+    environment:
+      - TZ=Asia/Shanghai
+      - DISPLAY=:99
+      - CHROME_BIN=/opt/ungoogled-chromium/chrome
+    # 资源限制
+    deploy:
+      resources:
+        limits:
+          cpus: '2.0'
+          memory: 4G
 
-  # visa-sweeper:
-  #   build: .
-  #   image: coordinator:latest
-  #   container_name: coordinator-sweeper
-  #   command: ["python3", "main_sweeper.py"]
-  #   restart: unless-stopped
-  #   volumes:
-  #     - ./config:/app/config
-  #     - ./logs:/app/logs
-  #   environment:
-  #     - TZ=Asia/Shanghai
-  #   # 资源限制极低,因为它只是个网络请求脚本,不运行浏览器
-  #   deploy:
-  #     resources:
-  #       limits:
-  #         cpus: '0.2'
-  #         memory: 256M
+  visa-sweeper:
+    build: .
+    image: coordinator:latest
+    container_name: coordinator-sweeper
+    command: ["python3", "main_sweeper.py"]
+    restart: unless-stopped
+    volumes:
+      - ./config:/app/config
+      - ./data:/app/data
+      - ./logs:/app/logs
+    environment:
+      - TZ=Asia/Shanghai
+    # 资源限制极低,因为它只是个网络请求脚本,不运行浏览器
+    deploy:
+      resources:
+        limits:
+          cpus: '0.2'
+          memory: 256M
 
   registration-bot:
     build: .
@@ -72,9 +73,10 @@ services:
     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"]
+    command: ["python3", "-u" ,"tls_registration_bot.py", "-n", "1", "-m", "50", "-p", "isp_all", "-u", "https://visas-fr.tlscontact.com/en-us/country/gb/vac/gbLON2fr"]
     volumes:
-      - ./config:/app/config     # 挂载代理配置文件
+      - ./config:/app/config
+      - ./data:/app/data
     deploy:
       resources:
         limits:

+ 73 - 25
tls_registration_bot.py

@@ -16,12 +16,14 @@ from DrissionPage.common import Keys
 from DrissionPage import ChromiumPage, ChromiumOptions
 from utils.cloudflare_bypass_for_scraping import CloudflareBypasser
 from toolkit.vs_cloud_api import VSCloudApi
+from toolkit.proxy_tunnel import ProxyTunnel
 from vs_types import NotFoundError, PermissionDeniedError, RateLimiteddError, SessionExpiredOrInvalidError, BizLogicError 
 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 生成随机账户信息,并保留指定固定字段。"""
     today = datetime.today()
@@ -137,19 +139,34 @@ class TlsRegistrator:
         self.instance_id = uuid.uuid4().hex[:8]
         self.tls_url = tls_url
         # self.instance_id = '18d389e9'
-        self.workspace = os.path.abspath(os.path.join("data", f"reg_session_{self.instance_id}"))
+        self.workspace = os.path.abspath(os.path.join("data/temp_browser_data", f"reg_session_{self.instance_id}"))
         self.page = None
         self.mouse = None
         self.keyboard = None
+        
+        # 持有隧道实例
+        self.tunnel = None
 
     def _log(self, msg):
-        print(f"[TLS-Reg-{self.instance_id}] {msg}")
+        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+        print(f"[{now}][TLS-Reg-{self.instance_id}] {msg}")
 
     def _get_free_port(self):
         """获取可用端口,防止 DrissionPage 解析日志报错"""
         with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
             s.bind(('', 0))
             return s.getsockname()[1]
+        
+    def save_screenshot(self, name_prefix):
+        try:
+            timestamp = int(time.time())
+            filename = f"{self.instance_id}_{name_prefix}_{timestamp}.jpg"
+            save_path = os.path.join("data", filename)
+            os.makedirs("data", exist_ok=True)
+            self.page.get_screenshot(path=save_path, full_page=False)
+            self._log(f"Screenshot saved to {save_path}")
+        except Exception as e:
+            self._log(f"Failed to save screenshot: {e}")
 
     def init_browser(self):
         """初始化独立、配置好代理的浏览器环境"""
@@ -161,16 +178,23 @@ class TlsRegistrator:
         co.set_local_port(port)
         co.set_user_data_path(self.workspace)
         
+        chrome_path = os.getenv("CHROME_BIN")
+        if chrome_path and os.path.exists(chrome_path):
+            co.set_paths(browser_path=chrome_path)
+        
         # 2. 代理配置 (支持账号密码)
         if self.proxy_config and self.proxy_config.get("ip"):
             p = self.proxy_config
             if p.get("username") and p.get("password"):
-                proxy_str = f"{p['username']}:{p['password']}@{p['ip']}:{p['port']}"
+                self.tunnel = ProxyTunnel(p['ip'], p['port'], p['username'], p['password'])
+                local_proxy = self.tunnel.start()
+                self._log(f"Tunnel started at {local_proxy}")
+                co.set_argument(f'--proxy-server={local_proxy}')
             else:
                 proxy_str = f"{p.get('scheme', 'http')}://{p['ip']}:{p['port']}"
-            
-            self._log(f"Setting proxy: {p['ip']}:{p['port']}")
-            co.set_proxy(proxy_str)
+                co.set_argument(f'--proxy-server={proxy_str}')
+        else:
+            self._log("[WARN] No proxy configured!")
 
         fingerprint_gen = FingerprintGenerator()
         specific_fp = fingerprint_gen.generate(self.instance_id)
@@ -187,9 +211,10 @@ class TlsRegistrator:
         co.set_argument(f"--fingerprint-brand={specific_fp.get('brand')}")
         self.page = ChromiumPage(co)
         self.page.get(self.tls_url)
+        time.sleep(5)
         cf_bypasser = CloudflareBypasser(self.page, log=True)
-        cf_bypasser.bypass()
-        time.sleep(3) 
+        cf_bypasser.bypass(max_retry=8)
+        time.sleep(3)
         cf_bypasser.handle_waiting_room()
         
         self._log("正在初始化拟人化工具...")
@@ -340,17 +365,42 @@ class TlsRegistrator:
         def fill_date_field(page, selector, date_str):
             if not date_str:
                 return
+                
             ele = page.ele(selector)
             ele.scroll.to_see(center=True)
-            ele.click()
-            time.sleep(0.2)
+            
+            js_detect_format = """
+                const parts = new Intl.DateTimeFormat().formatToParts(new Date(2023, 11, 31));
+                let format = [];
+                for (let part of parts) {
+                    if (part.type === 'year') format.push('Y');
+                    if (part.type === 'month') format.push('M');
+                    if (part.type === 'day') format.push('D');
+                }
+                return format;
+            """
+            date_format = page.run_js(js_detect_format)
+            
             year, month, day = date_str.split('-')
-            page.actions.type(year)
-            page.actions.type(Keys.TAB)
+            date_dict = {
+                'Y': year,
+                'M': month.zfill(2),
+                'D': day.zfill(2)
+            }
+            ele.click()
             time.sleep(0.1)
-            page.actions.type(month)
+            page.actions.type(Keys.LEFT * 3)
             time.sleep(0.1)
-            page.actions.type(day)
+            for i, char in enumerate(date_format):
+                val = date_dict[char]
+                page.actions.type(val)
+                time.sleep(0.1)
+                if char == 'Y':
+                    if i < 2:
+                        page.actions.type(Keys.RIGHT)
+                        time.sleep(0.1)
+                else:
+                    pass
         
         email = self.account_detail.get('email')
         password = self.account_detail.get('pwd')
@@ -592,6 +642,7 @@ class TlsRegistrator:
         submit_btn = self.page.ele('tag:button@@text():Confirm')
         submit_btn.scroll.to_see(center=True)
         submit_btn.click()
+        time.sleep(6)
         
     def upload_account_to_server(self):
         """
@@ -641,9 +692,7 @@ class TlsRegistrator:
             
 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"} 
-    
+    account_detail = generate_random_account_detail()
     bot = None
     try:
         bot = TlsRegistrator(
@@ -661,22 +710,21 @@ def register_worker(proxy_config, tls_url, capsolver_key):
         bot.activate(sent_at=sent_at)
         bot.make_account_useful()
         bot.upload_account_to_server()
-        
+        bot.save_screenshot(f'success_{account_detail.get("email")}')
         print(f"[SUCCESS] 账号 {account_detail.get('email')} 注册成功! 使用代理: {proxy_config.get('ip')}")
         return True
         
     except Exception as e:
         print(f"[ERROR] 注册失败 | 代理 IP: {proxy_config.get('ip')} | 异常信息: {e}")
+        bot.save_screenshot('tls_registration_failed')
         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
+        if bot:
+            try:
+                bot.cleanup()
+            except:
+                pass
 
 
 def main():