修复脚本。拔出设备,重新插回脚本可以继续执行。
This commit is contained in:
@@ -2,6 +2,7 @@ import random
|
||||
import re
|
||||
import threading
|
||||
import time
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
import wda
|
||||
@@ -34,6 +35,7 @@ class ScriptManager():
|
||||
# return cls._instance
|
||||
_device_cache = {}
|
||||
_cache_lock = threading.Lock() # 线程安全锁(可选,如果你有多线程)
|
||||
_udid_locks = defaultdict(threading.Lock)
|
||||
|
||||
@classmethod
|
||||
def get_screen_info(cls, udid: str):
|
||||
@@ -146,301 +148,359 @@ class ScriptManager():
|
||||
time.sleep(1)
|
||||
session.tap(100, 100)
|
||||
|
||||
# 养号
|
||||
# 养号
|
||||
def growAccount(self, udid, isComment, event, is_monitoring=False):
|
||||
LogManager.method_info(f"调用刷视频", "养号", udid)
|
||||
LogManager.method_info("调用刷视频", "养号", udid)
|
||||
|
||||
# ========= 初始化 =========
|
||||
client = wda.USBClient(udid, ev.wdaFunctionPort)
|
||||
session = client.session()
|
||||
AiUtils.makeUdidDir(udid)
|
||||
# ✅ 同一台设备同一时间只允许一个脚本跑,防止WDA并发爆炸(call depth exceed)
|
||||
lock = self._udid_locks[udid]
|
||||
if not lock.acquire(blocking=False):
|
||||
LogManager.method_error("同一UDID已有任务在运行,拒绝重复启动", "养号", udid=udid)
|
||||
return
|
||||
|
||||
while not event.is_set():
|
||||
try:
|
||||
if not is_monitoring:
|
||||
LogManager.method_info(f"开始养号,重启tiktok", "养号", udid)
|
||||
try:
|
||||
AiUtils.makeUdidDir(udid)
|
||||
|
||||
# 外层重启循环:任何异常都会回到这里重新建 session
|
||||
while not event.is_set():
|
||||
client = None
|
||||
session = None
|
||||
try:
|
||||
# ========= 初始化(每次重启都重新创建)=========
|
||||
client = wda.USBClient(udid, ev.wdaFunctionPort)
|
||||
session = client.session()
|
||||
|
||||
if not is_monitoring:
|
||||
LogManager.method_info("开始养号,重启tiktok", "养号", udid)
|
||||
ControlUtils.closeTikTok(session, udid)
|
||||
event.wait(timeout=1)
|
||||
ControlUtils.openTikTok(session, udid)
|
||||
event.wait(timeout=3)
|
||||
|
||||
recomend_cx = 0
|
||||
recomend_cy = 0
|
||||
|
||||
# ========= 主循环 =========
|
||||
while not event.is_set():
|
||||
# 设置手机的节点深度为15,判断该页面是否正确
|
||||
session.appium_settings({"snapshotMaxDepth": 15})
|
||||
|
||||
el = session.xpath(
|
||||
'//XCUIElementTypeButton[@name="top_tabs_recomend" or @name="推荐" or @label="推荐"]'
|
||||
)
|
||||
|
||||
# 获取推荐按钮所在的坐标
|
||||
if el.exists:
|
||||
bounds = el.bounds # [x, y, width, height]
|
||||
recomend_cx = bounds[0] + bounds[2] // 2
|
||||
recomend_cy = bounds[1] + bounds[3] // 2
|
||||
|
||||
if not el.exists:
|
||||
LogManager.method_error("找不到推荐按钮,养号出现问题,重启养号功能", "养号", udid=udid)
|
||||
raise Exception("找不到推荐按钮,养号出现问题,重启养号功能")
|
||||
|
||||
if el.value != "1":
|
||||
LogManager.method_error("当前页面不是推荐页面,养号出现问题,重启养号功能", "养号", udid=udid)
|
||||
raise Exception("当前页面不是推荐页面,养号出现问题,重启养号功能")
|
||||
|
||||
LogManager.method_info("当前页面是推荐页面,开始养号", "养号", udid=udid)
|
||||
|
||||
# 重新设置节点深度,防止手机卡顿
|
||||
session.appium_settings({"snapshotMaxDepth": 0})
|
||||
|
||||
# ---- 截图保存 ----
|
||||
try:
|
||||
img = client.screenshot()
|
||||
base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
resource_dir = os.path.join(base_dir, "resources", udid)
|
||||
os.makedirs(resource_dir, exist_ok=True)
|
||||
filePath = os.path.join(resource_dir, "bgv.png")
|
||||
img.save(filePath)
|
||||
LogManager.method_info(f"保存屏幕图像成功 -> {filePath}", "养号", udid)
|
||||
event.wait(timeout=1)
|
||||
except Exception as e:
|
||||
LogManager.method_info(f"截图或保存失败,失败原因:{e}", "养号", udid)
|
||||
raise Exception("截图或保存失败,重启养号功能")
|
||||
|
||||
# ---- 视频逻辑 ----
|
||||
try:
|
||||
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
|
||||
for _ in range(2):
|
||||
if scale == 3.0:
|
||||
tx, ty = AiUtils.findImageInScreen("add", udid)
|
||||
else:
|
||||
tx, ty = AiUtils.findImageInScreen("like1", udid)
|
||||
|
||||
if addX == tx and addY == ty:
|
||||
isSame = True
|
||||
event.wait(timeout=1)
|
||||
else:
|
||||
isSame = False
|
||||
|
||||
if addX > 0 and isSame:
|
||||
needLike = random.randint(0, 100)
|
||||
homeButton = AiUtils.findHomeButton(session)
|
||||
if homeButton:
|
||||
LogManager.method_info("有首页按钮,查看视频", "养号", udid)
|
||||
videoTime = random.randint(5, 15)
|
||||
|
||||
LogManager.method_info("准备停止脚本", method="task")
|
||||
for _ in range(videoTime):
|
||||
if event.is_set():
|
||||
LogManager.method_info("停止脚本中", method="task")
|
||||
break
|
||||
event.wait(timeout=1)
|
||||
LogManager.method_info("停止脚本成功", method="task")
|
||||
|
||||
session.appium_settings({"snapshotMaxDepth": 0})
|
||||
|
||||
if needLike < 25:
|
||||
LogManager.method_info("进行点赞", "养号", udid)
|
||||
ControlUtils.clickLike(session, udid)
|
||||
|
||||
LogManager.method_info("继续观看视频", "养号", udid)
|
||||
LogManager.method_info("准备划到下一个视频", "养号", udid)
|
||||
else:
|
||||
LogManager.method_error("找不到首页按钮。出错了", "养号", udid)
|
||||
|
||||
if isComment and random.random() > 0.70:
|
||||
self.comment_flow(filePath, session, udid, recomend_cx, recomend_cy)
|
||||
event.wait(timeout=2)
|
||||
|
||||
home = AiUtils.findHomeButton(session)
|
||||
if not home:
|
||||
raise Exception("没有找到首页按钮,重置")
|
||||
|
||||
videoTime = random.randint(15, 30)
|
||||
for _ in range(videoTime):
|
||||
if event.is_set():
|
||||
break
|
||||
event.wait(timeout=1)
|
||||
|
||||
ControlUtils.swipe_up(client)
|
||||
|
||||
# 如果is_monitoring 为True:检测收件箱消息
|
||||
if is_monitoring:
|
||||
el = session.xpath(
|
||||
'//XCUIElementTypeButton[@name="a11y_vo_inbox"]'
|
||||
' | '
|
||||
'//XCUIElementTypeButton[contains(@name,"收件箱")]'
|
||||
' | '
|
||||
'//XCUIElementTypeButton[.//XCUIElementTypeStaticText[@value="收件箱"]]'
|
||||
)
|
||||
|
||||
if el.exists:
|
||||
m = None
|
||||
try:
|
||||
m = re.search(r"(\d+)", (el.label or ""))
|
||||
except Exception as e:
|
||||
LogManager.method_error(f"解析收件箱数量异常: {e}", "检测消息", udid)
|
||||
|
||||
count = int(m.group(1)) if m else 0
|
||||
if count:
|
||||
break
|
||||
else:
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
print("刷视频脚本有错误:错误内容:", e)
|
||||
LogManager.method_error("刷视频过程出现错误,重试", "养号", udid)
|
||||
# 抛出给上层,触发重启(外层 while 会重建 session)
|
||||
raise
|
||||
|
||||
except Exception as e:
|
||||
print("刷视频遇到错误了。错误内容:", e)
|
||||
LogManager.method_error(f"[{udid}] 养号出现异常,将重启流程: {e}", "养号", udid)
|
||||
|
||||
# ✅ 关键:这里等待后回到外层 while,重新创建client/session
|
||||
event.wait(timeout=3)
|
||||
|
||||
finally:
|
||||
# 尽量释放 session(不同wda版本不一定有close)
|
||||
try:
|
||||
if session is not None:
|
||||
session.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
# 观看直播
|
||||
def watchLiveForGrowth(self, udid, event, max_retries=None):
|
||||
import random, wda
|
||||
|
||||
retry_count = 0
|
||||
backoff_sec = 10
|
||||
|
||||
# ✅ 同一台设备同一时间只允许一个任务跑
|
||||
lock = self._udid_locks[udid]
|
||||
if not lock.acquire(blocking=False):
|
||||
LogManager.method_error("同一UDID已有任务在运行,拒绝重复启动", "直播养号", udid=udid)
|
||||
return
|
||||
|
||||
try:
|
||||
# 外层重启循环:任何异常都会回到这里重新建 client/session
|
||||
while not event.is_set():
|
||||
|
||||
client = None
|
||||
session = None
|
||||
try:
|
||||
# —— 每次重启都新建 client/session ——
|
||||
client = wda.USBClient(udid, ev.wdaFunctionPort)
|
||||
session = client.session()
|
||||
|
||||
# 1) 先关再开
|
||||
ControlUtils.closeTikTok(session, udid)
|
||||
event.wait(timeout=1)
|
||||
ControlUtils.openTikTok(session, udid)
|
||||
event.wait(timeout=3)
|
||||
|
||||
recomend_cx = 0
|
||||
recomend_cy = 0
|
||||
# ========= 主循环 =========
|
||||
while not event.is_set():
|
||||
# 设置手机的节点深度为15,判断该页面是否正确
|
||||
# 2) 进入直播按钮(中英文都匹配)
|
||||
session.appium_settings({"snapshotMaxDepth": 15})
|
||||
el = session.xpath(
|
||||
'//XCUIElementTypeButton[@name="top_tabs_recomend" or @name="推荐" or @label="推荐"]'
|
||||
|
||||
live_button = session.xpath(
|
||||
'//XCUIElementTypeButton[@name="LIVE" or @label="LIVE" or @name="直播" or @label="直播"]'
|
||||
' | '
|
||||
'//XCUIElementTypeOther[@name="LIVE" or @label="LIVE" or @name="直播" or @label="直播"]'
|
||||
)
|
||||
# 获取推荐按钮所在的坐标
|
||||
if el.exists:
|
||||
bounds = el.bounds # 返回 [x, y, width, height]
|
||||
recomend_cx = bounds[0] + bounds[2] // 2
|
||||
recomend_cy = bounds[1] + bounds[3] // 2
|
||||
|
||||
if not el.exists:
|
||||
# 记录日志
|
||||
LogManager.method_error("找不到推荐按钮,养号出现问题,重启养号功能", "养号", udid=udid)
|
||||
# 手动的抛出异常 重启流程
|
||||
raise Exception("找不到推荐按钮,养号出现问题,重启养号功能")
|
||||
|
||||
if el.value != "1":
|
||||
LogManager.method_error("当前页面不是推荐页面,养号出现问题,重启养号功能", "养号", udid=udid)
|
||||
raise Exception("当前页面不是推荐页面,养号出现问题,重启养号功能")
|
||||
|
||||
LogManager.method_info("当前页面是推荐页面,开始养号", "养号", udid=udid)
|
||||
# 重新设置节点的深度,防止手机进行卡顿
|
||||
session.appium_settings({"snapshotMaxDepth": 0})
|
||||
|
||||
# ---- 截图保存 ----
|
||||
try:
|
||||
img = client.screenshot()
|
||||
base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
resource_dir = os.path.join(base_dir, "resources", udid)
|
||||
os.makedirs(resource_dir, exist_ok=True)
|
||||
filePath = os.path.join(resource_dir, "bgv.png")
|
||||
img.save(filePath)
|
||||
LogManager.method_info(f"保存屏幕图像成功 -> {filePath}", "养号", udid)
|
||||
print("保存了背景图:", filePath)
|
||||
event.wait(timeout=1)
|
||||
except Exception as e:
|
||||
LogManager.method_info(f"截图或保存失败,失败原因:{e}", "养号", udid)
|
||||
raise Exception("截图或保存失败,重启养号功能")
|
||||
|
||||
# ---- 视频逻辑 ----
|
||||
try:
|
||||
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
|
||||
for i in range(2):
|
||||
if scale == 3.0:
|
||||
tx, ty = AiUtils.findImageInScreen("add", udid)
|
||||
else:
|
||||
tx, ty = AiUtils.findImageInScreen("like1", udid)
|
||||
|
||||
if addX == tx and addY == ty:
|
||||
isSame = True
|
||||
event.wait(timeout=1)
|
||||
else:
|
||||
isSame = False
|
||||
# break
|
||||
|
||||
if addX > 0 and isSame:
|
||||
needLike = random.randint(0, 100)
|
||||
homeButton = AiUtils.findHomeButton(session)
|
||||
if homeButton:
|
||||
LogManager.method_info("有首页按钮,查看视频", "养号", udid)
|
||||
videoTime = random.randint(5, 15)
|
||||
LogManager.method_info("准备停止脚本", method="task")
|
||||
for _ in range(videoTime): # 0.2 秒一片
|
||||
if event.is_set():
|
||||
LogManager.method_info("停止脚本中", method="task")
|
||||
break
|
||||
event.wait(timeout=1)
|
||||
LogManager.method_info("停止脚本成功", method="task")
|
||||
|
||||
# 重置 session
|
||||
session.appium_settings({"snapshotMaxDepth": 0})
|
||||
|
||||
if needLike < 25:
|
||||
LogManager.method_info("进行点赞", "养号", udid)
|
||||
ControlUtils.clickLike(session, udid)
|
||||
LogManager.method_info("继续观看视频", "养号", udid)
|
||||
LogManager.method_info("准备划到下一个视频", "养号", udid)
|
||||
else:
|
||||
LogManager.method_error("找不到首页按钮。出错了", "养号", udid)
|
||||
|
||||
if isComment and random.random() > 0.70:
|
||||
self.comment_flow(filePath, session, udid, recomend_cx, recomend_cy)
|
||||
event.wait(timeout=2)
|
||||
|
||||
home = AiUtils.findHomeButton(session)
|
||||
if not home:
|
||||
raise Exception("没有找到首页按钮,重置")
|
||||
|
||||
videoTime = random.randint(15, 30)
|
||||
for _ in range(videoTime):
|
||||
if event.is_set():
|
||||
break
|
||||
event.wait(timeout=1)
|
||||
|
||||
ControlUtils.swipe_up(client)
|
||||
|
||||
# 如果is_monitoring 为False 则说明和监控消息没有联动,反正 则证明和监控消息进行联动
|
||||
if is_monitoring:
|
||||
# 监控消息按钮,判断是否有消息
|
||||
el = session.xpath(
|
||||
'//XCUIElementTypeButton[@name="a11y_vo_inbox"]'
|
||||
' | '
|
||||
'//XCUIElementTypeButton[contains(@name,"收件箱")]'
|
||||
' | '
|
||||
'//XCUIElementTypeButton[.//XCUIElementTypeStaticText[@value="收件箱"]]'
|
||||
)
|
||||
# 判断收件箱是否有消息
|
||||
if el.exists:
|
||||
try:
|
||||
m = re.search(r'(\d+)', el.label) # 抓到的第一个数字串
|
||||
except Exception as e:
|
||||
LogManager.method_error(f"解析收件箱数量异常: {e}", "检测消息", udid)
|
||||
|
||||
count = int(m.group(1)) if m else 0
|
||||
if count:
|
||||
break
|
||||
else:
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
print("刷视频脚本有错误:错误内容:", e)
|
||||
LogManager.method_error(f"刷视频过程出现错误,重试", "养号", udid)
|
||||
raise e # 抛出给上层,触发重生机制
|
||||
|
||||
except Exception as e:
|
||||
print("刷视频遇到错误了。错误内容:", e)
|
||||
LogManager.method_error(f"[{udid}] 养号出现异常,将重启流程: {e}", "养号", udid)
|
||||
event.wait(timeout=3)
|
||||
|
||||
# 观看直播
|
||||
def watchLiveForGrowth(self, udid, event, max_retries=None):
|
||||
import random, wda
|
||||
retry_count = 0
|
||||
backoff_sec = 10
|
||||
|
||||
# —— 每次重启都新建 client/session ——
|
||||
client = wda.USBClient(udid, ev.wdaFunctionPort)
|
||||
session = client.session()
|
||||
|
||||
while not event.is_set():
|
||||
try:
|
||||
|
||||
# 1) 先关再开
|
||||
ControlUtils.closeTikTok(session, udid)
|
||||
event.wait(timeout=1)
|
||||
ControlUtils.openTikTok(session, udid)
|
||||
event.wait(timeout=3)
|
||||
|
||||
session.appium_settings({"snapshotMaxDepth": 15})
|
||||
# 2) 进入直播 (使用英文)
|
||||
live_button = session(
|
||||
xpath='//XCUIElementTypeButton[@name="LIVE" or @label="LIVE" or @name="直播" or @label="直播"] '
|
||||
'| //XCUIElementTypeOther[@name="LIVE" or @label="LIVE" or @name="直播" or @label="直播"]'
|
||||
)
|
||||
|
||||
if live_button.exists:
|
||||
live_button.click()
|
||||
else:
|
||||
LogManager.method_error("无法找到直播间按钮 抛出异常 重新启动", "直播养号", udid)
|
||||
# 抛出异常
|
||||
raise Exception(f"找不到直播按钮,抛出异常 重新启动")
|
||||
waitTime = random.randint(15, 20)
|
||||
for _ in range(waitTime): # 0.2 秒一片
|
||||
if event.is_set():
|
||||
break
|
||||
event.wait(timeout=1)
|
||||
|
||||
# live_button = session(xpath='//XCUIElementTypeButton[@name="直播"]')
|
||||
live_button = session(
|
||||
xpath='//XCUIElementTypeButton[@name="LIVE" or @label="LIVE" or @name="直播" or @label="直播"] '
|
||||
'| //XCUIElementTypeOther[@name="LIVE" or @label="LIVE" or @name="直播" or @label="直播"]'
|
||||
)
|
||||
|
||||
if live_button.exists:
|
||||
continue
|
||||
|
||||
# 下滑一下
|
||||
ControlUtils.swipe_up(client)
|
||||
|
||||
# 3) 取分辨率;可选重建 session 规避句柄陈旧
|
||||
size = session.window_size()
|
||||
width, height = size.width, size.height
|
||||
|
||||
# 4) 主循环:刷直播
|
||||
while not event.is_set():
|
||||
event.wait(timeout=3)
|
||||
|
||||
# 找到一个看直播的时候肯定有的元素,当这个元素没有的时候,就代表当前的页面出现了问题
|
||||
# 需要抛出异常,重启这个流程
|
||||
el1 = session.xpath('//XCUIElementTypeOther[@name="GBLFeedRootViewComponent"]')
|
||||
el2 = session.xpath('//XCUIElementTypeOther[@value="0%"]')
|
||||
|
||||
if not (el1.exists or el2.exists):
|
||||
print("当前页面不是直播间,重启刷直播")
|
||||
LogManager.method_error("当前页面不是直播间,重启刷直播", "直播养号", udid=udid)
|
||||
raise Exception("当前页面不是直播间")
|
||||
if live_button.exists:
|
||||
live_button.click()
|
||||
else:
|
||||
print("当前页面是直播间,继续刷直播")
|
||||
LogManager.method_info("当前页面是直播间,继续刷直播", "直播养号", udid=udid)
|
||||
LogManager.method_error("无法找到直播间按钮,重启流程", "直播养号", udid=udid)
|
||||
raise Exception("找不到直播按钮")
|
||||
|
||||
# PK 直接划走
|
||||
if session(xpath='//XCUIElementTypeOther[@name="kGBLInteractionViewMatchScoreBar"]').exists:
|
||||
print("✅ 当前是 PK,跳过")
|
||||
LogManager.method_info("✅ 当前是 PK,跳过", "直播养号", udid=udid)
|
||||
ControlUtils.swipe_up(client)
|
||||
continue
|
||||
|
||||
# 计算直播显示窗口数量(主画面+连麦小窗)
|
||||
count = AiUtils.count_add_by_xml(session)
|
||||
print(f"检测到直播显示区域窗口数:{count}")
|
||||
|
||||
if count > 1:
|
||||
print("❌ 多窗口(有人连麦/分屏),划走")
|
||||
LogManager.method_info("❌ 多窗口(有人连麦/分屏),划走", "直播养号", udid=udid)
|
||||
ControlUtils.swipe_up(client)
|
||||
continue
|
||||
else:
|
||||
print("✅ 单窗口,(10%概率)开始点赞")
|
||||
LogManager.method_info("✅ 单窗口,(3%概率)开始点赞", "直播养号", udid=udid)
|
||||
|
||||
# 随机点赞(仍保留中途保护)
|
||||
if random.random() >= 0.97:
|
||||
print("开始点赞")
|
||||
LogManager.method_info("开始点赞", "直播养号", udid=udid)
|
||||
for _ in range(random.randint(10, 30)):
|
||||
# 中途转PK/连麦立即跳过
|
||||
if session(xpath='//XCUIElementTypeOther[@name="kGBLInteractionViewMatchScoreBar"]').exists \
|
||||
or AiUtils.count_add_by_xml(session) > 1:
|
||||
print("❗ 中途发生 PK/连麦,跳过")
|
||||
LogManager.method_info("❗ 中途发生 PK/连麦,跳过", "直播养号", udid=udid)
|
||||
ControlUtils.swipe_up(client)
|
||||
break
|
||||
x = width // 3 + random.randint(-10, 10)
|
||||
y = height // 3 + random.randint(10, 20)
|
||||
print("双击坐标:", x, y)
|
||||
|
||||
session.double_tap(x, y)
|
||||
|
||||
print("--------------------------------------------")
|
||||
# 换成
|
||||
total_seconds = random.randint(300, 600)
|
||||
for _ in range(total_seconds): # 0.2 秒一片
|
||||
# 进入后稍等
|
||||
wait_time = random.randint(15, 20)
|
||||
for _ in range(wait_time):
|
||||
if event.is_set():
|
||||
break
|
||||
event.wait(timeout=1)
|
||||
|
||||
# ✅ 恢复深度(避免一直高深度导致卡顿/递归)
|
||||
session.appium_settings({"snapshotMaxDepth": 0})
|
||||
|
||||
# 如果还在直播入口页(说明没进去),继续主流程重来
|
||||
live_button2 = session.xpath(
|
||||
'//XCUIElementTypeButton[@name="LIVE" or @label="LIVE" or @name="直播" or @label="直播"]'
|
||||
' | '
|
||||
'//XCUIElementTypeOther[@name="LIVE" or @label="LIVE" or @name="直播" or @label="直播"]'
|
||||
)
|
||||
if live_button2.exists:
|
||||
continue
|
||||
|
||||
# 下滑一下进入直播流
|
||||
ControlUtils.swipe_up(client)
|
||||
|
||||
# 正常退出(外部 event 触发)
|
||||
break
|
||||
# 取分辨率
|
||||
size = session.window_size()
|
||||
width, height = size.width, size.height
|
||||
|
||||
except Exception as e:
|
||||
retry_count += 1
|
||||
LogManager.method_error(f"watchLiveForGrowth 异常(第{retry_count}次):{repr(e)}", "直播养号", udid)
|
||||
# 尝试轻量恢复一次,避免一些短暂性 session 失效
|
||||
try:
|
||||
client = wda.USBClient(udid, ev.wdaFunctionPort)
|
||||
_ = client.session()
|
||||
except Exception:
|
||||
pass
|
||||
# 冷却后整段流程重来
|
||||
for _ in range(backoff_sec): # 0.2 秒一片
|
||||
if event.is_set():
|
||||
# 4) 主循环:刷直播
|
||||
while not event.is_set():
|
||||
|
||||
# 小停顿
|
||||
event.wait(timeout=3)
|
||||
|
||||
# 找一个直播间必存在的元素,用来判断是否还在直播间
|
||||
el1 = session.xpath('//XCUIElementTypeOther[@name="GBLFeedRootViewComponent"]')
|
||||
el2 = session.xpath('//XCUIElementTypeOther[@value="0%"]')
|
||||
|
||||
if not (el1.exists or el2.exists):
|
||||
LogManager.method_error("当前页面不是直播间,重启刷直播", "直播养号", udid=udid)
|
||||
raise Exception("当前页面不是直播间")
|
||||
else:
|
||||
LogManager.method_info("当前页面是直播间,继续刷直播", "直播养号", udid=udid)
|
||||
|
||||
# PK 直接划走
|
||||
if session.xpath('//XCUIElementTypeOther[@name="kGBLInteractionViewMatchScoreBar"]').exists:
|
||||
LogManager.method_info("✅ 当前是 PK,跳过", "直播养号", udid=udid)
|
||||
ControlUtils.swipe_up(client)
|
||||
continue
|
||||
|
||||
# 计算直播显示窗口数量(主画面+连麦小窗)
|
||||
count = AiUtils.count_add_by_xml(session)
|
||||
LogManager.method_info(f"检测到直播显示区域窗口数:{count}", "直播养号", udid=udid)
|
||||
|
||||
if count > 1:
|
||||
LogManager.method_info("❌ 多窗口(有人连麦/分屏),划走", "直播养号", udid=udid)
|
||||
ControlUtils.swipe_up(client)
|
||||
continue
|
||||
|
||||
# 随机点赞:约 3% 概率触发
|
||||
if random.random() >= 0.97:
|
||||
LogManager.method_info("✅ 单窗口,开始随机点赞", "直播养号", udid=udid)
|
||||
like_times = random.randint(10, 30)
|
||||
for _ in range(like_times):
|
||||
if event.is_set():
|
||||
break
|
||||
|
||||
# 中途转PK/连麦立即跳过
|
||||
is_pk = session.xpath(
|
||||
'//XCUIElementTypeOther[@name="kGBLInteractionViewMatchScoreBar"]').exists
|
||||
multi = AiUtils.count_add_by_xml(session) > 1
|
||||
if is_pk or multi:
|
||||
LogManager.method_info("❗ 中途发生 PK/连麦,跳过", "直播养号", udid=udid)
|
||||
ControlUtils.swipe_up(client)
|
||||
break
|
||||
|
||||
x = width // 3 + random.randint(-10, 10)
|
||||
y = height // 3 + random.randint(10, 20)
|
||||
session.double_tap(x, y)
|
||||
event.wait(timeout=random.uniform(0.3, 0.8))
|
||||
|
||||
# 停留观看 5~10 分钟
|
||||
total_seconds = random.randint(300, 600)
|
||||
for _ in range(total_seconds):
|
||||
if event.is_set():
|
||||
break
|
||||
event.wait(timeout=1)
|
||||
|
||||
# 下一场
|
||||
ControlUtils.swipe_up(client)
|
||||
|
||||
# 正常退出(外部 event 触发)
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
retry_count += 1
|
||||
LogManager.method_error(
|
||||
f"watchLiveForGrowth 异常(第{retry_count}次):{repr(e)}",
|
||||
"直播养号",
|
||||
udid
|
||||
)
|
||||
|
||||
# 达到最大重试次数则退出(如果你传了 max_retries)
|
||||
if max_retries is not None and retry_count >= max_retries:
|
||||
LogManager.method_error("达到最大重试次数,结束直播养号", "直播养号", udid)
|
||||
break
|
||||
event.wait(timeout=1)
|
||||
continue
|
||||
|
||||
# 冷却后整段流程重来(外层 while 会重建 session)
|
||||
for _ in range(backoff_sec):
|
||||
if event.is_set():
|
||||
break
|
||||
event.wait(timeout=1)
|
||||
continue
|
||||
|
||||
finally:
|
||||
# 尽量释放 session(不同wda版本不一定有close)
|
||||
try:
|
||||
if session is not None:
|
||||
session.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
"""
|
||||
外层包装,出现异常自动重试、
|
||||
|
||||
Reference in New Issue
Block a user