From c902e6f4d3b33fc6f32fbc54965fbe6136a16433 Mon Sep 17 00:00:00 2001 From: milk <53408947@qq.com> Date: Mon, 27 Oct 2025 16:25:47 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8E=89=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Module/FlaskService.py | 284 +++++++++++------- Module/__pycache__/DeviceInfo.cpython-312.pyc | Bin 24971 -> 24971 bytes .../__pycache__/FlaskService.cpython-312.pyc | Bin 34006 -> 40504 bytes .../FlaskSubprocessManager.cpython-312.pyc | Bin 13260 -> 13260 bytes Module/__pycache__/Main.cpython-312.pyc | Bin 3738 -> 3738 bytes .../__pycache__/ThreadManager.cpython-312.pyc | Bin 8165 -> 8837 bytes script/ScriptManager.py | 11 +- .../__pycache__/ScriptManager.cpython-312.pyc | Bin 66531 -> 66235 bytes 8 files changed, 175 insertions(+), 120 deletions(-) diff --git a/Module/FlaskService.py b/Module/FlaskService.py index 02bf6aa..fb9cd99 100644 --- a/Module/FlaskService.py +++ b/Module/FlaskService.py @@ -1,7 +1,9 @@ +# -*- coding: utf-8 -*- import json import os import socket import threading +import time from pathlib import Path from queue import Queue from typing import Any, Dict @@ -27,140 +29,223 @@ CORS(app) app.config['JSON_AS_ASCII'] = False # Flask jsonify 不转义中文/emoji app.config['JSONIFY_MIMETYPE'] = "application/json; charset=utf-8" +# ============ 设备状态内存表 ============ listData = [] listLock = threading.Lock() +# 历史遗留:不再消费队列,改为socket线程直接落地 dataQueue = Queue() +# ---- 黏性快照(避免瞬时空) ---- +_last_nonempty_snapshot = [] +_last_snapshot_ts = 0.0 +_STICKY_TTL_SEC = 10.0 # 在瞬时空时,回退到上一份非空快照10秒 +_empty_logged = False +_recovered_logged = False -def start_socket_listener(): - port = int(os.getenv('FLASK_COMM_PORT', 0)) - LogManager.info(f"Received port from environment: {port}") - print(f"Received port from environment: {port}") - if port <= 0: - LogManager.info("未获取到通信端口,跳过Socket监听") - print("未获取到通信端口,跳过Socket监听") - return +# ===== 设备集合变化跟踪 ===== +change_version = 0 +_device_ids_snapshot = set() +_last_device_count = 0 + +def _log_device_changes(action: str): + """记录设备集合增删变化""" + global _device_ids_snapshot, change_version + curr_ids = {d.get("deviceId") for d in listData if _is_online(d)} + added = curr_ids - _device_ids_snapshot + removed = _device_ids_snapshot - curr_ids + if added or removed: + change_version += 1 + try: + LogManager.info(f"[DEVICE][CHANGED][{action}] rev={change_version} count={len(curr_ids)} added={list(added)} removed={list(removed)}") + except Exception: + print(f"[DEVICE][CHANGED][{action}] rev={change_version} count={len(curr_ids)} added={list(added)} removed={list(removed)}") + _device_ids_snapshot = curr_ids + +def _normalize_type(v) -> int: + """把各种表示在线/离线的值,规范成 1/0""" + if isinstance(v, bool): + return 1 if v else 0 + if isinstance(v, (int, float)): + return 1 if int(v) == 1 else 0 + if isinstance(v, str): + s = v.strip().lower() + if s.isdigit(): + return 1 if int(s) == 1 else 0 + if s in ("true", "online", "on", "yes"): + return 1 + return 0 + return 1 if v else 0 + +def _is_online(d: Dict[str, Any]) -> bool: + return _normalize_type(d.get("type", 1)) == 1 + +def _apply_device_event(obj: Dict[str, Any]): + """把单条设备上线/下线事件落到 listData,并打印关键日志""" try: - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - # 设置端口复用,避免端口被占用时无法绑定 - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - - # 尝试绑定端口 - try: - s.bind(('127.0.0.1', port)) - print(f"[INFO] Socket successfully bound to port {port}") - LogManager.info(f"[INFO] Socket successfully bound to port {port}") - except Exception as bind_error: - print(f"[ERROR]端口绑定失败: {bind_error}") - LogManager.info(f"[ERROR]端口绑定失败: {bind_error}") - return - - # 开始监听 - s.listen() - LogManager.info(f"[INFO] Socket listener started on port {port}, waiting for connections...") - print(f"[INFO] Socket listener started on port {port}, waiting for connections...") - while True: - try: - conn, addr = s.accept() - except Exception as e: - LogManager.error(f"[ERROR] accept 失败: {e}") - continue - - # 独立线程处理单条连接,避免单客户端异常拖垮监听线程 - threading.Thread(target=_handle_conn, args=(conn, addr), daemon=True).start() - # while True: - # try: - # LogManager.info(f"[INFO] Waiting for a new connection on port {port}...") - # print(f"[INFO] Waiting for a new connection on port {port}...") - # conn, addr = s.accept() - # LogManager.info(f"[INFO] Connection accepted from: {addr}") - # print(f"[INFO] Connection accepted from: {addr}") - # - # raw_data = conn.recv(1024).decode('utf-8').strip() - # LogManager.info(f"[INFO] Raw data received: {raw_data}") - # print(f"[INFO] Raw data received: {raw_data}") - # - # data = json.loads(raw_data) - # LogManager.info(f"[INFO] Parsed data: {data}") - # print(f"[INFO] Parsed data: {data}") - # dataQueue.put(data) - # except Exception as conn_error: - # LogManager.error(f"[ERROR]连接处理失败: {conn_error}") - # print(f"[ERROR]连接处理失败: {conn_error}") + dev_id = obj.get("deviceId") + typ = _normalize_type(obj.get("type", 1)) + obj["type"] = typ # 写回规范后的值,避免后续被误判 + if dev_id is None: + LogManager.warning(f"[DEVICE][WARN] missing deviceId in obj={obj}") + return + with listLock: + before = len(listData) + # 删除同 udid 旧记录 + listData[:] = [d for d in listData if d.get("deviceId") != dev_id] + if typ == 1: + listData.append(obj) # 上线 + LogManager.info(f"[DEVICE][UPSERT] id={dev_id} type={typ} size={len(listData)} (replaced={before - (len(listData)-1)})") + _log_device_changes("UPSERT") + else: + LogManager.warning(f"[DEVICE][REMOVE] id={dev_id} type={typ} size={len(listData)} (removed_prev={before - len(listData)})") + _log_device_changes("REMOVE") except Exception as e: - LogManager.error(f"[ERROR]Socket服务启动失败: {e}") - print(f"[ERROR]Socket服务启动失败: {e}") - + LogManager.error(f"[DEVICE][APPLY_EVT][ERROR] {e}") +# ============ 设备事件 socket 监听 ============ def _handle_conn(conn: socket.socket, addr): + """统一的连接处理函数(外部全局,避免内嵌函数覆盖)""" try: + LogManager.info(f"[SOCKET][ACCEPT] from={addr}") with conn: - # 1. 循环收包直到拿到完整 JSON buffer = "" while True: data = conn.recv(1024) if not data: # 对端关闭 break buffer += data.decode('utf-8', errors='ignore') - # 2. 尝试切出完整 JSON(简单按行,也可按长度头、分隔符) + # 按行切 JSON;发送端每条以 '\n' 结尾 while True: line, sep, buffer = buffer.partition('\n') - if not sep: # 没找到完整行 + if not sep: break line = line.strip() - if not line: # 空行跳过 + if not line: continue try: obj = json.loads(line) except json.JSONDecodeError as e: - LogManager.warning(f"[WARN] 非法 JSON 丢弃: {line[:100]} {e}") + LogManager.warning(f"[SOCKET][WARN] 非法 JSON 丢弃: {line[:120]} err={e}") continue - # 3. 收到合法数据,塞进队列 - dataQueue.put(obj) - LogManager.info(f"[INFO] 收到合法消息: {obj}") + dev_id = obj.get("deviceId") + typ = _normalize_type(obj.get("type", 1)) + obj["type"] = typ + LogManager.info(f"[SOCKET][RECV] deviceId={dev_id} type={typ} keys={list(obj.keys())}") + _apply_device_event(obj) + LogManager.info(f"[SOCKET][APPLY] deviceId={dev_id} type={typ}") except Exception as e: - LogManager.error(f"[ERROR] 连接处理异常: {e}") + LogManager.error(f"[SOCKET][ERROR] 连接处理异常: {e}") +def start_socket_listener(): + """启动设备事件监听(与 HTTP 端口无关,走 FLASK_COMM_PORT)""" + # 统一使用 FLASK_COMM_PORT,默认 34566 + port = int(os.getenv('FLASK_COMM_PORT', 34566)) + LogManager.info(f"Received port from environment: {port}") + print(f"Received port from environment: {port}") -# 在独立线程中启动Socket服务 + if port <= 0: + LogManager.info("未获取到通信端口,跳过Socket监听") + print("未获取到通信端口,跳过Socket监听") + return + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + try: + s.bind(('127.0.0.1', port)) + print(f"[INFO] Socket successfully bound to port {port}") + LogManager.info(f"[INFO] Socket successfully bound to port {port}") + except Exception as bind_error: + print(f"[ERROR]端口绑定失败: {bind_error}") + LogManager.info(f"[ERROR]端口绑定失败: {bind_error}") + return + + s.listen() + LogManager.info(f"[INFO] Socket listener started on port {port}, waiting for connections...") + print(f"[INFO] Socket listener started on port {port}, waiting for connections...") + + while True: + try: + conn, addr = s.accept() + except Exception as e: + LogManager.error(f"[ERROR] accept 失败: {e}") + continue + threading.Thread(target=_handle_conn, args=(conn, addr), daemon=True).start() + +# 监听存活看门狗:端口不可连就拉起 +def _listener_watchdog(): + import socket as _s + port = int(os.getenv('FLASK_COMM_PORT', 34566)) + while True: + try: + ok = False + try: + with _s.socket(_s.AF_INET, _s.SOCK_STREAM) as c: + c.settimeout(1.0) + c.connect(('127.0.0.1', port)) + ok = True + except Exception: + ok = False + if not ok: + LogManager.warning(f"[SOCKET][WATCHDOG] listener not reachable on {port}, restarting...") + try: + threading.Thread(target=start_socket_listener, daemon=True).start() + except Exception as e: + LogManager.error(f"[SOCKET][WATCHDOG] restart failed: {e}") + except Exception as e: + LogManager.error(f"[SOCKET][WATCHDOG] error: {e}") + time.sleep(5) + +# 独立线程启动 Socket 服务 + 看门狗 listener_thread = threading.Thread(target=start_socket_listener, daemon=True) listener_thread.start() +watchdog_thread = threading.Thread(target=_listener_watchdog, daemon=True) +watchdog_thread.start() - -# 获取设备列表 +# ============ API 路由 ============ @app.route('/deviceList', methods=['GET']) def deviceList(): + global _last_device_count, change_version + global _last_nonempty_snapshot, _last_snapshot_ts, _STICKY_TTL_SEC + global _empty_logged, _recovered_logged try: with listLock: - # 1. 消费完队列 - while not dataQueue.empty(): - obj = dataQueue.get() - if obj["type"] == 1: - # 上线:先踢掉同 deviceId 的旧记录(端口可能变) - listData[:] = [d for d in listData if d.get("deviceId") != obj.get("deviceId")] - listData.append(obj) - else: - # 下线:只要同 deviceId 就删,不管端口 - listData[:] = [d for d in listData if d.get("deviceId") != obj.get("deviceId")] + # 宽容判定在线(字符串'1'/'true'/True 都算) + data = [d for d in listData if _is_online(d)] + now = time.time() - # 2. 兜底:只保留在线 - listData[:] = [d for d in listData if d.get('type') == 1] + # 记录最近一次非空快照 + if data: + _last_nonempty_snapshot = data.copy() + _last_snapshot_ts = now + if _recovered_logged: + LogManager.info(f"[API][deviceList][RECOVERED] count={len(data)} rev={change_version}") + _recovered_logged = False + _empty_logged = False + else: + # 瞬时空:在 TTL 内回退上一份非空快照 + if _last_nonempty_snapshot and (now - _last_snapshot_ts) <= _STICKY_TTL_SEC: + LogManager.warning(f"[API][deviceList][STICKY] serving last non-empty snapshot count={len(_last_nonempty_snapshot)} age={now - _last_snapshot_ts:.1f}s rev={change_version}") + return ResultData(data=_last_nonempty_snapshot).toJson() + if not _empty_logged: + LogManager.error(f"[API][deviceList][DROP_TO_EMPTY] last_count={_last_device_count} rev={change_version}") + _empty_logged = True + _recovered_logged = True - return ResultData(data=listData.copy()).toJson() + _last_device_count = len(data) + LogManager.info(f"[API][deviceList] return_count={len(data)} rev={change_version}") + return ResultData(data=data).toJson() except Exception as e: - LogManager.error("获取设备列表失败:", e) + LogManager.error(f"[API][deviceList] error={e}") return ResultData(data=[]).toJson() - -# 传递token @app.route('/passToken', methods=['POST']) def passToken(): data = request.get_json() print(data) return ResultData(data="").toJson() - # 获取设备应用列表 @app.route('/deviceAppList', methods=['POST']) def deviceAppList(): @@ -169,7 +254,6 @@ def deviceAppList(): apps = ControlUtils.getDeviceAppList(udid) return ResultData(data=apps).toJson() - # 打开指定app @app.route('/launchApp', methods=['POST']) def launchApp(): @@ -205,7 +289,6 @@ def tapAction(): session.tap(x, y) return ResultData(data="").toJson() - # 拖拽事件 @app.route('/swipeAction', methods=['POST']) def swipeAction(): @@ -224,7 +307,6 @@ def swipeAction(): session.swipe(sx, sy, ex, ey, duration) return ResultData(data="").toJson() - # 长按事件 @app.route('/longPressAction', methods=['POST']) def longPressAction(): @@ -238,7 +320,6 @@ def longPressAction(): session.tap_hold(x, y, 1.0) return ResultData(data="").toJson() - # 养号 @app.route('/growAccount', methods=['POST']) def growAccount(): @@ -254,7 +335,6 @@ def growAccount(): code, msg = ThreadManager.add(udid, thread, event) return ResultData(data="", code=code, message=msg).toJson() - # 观看直播 @app.route("/watchLiveForGrowth", methods=['POST']) def watchLiveForGrowth(): @@ -267,7 +347,6 @@ def watchLiveForGrowth(): ThreadManager.add(udid, thread, event) return ResultData(data="").toJson() - # 停止脚本 @app.route("/stopScript", methods=['POST']) def stopScript(): @@ -277,7 +356,6 @@ def stopScript(): code, msg = ThreadManager.stop(udid) return ResultData(code=code, data=[], message=msg).toJson() - # 关注打招呼 @app.route('/passAnchorData', methods=['POST']) def passAnchorData(): @@ -292,7 +370,6 @@ def passAnchorData(): acList = data.get("anchorList", []) Variables.commentList = data.get("comment") - LogManager.info(f"[INFO] 获取数据: {idList} {acList}") AiUtils.save_aclist_flat_append(acList) @@ -322,7 +399,6 @@ def passAnchorData(): LogManager.error(e) return ResultData(data="", code=1001).toJson() - @app.route('/followAndGreetUnion', methods=['POST']) def followAndGreetUnion(): try: @@ -361,14 +437,12 @@ def followAndGreetUnion(): LogManager.error(e) return ResultData(data="", code=1001).toJson() - # 获取私信数据 @app.route("/getPrologueList", methods=['GET']) def getPrologueList(): import Entity.Variables as Variables return ResultData(data=Variables.prologueList).toJson() - # 添加临时数据 # 批量追加主播到 JSON 文件 @app.route("/addTempAnchorData", methods=['POST']) @@ -385,7 +459,6 @@ def addTempAnchorData(): AiUtils.save_aclist_flat_append(data, "log/acList.json") return ResultData(data="ok").toJson() - # 获取当前屏幕上的聊天信息 @app.route("/getChatTextInfo", methods=['POST']) def getChatTextInfo(): @@ -432,7 +505,6 @@ def getChatTextInfo(): ] return ResultData(data=data, message="解析失败").toJson() - # 监控消息 @app.route("/replyMessages", methods=['POST']) def monitorMessages(): @@ -449,7 +521,6 @@ def monitorMessages(): ThreadManager.add(udid, thread, event) return ResultData(data="").toJson() - # 上传日志 @app.route("/setLoginInfo", methods=['POST']) def upLoadLogLogs(): @@ -463,7 +534,6 @@ def upLoadLogLogs(): else: return ResultData(data="", message="日志上传失败").toJson() - # 获取当前的主播列表数据 @app.route("/anchorList", methods=['POST']) def queryAnchorList(): @@ -481,7 +551,6 @@ def queryAnchorList(): data = [] return ResultData(data=data).toJson() - # 修改当前的主播列表数据 @app.route("/updateAnchorList", methods=['POST']) def updateAnchorList(): @@ -537,7 +606,6 @@ def updateAnchorList(): else: return ResultData(data=0, message="未找到符合条件的记录").toJson() - # 删除主播 @app.route("/deleteAnchorWithIds", methods=['POST']) def deleteAnchorWithIds(): @@ -546,7 +614,6 @@ def deleteAnchorWithIds(): deleted = AiUtils.delete_anchors_by_ids(ids) return ResultData(data={"deleted": deleted}).toJson() - # 配置ai人设 @app.route("/aiConfig", methods=['POST']) def aiConfig(): @@ -585,14 +652,12 @@ def aiConfig(): IOSAIStorage.overwrite(dict, "aiConfig.json") return ResultData(data="").toJson() - # 查询主播聊天发送的最后一条信息 @app.route("/select_last_message", methods=['GET']) def select_last_message(): data = JsonUtils.query_all_json_items() return ResultData(data=data).toJson() - # 修改消息(已读改成未读) @app.route("/update_last_message", methods=['POST']) def update_last_message(): @@ -611,7 +676,6 @@ def update_last_message(): return ResultData(data=updated_count, message="修改成功").toJson() return ResultData(data=updated_count, message="修改失败").toJson() - # 删除已读消息 @app.route("/delete_last_message", methods=['POST']) def delete_last_message(): @@ -630,7 +694,6 @@ def delete_last_message(): return ResultData(data=updated_count, message="修改失败").toJson() - # 停止所有任务 @app.route("/stopAllTask", methods=['POST']) def stopAllTask(): @@ -638,7 +701,6 @@ def stopAllTask(): code, msg = ThreadManager.batch_stop(idList) return ResultData(code, [], msg).toJson() - # 切换账号 @app.route('/changeAccount', methods=['POST']) def changeAccount(): @@ -657,7 +719,6 @@ def changeAccount(): # thread.start() return ResultData(data="", code=code, message=msg).toJson() - @app.route('/test', methods=['POST']) def test(): import wda @@ -723,4 +784,5 @@ def getAiConfig(): return ResultData(data=data).toJson() if __name__ == '__main__': - app.run("0.0.0.0", port=5000, debug=True, use_reloader=False) + # 注意:这里建议 debug=False,避免未来有人改成 use_reloader=True 导致多进程 + app.run("0.0.0.0", port=5000, debug=False, use_reloader=False, threaded=True) \ No newline at end of file diff --git a/Module/__pycache__/DeviceInfo.cpython-312.pyc b/Module/__pycache__/DeviceInfo.cpython-312.pyc index 3b0eedca2468b56b0bf916416c4378697dfa2e98..fd0ed3e69497bf5fe791657257248eede2890269 100644 GIT binary patch delta 22 ccmeA^%-DUHk^3|+FBbz4v`qQ8k-H%g08;Mh008|+VcmMzZ diff --git a/Module/__pycache__/FlaskService.cpython-312.pyc b/Module/__pycache__/FlaskService.cpython-312.pyc index 58ce35bfac0510b1031d829494ab55adce4fd725..400814975a3bb92270d96c41813b13d16b82cfe5 100644 GIT binary patch delta 15747 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 L$QWq`okIT(V4KWb delta 9693 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 diff --git a/Module/__pycache__/FlaskSubprocessManager.cpython-312.pyc b/Module/__pycache__/FlaskSubprocessManager.cpython-312.pyc index f993302b15e53a0f9c441cf51fad534d3fecc720..2d5c1bf005a7831347aa7931d6035bcd8c82a04e 100644 GIT binary patch delta 20 acmX?;ekPs!G%qg~0}!-K`L~h#pfLbT=m#eN delta 20 acmX?;ekPs!G%qg~0}#~B`n{3+pfLbT(+4B~ diff --git a/Module/__pycache__/Main.cpython-312.pyc b/Module/__pycache__/Main.cpython-312.pyc index 9c0f17fa0645db6fab6cd17d3b5939865588e9fa..9e23e1d7d70d829bf3c644c830f85ab2c637285d 100644 GIT binary patch delta 20 acmbOwJ4=@PG%qg~0}!-K`L~g~pAP^ziv`#K delta 20 acmbOwJ4=@PG%qg~0}!m3^?M_CKOX=(oCW^? diff --git a/Utils/__pycache__/ThreadManager.cpython-312.pyc b/Utils/__pycache__/ThreadManager.cpython-312.pyc index c2b09b701808562d3919e5a8783da987be6bf0d2..c6d05967c07f2de361d43022dfb860225ec5e9ce 100644 GIT binary patch delta 4809 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 delta 4244 zcmbVPYj9J?72f;O^Gdq5jBWX;AR!|IPAN7oLyT(&3?ZJ75GHoWcpP6DY>Y*&B#2WP zo9eORD#3>NO0Kk<7Je_v-3( z4D#rk8JYJOWWi%>=4H(xpR#Q(MBFV_dT<7k+j zyJ#Jf4V~OUu0v4U1Iz*RBv*$HFm7X@7~8qrHQ$`=iXR?|e{?MI^A8gzyS_Qwtq^wv zv_P(W^vRWxm+S%JWBcW?p7=Y1@u86z?{D<&*@&OxHsY_jN=hg&@!AAVGXA`duT|RRr{^Ch>kMR%Qzj6jA zhEIHR_DEvv@Rh#j<2~WaANRyG{@seFQLcSiw?;>f;nqNqDs|aTvS}raNVpi+)aFz2 zfRAzB$6I|IaCW<0$- zZ(|R!xKNvOO4esGt{~f*4tlqJK{m+w7fcIVGhR;1fg3r*<49f>E;l-HUXG4~nYh(h zR;Y()XHM%vv{!U${Kn~iYc}q;S=Sqa8X9+D<^mzvD5s>&Y!eH&0Cj2(YMNNc3HY1U zdkEo6X?9#9>UptNDZu68&NS*JKE}4l3OS|X!l%V29HyAi(B^I0qd18ymykk2?1ZSk z#9K`EP>W1l!uwnHdK7Ya#R|l&RTdJSxHx5$a794C<887+j_4yd4%|*YC5sL<%JNfb z+i3vrZ)|VeI)$nu)dM z#YPJZCW*#%39mI5OB&I%Ix1xji2dSG^Q9a+K4E@9Tr#cn-SwsTmbrB2au86vw~|X) z)9RZymQ8Ei^sR>-^i&GfE4j~dmjFGdt#nnbVn1J{uU;XXM}^fT!udjm@OB=C&zA^< zU%?T6rBGdgU$U%V3wUxkCo~i7ZK}3qzP6uS z3Hk-K(<>|KKd?LQ|Ja>0f4w{I?$xxyOm%l@^R?;8OXr{b4i?fOou%n>Kspz7>M+<0 zsWy#4-R)6>)MYw#2X%eSk?krs3*ip59Ql|7EQ9JG2Jpf#DQLF_2;#1xubc0b%c91E>_{Zr*xfBOdssz1fAXaf>f4VdD z1{2egH1`Jl0TP?X?D3x7%O4NL&-SW|Cc66KFP@lQEoDL-3fSjQxO)0bu9{XFo~|KOG>?99X3t)mUIP6(t73iO{KIsx5fHQg1--yH}t4INozI@y;1|#)!2K z-k*Ho#l+FysgHr1o%|B!2bKX*=gzD<*TUs9J@CrK`+gSh9liQV-_?^x+@eC}%z-@( zYK1;|K2!v}3%D5P^Y~{$FUGfeTiO*83b%d^{*{tRWrR2gnRPqCKNSgyuP6?xScnU< z^4C{`%y*Nb9zdHpO~pO8zJ`gWFBmB9FCHlAF9~P-E#1{s^-pVNG;7%nEg4P1>NWU4 zR(S}fW#kN0^jAz+ol(g;VCXjtnETCx{_yq*sW_TdbR&Oe0+U?GnI2`L(t-hfzy7E( zsY51f?|t3(g~hJ>Cd6e?tL+CZeJ$l&lw9MIYb0%?bi@_;(WXdI%>~!W327Tyq-=JP z>uZrI>!P@DTwEC57WRjmBTKhN9QR+?KOye;+LAk1HDR&iFBfEozQ5hu78x~1iYh+M zoRDhB5}TonOP!p#Udzx$9OI%R+&IFA_eYj_BF^T>?)C}M3y((cmhLURwcWLYrS#p1 zS~uNzXwLlauDcK}$Sw-~RjR+0f)pd) zUSbd)&A!LN04%dIOYxoya!``ueFg&$G7cBoY+3xfd}uFP2%5S{^nO}Lw1D%mSIkkt|imT39%DGGEq%WTCb48L?L?&cxBf^=@1Bf4+DX6 zF3sAdiDqO?Ax`h?s*Yyop3uFb3uhcRcW>^h?sG&f);`}$KPAPRsoyjx|6Ot#o!U0j_j1jcX+0#R>}mKkgR9u z_AEkCxM>POlNtmrxsVR&1ozqt?-;(d;)Wrbw4+Ut@t&T|yk;E;lP8 z@yb?0mIHy`a2@*~ED8x8b+kg#GA2FjZ)x+DZ1wI+{WwrG_}X1rPd!M~wh=-zp1OED z>5{8J-F{QIZm(Akw0Z7P9swCiKp(*gfOm%Z3Yoq_(pSj%FXS9Y&NwQ9hjY=&Ye-@i Y^fe_(Gv@r3G&fvNFpPT&5lW5!4{Bg)bpQYW diff --git a/script/ScriptManager.py b/script/ScriptManager.py index 5bfe692..cf20d3a 100644 --- a/script/ScriptManager.py +++ b/script/ScriptManager.py @@ -1111,23 +1111,18 @@ class ScriptManager(): " or @value='Inbox' or @label='Inbox' or @name='Inbox']" ) - print("11111111111111") # 查找所有收件箱节点 inbox_nodes = session.xpath(xpath_query).find_elements() - print("222222222222222") if len(inbox_nodes) < 2: LogManager.method_error(f"当前页面不再收件箱页面,重启", "检测消息", udid) raise Exception("当前页面不再收件箱页面,重启") - print("33333333333333") m = re.search(r'(\d+)', el.label) # 抓到的第一个数字串 count = int(m.group(1)) if m else 0 - print("444444444444444444") if not count: LogManager.method_info(f"当前收件箱的总数量{count}", "检测消息", udid) break - # print("5555555555555555555555") # 新粉丝 xp_new_fan_badge = ( @@ -1170,7 +1165,6 @@ class ScriptManager(): "[@value and translate(@value,'0123456789','')='']" "/ancestor::XCUIElementTypeCell[1]" ) - print("6666666666666666") try: # 如果 2 秒内找不到,会抛异常 @@ -1184,7 +1178,6 @@ class ScriptManager(): print("当前屏幕没有找到 用户消息 未读徽标数字", udid) user_text = None info_count += 1 - print("777777777777777777777") if user_text: @@ -1218,11 +1211,11 @@ class ScriptManager(): last_in = item['text'] if last_out is None and item['dir'] == 'out': last_out = item['text'] - if last_in or last_out: # 任一条拿到就提前停 + if last_in and last_out: # 任一条拿到就提前停 break # 2. 只有两条都空才重试 - if not last_in and not last_out: + if not last_in or not last_out: attempt += 1 if attempt == 3: break # 三次用完,放弃 diff --git a/script/__pycache__/ScriptManager.cpython-312.pyc b/script/__pycache__/ScriptManager.cpython-312.pyc index cc81b14c3ed40f665b5c293b75f1f4291155509a..78f3251be854ab035111a113ae1d039b57c1c9d0 100644 GIT binary patch delta 1662 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