diff --git a/.gitignore b/.gitignore
index 90e4b38..e69de29 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,127 +0,0 @@
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-*$py.class
-
-# Distribution / packaging
-.Python
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-.eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-*.egg-info/
-.installed.cfg
-*.egg
-out/
-
-# PyInstaller
-# Usually these files are written by a python script from a template
-# before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.nox/
-.coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-*.cover
-.hypothesis/
-.pytest_cache/
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-local_settings.py
-db.sqlite3
-db.sqlite3-journal
-
-# Flask stuff:
-instance/
-.webassets-cache
-
-# Scrapy stuff:
-.scrapy
-
-# Sphinx documentation
-docs/_build/
-docs/.doctrees/
-
-# PyBuilder
-target/
-
-# Jupyter Notebook
-.ipynb_checkpoints
-
-# IPython
-profile_default/
-ipython_config.py
-
-# pyenv
-.python-version
-
-# pipenv
-# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
-# However, in case of collaboration, if having platform-specific dependencies or dependencies
-# having no cross-platform support, pipenv may install dependencies that don't work, or not
-# install all needed dependencies.
-#Pipfile.lock
-
-# celery beat schedule file
-celerybeat-schedule
-
-# SageMath parsed files
-*.sage.py
-
-# Environments
-.env
-.venv
-env/
-venv/
-ENV/
-env.bak/
-venv.bak/
-
-# Spyder project settings
-.spyderproject
-.spyproject
-
-# Rope project settings
-.ropeproject
-
-# mkdocs documentation
-/site
-
-# mypy
-.mypy_cache/
-.dmypy.json
-dmypy.json
-
-# Pyre type checker
-.pyre/
-
-# pytype static type checker
-.pytype/
-
-# Cython debug symbols
-cython_debug/
-
-*.bat
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 54c1336..7ebb8ba 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -5,9 +5,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -65,9 +137,10 @@
"RunOnceActivity.git.unshallow": "true",
"SHARE_PROJECT_CONFIGURATION_FILES": "true",
"git-widget-placeholder": "main",
+ "ignore.virus.scanning.warn.message": "true",
"javascript.nodejs.core.library.configured.version": "20.17.0",
"javascript.nodejs.core.library.typings.version": "20.17.58",
- "last_opened_file_path": "F:/company code/AI item/20250820/iOSAI",
+ "last_opened_file_path": "C:/Users/zhangkai/Desktop/20250916部署的ios项目/iOSAI",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
@@ -189,6 +262,7 @@
+
@@ -200,8 +274,8 @@
-
+
@@ -210,7 +284,8 @@
-
+
+
@@ -276,6 +351,12 @@
+
+
+
+
+
+
@@ -347,8 +428,8 @@
-
+
@@ -356,7 +437,7 @@
-
-
+
+
\ No newline at end of file
diff --git a/Entity/DeviceModel.py b/Entity/DeviceModel.py
index cddf3c0..d4d595f 100644
--- a/Entity/DeviceModel.py
+++ b/Entity/DeviceModel.py
@@ -1,4 +1,4 @@
-from getpass import fallback_getpass
+# from getpass import fallback_getpass
# 设备模型
diff --git a/Module/DeviceInfo.py b/Module/DeviceInfo.py
index 4182e26..abf031f 100644
--- a/Module/DeviceInfo.py
+++ b/Module/DeviceInfo.py
@@ -30,12 +30,14 @@ class Deviceinfo(object):
self._lock = threading.Lock()
self._model_index: Dict[str, DeviceModel] = {} # udid -> model
- # ✅ 1. 失踪时间戳记录(替代原来的 miss_count)
- self._last_seen: Dict[str, float] = {}
- self._port_pool: List[int] = []
- self._port_in_use: set[int] = set()
+ self._miss_count: Dict[str, int] = {} # udid -> 连续未扫描到次数
+ self._port_pool: List[int] = [] # 端口回收池
+ self._port_in_use: set[int] = set() # 正在使用的端口
- # region iproxy 初始化(原逻辑不变)
+ # 🔥1. 启动 WDA 健康检查线程
+ # threading.Thread(target=self._wda_health_checker, daemon=True).start()
+
+ # region iproxy 初始化
try:
self.iproxy_path = self._iproxy_path()
self.iproxy_dir = self.iproxy_path.parent
@@ -83,11 +85,7 @@ class Deviceinfo(object):
LogManager.error(f"初始化 iproxy 失败:{e}")
# endregion
- # ------------------------------------------------------------------
- # ✅ 2. 主监听循环(已用“时间窗口+USB 层兜底”重写)
- # ------------------------------------------------------------------
def startDeviceListener(self):
- MISS_WINDOW = 5.0 # 5 秒连续失踪才判死刑
while True:
try:
lists = Usbmux().device_list()
@@ -97,23 +95,24 @@ class Deviceinfo(object):
continue
now_udids = {d.udid for d in lists if d.conn_type == ConnectionType.USB}
- # ✅ USB 层真断兜底
- usb_sn_set = self._usb_enumerate_sn()
- need_remove = None
+ # 1. 失踪登记 & 累加
+ need_remove = None # ← 新增:放锁外记录
with self._lock:
for udid in list(self._model_index.keys()):
if udid not in now_udids:
- last = self._last_seen.get(udid, time.time())
- if time.time() - last > MISS_WINDOW and udid not in usb_sn_set:
- need_remove = udid
+ self._miss_count[udid] = self._miss_count.get(udid, 0) + 1
+ if self._miss_count[udid] >= 3:
+ self._miss_count.pop(udid, None)
+ need_remove = udid # ← 只记录,不调用
else:
- self._last_seen[udid] = time.time()
+ self._miss_count.pop(udid, None)
+ # 🔓 锁已释放,再删设备(不会重入)
if need_remove:
self._remove_model(need_remove)
- # 新增设备(原逻辑不变)
+ # 2. 全新插入(只处理未在线且信任且未满)
for d in lists:
if d.conn_type != ConnectionType.USB:
continue
@@ -132,22 +131,14 @@ class Deviceinfo(object):
time.sleep(1)
- # ------------------------------------------------------------------
- # ✅ 3. USB 层枚举 SN(跨平台)
- # ------------------------------------------------------------------
- def _usb_enumerate_sn(self) -> set[str]:
- try:
- out = subprocess.check_output(["idevice_id", "-l"], text=True, timeout=3)
- return {line.strip() for line in out.splitlines() if line.strip()}
- except Exception:
- return set()
-
- # ===================== 以下代码与原文件完全一致 =====================
+ # 🔥2. WDA 健康检查
def _wda_health_checker(self):
while True:
time.sleep(1)
+ print(len(self.deviceModelList))
with self._lock:
- online = [m for m in self.deviceModelList if m.ready]
+ online = [m for m in self.deviceModelList if m.ready] # ← 只检查就绪的
+ print(len(online))
for model in online:
udid = model.deviceId
if not self._wda_ok(udid):
@@ -156,24 +147,32 @@ class Deviceinfo(object):
self._remove_model(udid)
self.connectDevice(udid)
+ # 🔥3. 真正做 health-check 的地方
def _wda_ok(self, udid: str) -> bool:
+ """返回 True 表示 WDA 活着,False 表示已死"""
try:
+ # 用 2 秒超时快速探测
c = wda.USBClient(udid, 8100)
+ # 下面这句就是“xctest launched but check failed” 的触发点
+ # 如果 status 里返回了 WebDriverAgent 运行信息就认为 OK
st = c.status()
if st.get("state") != "success":
return False
+ # 你也可以再苛刻一点,多做一次 /wda/healthcheck
+ # c.http.get("/wda/healthcheck")
return True
except Exception as e:
+ # 任何异常(连接拒绝、超时、json 解析失败)都认为已死
LogManager.error(f"WDA health-check 异常:{e}", udid)
return False
- # -------------------- 增删改查唯一入口(未改动) --------------------
+ # region ===================== 增删改查唯一入口(线程安全) =====================
def _has_model(self, udid: str) -> bool:
return udid in self._model_index
def _add_model(self, model: DeviceModel):
if model.deviceId in self._model_index:
- return
+ return # 防重复
model.ready = True
self.deviceModelList.append(model)
self._model_index[model.deviceId] = model
@@ -181,48 +180,60 @@ class Deviceinfo(object):
self.manager.send(model.toDict())
except Exception as e:
LogManager.warning(f"{model.deviceId} 发送上线事件失败:{e}")
- LogManager.method_info(f"{model.deviceId} 加入设备成功,当前在线数:{len(self.deviceModelList)}", method="device_count")
+ LogManager.method_info(f"{model.deviceId} 加入设备成功,当前在线数:{len(self.deviceModelList)}",method="device_count")
+
+ # 删除设备
def _remove_model(self, udid: str):
print(f"【删】进入删除方法 udid={udid}")
LogManager.method_info(f"【删】进入删除方法 udid={udid}", method="device_count")
+ # 1. 纯内存临界区——毫秒级
with self._lock:
print(f"【删】拿到锁 udid={udid}")
- LogManager.method_info(f"【删】拿到锁 udid={udid}", method="device_count")
+ LogManager.method_info(f"【删】拿到锁 udid={udid}",
+ method="device_count")
model = self._model_index.pop(udid, None)
if not model:
print(f"【删】模型已空,直接返回 udid={udid}")
- LogManager.method_info(f"【删】模型已空,直接返回 udid={udid}", method="device_count")
+ LogManager.method_info(f"【删】模型已空,直接返回 udid={udid}",method="device_count")
return
if model.deleting:
print(f"【删】正在删除中,幂等返回 udid={udid}")
LogManager.method_info(method="device_count", text=f"【删】正在删除中,幂等返回 udid={udid}")
return
model.deleting = True
+ # 标记维删除设备
model.type = 2
print(f"【删】标记 deleting=True udid={udid}")
- LogManager.method_info("【删】标记 deleting=True udid={udid}", "device_count")
+ LogManager.method_info("【删】标记 deleting=True udid={udid}","device_count")
+ # 过滤列表
before = len(self.deviceModelList)
self.deviceModelList = [m for m in self.deviceModelList if m.deviceId != udid]
after = len(self.deviceModelList)
print(f"【删】列表过滤 before={before} → after={after} udid={udid}")
- LogManager.method_info(f"【删】列表过滤 before={before} → after={after} udid={udid}", "device_count")
+ LogManager.method_info(f"【删】列表过滤 before={before} → after={after} udid={udid}","device_count")
+
+ # 端口
self._port_in_use.discard(model.screenPort)
self._port_pool.append(model.screenPort)
print(f"【删】回收端口 port={model.screenPort} udid={udid}")
LogManager.method_info(f"【删】回收端口 port={model.screenPort} udid={udid}", method="device_count")
+
+ # 进程
to_kill = [item for item in self.pidList if item.get("id") == udid]
self.pidList = [item for item in self.pidList if item.get("id") != udid]
print(f"【删】待杀进程数 count={len(to_kill)} udid={udid}")
LogManager.method_info(f"【删】待杀进程数 count={len(to_kill)} udid={udid}", method="device_count")
+ # 2. IO 区无锁
for idx, item in enumerate(to_kill, 1):
print(f"【删】杀进程 {idx}/{len(to_kill)} pid={item.get('target').pid} udid={udid}")
- LogManager.method_info(f"【删】杀进程 {idx}/{len(to_kill)} pid={item.get('target').pid} udid={udid}", method="device_count")
+ LogManager.method_error(f"【删】杀进程 {idx}/{len(to_kill)} pid={item.get('target').pid} udid={udid}", method="device_count")
self._terminate_proc(item.get("target"))
print(f"【删】进程清理完成 udid={udid}")
LogManager.method_info(f"【删】进程清理完成 udid={udid}", method="device_count")
+ # 3. 网络 IO
retry = 3
while retry:
try:
@@ -244,7 +255,7 @@ class Deviceinfo(object):
print(len(self.deviceModelList))
LogManager.method_info(f"当前剩余设备数量:{len(self.deviceModelList)}", method="device_count")
- # -------------------- 端口分配与回收(未改动) --------------------
+ # region ===================== 端口分配与回收 =====================
def _alloc_port(self) -> int:
if self._port_pool:
port = self._port_pool.pop()
@@ -258,17 +269,20 @@ class Deviceinfo(object):
if port in self._port_in_use:
self._port_in_use.remove(port)
self._port_pool.append(port)
+ # endregion
- # -------------------- 单台设备连接(未改动) --------------------
+ # region ===================== 单台设备连接 =====================
def connectDevice(self, udid: str):
if not self.is_device_trusted(udid):
LogManager.warning("设备未信任,跳过 WDA 启动", udid)
return
+
try:
d = wda.USBClient(udid, 8100)
except Exception as e:
LogManager.error(f"启动 WDA 失败: {e}", udid)
return
+
width, height, scale = 0, 0, 1.0
try:
size = d.window_size()
@@ -276,21 +290,26 @@ class Deviceinfo(object):
scale = d.scale
except Exception as e:
LogManager.warning(f"读取屏幕信息失败:{e}", udid)
+
port = self._alloc_port()
model = DeviceModel(udid, port, width, height, scale, type=1)
self._add_model(model)
+
try:
d.app_start(WdaAppBundleId)
d.home()
except Exception as e:
LogManager.warning(f"启动/切回桌面失败:{e}", udid)
+
time.sleep(2)
+
+ # 先清旧进程再启动新进程
self.pidList = [item for item in self.pidList if item.get("id") != udid]
target = self.relayDeviceScreenPort(udid, port)
if target:
self.pidList.append({"target": target, "id": udid})
- # -------------------- 工具方法(未改动) --------------------
+ # region ===================== 工具方法 =====================
def is_device_trusted(self, udid: str) -> bool:
try:
d = BaseDevice(udid)
@@ -300,16 +319,22 @@ class Deviceinfo(object):
return False
def relayDeviceScreenPort(self, udid: str, port: int) -> Optional[subprocess.Popen]:
+ """启动 iproxy 前:端口若仍被占用则先杀掉占用者,再启动"""
if not self._spawn_iproxy:
LogManager.error("iproxy 启动器未就绪", udid)
return None
+
+ # --- 新增:端口冲突检查 + 强制清理 ---
while self._port_in_use and self._is_port_open(port):
+ # 先查是哪个进程占用
pid = self._get_pid_by_port(port)
if pid and pid != os.getpid():
LogManager.warning(f"端口 {port} 仍被 PID {pid} 占用,尝试释放", udid)
self._kill_pid_gracefully(pid)
else:
break
+ # -------------------------------------
+
try:
p = self._spawn_iproxy(udid, port, 9100)
self._port_in_use.add(port)
@@ -319,12 +344,14 @@ class Deviceinfo(object):
LogManager.error(f"启动 iproxy 失败:{e}", udid)
return None
+ # ------------------- 新增三个小工具 -------------------
def _is_port_open(self, port: int) -> bool:
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
return s.connect_ex(("127.0.0.1", port)) == 0
def _get_pid_by_port(self, port: int) -> Optional[int]:
+ """跨平台根据端口号查 PID,失败返回 None"""
try:
if os.name == "nt":
cmd = ["netstat", "-ano", "-p", "tcp"]
@@ -340,6 +367,7 @@ class Deviceinfo(object):
return None
def _kill_pid_gracefully(self, pid: int):
+ """先 terminate 再 kill -9"""
try:
os.kill(pid, signal.SIGTERM)
time.sleep(1)
@@ -347,6 +375,7 @@ class Deviceinfo(object):
except Exception:
pass
+
def _terminate_proc(self, p: Optional[subprocess.Popen]):
if not p or p.poll() is not None:
return
diff --git a/Module/FlaskService.py b/Module/FlaskService.py
index 8ee6c8a..39dd30a 100644
--- a/Module/FlaskService.py
+++ b/Module/FlaskService.py
@@ -274,6 +274,7 @@ def stopScript():
@app.route('/passAnchorData', methods=['POST'])
def passAnchorData():
try:
+ LogManager.method_info("关注打招呼","关注打招呼")
data: Dict[str, Any] = request.get_json()
# 设备列表
idList = data.get("deviceList", [])
@@ -431,7 +432,6 @@ def aiConfig():
@app.route("/select_last_message", methods=['GET'])
def select_last_message():
data = JsonUtils.query_all_json_items()
-
return ResultData(data=data).toJson()
diff --git a/Module/log/acList.json b/Module/log/acList.json
new file mode 100644
index 0000000..e50ce15
--- /dev/null
+++ b/Module/log/acList.json
@@ -0,0 +1,22 @@
+[
+ {
+ "anchorId": "giulia.roma",
+ "country": "意大利"
+ },
+ {
+ "anchorId": "marcelo_brasil",
+ "country": "巴西"
+ },
+ {
+ "anchorId": "anna_krasnova",
+ "country": "俄罗斯"
+ },
+ {
+ "anchorId": "lee_jiwoo",
+ "country": "韩国"
+ },
+ {
+ "anchorId": "fatima_dxb",
+ "country": "阿联酋"
+ }
+]
\ No newline at end of file
diff --git a/SupportFiles/14.0.zip b/SupportFiles/14.0.zip
deleted file mode 100644
index bd7a539..0000000
Binary files a/SupportFiles/14.0.zip and /dev/null differ
diff --git a/SupportFiles/14.1.zip b/SupportFiles/14.1.zip
deleted file mode 100644
index ceb8328..0000000
Binary files a/SupportFiles/14.1.zip and /dev/null differ
diff --git a/SupportFiles/14.2.zip b/SupportFiles/14.2.zip
deleted file mode 100644
index 6190de1..0000000
Binary files a/SupportFiles/14.2.zip and /dev/null differ
diff --git a/SupportFiles/14.3.zip b/SupportFiles/14.3.zip
deleted file mode 100644
index 1203d70..0000000
Binary files a/SupportFiles/14.3.zip and /dev/null differ
diff --git a/SupportFiles/14.4.zip b/SupportFiles/14.4.zip
deleted file mode 100644
index a1025db..0000000
Binary files a/SupportFiles/14.4.zip and /dev/null differ
diff --git a/SupportFiles/14.5.zip b/SupportFiles/14.5.zip
deleted file mode 100644
index d91b781..0000000
Binary files a/SupportFiles/14.5.zip and /dev/null differ
diff --git a/SupportFiles/14.6.zip b/SupportFiles/14.6.zip
deleted file mode 100644
index 9ed5e41..0000000
Binary files a/SupportFiles/14.6.zip and /dev/null differ
diff --git a/SupportFiles/14.7.zip b/SupportFiles/14.7.zip
deleted file mode 100644
index 3a8f01a..0000000
Binary files a/SupportFiles/14.7.zip and /dev/null differ
diff --git a/SupportFiles/14.8.zip b/SupportFiles/14.8.zip
deleted file mode 100644
index ea9c3a6..0000000
Binary files a/SupportFiles/14.8.zip and /dev/null differ
diff --git a/SupportFiles/15.0.zip b/SupportFiles/15.0.zip
deleted file mode 100644
index a91df82..0000000
Binary files a/SupportFiles/15.0.zip and /dev/null differ
diff --git a/SupportFiles/15.1.zip b/SupportFiles/15.1.zip
deleted file mode 100644
index bd07873..0000000
Binary files a/SupportFiles/15.1.zip and /dev/null differ
diff --git a/SupportFiles/15.2.zip b/SupportFiles/15.2.zip
deleted file mode 100644
index 134c25b..0000000
Binary files a/SupportFiles/15.2.zip and /dev/null differ
diff --git a/SupportFiles/15.3.zip b/SupportFiles/15.3.zip
deleted file mode 100644
index 1a75175..0000000
Binary files a/SupportFiles/15.3.zip and /dev/null differ
diff --git a/SupportFiles/15.4.zip b/SupportFiles/15.4.zip
deleted file mode 100644
index b62fa2d..0000000
Binary files a/SupportFiles/15.4.zip and /dev/null differ
diff --git a/SupportFiles/15.5.zip b/SupportFiles/15.5.zip
deleted file mode 100644
index d84ff97..0000000
Binary files a/SupportFiles/15.5.zip and /dev/null differ
diff --git a/SupportFiles/15.6.zip b/SupportFiles/15.6.zip
deleted file mode 100644
index bc450f3..0000000
Binary files a/SupportFiles/15.6.zip and /dev/null differ
diff --git a/SupportFiles/15.7.zip b/SupportFiles/15.7.zip
deleted file mode 100644
index 13ab765..0000000
Binary files a/SupportFiles/15.7.zip and /dev/null differ
diff --git a/SupportFiles/15.8.zip b/SupportFiles/15.8.zip
deleted file mode 100644
index f413db6..0000000
Binary files a/SupportFiles/15.8.zip and /dev/null differ
diff --git a/SupportFiles/16.0.zip b/SupportFiles/16.0.zip
deleted file mode 100644
index ab9a02a..0000000
Binary files a/SupportFiles/16.0.zip and /dev/null differ
diff --git a/SupportFiles/16.1.zip b/SupportFiles/16.1.zip
deleted file mode 100644
index 07c9458..0000000
Binary files a/SupportFiles/16.1.zip and /dev/null differ
diff --git a/SupportFiles/16.2.zip b/SupportFiles/16.2.zip
deleted file mode 100644
index b5f95da..0000000
Binary files a/SupportFiles/16.2.zip and /dev/null differ
diff --git a/SupportFiles/16.3.zip b/SupportFiles/16.3.zip
deleted file mode 100644
index bc4e184..0000000
Binary files a/SupportFiles/16.3.zip and /dev/null differ
diff --git a/SupportFiles/16.4.zip b/SupportFiles/16.4.zip
deleted file mode 100644
index 8a785e3..0000000
Binary files a/SupportFiles/16.4.zip and /dev/null differ
diff --git a/SupportFiles/16.5.zip b/SupportFiles/16.5.zip
deleted file mode 100644
index fc2ec0b..0000000
Binary files a/SupportFiles/16.5.zip and /dev/null differ
diff --git a/SupportFiles/16.6.zip b/SupportFiles/16.6.zip
deleted file mode 100644
index 1e4924d..0000000
Binary files a/SupportFiles/16.6.zip and /dev/null differ
diff --git a/SupportFiles/16.7.zip b/SupportFiles/16.7.zip
deleted file mode 100644
index f2a36f8..0000000
Binary files a/SupportFiles/16.7.zip and /dev/null differ
diff --git a/Utils/ControlUtils.py b/Utils/ControlUtils.py
index 028983f..103fe2f 100644
--- a/Utils/ControlUtils.py
+++ b/Utils/ControlUtils.py
@@ -1,6 +1,8 @@
+import math
import random
import re
import time
+from typing import Tuple, List
import tidevice
import wda
@@ -70,14 +72,17 @@ class ControlUtils(object):
return True
elif session.xpath("//*[@name='nav_bar_start_back']").exists:
back = session.xpath("//*[@name='nav_bar_start_back']")
- back.click()
+ if back.exists:
+ back.click()
return True
elif session.xpath(
"//Window[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]").exists:
back = session.xpath(
"//Window[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]/Other[1]")
- back.click()
- return True
+
+ if back.exists:
+ back.click()
+ return True
else:
return False
except Exception as e:
@@ -129,6 +134,7 @@ class ControlUtils(object):
videoCell = session.xpath(
'(//XCUIElementTypeCollectionView//XCUIElementTypeCell[.//XCUIElementTypeImage[@name="profile_video"]])[1]')
+
tab = session.xpath(
'//XCUIElementTypeButton[@name="TTKProfileTabVideoButton_0" or contains(@label,"作品") or contains(@name,"作品")]'
).get(timeout=5) # 某些版本 tab.value 可能就是数量;或者 tab.label 类似 “作品 7”
@@ -204,33 +210,65 @@ class ControlUtils(object):
print(e)
return False
-
-
-
-
- # 随机滑动一点点距离
@classmethod
- def tap_mini_cluster(cls, center_x: int, center_y: int, session, points=5, duration_ms=60):
- try:
- response = session.http.post(
- "touchAndHold",
- data={
- "x": 100,
- "y": 100,+
- "duration": 0.1
- }
- )
- print(response)
- return response
- except Exception as e:
- print(e)
- return None
-
- # 检测五分钟前和当前的状态是否相同
- # @classmethod
- # def compareCurrentWithPreviousState(cls,xml):
-
+ def random_micro_swipe(
+ cls,
+ center_x: int,
+ center_y: int,
+ session,
+ points: int = 6,
+ duration_ms: int = 15,
+ ) -> None:
+ """
+ 在 (center_x, center_y) 附近做 20px 左右的不规则微滑动。
+ 使用 facebook-wda 的 session.swipe(x1, y1, x2, y2, duration) 接口。
+ """
+ # 1. 随机方向
+ angle = random.uniform(0, 2 * math.pi)
+ length = random.uniform(18, 22) # 20px 左右
+ end_x = center_x + length * math.cos(angle)
+ end_y = center_y + length * math.sin(angle)
+ # 2. 限制在 20px 圆内(防止超出)
+ def clamp_to_circle(x, y, cx, cy, r):
+ dx = x - cx
+ dy = y - cy
+ if dx * dx + dy * dy > r * r:
+ scale = r / math.hypot(dx, dy)
+ x = cx + dx * scale
+ y = cy + dy * scale
+ return int(round(x)), int(round(y))
+
+ end_x, end_y = clamp_to_circle(end_x, end_y, center_x, center_y, 20)
+
+ # 3. 加入轻微噪声,制造“不规则”曲线
+ noise = 3 # 最大偏移像素
+ mid_count = points - 2
+ mid_points: List[Tuple[int, int]] = []
+ for i in range(1, mid_count + 1):
+ t = i / (mid_count + 1)
+ # 线性插值 + 垂直方向噪声
+ x = center_x * (1 - t) + end_x * t
+ y = center_y * (1 - t) + end_y * t
+ perp_angle = angle + math.pi / 2 # 垂直方向
+ offset = random.uniform(-noise, noise)
+ x += offset * math.cos(perp_angle)
+ y += offset * math.sin(perp_angle)
+ x, y = clamp_to_circle(x, y, center_x, center_y, 20)
+ mid_points.append((int(round(x)), int(round(y))))
+
+ # 4. 构造完整轨迹
+ trajectory: List[Tuple[int, int]] = (
+ [(center_x, center_y)] + mid_points + [(end_x, end_y)]
+ )
+
+ # 5. 使用 facebook-wda 的 swipe 接口(逐段 swipe)
+ # 由于总时长太短,我们一次性 swipe 到终点,但用多点轨迹模拟
+ # facebook-wda 支持 swipe(x1, y1, x2, y2, duration)
+ # 我们直接用起点 -> 终点,duration 用总时长
+ print("开始微滑动")
+ session.swipe(center_x, center_y, end_x, end_y, duration_ms / 1000)
+ print("随机微滑动:", trajectory)
diff --git a/Utils/LogManager.py b/Utils/LogManager.py
index 67e8a24..6ace65c 100644
--- a/Utils/LogManager.py
+++ b/Utils/LogManager.py
@@ -1,217 +1,3 @@
-#
-# import datetime
-# import io
-# import logging
-# import os
-# import re
-# import sys
-# import shutil
-# import zipfile
-# from pathlib import Path
-# import requests
-#
-#
-# class LogManager:
-# # 运行根目录:打包后取 exe 目录;源码运行取项目目录
-# if getattr(sys, "frozen", False):
-# projectRoot = os.path.dirname(sys.executable)
-# else:
-# projectRoot = os.path.dirname(os.path.dirname(__file__))
-#
-# logDir = os.path.join(projectRoot, "log")
-# _loggers = {}
-# _method_loggers = {} # 新增:缓存“设备+方法”的 logger
-#
-# # ---------- 工具函数 ----------
-# @classmethod
-# def _safe_filename(cls, name: str, max_len: int = 80) -> str:
-# """
-# 将方法名/udid等转成安全文件名:
-# - 允许字母数字、点、下划线、连字符
-# - 允许常见 CJK 字符(中日韩)
-# - 其他非法字符替换为下划线
-# - 合并多余下划线,裁剪长度
-# """
-# if not name:
-# return "unknown"
-# name = str(name).strip()
-#
-# # 替换 Windows 非法字符和控制符
-# name = re.sub(r'[\\/:*?"<>|\r\n\t]+', '_', name)
-#
-# # 只保留 ① 英数._- ② CJK 统一表意文字、日文平/片假名、韩文音节
-# name = re.sub(rf'[^a-zA-Z0-9_.\-'
-# r'\u4e00-\u9fff' # 中
-# r'\u3040-\u30ff' # 日
-# r'\uac00-\ud7a3' # 韩
-# r']+', '_', name)
-# # 合并多余下划线,去两端空白与下划线
-# name = re.sub(r'_+', '_', name).strip(' _.')
-# # 避免空
-# name = name or "unknown"
-# # Windows 预留名避免(CON/PRN/AUX/NUL/COM1…)
-# if re.fullmatch(r'(?i)(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])', name):
-# name = f"_{name}"
-# # 限长
-# return name[:max_len] or "unknown"
-#
-# # ---------- 旧的:按级别写固定文件 ----------
-# @classmethod
-# def _setupLogger(cls, udid, name, logName, level=logging.INFO):
-# """创建或获取 logger,并绑定到设备目录下的固定文件(info.log / warning.log / error.log)"""
-# deviceLogDir = os.path.join(cls.logDir, cls._safe_filename(udid))
-# os.makedirs(deviceLogDir, exist_ok=True)
-# logFile = os.path.join(deviceLogDir, logName)
-#
-# logger_name = f"{udid}_{name}"
-# logger = logging.getLogger(logger_name)
-# logger.setLevel(level)
-#
-# # 避免重复添加 handler
-# if not any(
-# isinstance(h, logging.FileHandler) and h.baseFilename == os.path.abspath(logFile)
-# for h in logger.handlers
-# ):
-# fileHandler = logging.FileHandler(logFile, mode="a", encoding="utf-8")
-# formatter = logging.Formatter(
-# "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
-# datefmt="%Y-%m-%d %H:%M:%S"
-# )
-# fileHandler.setFormatter(formatter)
-# logger.addHandler(fileHandler)
-#
-# return logger
-#
-# @classmethod
-# def info(cls, text, udid="system"):
-# cls._setupLogger(udid, "infoLogger", "info.log", level=logging.INFO).info(f"[{udid}] {text}")
-#
-# @classmethod
-# def warning(cls, text, udid="system"):
-# cls._setupLogger(udid, "warningLogger", "warning.log", level=logging.WARNING).warning(f"[{udid}] {text}")
-#
-# @classmethod
-# def error(cls, text, udid="system"):
-# cls._setupLogger(udid, "errorLogger", "error.log", level=logging.ERROR).error(f"[{udid}] {text}")
-#
-# # ---------- 新增:按“设备+方法”分别写独立日志文件 ----------
-# @classmethod
-# def _setupMethodLogger(cls, udid: str, method: str, level=logging.INFO):
-# """
-# 为某设备的某个方法单独创建 logger:
-# log//.log
-# """
-# udid_key = cls._safe_filename(udid or "system")
-# method_key = cls._safe_filename(method or "general")
-# cache_key = (udid_key, method_key)
-#
-# # 命中缓存
-# if cache_key in cls._method_loggers:
-# return cls._method_loggers[cache_key]
-#
-# deviceLogDir = os.path.join(cls.logDir, udid_key)
-# os.makedirs(deviceLogDir, exist_ok=True)
-# logFile = os.path.join(deviceLogDir, f"{method_key}.log")
-#
-# logger_name = f"{udid_key}.{method_key}"
-# logger = logging.getLogger(logger_name)
-# logger.setLevel(level)
-# logger.propagate = False # 避免向根 logger 传播导致控制台重复打印
-#
-# # 避免重复添加 handler
-# if not any(
-# isinstance(h, logging.FileHandler) and h.baseFilename == os.path.abspath(logFile)
-# for h in logger.handlers
-# ):
-# fileHandler = logging.FileHandler(logFile, mode="a", encoding="utf-8")
-# formatter = logging.Formatter(
-# "%(asctime)s - %(levelname)s - %(name)s - %(message)s",
-# datefmt="%Y-%m-%d %H:%M:%S"
-# )
-# fileHandler.setFormatter(formatter)
-# logger.addHandler(fileHandler)
-#
-# cls._method_loggers[cache_key] = logger
-# return logger
-#
-# @classmethod
-# def method_info(cls, text, method, udid="system"):
-# """按设备+方法写 INFO 到 log//.log"""
-# cls._setupMethodLogger(udid, method, level=logging.INFO).info(f"[{udid}][{method}] {text}")
-#
-# @classmethod
-# def method_warning(cls, text, method, udid="system"):
-# cls._setupMethodLogger(udid, method, level=logging.WARNING).warning(f"[{udid}][{method}] {text}")
-#
-# @classmethod
-# def method_error(cls, text, method, udid="system"):
-# cls._setupMethodLogger(udid, method, level=logging.ERROR).error(f"[{udid}][{method}] {text}")
-#
-# # 清空日志
-# @classmethod
-# def clearLogs(cls):
-# """启动时清空 log 目录下所有文件"""
-#
-# # 关闭所有 handler
-# for name, logger in logging.Logger.manager.loggerDict.items():
-# if isinstance(logger, logging.Logger):
-# for handler in logger.handlers[:]:
-# try:
-# handler.close()
-# except Exception:
-# pass
-# logger.removeHandler(handler)
-#
-# # 删除 log 目录
-# log_path = Path(cls.logDir)
-# if log_path.exists():
-# for item in log_path.iterdir():
-# if item.is_file():
-# item.unlink()
-# elif item.is_dir():
-# shutil.rmtree(item)
-#
-# # 清缓存
-# cls._method_loggers.clear()
-#
-# @classmethod
-# def upload_all_logs(cls, server_url, token, userId, tenantId):
-# log_path = Path(cls.logDir)
-# if not log_path.exists():
-# return False
-#
-# timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
-# filename = f"{timestamp}_logs.zip"
-# print(filename)
-# zip_buf = io.BytesIO()
-# with zipfile.ZipFile(zip_buf, "w", compression=zipfile.ZIP_DEFLATED) as zf:
-# for p in log_path.rglob("*"):
-# if p.is_file():
-# arcname = str(p.relative_to(log_path))
-# zf.write(p, arcname=arcname)
-#
-# zip_bytes = zip_buf.getvalue()
-#
-# headers = {"vvtoken": token}
-# data = {"tenantId": tenantId, "userId": userId}
-#
-#
-# files = {
-# "file": (filename, io.BytesIO(zip_bytes), "application/zip")
-# }
-#
-# # 3) 上传
-# resp = requests.post(server_url, headers=headers, data=data, files=files)
-# if resp.json()['data']:
-# return True
-# return False
-
-
-
-
-
-
-
# -*- coding: utf-8 -*-
import datetime
import io
diff --git a/build-tidevice.bat b/build-tidevice.bat
index ca3b087..e69de29 100644
--- a/build-tidevice.bat
+++ b/build-tidevice.bat
@@ -1,9 +0,0 @@
-pyinstaller -F -n tidevice ^
- --hidden-import=tidevice._proto ^
- --hidden-import=tidevice._instruments ^
- --hidden-import=tidevice._usbmux ^
- --hidden-import=tidevice._wdaproxy ^
- --collect-all tidevice ^
- --noconsole ^
- --add-data="C:\Users\milk\AppData\Local\Programs\Python\Python312\Lib\site-packages\tidevice;tidevice" ^
- tidevice_entry.py
\ No newline at end of file
diff --git a/build.bat b/build.bat
index 6e939bf..8c15162 100644
--- a/build.bat
+++ b/build.bat
@@ -1,9 +1,9 @@
-python -m nuitka "Module/Main.py" ^
+python -m nuitka "C:\Users\zhangkai\Desktop\20250916部署的ios项目\iOSAI\Module\Main.py" ^
--standalone ^
--msvc=latest ^
--windows-console-mode=disable ^
--remove-output ^
---output-dir="F:/company code/AI item/20250820/iOSAI/out" ^
+--output-dir="C:\Users\zhangkai\Desktop\20250916部署的ios项目\iOSAI\out" ^
--output-filename=IOSAI ^
--include-package=Module,Utils,Entity,script ^
--include-module=flask ^
@@ -18,7 +18,7 @@ python -m nuitka "Module/Main.py" ^
--include-module=urllib3 ^
--include-module=certifi ^
--include-module=idna ^
---include-data-dir="F:/company code/AI item/20250820/iOSAI/SupportFiles=SupportFiles" ^
---include-data-dir="F:/company code/AI item/20250820/iOSAI/resources=resources" ^
---include-data-files="F:/company code/AI item/20250820/iOSAI/resources/iproxy/*=resources/iproxy/" ^
---windows-icon-from-ico="F:/company code/AI item/20250820/iOSAI/resources/icon.ico"
+--include-data-dir="C:\Users\zhangkai\Desktop\20250916部署的ios项目\iOSAI\SupportFiles=SupportFiles" ^
+--include-data-dir="C:\Users\zhangkai\Desktop\20250916部署的ios项目\iOSAI\resources=resources" ^
+--include-data-files="C:\Users\zhangkai\Desktop\20250916部署的ios项目\iOSAI\resources\iproxy\*=resources/iproxy/" ^
+--windows-icon-from-ico="C:\Users\zhangkai\Desktop\20250916部署的ios项目\iOSAI\resources\icon.ico"
diff --git a/resources/FlashLink.exe b/resources/FlashLink.exe
index 370f320..e69de29 100644
Binary files a/resources/FlashLink.exe and b/resources/FlashLink.exe differ
diff --git a/resources/add.png b/resources/add.png
index aa6b19e..e69de29 100644
Binary files a/resources/add.png and b/resources/add.png differ
diff --git a/resources/advertisement.png b/resources/advertisement.png
index 87de404..e69de29 100644
Binary files a/resources/advertisement.png and b/resources/advertisement.png differ
diff --git a/resources/back.png b/resources/back.png
index a679439..e69de29 100644
Binary files a/resources/back.png and b/resources/back.png differ
diff --git a/resources/comment.png b/resources/comment.png
index 7043f81..e69de29 100644
Binary files a/resources/comment.png and b/resources/comment.png differ
diff --git a/resources/icon.ico b/resources/icon.ico
index b40c169..e69de29 100644
Binary files a/resources/icon.ico and b/resources/icon.ico differ
diff --git a/resources/iproxy/idevice_id.exe b/resources/iproxy/idevice_id.exe
index 4d0da43..e69de29 100644
Binary files a/resources/iproxy/idevice_id.exe and b/resources/iproxy/idevice_id.exe differ
diff --git a/resources/iproxy/idevicebackup.exe b/resources/iproxy/idevicebackup.exe
index c2e0c9e..e69de29 100644
Binary files a/resources/iproxy/idevicebackup.exe and b/resources/iproxy/idevicebackup.exe differ
diff --git a/resources/iproxy/idevicebackup2.exe b/resources/iproxy/idevicebackup2.exe
index 93be5d2..e69de29 100644
Binary files a/resources/iproxy/idevicebackup2.exe and b/resources/iproxy/idevicebackup2.exe differ
diff --git a/resources/iproxy/idevicebtlogger.exe b/resources/iproxy/idevicebtlogger.exe
index dcf0451..e69de29 100644
Binary files a/resources/iproxy/idevicebtlogger.exe and b/resources/iproxy/idevicebtlogger.exe differ
diff --git a/resources/iproxy/idevicecrashreport.exe b/resources/iproxy/idevicecrashreport.exe
index a8d2eb4..e69de29 100644
Binary files a/resources/iproxy/idevicecrashreport.exe and b/resources/iproxy/idevicecrashreport.exe differ
diff --git a/resources/iproxy/idevicedate.exe b/resources/iproxy/idevicedate.exe
index 8333d7a..e69de29 100644
Binary files a/resources/iproxy/idevicedate.exe and b/resources/iproxy/idevicedate.exe differ
diff --git a/resources/iproxy/idevicedebug.exe b/resources/iproxy/idevicedebug.exe
index 2a8fb79..e69de29 100644
Binary files a/resources/iproxy/idevicedebug.exe and b/resources/iproxy/idevicedebug.exe differ
diff --git a/resources/iproxy/idevicedebugserverproxy.exe b/resources/iproxy/idevicedebugserverproxy.exe
index f284c71..e69de29 100644
Binary files a/resources/iproxy/idevicedebugserverproxy.exe and b/resources/iproxy/idevicedebugserverproxy.exe differ
diff --git a/resources/iproxy/idevicedevmodectl.exe b/resources/iproxy/idevicedevmodectl.exe
index b02a9a6..e69de29 100644
Binary files a/resources/iproxy/idevicedevmodectl.exe and b/resources/iproxy/idevicedevmodectl.exe differ
diff --git a/resources/iproxy/idevicediagnostics.exe b/resources/iproxy/idevicediagnostics.exe
index 155e132..e69de29 100644
Binary files a/resources/iproxy/idevicediagnostics.exe and b/resources/iproxy/idevicediagnostics.exe differ
diff --git a/resources/iproxy/ideviceenterrecovery.exe b/resources/iproxy/ideviceenterrecovery.exe
index 70d5d5e..e69de29 100644
Binary files a/resources/iproxy/ideviceenterrecovery.exe and b/resources/iproxy/ideviceenterrecovery.exe differ
diff --git a/resources/iproxy/ideviceimagemounter.exe b/resources/iproxy/ideviceimagemounter.exe
index 5a650c9..e69de29 100644
Binary files a/resources/iproxy/ideviceimagemounter.exe and b/resources/iproxy/ideviceimagemounter.exe differ
diff --git a/resources/iproxy/ideviceinfo.exe b/resources/iproxy/ideviceinfo.exe
index 55a4eed..e69de29 100644
Binary files a/resources/iproxy/ideviceinfo.exe and b/resources/iproxy/ideviceinfo.exe differ
diff --git a/resources/iproxy/idevicename.exe b/resources/iproxy/idevicename.exe
index 8abae73..e69de29 100644
Binary files a/resources/iproxy/idevicename.exe and b/resources/iproxy/idevicename.exe differ
diff --git a/resources/iproxy/idevicenotificationproxy.exe b/resources/iproxy/idevicenotificationproxy.exe
index 5303f2c..e69de29 100644
Binary files a/resources/iproxy/idevicenotificationproxy.exe and b/resources/iproxy/idevicenotificationproxy.exe differ
diff --git a/resources/iproxy/idevicepair.exe b/resources/iproxy/idevicepair.exe
index 150d8ba..e69de29 100644
Binary files a/resources/iproxy/idevicepair.exe and b/resources/iproxy/idevicepair.exe differ
diff --git a/resources/iproxy/ideviceprovision.exe b/resources/iproxy/ideviceprovision.exe
index 1805e12..e69de29 100644
Binary files a/resources/iproxy/ideviceprovision.exe and b/resources/iproxy/ideviceprovision.exe differ
diff --git a/resources/iproxy/idevicerestore.exe b/resources/iproxy/idevicerestore.exe
index 96974ff..e69de29 100644
Binary files a/resources/iproxy/idevicerestore.exe and b/resources/iproxy/idevicerestore.exe differ
diff --git a/resources/iproxy/idevicescreenshot.exe b/resources/iproxy/idevicescreenshot.exe
index c67f274..e69de29 100644
Binary files a/resources/iproxy/idevicescreenshot.exe and b/resources/iproxy/idevicescreenshot.exe differ
diff --git a/resources/iproxy/idevicesetlocation.exe b/resources/iproxy/idevicesetlocation.exe
index 21ec82a..e69de29 100644
Binary files a/resources/iproxy/idevicesetlocation.exe and b/resources/iproxy/idevicesetlocation.exe differ
diff --git a/resources/iproxy/idevicesyslog.exe b/resources/iproxy/idevicesyslog.exe
index 2a81ef0..e69de29 100644
Binary files a/resources/iproxy/idevicesyslog.exe and b/resources/iproxy/idevicesyslog.exe differ
diff --git a/resources/iproxy/inetcat.exe b/resources/iproxy/inetcat.exe
index d0fa729..e69de29 100644
Binary files a/resources/iproxy/inetcat.exe and b/resources/iproxy/inetcat.exe differ
diff --git a/resources/iproxy/irecovery.exe b/resources/iproxy/irecovery.exe
index 8982a6a..e69de29 100644
Binary files a/resources/iproxy/irecovery.exe and b/resources/iproxy/irecovery.exe differ
diff --git a/resources/iproxy/libcrypto-3.dll b/resources/iproxy/libcrypto-3.dll
index dce1596..e69de29 100644
Binary files a/resources/iproxy/libcrypto-3.dll and b/resources/iproxy/libcrypto-3.dll differ
diff --git a/resources/iproxy/libcurl.dll b/resources/iproxy/libcurl.dll
index 773a027..e69de29 100644
Binary files a/resources/iproxy/libcurl.dll and b/resources/iproxy/libcurl.dll differ
diff --git a/resources/iproxy/libssl-3.dll b/resources/iproxy/libssl-3.dll
index b9667cd..e69de29 100644
Binary files a/resources/iproxy/libssl-3.dll and b/resources/iproxy/libssl-3.dll differ
diff --git a/resources/iproxy/plistutil.exe b/resources/iproxy/plistutil.exe
index d3e5a38..e69de29 100644
Binary files a/resources/iproxy/plistutil.exe and b/resources/iproxy/plistutil.exe differ
diff --git a/resources/iproxy/readline.dll b/resources/iproxy/readline.dll
index 3ea56da..e69de29 100644
Binary files a/resources/iproxy/readline.dll and b/resources/iproxy/readline.dll differ
diff --git a/resources/iproxy/tidevice.exe b/resources/iproxy/tidevice.exe
index c40b70f..e69de29 100644
Binary files a/resources/iproxy/tidevice.exe and b/resources/iproxy/tidevice.exe differ
diff --git a/resources/like.png b/resources/like.png
index 46c6c25..e69de29 100644
Binary files a/resources/like.png and b/resources/like.png differ
diff --git a/resources/search.png b/resources/search.png
index 8aef0b5..e69de29 100644
Binary files a/resources/search.png and b/resources/search.png differ
diff --git a/script/ScriptManager.py b/script/ScriptManager.py
index b48a3d4..4a98825 100644
--- a/script/ScriptManager.py
+++ b/script/ScriptManager.py
@@ -361,10 +361,12 @@ class ScriptManager():
else:
print(f"找不到输入框")
- input.clear_text()
- time.sleep(1)
- # 输入主播id
- input.set_text(f"{aid or '暂无数据'}\n")
+ input = session.xpath('//XCUIElementTypeSearchField')
+ if input.exists:
+ input.clear_text()
+ time.sleep(1)
+ # 输入主播id
+ input.set_text(f"{aid or '暂无数据'}\n")
# 定位 "关注" 按钮 通过关注按钮的位置点击主播首页
@@ -449,11 +451,11 @@ class ScriptManager():
time.sleep(2)
msgButton = AiUtils.getSendMesageButton(session)
time.sleep(2)
- if msgButton is not None:
- LogManager.method_info("找到发消息按钮了", "关注打招呼", udid)
- print("找到发消息按钮了")
+ if msgButton.exists:
# 进入聊天页面
msgButton.click()
+ LogManager.method_info("找到发消息按钮了", "关注打招呼", udid)
+ print("找到发消息按钮了")
else:
LogManager.method_info("没有识别出发消息按钮", "关注打招呼", udid)
print("没有识别出发消息按钮")
@@ -494,10 +496,12 @@ class ScriptManager():
LogManager.method_info(f"即将发送的私信内容:{msg}", "关注打招呼", udid)
# 准备发送一条信息
- chatInput.click()
- time.sleep(2)
- # 发送消息
- chatInput.set_text(f"{msg or '暂无数据'}\n")
+ chatInput = session.xpath("//TextView")
+ if chatInput.exists:
+ chatInput.click()
+ time.sleep(2)
+ # 发送消息
+ chatInput.set_text(f"{msg or '暂无数据'}\n")
# input.set_text(f"{aid or '暂无数据'}\n")
diff --git a/tidevice_entry.py b/tidevice_entry.py
index 2cc9028..e69de29 100644
--- a/tidevice_entry.py
+++ b/tidevice_entry.py
@@ -1,18 +0,0 @@
-# from tidevice.__main__ import main
-# if __name__ == '__main__':
-# main()
-
-
-# tidevice_entry.py
-import sys, traceback, os
-from tidevice.__main__ import main
-
-if __name__ == "__main__":
- try:
- main()
- except Exception:
- # 把 traceback 写到日志文件,但**不输出到控制台**
- with open(os.path.expanduser("~/tidevice_crash.log"), "a", encoding="utf-8") as f:
- traceback.print_exc(file=f)
- # 静默退出,**返回码 1**(父进程只认 returncode)
- sys.exit(1)
\ No newline at end of file