1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > auto.js小记:蚂蚁森林定时自动收取能量脚本

auto.js小记:蚂蚁森林定时自动收取能量脚本

时间:2019-05-08 11:43:04

相关推荐

auto.js小记:蚂蚁森林定时自动收取能量脚本

简介

Autojs是一个支持无障碍服务的Android平台上的JavaScript IDE,其发展目标是JsBox和Workflow。同时有VS Code 插件可提供基础的在桌面开发的功能。

下载地址:酷安

官方文档:https://hyb1996.github.io/AutoJs-Docs/

特性

由无障碍服务实现的简单易用的自动操作函数悬浮窗录制和运行更专业&强大的选择器API,提供对屏幕上的控件的寻找、遍历、获取信息、操作等。类似于Google的UI测试框架UiAutomator,您也可以把他当做移动版UI测试框架使用采用JavaScript为脚本语言,并支持代码补全、变量重命名、代码格式化、查找替换等功能,可以作为一个JavaScript IDE使用支持使用e4x编写界面,并可以将JavaScript打包为apk文件,您可以用它来开发小工具应用支持使用Root权限以提供更强大的屏幕点击、滑动、录制功能和运行shell命令。录制录制可产生js文件或二进制文件,录制动作的回放比较流畅提供截取屏幕、保存截图、图片找色、找图等函数可作为Tasker插件使用,结合Tasker可胜任日常工作流带有界面分析工具,类似Android Studio的LayoutInspector,可以分析界面层次和范围、获取界面上的控件信息

本软件与按键精灵等软件不同,主要区别是:

Auto.js主要以自动化、工作流为目标,更多地是方便日常生活工作,例如启动游戏时自动屏蔽通知、一键与特定联系人微信视频(知乎上出现过该问题,老人难以进行复杂的操作和子女进行微信视频)等Auto.js兼容性更好。以坐标为基础的按键精灵、脚本精灵很容易出现分辨率问题,而以控件为基础的Auto.js则没有这个问题Auto.js执行大部分任务不需要root权限。只有需要精确坐标点击、滑动的相关函数才需要root权限Auto.js可以提供界面编写等功能,不仅仅是作为一个脚本软件而存在

信息

官方论坛: 文档:可在这里查看在线文档。目前文档仍然不完善。示例:可在这里查看一些示例,或者直接在应用内查看和运行。

典型应用

关键补充说明

_show_floaty:floaty.window(layout) 指定悬浮窗的布局,创建并显示一个悬浮窗,返回一个FloatyWindow对象。该悬浮窗自带关闭、调整大小、调整位置按键,可根据需要调用setAdjustEnabled()函数来显示或隐藏。可以注释掉_show_floaty,如果用不好的话。"ui":ui模块提供了编写用户界面的支持。带有ui的脚本的的最前面必须使用"ui";指定ui模式,否则脚本将不会以ui模式运行。字符串"ui"的前面可以有注释、空行和空格[v4.1.0新增],但是不能有其他代码。也就是说,老版本autojs,"ui";前面不能有注释,不能有空格,空行等等。计时不对:锁屏(关屏不一定加锁)状态下,计时走时缓慢,或者干脆就不走时了。

如果想要在电脑而不是手机上开发Auto.js,可以使用VS Code以及相应的Auto.js插件使得在电脑上编辑的脚本能推送到手机运行,参见前面提到的VS code插件。

例子程序

根目录

update.js

importClass(java.io.File);importClass(java.io.IOException);importClass(java.io.InputStream);importClass(java.io.FileOutputStream);importClass(java.security.MessageDigest);/*** 向服务器查询发生更改的文件列表* * @param {*} server 提供更新文件的服务器地址* @param {*} path 本地需要更新文件的目录路径*/function GetChangedFileList(server, path) {const _server = server,_path = path,_processor = "generate_change_list.php";let _ignore_list = [".", "..", ".git"];const _generate_md5 = function(file) {let md5 = MessageDigest.getInstance("MD5");let hex = [];md5.update(file);md5.digest().forEach((byte) => {let temp = (0xFF & byte).toString(16);while (temp.length < 2) temp = "0" + temp;hex.push(temp);});return hex.join("");};const _generate_postdata = function func(path, data) {data = data || {};if (files.isEmptyDir(path)) return data;files.listDir(path).forEach(function(file_name) {let new_path = files.join(path, file_name);if (_ignore_list.indexOf(file_name) < 0) {if (!files.isDir(new_path)) {let file = files.readBytes(new_path);data[new_path.replace(_path, "")] = _generate_md5(file);} else {func(new_path, data);}}});return data;};return {get ignore_list() {return _ignore_list;},set ignore_list(arr) {_ignore_list = arr;},exec: function() {const postdata = _generate_postdata(_path);let url = _server + "/" + _processor;let res = http.postJson(url, postdata);if (res.statusCode != 200) {toastLog("请求失败: " + res.statusCode + " " + res.statusMessage);} else {return res.body.json();}}}}/*** 下载工具类,可监听下载进度* * @param {*} url 下载链接* @param {*} path 保存地址* @param {*} listener 下载监听*/function DownloadUtil(url, path, listener) {const _url = url,_path = path,_listener = listener;let _len = -1,_total_bytes = 0,_input_stream = null,_output_stream = null,_file_temp = null,_file_dir = null,_buffer = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, 2048);return {download: function() {let client = new OkHttpClient();let request = new Request.Builder().url(_url).get().addHeader("Accept-Encoding", "identity").build();client.newCall(request).enqueue(new Callback({onFailure: function(call, err) {toast("请求失败");console.error("请求失败:" + err);},onResponse: function(call, res) {try {if (res.code() != 200) throw res.code() + " " + res.message();_total_bytes = res.body().contentLength();_input_stream = res.body().byteStream();_file_temp = new File(_path);_file_dir = _file_temp.getParentFile();if(!_file_dir.exists()) _file_dir.mkdirs();_output_stream = new FileOutputStream(_file_temp);while ((_len = _input_stream.read(_buffer)) != -1) {_output_stream.write(_buffer, 0, _len);_listener.onDownloading((_len / _total_bytes) * 100);}_output_stream.flush();_listener.onDownloadSuccess();} catch (err) {_listener.onDownloadFailed(err);} finally {try {if (_input_stream != null)_input_stream.close();} catch (err) {toast("文件流处理失败");console.error("文件流处理失败:" + err);}}}}));}}}(function main() {let server = "";let local = "/sdcard/脚本/Ant-Forest-autoscript";let changed_files = new GetChangedFileList(server, local).exec();let remove_files = changed_files.remove || [];let update_files = changed_files.update || [];if (remove_files.length) {remove_files.forEach((file) => {let dir = files.join(local, file).replace(files.getName(file), '');files.remove(files.join(local, file));if (files.isEmptyDir(dir)) files.removeDir(dir);toastLog("更新完成");});} else if (update_files.length) {let downloadDialog = null;let res = http.get(server + "/ant-forest/CHANGELOG.md");if (res.statusCode != 200) {toastLog("请求失败: " + res.statusCode + " " + res.statusMessage);} else {dialogs.build({ title: "发现新版本",content: res.body.string(),positive: "更新",negative: "取消",}).on("positive", () => {downloadDialog = dialogs.build({title: "更新中...",negative: "取消",progress: {max: 100,showMinMax: true},autoDismiss: false}).on("negative", () => {downloadDialog.dismiss();downloadDialog = null;}).show();let counter = 0,total = 0,realurl = update_files.map((uri) => {return server + "/ant-forest" + uri}),abspath = update_files.map((uri) => {return files.join(local, uri)});let callback = {onDownloadSuccess: function(file) {if (counter == update_files.length - 1) {downloadDialog.dismiss();downloadDialog = null;toastLog("更新完成");} else {counter++;new DownloadUtil(realurl[counter], abspath[counter], callback).download();}},onDownloading: function(progress) {downloadDialog.setProgress((total += progress) / update_files.length);},onDownloadFailed: function(err) {toast("下载失败");console.error("下载失败:" + err);}};new DownloadUtil(realurl[counter], abspath[counter], callback).download();}).show();} } else {toastLog("当前已经是最新版本了");}})();

config.js

"ui";//带有ui的脚本的的最前面必须使用"ui";指定ui模式,否则脚本将不会以ui模式运行。var config = storages.create("ant_forest_config");if (!config.contains("color_offset")) {toastLog("使用默认配置");// 默认执行配置var default_conf = {color_offset: 50,password: "",help_friend: true,is_cycle: false,cycle_times: 10,delay_unlock: 1000,timeout_findOne: 1000,max_collect_wait_time: 20,white_list: []};// 储存默认配置到本地Object.keys(default_conf).forEach(function(key) {config.put(key, default_conf[key]);});}//一个表示给定对象的所有可枚举属性的字符串数组。//.forEach对每一个都做一定的操作function draw_view() {ui.layout(<ScrollView><vertical id="viewport"><frame><appbar><toolbar id="toolbar" title="执行配置" /></appbar></frame><vertical w="*" gravity="left" layout_gravity="left" margin="10"><text text="设置执行模式:" textColor="#666666" textSize="14sp" /><radiogroup id="exec_pattern" orientation="horizontal" margin="0 10"><radio text="计时" checked="{{!config.get('is_cycle')}}" /><radio text="循环" checked="{{config.get('is_cycle')}}" marginLeft="20" /></radiogroup><vertical visibility="{{config.get('is_cycle') ? 'visible' : 'gone'}}" w="*" gravity="left" layout_gravity="left"><text text="循环次数:" textColor="#666666" textSize="14sp" /><input id="cycle_times" inputType="number" text="{{config.get('cycle_times')}}" /></vertical></vertical><horizontal w="*" h="1sp" bg="#cccccc" margin="10 0"></horizontal><vertical w="*" gravity="left" layout_gravity="left" margin="10"><text text="帮好友收取:" textColor="#666666" textSize="14sp" /><radiogroup id="is_help_fris" orientation="horizontal" margin="0 10"><radio text="是" checked="{{config.get('help_friend')}}" /><radio text="否" checked="{{!config.get('help_friend')}}" marginLeft="20" /></radiogroup></vertical><horizontal w="*" h="1sp" bg="#cccccc" margin="10 0"></horizontal><vertical w="*" gravity="left" layout_gravity="left" margin="10"><text text="颜色偏移量:" textColor="#666666" textSize="14sp" /><input id="color_offset" inputType="number" text="{{config.get('color_offset')}}" /></vertical><vertical w="*" gravity="left" layout_gravity="left" margin="10"><text text="解锁密码:" textColor="#666666" textSize="14sp" /><input id="password" inputType="textPassword" text="{{config.get('password')}}" /></vertical><vertical w="*" gravity="left" layout_gravity="left" margin="10"><text text="最大等待时间(分钟):" textColor="#666666" textSize="14sp" /><input id="max_collect_wait_time" inputType="number" text="{{config.get('max_collect_wait_time')}}" /></vertical><vertical w="*" gravity="left" layout_gravity="left" margin="10"><text text="解锁操作时延:" textColor="#666666" textSize="14sp" /><input id="delay_unlock" inputType="number" text="{{config.get('delay_unlock')}}" /></vertical><vertical w="*" gravity="left" layout_gravity="left" margin="10"><text text="控件搜索超时:" textColor="#666666" textSize="14sp" /><input id="timeout_findOne" inputType="number" text="{{config.get('timeout_findOne')}}" /></vertical><vertical w="*" gravity="left" layout_gravity="left" margin="10"><text text="白名单:" textColor="#666666" textSize="14sp" /><text visibility="{{config.get('white_list').length == 0 ? 'visible' : 'gone'}}" w="*" h="80" gravity="center" layout_gravity="center" text="白名单为空" textColor="#999999" textSize="18sp" margin="0 20" bg="#eeeeee" /><frame><list id="white_list"><horizontal w="*" h="40" gravity="left" bg="#efefef" margin="0 5"><text id="name" layout_weight='1' h="30" gravity="left|center" layout_gravity="left|center" textSize="16sp" text="{{name}}" margin="10 0" /><card id="delete" w="30" h = "30" cardBackgroundColor = "#fafafa" cardCornerRadius = "15dp" layout_gravity="center" marginRight="10"><text textSize = "16dp" textColor = "#555555" gravity="center">×</text></card></horizontal></list></frame><button w="*" id="add" text="添加" gravity="center" layout_gravity="center" /></vertical><horizontal w="*" h="1sp" bg="#cccccc" margin="10 0"></horizontal><vertical w="*" gravity="left" layout_gravity="left" margin="10"><button w="*" id="clear" text="清除本地储存" gravity="center" layout_gravity="center" /></vertical></vertical></ScrollView>);// 更新本地配置同时重绘UIfunction update(target, new_val) {config.put(target, new_val);if (target == "is_cycle" || target == "white_list") draw_view();}// 格式化function format(val) {return val.toString();//结合 Function.toString()的方法来执行特定函数:}// 更新选中的执行方法ui.exec_pattern.setOnCheckedChangeListener(function(radioGroup, id) {let index = (id + 1) % radioGroup.getChildCount();//toast(radioGroup.getChildAt(index).getText());if (radioGroup.getChildAt(index).getText() == "循环") {update("is_cycle", true);} else {update("is_cycle", false);}});// 更新是否帮助好友ui.is_help_fris.setOnCheckedChangeListener(function(radioGroup, id) {let index = (id + 1) % radioGroup.getChildCount();//toast(radioGroup.getChildAt(index).getText());if (radioGroup.getChildAt(index).getText() == "是") {update("help_friend", true);} else {update("help_friend", false);}});// 更新颜色偏移ui.emitter.on("pause", () => {if (config.contains("color_offset")) {update("cycle_times", format(ui.cycle_times.getText()));update("color_offset", format(ui.color_offset.getText()));update("password", format(ui.password.getText()));update("max_collect_wait_time", format(ui.max_collect_wait_time.getText()));update("delay_unlock", format(ui.delay_unlock.getText()));update("timeout_findOne", format(ui.timeout_findOne.getText()));}});// 白名单缓存var list_temp = config.get("white_list").map(i => {return {name: i}});// 生成白名单ui.white_list.setDataSource(list_temp);// 从白名单中删除ui.white_list.on("item_bind", function(itemView, itemHolder){itemView.delete.on("click", function() {list_temp.splice(itemHolder.position, 1);update("white_list", list_temp.map(i => i['name']));});});// 添加到白名单ui.add.on("click", () => {dialogs.rawInput("请输入好友昵称").then(fri_name => {if (!fri_name) return;list_temp.push({name: fri_name});update("white_list", list_temp.map(i => i['name']));});});// 清除本地储存ui.clear.on("click", () => {confirm("确定要清除本地储存吗?").then(ok => {if (ok) {storages.remove("ant_forest_config");toastLog("清除成功");}});});}draw_view();

main.js

/************************ 初始化***********************/// 检查手机是否开启无障碍服务auto();//基于控件的操作依赖于无障碍服务,因此最好在脚本开头使用auto()函数来确保无障碍服务已经启用。// 检查脚本是否重复运行engines.all().slice(1).forEach(script => {if (script.getSource().getName().indexOf(engines.myEngine().getSource())) {toastLog("脚本正在运行中");//显示信息message并在控制台中输出engines.myEngine().forceStop();//engines.myEngine()返回当前脚本的脚本引擎对象(ScriptEngine)}});//engines.all(),返回当前所有正在运行的脚本的脚本引擎ScriptEngine的数组。//slice() 方法可从已有的数组中返回选定的元素,1规定从何处开始选取。//使用forEach函数来遍历,script是返回的变量,=>表示对变量进行如下操作//getSource()返回当前脚本引擎正在执行的脚本对象//indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置//从1脚本之后,如果判断字符出现当前脚本,那么当前脚本为重复运行// 请求截图权限if (! requestScreenCapture()) {toast("请求截图失败");//以气泡显示信息message几秒。exit();}//requestScreenCapture向系统申请屏幕截图权限,返回是否请求成功。/************************* 依赖加载***********************/// 加载本地配置var config = storages.create("ant_forest_config");//创建一个本地存储并返回一个Storage对象。不同名称的本地存储的数据是隔开的,而相同名称的本地存储的数据是共享的。if (!config.contains("color_offset")) {toastLog("请完善配置后再运行");engines.execScriptFile("./config.js");//在新的脚本环境中运行脚本文件path。engines.myEngine().forceStop();}//.contains返回该本地存储是否包含键值为key的数据。var Automator = require("./lib/Automator.js");//require() 以下的对象是特定于 Auto.js 的。var Unlock = require("./lib/Unlock.js");var Ant_forest = require("./core/Ant_forest.js");var automator = Automator();//实例化对象var unlock = Unlock(automator);var ant_forest = Ant_forest(automator, unlock);/************************* 主程序***********************/ant_forest.exec();//运行程序,exec为return的内容

core目录

Ant_forest.js

/** @Author: NickHopps* @Last Modified by: NickHopps* @Last Modified time: -03-14 10:29:30* @Description: 蚂蚁森林操作集*/function Ant_forest(automator, unlock) {const _automator = automator,//const定义的变量不可以修改,而且必须初始化。_unlock = unlock,_config = storages.create("ant_forest_config"),//创建一个本地存储并返回一个Storage对象。//storages模块提供了保存简单数据、用户配置等的支持。保存的数据除非应用被卸载或者被主动删除,否则会一直保留。_package_name = "com.eg.android.AlipayGphone";let _pre_energy = 0, // 记录收取前能量值_post_energy = 0,// 记录收取后能量值_timestamp = 0, // 记录获取自身能量倒计时_min_countdown = 0, // 最小可收取倒计时_current_time = 0,// 当前收集次数_fisrt_running = true, // 是否第一次进入蚂蚁森林_has_next = true,// 是否下一次运行_avil_list = [], // 可收取好友列表_has_protect = [];// 开启能量罩好友//let允许你声明一个作用域被限制在块级中的变量、语句或者表达式。/************************ 综合操作***********************/// 进入蚂蚁森林主页const _start_app = function() {app.startActivity({ action: "VIEW",data: "alipays://platformapi/startapp?appId=60000002", });}// 启动Auto.js的特定界面。// 关闭提醒弹窗const _clear_popup = function() {// 合种/添加快捷方式提醒threads.start(function() {let popup = idEndsWith("J_pop_treedialog_close").findOne(_config.get("timeout_findOne"));if (popup) popup.click();});// 活动threads.start(function() {let popup = descEndsWith("关闭蒙层").findOne(_config.get("timeout_findOne"));if (popup) popup.click();}); }//idEndsWith此方法测试id字符串是否以指定的后缀结束//threads.start启动一个新线程并执行action。 //findOne根据选择器selector在该控件的子控件、孙控件...中搜索符合该选择器条件的控件,并返回找到的第一个控件;如果没有找到符合条件的控件则返回null。// 显示文字悬浮窗const _show_floaty = function(text) {let window = floaty.window(<card cardBackgroundColor = "#aa000000" cardCornerRadius = "20dp"><horizontal w = "250" h = "40" paddingLeft = "15" gravity="center"><text id = "log" w = "180" h = "30" textSize = "12dp" textColor = "#ffffff" layout_gravity="center" gravity="left|center"></text><card id = "stop" w = "30" h = "30" cardBackgroundColor = "#fafafa" cardCornerRadius = "15dp" layout_gravity="right|center" paddingRight = "-15"><text w = "30" h = "30" textSize = "16dp" textColor = "#000000" layout_gravity="center" gravity="center">×</text></card></horizontal></card>);window.stop.on("click", () => {engines.stopAll();});setInterval(()=>{ui.run(function(){window.log.text(text)});}, 0);}//floaty模块提供了悬浮窗的相关函数,可以在屏幕上显示自定义悬浮窗,控制悬浮窗大小、位置等。// 同步获取 toast 内容const _get_toast_sync = function(filter, limit, exec) {filter = (typeof filter == null) ? "" : filter;let messages = threads.disposable();// 在新线程中开启监听let thread = threads.start(function() {let temp = [];let counter = 0;// 监控 toastevents.onToast(function(toast) {if (toast) {if (toast.getPackageName().indexOf(filter) >= 0) {counter++;temp.push(toast.getText())if (counter == limit) messages.setAndNotify(temp);}}});// 触发 toastexec();});// 获取结果let result = messages.blockedGet();thread.interrupt();return result;}/************************ 获取下次运行倒计时***********************/// 获取自己的能量球中可收取倒计时的最小值const _get_min_countdown_own = function() {let target = className("Button").descMatches(/\s/).filter(function(obj) {return obj.bounds().height() / obj.bounds().width() > 1.1; });if (target.exists()) {let ball = target.untilFind();let temp = [];let toasts = _get_toast_sync(_package_name, ball.length, function() {ball.forEach(function(obj) {_automator.clickCenter(obj);sleep(500);});});toasts.forEach(function(toast) {let countdown = toast.match(/\d+/g);temp.push(countdown[0] * 60 - (-countdown[1]));});_min_countdown = Math.min.apply(null, temp);_timestamp = new Date();} else {_min_countdown = null;log("无可收取能量");}}// 确定下一次收取倒计时const _get_min_countdown = function() {let temp = [];if (_min_countdown && _timestamp instanceof Date) {let countdown_own = _min_countdown - Math.floor((new Date() - _timestamp) / 60000);countdown_own >= 0 ? temp.push(countdown_own) : temp.push(0);}if (descEndsWith("’").exists()) {descEndsWith("’").untilFind().forEach(function(countdown) {let countdown_fri = parseInt(countdown.desc().match(/\d+/));temp.push(countdown_fri);});}if (!temp.length) return;_min_countdown = Math.min.apply(null, temp);}/************************ 构建下次运行操作***********************/// 构建下一次运行const _generate_next = function() {if (_config.get("is_cycle")) {if (_current_time < _config.get("cycle_times")) {_has_next = true;} else {_has_next = false;}} else {if (_min_countdown != null && _min_countdown <= _config.get("max_collect_wait_time")) {_has_next = true;} else {_has_next = false;} }}//只有当我们获取到的最小时间小于最大等待时间,才有下一个// 按分钟延时const _delay = function(minutes) {minutes = (typeof minutes != null) ? minutes : 0;for (let i = 0; i < minutes; i++) {log("距离下次运行还有 " + (minutes - i) + " 分钟");//要打印到控制台的信息sleep(60000);}}/************************ 记录能量***********************/// 记录当前能量const _get_current_energy = function() {if (descEndsWith("背包").exists()) {return parseInt(descEndsWith("g").findOne(_config.get("timeout_findOne")).desc().match(/\d+/));}}// 记录初始能量值const _get_pre_energy = function() {if (_fisrt_running && _has_next) {_pre_energy = _get_current_energy();log("当前能量:" + _pre_energy);}}// 记录最终能量值const _get_post_energy = function() {if (!_fisrt_running && !_has_next) {if (descEndsWith("返回").exists()) descEndsWith("返回").findOne(_config.get("timeout_findOne")).click();descEndsWith("背包").waitFor();_post_energy = _get_current_energy();log("当前能量:" + _post_energy);log("共收取:" + (_post_energy - _pre_energy) + "g 能量");// _show_floaty("共收取:" + (_post_energy - _pre_energy) + "g 能量");}if (descEndsWith("关闭").exists()) descEndsWith("关闭").findOne(_config.get("timeout_findOne")).click();home();}/************************ 收取能量***********************/// 收取能量const _collect = function() {if (descEndsWith("克").exists()) {descEndsWith("克").untilFind().forEach(function(ball) {_automator.clickCenter(ball);sleep(500);});}}// 收取能量同时帮好友收取const _collect_and_help = function() {let screen = captureScreen();// 收取好友能量_collect();// 帮助好友收取能量if (className("Button").descMatches(/\s/).exists()) {className("Button").descMatches(/\s/).untilFind().forEach(function(ball) {let x = ball.bounds().left,y = ball.bounds().top,w = ball.bounds().width(),h = ball.bounds().height(),t = _config.get("color_offset");if (images.findColor(screen, "#f99236", {region: [x, y, w, h], threshold: t})) {_automator.clickCenter(ball);sleep(500);}});}}// 判断是否可收取const _is_obtainable = function(obj, screen) {let len = obj.childCount();let x = obj.child(len - 3).bounds().right,y = obj.bounds().top,w = 5,h = obj.bounds().height() - 10,t = _config.get("color_offset");if (h > 0 && !obj.child(len - 2).childCount()) {if (_config.get("help_friend")) {return images.findColor(screen, "#1da06a", {region: [x, y, w, h], threshold: t}) || images.findColor(screen, "#f99236", {region: [x, y, w, h], threshold: t});} else {return images.findColor(screen, "#1da06a", {region: [x, y, w, h], threshold: t});}} else {return false;}}// 记录好友信息const _record_avil_list = function(fri) {let temp = {};// 记录可收取对象temp.target = fri.bounds();// 记录好友IDif (fri.child(1).desc() == "") {temp.name = fri.child(2).desc();} else {temp.name = fri.child(1).desc();}// 记录是否有保护罩temp.protect = false;_has_protect.forEach(function(obj) {if (temp.name == obj) temp.protect = true});// 添加到可收取列表if (_config.get("white_list").indexOf(temp.name) < 0) _avil_list.push(temp);}// 判断并记录保护罩const _record_protected = function(toast) {if (toast.indexOf("能量罩") > 0) {let title = textContains("的蚂蚁森林").findOne(_config.get("timeout_findOne")).text();_has_protect.push(title.substring(0, title.indexOf("的")));}}// 检测能量罩const _protect_detect = function(filter) {filter = (typeof filter == null) ? "" : filter;// 在新线程中开启监听return threads.start(function() {events.onToast(function(toast) {if (toast.getPackageName().indexOf(filter) >= 0) _record_protected(toast.getText());});});}// 根据可收取列表收取好友const _collect_avil_list = function() {while (_avil_list.length) {let obj = _avil_list.shift();if (!obj.protect) {let temp = _protect_detect(_package_name);_automator.click(obj.target.centerX(), obj.target.centerY());descEndsWith("浇水").waitFor();if (_config.get("help_friend")) {_collect_and_help();} else {_collect();}_automator.back();temp.interrupt();while(!textContains("好友排行榜").exists()) sleep(1000);}}}// 识别可收取好友并记录const _find_and_collect = function() {while (!(descEndsWith("没有更多了").exists() && descEndsWith("没有更多了").findOne(_config.get("timeout_findOne")).bounds().centerY() < device.height)) {let screen = captureScreen();let friends_list = idEndsWith("J_rank_list").findOne(_config.get("timeout_findOne"));if (friends_list) {friends_list.children().forEach(function(fri) {if (fri.visibleToUser() && fri.childCount() > 3)if (_is_obtainable(fri, screen)) _record_avil_list(fri);});_collect_avil_list();}scrollDown();sleep(1000);}}/************************ 主要函数***********************/// 收取自己的能量const _collect_own = function() {log("开始收集自己能量");if (!textContains("蚂蚁森林").exists()) _start_app();descEndsWith("背包").waitFor();_clear_popup();_get_pre_energy();_collect();if (!_config.get("is_cycle")) _get_min_countdown_own();//不是循环收取的话,我们获取最小时间_fisrt_running = false;}// 收取好友的能量const _collect_friend = function() {log("开始收集好友能量");descEndsWith("查看更多好友").findOne(_config.get("timeout_findOne")).click();while(!textContains("好友排行榜").exists()) sleep(1000);_find_and_collect();if (!_config.get("is_cycle")) _get_min_countdown();_generate_next();_get_post_energy();}return {exec: function() {let thread = threads.start(function() {events.setMaxListeners(0);//返回 EventEmitter 当前的最大监听器限制值,该值可以通过.setMaxListeners(n) 设置或默认。//0表示不监听events.observeToast();//开启Toast监听。Toast监听依赖于无障碍服务,因此此函数会确保无障碍服务运行。});while (true) {_delay(_min_countdown);log("第 " + (++_current_time) + " 次运行");_unlock.exec();//执行实例化的unlock_collect_own();//收集自己的能量_collect_friend();//收集别人的能量if (_config.get("is_cycle")) sleep(1000);//循环收取的话,sleep一下events.removeAllListeners();if (_has_next == false) {log("收取结束");//没有可收的,就收取结束break;}}thread.interrupt();//中断线程}}}module.exports = Ant_forest;//模块内的本地变量是私有的,不会影响到加载他的脚本的变量环境。//module.exports属性可以被赋予一个新的值(例如函数或对象)

lib目录

Automator.js

function Automation_root() {this.check_root = function() {if (!(files.exists("/sbin/su") || files.exists("/system/xbin/su") || files.exists("/system/bin/su"))) throw new Error("未获取ROOT权限");}this.click = function (x, y) {this.check_root();return (shell("input tap " + x + " " + y, true).code === 0);}this.swipe = function (x1, y1, x2, y2, duration) {this.check_root();return (shell("input swipe " + x1 + " " + y1 + " " + x2 + " " + y2 + " " + duration, true).code === 0);}this.gesture = function(duration, points) {this.check_root();let len = points.length,step = duration / len,start = points.shift();// 使用 RootAutomator 模拟手势,仅适用于安卓5.0及以上let ra = new RootAutomator();ra.touchDown(start[0], start[1]);sleep(step);points.forEach(function(el) {ra.touchMove(el[0], el[1]);sleep(step);});ra.touchUp();ra.exit();return true;}this.back = function() {this.check_root();return (shell("input keyevent KEYCODE_BACK", true).code === 0);}}function Automation() {this.click = function (x, y) {return click(x, y);}this.swipe = function (x1, y1, x2, y2, duration) {return swipe(x1, y1, x2, y2, duration);}this.gesture = function(duration, points) {return gesture(duration, points);}this.back = function() {return back();}}// 工厂方法function Automator() {const _automator = (device.sdkInt < 24) ? new Automation_root() : new Automation();return {click: function (x, y) {return _automator.click(x, y);},clickCenter: function (obj) {return _automator.click(obj.bounds().centerX(), obj.bounds().centerY());},swipe: function (x1, y1, x2, y2, duration) {return _automator.swipe(x1, y1, x2, y2, duration);},gesture: function(duration, points) {return _automator.gesture(duration, points);},back: function () {return _automator.back();}}}module.exports = Automator;

Unlock.js

var Devices = {HUAWEI_EMUI8: function(obj) {this.__proto__ = obj;// 图形密码解锁this.unlock_pattern = function(password) {if (typeof password !== "string") throw new Error("密码应为字符串!");let pattern_view = id("com.android.systemui:id/lockPatternView").findOne(this.config.get("timeout_findOne")).bounds(),pattern_size = 3,len = password.length,view_x = pattern_view.left,view_y = pattern_view.top,width = (pattern_view.right - pattern_view.left) / pattern_size,height = (pattern_view.bottom - pattern_view.top) / pattern_size,points = [],ges_param = [];// 记录图形点信息for (let i = 0; i < pattern_size; i++) {for (let j = 0; j < pattern_size; j++) {let index = pattern_size * i + (j + 1);points[index] = [parseInt(view_x + j * width + width / 2), parseInt(view_y + i * height + height / 2)];}}// 构造滑动参数for (var i = 0; i < len; i++) ges_param.push(points[password[i]]);// 使用手势解锁this.automator.gesture(300 * len, ges_param);return this.check_unlock();}// 密码解锁(仅ROOT可用)this.unlock_password = function(password) {if (typeof password !== "string") throw new Error("密码应为字符串!");// 直接在控件中输入密码setText(0, password);// 执行确认操作KeyCode("KEYCODE_ENTER");return this.check_unlock();}// PIN解锁this.unlock_pin = function(password) {if (typeof password !== "string") throw new Error("密码应为字符串!");// 模拟按键for (let i = 0; i < password.length; i++) {let key_id = "com.android.systemui:id/key" + password[i];id(key_id).findOne(this.config.get("timeout_findOne")).click();sleep(100);}return this.check_unlock();}// 判断解锁方式并解锁this.unlock = function(password) {if (id("com.android.systemui:id/lockPatternView").exists()) {return this.unlock_pattern(password);} else if (id("com.android.systemui:id/passwordEntry").exists()) {return this.unlock_password(password);} else if (id("com.android.systemui:id/pinEntry").exists()) {return this.unlock_pin(password);} else {toastLog("识别锁定方式失败,型号:" + device.brand + " " + device.product + " " + device.release);return this.check_unlock();}}}}var MyDevice = Devices.HUAWEI_EMUI8;//实例化一个类function Unlocker(automator) {const _device = new MyDevice(this),_HEIGHT = device.height,_WIDTH = device.width,_km = context.getSystemService(context.KEYGUARD_SERVICE);this.automator = automator;this.config = storages.create("ant_forest_config");// 设备是否锁屏this.is_locked = function() {return _km.inKeyguardRestrictedInputMode();}// 设备是否加密this.is_passwd = function() {return _km.isKeyguardSecure();}// 解锁失败this.failed = function() {log("解锁失败,停止运行");exit();}// 检测是否解锁成功this.check_unlock = function() {sleep(this.config.get("delay_unlock"));if (this.is_locked() == true) this.failed();return !this.is_locked();}// 唤醒设备this.wakeup = function() {while (!device.isScreenOn()) {device.wakeUp();sleep(this.config.get("delay_unlock"));}}// 划开图层this.swipe_layer = function() {let x = _WIDTH / 2;let y = _HEIGHT / 4;this.automator.swipe(x, (3 * y), x, y, 300);sleep(this.config.get("delay_unlock"));}// 执行解锁操作this.run_unlock = function() {// 如果已经解锁则返回if (!this.is_locked()) return true;// 首先点亮屏幕this.wakeup();// 打开滑动层this.swipe_layer();// 如果有锁屏密码则输入密码if (this.is_passwd()) _device.unlock(this.config.get("password"))// 检测是否解锁成功this.check_unlock();}}function Unlock(automator) {const _unlocker = new Unlocker(automator);//使用构造函数return {exec: function() {//运行这个解锁程序,_unlocker.run_unlock();}}}module.exports = Unlock;

不想用vs code也可以使用Total Control进行实时编辑,也可以利用其中的文件管理功能进行复制移动文件。

ps:出了问题,记得更新蚂蚁脚本和auto.js的版本,最新的auto.js在其官方提供的qq群里面有。用新不用旧问题会少很多。如果出现了一些之前没出现的问题,又能确保自己什么都没干的话,不妨把支付宝、autojs、偷能量脚本都更新到最新,然后重启手机试试。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。