AutojsPro - 豆角酱油(QQ信息组件)1. openQQ(qqNum)方法跳转 QQ 名片2. sendText(message)发送文本3. 点击控件 1.btn_page() 名片页点击发消息2.btn_send() 消息框点击发送3.menu() 打开+4.function clickGiftButton() 点击礼物5.closeAd() 关闭广告4. btn_over(x,y)基于坐标换算成当前屏幕上的对应坐标,实现跨机型的比例点击5. readContactList(filePath)从文件读取联系人信息6. sendMessage(qqNum, message)发送消息给指定QQ7. findAnyImg(imgArray, num) 屏幕上识别目标图片数组中的任意一张,找到后按指定次数点击8. logToFile(message)记录日志到文件(追加模式)9. 常用服务10. getRandomLineFromFile(filePath)方法从文件中随机读取一行文本,并忽略行首的数字序号和标点11.saveToFile(content, fileName = null, dir = "")方法保存内容到文件(带错误处理和智能命名)
xxxxxxxxxx// 定义跳转 QQ 名片的函数,接收 QQ 号码作为参数function openQQ(qqNum) { // try-catch 用于捕获可能出现的异常,避免程序因错误崩溃 try { // 调用 app 的 startActivity 方法,发起跳转动作 // action: "VIEW" 表示执行查看操作 // data 拼接 QQ 名片跳转的协议,通过 mqqapi 协议传递 QQ 号等参数,实现打开对应 QQ 名片 // packageName 指定要跳转的应用包名,这里是手机 QQ 的包名 "com.tencent.mobileqq" app.startActivity({ action: "VIEW", data: "mqqapi://card/show_pslcard?src_type=internal&version=1&uin=" + qqNum + "&card_type=person&source=external", packageName: "com.tencent.mobileqq" }); // 线程休眠 3000 毫秒(3 秒),等待跳转操作完成、应用启动 sleep(3000); // 获取当前前台运行的应用包名,判断是否等于手机 QQ 的包名 // 如果等于,说明跳转成功进入了 QQ ,返回 true ;否则返回 false return currentPackage() === "com.tencent.mobileqq"; } catch (e) { // 如果 try 块中代码执行出错,进入 catch 块 // 弹出 toast 提示跳转 QQ 失败,并显示具体的错误信息 toast("跳转QQ失败: " + e); // 返回 false 表示跳转操作失败 return false; }}
xxxxxxxxxx// 发送文本function sendText(message) { try { let inputArea = className("EditText").findOne(5000); if (!inputArea) { // 尝试其他方式查找输入框 inputArea = descMatches("发送消息|输入消息").findOne(2000); } if (!inputArea) { toast("未找到输入框"); return false; } inputArea.click(); sleep(1000); // 清空输入框 inputArea.setText(""); sleep(500); // 输入文本 setText(message); sleep(1500); // 点击发送按钮 let sendBtn = text("发送").findOne(2000) || desc("发送").findOne(2000) || id("com.tencent.mobileqq:id/fun_btn").findOne(2000); if (sendBtn) { sendBtn.click(); return true; } // 如果找不到发送按钮,使用回车键发送 KeyCode("KEYCODE_ENTER"); return true; } catch (e) { toast("发送文本失败: " + e); return false; }}
xxxxxxxxxx//名片页点击发消息function btn_page(){ let target = text("发消息").id("p0b").findOne(5000); if (target) { target.click(); toast("已点击控件"); } else { toast("5秒内未找到控件"); }}
//消息框点击发送function btn_send(){ // 定位并点击控件(最多等待5秒) let sendBtn = text("发送").id("send_btn").findOne(); if (sendBtn) { sendBtn.click(); toast("已点击发送按钮"); } else { toast("5秒内未找到发送按钮"); } }
// 展开功能菜单 function menu(){ let menu = desc('更多功能').findOne(3000); if (menu) { menu.click(); toast("菜单已展开"); } else { click(device.width - 50, 100); // 右上角菜单 } sleep(500);}
/** * 尝试定位并点击"礼物"按钮 * @returns {boolean} 是否成功点击礼物按钮 */function clickGiftButton() { // 等待界面加载完成 sleep(500); // 尝试通过desc或text定位礼物按钮(设置超时时间为3秒) let giftButton = desc("礼物").findOne(3000) || text("礼物").findOne(3000); if (giftButton) { // 增加点击前的确认,避免误触 log("已定位到礼物按钮,准备点击"); // 点击按钮并等待页面跳转 let isClicked = giftButton.click(); sleep(1000); if (isClicked) { toast("成功进入礼物页"); return true; } else { toast("点击礼物按钮失败"); return false; } } else { // 记录详细的错误信息 toast("错误:找不到礼物入口,可能界面元素已变化"); return false; }}
function closeAd(){ // 尝试关闭广告 let closeAd = desc('关闭广告').findOne(3000); if (closeAd) { closeAd.click(); }}
xxxxxxxxxxfunction btn_over(x,y){// 原坐标(基于原分辨率的点)var originalX = x;var originalY = y;// 原分辨率(记录这个点所在的屏幕尺寸)var originalWidth = 1440;var originalHeight = 3200;// 获取当前设备的屏幕尺寸var currentWidth = device.width;var currentHeight = device.height;// 计算比例缩放后的坐标(四舍五入取整数)var clickX = Math.round(originalX * (currentWidth / originalWidth));var clickY = Math.round(originalY * (currentHeight / originalHeight));// 执行点击click(clickX, clickY);console.log("适配后点击坐标:", clickX, clickY);}btn_over(1085,3096)//点击表情sleep(1000)btn_over(600,3124)//点击收藏表情sleep(1000)btn_over(920,2370)//发送第一张
xxxxxxxxxx/** * 从文件读取联系人信息 * @param {string} filePath 文件路径 * @returns {Array} 联系人数组 */function readContactList(filePath) { try { // 检查文件是否存在 if (!files.exists(filePath)) { toast("联系人文件不存在: " + filePath); exit(); }
// 读取文件内容 let content = files.read(filePath); if (!content) { toast("文件内容为空: " + filePath); exit(); }
// 按行分割内容 let lines = content.split(/\r?\n/); let contacts = [];
// 解析每行数据 for (let line of lines) { // 跳过空行和注释行 if (!line.trim() || line.trim().startsWith("#")) { continue; }
// 分割地区、昵称和QQ号(支持多种分隔符) let parts = line.split(/[|,-]/); if (parts.length < 2) { continue; }
// 提取并处理地区和QQ号 let region = parts[0].trim(); let qq = parts[parts.length - 1].replace(/\D/g, '');
// 验证并添加有效联系人 if (region && qq.length >= 5) { contacts.push({ region: region, qq: qq }); } }
// 检查是否有有效联系人 if (contacts.length === 0) { toast("未找到有效联系人"); exit(); }
return contacts; } catch (e) { toast("读取联系人文件失败: " + e); exit(); }}
xxxxxxxxxx/** * 发送消息给指定QQ * @param {string} qqNum - QQ号码 * @param {string} message - 要发送的消息 */function sendMessage(qqNum, message) { try { // 记录开始时间 let startTime = new Date().getTime();
// 打开QQ并检查是否成功 if (!openQQ(qqNum)) { toast("无法跳转到QQ: " + qqNum); return; } sleep(3000);
// 查找发消息按钮(多种匹配方式) let sendMsgBtn = textContains("发消息").findOne(3000) || descContains("发消息").findOne(3000) || className("android.widget.Button").text("发消息").findOne(3000);
// 点击发消息按钮或屏幕底部 if (sendMsgBtn) { sendMsgBtn.click(); sleep(2000); } else { // 尝试点击屏幕底部中间位置 click(device.width / 2, device.height - 100); sleep(2000); }
// 发送文本消息 if (!sendText(message)) { toast("文本消息发送失败"); return; } sleep(1500);
btn_over(1085,3096)//点击表情 sleep(1000) btn_over(600,3124)//点击收藏表情 sleep(1000) btn_over(920,2370)//发送第一张 sleep(1500);
// 执行QQ操作
// 返回主界面 back(); sleep(1000); back(); sleep(1000);
// 计算并显示耗时 let timeUsed = (new Date().getTime() - startTime) / 1000; toast("发送给 " + qqNum + " 完成,耗时: " + timeUsed.toFixed(1) + "秒"); } catch (e) { toast("发送消息给 " + qqNum + " 失败: " + e); }}
xxxxxxxxxx/** * 图片识别与点击函数 - 功能:在屏幕上识别目标图片数组中的任意一张,找到后按指定次数点击 * @param {Array<string>} imgArray - 目标图片路径数组(需要识别的图片路径集合) * @param {number} num - 找到图片后需要点击的次数 * @returns {boolean} 识别并点击成功返回true,未找到或识别失败返回false */function findAnyImg(imgArray, num) { // 存储成功读取的目标图片对象(用于后续识别) let targetImages = []; // 遍历图片路径数组,读取每张图片并存储 for (let i = 0; i < imgArray.length; i++) { // 读取图片文件:images.read()接收图片路径,返回图片对象 let img = images.read(imgArray[i]); // 如果图片读取成功,添加到目标图片数组 if (img) { targetImages.push(img); } else { // 读取失败时,弹出提示显示失败的图片路径 toast("读取图片失败: " + imgArray[i]); } } // 检查是否有成功读取的目标图片 if (targetImages.length === 0) { toast("所有图片读取失败"); return false; // 无可用图片,直接返回失败 }
// 配置最大识别尝试次数(防止无限循环),初始化"是否找到图片"标记 let maxTry = 10, found = false; // 循环尝试识别图片,最多尝试maxTry次 for (let attempt = 0; attempt < maxTry; attempt++) { // 截取当前屏幕画面,用于后续识别 let screenshot = images.captureScreen(); // 如果截图失败,等待500毫秒后继续下一次尝试 if (!screenshot) { sleep(500); continue; }
// 存储找到的图片坐标点(初始为null,未找到) let foundPoint = null; // 遍历目标图片数组,逐个与截图比对 for (let i = 0; i < targetImages.length; i++) { // 识别图片:在截图中查找当前目标图片 // threshold: 0.7 表示匹配度阈值(70%以上视为匹配成功) let point = images.findImage(screenshot, targetImages[i], { threshold: 0.7 }); // 如果找到匹配的图片,记录坐标并退出循环 if (point) { foundPoint = point; break; } }
// 如果找到目标图片的坐标 if (foundPoint) { // 按指定次数点击该坐标 for (let clickCount = 0; clickCount < num; clickCount++) { click(foundPoint.x, foundPoint.y); // 点击坐标(x,y) sleep(300); // 每次点击后等待300毫秒,防止连续点击过快 } // 标记为找到图片 found = true; // 释放截图资源(避免内存占用) screenshot.recycle(); // 退出识别循环(已找到目标,无需继续尝试) break; }
// 未找到图片时,释放截图资源 screenshot.recycle(); // 等待500毫秒后进行下一次识别尝试 sleep(500); }
// 释放所有目标图片资源(遍历数组,逐个释放) targetImages.forEach(function(img) { img.recycle(); }); // 返回最终结果:是否成功找到并点击图片 return found;}
xxxxxxxxxx/** * 记录日志到文件(追加模式) * @param {string} message - 日志内容 */function logToFile(message) { const LOG_FILE = "weather_send_log.txt"; const timestamp = new Date().toLocaleString(); const logEntry = `[${timestamp}] ${message}\n`;
// 追加日志到文件 files.append(LOG_FILE, logEntry);}
无障碍启动服务1
xxxxxxxxxxauto.waitFor();
// 检查无障碍服务if (!auto.service) { app.startActivity({ action: "android.settings.ACCESSIBILITY_SETTINGS" }); toast("请开启无障碍服务"); while (!auto.service) sleep(1000); toast("无障碍服务已开启");}
// 申请截图权限if (!requestScreenCapture()) { toast("请求截图失败"); exit();}无障碍启动服务2
xxxxxxxxxxsleep(1000)// 检查无障碍服务是否开启if (!auto.service) { // 如果无障碍服务未开启,则跳转到无障碍设置页面 app.startActivity({ action: "android.settings.ACCESSIBILITY_SETTINGS" }); // 提示用户开启服务 toast("请找到本应用并开启无障碍服务"); // 循环检查直到服务开启 while (!auto.service) { sleep(1000); } // 服务已开启,提示用户 toast("无障碍服务已开启,继续执行脚本");}
// 无障碍服务已开启,继续执行后面的代码toast("开始执行主脚本");// 在这里添加你的主要代码...
// 示例代码console.log("无障碍服务已开启,脚本正常运行");截图权限
xxxxxxxxxx/* 申请屏幕截图权限. */if(!requestScreenCapture()){ toast("请求截图失败"); exit();}屏幕常亮
xxxxxxxxxx// 保持屏幕常亮:调用device对象的keepScreenOn()方法,确保脚本运行期间屏幕不会自动熄灭// 避免因屏幕休眠导致的界面元素无法识别或操作中断device.keepScreenOn();
// 取消屏幕常亮:脚本主逻辑执行完毕后,调用cancelKeepingAwake()方法,恢复系统默认的屏幕休眠设置// 避免脚本结束后屏幕一直亮着消耗设备电量device.cancelKeepingAwake();调用shizuki ADB权限
xxxxxxxxxx/* 检查是否有adb权限 * $shell.checkAccess("adb") 用于检测当前脚本是否拥有adb权限 * adb权限是执行部分系统级操作(如强制关闭应用、模拟系统输入)的必要条件 * 缺少此权限会导致后续依赖adb的命令执行失败 */var isAdb = $shell.checkAccess("adb");// 如果没有adb权限,弹出提示并退出脚本if (!isAdb) { alert("请先使用$shell授权,再运行"); // 提示用户需要通过$shell工具授予adb权限 exit(); // 终止脚本执行,因为核心功能依赖adb权限}
// 等待1秒,确保权限检查和提示弹窗的操作完全完成// 避免因弹窗未关闭导致后续操作受干扰sleep(1000);
/* 申请屏幕截图权限 * requestScreenCapture() 向系统请求屏幕截图权限(属于敏感权限,需用户手动授权) * 许多自动化操作(如图片识别、界面元素定位)依赖屏幕截图功能 */if (!requestScreenCapture()) { toast("请求截图失败"); // 以轻量提示告知用户截图权限申请失败 exit(); // 终止脚本,因为截图是后续操作(如图片识别)的基础}
// 将shell操作默认切换到adb模式// 后续通过$shell执行的命令会默认使用adb方式运行,确保操作拥有足够权限// 适用于需要系统级权限的操作(如关闭其他应用)$shell.setDefaultOptions({ adb: true });
调用shizuki ADB权限下关闭程序
xxxxxxxxxx/** * 关闭指定应用程序(增强版) * @param {string} packageName 应用程序包名 * @param {number} [timeout=3000] 等待应用关闭的最大时间(毫秒) * @returns {boolean} 应用是否成功关闭 */function closeApp(packageName, timeout = 3000) { try { // 执行关闭应用命令 const result = $shell(`am force-stop ${packageName}`, true); // 检查命令执行状态 if (!result.success) { throw new Error(`命令执行失败: ${result.error}`); } // 验证应用是否已关闭(通过检查进程是否存在) const startTime = Date.now(); while (Date.now() - startTime < timeout) { // 通过ps命令检查应用进程是否还在运行 const psResult = $shell(`ps | grep ${packageName}`, true); // 如果没有找到匹配的进程,说明应用已关闭 if (!psResult.stdout.trim()) { return true; } sleep(300); // 等待一段时间后再次检查 } throw new Error(`超时: 应用 ${packageName} 未在 ${timeout}ms 内关闭`); } catch (e) { log(`关闭应用 ${packageName} 失败: ${e.message}`); return false; }}以下是对代码的详细解释:
从文件中随机读取一行文本,并忽略行首的数字序号和标点
@param {string} filePath 文件路径
@returns {string} 随机选择的文本行(已清理行首序号)
xxxxxxxxxx/** * 从文件中随机读取一行文本,并忽略行首的数字序号和标点 * @param {string} filePath 文件路径 * @returns {string} 随机选择的文本行(已清理行首序号) */function getRandomLineFromFile(filePath) { sleep(1000) try { // 读取文件内容 let content = files.read(filePath); if (!content) { toast("文件内容为空: " + filePath); return "每日分享:~o( =∩ω∩= )m"; // 默认文本 } // 分割为行数组并过滤空行 let lines = content.split(/\r?\n/).filter(line => line.trim() !== ""); if (lines.length === 0) { return "每日分享:~o( =∩ω∩= )m"; // 默认文本 } // 随机选择一行 let randomIndex = Math.floor(Math.random() * lines.length); let selectedLine = lines[randomIndex]; // 清理行首的序号和标点(如"1. ", "2) "等格式) let cleanedLine = selectedLine.replace(/^\s*\d+[.)]\s*/, ''); // 如果清理后内容为空,则使用原始行 return cleanedLine.trim() || selectedLine; } catch (e) { toast("读取文件失败: " + filePath); return "每日分享:~o( =∩ω∩= )m"; // 默认文本 }}以下是对代码的详细解释:
保存内容到文件(带错误处理和智能命名)
@param {string} content - 要保存的内容
@param {string} [fileName="report.txt"] - 文件名(可选,默认使用时间戳命名)
@param {string} [dir=""] - 保存目录(可选,默认为脚本当前目录)
@returns {boolean} 保存成功返回true,失败返回false
xxxxxxxxxx/** * 保存内容到文件(带错误处理和智能命名) * @param {string} content - 要保存的内容 * @param {string} [fileName="report.txt"] - 文件名(可选,默认使用时间戳命名) * @param {string} [dir=""] - 保存目录(可选,默认为脚本当前目录) * @returns {boolean} 保存成功返回true,失败返回false */function saveToFile(content, fileName = null, dir = "") { try { // 处理默认文件名(使用时间戳避免重复) if (!fileName) { const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); fileName = `report_${timestamp}.txt`; }
// 构建完整文件路径 const saveDir = dir || files.cwd(); const filePath = files.join(saveDir, fileName);
// 确保目录存在 if (!files.ensureDir(saveDir)) { throw new Error(`创建目录失败: ${saveDir}`); }
// 写入文件(使用utf-8编码) const success = files.write(filePath, content); if (!success) { throw new Error(`写入文件失败: ${filePath}`); }
// 显示保存成功提示(包含文件大小信息) const fileSize = (files.size(filePath) / 1024).toFixed(2); toast(`文件已保存: ${filePath} (${fileSize} KB)`); log(`保存成功: ${filePath}`); return true;
} catch (e) { // 错误处理(区分不同类型的错误) loge(`保存文件失败: ${e.message}`); toast(`保存失败: ${e.message}`); return false; }}