diff --git a/.idea/iOSAI.iml b/.idea/iOSAI.iml index df5cbff..f571432 100644 --- a/.idea/iOSAI.iml +++ b/.idea/iOSAI.iml @@ -2,7 +2,7 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index c27b771..db8786c 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index cd899f8..33c043d 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,12 +4,11 @@ - - + + + - - - { - "keyToString": { - "ASKED_ADD_EXTERNAL_FILES": "true", - "Python.123.executor": "Run", - "Python.Main.executor": "Run", - "Python.tidevice_entry.executor": "Run", - "RunOnceActivity.ShowReadmeOnStart": "true", - "SHARE_PROJECT_CONFIGURATION_FILES": "true", - "git-widget-placeholder": "main", - "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", - "node.js.detected.package.eslint": "true", - "node.js.detected.package.tslint": "true", - "node.js.selected.package.eslint": "(autodetect)", - "node.js.selected.package.tslint": "(autodetect)", - "nodejs_package_manager_path": "npm", - "settings.editor.selected.configurable": "vcs.Git", - "vue.rearranger.settings.migration": "true" + +}]]> @@ -181,9 +180,8 @@ - - - + + - - - @@ -224,19 +214,18 @@ - - - - - + + - - + + + + + \ No newline at end of file diff --git a/Entity/AnchorModel.py b/Entity/AnchorModel.py index 4d27505..8731735 100644 --- a/Entity/AnchorModel.py +++ b/Entity/AnchorModel.py @@ -14,3 +14,8 @@ class AnchorModel: model.anchorId = d.get('anchorId', "") model.country = d.get('country', "") return model + + # 模型转字典 + @classmethod + def modelToDict(cls, model): + return {"anchorId": model.anchorId, "country": model.country} diff --git a/Entity/Variables.py b/Entity/Variables.py index f499be5..b8d9506 100644 --- a/Entity/Variables.py +++ b/Entity/Variables.py @@ -2,6 +2,7 @@ import threading from typing import Dict, Any from Entity.AnchorModel import AnchorModel + # wda apple bundle id WdaAppBundleId = "com.yolozsAgent.wda.xctrunner" diff --git a/Module/DeviceInfo.py b/Module/DeviceInfo.py index 8232e56..2834562 100644 --- a/Module/DeviceInfo.py +++ b/Module/DeviceInfo.py @@ -10,7 +10,7 @@ import subprocess from pathlib import Path from typing import List, Dict, Optional -from tidevice import Usbmux +from tidevice import Usbmux, ConnectionType from Entity.DeviceModel import DeviceModel from Entity.Variables import WdaAppBundleId from Module.FlaskSubprocessManager import FlaskSubprocessManager @@ -110,13 +110,15 @@ class Deviceinfo(object): continue # 新接入设备 for device in lists: - if (device not in self.deviceArray) and (len(self.deviceArray) < self.maxDeviceCount): + # usb设备,并且为新设备。并且大于总限制数量 + if device.conn_type == ConnectionType.USB and (device not in self.deviceArray) and (len(self.deviceArray) < self.maxDeviceCount): self.screenProxy += 1 try: self.connectDevice(device.udid) self.deviceArray.append(device) except Exception as e: LogManager.error(f"连接设备失败 {device.udid}: {e}", device.udid) + # 拔出设备处理 self._removeDisconnected(lists) time.sleep(1) @@ -126,9 +128,9 @@ class Deviceinfo(object): # ---------------------------- def connectDevice(self, identifier: str): # 1) 连接 WDA(USBClient -> 设备 8100) - try: d = wda.USBClient(identifier, 8100) + LogManager.info("启动 WDA 成功", identifier) except Exception as e: LogManager.error(f"启动 WDA 失败,请检查手机是否已信任、WDA 是否正常。错误: {e}", identifier) diff --git a/Module/FlaskService.py b/Module/FlaskService.py index c464536..c860da5 100644 --- a/Module/FlaskService.py +++ b/Module/FlaskService.py @@ -2,13 +2,12 @@ import json import os import socket import threading -import warnings from queue import Queue from typing import Any, Dict + +from Entity.AnchorModel import AnchorModel from Utils.AiUtils import AiUtils from Utils.LogManager import LogManager -from Utils.Requester import Requester - import tidevice import wda from flask import Flask, request @@ -17,8 +16,8 @@ from Entity.ResultData import ResultData from Utils.ControlUtils import ControlUtils from Utils.ThreadManager import ThreadManager from script.ScriptManager import ScriptManager -from Entity.Variables import anchorList, addModelToAnchorList,prologueList - +from Entity.Variables import anchorList, addModelToAnchorList, prologueList, removeModelFromAnchorList +import Entity.Variables as ev app = Flask(__name__) CORS(app) @@ -81,17 +80,17 @@ def start_socket_listener(): listener_thread = threading.Thread(target=start_socket_listener, daemon=True) listener_thread.start() - -@app.route('/passToken', methods=['POST']) -def passToken(): - try: - data = request.get_json() - token = data['token'] - Requester.requestPrologue(token) - return ResultData(data="").toJson() - except Exception as e: - print(e) - return ResultData(data="").toJson() +# 传递token,暂时用不到了 +# @app.route('/passToken', methods=['POST']) +# def passToken(): +# try: +# data = request.get_json() +# token = data['token'] +# Requester.requestPrologue(token) +# return ResultData(data="").toJson() +# except Exception as e: +# print(e) +# return ResultData(data="").toJson() # 获取设备列表 @@ -245,6 +244,11 @@ def passAnchorData(): acList = data.get("anchorList", []) # 是否需要回复 needReply = data.get("needReply", True) + + # 获取打招呼数据 + ev.prologueList = data.get("prologueList", []) + + # 添加主播数据 addModelToAnchorList(acList) # 启动线程,执行脚本 @@ -252,13 +256,17 @@ def passAnchorData(): manager = ScriptManager() event = threading.Event() # 启动脚本 - # thread = threading.Thread(target=manager.greetNewFollowers, args=(udid, needReply, event)) thread = threading.Thread(target=manager.safe_greetNewFollowers, args=(udid, needReply, event)) thread.start() # 添加到线程管理 ThreadManager.add(udid, thread, event) return ResultData(data="").toJson() +# 获取私信数据 +@app.route("/getPrologueList", methods=['GET']) +def getPrologueList(): + print(ev.prologueList) + return ResultData(data=ev.prologueList).toJson() # 添加临时数据 @app.route("/addTempAnchorData", methods=['POST']) @@ -277,7 +285,6 @@ def getChatTextInfo(): session = client.session() xml = session.source() try: - print(xml) result = AiUtils.extract_messages_from_xml(xml) print(result) return ResultData(data=result).toJson() @@ -312,24 +319,34 @@ def monitorMessages(): @app.route("/setLoginInfo", methods=['POST']) -def upLoadLogFile(): +def upLoadLogLogs(): data = request.get_json() # 解析 JSON token = data.get("token") userId = data.get("userId") tenantId = data.get("tenantId") ok = LogManager.upload_all_logs("http://47.79.98.113:8101/api/log/upload", token, userId, tenantId) - if ok: return ResultData(data="日志上传成功").toJson() else: return ResultData(data="", msg="日志上传失败").toJson() -# @app.route("/", methods=['POST']) -# def upLoadLogFile(): -# datas = request.get_json() # 解析 JSON -# # 先清空打招呼语 -# prologueList.clear() -# # 添加新的打招呼语句 -# for data in datas: -# prologueList.append(data) +# 获取当前的主播列表数据 +@app.route("/anchorList", methods=['POST']) +def queryAnchorList(): + data = [] + for model in anchorList: + data.append(AnchorModel.modelToDict(model)) + return ResultData(data=data).toJson() +# 删除主播 +@app.route("/deleteAnchorWithIds", methods=['POST']) +def deleteAnchorWithIds(): + ls: list[dict] = request.get_json() + for dic in ls: + for model in anchorList: + if dic.get("anchorId") == model.anchorId: + removeModelFromAnchorList(model) + return ResultData(data="").toJson() + +if __name__ == '__main__': + app.run("0.0.0.0", port=5000, debug=True, use_reloader=False) diff --git a/Module/Main.py b/Module/Main.py index 837b0d1..c86317d 100644 --- a/Module/Main.py +++ b/Module/Main.py @@ -7,6 +7,7 @@ from Module.FlaskSubprocessManager import FlaskSubprocessManager from Utils.DevDiskImageDeployer import DevDiskImageDeployer from Utils.LogManager import LogManager + # 确定 exe 或 py 文件所在目录 BASE = Path(getattr(sys, 'frozen', False) and sys.executable or __file__).resolve().parent LOG_DIR = BASE / "log" @@ -25,8 +26,10 @@ if "--role=flask" in sys.argv: _run_flask_role() sys.exit(0) + # 项目入口 if __name__ == "__main__": + # 添加iOS开发包到电脑上 deployer = DevDiskImageDeployer(verbose=True) deployer.deploy_all() diff --git a/Utils/AiUtils.py b/Utils/AiUtils.py index 1550cf5..9574bda 100644 --- a/Utils/AiUtils.py +++ b/Utils/AiUtils.py @@ -459,3 +459,18 @@ class AiUtils(object): return t return "" + + # 检查字符串中是否包含中文 + @classmethod + def contains_chinese(cls, text): + """ + 判断字符串中是否包含中文字符。 + 参数: + text (str): 要检测的字符串。 + + 返回: + bool: 如果字符串中包含中文,返回 True;否则返回 False。 + """ + # 使用正则表达式匹配中文字符 + pattern = re.compile(r'[\u4e00-\u9fff]') + return bool(pattern.search(text)) diff --git a/Utils/LogManager.py b/Utils/LogManager.py index afb11f3..cbb1b00 100644 --- a/Utils/LogManager.py +++ b/Utils/LogManager.py @@ -1,85 +1,4 @@ -# import logging -# import os -# import sys -# import shutil -# from pathlib import Path -# -# -# 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 = {} -# -# @classmethod -# def _setupLogger(cls, udid, name, logName, level=logging.INFO): -# """创建或获取 logger,并绑定到文件""" -# deviceLogDir = os.path.join(cls.logDir, 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 clearLogs(cls): -# """启动时清空 log 目录下所有文件""" -# print("开始清空日志...") -# -# # 关闭所有 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_path = Path(cls.logDir) -# if log_path.exists(): -# for item in log_path.iterdir(): -# if item.is_file(): -# item.unlink() -# elif item.is_dir(): -# import shutil -# shutil.rmtree(item) -# -# print("日志清空完成") + import datetime import io import logging @@ -89,11 +8,8 @@ import sys import shutil import zipfile from pathlib import Path - import requests -import Entity.Variables - class LogManager: # 运行根目录:打包后取 exe 目录;源码运行取项目目录 @@ -235,7 +151,6 @@ class LogManager: @classmethod def clearLogs(cls): """启动时清空 log 目录下所有文件""" - print("开始清空日志...") # 关闭所有 handler for name, logger in logging.Logger.manager.loggerDict.items(): @@ -258,13 +173,11 @@ class LogManager: # 清缓存 cls._method_loggers.clear() - print("日志清空完成") @classmethod def upload_all_logs(cls, server_url, token, userId, tenantId): log_path = Path(cls.logDir) if not log_path.exists(): - print("[upload_all_logs] 日志目录不存在") return False timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") @@ -286,20 +199,9 @@ class LogManager: files = { "file": (filename, io.BytesIO(zip_bytes), "application/zip") } - print( - f"[upload_all_logs] " - f"server={server_url} (type={type(server_url)}) " - f"tenantId={tenantId} (type={type(tenantId)}) " - f"userId={userId} (type={type(userId)}) " - f"token={token} (type={type(token)})" - ) - print("开始上传") # 3) 上传 resp = requests.post(server_url, headers=headers, data=data, files=files) - print(resp.text) - print("上传结束") - if resp.json()['data']: return True return False diff --git a/build-tidevice.bat b/build-tidevice.bat index cb175bb..52df5ee 100644 --- a/build-tidevice.bat +++ b/build-tidevice.bat @@ -4,6 +4,6 @@ pyinstaller -F -n tidevice ^ --hidden-import=tidevice._usbmux ^ --hidden-import=tidevice._wdaproxy ^ --collect-all tidevice ^ - -c ^ + --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 + tidevice_entry.py diff --git a/build.bat b/build.bat index 2bdf314..98699cd 100644 --- a/build.bat +++ b/build.bat @@ -1,17 +1,11 @@ -@echo off -python -m nuitka Module\Main.py ^ +python -m nuitka Module/Main.py ^ --standalone ^ --msvc=latest ^ --windows-console-mode=disable ^ --remove-output ^ --output-dir=out ^ --output-filename=IOSAI ^ - --include-package=Module,Utils,Entity,script,tidevice ^ - --include-module=tidevice.__main__ ^ - --include-module=tidevice._proto ^ - --include-module=tidevice._instruments ^ - --include-module=tidevice._usbmux ^ - --include-module=tidevice._wdaproxy ^ + --include-package=Module,Utils,Entity,script ^ --include-module=flask ^ --include-module=flask_cors ^ --include-module=jinja2 ^ @@ -27,5 +21,4 @@ python -m nuitka Module\Main.py ^ --include-data-dir="E:/Code/python/iOSAI/SupportFiles=SupportFiles" ^ --include-data-dir="E:/Code/python/iOSAI/resources=resources" ^ --include-data-files="E:/Code/python/iOSAI/resources/iproxy/*=resources/iproxy/" ^ - --windows-icon-from-ico=resources/icon.ico -pause \ No newline at end of file + --windows-icon-from-ico=resources/icon.ico \ No newline at end of file diff --git a/resources/iproxy/tidevice.exe b/resources/iproxy/tidevice.exe index 466c364..fdd3048 100644 Binary files a/resources/iproxy/tidevice.exe and b/resources/iproxy/tidevice.exe differ