This commit is contained in:
HuangHai
2026-01-12 22:59:53 +08:00
parent fa1beb3b3f
commit b07dca28b5
9 changed files with 226 additions and 12 deletions

View File

@@ -73,9 +73,25 @@ async def get_station_list(d, service, max_scrolls=MAX_SCROLLS):
logger.info("检查是否存在广告弹窗...")
ad_res = await ReadImageKit.detect_ad_popup(screenshot_path, device_info=device_info)
if ad_res:
logger.info(f"检测到广告弹窗,准备关闭: {ad_res}")
d.click(ad_res['x'], ad_res['y'])
await asyncio.sleep(1.5)
ad_type = ad_res.get("ad_type")
logger.info(f"检测到广告弹窗,准备关闭: {ad_res} [Type: {ad_type}]")
x, y = ad_res['x'], ad_res['y']
if ad_type == "rabbit":
logger.info(">>> 检测到顽固的兔子广告,执行强力双击策略...")
# 使用 u2 的 double_click,它比两次 click + sleep 更快
d.double_click(x, y, duration=0.05)
# 处理“误触背景卡片”的逻辑
logger.info(">>> 考虑到可能误触背景卡片,等待 2s 后执行返回操作...")
await asyncio.sleep(2.0)
d.press("back")
await asyncio.sleep(1.0)
logger.info(">>> 已尝试从误触页面返回。")
else:
d.click(x, y)
await asyncio.sleep(1.5)
# 重新截图
if os.path.exists(screenshot_path): os.remove(screenshot_path)
screenshot_path = take_screenshot(d, image_uuid, save_dir=TEMP_IMAGE_DIR)

View File

@@ -133,9 +133,14 @@ class ReadImageKit:
if result.get("has_ad") and result.get("close_point"):
if result.get("ad_type") == "rabbit":
logger.info("大模型找到了兔子广告!")
# 强制使用固定坐标 (94, 830) - 归一化 0-1000
# 根据多次 CV 测试结果 (94, 828) 和 (93, 830) 的综合评估
logger.info(">>> 采用强制固定坐标策略 (94, 830) 以确保点击准确性")
norm_point = [94, 830]
else:
norm_point = result["close_point"]
# 转换坐标
norm_point = result["close_point"]
if len(norm_point) == 2:
if not device_info:
device_info = cls._FALLBACK_DEVICE_INFO
@@ -147,7 +152,11 @@ class ReadImageKit:
x = int(norm_point[0] / 1000 * w)
y = int(norm_point[1] / 1000 * h)
return {"x": x, "y": y}
return {
"x": x,
"y": y,
"ad_type": result.get("ad_type", "unknown")
}
return None

View File

@@ -168,9 +168,25 @@ async def get_station_list(d, service, uploader, max_scrolls=MAX_SCROLLS):
logger.info("检查是否存在广告弹窗...")
ad_res = await ReadImageKit.detect_ad_popup(screenshot_path, device_info=device_info)
if ad_res:
logger.info(f"检测到广告弹窗,准备关闭: {ad_res}")
d.click(ad_res['x'], ad_res['y'])
await asyncio.sleep(1.5)
ad_type = ad_res.get("ad_type")
logger.info(f"检测到广告弹窗,准备关闭: {ad_res} [Type: {ad_type}]")
x, y = ad_res['x'], ad_res['y']
if ad_type == "rabbit":
logger.info(">>> 检测到顽固的兔子广告,执行强力双击策略...")
# 使用 u2 的 double_click,它比两次 click + sleep 更快
d.double_click(x, y, duration=0.05)
# 处理“误触背景卡片”的逻辑:既然可能进去了,就等一会儿再退出来
logger.info(">>> 考虑到可能误触背景卡片,等待 2s 后执行返回操作...")
await asyncio.sleep(2.0)
d.press("back")
await asyncio.sleep(1.0)
logger.info(">>> 已尝试从误触页面返回。")
else:
d.click(x, y)
await asyncio.sleep(1.5)
# 重新截图
if os.path.exists(screenshot_path): os.remove(screenshot_path)
screenshot_path = take_screenshot(d, image_uuid, save_dir=TEMP_IMAGE_DIR)

View File

@@ -61,8 +61,25 @@ async def check_and_close_ad(d):
ad_result = await ReadImageKit.detect_ad_popup(screenshot_path, device_info=device_info)
if ad_result:
x, y = ad_result["x"], ad_result["y"]
logger.info(f"通过视觉大模型检测到广告关闭按钮: ({x}, {y})")
d.click(x, y)
ad_type = ad_result.get("ad_type")
logger.info(f"通过视觉大模型检测到广告关闭按钮: ({x}, {y}) [Type: {ad_type}]")
# 针对“新电兔AI”兔子广告的强力关闭策略
if ad_type == "rabbit":
logger.info(">>> 检测到顽固的兔子广告,执行强力双击策略...")
# 使用 u2 的 double_click,它比两次 click + sleep 更快
d.double_click(x, y, duration=0.05)
# 处理“误触背景卡片”的逻辑
logger.info(">>> 考虑到可能误触背景卡片,等待 2s 后执行返回操作...")
await asyncio.sleep(2.0)
d.press("back")
await asyncio.sleep(1.0)
logger.info(">>> 已尝试从误触页面返回。")
else:
d.click(x, y)
if os.path.exists(screenshot_path):
os.remove(screenshot_path)
return True

View File

@@ -129,9 +129,14 @@ class ReadImageKit:
if result.get("has_ad") and result.get("close_point"):
if result.get("ad_type") == "rabbit":
logger.info("大模型找到了兔子广告!")
# 强制使用固定坐标 (94, 830) - 归一化 0-1000
# 根据多次 CV 测试结果 (94, 828) 和 (93, 830) 的综合评估
logger.info(">>> 采用强制固定坐标策略 (94, 830) 以确保点击准确性")
norm_point = [94, 830]
else:
norm_point = result["close_point"]
# 转换坐标
norm_point = result["close_point"]
if len(norm_point) == 2:
if not device_info:
device_info = cls._FALLBACK_DEVICE_INFO
@@ -143,7 +148,11 @@ class ReadImageKit:
x = int(norm_point[0] / 1000 * w)
y = int(norm_point[1] / 1000 * h)
return {"x": x, "y": y}
return {
"x": x,
"y": y,
"ad_type": result.get("ad_type", "unknown")
}
return None

147
Tools/TestAdClick.py Normal file
View File

@@ -0,0 +1,147 @@
import uiautomator2 as u2
import time
import os
import cv2
import numpy as np
def test_click():
d = u2.connect()
w, h = d.window_size()
print(f"Device size: {w}x{h}")
# 1. Take screenshot
print("Taking screenshot...")
screenshot_path = r"d:\dsWork\aiData\Output\debug_ad_before.jpg"
d.screenshot(screenshot_path)
# 2. Check hierarchy
print("Dumping hierarchy...")
try:
xml = d.dump_hierarchy()
with open(r"d:\dsWork\aiData\Output\hierarchy.xml", "w", encoding="utf-8") as f:
f.write(xml)
print(r"Hierarchy saved to d:\dsWork\aiData\Output\hierarchy.xml")
except Exception as e:
print(f"Failed to dump hierarchy: {e}")
# 3. Visualize the current fixed point (80/1000, 835/1000)
# Norm: 0.08, 0.835
# Previous attempt was: (86, 2004) for 1080x2400
# Let's try to detect the black circle using HSV or thresholding
img = cv2.imread(screenshot_path)
if img is None:
print("Failed to load screenshot")
return
# ROI: Left side, bottom half
# x: 0 - 200, y: 1500 - 2200 (approx for 1080x2400)
roi_x1, roi_x2 = 0, int(w * 0.25)
roi_y1, roi_y2 = int(h * 0.6), int(h * 0.9)
roi = img[roi_y1:roi_y2, roi_x1:roi_x2]
# Convert to grayscale
gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
# The close button is a black circle. So it should be very dark.
# Try multiple thresholds to find the best candidates
candidates = []
for threshold_val in [50, 70, 90, 110]:
_, thresh = cv2.threshold(gray, threshold_val, 255, cv2.THRESH_BINARY_INV)
# Find contours
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
area = cv2.contourArea(cnt)
# Approximate circle check
perimeter = cv2.arcLength(cnt, True)
if perimeter == 0: continue
circularity = 4 * np.pi * (area / (perimeter * perimeter))
# Filter by size and circularity
# Size: it's a small button
if 50 < area < 6000 and circularity > 0.35:
M = cv2.moments(cnt)
if M["m00"] != 0:
cX = int(M["m10"] / M["m00"]) + roi_x1
cY = int(M["m01"] / M["m00"]) + roi_y1
# Calculate normalized coordinates
norm_x = int(cX / w * 1000)
norm_y = int(cY / h * 1000)
# Avoid duplicates
if not any(abs(cX - c[0]) < 10 and abs(cY - c[1]) < 10 for c in candidates):
candidates.append((cX, cY, area, norm_x, norm_y))
# Save thresh image for debugging
_, debug_thresh = cv2.threshold(gray, 70, 255, cv2.THRESH_BINARY_INV)
cv2.imwrite(r"d:\dsWork\aiData\Output\debug_thresh.jpg", debug_thresh)
# Sort by proximity to expected location (Left side, Middle-Bottom)
# Expected norm: (93, 830)
def score_candidate(c):
# Weighting: X proximity is important, Y proximity is also important, Area around 400-500 is ideal
dist_x = abs(c[3] - 93)
dist_y = abs(c[4] - 830)
area_diff = abs(c[2] - 450) / 450.0
return dist_x * 2 + dist_y + area_diff * 100
candidates.sort(key=score_candidate)
target_x, target_y = int(w * 0.08), int(h * 0.835) # Default fallback
norm_target_x, norm_target_y = 80, 835
if candidates:
print(f"Found {len(candidates)} candidate close buttons via CV:")
for i, (cx, cy, area, nx, ny) in enumerate(candidates):
score = score_candidate((cx, cy, area, nx, ny))
print(f" Candidate {i}: ({cx}, {cy}), Area: {area}, Norm: ({nx}, {ny}), Score: {score:.2f}")
# Pick the best one
best_c = candidates[0]
target_x, target_y = best_c[0], best_c[1]
norm_target_x, norm_target_y = best_c[3], best_c[4]
print(f"Selected CV target: ({target_x}, {target_y}), Norm: ({norm_target_x}, {norm_target_y})")
else:
print("No CV candidates found. Using fixed coordinates.")
# Visualize
cv2.circle(img, (target_x, target_y), 20, (0, 255, 0), -1)
cv2.putText(img, f"Click ({target_x}, {target_y})", (target_x + 30, target_y), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
# Also draw the fixed point for comparison (Blue)
fixed_x = int(w * 0.08)
fixed_y = int(h * 0.835)
cv2.circle(img, (fixed_x, fixed_y), 15, (255, 0, 0), -1)
debug_path = r"d:\dsWork\aiData\Output\debug_ad_point.jpg"
cv2.imwrite(debug_path, img)
print(f"Debug image saved to {debug_path}")
# 4. Perform click
print(f"Clicking ({target_x}, {target_y}) using double_click...")
# Use d.double_click for a faster interaction
d.double_click(target_x, target_y, duration=0.05)
# NEW: Handle potential background click as per user's latest request
print("Waiting 2s for potential background card to load...")
time.sleep(2)
print("Pressing BACK to return to main page...")
d.press("back")
time.sleep(2)
# 5. Take after screenshot
after_path = r"d:\dsWork\aiData\Output\debug_ad_after.jpg"
d.screenshot(after_path)
print(f"After screenshot saved to {after_path}")
# Check if successful (compare image hash or just file size, though file size varies with compression)
# Simple check: If the black button is gone, the area should be brighter?
# Or just let user check.
if __name__ == "__main__":
test_click()