Compare commits
2 Commits
58d124919b
...
bc83fcd9d7
| Author | SHA1 | Date | |
|---|---|---|---|
| bc83fcd9d7 | |||
| f799a6df77 |
@@ -86,7 +86,7 @@ class AiUtils(object):
|
|||||||
|
|
||||||
# 模板匹配
|
# 模板匹配
|
||||||
res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
|
res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
|
||||||
threshold = 0.7
|
threshold = 0.85
|
||||||
loc = np.where(res >= threshold)
|
loc = np.where(res >= threshold)
|
||||||
# 放在 cv2.matchTemplate 之前
|
# 放在 cv2.matchTemplate 之前
|
||||||
cv2.imwrite(f'/tmp/runtime_bg_{udid}.png', image)
|
cv2.imwrite(f'/tmp/runtime_bg_{udid}.png', image)
|
||||||
@@ -1394,4 +1394,14 @@ class AiUtils(object):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _screen_info(cls, udid: str):
|
||||||
|
try:
|
||||||
|
# 避免 c.home() 可能触发的阻塞,直接取 window_size
|
||||||
|
c = wda.USBClient(udid, wdaFunctionPort)
|
||||||
|
size = c.window_size()
|
||||||
|
print(f"[Screen] 成功获取屏幕 {int(size.width)}x{int(size.height)} {udid}")
|
||||||
|
return int(size.width), int(size.height), float(c.scale)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[Screen] 获取屏幕信息异常: {e} {udid}")
|
||||||
|
return 0, 0, 0.0
|
||||||
@@ -151,16 +151,31 @@ class ControlUtils(object):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def clickLike(cls, session: Client, udid):
|
def clickLike(cls, session: Client, udid):
|
||||||
try:
|
try:
|
||||||
scale = session.scale
|
from script.ScriptManager import ScriptManager
|
||||||
x, y = AiUtils.findImageInScreen("add", udid)
|
|
||||||
print(x, y)
|
width, height, scale = ScriptManager.get_screen_info(udid)
|
||||||
if x > -1:
|
|
||||||
LogManager.method_info("点赞了", "关注打招呼", udid)
|
if scale == 3.0:
|
||||||
session.click(x // scale, y // scale + 50)
|
x, y = AiUtils.findImageInScreen("add", udid)
|
||||||
return True
|
if x > -1:
|
||||||
|
LogManager.method_info(f"点赞了,点赞的坐标是:{x // scale, y // scale + 50}", "关注打招呼", udid)
|
||||||
|
session.click(int(x // scale), int(y // scale + 50))
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
LogManager.method_info("没有找到目标", "关注打招呼", udid)
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
LogManager.method_info("没有找到目标", "关注打招呼", udid)
|
x, y = AiUtils.findImageInScreen("like1", udid)
|
||||||
return False
|
if x > -1:
|
||||||
|
LogManager.method_info(f"点赞了,点赞的坐标是:{x // scale, y // scale}", "关注打招呼", udid)
|
||||||
|
session.click(int(x // scale), int(y // scale))
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
LogManager.method_info("没有找到目标", "关注打招呼", udid)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LogManager.method_info(f"点赞出现异常,异常的原因:{e}", "关注打招呼", udid)
|
LogManager.method_info(f"点赞出现异常,异常的原因:{e}", "关注打招呼", udid)
|
||||||
raise False
|
raise False
|
||||||
@@ -191,8 +206,6 @@ class ControlUtils(object):
|
|||||||
# 获取主播详情页的第一个视频
|
# 获取主播详情页的第一个视频
|
||||||
@classmethod
|
@classmethod
|
||||||
def clickFirstVideoFromDetailPage(cls, session: Client):
|
def clickFirstVideoFromDetailPage(cls, session: Client):
|
||||||
# videoCell = session.xpath(
|
|
||||||
# '//Window/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[2]/Other[1]/ScrollView[1]/Other[1]/CollectionView[1]/Cell[2]')
|
|
||||||
|
|
||||||
videoCell = session.xpath(
|
videoCell = session.xpath(
|
||||||
'(//XCUIElementTypeCollectionView//XCUIElementTypeCell[.//XCUIElementTypeImage[@name="profile_video"]])[1]')
|
'(//XCUIElementTypeCollectionView//XCUIElementTypeCell[.//XCUIElementTypeImage[@name="profile_video"]])[1]')
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from typing import List, Tuple, Union, Optional
|
from typing import List, Tuple, Union, Optional
|
||||||
@@ -162,6 +164,15 @@ class OCRUtils:
|
|||||||
centers: [(cx, cy), ...]
|
centers: [(cx, cy), ...]
|
||||||
boxes: [[x1,y1,x2,y2], ...] (np.ndarray, int)
|
boxes: [[x1,y1,x2,y2], ...] (np.ndarray, int)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if not os.path.isfile(template_path):
|
||||||
|
print(f"模板文件不存在 → {template_path}")
|
||||||
|
raise FileNotFoundError(f"模板文件不存在 → {template_path}")
|
||||||
|
|
||||||
|
size = os.path.getsize(template_path)
|
||||||
|
if size == 0:
|
||||||
|
print(f"模板文件大小为 0 → {template_path} ")
|
||||||
|
raise ValueError(f"模板文件大小为 0 → {template_path}")
|
||||||
# 模板(灰度)
|
# 模板(灰度)
|
||||||
template = cv2.imread(template_path, cv2.IMREAD_GRAYSCALE)
|
template = cv2.imread(template_path, cv2.IMREAD_GRAYSCALE)
|
||||||
if template is None:
|
if template is None:
|
||||||
@@ -230,4 +241,4 @@ class OCRUtils:
|
|||||||
# 全部尝试失败
|
# 全部尝试失败
|
||||||
if return_boxes:
|
if return_boxes:
|
||||||
return last_centers, last_boxes
|
return last_centers, last_boxes
|
||||||
return last_centers
|
return last_centers
|
||||||
BIN
resources/comment2.png
Normal file
BIN
resources/comment2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
BIN
resources/insert_comment2x.png
Normal file
BIN
resources/insert_comment2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
BIN
resources/like1.png
Normal file
BIN
resources/like1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@@ -32,6 +32,17 @@ class ScriptManager():
|
|||||||
# cls._instance = super(ScriptManager, cls).__new__(cls)
|
# cls._instance = super(ScriptManager, cls).__new__(cls)
|
||||||
# # 返回已存在的实例
|
# # 返回已存在的实例
|
||||||
# return cls._instance
|
# return cls._instance
|
||||||
|
_device_cache = {}
|
||||||
|
_cache_lock = threading.Lock() # 线程安全锁(可选,如果你有多线程)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_screen_info(cls, udid: str):
|
||||||
|
# 如果缓存中没有该设备的信息,则获取并缓存
|
||||||
|
if udid not in cls._device_cache:
|
||||||
|
with cls._cache_lock: # 防止并发写入
|
||||||
|
if udid not in cls._device_cache: # 双重检查
|
||||||
|
cls._device_cache[udid] = AiUtils._screen_info(udid)
|
||||||
|
return cls._device_cache[udid]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -40,87 +51,100 @@ class ScriptManager():
|
|||||||
current_dir = Path(__file__).resolve().parent
|
current_dir = Path(__file__).resolve().parent
|
||||||
# 项目根目录(假设你的类文件在项目的子目录里,比如 Module/OCR/OCRUtils.py)
|
# 项目根目录(假设你的类文件在项目的子目录里,比如 Module/OCR/OCRUtils.py)
|
||||||
self.lock = threading.Lock()
|
self.lock = threading.Lock()
|
||||||
|
|
||||||
project_root = current_dir.parent # 如果你确定这个文件就在项目根目录下,可省略这行
|
project_root = current_dir.parent # 如果你确定这个文件就在项目根目录下,可省略这行
|
||||||
# resources 文件夹路径
|
# resources 文件夹路径
|
||||||
|
|
||||||
# 获取相应的模板的地址
|
# 获取相应的模板的地址
|
||||||
self.resources_dir = project_root / "resources"
|
self.resources_dir = project_root / "resources"
|
||||||
|
|
||||||
|
# === 2. @2x 素材 (scale=2) ===
|
||||||
|
self.comment_dir2 = self.resources_dir / "comment2.png"
|
||||||
|
self.comment_add_dir_2x = self.resources_dir / "insert_comment2x.png"
|
||||||
|
self.comment_add_dir2_2x = self.resources_dir / "insert_comment2x.png"
|
||||||
|
|
||||||
|
# === 3. @3x 素材 (scale=3) ===
|
||||||
self.comment_dir = self.resources_dir / "comment.png"
|
self.comment_dir = self.resources_dir / "comment.png"
|
||||||
self.comment_add_dir = self.resources_dir / "insert_comment.png"
|
self.comment_add_dir = self.resources_dir / "insert_comment.png"
|
||||||
self.comment_add_dir2 = self.resources_dir / "insert_comment2.png"
|
self.comment_add_dir2 = self.resources_dir / "insert_comment2.png"
|
||||||
|
|
||||||
self.initialized = True # 标记已初始化
|
self.initialized = True # 标记已初始化
|
||||||
|
|
||||||
|
def _pick_template(self, scale: float):
|
||||||
|
"""
|
||||||
|
scale≈3 -> 返回 @3x 素材,除 3
|
||||||
|
其余默认 @2x 素材,除 2
|
||||||
|
"""
|
||||||
|
if abs(scale - 3.0) < 0.3: # 3x
|
||||||
|
return (self.comment_dir,
|
||||||
|
self.comment_add_dir,
|
||||||
|
self.comment_add_dir2, # 3x 兜底
|
||||||
|
3) # 除数
|
||||||
|
else: # 2x(默认)
|
||||||
|
return (self.comment_dir2,
|
||||||
|
self.comment_add_dir_2x,
|
||||||
|
self.comment_add_dir2_2x, # 2x 兜底
|
||||||
|
2) # 除数
|
||||||
|
|
||||||
def comment_flow(self, filePath, session, udid, recomend_cx, recomend_cy):
|
def comment_flow(self, filePath, session, udid, recomend_cx, recomend_cy):
|
||||||
"""评论一条龙:点评论框->输入->发送->返回"""
|
width, height, scale = self.get_screen_info(udid)
|
||||||
|
|
||||||
|
# 取当前分辨率的三张图 + 除数
|
||||||
|
comment_tpl, add_tpl, add_fb_tpl, div = self._pick_template(scale)
|
||||||
|
|
||||||
|
# ① 点评论按钮
|
||||||
|
coord = OCRUtils.find_template(str(comment_tpl), filePath)
|
||||||
|
|
||||||
|
LogManager.method_info(f"使用的模板路径是:{str(comment_tpl)}", "养号", udid)
|
||||||
|
|
||||||
coord = OCRUtils.find_template(str(self.comment_dir), filePath)
|
|
||||||
if not coord:
|
if not coord:
|
||||||
return # 没检测到评论按钮就拉倒
|
print("无法检测到评论按钮")
|
||||||
|
LogManager.method_info("无法检测到评论按钮", "养号", udid)
|
||||||
|
return
|
||||||
|
|
||||||
cx, cy = coord[0] # ✅ 注意这里取第一个点
|
cx, cy = coord[0]
|
||||||
session.click(int(cx / 3), int(cy / 3))
|
session.click(int(cx / div), int(cy / div))
|
||||||
|
LogManager.method_info(f"点击评论坐标:{int(cx / div)}, {int(cy / div)}", "养号", udid)
|
||||||
LogManager.method_info(f"点击评论的坐标:{int(cx / 3)}, {int(cy / 3)}", "养号", udid)
|
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
# 截图二判(防止键盘弹出后坐标变化)
|
# ② 重新截图(防止键盘弹起)
|
||||||
img = session.screenshot()
|
img = session.screenshot()
|
||||||
time.sleep(2)
|
|
||||||
filePath = os.path.join(os.path.dirname(filePath), "bgv_comment.png")
|
filePath = os.path.join(os.path.dirname(filePath), "bgv_comment.png")
|
||||||
img.save(filePath)
|
img.save(filePath)
|
||||||
|
|
||||||
# 从评论列表中随机取出一条数据,进行评论
|
# ③ 随机评论语
|
||||||
if Variables.commentList:
|
single_comment = random.choice(Variables.commentList) if Variables.commentList else "评论没有导入数据"
|
||||||
single_comment = random.choice(Variables.commentList)
|
|
||||||
else:
|
|
||||||
single_comment = "评论没有导入数据"
|
|
||||||
|
|
||||||
coord2 = OCRUtils.find_template(str(self.comment_add_dir), filePath)
|
|
||||||
|
|
||||||
|
# ④ 找「添加评论」按钮
|
||||||
|
coord2 = OCRUtils.find_template(str(add_tpl), filePath)
|
||||||
click_count = False
|
click_count = False
|
||||||
|
if coord2: # 方案 1 命中
|
||||||
if coord2: # 二判命中
|
|
||||||
LogManager.method_info(f"方案1", "养号", udid)
|
|
||||||
cx2, cy2 = coord2[0]
|
cx2, cy2 = coord2[0]
|
||||||
session.tap(int(cx2 / 3), int(cy2 / 3))
|
session.tap(int(cx2 / div), int(cy2 / div))
|
||||||
LogManager.method_info(f"点击添加评论的坐标:{int(cx2 / 3)}, {int(cy2 / 3)}", "养号", udid)
|
|
||||||
session.send_keys(f"{single_comment}\n")
|
session.send_keys(f"{single_comment}\n")
|
||||||
time.sleep(2)
|
|
||||||
LogManager.method_info("评论成功", "养号", udid)
|
|
||||||
click_count = True
|
click_count = True
|
||||||
|
LogManager.method_info("评论成功(方案1)", "养号", udid)
|
||||||
else:
|
else: # 方案 2 兜底
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
LogManager.method_info(f"方案2", "养号", udid)
|
|
||||||
img = session.screenshot()
|
img = session.screenshot()
|
||||||
filePath = os.path.join(os.path.dirname(filePath), "bgv_comment.png")
|
|
||||||
img.save(filePath)
|
img.save(filePath)
|
||||||
coord3 = OCRUtils.find_template(str(self.comment_add_dir2), filePath)
|
coord3 = OCRUtils.find_template(str(add_fb_tpl), filePath)
|
||||||
if coord3: # 二判命中
|
if coord3:
|
||||||
cx3, cy3 = coord3[0]
|
cx3, cy3 = coord3[0]
|
||||||
session.tap(int(cx3 / 3), int(cy3 / 3))
|
session.tap(int(cx3 / div), int(cy3 / div))
|
||||||
session.send_keys(f"{single_comment}\n")
|
session.send_keys(f"{single_comment}\n")
|
||||||
time.sleep(2)
|
|
||||||
LogManager.method_info("评论成功", "养号", udid)
|
|
||||||
click_count = True
|
click_count = True
|
||||||
|
LogManager.method_info("评论成功(方案2)", "养号", udid)
|
||||||
|
|
||||||
# 点返回/取消按钮:优先用推荐按钮坐标,没有就兜底 100,100
|
# ⑤ 返回 / 取消
|
||||||
tap_x = int(recomend_cx) if recomend_cx else 100
|
# tap_x = int(recomend_cx) if recomend_cx else 100
|
||||||
tap_y = int(recomend_cy) if recomend_cy else 100
|
# tap_y = int(recomend_cy) if recomend_cy else 100
|
||||||
if click_count:
|
|
||||||
|
|
||||||
print("点击一次")
|
time.sleep(1)
|
||||||
LogManager.method_info("点击一次", "养号", udid)
|
|
||||||
|
|
||||||
session.tap(tap_x, tap_y)
|
session.tap(100, 100)
|
||||||
else:
|
|
||||||
print("点击两次")
|
|
||||||
LogManager.method_info("点击两次", "养号", udid)
|
|
||||||
|
|
||||||
session.tap(tap_x, tap_y)
|
if not click_count: # 兜底多点一次
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
session.tap(tap_x, tap_y)
|
session.tap(100, 100)
|
||||||
|
|
||||||
# 养号
|
# 养号
|
||||||
def growAccount(self, udid, isComment, event, is_monitoring=False):
|
def growAccount(self, udid, isComment, event, is_monitoring=False):
|
||||||
@@ -186,10 +210,19 @@ class ScriptManager():
|
|||||||
|
|
||||||
# ---- 视频逻辑 ----
|
# ---- 视频逻辑 ----
|
||||||
try:
|
try:
|
||||||
addX, addY = AiUtils.findImageInScreen("add", udid)
|
width, height, scale = self.get_screen_info(udid)
|
||||||
|
if scale == 3.0:
|
||||||
|
addX, addY = AiUtils.findImageInScreen("add", udid)
|
||||||
|
else:
|
||||||
|
addX, addY = AiUtils.findImageInScreen("like1", udid)
|
||||||
|
|
||||||
isSame = False
|
isSame = False
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
tx, ty = AiUtils.findImageInScreen("add", udid)
|
if scale == 3.0:
|
||||||
|
tx, ty = AiUtils.findImageInScreen("add", udid)
|
||||||
|
else:
|
||||||
|
tx, ty = AiUtils.findImageInScreen("like1", udid)
|
||||||
|
|
||||||
if addX == tx and addY == ty:
|
if addX == tx and addY == ty:
|
||||||
isSame = True
|
isSame = True
|
||||||
event.wait(timeout=1)
|
event.wait(timeout=1)
|
||||||
@@ -214,7 +247,7 @@ class ScriptManager():
|
|||||||
# 重置 session
|
# 重置 session
|
||||||
session.appium_settings({"snapshotMaxDepth": 0})
|
session.appium_settings({"snapshotMaxDepth": 0})
|
||||||
|
|
||||||
if needLike < 23:
|
if needLike < 25:
|
||||||
LogManager.method_info("进行点赞", "养号", udid)
|
LogManager.method_info("进行点赞", "养号", udid)
|
||||||
ControlUtils.clickLike(session, udid)
|
ControlUtils.clickLike(session, udid)
|
||||||
LogManager.method_info("继续观看视频", "养号", udid)
|
LogManager.method_info("继续观看视频", "养号", udid)
|
||||||
@@ -631,6 +664,7 @@ class ScriptManager():
|
|||||||
LogManager.method_info(f"是否进行评论:{isComment}", "关注打招呼", udid)
|
LogManager.method_info(f"是否进行评论:{isComment}", "关注打招呼", udid)
|
||||||
# 使用OCR进行评论
|
# 使用OCR进行评论
|
||||||
if isComment:
|
if isComment:
|
||||||
|
LogManager.method_info("调用方法进行评论", "关注打招呼", udid)
|
||||||
self.comment_flow(filePath, session, udid, 100, 100)
|
self.comment_flow(filePath, session, udid, 100, 100)
|
||||||
event.wait(timeout=2)
|
event.wait(timeout=2)
|
||||||
|
|
||||||
@@ -1590,9 +1624,16 @@ class ScriptManager():
|
|||||||
)
|
)
|
||||||
|
|
||||||
# 活动
|
# 活动
|
||||||
|
# xp_activity_badge = (
|
||||||
|
# "//XCUIElementTypeCell[.//XCUIElementTypeLink[@name='活动']]"
|
||||||
|
# "//XCUIElementTypeStaticText[string-length(@value)>0 and translate(@value,'0123456789','')='']"
|
||||||
|
# )
|
||||||
xp_activity_badge = (
|
xp_activity_badge = (
|
||||||
"//XCUIElementTypeCell[.//XCUIElementTypeLink[@name='活动']]"
|
"//XCUIElementTypeLink[@name='活动']/ancestor::XCUIElementTypeCell[1]"
|
||||||
"//XCUIElementTypeStaticText[string-length(@value)>0 and translate(@value,'0123456789','')='']"
|
"//XCUIElementTypeStaticText["
|
||||||
|
" @value and "
|
||||||
|
" (translate(@value,'0123456789','')='' or @value='99+')"
|
||||||
|
"]"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 系统通知
|
# 系统通知
|
||||||
@@ -1931,11 +1972,12 @@ class ScriptManager():
|
|||||||
|
|
||||||
client = wda.USBClient(udid, ev.wdaFunctionPort)
|
client = wda.USBClient(udid, ev.wdaFunctionPort)
|
||||||
session = client.session()
|
session = client.session()
|
||||||
|
width, height, scale = self.get_screen_info(udid)
|
||||||
count = 0
|
count = 0
|
||||||
while count <= 5:
|
while count <= 5:
|
||||||
try:
|
try:
|
||||||
|
|
||||||
|
|
||||||
# 重启打开
|
# 重启打开
|
||||||
ControlUtils.closeTikTok(session, udid)
|
ControlUtils.closeTikTok(session, udid)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
@@ -2079,8 +2121,12 @@ class ScriptManager():
|
|||||||
# 随机偏移(增强拟人)
|
# 随机偏移(增强拟人)
|
||||||
num = random.randint(-10, 10)
|
num = random.randint(-10, 10)
|
||||||
# 分辨率/坐标映射(按你设备比例;你原来是 /3)
|
# 分辨率/坐标映射(按你设备比例;你原来是 /3)
|
||||||
tap_x = int((center_x + num) / 3)
|
if scale == 3.0:
|
||||||
tap_y = int((center_y + num) / 3)
|
tap_x = int((center_x + num) / 3)
|
||||||
|
tap_y = int((center_y + num) / 3)
|
||||||
|
else:
|
||||||
|
tap_x = int((center_x + num) / 2)
|
||||||
|
tap_y = int((center_y + num) / 2)
|
||||||
|
|
||||||
LogManager.method_info(f"点击坐标: ({tap_x}, {tap_y}),账号: {target_account}", "切换账号", udid)
|
LogManager.method_info(f"点击坐标: ({tap_x}, {tap_y}),账号: {target_account}", "切换账号", udid)
|
||||||
session.tap(tap_x, tap_y)
|
session.tap(tap_x, tap_y)
|
||||||
|
|||||||
Reference in New Issue
Block a user