# coding=utf-8 import asyncio import logging import os import time import uuid import uiautomator2 as u2 from Apps.XinDianTu.Kit import take_screenshot, detect_black_agree_button, detect_any_ad_close, \ detect_bottom_close_circle, is_background_dimmed from Config.Config import TEMP_IMAGE_DIR # pip install adbutils # 配置日志输出,方便调试和监控 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger("OpenXinDianTu") async def check_and_close_ad(d): """ 检测并关闭广告弹窗(优先使用计算机图形学方案,节省成本和时间) """ logger.info("开始检测广告弹窗...") # 1. 拍摄截图 t1 = time.time() image_uuid = str(uuid.uuid4()) # 使用相对路径: 基于当前脚本目录下的 Images 文件夹 base_dir = os.path.dirname(os.path.abspath(__file__)) save_dir = TEMP_IMAGE_DIR screenshot_path = take_screenshot(d, image_uuid, save_dir=save_dir) logger.info(f"Step [广告检测截图] 耗时: {time.time() - t1:.4f}s") # 1.1 特征预检:检测背景蒙板 is_dimmed = is_background_dimmed(screenshot_path) if is_dimmed: logger.info("检测到背景变暗(蒙板),极大概率存在广告弹窗。") # 2. 图形学检测方案 (Local CV) t_cv = time.time() # 方案 A: 检测黑色的"同意"按钮 (隐私协议) # 这一步不需要模板,直接通过颜色和形状特征检测 agree_pos = detect_black_agree_button(screenshot_path, debug_dir=save_dir) if agree_pos: x, y = agree_pos logger.info(f"通过图形学算法检测到隐私协议'同意'按钮: ({x}, {y})") d.click(x, y) logger.info(f"Step [图形学检测-同意按钮] 耗时: {time.time() - t_cv:.4f}s") # 清理截图 if os.path.exists(screenshot_path): os.remove(screenshot_path) return True # 方案 B: 多模板匹配检测 "关闭(X)" 按钮 (支持多种广告样式) template_dir = os.path.join(base_dir, "Templates") if os.path.exists(template_dir): logger.info(f"正在尝试多模板匹配关闭按钮: {template_dir}") # 如果检测到蒙板,可以适当调低匹配阈值 match_threshold = 0.6 if is_dimmed else 0.7 close_pos = detect_ad_close_x_with_threshold(screenshot_path, template_dir, save_dir, match_threshold) if close_pos: x, y = close_pos logger.info(f"通过多模板匹配检测到广告关闭按钮: ({x}, {y})") d.click(x, y) logger.info(f"Step [图形学检测-关闭按钮] 耗时: {time.time() - t_cv:.4f}s") if os.path.exists(screenshot_path): os.remove(screenshot_path) return True # 方案 C: 底部圆形关闭按钮检测 (无模板,基于几何特征) # 针对插屏广告底部中间的圆形关闭按钮 circle_pos = detect_bottom_close_circle(screenshot_path, debug_dir=save_dir) if circle_pos: x, y = circle_pos logger.info(f"通过几何特征检测到底部圆形关闭按钮: ({x}, {y})") d.click(x, y) logger.info(f"Step [图形学检测-底部圆形按钮] 耗时: {time.time() - t_cv:.4f}s") if os.path.exists(screenshot_path): os.remove(screenshot_path) return True logger.info(f"本地图形学检测完成,未发现已知广告。") # 清理本地截图 if os.path.exists(screenshot_path): os.remove(screenshot_path) return False def detect_ad_close_x_with_threshold(screenshot_path, template_dir, debug_dir, threshold): """ 带自定义阈值的多模板匹配 """ for filename in os.listdir(template_dir): if filename.startswith("ad_close") and filename.endswith(".jpg"): template_path = os.path.join(template_dir, filename) from Apps.XinDianTu.Kit import detect_ad_close_x pos = detect_ad_close_x(screenshot_path, template_path, debug_dir=debug_dir, threshold=threshold) if pos: return pos return None async def open_mini_program(): """ 异步形式的进入微信小程序 """ # 连接设备 d = u2.connect() logger.info("执行进入小程序.") # 启动微信 (强制停止后再启动,确保回到主页) logger.info("启动微信...") d.app_start("com.tencent.mm", stop=True) await asyncio.sleep(5) # 给微信充足的启动时间 # 0. 确保在“微信”消息列表标签页 tab_chat = d(text="微信", resourceId="com.tencent.mm:id/f2s") # 常见底部标签 ID if not tab_chat.exists: tab_chat = d(text="微信") if tab_chat.exists: # 如果没选中(通常选中的文本颜色不同或有其他特征,这里直接点一下确保切换) logger.info("点击底部‘微信’标签,确保在消息列表页.") tab_chat.click() await asyncio.sleep(1) # 1. 点击搜索按钮(放大镜图标) # 仅保留坐标点击逻辑 window_size = d.window_size() w, h = window_size[0], window_size[1] # 精确相对坐标:x=84%, y=8% (基于截图推算) click_x = int(w * 0.84) click_y = int(h * 0.08) logger.info(f"使用精确坐标点击搜索按钮: ({click_x}, {click_y})") d.click(click_x, click_y) # 点击后给予一定的加载时间 await asyncio.sleep(2) # 2. 直接尝试输入内容 (既然已经看到了搜索界面,我们不再进行严格的特征校验) logger.info("准备输入搜索内容: 新电途") # 启用 u2 快速输入法 (使用更新的方法名) d.set_input_ime(True) # 尝试点击搜索框位置以确保获取焦点 (通常在顶部居中偏左) search_bar_x, search_bar_y = int(w * 0.4), int(h * 0.08) d.click(search_bar_x, search_bar_y) await asyncio.sleep(1.5) # 稍微多等一会儿让输入法准备好 # 直接发送按键 try: # 尝试先不使用 clear=True,因为它在某些设备上会导致 ADB_KEYBOARD_CLEAR_TEXT 错误 # 如果需要清除文本,可以尝试全选+删除,或者假设搜索框是空的 logger.info("尝试输入文字: 新电途") d.send_keys("新电途") logger.info("文字输入指令已发送.") except Exception as e: logger.warning(f"直接 send_keys 失败: {e}") try: # 备选方案:尝试获取焦点元素并设置文本 logger.info("尝试使用 set_text 设置文本...") d(focused=True).set_text("新电途") logger.info("set_text 指令已发送.") except Exception as e2: logger.error(f"文字输入彻底失败: {e2}") # 最后的尝试:切换回默认输入法(有时 FastInputIME 会卡住) # d.set_fastinput_ime(False) # d.send_keys("新电途") await asyncio.sleep(3) # 兜底点击:点击搜索结果的第一项位置 (x=50%, y=18%) res_x, res_y = int(w * 0.5), int(h * 0.18) logger.info(f"尝试坐标点击第一项: ({res_x}, {res_y})") d.click(res_x, res_y) await asyncio.sleep(10) # 进入小程序后,检查并处理广告 await check_and_close_ad(d) return True async def main(): success = await open_mini_program() if success: logger.info("任务执行成功.") else: logger.error("任务执行失败.") if __name__ == "__main__": try: asyncio.run(main()) except KeyboardInterrupt: logger.info("程序被用户中断.") except Exception as e: logger.exception(f"运行过程中出现异常: {e}")