From f3fe7a661f16ca89e2860dc76ba405c9838bd26e Mon Sep 17 00:00:00 2001 From: zw <12345678> Date: Thu, 14 Aug 2025 14:30:36 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=B4=E6=97=B6=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Flask/FlaskService.py | 18 ++++++++++ Utils/AiUtils.py | 77 +++++++++++++++++++++++------------------ Utils/Requester.py | 1 + script/ScriptManager.py | 7 ++-- 4 files changed, 67 insertions(+), 36 deletions(-) diff --git a/Flask/FlaskService.py b/Flask/FlaskService.py index 9935adc..f2aa47c 100644 --- a/Flask/FlaskService.py +++ b/Flask/FlaskService.py @@ -5,6 +5,8 @@ import threading import warnings from queue import Queue from typing import Any, Dict +from Utils.AiUtils import AiUtils +from Utils.Requester import Requester import tidevice import wda @@ -72,6 +74,11 @@ listener_thread.start() def passToken(): data = request.get_json() accountToken = data['token'] + print(accountToken) + + Requester.requestComments() + + return ResultData(data="").toJson() # 获取设备列表 @@ -230,5 +237,16 @@ def addTempAnchorData(): addModelToAnchorList(data) return ResultData(data="").toJson() +# 获取当前屏幕上的聊天信息 +@app.route("/getChatTextInfo", methods=['POST']) +def getChatTextInfo(): + data = request.get_json() + udid = data.get("udid") + client = wda.USBClient(udid) + session = client.session() + xml = session.source() + result = AiUtils.extract_messages_from_xml(xml) + return ResultData(data=result).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 721a8fe..b9232bf 100644 --- a/Utils/AiUtils.py +++ b/Utils/AiUtils.py @@ -286,39 +286,59 @@ class AiUtils(object): @classmethod def extract_messages_from_xml(cls, xml: str): """ - 输入 WDA 的页面 XML,输出按时间顺序的消息列表: - 每项形如: - {'type': 'time', 'text': '昨天 下午8:48'} - {'type': 'msg', 'dir': 'in'|'out', 'text': 'hello'} + 仅返回当前屏幕中“可见的”聊天内容(含时间分隔) """ + from lxml import etree root = etree.fromstring(xml.encode("utf-8")) items = [] - # 屏幕宽度(用于右对齐判断) + # 屏幕宽度 app = root.xpath('/XCUIElementTypeApplication') screen_w = cls.parse_float(app[0], 'width', 414.0) if app else 414.0 - # 1) 时间分隔 + # 找 Table 的可见范围 + table = root.xpath('//XCUIElementTypeTable') + if table: + table = table[0] + table_top = cls.parse_float(table, 'y', 0.0) + table_h = cls.parse_float(table, 'height', 0.0) + table_bottom = table_top + table_h + else: + table_top, table_bottom = 0.0, cls.parse_float(app[0], 'height', 736.0) if app else 736.0 + + def in_view(el) -> bool: + """元素在聊天区内并且可见""" + if el.get('visible') != 'true': + return False + y = cls.parse_float(el, 'y', -1e9) + h = cls.parse_float(el, 'height', 0.0) + by = y + h + return not (by <= table_top or y >= table_bottom) + + # 时间分隔 for t in root.xpath('//XCUIElementTypeStaticText[contains(@traits, "Header")]'): - txt = t.get('label') or t.get('name') or t.get('value') or '' - y = cls.parse_float(t, 'y') - if txt.strip(): - items.append({'type': 'time', 'text': txt.strip(), 'y': y}) - - # 2) 消息气泡 - msg_nodes = root.xpath( - '//XCUIElementTypeTable//XCUIElementTypeCell' - '//XCUIElementTypeOther[@name or @label]' - ) + if not in_view(t): + continue + txt = (t.get('label') or t.get('name') or t.get('value') or '').strip() + if txt: + items.append({'type': 'time', 'text': txt, 'y': cls.parse_float(t, 'y')}) + # 消息气泡 EXCLUDES = {'Heart', 'Lol', 'ThumbsUp', '分享发布内容', '视频贴纸标签页', '双击发送表情'} + msg_nodes = table.xpath( + './/XCUIElementTypeCell[@visible="true"]' + '//XCUIElementTypeOther[@visible="true" and (@name or @label) and not(ancestor::XCUIElementTypeCollectionView)]' + ) if table is not None else [] + for o in msg_nodes: text = (o.get('label') or o.get('name') or '').strip() if not text or text in EXCLUDES: continue + if not in_view(o): + continue - # 拿到所在的 Cell(用来找头像按钮) + # 找所在 Cell cell = o.getparent() while cell is not None and cell.get('type') != 'XCUIElementTypeCell': cell = cell.getparent() @@ -329,29 +349,20 @@ class AiUtils(object): right_edge = x + w direction = None - - # 2.1 依据同 Cell 内“图片头像”的位置判定(优先,最稳) + # 头像位置判定 if cell is not None: - avatar_btns = cell.xpath('.//XCUIElementTypeButton[@name="图片头像" or @label="图片头像"]') + avatar_btns = cell.xpath( + './/XCUIElementTypeButton[@visible="true" and (@name="图片头像" or @label="图片头像")]') if avatar_btns: ax = cls.parse_float(avatar_btns[0], 'x') - # 头像在左侧 → 对方;头像在右侧 → 自己 - if ax < screen_w / 2: - direction = 'in' - else: - direction = 'out' - - # 2.2 退化规则:看是否右对齐 + direction = 'in' if ax < (screen_w / 2) else 'out' + # 右对齐兜底 if direction is None: - # 离右边 <= 20px 视为右对齐(自己发的) - if right_edge > screen_w - 20: - direction = 'out' - else: - direction = 'in' + direction = 'out' if right_edge > (screen_w - 20) else 'in' items.append({'type': 'msg', 'dir': direction, 'text': text, 'y': y}) - # 3) 按 y 排序并清理 + # 排序 & 清理 items.sort(key=lambda i: i['y']) for it in items: it.pop('y', None) diff --git a/Utils/Requester.py b/Utils/Requester.py index bb569e5..2cf8c7e 100644 --- a/Utils/Requester.py +++ b/Utils/Requester.py @@ -8,6 +8,7 @@ BaseUrl = "http://192.168.1.174:8101/api/common/" class Requester(): comment = "comment" prologue = "prologue" + @classmethod def requestComments(cls): headers = { diff --git a/script/ScriptManager.py b/script/ScriptManager.py index 0bf250c..49adf12 100644 --- a/script/ScriptManager.py +++ b/script/ScriptManager.py @@ -317,10 +317,9 @@ class ScriptManager(): if chatInput.exists: print("找到输入框了, 准备发送一条打招呼消息") # 准备打招呼的文案 - # text = random.choice(prologueList) - text = "你好" + text = random.choice(prologueList) # 翻译成主播国家的语言 - msg = Requester.translation(text, "法国") + msg = Requester.translation(text, anchorCountry) # 准备发送一条信息 chatInput.click() time.sleep(2) @@ -329,6 +328,8 @@ class ScriptManager(): time.sleep(1) else: print("无法发送信息") + LogManager.error(f"给主播{anchor.anchorId} 发送消息失败", udid) + # 接着下一个主播 removeModelFromAnchorList(anchor) goBack(4)