Resolved conflicts and committed changes
This commit is contained in:
@@ -14,14 +14,7 @@ anchorList: list[AnchorModel] = []
|
|||||||
# 线程锁
|
# 线程锁
|
||||||
anchorListLock = threading.Lock()
|
anchorListLock = threading.Lock()
|
||||||
# 打招呼数据
|
# 打招呼数据
|
||||||
prologueList = {
|
prologueList = {}
|
||||||
"Cantonese": ["你好", "你係邊個", "我好鍾意你", "我係你爸爸"],
|
|
||||||
"English": ["Hello,", "Who are you?", "I like you.", "I'm your dad."],
|
|
||||||
"Greek": ["Γεια σου,", "Ποιος είσαι;", "Μου αρέσεις.", "Είμαι ο μπαμπάς σου."],
|
|
||||||
"Japanese": ["こんにちは", "あなたは誰ですか", "あなたが好きです", "私はあなたのパパです"],
|
|
||||||
"Khmer": ["សួស្តី", "អ្នកជាអ្នកណា", "ខ្ញុំចូលចិត្តអ្នក", "ខ្ញុំគឺជាប៉ារបស់អ្នក"],
|
|
||||||
"Malay": ["Hai,", "Siapakah kamu?", "Aku suka kamu,", "Aku adalah ayahmu."]
|
|
||||||
}
|
|
||||||
|
|
||||||
# 评论数据
|
# 评论数据
|
||||||
commentList = []
|
commentList = []
|
||||||
|
|||||||
Binary file not shown.
@@ -204,11 +204,10 @@ class DeviceInfo:
|
|||||||
if major > 17:
|
if major > 17:
|
||||||
print("进入iOS17设备的分支")
|
print("进入iOS17设备的分支")
|
||||||
print(f"[WDA] iOS>17 调用 IOSActivator (port={wdaScreenPort})")
|
print(f"[WDA] iOS>17 调用 IOSActivator (port={wdaScreenPort})")
|
||||||
try:
|
print("准备启动隧道")
|
||||||
IOSActivator().activate(udid)
|
out = IOSActivator().activate(udid)
|
||||||
print("wda启动完成")
|
print("------------------",out)
|
||||||
except Exception as e:
|
print("wda启动完成")
|
||||||
print("错误信息:",e)
|
|
||||||
else:
|
else:
|
||||||
print(f"[WDA] iOS<=17 启动 WDA app_start (port={wdaScreenPort})")
|
print(f"[WDA] iOS<=17 启动 WDA app_start (port={wdaScreenPort})")
|
||||||
dev = tidevice.Device(udid)
|
dev = tidevice.Device(udid)
|
||||||
|
|||||||
@@ -382,8 +382,7 @@ def followAndGreetUnion():
|
|||||||
# 是否需要回复
|
# 是否需要回复
|
||||||
needReply = data.get("needReply", True)
|
needReply = data.get("needReply", True)
|
||||||
|
|
||||||
# 是否需要进行翻译
|
|
||||||
needTranslate = data.get("needTranslate", True)
|
|
||||||
|
|
||||||
# 获取打招呼数据
|
# 获取打招呼数据
|
||||||
ev.prologueList = data.get("prologueList", [])
|
ev.prologueList = data.get("prologueList", [])
|
||||||
@@ -396,7 +395,7 @@ def followAndGreetUnion():
|
|||||||
event = threading.Event()
|
event = threading.Event()
|
||||||
# 启动脚本
|
# 启动脚本
|
||||||
thread = threading.Thread(target=manager.safe_followAndGreetUnion,
|
thread = threading.Thread(target=manager.safe_followAndGreetUnion,
|
||||||
args=(udid, needReply, needTranslate, event))
|
args=(udid, needReply, event))
|
||||||
# 添加到线程管理
|
# 添加到线程管理
|
||||||
ThreadManager.add(udid, thread, event)
|
ThreadManager.add(udid, thread, event)
|
||||||
return ResultData(data="").toJson()
|
return ResultData(data="").toJson()
|
||||||
@@ -750,6 +749,18 @@ def getAiConfig():
|
|||||||
data = IOSAIStorage.load("aiConfig.json")
|
data = IOSAIStorage.load("aiConfig.json")
|
||||||
return ResultData(data=data).toJson()
|
return ResultData(data=data).toJson()
|
||||||
|
|
||||||
|
# 重新开启tiktok
|
||||||
|
@app.route("/restartTikTok", methods=['POST'])
|
||||||
|
def restartTikTok():
|
||||||
|
json = request.get_json()
|
||||||
|
udid = json.get("udid")
|
||||||
|
client = wda.USBClient(udid, wdaFunctionPort)
|
||||||
|
session = client.session()
|
||||||
|
ControlUtils.closeTikTok(session, udid)
|
||||||
|
time.sleep(1)
|
||||||
|
ControlUtils.openTikTok(session, udid)
|
||||||
|
return ResultData(data="").toJson()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# 注意:这里建议 debug=False,避免未来有人改成 use_reloader=True 导致多进程
|
# 注意:这里建议 debug=False,避免未来有人改成 use_reloader=True 导致多进程
|
||||||
app.run("0.0.0.0", port=5000, debug=False, use_reloader=False, threaded=True)
|
app.run("0.0.0.0", port=5000, debug=False, use_reloader=False, threaded=True)
|
||||||
@@ -9,6 +9,7 @@ import subprocess
|
|||||||
from typing import Optional, List, Tuple, Dict, Set
|
from typing import Optional, List, Tuple, Dict, Set
|
||||||
|
|
||||||
from Entity.Variables import WdaAppBundleId
|
from Entity.Variables import WdaAppBundleId
|
||||||
|
import time as _t
|
||||||
|
|
||||||
|
|
||||||
class IOSActivator:
|
class IOSActivator:
|
||||||
@@ -32,139 +33,178 @@ class IOSActivator:
|
|||||||
self._live_ifaces: Dict[str, Set[str]] = {} # udid -> {iface names}
|
self._live_ifaces: Dict[str, Set[str]] = {} # udid -> {iface names}
|
||||||
self._registered = False
|
self._registered = False
|
||||||
|
|
||||||
|
|
||||||
# =============== 公共入口 ===============
|
# =============== 公共入口 ===============
|
||||||
def activate(
|
def activate(
|
||||||
self,
|
self,
|
||||||
udid: str,
|
udid: str,
|
||||||
wda_bundle_id: str = WdaAppBundleId,
|
wda_bundle_id: Optional[str] = WdaAppBundleId,
|
||||||
ready_timeout_sec: float = 60.0,
|
ready_timeout_sec: float = 120.0,
|
||||||
|
mount_retries: int = 3,
|
||||||
|
backoff_seconds: float = 2.0,
|
||||||
|
rsd_probe_retries: int = 5,
|
||||||
|
rsd_probe_delay_sec: float = 3.0,
|
||||||
pre_mount_first: bool = True,
|
pre_mount_first: bool = True,
|
||||||
mount_retries: int = 2,
|
keep_tunnel: bool = False, # 默认 False:WDA 拉起后关闭隧道
|
||||||
backoff_seconds: float = 1.5,
|
broad_cleanup_on_exit: bool = True, # 退出时顺带清理所有 pmd3 残留网卡
|
||||||
keep_tunnel: bool = False,
|
|
||||||
broad_cleanup_on_exit: bool = True,
|
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Windows 简版:不读任何 tunneld 日志,也不做 RSD 解析。
|
流程:挂镜像(可选) -> 开隧道 -> 等 RSD -> 启动 WDA
|
||||||
逻辑:先探活 -> 开隧道 -> 直接用 HTTP 隧道端口反复尝试启动 WDA -> 探活成功即返回。
|
- keep_tunnel=False:WDA 启动后关闭隧道并清理
|
||||||
|
- keep_tunnel=True:隧道常驻,由上层/atexit 清理
|
||||||
"""
|
"""
|
||||||
import time, ctypes, traceback
|
|
||||||
|
|
||||||
if not udid or not isinstance(udid, str):
|
if not udid or not isinstance(udid, str):
|
||||||
raise ValueError("udid is required and must be a non-empty string")
|
raise ValueError("udid is required and must be a non-empty string")
|
||||||
|
|
||||||
print(f"[activate] UDID={udid}", flush=True)
|
print(f"[activate] UDID = {udid}")
|
||||||
|
self._ensure_exit_hooks(broad_cleanup_on_exit=broad_cleanup_on_exit)
|
||||||
|
|
||||||
# —— 管理员提示(Windows 清理虚拟网卡常用)——
|
# Windows 管理员检测
|
||||||
try:
|
if os.name == "nt":
|
||||||
if ctypes.windll.shell32.IsUserAnAdmin() == 0:
|
import ctypes
|
||||||
print("[⚠] 未以管理员运行:若需要移除虚拟网卡,可能失败。", flush=True)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# —— 退出钩子(可选)——
|
|
||||||
try:
|
|
||||||
self._ensure_exit_hooks(broad_cleanup_on_exit=broad_cleanup_on_exit) # type: ignore[attr-defined]
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[activate] _ensure_exit_hooks warn: {e}", flush=True)
|
|
||||||
|
|
||||||
# —— 小工具:探活 WDA —— #
|
|
||||||
def _wda_alive(timeout: float = 2.0) -> bool:
|
|
||||||
try:
|
try:
|
||||||
if hasattr(self, "_wda_alive_now"):
|
is_admin = ctypes.windll.shell32.IsUserAnAdmin() != 0
|
||||||
return bool(self._wda_alive_now(udid, timeout=timeout)) # type: ignore[attr-defined]
|
|
||||||
if hasattr(self, "_wda_client"):
|
|
||||||
cli = self._wda_client(udid) # type: ignore[attr-defined]
|
|
||||||
if hasattr(cli, "wait_ready"):
|
|
||||||
return bool(cli.wait_ready(timeout=timeout))
|
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
is_admin = False
|
||||||
return False
|
if not is_admin:
|
||||||
|
print("[⚠] 未以管理员运行:若需要移除虚拟网卡,可能失败。")
|
||||||
|
|
||||||
# 0) 快路径:WDA 已活
|
start_ts = _t.time()
|
||||||
if _wda_alive(2.0):
|
|
||||||
print("[activate] WDA already alive, skip launching.", flush=True)
|
|
||||||
return "WDA already alive"
|
|
||||||
|
|
||||||
# 1) 预挂载(失败不致命)
|
# 1) 预挂载(失败不致命)
|
||||||
if pre_mount_first and hasattr(self, "_auto_mount_developer_disk"):
|
if pre_mount_first:
|
||||||
try:
|
try:
|
||||||
self._auto_mount_developer_disk(udid, retries=mount_retries,
|
self._auto_mount_developer_disk(
|
||||||
backoff_seconds=backoff_seconds) # type: ignore[attr-defined]
|
udid, retries=mount_retries, backoff_seconds=backoff_seconds
|
||||||
time.sleep(1.5)
|
)
|
||||||
|
_t.sleep(2)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[activate] 预挂载失败(继续):{e}", flush=True)
|
print(f"[activate] 预挂载失败(稍后再试):{e}")
|
||||||
|
|
||||||
# 2) 开隧道(关键:拿到 HTTP 端口即可;不读取任何 stdout/stderr)
|
# 2) 启动 tunneld
|
||||||
proc = None
|
http_host: Optional[str] = None
|
||||||
http_host, http_port = "127.0.0.1", None
|
http_port: Optional[str] = None # ⚠️ 端口以 str 存储
|
||||||
try:
|
rsd_host: Optional[str] = None
|
||||||
ret = self._start_tunneld(udid) # type: ignore[attr-defined]
|
rsd_port: Optional[str] = None # ⚠️ 端口以 str 存储
|
||||||
if isinstance(ret, tuple):
|
iface_names: Set[str] = set()
|
||||||
proc, http_port = ret[0], ret[1]
|
|
||||||
else:
|
|
||||||
proc = ret
|
|
||||||
if http_port is None:
|
|
||||||
# 若你的 _start_tunneld 固定端口,可在这里写死(例如 8100/某自定义端口)
|
|
||||||
raise RuntimeError("未获取到 HTTP 隧道端口(_start_tunneld 未返回端口)")
|
|
||||||
except Exception:
|
|
||||||
# 即便开隧道失败,也再探活一次(可能本来就活)
|
|
||||||
if _wda_alive(2.0):
|
|
||||||
print("[activate] WDA already alive (tunnel start failed but OK).", flush=True)
|
|
||||||
return "WDA already alive"
|
|
||||||
raise
|
|
||||||
|
|
||||||
print(f"[tunneld] HTTP tunnel at {http_host}:{http_port}", flush=True)
|
proc, _port_ignored = self._start_tunneld(udid)
|
||||||
|
self._live_procs[udid] = proc
|
||||||
|
self._live_ifaces[udid] = iface_names
|
||||||
|
|
||||||
# 3) 直接用 HTTP 隧道反复尝试启动 WDA + 探活
|
captured: List[str] = []
|
||||||
deadline = time.time() + (ready_timeout_sec if ready_timeout_sec > 0 else 60.0)
|
out: str = ""
|
||||||
launched = False
|
wda_started = False
|
||||||
|
mount_done = pre_mount_first
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while time.time() < deadline:
|
assert proc.stdout is not None
|
||||||
# 已活则成功返回
|
for line in proc.stdout:
|
||||||
if _wda_alive(1.5):
|
captured.append(line)
|
||||||
print("[activate] WDA detected alive.", flush=True)
|
# 日志长度控制,防止常驻时内存涨太多
|
||||||
launched = True
|
if len(captured) > 20000:
|
||||||
|
captured = captured[-10000:]
|
||||||
|
|
||||||
|
print(f"[tunneld] {line}", end="")
|
||||||
|
|
||||||
|
# 捕获虚拟网卡名
|
||||||
|
for m in self.IFACE_RE.finditer(line):
|
||||||
|
iface_names.add(m.group(1))
|
||||||
|
|
||||||
|
# 子进程若退出则停止读取
|
||||||
|
if proc.poll() is not None:
|
||||||
break
|
break
|
||||||
|
|
||||||
# 尝试发起一次 HTTP 启动(失败就下一轮重试)
|
# 捕获 HTTP 网关端口(保持为字符串)
|
||||||
try:
|
if http_port is None:
|
||||||
if hasattr(self, "_launch_wda_via_http_tunnel"):
|
m = self.HTTP_RE.search(line)
|
||||||
self._launch_wda_via_http_tunnel( # type: ignore[attr-defined]
|
if m:
|
||||||
|
http_host = m.group(1)
|
||||||
|
http_port = m.group(2)
|
||||||
|
# 简单校验
|
||||||
|
try:
|
||||||
|
_ = int(http_port)
|
||||||
|
except Exception:
|
||||||
|
print(f"[tunneld] bad http port: {http_port}")
|
||||||
|
http_host, http_port = None, None
|
||||||
|
else:
|
||||||
|
print(f"[tunneld] Tunnel API: {http_host}:{http_port}")
|
||||||
|
|
||||||
|
# 只处理当前 UDID 的 RSD 行
|
||||||
|
if not self._line_is_for_udid(line, udid):
|
||||||
|
continue
|
||||||
|
|
||||||
|
m = self.RSD_CREATED_RE.search(line) or self.RSD_FALLBACK_RE.search(line)
|
||||||
|
if m and rsd_host is None and rsd_port is None:
|
||||||
|
rsd_host = m.group(1)
|
||||||
|
rsd_port = m.group(2)
|
||||||
|
try:
|
||||||
|
_ = int(rsd_port) # 仅作数字校验
|
||||||
|
except Exception:
|
||||||
|
print(f"[tunneld] bad rsd port: {rsd_port}")
|
||||||
|
rsd_host, rsd_port = None, None
|
||||||
|
else:
|
||||||
|
print(f"[tunneld] Device-level tunnel ready (RSD {rsd_host}:{rsd_port}).")
|
||||||
|
|
||||||
|
# ========= 尝试启动 WDA =========
|
||||||
|
if (not wda_started) and wda_bundle_id and (rsd_host is not None) and (rsd_port is not None):
|
||||||
|
if not mount_done:
|
||||||
|
self._auto_mount_developer_disk(
|
||||||
|
udid, retries=mount_retries, backoff_seconds=backoff_seconds
|
||||||
|
)
|
||||||
|
_t.sleep(2)
|
||||||
|
mount_done = True
|
||||||
|
|
||||||
|
# RSD 优先;探测时临时转 int;启动命令仍传 str 端口
|
||||||
|
rsd_port_int = int(rsd_port)
|
||||||
|
if self._wait_for_rsd_ready(
|
||||||
|
rsd_host, rsd_port_int, retries=rsd_probe_retries, delay=rsd_probe_delay_sec
|
||||||
|
):
|
||||||
|
# 这里的实现通常会拼 subprocess 命令行,故端口保持 str
|
||||||
|
self._launch_wda_via_rsd(
|
||||||
bundle_id=wda_bundle_id,
|
bundle_id=wda_bundle_id,
|
||||||
http_host=http_host,
|
rsd_host=rsd_host,
|
||||||
http_port=str(http_port),
|
rsd_port=rsd_port, # ⚠️ 传入 str,避免 subprocess 报错
|
||||||
udid=udid,
|
udid=udid,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
wda_started = True
|
||||||
# 仅打印,不中断;下一次循环再试
|
elif (http_host is not None) and (http_port is not None):
|
||||||
print(f"[activate] _launch_wda_via_http_tunnel error: {e}", flush=True)
|
self._launch_wda_via_http_tunnel(
|
||||||
|
bundle_id=wda_bundle_id,
|
||||||
|
http_host=http_host,
|
||||||
|
http_port=http_port, # ⚠️ 传入 str
|
||||||
|
udid=udid,
|
||||||
|
)
|
||||||
|
wda_started = True
|
||||||
|
else:
|
||||||
|
raise RuntimeError("No valid tunnel endpoint for WDA.")
|
||||||
|
|
||||||
# 启动后给一点时间让 WDA ready
|
# ✅ WDA 已启动;默认一次性模式直接退出读取循环
|
||||||
for _ in range(3):
|
if wda_started and not keep_tunnel:
|
||||||
if _wda_alive(1.0):
|
_t.sleep(0.5) # 给隧道多刷几行
|
||||||
launched = True
|
print("[activate] WDA launched; exiting reader loop (keep_tunnel=False).")
|
||||||
break
|
break
|
||||||
time.sleep(0.5)
|
|
||||||
|
|
||||||
if launched:
|
# 超时保护(仅在 WDA 尚未启动时生效)
|
||||||
|
if (not wda_started) and ready_timeout_sec > 0 and (_t.time() - start_ts > ready_timeout_sec):
|
||||||
|
print(f"[tunneld] Timeout waiting for device tunnel ({ready_timeout_sec}s). Aborting.")
|
||||||
break
|
break
|
||||||
|
|
||||||
time.sleep(1.0) # 下一轮重试
|
print("[activate] 启动 WDA 读取阶段结束")
|
||||||
|
out = "".join(captured)
|
||||||
if not launched:
|
|
||||||
raise RuntimeError(f"WDA not ready within {ready_timeout_sec}s via HTTP tunnel")
|
|
||||||
|
|
||||||
print("[activate] Done.", flush=True)
|
|
||||||
return f"http://{http_host}:{http_port}"
|
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[activate] 发生异常:{e}")
|
||||||
|
raise
|
||||||
finally:
|
finally:
|
||||||
if not keep_tunnel:
|
if not keep_tunnel:
|
||||||
try:
|
try:
|
||||||
self.stop_tunnel(udid, broad_cleanup=broad_cleanup_on_exit) # type: ignore[attr-defined]
|
self.stop_tunnel(udid, broad_cleanup=broad_cleanup_on_exit)
|
||||||
except Exception as e:
|
except Exception as ce:
|
||||||
print(f"[activate] stop_tunnel warn: {e}", flush=True)
|
print(f"[activate] stop_tunnel 清理异常:{ce}")
|
||||||
|
|
||||||
|
print("[activate] Done.")
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
# =============== 外部可显式调用的清理 ===============
|
# =============== 外部可显式调用的清理 ===============
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 482 KiB |
@@ -50,30 +50,6 @@ class ScriptManager():
|
|||||||
|
|
||||||
self.initialized = True # 标记已初始化
|
self.initialized = True # 标记已初始化
|
||||||
|
|
||||||
# 放在类里或公共工具模块里均可
|
|
||||||
def interruptible_wait(self,event: threading.Event, total: float, step: float = 0.2) -> bool:
|
|
||||||
"""
|
|
||||||
等待 total 秒,但每 step 秒检查一次停止信号。
|
|
||||||
若在等待期间 event 被置位,立刻返回 True;否则到点返回 False。
|
|
||||||
"""
|
|
||||||
deadline = time.time() + total
|
|
||||||
while time.time() < deadline:
|
|
||||||
if event.is_set():
|
|
||||||
return True
|
|
||||||
|
|
||||||
event.wait(timeout=min(step, max(0, deadline - time.time())))
|
|
||||||
return event.is_set()
|
|
||||||
|
|
||||||
def interruptible_sleep(self,event: threading.Event, seconds: float, step: float = 0.2) -> bool:
|
|
||||||
"""语义同上;返回 True 表示期间接到停止信号。"""
|
|
||||||
return self.interruptible_wait(event, seconds, step)
|
|
||||||
|
|
||||||
def check_stop(self,event: threading.Event, tag: str = ""):
|
|
||||||
"""在关键点快速失败,保持调用栈整洁(不改变业务路径,只是早退出)。"""
|
|
||||||
if event.is_set():
|
|
||||||
raise RuntimeError(f"stop-requested:{tag}")
|
|
||||||
|
|
||||||
|
|
||||||
# ========= 评论逻辑 =========
|
# ========= 评论逻辑 =========
|
||||||
def comment_flow(self, filePath, session, udid, recomend_cx, recomend_cy):
|
def comment_flow(self, filePath, session, udid, recomend_cx, recomend_cy):
|
||||||
"""评论一条龙:点评论框->输入->发送->返回"""
|
"""评论一条龙:点评论框->输入->发送->返回"""
|
||||||
@@ -450,11 +426,9 @@ class ScriptManager():
|
|||||||
break
|
break
|
||||||
LogManager.method_error("greetNewFollowers 重试次数耗尽,任务终止", "关注打招呼", udid)
|
LogManager.method_error("greetNewFollowers 重试次数耗尽,任务终止", "关注打招呼", udid)
|
||||||
|
|
||||||
|
# 关注打招呼
|
||||||
def greetNewFollowers(self, udid, needReply, isComment, event):
|
def greetNewFollowers(self, udid, needReply, isComment, event):
|
||||||
|
|
||||||
if self.check_stop(event, "init"): # [ADD]
|
|
||||||
return
|
|
||||||
|
|
||||||
client = wda.USBClient(udid, ev.wdaFunctionPort)
|
client = wda.USBClient(udid, ev.wdaFunctionPort)
|
||||||
session = client.session()
|
session = client.session()
|
||||||
|
|
||||||
@@ -464,18 +438,11 @@ class ScriptManager():
|
|||||||
|
|
||||||
# 先关闭Tik Tok
|
# 先关闭Tik Tok
|
||||||
ControlUtils.closeTikTok(session, udid)
|
ControlUtils.closeTikTok(session, udid)
|
||||||
|
event.wait(timeout=1)
|
||||||
|
|
||||||
if self.interruptible_sleep(event, 1): # [ADD] 可中断等待
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.check_stop(event, "after-close-app"): # [ADD]
|
|
||||||
return
|
|
||||||
|
|
||||||
# 重新打开Tik Tok
|
# 重新打开Tik Tok
|
||||||
ControlUtils.openTikTok(session, udid)
|
ControlUtils.openTikTok(session, udid)
|
||||||
if self.interruptible_sleep(event, 3): # [ADD]
|
event.wait(timeout=3)
|
||||||
return
|
|
||||||
LogManager.method_info(f"重启tiktok", "关注打招呼", udid)
|
LogManager.method_info(f"重启tiktok", "关注打招呼", udid)
|
||||||
|
|
||||||
# 设置查找深度
|
# 设置查找深度
|
||||||
@@ -488,12 +455,9 @@ class ScriptManager():
|
|||||||
def goBack(count):
|
def goBack(count):
|
||||||
for i in range(count):
|
for i in range(count):
|
||||||
LogManager.method_info(f"返回上一步", "关注打招呼", udid)
|
LogManager.method_info(f"返回上一步", "关注打招呼", udid)
|
||||||
if self.check_stop(event, f"goBack-{i + 1}/{count}"): # [ADD]
|
|
||||||
return
|
|
||||||
session.appium_settings({"snapshotMaxDepth": 15})
|
session.appium_settings({"snapshotMaxDepth": 15})
|
||||||
ControlUtils.clickBack(session)
|
ControlUtils.clickBack(session)
|
||||||
if self.interruptible_sleep(event, 2): # [ADD]
|
event.wait(timeout=2)
|
||||||
return
|
|
||||||
|
|
||||||
LogManager.method_info(f"循环条件1:{not event.is_set()}", "关注打招呼", udid)
|
LogManager.method_info(f"循环条件1:{not event.is_set()}", "关注打招呼", udid)
|
||||||
LogManager.method_info(f"循环条件2:{len(anchorList) > 0}", "关注打招呼", udid)
|
LogManager.method_info(f"循环条件2:{len(anchorList) > 0}", "关注打招呼", udid)
|
||||||
@@ -502,15 +466,13 @@ class ScriptManager():
|
|||||||
# 循环条件。1、 循环关闭 2、 数据处理完毕
|
# 循环条件。1、 循环关闭 2、 数据处理完毕
|
||||||
while not event.is_set():
|
while not event.is_set():
|
||||||
|
|
||||||
if self.check_stop(event, "loop-top"): # [ADD]
|
|
||||||
return
|
|
||||||
|
|
||||||
LogManager.method_info("=== 外层 while 新一轮 ===", "关注打招呼", udid)
|
LogManager.method_info("=== 外层 while 新一轮 ===", "关注打招呼", udid)
|
||||||
if event.is_set():
|
if event.is_set():
|
||||||
break
|
break
|
||||||
|
|
||||||
# 获取一个主播,
|
# 获取一个主播,
|
||||||
LogManager.method_info(f"开始获取数据", "关注打招呼", udid)
|
LogManager.method_info(f"开始获取数据", "关注打招呼", udid)
|
||||||
|
# 获取一个主播,
|
||||||
result = AiUtils.peek_aclist_first()
|
result = AiUtils.peek_aclist_first()
|
||||||
LogManager.method_info(f"数据是:{result}", "关注打招呼", udid)
|
LogManager.method_info(f"数据是:{result}", "关注打招呼", udid)
|
||||||
|
|
||||||
@@ -528,21 +490,17 @@ class ScriptManager():
|
|||||||
|
|
||||||
if not anchor:
|
if not anchor:
|
||||||
LogManager.method_info(f"数据库中的数据不足", "关注打招呼", udid)
|
LogManager.method_info(f"数据库中的数据不足", "关注打招呼", udid)
|
||||||
# 你原来的写法:等待完成就 continue;中途被打断就 return
|
|
||||||
if not self.interruptible_sleep(event, 30):
|
if not self.interruptible_sleep(event, 30):
|
||||||
continue
|
continue
|
||||||
return # [ADD] 被打断则退出
|
|
||||||
|
|
||||||
aid = anchor.get("anchorId", "")
|
aid = anchor.get("anchorId", "")
|
||||||
anchorCountry = anchor.get("country", "")
|
anchorCountry = anchor.get("country", "")
|
||||||
|
|
||||||
LogManager.method_info(f"主播的数据,用户名:{aid},国家:{anchorCountry}", "关注打招呼", udid)
|
LogManager.method_info(f"主播的数据,用户名:{aid},国家:{anchorCountry}", "关注打招呼", udid)
|
||||||
|
|
||||||
if self.check_stop(event, "before-search"): # [ADD]
|
|
||||||
return
|
|
||||||
|
|
||||||
# 点击搜索按钮
|
# 点击搜索按钮
|
||||||
ControlUtils.clickSearch(session)
|
ControlUtils.clickSearch(session)
|
||||||
|
|
||||||
LogManager.method_info(f"点击搜索按钮", "关注打招呼", udid)
|
LogManager.method_info(f"点击搜索按钮", "关注打招呼", udid)
|
||||||
|
|
||||||
# 强制刷新session
|
# 强制刷新session
|
||||||
@@ -554,21 +512,21 @@ class ScriptManager():
|
|||||||
# 如果找到了输入框,就点击并且输入内容
|
# 如果找到了输入框,就点击并且输入内容
|
||||||
if input.exists:
|
if input.exists:
|
||||||
input.click()
|
input.click()
|
||||||
# 稍作停顿(用你的可中断等待)
|
# 稍作停顿
|
||||||
if self.interruptible_sleep(event, 0.5): # [ADD]
|
event.wait(timeout=0.5)
|
||||||
return
|
|
||||||
else:
|
else:
|
||||||
print(f"找不到输入框")
|
print(f"找不到输入框")
|
||||||
|
raise Exception("找不到输入框")
|
||||||
|
|
||||||
input = session.xpath('//XCUIElementTypeSearchField')
|
input = session.xpath('//XCUIElementTypeSearchField')
|
||||||
if input.exists:
|
if input.exists:
|
||||||
input.clear_text()
|
input.clear_text()
|
||||||
if self.interruptible_sleep(event, 1): # [ADD]
|
event.wait(timeout=1)
|
||||||
return
|
|
||||||
# 输入主播id
|
# 输入主播id
|
||||||
input.set_text(f"{aid or '暂无数据'}\n")
|
input.set_text(f"{aid or '暂无数据'}\n")
|
||||||
|
|
||||||
# 定位 "关注" 按钮 通过关注按钮的位置点击主播首页
|
# 定位 "关注" 按钮 通过关注按钮的位置点击主播首页
|
||||||
|
|
||||||
session.appium_settings({"snapshotMaxDepth": 25})
|
session.appium_settings({"snapshotMaxDepth": 25})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -583,22 +541,15 @@ class ScriptManager():
|
|||||||
session.appium_settings({"snapshotMaxDepth": 15})
|
session.appium_settings({"snapshotMaxDepth": 15})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if self.interruptible_sleep(event, 2): # [ADD]
|
event.wait(timeout=2)
|
||||||
return
|
|
||||||
|
|
||||||
# 找到并点击第一个视频
|
# 找到并点击第一个视频
|
||||||
cellClickResult, workCount = ControlUtils.clickFirstVideoFromDetailPage(session)
|
cellClickResult, workCount = ControlUtils.clickFirstVideoFromDetailPage(session)
|
||||||
LogManager.method_info(f"点击第一个视频", "关注打招呼", udid)
|
|
||||||
|
|
||||||
if self.interruptible_sleep(event, 2): # [ADD]
|
LogManager.method_info(f"点击第一个视频", "关注打招呼", udid)
|
||||||
return
|
event.wait(timeout=2)
|
||||||
|
|
||||||
# 观看主播视频
|
# 观看主播视频
|
||||||
def viewAnchorVideo(workCount):
|
def viewAnchorVideo(workCount):
|
||||||
|
|
||||||
if self.check_stop(event, "viewVideo-enter"): # [ADD]
|
|
||||||
return
|
|
||||||
|
|
||||||
print("开始查看视频,并且重新调整查询深度")
|
print("开始查看视频,并且重新调整查询深度")
|
||||||
session.appium_settings({"snapshotMaxDepth": 5})
|
session.appium_settings({"snapshotMaxDepth": 5})
|
||||||
|
|
||||||
@@ -613,16 +564,12 @@ class ScriptManager():
|
|||||||
LogManager.method_info("停止脚本中", method="task")
|
LogManager.method_info("停止脚本中", method="task")
|
||||||
if event.is_set():
|
if event.is_set():
|
||||||
break
|
break
|
||||||
if self.interruptible_sleep(event, 1): # [ADD]
|
event.wait(timeout=1)
|
||||||
return
|
|
||||||
LogManager.method_info("停止脚本成功", method="task")
|
LogManager.method_info("停止脚本成功", method="task")
|
||||||
|
|
||||||
if self.check_stop(event, "before-screenshot"): # [ADD]
|
|
||||||
return
|
|
||||||
|
|
||||||
img = client.screenshot()
|
img = client.screenshot()
|
||||||
if self.interruptible_sleep(event, 1): # [ADD]
|
event.wait(timeout=1)
|
||||||
return
|
|
||||||
|
# filePath = f"resources/{udid}/bgv.png"
|
||||||
|
|
||||||
base_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) # 当前脚本目录的上一级
|
base_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) # 当前脚本目录的上一级
|
||||||
filePath = os.path.join(base_dir, "resources", udid, "bgv.png")
|
filePath = os.path.join(base_dir, "resources", udid, "bgv.png")
|
||||||
@@ -633,9 +580,7 @@ class ScriptManager():
|
|||||||
img.save(filePath)
|
img.save(filePath)
|
||||||
|
|
||||||
LogManager.method_info("保存屏幕图像成功", "关注打招呼", udid)
|
LogManager.method_info("保存屏幕图像成功", "关注打招呼", udid)
|
||||||
if self.interruptible_sleep(event, 2): # [ADD]
|
event.wait(timeout=2)
|
||||||
return
|
|
||||||
|
|
||||||
# 查找add图标
|
# 查找add图标
|
||||||
r = ControlUtils.clickLike(session, udid)
|
r = ControlUtils.clickLike(session, udid)
|
||||||
|
|
||||||
@@ -646,8 +591,7 @@ class ScriptManager():
|
|||||||
LogManager.method_info("停止脚本中", method="task")
|
LogManager.method_info("停止脚本中", method="task")
|
||||||
if event.is_set():
|
if event.is_set():
|
||||||
break
|
break
|
||||||
if self.interruptible_sleep(event, 1): # [ADD]
|
event.wait(timeout=1)
|
||||||
return
|
|
||||||
LogManager.method_info("停止脚本成功", method="task")
|
LogManager.method_info("停止脚本成功", method="task")
|
||||||
|
|
||||||
# 使用OCR进行评论
|
# 使用OCR进行评论
|
||||||
@@ -668,20 +612,16 @@ class ScriptManager():
|
|||||||
# 观看主播视频
|
# 观看主播视频
|
||||||
LogManager.method_info("去查看主播视频", "关注打招呼", udid)
|
LogManager.method_info("去查看主播视频", "关注打招呼", udid)
|
||||||
viewAnchorVideo(workCount)
|
viewAnchorVideo(workCount)
|
||||||
if self.interruptible_sleep(event, 3): # [ADD]
|
event.wait(timeout=3)
|
||||||
return
|
|
||||||
LogManager.method_info("视频看完了,重置试图查询深度", "关注打招呼", udid)
|
LogManager.method_info("视频看完了,重置试图查询深度", "关注打招呼", udid)
|
||||||
session.appium_settings({"snapshotMaxDepth": 25})
|
session.appium_settings({"snapshotMaxDepth": 25})
|
||||||
if self.interruptible_sleep(event, 0.5): # [ADD]
|
event.wait(timeout=0.5)
|
||||||
return
|
|
||||||
# 向上滑动
|
# 向上滑动
|
||||||
ControlUtils.swipe_down(udid)
|
ControlUtils.swipe_down(udid)
|
||||||
|
|
||||||
if self.interruptible_sleep(event, 2): # [ADD]
|
event.wait(timeout=2)
|
||||||
return
|
|
||||||
msgButton = AiUtils.getSendMesageButton(session)
|
msgButton = AiUtils.getSendMesageButton(session)
|
||||||
if self.interruptible_sleep(event, 2): # [ADD]
|
event.wait(timeout=2)
|
||||||
return
|
|
||||||
|
|
||||||
if msgButton.exists:
|
if msgButton.exists:
|
||||||
# 进入聊天页面
|
# 进入聊天页面
|
||||||
@@ -695,8 +635,7 @@ class ScriptManager():
|
|||||||
session.appium_settings({"snapshotMaxDepth": 15})
|
session.appium_settings({"snapshotMaxDepth": 15})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if self.interruptible_sleep(event, 3): # [ADD]
|
event.wait(timeout=3)
|
||||||
return
|
|
||||||
# 查找聊天界面中的输入框节点
|
# 查找聊天界面中的输入框节点
|
||||||
chatInput = session.xpath("//TextView")
|
chatInput = session.xpath("//TextView")
|
||||||
if chatInput.exists:
|
if chatInput.exists:
|
||||||
@@ -705,15 +644,15 @@ class ScriptManager():
|
|||||||
LogManager.method_info("找到输入框了, 准备发送一条打招呼消息", "关注打招呼", udid)
|
LogManager.method_info("找到输入框了, 准备发送一条打招呼消息", "关注打招呼", udid)
|
||||||
|
|
||||||
print("打招呼的数据", ev.prologueList)
|
print("打招呼的数据", ev.prologueList)
|
||||||
LogManager.method_info(f"传递的打招呼的数据:{ev.prologueList}", "关注打招呼", udid)
|
# LogManager.method_info(f"传递的打招呼的数据:{ev.prologueList}", "关注打招呼", udid)
|
||||||
|
|
||||||
|
|
||||||
# 取出国家进行对应国家语言代码
|
# 取出国家进行对应国家语言代码
|
||||||
anchorCountry_code = CountryLanguageMapper.get_language_code(anchorCountry)
|
anchorCountry_code = CountryLanguageMapper.get_language_code(anchorCountry)
|
||||||
|
print(anchorCountry_code)
|
||||||
|
|
||||||
|
|
||||||
|
print("存储的是:",ev.prologueList)
|
||||||
|
|
||||||
# 判断对应的语言代码是否在传入的字典中
|
# 判断对应的语言代码是否在传入的字典中
|
||||||
if anchorCountry_code in ev.prologueList:
|
if anchorCountry_code in ev.prologueList:
|
||||||
# 进行原本的进行传入
|
# 进行原本的进行传入
|
||||||
@@ -746,10 +685,10 @@ class ScriptManager():
|
|||||||
if chatInput.exists:
|
if chatInput.exists:
|
||||||
chatInput.click()
|
chatInput.click()
|
||||||
chatInput.set_text(f"{msg or '暂无数据'}\n")
|
chatInput.set_text(f"{msg or '暂无数据'}\n")
|
||||||
if self.interruptible_sleep(event, 2): # [ADD]
|
event.wait(timeout=2)
|
||||||
return
|
# 发送消息
|
||||||
if self.interruptible_sleep(event, 1): # [ADD]
|
# input.set_text(f"{aid or '暂无数据'}\n")
|
||||||
return
|
event.wait(timeout=1)
|
||||||
else:
|
else:
|
||||||
print("无法发送信息")
|
print("无法发送信息")
|
||||||
LogManager.method_info(f"给主播{aid} 发送消息失败", "关注打招呼", udid)
|
LogManager.method_info(f"给主播{aid} 发送消息失败", "关注打招呼", udid)
|
||||||
@@ -757,6 +696,27 @@ class ScriptManager():
|
|||||||
# 接着下一个主播
|
# 接着下一个主播
|
||||||
goBack(1)
|
goBack(1)
|
||||||
|
|
||||||
|
# 点击关注按钮
|
||||||
|
# followButton = AiUtils.getFollowButton(session).get(timeout=5)
|
||||||
|
# if followButton is not None:
|
||||||
|
# # LogManager.method_info("找到关注按钮了", "关注打招呼", udid)
|
||||||
|
# # followButton.click()
|
||||||
|
# x, y, w, h = followButton.bounds
|
||||||
|
# cx = int(x + w / 2)
|
||||||
|
# cy = int(y + h / 2)
|
||||||
|
# # 随机偏移 ±5 px(可自己改范围)
|
||||||
|
# cx += random.randint(-5, 5)
|
||||||
|
# cy += random.randint(-5, 5)
|
||||||
|
#
|
||||||
|
# session.click(cx, cy)
|
||||||
|
#
|
||||||
|
# else:
|
||||||
|
# LogManager.method_info("没找到关注按钮", "关注打招呼", udid)
|
||||||
|
# time.sleep(1)
|
||||||
|
# goBack(4)
|
||||||
|
# session.appium_settings({"snapshotMaxDepth": 15})
|
||||||
|
# continue
|
||||||
|
|
||||||
session.appium_settings({"snapshotMaxDepth": 15})
|
session.appium_settings({"snapshotMaxDepth": 15})
|
||||||
goBack(3)
|
goBack(3)
|
||||||
|
|
||||||
@@ -769,8 +729,7 @@ class ScriptManager():
|
|||||||
|
|
||||||
# 设置查找深度
|
# 设置查找深度
|
||||||
session.appium_settings({"snapshotMaxDepth": 15})
|
session.appium_settings({"snapshotMaxDepth": 15})
|
||||||
if self.interruptible_sleep(event, 2): # [ADD]
|
event.wait(timeout=2)
|
||||||
return
|
|
||||||
print("即将要回复消息")
|
print("即将要回复消息")
|
||||||
LogManager.method_info("即将要回复消息", "关注打招呼", udid)
|
LogManager.method_info("即将要回复消息", "关注打招呼", udid)
|
||||||
|
|
||||||
@@ -787,11 +746,9 @@ class ScriptManager():
|
|||||||
homeButton.click()
|
homeButton.click()
|
||||||
else:
|
else:
|
||||||
ControlUtils.closeTikTok(session, udid)
|
ControlUtils.closeTikTok(session, udid)
|
||||||
if self.interruptible_sleep(event, 2): # [ADD]
|
event.wait(timeout=2)
|
||||||
return
|
|
||||||
ControlUtils.openTikTok(session, udid)
|
ControlUtils.openTikTok(session, udid)
|
||||||
if self.interruptible_sleep(event, 3): # [ADD]
|
event.wait(timeout=3)
|
||||||
return
|
|
||||||
|
|
||||||
print("重新创建wda会话 防止wda会话失效")
|
print("重新创建wda会话 防止wda会话失效")
|
||||||
client = wda.USBClient(udid, ev.wdaFunctionPort)
|
client = wda.USBClient(udid, ev.wdaFunctionPort)
|
||||||
@@ -803,12 +760,12 @@ class ScriptManager():
|
|||||||
|
|
||||||
print("greetNewFollowers方法执行完毕")
|
print("greetNewFollowers方法执行完毕")
|
||||||
|
|
||||||
def safe_followAndGreetUnion(self, udid, needReply, needTranslate, event):
|
def safe_followAndGreetUnion(self, udid, needReply, event):
|
||||||
|
|
||||||
retries = 0
|
retries = 0
|
||||||
while not event.is_set():
|
while not event.is_set():
|
||||||
try:
|
try:
|
||||||
self.followAndGreetUnion(udid, needReply, needTranslate, event)
|
self.followAndGreetUnion(udid, needReply, event)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
retries += 1
|
retries += 1
|
||||||
@@ -821,7 +778,7 @@ class ScriptManager():
|
|||||||
LogManager.method_error("greetNewFollowers 重试次数耗尽,任务终止", "关注打招呼", udid)
|
LogManager.method_error("greetNewFollowers 重试次数耗尽,任务终止", "关注打招呼", udid)
|
||||||
|
|
||||||
# 关注打招呼以及回复主播消息(联盟号)
|
# 关注打招呼以及回复主播消息(联盟号)
|
||||||
def followAndGreetUnion(self, udid, needReply, needTranslate, event):
|
def followAndGreetUnion(self, udid, needReply, event):
|
||||||
|
|
||||||
client = wda.USBClient(udid, ev.wdaFunctionPort)
|
client = wda.USBClient(udid, ev.wdaFunctionPort)
|
||||||
session = client.session()
|
session = client.session()
|
||||||
@@ -910,6 +867,8 @@ class ScriptManager():
|
|||||||
event.wait(timeout=0.5)
|
event.wait(timeout=0.5)
|
||||||
else:
|
else:
|
||||||
print(f"找不到输入框")
|
print(f"找不到输入框")
|
||||||
|
raise Exception("找不到输入框")
|
||||||
|
|
||||||
|
|
||||||
input = session.xpath('//XCUIElementTypeSearchField')
|
input = session.xpath('//XCUIElementTypeSearchField')
|
||||||
if input.exists:
|
if input.exists:
|
||||||
@@ -965,16 +924,25 @@ class ScriptManager():
|
|||||||
print("找到输入框了, 准备发送一条打招呼消息")
|
print("找到输入框了, 准备发送一条打招呼消息")
|
||||||
LogManager.method_info("找到输入框了, 准备发送一条打招呼消息", "关注打招呼(联盟号)", udid)
|
LogManager.method_info("找到输入框了, 准备发送一条打招呼消息", "关注打招呼(联盟号)", udid)
|
||||||
|
|
||||||
print("打招呼的数据", ev.prologueList)
|
# 取出国家进行对应国家语言代码
|
||||||
LogManager.method_info(f"传递的打招呼的数据:{ev.prologueList}", "关注打招呼(联盟号)", udid)
|
anchorCountry_code = CountryLanguageMapper.get_language_code(anchorCountry)
|
||||||
|
print(anchorCountry_code)
|
||||||
|
|
||||||
|
print("存储的是:", ev.prologueList)
|
||||||
|
# 判断对应的语言代码是否在传入的字典中
|
||||||
|
if anchorCountry_code in ev.prologueList:
|
||||||
|
# 进行原本的进行传入
|
||||||
|
privateMessageList = ev.prologueList[anchorCountry_code]
|
||||||
|
needTranslate = False
|
||||||
|
|
||||||
|
else:
|
||||||
|
# 需要翻译
|
||||||
|
privateMessageList = ev.prologueList['yolo']
|
||||||
|
needTranslate = True # 使用yolo必须翻译
|
||||||
|
|
||||||
# 准备打招呼的文案
|
# 准备打招呼的文案
|
||||||
text = random.choice(ev.prologueList)
|
text = random.choice(privateMessageList)
|
||||||
# text = '你好'
|
|
||||||
|
|
||||||
LogManager.method_info(f"取出打招呼的数据,{text}, 判断是否需要翻译", "关注打招呼(联盟号)", udid)
|
|
||||||
|
|
||||||
isContainChniese = AiUtils.contains_chinese(text)
|
|
||||||
|
|
||||||
if needTranslate:
|
if needTranslate:
|
||||||
# 翻译成主播国家的语言
|
# 翻译成主播国家的语言
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user