/*
* @Author: NickHopps
* @Last Modified by: NickHopps
* @Last Modified time: 2019-03-14 10:29:30
* @Description: 蚂蚁森林操作集
*/
//此代码由飞云脚本圈整理提供(www.feiyunjs.com)
function Ant_forest(automator, unlock) {
const _automator = automator,
_unlock = unlock,
_config = storages.create("ant_forest_config"),
_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 = []; // 开启能量罩好友
/***********************
* 综合操作
***********************/
// 进入蚂蚁森林主页
const _start_app = function() {
app.startActivity({
action: "VIEW",
data: "alipays://platformapi/startapp?appId=60000002",
});
}
// 关闭提醒弹窗
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();
});
}
// 显示文字悬浮窗
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);
}
// 同步获取 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;
// 监控 toast
events.onToast(function(toast) {
if (toast) {
if (toast.getPackageName().indexOf(filter) >= 0) {
counter++;
temp.push(toast.getText())
if (counter == limit) messages.setAndNotify(temp);
}
}
});
// 触发 toast
exec();
});
// 获取结果
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);
_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();
// 记录好友ID
if (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);
events.observeToast();
});
while (true) {
_delay(_min_countdown);
log("第 " + (++_current_time) + " 次运行");
_unlock.exec();
_collect_own();
_collect_friend();
if (_config.get("is_cycle")) sleep(1000);
events.removeAllListeners();
if (_has_next == false) {
log("收取结束");
break;
}
}
thread.interrupt();
}
}
}
module.exports = Ant_forest;
说明
本文提供的代码仅供参考。
可能有些地方在最新版本的Auto.js上面需要做修改,才能运行。
Auto.js简介
Auto.js是利用安卓系统的“辅助功能”实现类似于按键精灵一样,可以通过代码模拟一系列界面动作的辅助工作。
与“按键精灵”不同的是,它的模拟动作并不是简单的使用在界面定坐标点来实现,而是类似与win一般,找窗口句柄来实现的。
Auto.js使用JavaScript作为脚本语言,目前使用Rhino 1.7.7.2作为脚本引擎,支持ES5与部分ES6特性。
开发文档
Auto.js Pro开发文档优化版
文档尚在完善中,可能有文档描述和代码实际行为有出入的情况。
模板、样式、generator来自Node.js。
为什么要使用Auto.js Pro开发脚本,有什么特点?
吸引我使用Auto.js Pro的原因有很多。最主要的几个原因是:
- Auto.js Pro能开发免ROOT的安卓脚本
- Auto.js Pro基于节点操作,能开发全分辨率的脚本,自动适配各种安卓机型
- Auto.js Pro丰富的UI组件,能自定义各种样式的安卓界面
- Auto.js Pro使用的javascript的语法比较优雅,代码可读性强
- Auto.js Pro的命令库非常的丰富,接口比较多
- Auto.js Pro脚本文件体积比较小。1000行的代码,打包后的apk文件只有3-5M,还没有广告