diff --git a/worker/tasks/boss_recruit.py b/worker/tasks/boss_recruit.py index 8255c6b..8ac5a3d 100644 --- a/worker/tasks/boss_recruit.py +++ b/worker/tasks/boss_recruit.py @@ -395,67 +395,123 @@ class BossRecruitHandler(BaseTaskHandler): ) return added, records + @staticmethod + def _close_all_panels(page): + """ + 强制关闭所有可能打开的面板和弹窗,确保 UI 恢复到干净状态。 + 每一步都独立 try/except,不管成功或失败都继续。 + """ + # 关闭侧边聊天面板 + try: + close_side = page.ele('x://*[@class="iboss iboss-close"]', timeout=1) + if close_side: + close_side.click(by_js=True) + time.sleep(0.3) + except Exception: + pass + + # 关闭 boss-popup 弹窗 + try: + close_popup = page.ele('x://*[@class="boss-popup__close"]', timeout=1) + if close_popup: + close_popup.click(by_js=True) + time.sleep(0.3) + except Exception: + pass + + # 兜底:尝试关闭所有可能的弹窗关闭按钮 + for selector in [ + 'x://*[contains(@class,"close") and contains(@class,"popup")]', + 'x://*[contains(@class,"dialog")]//button[contains(@class,"close")]', + ]: + try: + btn = page.ele(selector, timeout=0.5) + if btn: + btn.click(by_js=True) + time.sleep(0.2) + except Exception: + pass + def _greet_one_geek(self, page, container, item: dict) -> bool: """ - 对单个牛人打招呼,完全对齐 1.py _greet_one_geek 逻辑: - 1. 找姓名 → scrollIntoView → 点击姓名 - 2. 获取 frame(1)(右侧面板) - 3. 点击"打招呼" - 4. 点击"收藏" - 5. 点击 3 次 btn-outline 按钮 - 6. 输入快速回复 → 发送 - 7. 关闭侧边栏 + 关闭弹窗 + 对单个牛人打招呼,对齐 1.py _greet_one_geek 逻辑。 + 关键改进:使用 finally 确保失败时关闭所有面板/弹窗, + 防止 UI 残留导致下一个人卡死在循环中。 """ geek_name = str((item.get("geekCard") or {}).get("geekName", "")).strip() if not geek_name: return False + panel_opened = False # 标记是否打开了右侧面板 + try: - # 查找姓名元素(与 1.py 一致的双重查找) + # 查找姓名元素 name_ele = container.ele(f'x://span[contains(text(),"{geek_name}")]', timeout=5) if not name_ele: name_ele = container.ele(f'x://span[text()="{geek_name}"]', timeout=2) if not name_ele: return False - # scrollIntoView + 点击(与 1.py 一致) + # scrollIntoView + 点击 name_ele.run_js("this.scrollIntoView()") name_ele.click() time.sleep(3) + panel_opened = True - # 获取右侧面板(与 1.py 一致:page.get_frame(1)) + # 获取右侧面板 panel = page.get_frame(1) time.sleep(random.uniform(0.5, 1.2)) - # 打招呼(与 1.py 一致:by_js=True) - panel.ele('x://*[contains(text(),"打招呼")]', timeout=2).click(by_js=True) + # 打招呼 + greet_btn = panel.ele('x://*[contains(text(),"打招呼")]', timeout=2) + if not greet_btn: + return False + greet_btn.click(by_js=True) time.sleep(random.uniform(0.5, 1.2)) - # 收藏(与 1.py 一致) - panel.ele('x://*[contains(text(),"收藏")]', timeout=2).click(by_js=True) - time.sleep(random.uniform(0.5, 1.2)) + # 收藏(可选,失败不影响) + try: + collect_btn = panel.ele('x://*[contains(text(),"收藏")]', timeout=2) + if collect_btn: + collect_btn.click(by_js=True) + time.sleep(random.uniform(0.5, 1.2)) + except Exception: + pass - # 点击 3 次 btn-outline 按钮(与 1.py 一致) + # 点击 btn-outline 按钮(可选,失败不影响) for _ in range(3): - panel.ele('x://*[@class="btn-v2 btn-outline-v2"]', timeout=2).click(by_js=True) - time.sleep(random.uniform(0.5, 1.2)) + try: + extra_btn = panel.ele('x://*[@class="btn-v2 btn-outline-v2"]', timeout=1) + if extra_btn: + extra_btn.click(by_js=True) + time.sleep(random.uniform(0.5, 1.2)) + except Exception: + break - # 快速回复(与 1.py 一致) - page.ele('x://*[@data-placeholder="快速回复"]', timeout=2).input(FAST_REPLY_TEXT) - page.ele('x://*[contains(text(),"发送")]', timeout=2).click() - time.sleep(random.uniform(0.5, 1.2)) - - # 关闭侧边栏(与 1.py 一致) - page.ele('x://*[@class="iboss iboss-close"]').click() - time.sleep(random.uniform(0.5, 1.2)) - - # 关闭弹窗(与 1.py 一致) - panel.ele('x://*[@class="boss-popup__close"]').click() - time.sleep(random.uniform(0.5, 1.2)) + # 快速回复(可选,失败不影响) + try: + input_box = page.ele('x://*[@data-placeholder="快速回复"]', timeout=2) + if input_box: + input_box.input(FAST_REPLY_TEXT) + send_btn = page.ele('x://*[contains(text(),"发送")]', timeout=2) + if send_btn: + send_btn.click() + time.sleep(random.uniform(0.5, 1.2)) + except Exception: + pass + # 关闭面板 + self._close_all_panels(page) + panel_opened = False return True + except Exception: return False + finally: + # 无论成功还是失败,都确保关闭所有面板 + if panel_opened: + self._close_all_panels(page) + time.sleep(0.5) # ────────────────────── 联系记录入库 ──────────────────────