Autox.js实现每日定时小程序抢课

Autox.js实现每日定时小程序抢课

1、Autox.js下载与配置

1.1 简介和文档地址

autox.js(项目地址)是大佬在auto.js 4.1(项目地址)版本的基础上继续维护项目,Autox.js(文档地址)是一款不需要root便能实现自动化的脚本框架,很适合新手小白安卓自动化的学习和编写。

1.2 连接电脑调试/定时启动

一些配置方法以及定时启用可参考之前写的另一篇博文,这里不多赘述了
Autox.js实现全自动每日抖音续火花

2、大致流程叙述

工具:autoxjs本质还是模拟点击,没有绕过前端直接向服务器发送消息,所以只要不频繁多次点击,不会对服务器造成多大影响,也无法被发现。

  1. 由于发现所用的小程序是获取手机系统时间而不是服务器时间的,所以如果将手机的系统时间提前,可以比别人提早进入抢课页面,在点击抢课的时候比其他人少按一步,保证最快。所以提前将手机时间调快了两分钟。
  2. 在抢课前几分钟解锁屏幕。autoxjs因为没有root,没办法绕过开机密码,所以需要模拟开机密码的位置进行点击,进入桌面。
  3. 正确打开约课小程序
  4. 提前筛选好要抢的课程并等待约课时间到达
  5. 到达时间依次约课
auto();
//---------------------一些全局参数------------
//抢课时间(小时,分钟,延时(单位毫秒,可以写随机数))
//抢课时间为北京时间21:50,我将系统时间调快了2分钟,所以设置的抢课时间调为了21:52,并延时500毫秒运行
var CLASSHOUR = 21;
var CLASSMINUTE = 52;
var LAYBACK = 500;
//-----------------以下是主程序--------------
//1、解锁屏幕、打开报告窗口
unLockPhone();
console.show();
//打开小程序,选择门店(比如nancun为南村万博,huangpu为黄埔,自己设定)
openfoxclass("nancun");
//获取今天的时间和星期
var date = new Date();
var dayofweek_tomo = (date.getDay() + 1) % 7;
//筛选要抢的课程
ChooseForDay(dayofweek_tomo);
//点击明天的课(坐标点击简单但是兼容性差)
//click(218,1020);
sleep(3000);
//抢课
reservation(CLASSHOUR, CLASSMINUTE, LAYBACK);
//结束后锁屏
sleep(5000)
lockScreen();

3、具体细节

3.1 屏幕唤醒解锁

由于没有root权限,autojs无法从有密码的锁屏下直接进入安卓系统,只能模拟坐标点击,依次点击密码进入。PS:autojs点击过快会无法点到,所以需要在两次点击之间增加sleep间隙。

//输入密码解锁手机
function unLockPhone() {
    //为了方便如果本身是亮屏的,从先锁屏开始
    lockScreen();
    sleep(1000);
    //亮屏+输入密码
    device.wakeUp();
    sleep(1000);
    //下滑,开始输入密码
    swipe(500, 2322, 500, 400, 900);
    sleep(3000);
    //密码点击部分-----------
    //由于我的密码为3333,所以循环点击了4次,需要重新修改为自己的密码坐标
    for (var i = 0; i < 4; i++) {
        sleep(500);
        //点击位置(553, 779)
        press(553, 779, 400);
    }
    //---------------------------
    //保持屏幕常亮
    device.keepScreenOn();
    sleep(2000);
}

3.2 打开微信小程序并选择分店

思考过一些方法去打开小程序,但是实际操作起来有点麻烦,最终使用如下方法。

  • 先将小程序提前登录好,保存在首页的桌面(以圈中小程序为例进行演示)

小程序放置在桌面首页

  • 切换需要预约的门店
    在这里插入图片描述 在这里插入图片描述 在这里插入图片描述
//打开小程序并打开课程页面
//南村万博,黄埔店选店
function openfoxclass(house){
    //回到首页
    home();
    sleep(3000);
    home();
    sleep(1000);
    //小程序控件无法被点击,只能找到控件的位置然后点击中心区域
    className("android.widget.ImageView").desc("Fox舞蹈").waitFor();
    var fox_index = className("android.widget.ImageView").desc("Fox舞蹈").findOne().bounds();
    click(fox_index.centerX(), fox_index.centerY());
    sleep(10000);
    //点击主页
    text("主页").waitFor();
    text("主页").findOne().click();
    sleep(4000);
    //点击切换门店
    if (house == "huangpu")  var housename = "黄埔店";
    if (house == "nancun")   var housename = "南村万博店";
    log("切换门店"+ housename);
    className("android.widget.Image").depth(9).indexInParent(0).findOne().click();
    sleep(5000);
    log("切换成功");
    className("android.widget.TextView").text(housename).findOne().click();
    sleep(3000);
    //点击课程
    text("课程").waitFor();
    text("课程").findOne().click();
    sleep(2000);
}


3.3 抢课前的准备

3.3.1 提前设置好每周的抢课表

根据自己需要进行修改,仅作为方法演示
在这里插入图片描述

//提前设置一周内每天要上的课程
function geteverydayclass(num) {
    var reserclass = new Array();
    switch (num) {
        //星期天~星期一
        case 0: 
            reserclass.push("JAZZ FUNK 入门课程---爆米花");
            break;
        case 1:
            reserclass.push("HIPHOP/CHOREO入门课程---大灰");
            break;
        case 2: 
            reserclass.push("JAZZ入门课程---大灰");
            break;
        case 3:
            reserclass.push("芬仔");
            //reserclass.push("晚上");
            break;
        case 4: 
            reserclass.push("HIPHOP/CHOREO入门课堂---芬仔");
            break;
        case 5: 
            reserclass.push("CHOREO/HIPHOP入门课程---糖果");
            break;
        case 6:
            reserclass.push("JAZZ入门课程---糖果");
            break;
    }
    return reserclass;
}
3.3.2 根据抢课表筛选出要抢的课程

根据前面设定的抢课表,筛选出今天要抢的课

手动操作流程
  • 点击明天(或者明天对应的星期)这个小程序有时候按钮会显示星期而不是“明天“
  • 点击筛选按钮
  • 点击重置重新筛选
  • 根据设定的抢课表,点击选择课程
  • 点击确定
  • 界面就筛选出符合要求的课程了,比如选择明天强子老师的课程,筛选结果有以下两个
    在这里插入图片描述 在这里插入图片描述 在这里插入图片描述
代码部分
//将数字转化为星期时间
function get_dayofweek_tomo(dayofweek_tomo){
    var list1  = new Array("周日","周一","周二","周三","周四","周五","周六");
    return list1[dayofweek_tomo];
}
//根据要上的课进行筛选(输入项:明天的星期)
function ChooseForDay(dayofweek_tomo) {
    sleep(3000);
    //点击明天的课表
    var tip = get_dayofweek_tomo(dayofweek_tomo);
    if (className("android.widget.TextView").text("明天").exists()){
        className("android.widget.TextView").text("明天").findOne().click();
    }
    else if (className("android.widget.TextView").text(tip).exists()){
        className("android.widget.TextView").text(tip).findOne().click();
    }

    sleep(2000);
    //点击筛选
    className("android.widget.TextView").text("筛选").findOne().click();
    log("筛选中");
    sleep(2000);
    //先重置一下
    className("android.widget.Button").text("重置").waitFor();
    className("android.widget.Button").text("重置").findOne().click();
    log("重置");
    sleep(2000);
    //选出明天要上的课
    var chooseclass = geteverydayclass(dayofweek_tomo);
    //测试
    //var chooseclass = geteverydayclass(0);
    for (var i = 0; i < chooseclass.length; i++) {
        //如果打错了不存在的课
        //if (!className("android.widget.Button").text(chooseclass[i]).exists()) {
        if (!className("android.widget.Button").textContains(chooseclass[i]).exists()) {
            log(chooseclass[i] + "不存在");
            continue;
        }
        //点击符合的,提前进入到抢课页面
        className("android.widget.Button").textContains(chooseclass[i]).untilFind().forEach(function(tv){
            tv.click();
        });
    }
    sleep(1000);
    //点击确定
    className("android.widget.Button").text("确定").waitFor();
    className("android.widget.Button").text("确定").findOne().click();
    log("确定");
    sleep(3000);
}

3.4 等待抢课时间的到来

  • 到达真正的抢课时间便开始抢课
//查看抢课时间
function checkTime(hour, minute, layback) {
    var currentTime = new Date();
    var targetTime = new Date();
    targetTime.setHours(hour);
    targetTime.setMinutes(minute);
    targetTime.setSeconds(0);
    targetTime.setMilliseconds(0);
 
    // 如果当前时间超过了目标时间,则将目标时间设置到第二天
    //(此条一般不会被执行,除非抢课晚了
    if (currentTime > targetTime) {
        //到时间也要先sleep
        sleep(2000);
        log("到时间了");
        return true;
    }
    // 一般会执行这一条:如果没到时间,延时到达目标时间
    var waittime = targetTime - currentTime;
    log("等待" + String(waittime/1000) + "秒自动抢课");toast("等待" + String(waittime/1000) + "秒自动抢课");
    sleep(waittime + layback);
    return true;
}

3.5 依次点击预约列表进行预约

  • 经过筛选后,界面显示所有待预约的课程
  • 先进入到一个预约页面
  • 到达时间了再点击“马上预约”
  • 预约成功后会出现上述界面,点击确定
  • 点击返回回到上面的界面,继续预约下一个课程,直到所有待预约的课程全部预约完成
  • 预约不到的课也进行排队抢位操作(会记录最终的预约情况)
    在这里插入图片描述 在这里插入图片描述
//预约课程
function reservation (CLASSHOUR, CLASSMINUTE, LAYBACK) {
    sleep(3000);
    //找到所有的预约按钮
    className("android.widget.Button").waitFor();
    while (true) {
        sleep(1500);
        var allclass1 = className("android.w   idget.Button").find();
        sleep(1500);
        //找两次,看看数量一不一样
        var allclass2 = className("android.widget.Button").find();
        if (allclass2.length == allclass1.length) {
            var allclass = allclass2;
            break;
        }        
    }
    //第0位是空按钮,所以用shift删掉一个
    allclass.shift();
    log(allclass.length);
    //是否约上课,0是未预约,1是成功预约,2是已经在排位
    var ifsuccess = new Array(allclass.length).fill(0);
    //记录所有的课程名
    try {
        var allclass_time = className("android.widget.TextView").textContains("~").untilFind();
        //var allclass_name = className("android.widget.TextView").textContains("课程——").untilFind();
        log("今日待约课程——"+String(allclass_time.length));
        toast("今日待约课程数量:"+String(allclass_time.length));
    }  catch(error) {
        log("课程识别有问题");
    }
    var retry = 0;
    //预约开始
    for(var i = 0; i < allclass.length; i++) {
        var one = allclass[i];
        //预约,余x席,加入等位都是可以预约的,其他的没到时间不能预约
        if(one.text().length < 6 && one.text() != "再约一节") {
            log("准备预约"+ String(allclass_time[i].text()));
            toast("准备预约"+ String(allclass_time[i].text()));
            //点击预约按钮
            toast(one.text());log(one.text());
            one.click();
            //查看是否到时间,没到时间就先等等(?)
            while(true) {
                if(checkTime(CLASSHOUR, CLASSMINUTE, LAYBACK)) {
                    break;
                }
            }
            //进入到会员卡选择的界面
            //className("android.widget.TextView").text("会员卡选择").waitFor();
            //进入到会员卡选择的界面
            //id("go").waitFor();
            //sleep(2000);
            //className("android.widget.Button").clickable(true).depth(7).waitFor();
            if (className("android.widget.Button").clickable(true).depth(7).exists()){
                var yuyue = className("android.widget.Button").clickable(true).depth(7).findOne();
                yuyue.click();
                //sleep(2000);
            } else {
                //直接点了管不了了
                log("找不到等位的控件,直接点击了");
                click(737,2324);
                sleep(2000);
            }
            //3是提示,4是里面的文字
            var warning = className("android.widget.TextView").depth(4).findOne().text();
            log(warning);
            toast(warning);
            //如果是请稍后重试
            //找不到提示语点击确定
            id("mm_alert_ok_btn").findOne(5000);
            if (id("mm_alert_ok_btn").exists()){
                sleep(500);
                id("mm_alert_ok_btn").findOne().click();
                sleep(500);
            } else {
                sleep(500);
                //直接点了管不了了
                log("找不到确认的控件,直接点击了");
                click(530, 1413);
                sleep(2000);
            }
            //如果是请稍后重试(只试3遍,超了就算了)
            if (warning == "请稍后重试") {
                sleep(4000);                
                if (retry < 3) {
                    //三秒后再试
                    i = i - 1;
                    retry = retry + 1;
                    continue;                    
                }
                else {
                    continue;
                }
            }
            //sleep(2000);
            //等待跳转到课程详情
            className("android.widget.TextView").text("课程信息").findOne(5000);
            //看看怎么回事
            //1、显示在等位
            if(className("android.widget.TextView").text("等位中").exists()) {
                ifsuccess[i] = 2;
                toast(String(allclass_time[i].text())+"成功等位");
            }
            if (className("android.widget.TextView").text("已预约").exists()){
                toast(String(allclass_time[i].text())+"成功预约");
                ifsuccess[i] = 1;
            }
            //返回键(?)
            sleep(2000);
            if (className("android.widget.TextView").text("").exists()) {
                className("android.widget.TextView").text("").findOne().click();
            }  if (!className("android.widget.TextView").text("").exists()) {
                log("没找到返回键,直接点了")
                click(49,179);
            }
        }
        sleep(2000);
    }
    log(ifsuccess);
    toast(ifsuccess);
}

4、无法解决的问题

  • 😰有时候无法正确识别出界面的控件,使用了waitFor()以后有时阻塞完全卡住,也找不到其他的办法,只能将识别不出的控件的位置记录下来,改成点击控件所在的位置。这样写会导致兼容性极低,只能自己用,换个设备就会出问题。只能增加加载时间(凭运气),尽量加载出控件再点击,但是有些控件无论等多久都加载不出来,还是有点难受的。。。

  • 所以该脚本仅供参考,换到其他手机上需要多次调试再使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值