1. 程式人生 > >Auto.js (支付寶篇):螞蟻森林自動收取能量和偷取能量

Auto.js (支付寶篇):螞蟻森林自動收取能量和偷取能量

說在前面

對於螞蟻森林,我有一段時間沒有玩,後來被人超了。然後,我又開始偷取,每天定鬧鐘,每天走步數。相信不是土豪,還想成為排行榜的第一。肯定也像我一樣(幼稚),O(∩_∩)O哈哈~

當然,作為一名還沒入門的程式猿,“懶”便讓我想起了如何“偷懶”。

首先宣告,程式碼部分不是本人所寫,通過大量搜尋,只是做了部分改動,增加自己的想法,以及從0入門的更易理解。本文的目的一是分享給大家,二算是記個筆記方便以後看。

前幾天在CSDN看到一篇用android逆向編譯的,完全看不懂。後來想起了 WorkFlow,但是感覺也很無聊。於是想起了用js試一試。

開發環境

手機支援

小米 5S(無需ROOT,但是需要開啟無障礙模式)

Auto.js APP

百度手機助手: http://shouji.baidu.com/software/23334313.html
酷安:https://www.coolapk.com/apk/129872

MIUI 版本

MIUI 9.2 | 穩定版

Android 版本

7.0

解析度

1920 x 1080 畫素

電腦支援

一臺Windows

安裝Auto.js 

桌面編輯器Visual Studio Code的外掛。可以讓Visual Studio Code支援Auto.js開發。

在VS Code中選單“檢視” - >“擴充套件” - >輸入“Auto.js”或“hyb1996”搜尋,即可看到“Auto.js-VSCodeExt”外掛,安裝即可。外掛的更新也可以在這裡更新。

如何使用

第1步

Ctrl+Shift+P或點選“檢視” - > “命令面板”可調出命令面板,輸入側Auto.js可以看到幾個命令,移動游標到命令Auto.js: Start Server,按回車鍵執行該命令。

此時VS Code會在右上角顯示“Auto.js server running”,即開啟服務成功。

第2步

將手機連線到電腦啟用的無線上網或者同一區域網中。通過命令列IPCONFIG(或者其他作業系統的相同功能命令)檢視電腦的IP地址。在Auto.js的側拉選單中啟用除錯服務,並輸入IP地址,等待連線成功。

(md,這個地方的Auto.js是手機上的。找了大半天沒找到ip。官方下載地址:

https://www.coolapk.com/apk/org.autojs.autojs

第3步

之後就可以在電腦上編輯的JavaScript並檔案通過命令Run或者按鍵F5在手機上運行了。

命令

Ctrl+Shift+P或點選“檢視” - > “命令面板”可調出命令面板,輸入側Auto.js可以看到幾個命令:

  • Start Server:啟動外掛服務。之後在確保手機和電腦在同一區域網的情況下,在Auto.js的側拉選單中使用連線電腦功能連線。
  • 停止伺服器:停止外掛服務。
  • 執行運行當前編輯器的指令碼。如果有多個裝置連線,則在所有裝置執行。
  • Rerun停止當前檔案對應的指令碼並重新執行。如果有多個裝置連線,則在所有裝置重新執行。
  • 停止當前檔案對應的指令碼。如果有多個裝置連線,則在所有裝置停止。
  • StopAll停止所有正在執行的指令碼。如果有多個裝置連線,則在所有裝置執行所有指令碼。
  • 儲存當前檔案到手機的指令碼預設目錄(檔名會加上字首remote)。如果有多個裝置連線,則在所有裝置儲存。
  • RunOnDevice:彈出裝置選單並在指定裝置執行指令碼。
  • SaveToDevice:彈出裝置選單並在指定裝置儲存指令碼。

以上命令一些有對應的快捷鍵,參照命令後面的說明即可。

日誌

要顯示來自Auto.js的日誌,開啟VS Code上面選單的“幫助” - >“切換開發人員工具” - >“控制檯”即可。

官方文件來源:https://github.com/hyb1996/Auto.js-VSCode-Extension

實現步驟

其實說白了,就是類似於巨集記錄,類似按鍵精靈,其實就是模擬你的操作,讓你放手做其他的事情。

本身外掛也好,輔助也罷,道理都差不多,好了不廢話了,不住簡單如下幾步:

  1. 解鎖(點亮螢幕,切換到輸入密碼介面,輸入密碼)
  2. 開啟支付寶
  3. 從主頁進入螞蟻森林主頁
  4. 收集自己的能量
  5. 進入排行榜
  6. 在排行榜檢測是否有好有的能量可以收集
  7. 結束後返回主頁面

程式碼

當然,如果不想使用電腦,可以直接在手機端 下載 Auto.js,直接把程式碼複製 執行即可。

/**
* author lvyimeng
* @version V1.0
* @Title: 螞蟻森林
* @Description:螞蟻森林自動收取能量和偷取能量
* @date: 2018-9-14 12:33:56
*/
var myEnergeType=["線下支付","行走","共享單車","地鐵購票","網路購票","網購火車票","生活繳費","ETC繳費","電子發票","綠色辦公","鹹魚交易","預約掛號"];
var morningTime="07:03";//自己運動能量生成時間
unlock();
sleep(1000);
mainEntrence();
 
//解鎖
function unlock(){
    if(!device.isScreenOn()){
    	//點亮螢幕
        device.wakeUp();
        sleep(1000);
		//滑動螢幕到輸入密碼介面
        swipe(500, 1900, 500, 1000, 1000);
        sleep(1000);
        
       //輸入四次 1 (密碼為1111)其他密碼請自行修改 數字鍵1的畫素座標為(200,1000)
        click(200,1000);
        sleep(500);
       
        click(200,1000);
        sleep(500);
        
        click(200,1000);
        sleep(500);
        
        click(200,1000);
        sleep(500);
    
    }
}

/**
 * 日誌輸出
 */
function tLog(msg) {
    toast(msg);
    console.log(msg)
}

/**
 * 獲取許可權和設定引數
 */
function prepareThings(){
    setScreenMetrics(1080, 1920);
    //請求截圖
   if(!requestScreenCapture()){
        tLog("請求截圖失敗");
        exit();
    }
    
}
/**
 * 設定按鍵監聽 當指令碼執行時候按音量減 退出指令碼
 */
function registEvent() {
    //啟用按鍵監聽
    events.observeKey();
    //監聽音量上鍵按下
    events.onKeyDown("volume_down", function(event){
        tLog("指令碼手動退出");
        exit();
    });
}
/**
 * 獲取截圖
 */
function getCaptureImg(){
    var img0 = captureScreen();
    if(img0==null || typeof(img0)=="undifined"){
        tLog("截圖失敗,退出指令碼");
        exit();
    }else{
        return img0;
    }
}
/**
 * 預設程式出錯提示操作
 */
function defaultException() {
    tLog("程式當前所處狀態不合預期,指令碼退出");
    exit();
}
/**
 * 等待載入收集能量頁面,採用未找到指定元件阻塞的方式,等待頁面載入完成
 */
function waitPage(type){
    // 等待進入自己的能量主頁
    if(type==0){
        desc("訊息").findOne();
    }
    // 等待進入他人的能量主頁
    else if(type==1){
        desc("澆水").findOne();
    }
    //再次容錯處理
    sleep(3000);
}
/**
 * 從支付寶主頁進入螞蟻森林我的主頁
 */
function enterMyMainPage(){
    launchApp("支付寶");
    tLog("等待支付寶啟動");
    var i=0;
    sleep(1000);
    //五次嘗試螞蟻森林入口
    while (!textEndsWith("螞蟻森林").exists() && i<=5){
        sleep(2000);
        i++;
    }
    clickByText("螞蟻森林",true,"請把螞蟻森林入口新增到主頁我的應用");
    //等待進入自己的主頁
    waitPage(0);
}
/**
 * 進入排行榜
 */
function enterRank(){
    tLog("進入排行榜");
    Swipe(520,1860,520,100);
    sleep(2500);
    clickByDesc("檢視更多好友",0,true,"程式未找到排行榜入口,指令碼退出");
    var i=0;
    //等待排行榜主頁出現
    sleep(2000);
    while (!textEndsWith("好友排行榜").exists() && i<=5){
        sleep(2000);
        i++;
    }
    if(i>=5){
        defaultException();
    }
}
/**
 * 從排行榜獲取可收集好友的點選位置
 * @returns {*}
 */
function  getHasEnergyfriend(type) {
    var img = getCaptureImg();
    var p=null;
    if(type==1){
        //img 是圖片
        //"#30bf6c" 第一個顏色
        //[0, 33, "#30bf6c"] 第二顏色和它的相對座標
        //[34,45, "#ffffff"] 第三個顏色和他的相對座標
        //region: [1030, 100, 1, 1700] 第一個顏色的檢測區域1030,100為起始座標,1,1700為區域寬度!!!
        p = images.findMultiColors(img, "#30bf6c",[[60, 0, "#30bf6c"], [46,45, "#ffffff"]], {
            region: [1018, 100, 1, 1700]
        });
    }
    if(p!=null){
        return p;
    }else {
        return null;
    }
}
/**
 * 判斷是否好有排行榜已經結束
 * @returns {boolean}
 */
function isRankEnd() {
    if(descEndsWith("沒有更多了").exists()){
        var b=descEndsWith("沒有更多了").findOne();
        var bs=b.bounds();
        if(bs.centerY()<1920){
            return true;
        }
    }
    return false;
}
/**
 * 在排行榜頁面,迴圈查詢可收集好友
 * @returns {boolean}
 */
function enterOthers(){
    tLog("開始檢查排行榜");
    var i=1;
    var ePoint=getHasEnergyfriend(1);
    //確保當前操作是在排行榜介面
    while(ePoint==null && textEndsWith("好友排行榜").exists()){
         //滑動排行榜 root方式的的點選呼叫.如無root許可權,7.0及其以上可採用無障礙模式的相關函式
        swipe(520,1800,520,300,1000);
        sleep(3000);
        ePoint=getHasEnergyfriend(1);
        i++;
        //檢測是否排行榜結束了
        if(isRankEnd()){
            return false;
        }
        //如果連續32次都未檢測到可收集好友,無論如何停止查詢(由於程式控制了在排行榜介面,且判斷了結束標記,基本已經不存在這種情況了)
        else if(i>32){
            tLog("程式可能出錯,連續"+i+"次未檢測到可收集好友");
            exit();
        }
    }
    if(ePoint!=null){
        
        //點選位置相對找圖後的修正
        tLog(ePoint.x,ePoint.y);
        click(ePoint.x,ePoint.y+20);
        waitPage(1);
        clickByDesc("可收取",80);
        //進去收集完後,遞迴呼叫enterOthers
        back();
        sleep(2000);
        var j=0;
        //等待返回好有排行榜
        if(!textEndsWith("好友排行榜").exists() && j<=5){
            sleep(2000);
            j++;
        }
        if(j>=5){
            defaultException();
        }
        enterOthers();
    }else{
        defaultException();
    }
}
/**
 * 根據描述值 點選
 * @param energyType
 * @param noFindExit
 */
function clickByDesc(energyType,paddingY,noFindExit,exceptionMsg){
    if(descEndsWith(energyType).exists()){
        descEndsWith(energyType).find().forEach(function(pos){
            var posb=pos.bounds();
            click(posb.centerX(),posb.centerY()-paddingY);
            sleep(2000);
        });
    }else{
        if(noFindExit!=null && noFindExit){
            if(exceptionMsg !=null){
                tLog(exceptionMsg);
                exit();
            }else{
                defaultException();
            }
        }
    }
}
/**
 * 根據text值 點選 * @param energyType * @param noFindExit
 */
function clickByText(energyType,noFindExit,exceptionMsg){
    if(textEndsWith(energyType).exists()){
        textEndsWith(energyType).find().forEach(function(pos){
            var posb=pos.bounds();
            click(posb.centerX(),posb.centerY()-60);
        });
    }else{
        if(noFindExit!=null && noFindExit){
            if(exceptionMsg !=null){
                tLog(exceptionMsg);
                exit();
            }else{
                defaultException();
            }
        }
    }
}
/**
 * 遍歷能量型別,收集自己的能量
 */
function collectionMyEnergy(){
    var energyRegex=generateCollectionType();
    var checkInMorning=false;
    //如果是早上7點03分左右的話.等待主頁能量出現 每隔一秒檢測一次
    while(isMorningTime() && descEndsWith("行走").exists()){
        if (!checkInMorning){
            tLog("等待運動能量生成中...");
            checkInMorning=true;
        }
        descEndsWith("行走").find().forEach(function(pos){
            var posb=pos.bounds();
            click(posb.centerX(),posb.centerY()-80);
            sleep(1500);
        });
    }
    if(checkInMorning){
        tLog("運動能量收集完成");
    }
    if(descMatches(energyRegex).exists()){
        if(!checkInMorning){
            tLog("防止小樹的提示遮擋,等待中");
            sleep(3000);
        }
        //這裡存在一定的問題:如果sleep時間短的話,就會出現迴圈程式碼在執行,迴圈之後的程式碼也在執行,感覺出現了非同步,具體原因不明
       descMatches(energyRegex).find().forEach(function(pos){
            var posb=pos.bounds();
            //tLog( posb.centerX());
            click(posb.centerX(),posb.centerY()-100);
            sleep(3000);
        });
    }
    tLog("自己能量收集完成");
    sleep(1000);
}
/**
 * 結束後返回主頁面
 */
function whenComplete() {
    tLog("結束");
    back();
    sleep(1500);
    back();
    exit();
}
/**
 * 根據能量型別陣列生成我的能量型別正則查詢字串
 * @returns {string}
 */
function generateCollectionType() {
    var regex="/";
    myEnergeType.forEach(function (t,num) {
        if(num==0){
            regex+="(\\s*"+t+"$)";
        }else{
            regex+="|(\\s*"+t+"$)";
        }
    });
    regex+="/";
    return regex;
}
function isMorningTime() {
    var now =new Date();
    var hour=now.getHours();
    var minu=now.getMinutes();
    var targetTime=morningTime.split(":");
    if(Number(targetTime[0])==hour && Math.abs(Number(targetTime[1])-minu)<=2){
        return true;
    }else{
        return false;
    }
}
//程式主入口
function mainEntrence(){
    //前置操作
    prepareThings();
    //註冊音量下按下退出指令碼監聽
    registEvent();
    //從主頁進入螞蟻森林主頁
    enterMyMainPage();
    //收集自己的能量
    collectionMyEnergy();
    //進入排行榜
    enterRank();
    //在排行榜檢測是否有好有的能量可以收集
    enterOthers();
    //結束後返回主頁面
    whenComplete();
}

其實理論很簡單,無非就是截圖,獲取截圖尺寸,按著座標去觸發點選,比如:

這個函式的註釋在程式碼裡有就不多說了。主要是區別手型,倒計時數字,和邀請按鈕。

它們的共同點是都有綠色的背景,區別是手型的白色區域比數字的高,而邀請按鈕的右邊綠色背景沒有到手機的邊緣,利用這個差別,我們就可以分辨出來。我的做法是用了三個點(如下圖,紅色的三個點),第一個為綠色,第二個在第一個的右邊並且顏色相同,第三個在第一個右下方為白色。第一個點的橫座標為1018,第二個點在第一個點的[60,0]方向,第三個在第一個的[46,45]位置,這就是為什麼第一點的取點區域為[1018, 100, 1, 1700],這樣就可以保證取到的第一個點一定是左上角的綠點。

此方法,僅適用於 安卓,IOS版 我將採用workflow 規則形式來完成,敬請記得~~~

總結

程式碼並不難,思路也很簡單,無非就是 按鍵精靈 指令碼檔案。希望能在看懂程式碼之後再進行實際操作。

原始碼下載

[相關示例完整程式碼]