diff --git a/Entity/AnchorModel.py b/Entity/AnchorModel.py new file mode 100644 index 0000000..4d27505 --- /dev/null +++ b/Entity/AnchorModel.py @@ -0,0 +1,16 @@ + +# 主播模型 +class AnchorModel: + def __init__(self, anchorId= "", country= ""): + # 主播ID + self.anchorId = anchorId + # 主播国家 + self.country = country + + # 字典转模型 + @classmethod + def dictToModel(cls, d): + model = AnchorModel() + model.anchorId = d.get('anchorId', "") + model.country = d.get('country', "") + return model diff --git a/Entity/Variables.py b/Entity/Variables.py index 3cc17e4..ed852e1 100644 --- a/Entity/Variables.py +++ b/Entity/Variables.py @@ -1,4 +1,24 @@ -# Tik Tok app bundle id +import threading +from typing import Dict, Any + +from Entity.AnchorModel import AnchorModel + tikTokApp = "com.zhiliaoapp.musically" # wda apple bundle id WdaAppBundleId = "com.vv.wda.xctrunner" +# 全局主播列表 +anchorList: list[AnchorModel] = [] +# 线程锁 +anchorListLock = threading.Lock() + +# 安全删除数据 +def removeModelFromAnchorList(model: AnchorModel): + with anchorListLock: + anchorList.remove(model) + +# 添加数据 +def addModelToAnchorList(models: list[Dict[str, Any]]): + with anchorListLock: + for dic in models: + obj = AnchorModel.dictToModel(dic) + anchorList.append(obj) \ No newline at end of file diff --git a/Flask/FlaskService.py b/Flask/FlaskService.py index cf83f56..c7cb3a9 100644 --- a/Flask/FlaskService.py +++ b/Flask/FlaskService.py @@ -2,14 +2,20 @@ import json import os import socket import threading +import warnings from queue import Queue +from typing import Any, Dict + import tidevice import wda from flask import Flask, request from flask_cors import CORS from Entity.ResultData import ResultData +from Utils.ControlUtils import ControlUtils from Utils.ThreadManager import ThreadManager from script.ScriptManager import ScriptManager +from Entity.AnchorModel import AnchorModel +from Entity.Variables import anchorList, addModelToAnchorList app = Flask(__name__) CORS(app) @@ -81,21 +87,8 @@ def deviceList(): def deviceAppList(): param = request.get_json() udid = param["udid"] - t = tidevice.Device(udid) - - # 获取已安装的应用列表 - apps = [] - for app in t.installation.iter_installed(): - apps.append({ - "name": app.get("CFBundleDisplayName", "Unknown"), - "bundleId": app.get("CFBundleIdentifier", "Unknown"), - "version": app.get("CFBundleShortVersionString", "Unknown"), - "path": app.get("Path", "Unknown") - }) - - # 筛选非系统级应用(过滤掉以 com.apple 开头的系统应用) - non_system_apps = [app for app in apps if not app["bundleId"].startswith("com.apple")] - return ResultData(data=non_system_apps).toJson() + apps = ControlUtils.getDeviceAppList(udid) + return ResultData(data=apps).toJson() # 打开指定app @app.route('/launchApp', methods=['POST']) @@ -188,12 +181,36 @@ def stopScript(): ThreadManager.stop(udid) return ResultData(data="").toJson() -# AI聊天 -@app.route('/aiChat', methods=['POST']) -def aiChat(): - body = request.get_json() - udid = body.get("udid") +# 传递主播数据 +@app.route('/passAnchorData', methods=['POST']) +def passAnchorData(): + data: Dict[str, Any] = request.get_json() + # 设备列表 + idList = data.get("deviceList", []) + # 主播列表 + acList = data.get("anchorList", []) + # 是否需要回复 + needReply = data.get("needReply", False) + # 添加主播数据 + addModelToAnchorList(acList) + # 启动线程,执行脚本 + for udid in idList: + manager = ScriptManager() + event = threading.Event() + # 启动脚本 + thread = threading.Thread(target=manager.greetNewFollowers, args=(udid, needReply, event)) + thread.start() + # 添加到线程管理 + ThreadManager.add(udid, thread, event) + return ResultData(data="").toJson() + +# 添加临时数据 +@app.route("/addTempAnchorData", methods=['POST']) +def addTempAnchorData(): + data = request.get_json() + addModelToAnchorList(data) + return ResultData(data="").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 6373daf..53c982a 100644 --- a/Utils/AiUtils.py +++ b/Utils/AiUtils.py @@ -4,10 +4,8 @@ import re import cv2 import numpy as np import wda - from Utils.LogManager import LogManager - # 工具类 class AiUtils(object): @@ -225,3 +223,4 @@ class AiUtils(object): print(e) return None +# AiUtils.screenshot() \ No newline at end of file diff --git a/Utils/ControlUtils.py b/Utils/ControlUtils.py index 682be17..a7bc6f0 100644 --- a/Utils/ControlUtils.py +++ b/Utils/ControlUtils.py @@ -1,3 +1,4 @@ +import tidevice from wda import Client from Entity.Variables import tikTokApp @@ -7,13 +8,47 @@ from Utils.LogManager import LogManager # 页面控制工具类 class ControlUtils(object): + # 获取设备上的app列表 + @classmethod + def getDeviceAppList(self, udid): + device = tidevice.Device(udid) + # 获取已安装的应用列表 + apps = [] + for app in device.installation.iter_installed(): + apps.append({ + "name": app.get("CFBundleDisplayName", "Unknown"), + "bundleId": app.get("CFBundleIdentifier", "Unknown"), + "version": app.get("CFBundleShortVersionString", "Unknown"), + "path": app.get("Path", "Unknown") + }) + + # 筛选非系统级应用(过滤掉以 com.apple 开头的系统应用) + noSystemApps = [app for app in apps if not app["bundleId"].startswith("com.apple")] + return noSystemApps + # 打开Tik Tok @classmethod - def openTikTok(cls, session: Client): + def openTikTok(cls, session: Client, udid): + apps = cls.getDeviceAppList(udid) + tk = "" + for app in apps: + if app.get("name", "") == "TikTok": + tk = app.get("bundleId", "") + currentApp = session.app_current() - if currentApp != tikTokApp: + if currentApp != tk: session.app_start(tikTokApp) + # 关闭Tik Tok + @classmethod + def closeTikTok(cls, session: Client, udid): + apps = cls.getDeviceAppList(udid) + tk = "" + for app in apps: + if app.get("name", "") == "TikTok": + tk = app.get("bundleId", "") + session.app_stop(tk) + # 返回 @classmethod def clickBack(cls, session: Client): @@ -63,5 +98,16 @@ class ControlUtils(object): # return False + # 点击搜索 + @classmethod + def clickSearch(cls, session: Client): + obj = session.xpath("//*[@name='搜索']") + try: + if obj.label == "搜索": + return obj + else: + return None + except Exception as e: + print(e) + return None -# ControlUtils.backAction("eca000fcb6f55d7ed9b4c524055214c26a7de7aa") \ No newline at end of file diff --git a/resources/eca000fcb6f55d7ed9b4c524055214c26a7de7aa/bgv.png b/resources/eca000fcb6f55d7ed9b4c524055214c26a7de7aa/bgv.png index 6e32b37..f00ba37 100644 Binary files a/resources/eca000fcb6f55d7ed9b4c524055214c26a7de7aa/bgv.png and b/resources/eca000fcb6f55d7ed9b4c524055214c26a7de7aa/bgv.png differ diff --git a/resources/search.png b/resources/search.png index f68d915..8aef0b5 100644 Binary files a/resources/search.png and b/resources/search.png differ diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..92687f3 Binary files /dev/null and b/screenshot.png differ diff --git a/script/ScriptManager.py b/script/ScriptManager.py index ec33dac..f59fa0d 100644 --- a/script/ScriptManager.py +++ b/script/ScriptManager.py @@ -1,10 +1,13 @@ import random +import threading import time +from enum import Enum import wda import os from Utils.AiUtils import AiUtils from Utils.ControlUtils import ControlUtils from Utils.LogManager import LogManager +from Entity.Variables import anchorList, removeModelFromAnchorList # 脚本管理类 @@ -28,10 +31,14 @@ class ScriptManager(): def growAccount(self, udid, event): client = wda.USBClient(udid) session = client.session() - session.appium_settings({"snapshotMaxDepth": 15}) + session.appium_settings({"snapshotMaxDepth": 0}) - # 检测当前打开的是否是Tik Tok。如果不是就打开 - ControlUtils.openTikTok(session) + # 先关闭Tik Tok + ControlUtils.closeTikTok(session, udid) + time.sleep(1) + + # 重新打开Tik Tok + ControlUtils.openTikTok(session, udid) time.sleep(3) # 创建udid名称的目录 @@ -93,6 +100,86 @@ class ScriptManager(): print(f"发生异常:{e}") client.swipe_up() + + # 观看直播 + def viewLive(self): + pass + + # 关注打招呼 + def greetNewFollowers(self, udid, needReply, event): + client = wda.USBClient(udid) + session = client.session() + session.appium_settings({"snapshotMaxDepth": 15}) + + # 先关闭Tik Tok + ControlUtils.closeTikTok(session, udid) + time.sleep(1) + + # 重新打开Tik Tok + ControlUtils.openTikTok(session, udid) + time.sleep(3) + + # 点击搜索按钮 + searchButton = ControlUtils.clickSearch(session) + if searchButton is not None: + searchButton.click() + else: + print("没找到搜索按钮") + return + + # 搜索框 + input = session.xpath('//XCUIElementTypeSearchField') + + # 获取一个主播 + anchor = anchorList[0] + aid = anchor.anchorId + + # 如果找到了输入框,就点击并且输入内容 + if input.exists: + input.click() + time.sleep(1) + input.set_text(aid + "\n") + + # 切换UI查找深度 + session.appium_settings({"snapshotMaxDepth": 25}) + + # 点击进入主播首页 + session.xpath( + '(//XCUIElementTypeCollectionView[@name="TTKSearchCollectionComponent"]' + '//XCUIElementTypeCell' + '//XCUIElementTypeButton[following-sibling::XCUIElementTypeButton[@name="关注" or @label="关注"]])[1]' + ).get(timeout=5).tap() + + time.sleep(3) + + # 向上划一点 + r = client.swipe_up() + print(r) + + # 分析页面节点 + # print(session.source()) + + # 观看主播视频 + def viewAnchorVideo(): + pass + + viewAnchorVideo() + else: + pass + # 功能待完善 + + + + # while not event.is_set() and len(anchorList) > 0: + # anchor = anchorList[0] + # aid = anchor.anchorId + # + # ControlUtils.clickSearch(session, udid) + # + # # 删除数据 + # removeModelFromAnchorList(anchor) + # print(len(anchorList)) + # manager = ScriptManager() # manager.growAccount("eca000fcb6f55d7ed9b4c524055214c26a7de7aa")