Files
aiData/Apps/XinDianTu/Opener.py

264 lines
10 KiB
Python
Raw Normal View History

2026-01-12 07:49:18 +08:00
# coding=utf-8
import asyncio
import logging
2026-01-12 08:26:13 +08:00
import os
2026-01-12 07:49:18 +08:00
import time
import uuid
2026-01-12 08:26:13 +08:00
2026-01-12 07:49:18 +08:00
import uiautomator2 as u2
2026-01-12 08:26:13 +08:00
from Apps.XinDianTu.Kit import take_screenshot, detect_black_agree_button, detect_any_ad_close, \
2026-01-12 13:59:06 +08:00
detect_bottom_close_circle, is_background_dimmed, click_image_template
2026-01-12 16:53:43 +08:00
from Apps.XinDianTu.ReadImageKit import ReadImageKit
2026-01-12 08:26:13 +08:00
from Config.Config import TEMP_IMAGE_DIR
2026-01-12 07:49:18 +08:00
# pip install adbutils
# 配置日志输出,方便调试和监控
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("OpenXinDianTu")
2026-01-12 13:59:06 +08:00
# 获取当前脚本所在目录
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
2026-01-12 07:49:18 +08:00
async def check_and_close_ad(d):
"""
检测并关闭广告弹窗(优先使用计算机图形学方案,节省成本和时间)
"""
logger.info("开始检测广告弹窗...")
# 1. 拍摄截图
t1 = time.time()
image_uuid = str(uuid.uuid4())
# 使用相对路径: 基于当前脚本目录下的 Images 文件夹
2026-01-12 08:09:32 +08:00
save_dir = TEMP_IMAGE_DIR
2026-01-12 07:49:18 +08:00
screenshot_path = take_screenshot(d, image_uuid, save_dir=save_dir)
logger.info(f"Step [广告检测截图] 耗时: {time.time() - t1:.4f}s")
2026-01-12 08:48:06 +08:00
# 1.1 特征预检:检测背景蒙板
is_dimmed = is_background_dimmed(screenshot_path)
if is_dimmed:
logger.info("检测到背景变暗(蒙板),极大概率存在广告弹窗。")
2026-01-12 07:49:18 +08:00
# 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)" 按钮 (支持多种广告样式)
2026-01-12 13:59:06 +08:00
template_dir = os.path.join(BASE_DIR, "Templates")
2026-01-12 07:49:18 +08:00
if os.path.exists(template_dir):
logger.info(f"正在尝试多模板匹配关闭按钮: {template_dir}")
2026-01-12 08:48:06 +08:00
# 如果检测到蒙板,可以适当调低匹配阈值
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)
2026-01-12 07:49:18 +08:00
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
2026-01-12 16:53:43 +08:00
# 3. 视觉大模型检测方案 (VL Model) - 作为兜底
logger.info("本地图形学检测未发现广告,尝试调用视觉大模型检测...")
try:
window_size = d.window_size()
device_info = {
"width": window_size[0],
"height": window_size[1],
"productName": d.info.get('productName', 'unknown')
}
ad_result = await ReadImageKit.detect_ad(screenshot_path, device_info=device_info)
if ad_result.get("has_ad") and ad_result.get("uia_center_x") is not None:
x, y = ad_result["uia_center_x"], ad_result["uia_center_y"]
logger.info(f"通过视觉大模型检测到广告关闭按钮: ({x}, {y})")
d.click(x, y)
if os.path.exists(screenshot_path):
os.remove(screenshot_path)
return True
except Exception as e:
logger.error(f"视觉大模型广告检测异常: {e}")
logger.info(f"本地图形学和 VL 检测完成,未发现已知广告。")
2026-01-12 07:49:18 +08:00
# 清理本地截图
if os.path.exists(screenshot_path):
os.remove(screenshot_path)
return False
2026-01-12 08:48:06 +08:00
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
2026-01-12 07:49:18 +08:00
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. 点击搜索按钮(放大镜图标)
# 仅保留坐标点击逻辑
2026-01-12 13:48:14 +08:00
# 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)
# 点击搜索按钮(放大镜图标)
logger.info("尝试查找并点击 '搜索按钮(放大镜图标)'...")
2026-01-12 13:59:06 +08:00
search_template = os.path.join(BASE_DIR, "Templates", "SearchButton.jpg")
if not os.path.exists(search_template):
search_template = os.path.join(BASE_DIR, "SearchButton.jpg")
if click_image_template(d, search_template):
logger.info("点击了搜索按钮(放大镜图标)")
else:
logger.warning("未找到搜索按钮(放大镜图标),尝试兜底坐标点击...")
# 兜底点击:精确相对坐标:x=84%, y=8% (基于截图推算)
window_size = d.window_size()
w, h = window_size[0], window_size[1]
click_x = int(w * 0.84)
click_y = int(h * 0.08)
d.click(click_x, click_y)
logger.info(f"使用精确坐标点击搜索按钮: ({click_x}, {click_y})")
2026-01-12 07:49:18 +08:00
# 点击后给予一定的加载时间
await asyncio.sleep(2)
# 2. 直接尝试输入内容 (既然已经看到了搜索界面,我们不再进行严格的特征校验)
logger.info("准备输入搜索内容: 新电途")
# 启用 u2 快速输入法 (使用更新的方法名)
2026-01-12 13:48:14 +08:00
# d.set_input_ime(True)
2026-01-12 07:49:18 +08:00
# 尝试点击搜索框位置以确保获取焦点 (通常在顶部居中偏左)
2026-01-12 13:48:14 +08:00
# 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) # 稍微多等一会儿让输入法准备好
2026-01-12 07:49:18 +08:00
# 直接发送按键
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)
2026-01-12 13:48:14 +08:00
# # 兜底点击:点击搜索结果的第一项位置 (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)
# 点击符合新电途图标的小程序
logger.info("尝试查找并点击 '新电途小程序'...")
2026-01-12 13:59:06 +08:00
xdt_template = os.path.join(BASE_DIR, "Templates", "xdt.jpg")
if click_image_template(d, xdt_template):
logger.info("点击了新电途的小程序")
else:
logger.warning("未找到新电途小程序图标,尝试坐标点击第一项...")
window_size = d.window_size()
w, h = window_size[0], window_size[1]
res_x, res_y = int(w * 0.5), int(h * 0.18)
d.click(res_x, res_y)
logger.info(f"坐标点击搜索结果第一项: ({res_x}, {res_y})")
2026-01-12 13:48:14 +08:00
2026-01-12 07:49:18 +08:00
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}")