diff --git a/Flask/FlaskService.py b/Flask/FlaskService.py index f6b29c2..cf83f56 100644 --- a/Flask/FlaskService.py +++ b/Flask/FlaskService.py @@ -8,6 +8,7 @@ import wda from flask import Flask, request from flask_cors import CORS from Entity.ResultData import ResultData +from Utils.ThreadManager import ThreadManager from script.ScriptManager import ScriptManager app = Flask(__name__) @@ -170,9 +171,29 @@ def growAccount(): body = request.get_json() udid = body.get("udid") + manager = ScriptManager() + event = threading.Event() # 启动脚本 - threading.Thread(target=ScriptManager.growAccount, args=(udid,)).start() + thread = threading.Thread(target=manager.growAccount, args=(udid,event)) + thread.start() + # 添加到线程管理 + ThreadManager.add(udid, thread, event) return ResultData(data="").toJson() +# 停止脚本 +@app.route("/stopScript", methods=['POST']) +def stopScript(): + body = request.get_json() + udid = body.get("udid") + ThreadManager.stop(udid) + return ResultData(data="").toJson() + +# AI聊天 +@app.route('/aiChat', methods=['POST']) +def aiChat(): + body = request.get_json() + udid = body.get("udid") + + if __name__ == '__main__': app.run("0.0.0.0", port=5000, debug=True, use_reloader=False) diff --git a/Module/DeviceInfo.py b/Module/DeviceInfo.py index 88e0ab7..d8e21a6 100644 --- a/Module/DeviceInfo.py +++ b/Module/DeviceInfo.py @@ -74,7 +74,7 @@ class Deviceinfo(object): d = wda.USBClient(identifier, 8100) d.app_start(WdaAppBundleId) d.home() - # time.sleep(2) + time.sleep(2) # d.app_start(tikTokApp) target = self.relayDeviceScreenPort() self.pidList.append({ diff --git a/Module/Main.py b/Module/Main.py index 8248797..0818a43 100644 --- a/Module/Main.py +++ b/Module/Main.py @@ -1,7 +1,11 @@ from Module.DeviceInfo import Deviceinfo from Module.FlaskSubprocessManager import FlaskSubprocessManager +from Utils.LogManager import LogManager if __name__ == "__main__": + + LogManager.clearLogs() + print("启动flask") manager = FlaskSubprocessManager.get_instance() manager.start() diff --git a/Utils/AiUtils.py b/Utils/AiUtils.py index 04c75f0..53d4e8c 100644 --- a/Utils/AiUtils.py +++ b/Utils/AiUtils.py @@ -1,11 +1,53 @@ import os import re +import cv2 +import numpy as np # 工具类 class AiUtils(object): + # 在屏幕中找到对应的图片 + @classmethod + def findImageInScreen(cls, target): + # 加载原始图像和模板图像 + image_path = AiUtils.imagePathWithName("bgv") # 替换为你的图像路径 + template_path = AiUtils.imagePathWithName(target) # 替换为你的模板路径 + + # 读取图像和模板,确保它们都是单通道灰度图 + image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) + template = cv2.imread(template_path, cv2.IMREAD_GRAYSCALE) + + if image is None: + raise ValueError("背景图无法加载") + + if template is None: + raise ValueError("模板图无法加载") + + + # 获取模板的宽度和高度 + w, h = template.shape[::-1] + + # 使用模板匹配方法 + res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED) + threshold = 0.7 # 匹配度阈值,可以根据需要调整 + loc = np.where(res >= threshold) + + # 检查是否有匹配结果 + if loc[0].size > 0: + # 取第一个匹配位置 + pt = zip(*loc[::-1]).__next__() # 获取第一个匹配点的坐标 + center_x = int(pt[0] + w // 2) + center_y = int(pt[1] + h // 2) + # print(f"第一个匹配到的小心心中心坐标: ({center_x}, {center_y})") + return center_x, center_y + else: + print("未找到匹配的目标") + return -1, -1 + + + # 使用正则查找字符串中的数字 @classmethod def findNumber(cls, str): # 使用正则表达式匹配数字 @@ -15,15 +57,91 @@ class AiUtils(object): return None # 如果没有找到数字,返回 None - # 根据名称获取图片地址 + # 选择截图 @classmethod - def pathWithName(cls, name): + def screenshot(cls): + image_path = AiUtils.imagePathWithName("bgv") # 替换为你的图像路径 + image = cv2.imread(image_path) + + # 如果图像过大,缩小显示 + scale_percent = 50 # 缩小比例 + width = int(image.shape[1] * scale_percent / 100) + height = int(image.shape[0] * scale_percent / 100) + dim = (width, height) + resized_image = cv2.resize(image, dim, interpolation=cv2.INTER_AREA) + + # 创建一个窗口并显示缩小后的图像 + cv2.namedWindow("Image") + cv2.imshow("Image", resized_image) + print("请在图像上选择爱心图标区域,然后按Enter键确认。") + + # 使用selectROI函数手动选择区域 + roi = cv2.selectROI("Image", resized_image, showCrosshair=True, fromCenter=False) + + # 将ROI坐标按原始图像尺寸放大 + x, y, w, h = roi + x = int(x * image.shape[1] / resized_image.shape[1]) + y = int(y * image.shape[0] / resized_image.shape[0]) + w = int(w * image.shape[1] / resized_image.shape[1]) + h = int(h * image.shape[0] / resized_image.shape[0]) + + # 根据选择的ROI提取爱心图标 + if w > 0 and h > 0: # 确保选择的区域有宽度和高度 + heart_icon = image[y:y + h, x:x + w] + + # 转换为HSV颜色空间 + hsv = cv2.cvtColor(heart_icon, cv2.COLOR_BGR2HSV) + + # 定义红色的HSV范围 + lower_red1 = np.array([0, 120, 70]) + upper_red1 = np.array([10, 255, 255]) + lower_red2 = np.array([170, 120, 70]) + upper_red2 = np.array([180, 255, 255]) + + # 创建掩模 + mask1 = cv2.inRange(hsv, lower_red1, upper_red1) + mask2 = cv2.inRange(hsv, lower_red2, upper_red2) + mask = mask1 + mask2 + + # 反转掩模,因为我们想要的是爱心图标,而不是背景 + mask_inv = cv2.bitwise_not(mask) + + # 应用掩模 + heart_icon = cv2.bitwise_and(heart_icon, heart_icon, mask=mask_inv) + + # 创建一个全透明的背景 + height, width, channels = heart_icon.shape + roi = np.zeros((height, width, channels), dtype=np.uint8) + + # 将爱心图标粘贴到透明背景上 + for c in range(channels): + roi[:, :, c] = np.where(mask_inv == 255, heart_icon[:, :, c], roi[:, :, c]) + + # 图片名称 + imgName = "temp.png" + + # 保存结果 + cv2.imwrite(imgName, roi) + + # 显示结果 + cv2.imshow("Heart Icon with Transparent Background", roi) + cv2.waitKey(0) + cv2.destroyAllWindows() + else: + print("未选择有效区域。") + + + # 根据名称获取图片完整地址 + @classmethod + def imagePathWithName(cls, name): current_file_path = os.path.abspath(__file__) # 获取当前文件所在的目录(即script目录) current_dir = os.path.dirname(current_file_path) # 由于script目录位于项目根目录下一级,因此需要向上一级目录移动两次 project_root = os.path.abspath(os.path.join(current_dir, '..')) # 构建资源文件的完整路径,向上两级目录,然后进入 resources 目录 - resource_path = os.path.abspath(os.path.join(project_root, 'resources', name + ".jpg")).replace('/', '\\') + resource_path = os.path.abspath(os.path.join(project_root, 'resources', name + ".png")).replace('/', '\\') return resource_path + +# AiUtils.screenshot() \ No newline at end of file diff --git a/Utils/ControlUtils.py b/Utils/ControlUtils.py new file mode 100644 index 0000000..0b23f25 --- /dev/null +++ b/Utils/ControlUtils.py @@ -0,0 +1,68 @@ +from wda import Client + +from Entity.Variables import tikTokApp +from Utils.AiUtils import AiUtils +from Utils.LogManager import LogManager + +# 页面控制工具类 +class ControlUtils(object): + + # 打开Tik Tok + @classmethod + def openTikTok(cls, session: Client): + currentApp = session.app_current() + if currentApp != tikTokApp: + session.app_start(tikTokApp) + + # 返回 + @classmethod + def clickBack(cls, session: Client): + x, y = AiUtils.findImageInScreen("back") + if x > 0: + r = session.swipe_right() + if r.status == 0: + return True + else: + return False + else: + print("本页面无法返回") + LogManager.info("没有返回按钮") + return False + + # 点赞 + @classmethod + def clickLike(cls, session: Client): + scale = session.scale + x, y = AiUtils.findImageInScreen("add") + print(x,y) + if x > -1: + print("找到目标了") + r = session.click(x // scale, y // scale + 50) + if r.status == 0: + return True + else: + return False + else: + print("没有找到目标") + return False + + # 点击评论 + # @classmethod + # def clickComment(cls, session: Client): + # scale = session.scale + # x, y = AiUtils.findImageInScreen("add") + # print(x,y) + # if x > -1: + # print("找到目标了") + # r = session.click(x // scale, y // scale + 120) + # if r.status == 0: + # return True + # else: + # return False + # else: + # print("没有找到目标") + # return False + + + +# ControlUtils.backAction("eca000fcb6f55d7ed9b4c524055214c26a7de7aa") \ No newline at end of file diff --git a/Utils/ThreadManager.py b/Utils/ThreadManager.py new file mode 100644 index 0000000..e1381d7 --- /dev/null +++ b/Utils/ThreadManager.py @@ -0,0 +1,27 @@ +from threading import Thread, Event + +from Utils.LogManager import LogManager +from script.ScriptManager import ScriptManager + + +class ThreadManager(): + threads = {} + + @classmethod + def add(cls, udid, t: Thread, stopEvent: Event): + if udid in cls.threads: + print("▲ 线程已存在") + return + cls.threads[udid] = {"thread": t, "stopEvent": stopEvent} + + + @classmethod + def stop(cls, udid): + info = cls.threads[udid] + if info: + info["stopEvent"].set() # 停止线程 + info["thread"].join(timeout=3) # 等待线程退出 + del cls.threads[udid] + LogManager.info("停止线程成功") + else: + LogManager.info("无此线程,无需关闭") \ No newline at end of file diff --git a/resources/add.jpg b/resources/add.jpg deleted file mode 100644 index bf32874..0000000 Binary files a/resources/add.jpg and /dev/null differ diff --git a/resources/add.png b/resources/add.png new file mode 100644 index 0000000..aa6b19e Binary files /dev/null and b/resources/add.png differ diff --git a/resources/advertisement.png b/resources/advertisement.png new file mode 100644 index 0000000..87de404 Binary files /dev/null and b/resources/advertisement.png differ diff --git a/resources/back.png b/resources/back.png new file mode 100644 index 0000000..a679439 Binary files /dev/null and b/resources/back.png differ diff --git a/resources/bgv.jpg b/resources/bgv.jpg deleted file mode 100644 index 9224af7..0000000 Binary files a/resources/bgv.jpg and /dev/null differ diff --git a/resources/bgv.png b/resources/bgv.png new file mode 100644 index 0000000..abbcebc Binary files /dev/null and b/resources/bgv.png differ diff --git a/resources/comment.jpg b/resources/comment.jpg deleted file mode 100644 index e8140b5..0000000 Binary files a/resources/comment.jpg and /dev/null differ diff --git a/resources/comment.png b/resources/comment.png new file mode 100644 index 0000000..7043f81 Binary files /dev/null and b/resources/comment.png differ diff --git a/resources/like.jpg b/resources/like.jpg deleted file mode 100644 index b749a85..0000000 Binary files a/resources/like.jpg and /dev/null differ diff --git a/resources/like.png b/resources/like.png new file mode 100644 index 0000000..46c6c25 Binary files /dev/null and b/resources/like.png differ diff --git a/resources/more.jpg b/resources/more.jpg deleted file mode 100644 index be17c44..0000000 Binary files a/resources/more.jpg and /dev/null differ diff --git a/resources/search.jpg b/resources/search.jpg deleted file mode 100644 index 3940ba5..0000000 Binary files a/resources/search.jpg and /dev/null differ diff --git a/resources/search.png b/resources/search.png new file mode 100644 index 0000000..f68d915 Binary files /dev/null and b/resources/search.png differ diff --git a/script/ScriptManager.py b/script/ScriptManager.py index d7d5b7c..14301d6 100644 --- a/script/ScriptManager.py +++ b/script/ScriptManager.py @@ -1,7 +1,9 @@ - +import random +import time import wda from Utils.AiUtils import AiUtils +from Utils.ControlUtils import ControlUtils # 脚本管理类 @@ -19,23 +21,52 @@ class ScriptManager(): def __init__(self): super().__init__() - # 脚本开关 - self.running = False self.initialized = True # 标记已初始化 # 养号 - def growAccount(self, udid): + def growAccount(self, udid, event): client = wda.USBClient(udid) session = client.session() session.appium_settings({"snapshotMaxDepth": 15}) - numberLabel = session.xpath("//*[@name='a11y_vo_inbox']") - if numberLabel: - content = numberLabel.label - number = AiUtils.findNumber(content) - print(number) - else: - print("没找到") -manager = ScriptManager() -manager.growAccount("eca000fcb6f55d7ed9b4c524055214c26a7de7aa") + # 检测当前打开的是否是Tik Tok。如果不是就打开 + ControlUtils.openTikTok(session) + time.sleep(3) + + # 假设此时有个开关 + while not event.is_set(): + try: + img = client.screenshot() + filePath = "resources/bgv.png" + img.save(filePath) + except Exception as e: + print(e) + + # 判断视频类型。普通视频、广告、直播,只有普通视频停留。其他视频全部划走 + addX, addY = AiUtils.findImageInScreen("add") + # 如果找到普通视频 + if addX > 0: + needLike = random.randint(0, 10) + + # 随机时间间隔 + videoTime = random.randint(2,5) + time.sleep(videoTime) + + # 百分之三的概率点赞 + if needLike < 3: + ControlUtils.clickLike(session) + + # 随机时间间隔 + # videoTime = random.randint(10,30) + # time.sleep(videoTime) + session.swipe_up() + else: + # 随机时间 + nextTime = random.randint(1,5) + time.sleep(nextTime) + session.swipe_up() + continue + +# manager = ScriptManager() +# manager.growAccount("eca000fcb6f55d7ed9b4c524055214c26a7de7aa")