临时提交

This commit is contained in:
zw
2025-08-15 20:04:59 +08:00
parent 6332bda929
commit 690b17ec58
66 changed files with 5075 additions and 156 deletions

View File

@@ -1,111 +1,199 @@
import subprocess # -*- coding: utf-8 -*-
import threading import os
import sys
import time import time
import json
import wda import wda
import threading
import subprocess
from pathlib import Path
from typing import List, Dict, Optional
from tidevice import Usbmux from tidevice import Usbmux
from Entity.DeviceModel import DeviceModel from Entity.DeviceModel import DeviceModel
from Entity.Variables import WdaAppBundleId from Entity.Variables import WdaAppBundleId
from Module.FlaskSubprocessManager import FlaskSubprocessManager from Module.FlaskSubprocessManager import FlaskSubprocessManager
from Utils.LogManager import LogManager from Utils.LogManager import LogManager
threadLock = threading.Lock()
class Deviceinfo(object): class Deviceinfo(object):
def __init__(self): def __init__(self):
self.deviceIndex = 0 self.deviceIndex = 0
# 投屏端口 # 投屏端口(本地映射端口起始值,会递增)
self.screenProxy = 9110 self.screenProxy = 9110
# 存放pid的数组
self.pidList = []
# 设备列表
self.deviceArray = []
# 获取到县城管理类
self.manager = FlaskSubprocessManager.get_instance()
# 给前端的设备模型数组
self.deviceModelList = []
# 监听设备连接 # 记录 iproxy Popen 进程:[{ "id": udid, "target": Popen }, ...]
self.pidList: List[Dict] = []
# 当前已连接的设备tidevice 的 Device 对象列表)
self.deviceArray: List = []
# 子进程通信(向前端发送设备信息)
self.manager = FlaskSubprocessManager.get_instance()
# 已发给前端的设备模型列表(用于拔出时发 type=2
self.deviceModelList: List[DeviceModel] = []
# ----------------------------
# 监听设备连接(死循环,内部捕获异常)
# ----------------------------
def startDeviceListener(self): def startDeviceListener(self):
LogManager.info("Device Listener started", "listener")
while True: while True:
lists = Usbmux().device_list() try:
# 添加设备逻辑 lists = Usbmux().device_list()
except Exception as e:
# 另一台电脑常见usbmuxd 连接失败(未安装 iTunes/Apple Mobile Device Support
LogManager.warning(f"usbmuxd 连接失败: {e}。请确认已安装 iTunes/Apple Mobile Device Support并在手机上“信任此电脑”", "listener")
time.sleep(2)
continue
# 新接入设备
for device in lists: for device in lists:
if device not in self.deviceArray: if device not in self.deviceArray:
self.screenProxy += 1 self.screenProxy += 1
self.connectDevice(device.udid) try:
self.deviceArray.append(device) self.connectDevice(device.udid)
self.deviceArray.append(device)
except Exception as e:
LogManager.error(f"连接设备失败 {device.udid}: {e}", device.udid)
# 处理拔出设备的逻辑 # 拔出设备处理
def removeDevice(): self._removeDisconnected(lists)
set1 = set(self.deviceArray)
set2 = set(lists)
difference = set1 - set2
differenceList = list(difference)
for i in differenceList:
for j in self.deviceArray:
# 判断是否为差异设备
if i.udid == j.udid:
# 从设备模型中删除数据
for a in self.deviceModelList:
if i.udid == a.deviceId:
a.type = 2
# 发送数据
self.manager.send(a.toDict())
self.deviceModelList.remove(a)
for k in self.pidList:
# 干掉端口短发进程
if j.udid == k["id"]:
target = k["target"]
target.kill()
self.pidList.remove(k)
# 删除已经拔出的设备
self.deviceArray.remove(j)
removeDevice()
time.sleep(1) time.sleep(1)
# 连接设备 # ----------------------------
def connectDevice(self, identifier): # 连接单台设备:启动 WDA、读取屏参、通知前端、映射投屏端口
# ----------------------------
def connectDevice(self, identifier: str):
# 1) 连接 WDAUSBClient -> 设备 8100
try: try:
d = wda.USBClient(identifier, 8100) d = wda.USBClient(identifier, 8100)
LogManager.info("启动wda成功", identifier) LogManager.info("启动 WDA 成功", identifier)
size = d.window_size()
width = size.width
height = size.height
scale = d.scale
# 创建模型
model = DeviceModel(identifier, self.screenProxy, width, height, scale, type=1)
self.deviceModelList.append(model)
# 发送数据
self.manager.send(model.toDict())
except Exception as e: except Exception as e:
LogManager.error("启动wda失败请检查wda是否正常", identifier) LogManager.error(f"启动 WDA 失败请检查手机是否已信任、WDA 是否正常。错误: {e}", identifier)
return return # 不抛出到外层,保持监听循环健壮
d.app_start(WdaAppBundleId) # 2) 读取屏幕信息(失败不影响主流程)
d.home() width, height, scale = 0, 0, 1.0
time.sleep(2)
target = self.relayDeviceScreenPort(identifier)
self.pidList.append({
"target": target,
"id": identifier
})
# 转发设备端口
def relayDeviceScreenPort(self, udid):
try: try:
command = f"iproxy.exe -u {udid} {self.screenProxy} 9100" size = d.window_size()
# 创建一个没有窗口的进程 width, height = size.width, size.height
startupinfo = subprocess.STARTUPINFO() scale = d.scale
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = 0
r = subprocess.Popen(command, shell=True, startupinfo=startupinfo)
return r
except Exception as e: except Exception as e:
print(e) LogManager.warning(f"读取屏幕信息失败:{e}", identifier)
return 0
# 3) 组装模型并发送给前端
model = DeviceModel(identifier, self.screenProxy, width, height, scale, type=1)
self.deviceModelList.append(model)
try:
self.manager.send(model.toDict())
except Exception as e:
LogManager.warning(f"向前端发送设备模型失败:{e}", identifier)
# 4) 可选:启动你的 app 并回到桌面
try:
d.app_start(WdaAppBundleId)
d.home()
except Exception as e:
LogManager.warning(f"启动/切回桌面失败:{e}", identifier)
time.sleep(2)
# 5) 本地端口 -> 设备端口 的映射(投屏:本地 self.screenProxy -> 设备 9100
target = self.relayDeviceScreenPort(identifier)
self.pidList.append({"target": target, "id": identifier})
# ----------------------------
# 处理拔出设备:发通知、关掉 iproxy、移出状态
# ----------------------------
def _removeDisconnected(self, current_list):
set1 = set(self.deviceArray)
set2 = set(current_list)
difference = list(set1 - set2) # 在旧集合中但不在新集合中 -> 已拔出
for i in difference:
udid = i.udid
# 1) 通知前端type = 2
for a in list(self.deviceModelList):
if udid == a.deviceId:
a.type = 2
try:
self.manager.send(a.toDict())
except Exception as e:
LogManager.warning(f"发送下线事件失败:{e}", udid)
self.deviceModelList.remove(a)
# 2) 关掉对应的 iproxy
for k in list(self.pidList):
if udid == k["id"]:
target = k.get("target")
try:
if target and target.poll() is None:
target.kill()
except Exception:
pass
self.pidList.remove(k)
# 3) 从已连接集合中移除
try:
self.deviceArray.remove(i)
except Exception:
pass
# ----------------------------
# 路径:打包/源码都能找到根目录、iproxy 目录和 iproxy 可执行文件
# ----------------------------
def _base_dir(self) -> Path:
"""
打包后:返回 exe 所在目录;
源码运行返回项目根目录Module 的上一级)
"""
if getattr(sys, "frozen", False):
return Path(sys.executable).resolve().parent
return Path(__file__).resolve().parents[1] # iOSAI/ 作为根
def _iproxy_dir(self) -> Path:
"""返回打包后的 iproxy 目录(你现在放在 Module/iproxy/"""
return self._base_dir() / "Module" / "iproxy"
def _iproxy_path(self) -> Path:
"""返回 iproxy 可执行文件路径Windows 为 iproxy.exe"""
exe_name = "iproxy.exe" if os.name == "nt" else "iproxy"
return self._iproxy_dir() / exe_name
# ----------------------------
# 端口映射:启动 iproxy设置 cwd 和 PATH隐藏窗口
# ----------------------------
def relayDeviceScreenPort(self, udid: str) -> Optional[subprocess.Popen]:
try:
iproxy = self._iproxy_path()
iproxy_dir = self._iproxy_dir()
if not iproxy.exists():
raise FileNotFoundError(f"iproxy not found: {iproxy}")
# 继承环境并把 iproxy 目录加入 PATH方便 DLL 解析
env = os.environ.copy()
env["PATH"] = str(iproxy_dir) + os.pathsep + env.get("PATH", "")
# Windows 隐藏子进程窗口
CREATE_NO_WINDOW = 0x08000000 if os.name == "nt" else 0
p = subprocess.Popen(
[str(iproxy), "-u", udid, str(self.screenProxy), "9100"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
creationflags=CREATE_NO_WINDOW,
cwd=str(iproxy_dir), # 关键:工作目录设为 iproxy 所在目录
env=env, # 关键:把 iproxy_dir 注入 PATH
text=True, # 你后面如果要读 stdout/stderr 的话更方便
encoding="utf-8",
bufsize=1
)
LogManager.info(f"启动 iproxy 成功,本地 {self.screenProxy} -> 设备 9100", udid)
return p
except Exception as e:
LogManager.error(f"启动 iproxy 失败:{e}", udid)
return None

View File

@@ -137,10 +137,8 @@ def tapAction():
client = wda.USBClient(udid) client = wda.USBClient(udid)
session = client.session() session = client.session()
session.appium_settings({"snapshotMaxDepth": 0}) session.appium_settings({"snapshotMaxDepth": 0})
x = body.get("x") x = body.get("x")
y = body.get("y") y = body.get("y")
session.tap(x, y) session.tap(x, y)
return ResultData(data="").toJson() return ResultData(data="").toJson()

View File

@@ -1,10 +1,12 @@
import subprocess import subprocess
import sys
import threading import threading
import atexit import atexit
import json import json
import os import os
import socket import socket
import time import time
from pathlib import Path
from typing import Optional, Union, Dict, List from typing import Optional, Union, Dict, List
class FlaskSubprocessManager: class FlaskSubprocessManager:
@@ -20,38 +22,59 @@ class FlaskSubprocessManager:
def _init_manager(self): def _init_manager(self):
self.process: Optional[subprocess.Popen] = None self.process: Optional[subprocess.Popen] = None
self.comm_port = self._find_available_port() self.comm_port = 34567
self._stop_event = threading.Event() self._stop_event = threading.Event()
atexit.register(self.stop) atexit.register(self.stop)
def _find_available_port(self): # 可以把 _find_available_port 留着备用,但 start 前先校验端口是否被占用
"""动态获取可用端口""" def _is_port_busy(self, port: int) -> bool:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(('0.0.0.0', 0)) s.settimeout(0.2)
return s.getsockname()[1] return s.connect_ex(("127.0.0.1", port)) == 0
# 启动flask
def start(self): def start(self):
"""启动子进程Windows兼容方案""" """启动 Flask 子进程(兼容打包后的 exe 和源码运行"""
with self._lock: with self._lock:
if self.process is not None: if self.process is not None:
raise RuntimeError("子进程已在运行中!") raise RuntimeError("子进程已在运行中!")
# 通过环境变量传递通信端口
env = os.environ.copy() env = os.environ.copy()
env['FLASK_COMM_PORT'] = str(self.comm_port) env["FLASK_COMM_PORT"] = str(self.comm_port)
# —— 解析打包 exe 的稳健写法 ——
exe_path = Path(sys.executable).resolve()
if exe_path.name.lower() in ("python.exe", "pythonw.exe"):
# Nuitka 某些场景里 sys.executable 可能指向 dist\python.exe并不存在
exe_path = Path(sys.argv[0]).resolve()
is_frozen = exe_path.suffix.lower() == ".exe" and exe_path.exists()
if is_frozen:
# 打包后的 exe用当前 exe 自举
cmd = [str(exe_path), "--role=flask"]
cwd = str(exe_path.parent)
else:
# 源码运行:模块方式更稳
cmd = [sys.executable, "-m", "Module.Main", "--role=flask"]
cwd = str(Path(__file__).resolve().parent) # Module 目录
print(f"[DEBUG] spawn: {cmd} (cwd={cwd}) exists(exe)={os.path.exists(cmd[0])}")
self.process = subprocess.Popen( self.process = subprocess.Popen(
['python', 'Flask/FlaskService.py'], # 启动一个子进程 FlaskService.py cmd,
stdin=subprocess.PIPE, # 标准输入流,用于向子进程发送数据 stdin=subprocess.PIPE,
stdout=subprocess.PIPE, # 标准输出流,用于接收子进程的输出 stdout=subprocess.PIPE,
stderr=subprocess.PIPE, # 标准错误流,用于接收子进程的错误信息 stderr=subprocess.PIPE,
text=True, # 以文本模式打开流,否则以二进制模式打开 text=True,
bufsize=1, # 缓冲区大小设置为 1表示行缓冲 encoding="utf-8",
encoding='utf-8', # 指定编码为 UTF-8确保控制台输出不会报错 errors="replace", # 新增:遇到非 UTF-8 字节用 <20> 代替,避免崩溃
env=env # 指定子进程的环境变量 bufsize=1,
env=env,
cwd=cwd,
) )
print(f"Flask子进程启动 (PID: {self.process.pid}, 通信端口: {self.comm_port})") print(f"Flask子进程启动 (PID: {self.process.pid}, 端口: {self.comm_port})")
# 将日志通过主进程输出
def print_output(stream, stream_name): def print_output(stream, stream_name):
while True: while True:
line = stream.readline() line = stream.readline()
@@ -59,7 +82,6 @@ class FlaskSubprocessManager:
break break
print(f"{stream_name}: {line.strip()}") print(f"{stream_name}: {line.strip()}")
# 启动两个线程分别处理 stdout 和 stderr
threading.Thread(target=print_output, args=(self.process.stdout, "STDOUT"), daemon=True).start() threading.Thread(target=print_output, args=(self.process.stdout, "STDOUT"), daemon=True).start()
threading.Thread(target=print_output, args=(self.process.stderr, "STDERR"), daemon=True).start() threading.Thread(target=print_output, args=(self.process.stderr, "STDERR"), daemon=True).start()

View File

@@ -1,17 +1,66 @@
import os
import sys
import time import time
from pathlib import Path
from Module.DeviceInfo import Deviceinfo from Module.DeviceInfo import Deviceinfo
from Module.FlaskSubprocessManager import FlaskSubprocessManager from Module.FlaskSubprocessManager import FlaskSubprocessManager
from Utils.LogManager import LogManager from Utils.LogManager import LogManager
# 项目入口 # 确定 exe 或 py 文件所在目录
if __name__ == "__main__": BASE = Path(getattr(sys, 'frozen', False) and sys.executable or __file__).resolve().parent
# 清空日志 LOG_DIR = BASE / "log"
LogManager.clearLogs() LOG_DIR.mkdir(exist_ok=True) # 确保 log 目录存在
time.sleep(1)
print(f"日志目录: {LOG_DIR}")
def _run_flask_role():
from Module import FlaskService
port = int(os.getenv("FLASK_COMM_PORT", "34567")) # 固定端口的兜底仍是 34567
app_factory = getattr(FlaskService, "create_app", None)
app = app_factory() if callable(app_factory) else FlaskService.app
app.run(host="0.0.0.0", port=port, debug=False, use_reloader=False)
if "--role=flask" in sys.argv:
_run_flask_role()
sys.exit(0)
# 项目入口
# ... 省略前面的 import 和函数 ...
if __name__ == "__main__":
# 清空日志等
LogManager.clearLogs()
# 启动 Flask 子进程
manager = FlaskSubprocessManager.get_instance() manager = FlaskSubprocessManager.get_instance()
manager.start() manager.start()
info = Deviceinfo() # 设备监听(即使失败/很快返回,也不会导致主进程退出)
info.startDeviceListener() try:
info = Deviceinfo()
info.startDeviceListener()
except Exception as e:
print("[WARN] Device listener not running:", e)
# === 保活:阻塞主线程,直到收到 Ctrl+C/关闭 ===
import threading, time, signal
stop = threading.Event()
def _handle(_sig, _frm):
stop.set()
# Windows 上 SIGINT/SIGTERM 都可以拦到
try:
signal.signal(signal.SIGINT, _handle)
signal.signal(signal.SIGTERM, _handle)
except Exception:
pass # 某些环境可能不支持,忽略
try:
while not stop.is_set():
time.sleep(1)
finally:
# 进程退出前记得把子进程关掉
manager.stop()

BIN
Module/iproxy/bz2.dll Normal file

Binary file not shown.

BIN
Module/iproxy/getopt.dll Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Module/iproxy/inetcat.exe Normal file

Binary file not shown.

BIN
Module/iproxy/irecovery.exe Normal file

Binary file not shown.

Binary file not shown.

BIN
Module/iproxy/libcurl.dll Normal file

Binary file not shown.

BIN
Module/iproxy/libssl-3.dll Normal file

Binary file not shown.

BIN
Module/iproxy/plistutil.exe Normal file

Binary file not shown.

BIN
Module/iproxy/readline.dll Normal file

Binary file not shown.

BIN
Module/iproxy/zip.dll Normal file

Binary file not shown.

BIN
Module/iproxy/zlib1.dll Normal file

Binary file not shown.

View File

@@ -1,78 +1,76 @@
import logging import logging
import os import os
import sys
import shutil import shutil
class LogManager: class LogManager:
# 取项目目录 # 运行根目录:打包后取 exe 目录;源码运行取项目目录
projectRoot = os.path.dirname(os.path.dirname(__file__)) if getattr(sys, "frozen", False):
logDir = os.path.join(projectRoot, "log") projectRoot = os.path.dirname(sys.executable)
else:
projectRoot = os.path.dirname(os.path.dirname(__file__))
# 类变量,存储日志记录器 logDir = os.path.join(projectRoot, "log")
_loggers = {} _loggers = {}
@classmethod @classmethod
def _setupLogger(cls, udid, name, logName, level=logging.INFO): def _setupLogger(cls, udid, name, logName, level=logging.INFO):
"""设置日志记录器""" """创建或获取 logger并绑定到文件"""
deviceLogDir = os.path.join(cls.logDir, udid) deviceLogDir = os.path.join(cls.logDir, udid)
os.makedirs(deviceLogDir, exist_ok=True) # 确保日志目录存在 os.makedirs(deviceLogDir, exist_ok=True)
logFile = os.path.join(deviceLogDir, logName) logFile = os.path.join(deviceLogDir, logName)
logger = logging.getLogger(f"{udid}_{name}")
logger_name = f"{udid}_{name}"
logger = logging.getLogger(logger_name)
logger.setLevel(level) logger.setLevel(level)
fileHandler = logging.FileHandler(logFile, mode="a", encoding="utf-8")
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s", # 避免重复添加 handler
datefmt="%Y-%m-%d %H:%M:%S") if not any(
fileHandler.setFormatter(formatter) isinstance(h, logging.FileHandler) and h.baseFilename == os.path.abspath(logFile)
logger.addHandler(fileHandler) 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 return logger
@classmethod
def _getLogger(cls, udid, name, logName, level=logging.INFO):
"""获取或初始化日志记录器"""
if udid not in cls._loggers:
cls._loggers[udid] = {}
if name not in cls._loggers[udid]:
cls._loggers[udid][name] = cls._setupLogger(udid, name, logName, level)
return cls._loggers[udid][name]
@classmethod @classmethod
def info(cls, text, udid): def info(cls, text, udid):
"""记录 INFO 级别的日志""" cls._setupLogger(udid, "infoLogger", "info.log", level=logging.INFO).info(f"[{udid}] {text}")
logger = cls._getLogger(udid, "infoLogger", "info.log", level=logging.INFO)
logger.info(f"[{udid}] {text}")
@classmethod @classmethod
def warning(cls, text, udid): def warning(cls, text, udid):
"""记录 WARNING 级别的日志""" cls._setupLogger(udid, "warningLogger", "warning.log", level=logging.WARNING).warning(f"[{udid}] {text}")
logger = cls._getLogger(udid, "warningLogger", "warning.log", level=logging.WARNING)
logger.warning(f"[{udid}] {text}")
@classmethod @classmethod
def error(cls, text, udid): def error(cls, text, udid):
"""记录 ERROR 级别的日志""" cls._setupLogger(udid, "errorLogger", "error.log", level=logging.ERROR).error(f"[{udid}] {text}")
logger = cls._getLogger(udid, "errorLogger", "error.log", level=logging.ERROR)
logger.error(f"[{udid}] {text}")
@classmethod @classmethod
def clearLogs(cls): def clearLogs(cls):
"""清空整个 log 目录下的所有内容""" """启动时清空 log 目录"""
print("开始清空日志...") print("开始清空日志...")
# 关闭所有日志记录器的处理器 # 关闭所有 handler
for udid in cls._loggers: for name, logger in logging.Logger.manager.loggerDict.items():
for name in cls._loggers[udid]: if isinstance(logger, logging.Logger):
logger = cls._loggers[udid][name] for handler in logger.handlers[:]:
for handler in logger.handlers[:]: # 使用切片避免在迭代时修改列表 try:
handler.close() handler.close()
except:
pass
logger.removeHandler(handler) logger.removeHandler(handler)
print(f"关闭了 {udid}_{name} 的处理器")
# 删除整个 log 目录 # 删除并重建日志目录
if os.path.exists(cls.logDir): if os.path.exists(cls.logDir):
shutil.rmtree(cls.logDir) # 删除目录及其所有内容 shutil.rmtree(cls.logDir)
print(f"删除了 {cls.logDir}") print(f"删除了 {cls.logDir}")
os.makedirs(cls.logDir, exist_ok=True) # 重新创建空的 log 目录 os.makedirs(cls.logDir, exist_ok=True)
print(f"重新创建了 {cls.logDir}") print(f"重新创建了 {cls.logDir}")
else: print("日志清空完成")
print(f"{cls.logDir} 不存在,无需删除")
print("日志清空完成")

23
build.bat Normal file
View File

@@ -0,0 +1,23 @@
python -m nuitka Module/Main.py ^
--standalone ^
--msvc=latest ^
--windows-console-mode=force ^
--remove-output ^
--output-dir=out ^
--output-filename=IOSAI ^
--include-package=Module,Utils,Entity,script ^
--include-module=flask ^
--include-module=flask_cors ^
--include-module=jinja2 ^
--include-module=werkzeug ^
--include-module=cv2 ^
--include-module=numpy ^
--include-module=lxml ^
--include-module=lxml.etree ^
--include-module=requests ^
--include-module=urllib3 ^
--include-module=certifi ^
--include-module=idna ^
--include-data-dir=resources=resources ^
--include-data-dir=Module/iproxy=Module/iproxy ^
--windows-icon-from-ico=resources/icon.ico

BIN
out/Main.dist/IOSAI.exe Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,2 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac habitasse platea dictumst.

Binary file not shown.

BIN
out/Main.dist/libffi-8.dll Normal file

Binary file not shown.

BIN
out/Main.dist/libssl-3.dll Normal file

Binary file not shown.

BIN
out/Main.dist/python3.dll Normal file

Binary file not shown.

BIN
out/Main.dist/python312.dll Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 899 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
out/Main.dist/tcl86t.dll Normal file

Binary file not shown.

BIN
out/Main.dist/tk86t.dll Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
out/Main.dist/zlib1.dll Normal file

Binary file not shown.

BIN
resources/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@@ -348,6 +348,7 @@ class ScriptManager():
if AiUtils.getUnReadMsgCount(session) > 0: if AiUtils.getUnReadMsgCount(session) > 0:
# 执行回复消息逻辑 # 执行回复消息逻辑
self.monitorMessages(session, udid) self.monitorMessages(session, udid)
# 判断是否有首页按钮
homeButton = AiUtils.findHomeButton(udid) homeButton = AiUtils.findHomeButton(udid)
if homeButton.exists: if homeButton.exists:
homeButton.click() homeButton.click()
@@ -362,12 +363,12 @@ class ScriptManager():
session.appium_settings({"snapshotMaxDepth": 15}) session.appium_settings({"snapshotMaxDepth": 15})
# 点击搜索按钮 # 点击搜索按钮
ControlUtils.clickSearch(session) ControlUtils.clickSearch(session)
else: else:
session.appium_settings({"snapshotMaxDepth": 15}) session.appium_settings({"snapshotMaxDepth": 15})
# 点击搜索按钮 # 点击搜索按钮
ControlUtils.clickSearch(session) ControlUtils.clickSearch(session)
def replyMessages(self, udid, event): def replyMessages(self, udid, event):
client = wda.USBClient(udid) client = wda.USBClient(udid)
session = client.session() session = client.session()