From aa2f291f4992918516b489ff6a24f5c570d93506 Mon Sep 17 00:00:00 2001 From: zhangkai <2403741920@qq.com> Date: Fri, 12 Sep 2025 21:36:47 +0800 Subject: [PATCH] =?UTF-8?q?20250904-=E5=88=9D=E6=AD=A5=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=B7=B2=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/iOSAI.iml | 2 +- .idea/misc.xml | 2 +- .idea/workspace.xml | 121 ++++++++++++++++++++++++++++++---------- Module/FlaskService.py | 19 +++++++ Utils/AiUtils.py | 75 ++++++++++++++++++++++++- Utils/JsonUtils.py | 20 ++++++- Utils/LogManager.py | 2 +- Utils/Requester.py | 30 ++++++---- script/ScriptManager.py | 77 +++++++++++++++++++------ 9 files changed, 284 insertions(+), 64 deletions(-) diff --git a/.idea/iOSAI.iml b/.idea/iOSAI.iml index 6cb8b9a..894b6b0 100644 --- a/.idea/iOSAI.iml +++ b/.idea/iOSAI.iml @@ -4,7 +4,7 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index db8786c..c27b771 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 7549b82..24dd257 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -8,8 +8,12 @@ - + + + + + - { - "keyToString": { - "ASKED_ADD_EXTERNAL_FILES": "true", - "Python.12.executor": "Run", - "Python.123.executor": "Run", - "Python.Main.executor": "Run", - "Python.tidevice_entry.executor": "Run", - "RunOnceActivity.ShowReadmeOnStart": "true", - "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true", - "RunOnceActivity.git.unshallow": "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": "preferences.editor.code.editing", - "vue.rearranger.settings.migration": "true" + +}]]> @@ -144,6 +150,52 @@ - @@ -242,6 +297,7 @@ + - + - - - - + + + + + + + \ No newline at end of file diff --git a/Module/FlaskService.py b/Module/FlaskService.py index b1fe646..f437238 100644 --- a/Module/FlaskService.py +++ b/Module/FlaskService.py @@ -2,6 +2,7 @@ import json import os import socket import threading +import time from pathlib import Path from queue import Queue from typing import Any, Dict @@ -87,6 +88,7 @@ def start_socket_listener(): listener_thread = threading.Thread(target=start_socket_listener, daemon=True) listener_thread.start() + # 获取设备列表 @app.route('/deviceList', methods=['GET']) def deviceList(): @@ -358,6 +360,7 @@ def queryAnchorList(): data = [] return ResultData(data=data).toJson() + # 删除主播 @app.route("/deleteAnchorWithIds", methods=['POST']) def deleteAnchorWithIds(): @@ -366,6 +369,7 @@ def deleteAnchorWithIds(): deleted = AiUtils.delete_anchors_by_ids(ids) return ResultData(data={"deleted": deleted}).toJson() + @app.route("/aiConfig", methods=['POST']) def aiConfig(): data = request.get_json() @@ -384,6 +388,7 @@ def aiConfig(): JsonUtils.write_json("aiConfig", dict) return ResultData(data="").toJson() + # 查询主播聊天发送的最后一条信息 @app.route("/select_last_message", methods=['GET']) def select_last_message(): @@ -427,5 +432,19 @@ def delete_last_message(): return ResultData(data=updated_count, msg="修改失败").toJson() +# @app.route("/killWda", methods=['POST']) +# def killWda(): +# data = request.get_json() # 解析 JSON +# udid = data.get("device") +# print(udid) +# +# +# AiUtils.kill_wda(udid) +# time.sleep(10) +# AiUtils.launch_wda(udid) +# +# return ResultData(data="", msg="WDA重新启动").toJson() + + if __name__ == '__main__': app.run("0.0.0.0", port=5000, debug=True, use_reloader=False) diff --git a/Utils/AiUtils.py b/Utils/AiUtils.py index f4f7934..89052b8 100644 --- a/Utils/AiUtils.py +++ b/Utils/AiUtils.py @@ -1,11 +1,16 @@ import json import os +import shlex +import subprocess +import time from pathlib import Path import cv2 import numpy as np import unicodedata import wda + +from Entity.Variables import WdaAppBundleId from Utils.LogManager import LogManager import xml.etree.ElementTree as ET import re, html @@ -709,4 +714,72 @@ class AiUtils(object): json.dump(data, f, ensure_ascii=False, indent=2) except Exception as e: LogManager.error(f"[delete_anchors_by_ids] 写入失败: {e}") - return deleted \ No newline at end of file + return deleted + + @staticmethod + def run_tidevice_command(udid, action, bundle_id, timeout=30): + """ + 执行tidevice命令的辅助函数 + + :param udid: 设备UDID + :param action: 动作类型 ('kill' 或 'launch') + :param bundle_id: 应用的Bundle ID + :param timeout: 命令执行超时时间(秒) + :return: (bool) 成功返回True,失败返回False + """ + # 构建命令列表 + cmd = ["tidevice", "--udid", udid, action, bundle_id] + try: + # 执行命令并捕获输出 + result = subprocess.run( + cmd, + capture_output=True, + text=True, + timeout=timeout + ) + # 检查命令是否成功执行(返回码为0通常表示成功) + if result.returncode == 0: + LogManager.info(f"Successfully {action}ed {bundle_id} on device {udid}.") + return True + else: + # 记录错误信息 + LogManager.error(f"Failed to {action} {bundle_id} on device {udid}. Error: {result.stderr}") + return False + + except subprocess.TimeoutExpired: + # 处理命令执行超时 + LogManager.error(f"Command 'tidevice {action}' timed out after {timeout} seconds for device {udid}.") + return False + except FileNotFoundError: + # 处理tidevice命令未找到的情况(通常意味着tidevice未安装或不在PATH中) + LogManager.error("The 'tidevice' command was not found. Please ensure it is installed and in your system PATH.") + return False + except Exception as e: + # 捕获其他可能异常 + LogManager.error(f"An unexpected error occurred while trying to {action} the app: {e}") + return False + + @classmethod + def kill_wda(cls, udid, bundle_id="com.yolozsAgent.wda.xctrunner"): + """ + 杀死指定设备上的WDA应用 + + :param udid: 设备UDID + :param bundle_id: WDA的Bundle ID,默认为 com.yolozsAgent.wda.xctrunner + :return: (bool) 成功返回True,失败返回False + """ + return cls.run_tidevice_command(udid, "kill", bundle_id) + + @classmethod + def launch_wda(cls, udid, bundle_id="com.yolozsAgent.wda.xctrunner", timeout=60): + """ + 启动指定设备上的WDA应用 + + :param udid: 设备UDID + :param bundle_id: WDA的Bundle ID,默认为 com.yolozsAgent.wda.xctrunner + :param timeout: 启动命令超时时间,默认为60秒(启动可能较慢) + :return: (bool) 成功返回True,失败返回False + """ + return cls.run_tidevice_command(udid, "launch", bundle_id, timeout) + + diff --git a/Utils/JsonUtils.py b/Utils/JsonUtils.py index 0dda46c..d63dcc3 100644 --- a/Utils/JsonUtils.py +++ b/Utils/JsonUtils.py @@ -179,17 +179,31 @@ class JsonUtils: return updated + # @classmethod + # def query_all_json_items(cls, filename="log/last_message.json") -> list: + # """ + # 查询 JSON 文件(数组)中的所有项 + # :param filename: JSON 文件路径 + # :return: list,可能为空 + # """ + # file_path = Path(filename) + # print(file_path) + # data = cls._read_json_list(file_path) + # return data if isinstance(data, list) else [] + @classmethod def query_all_json_items(cls, filename="log/last_message.json") -> list: """ - 查询 JSON 文件(数组)中的所有项 + 查询 JSON 文件(数组)中的所有项,并剔除 sender 为空的记录 :param filename: JSON 文件路径 :return: list,可能为空 """ file_path = Path(filename) - print(file_path) data = cls._read_json_list(file_path) - return data if isinstance(data, list) else [] + if not isinstance(data, list): + return [] + # 过滤 sender 为空字符串的项 + return [item for item in data if isinstance(item, dict) and item.get("sender", "").strip()] @classmethod def delete_json_items(cls, diff --git a/Utils/LogManager.py b/Utils/LogManager.py index a39050c..67e8a24 100644 --- a/Utils/LogManager.py +++ b/Utils/LogManager.py @@ -240,7 +240,7 @@ def _force_utf8_everywhere(): except Exception: pass -_force_utf8_everywhere() +# _force_utf8_everywhere() # ========= 全局:强制 UTF-8 + 关闭缓冲(运行期立刻生效) ========= diff --git a/Utils/Requester.py b/Utils/Requester.py index 2e0dbe0..4dd6fe1 100644 --- a/Utils/Requester.py +++ b/Utils/Requester.py @@ -29,15 +29,19 @@ class Requester(): @classmethod def translation(cls, msg, country="英国"): try: - parame = { + param = { "msg": msg, "country": country, } url = "https://ai.yolozs.com/translation" - result = requests.request(url=url, json=parame, method="POST") + result = requests.post(url=url, json=param) + + if result.status_code != 200: + LogManager.error(f"翻译失败,状态码:{result.status_code},服务器返回的内容:{result.text}") + return None + json = result.json() data = json.get("data") - print(data) return data except Exception as e: LogManager.method_error(f"翻译失败,报错的原因:{e}", "翻译失败异常") @@ -52,19 +56,25 @@ class Requester(): contact = aiConfig.get("contact", "") inputs = { - "agentName":agentName, - "guildName":guildName, - "contactTool":contactTool, - "contact":contact + "name":agentName, + "Trade_union":guildName, + "contcat_method":contactTool, + "contcat_info":contact } param["inputs"] = inputs + print(param) + try: url = "https://ai.yolozs.com/chat" - result = requests.request(url=url, json=param, method="POST") + result = requests.post(url=url, json=param) + + json = result.json() - data = json.get("data", {}) - return data + + data = json.get("answer", {}) + session_id = json.get("conversation_id", {}) + return data,session_id except Exception as e: LogManager.method_error(f"ai聊天失败,ai聊天出现异常,报错的原因:{e}", "ai聊天接口异常") diff --git a/script/ScriptManager.py b/script/ScriptManager.py index e2186c2..650b4db 100644 --- a/script/ScriptManager.py +++ b/script/ScriptManager.py @@ -145,10 +145,21 @@ class ScriptManager(): LogManager.method_error(f"刷视频过程出现错误,重试", "养号", udid) raise e # 抛出给上层,触发重生机制 + except wda.WDARequestError as e: + print(e) + LogManager.method_info("WDA 内部崩溃(nil 插入),准备重启 WDA", "养号", udid) + AiUtils.kill_wda(udid, ev.WdaAppBundleId) + time.sleep(1) + AiUtils.launch_wda(udid, ev.WdaAppBundleId) except Exception as e: - LogManager.method_error(f"[{udid}] 养号出现异常,将重启流程: {e}", "养号", udid) retries += 1 - time.sleep(3) # 等待后重生 + LogManager.method_error(f"greetNewFollowers 出现最终异常: {e},准备第 {retries} 次重试", "养号", udid) + time.sleep(3) + if retries: + LogManager.method_info("WDA 连接失败,准备重启 WDA", "养号", udid) + AiUtils.kill_wda(udid, ev.WdaAppBundleId) + time.sleep(1) + AiUtils.launch_wda(udid, ev.WdaAppBundleId) # 观看直播 def watchLiveForGrowth(self, udid, event, max_retries=None): @@ -285,11 +296,21 @@ class ScriptManager(): # break self.greetNewFollowers(udid, needReply, event) - + except wda.WDARequestError as e: + LogManager.method_info("WDA 内部崩溃(nil 插入),准备重启 WDA", "关注打招呼", udid) + AiUtils.kill_wda(udid, ev.WdaAppBundleId) + time.sleep(1) + AiUtils.launch_wda(udid, ev.WdaAppBundleId) except Exception as e: retries += 1 - LogManager.method_error(f"greetNewFollowers 出现异常: {e},准备第 {retries} 次重试", "关注打招呼", udid) + LogManager.method_error(f"greetNewFollowers 出现最终异常: {e},准备第 {retries} 次重试", "关注打招呼", + udid) time.sleep(3) + if retries % 5 == 0: + LogManager.method_info("WDA 连接失败,准备重启 WDA", "关注打招呼", udid) + AiUtils.kill_wda(udid, ev.WdaAppBundleId) + time.sleep(1) + AiUtils.launch_wda(udid, ev.WdaAppBundleId) LogManager.method_error("greetNewFollowers 重试次数耗尽,任务终止", "关注打招呼", udid) # 关注打招呼以及回复主播消息 @@ -319,6 +340,7 @@ class ScriptManager(): # 返回上一步 def goBack(count): for i in range(count): + session.appium_settings({"snapshotMaxDepth": 15}) ControlUtils.clickBack(session) time.sleep(2) @@ -567,19 +589,25 @@ class ScriptManager(): time.sleep(2) ControlUtils.openTikTok(session, udid) time.sleep(3) - + retries = 0 while not event.is_set(): try: # 调用检测消息的方法 self.monitorMessages(session, udid) + except wda.WDARequestError as e: + LogManager.method_info("WDA 内部崩溃(nil 插入),准备重启 WDA", "养号", udid) + AiUtils.kill_wda(udid, ev.WdaAppBundleId) + time.sleep(1) + AiUtils.launch_wda(udid, ev.WdaAppBundleId) except Exception as e: - LogManager.method_error(f"监控消息 出现异常: {e},重新启动监控直播", "检测消息", udid) - # 出现异常时,稍等再重启 TikTok 并重试 - ControlUtils.closeTikTok(session, udid) - time.sleep(2) - ControlUtils.openTikTok(session, udid) + retries += 1 + LogManager.method_error(f"greetNewFollowers 出现最终异常: {e},准备第 {retries} 次重试", "养号", udid) time.sleep(3) - continue # 重新进入 while 循环,调用 monitorMessages + if retries % 5 == 0: + LogManager.method_info("WDA 连接失败,准备重启 WDA", "养号", udid) + AiUtils.kill_wda(udid, ev.WdaAppBundleId) + time.sleep(1) + AiUtils.launch_wda(udid, ev.WdaAppBundleId) # 检查未读消息并回复 def monitorMessages(self, session, udid): @@ -725,6 +753,13 @@ class ScriptManager(): else: last_msg_text = random.choice(text_list) + # 111111 + if AiUtils.contains_chinese(last_msg_text): + LogManager.method_info(f"需要翻译:{last_msg_text}, 即将进行翻译", "检测消息", udid) + + last_msg_text = Requester.translation(last_msg_text) + LogManager.method_info(f"翻译成功:{last_msg_text}, ", "检测消息", udid) + # 向ai发送信息 # 获取主播的名称 @@ -732,6 +767,10 @@ class ScriptManager(): LogManager.method_info(f"获取主播的名称:{anchor_name}", "检测消息", udid) + LogManager.method_info(f"获取主播最后发送的消息 进行翻译:{last_msg}", "检测消息", udid) + last_msg = Requester.translation(last_msg, "中国") + LogManager.method_info(f"翻译后的内容:{last_msg}", "检测消息", udid) + # 找到输入框 last_data = [{ "sender": anchor_name, @@ -748,12 +787,14 @@ class ScriptManager(): if anchor_name not in anchorWithSession: # 如果是第一次发消息(没有sessionId的情况) - response = Requester.chatToAi({"query": last_msg_text}) - aiResult = response['result'] - sessionId = response['session_id'] + aiResult, sessionId = Requester.chatToAi({"query": last_msg_text, "user": "1"}) + # aiResult = response['result'] + # sessionId = response['session_id'] anchorWithSession[anchor_name] = sessionId # 找到输入框,输入ai返回出来的消息 + + # 123456 if sel.exists: sel.click() # 聚焦 time.sleep(1) @@ -766,8 +807,12 @@ class ScriptManager(): else: # 如果不是第一次发消息(证明存储的有sessionId) sessionId = anchorWithSession[anchor_name] - response = Requester.chatToAi({"query": last_msg_text, "conversation_id": sessionId}) - aiResult = response['result'] + + # TODO: user后续添加,暂时写死 + + aiResult, sessionId = Requester.chatToAi( + {"query": last_msg_text, "conversation_id": sessionId, "user": "1"}) + # aiResult = response['result'] if sel.exists: sel.click() # 聚焦 time.sleep(1)