tiktok.js

自用程序调用库

当前为 2024-12-13 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/518778/1502215/tiktokjs.js

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

auto.waitFor();
global.AppName = "抖音极速版";
global.packageName = "com.ss.android.ugc.aweme.lite";
global.running = false; //是否正在刷视频中
global.working = false; //是否正在刷视频中
global.startSec = Date.now();//刷视频计时
global.Samelogin = true; //是否为同一个账户切换
global.img_box = storages.create("tiktok").get('img_box');
global.img_close = storages.create("tiktok").get('img_close');
global.img_single = storages.create("tiktok").get('img_single');
global.img_adtitle = storages.create("tiktok").get('img_adtitle');
global.ver = 'v2.9';//版本号
if (!auto.service || device.width == 0) {
    console.warn("2.请重新开启无障碍服务");
    auto.service.disableSelf();
    app.startActivity({ action: "android.settings.ACCESSIBILITY_SETTINGS" });
    android.os.Process.killProcess(android.os.Process.myPid());
}
if (device.fingerprint + '/' + ver != storages.create("tiktok").get('device_info')) { setTimeout(function () { update(); }, 60 * 1000); }
engines.all().map((ScriptEngine) => { if (engines.myEngine().toString() !== ScriptEngine.toString()) { ScriptEngine.forceStop(); } });

/**点赚钱图标是否进入了照相机 */
function ifcarmer(btn) {
    var fudai=className('Button').desc('福袋').boundsInside(device.width/2, 0, device.width, 500).visibleToUser(true).findOne(1000);
    if (fudai) {
        if(btn)click(fudai.bounds().centerX(), fudai.bounds().centerY());//点赚钱图标
        sleep(2000);
        return false;
    }
    if (className('ImageView').desc('拍摄,按钮').boundsInside(0, device.height-300, device.width, device.height).visibleToUser(true).findOne(1000)) {
        toastLog('1.当前账户没有赚钱福袋');
        console.info('【ifcarmer_截图】',currentActivity());
        //截图保存界面,以备后续查看
        captureScreen(files.getSdcardPath() + '/脚本/ifcarmer_' + currentActivity() + '.png');
        return true;
    }
    if(btn)click(device.width / 2, btn.bounds().centerY());//点赚钱图标
    sleep(2000);
    if (currentActivity().match(/.*VideoRecordNewActivity/)) {
        back();
        toastLog('2.当前账户没有赚钱福袋');
        return true;
    } 
    return false;
}

//执行赚钱任务函数
function takelist() {
    toastLog("查找看广告赚金币位置");
    swipe(device.width / 2, device.height * 0.2, device.width / 2, device.height * 0.8, random(500, 1500));
    var adstr=className('android.view.View').descMatches(/^\d{2}:\d{2}$/).boundsInside(device.width-500, 0, device.width, device.height).visibleToUser(true).findOne(1000);
    if(adstr){
        toastLog('等待赚金币倒计时'+adstr.desc());
        return;
    }
    var findtask = className('android.view.View').descMatches(/.*分钟完成一次广告任务.*/).visibleToUser(true).findOne(1000);
    var taskpage = findtask?findtask:FindPicture('img_box');
    if (!taskpage) {
        toastLog("不在任务页");
        var homebtn = getHomeBtn();
        if (!homebtn) {homebtn=gohome();}
        if(homebtn){
            toastLog("点击首页赚钱");
            if (ifcarmer(homebtn)) { return; }
            sleep(3000);
            toastLog("是否回到任务页");
            taskpage = FindPicture('img_box');
        }
    }
    if (taskpage) {
        findtask = className('android.view.View').descMatches(/.*分钟完成一次广告任务.*/).visibleToUser(true).findOne(1000);
        var lookAd=findtask?findtask:FindPicture('img_adtitle');
        if (lookAd) {
            toastLog("点击看广告赚金币");
            click(device.width-500, findtask?findtask.bounds().centerY():lookAd.centerY);
            sleep(3000);
            //关闭自动弹出的层
            if (currentActivity().match(/.*BulletContainerActivity|.*NoMarginSheetBaseDialog/)) { 
                console.log('点左上角关闭弹出层takelist');click(80, 150); left2right(2);sleep(1000);left2right(2);sleep(1000);
            }
            var living = id("root").desc("关闭").clickable(true).boundsInside(device.width-300, 0, device.width, 300).visibleToUser(true).findOne(1000);//直播间
            if (living) {
                toastLog('退出直播间takelist');
                //退出直播间
                click(living.bounds().centerX(), living.bounds().centerY());
                sleep(3000);
            }
            var videopage = className("com.lynx.tasm.behavior.ui.text.FlattenUIText").desc("反馈").boundsInside(0, 0, device.width, 300).visibleToUser(true).findOne(1000);
            if (videopage) {
                playvideo();
                toastLog('看广告赚金币任务完成');
            }else{
                toastLog('等待看广告赚金币倒计时');
            }
        }else{
            //无法定位是因为刚看过有倒计时,被排后列表后面了,当然也有可能是被弹出的签到遮挡了
            toastLog('无法定位看广告赚金币位置');
            storages.create("tiktok").put('singlecheck', 0);
            singlecheck();
            //console.info('无法定位看广告赚金币位置【截图】takelist1_');
            //截图保存界面,以备后续查看
            //captureScreen(files.getSdcardPath() + '/脚本/takelist1_' + currentActivity() + '.png');
        }
    } else {
        toastLog('无法定位宝箱位置');
        console.info('【截图】takelist2_');
        //截图保存界面,以备后续查看
        captureScreen(files.getSdcardPath() + '/脚本/takelist2_' + currentActivity() + '.png');
    }
}

//判断进入看广告视频赚金币
function playvideo() {
    function stopvideo(j) {
        let plug = ['很糟糕', '一般般', '太赞了'];
        var s = plug[2];//randomPointLoc(0,3)
        var uc = className("com.lynx.tasm.behavior.ui.text.UIText").desc(s).visibleToUser(true).findOne(1000);
        if(uc){
            toastLog(j+'.2.点击评价:' + s);
            click(uc.bounds().centerX(), uc.bounds().centerY() - 50);
            sleep(3000);
            toastLog(j+'.3.点击收下金币');
            click(uc.bounds().centerX(), uc.bounds().centerY() + 230);
            sleep(1000);            
        }
    }
    working=true;
    console.error('开始循环看广告playvideo');
    for (var i = 1; i < 99; i++) {
        console.log(i+'.----------------------------');
        if (text('当前无新视频').visibleToUser(true).findOne(1000)) {
            toastLog(i+".当前无新视频");
            click(device.width - 100, 100);
            sleep(1000);
        }
        //关闭自动弹出的层
        if (currentActivity().match(/.*BulletContainerActivity|.*NoMarginSheetBaseDialog/)) { 
            console.log('点左上角关闭弹出层playvideo');click(80, 150); left2right(2);sleep(1000);left2right(2);sleep(1000);
        }
        //判断进入了直播间
        var living = id("root").desc("关闭").clickable(true).boundsInside(device.width - 300, 0, device.width, 300).visibleToUser(true).findOne(1000);//直播间
        if (living) {
            toastLog(i+'.0.退出直播间');
            click(living.bounds().centerX(), living.bounds().centerY());
            sleep(3000);
        }
        //判断是否进入了广告页面/^playvideocom.bytedance.*/
        var adpage = className("android.widget.Button").desc("返回").boundsInside(0, 0, 300, 300).visibleToUser(true).findOne(1000);
        if (adpage) {
            toastLog(i+'.0.退出广告页');
            click(adpage.bounds().centerX(), adpage.bounds().centerY());
            sleep(3000);
            var tv = text('评价并收下金币').visibleToUser(true).findOne(1000);
            if (tv) {
                stopvideo(i);
                break;
            }
        }
        video = className("com.lynx.tasm.behavior.ui.text.FlattenUIText").desc("反馈").boundsInside(0, 0, device.width, 300).visibleToUser(true).findOne(1000);
        if (!video) {
            toastLog(i+'.0.未进入广告视频模式');
            //截图保存界面,以备后续查看
            if(!currentActivity().match(/.*PrimaryDialog/)){
                console.info('【playvideo截图】',currentActivity());
                captureScreen(files.getSdcardPath() + '/脚本/playvideo_' + currentActivity() + '.png');
            }
            stopvideo(i);
            break;
        }
        var okbtn = getOKBtn();
        if (!okbtn) {
            toastLog(i+'.无法取得领取成功,点击右上角');
            click(device.width - 200, video.bounds().centerY());
            okbtn = true;
            sleep(5000);
        } else {
            if (okbtn.desc().match(/领取成功.*/)) {
                var t = random(8,13);
                cutDownBySleep(t,i+'.0.等待');
                //这里经过长时间等待可能自动进入直播间,okbtn已经不在了,所以需要再找一次
                okbtn = getOKBtn();
                if(okbtn){
                    toastLog(i+'.0.点击领取成功');
                    click(okbtn.bounds().centerX(), okbtn.bounds().centerY());
                    sleep(3000);
                }
                okbtn = true;//防止break;
            } else {
                var b = okbtn.desc().match(/\d+/);
                var t = 1 * (b ? b[0] : 1) + random(8,13);
                cutDownBySleep(t,i+'.1.等待');
                //这里经过长时间等待可能自动进入直播间,okbtn已经不在了
                okbtn = getOKBtn();
                if(okbtn){
                    toastLog(i+'.1.点击领取成功');
                    click(okbtn.bounds().centerX(), okbtn.bounds().centerY());
                    sleep(3000);
                }
                okbtn = true;//防止break;
            }
        }
        if (okbtn) {
            //随时点击弹出层className("com.lynx.tasm.behavior.ui.text.FlattenUIText")
            var tv = textMatches(/继续观看|领取奖励|评价并收下金币/).visibleToUser(true).findOne(3000);
            if (tv&&isRectInScreen(tv.bounds())) {
                try {
                    if (tv.text() == '评价并收下金币') {
                        stopvideo(i);
                        break;
                    } else {
                        toastLog(i+'.2.点击' + tv.text());
                        click(tv.bounds().centerX(), tv.bounds().centerY());
                        sleep(3000);
                        var a = textMatches(/再看\d{1,2}秒可领奖励|评价并收下金币/).visibleToUser(true).findOne(3000);
                        if (a) {
                            if (a.text() == '评价并收下金币') {
                                stopvideo(i);
                                break;
                            } else {
                                var b = a.text().match(/\d+/);
                                var t = 1 * (b ? b[0] : 1) + random(8,13);
                                cutDownBySleep(t,i+'.2.等待');
                            }
                        }
                    }
                } catch (e) {
                    console.info(i+'.3.点击' + tv.text() + '=====' + e);
                }
                sleep(1000);
            }
        } else {
            break;
        }
        sleep(1000);
    }//end for
    console.error('循环看广告结束playvideo');
    working=false;
}

//判断签到层
function singlecheck() {
    toastLog('判断签到提示');
    var today = new Date();
    if(storages.create("tiktok").get('singlecheck')==today.getDate()){
        toastLog('今天已完成签到');
        return;
    }
    var singletxt = className('android.view.View').textMatches(/.*签到.*/).visibleToUser(true).findOne(1000);
    if(singletxt){
        click(singletxt.bounds().centerX(), singletxt.bounds().top - 20);
        sleep(3000);
    }
    //图像识别查找
    var single = FindPicture('img_single');
    if (single) {
        toastLog('1.点击立即签到',click(single.left, single.top - 130));
        sleep(3000);
        //single = FindPicture('img_single');
        toastLog('2.再次点击签到',click(single.left, single.top - 130));
        sleep(3000);
        var popup = textMatches(/手机充值|确认身份信息/).visibleToUser(true).findOne(1000);
        if (popup) {
            toastLog('3.关闭弹出层:'+click(80, popup.bounds().centerY()));
            sleep(2000);
            var div = textMatches(/领惊喜现金|确认放弃.*/).visibleToUser(true).findOne(1000);
            if (div) {
                if (div.text() != '领惊喜现金') {
                    click(div.bounds().left + 50, div.bounds().top + 50);
                    sleep(1000);
                }
                back();
            }
        }
        sleep(3000);
        toastLog('4.是否进入看视频');
        var video = className("com.lynx.tasm.behavior.ui.text.FlattenUIText").desc("反馈").boundsInside(0, 0, device.width, 300).visibleToUser(true).findOne(1000);
        if (video) {
            playvideo();
        }else{
            toastLog('5.没有进看视频');
        }
        console.error('6.签到结束关闭');
    } else {
        toastLog('没有签到提示');
    }
    storages.create("tiktok").put('singlecheck', today.getDate());//记录是否检测过签到
}

//点击右下角宝箱函数
function moneybox() {
    toastLog('查找右下角宝箱的位置');
    var boximg = FindPicture('img_box');
    if (!boximg) {
        toastLog("不在任务页moneybox");
        var homebtn = getHomeBtn();
        if (!homebtn) {homebtn=gohome();}
        if(homebtn){
            toastLog("点击首页赚钱moneybox");
            if (ifcarmer(homebtn)) { return; }
            sleep(3000);
            toastLog("是否回到任务页");
            boximg = FindPicture('img_box');
        }
    }
    if (boximg) {
        console.error('点击右下角宝箱moneybox');
        click(boximg.centerX, boximg.centerY);
        sleep(7000);
        let x=FindPicture('img_close');
        if(x){
            click(x.centerX,x.centerY-220);//再次点弹出宝箱层中的红色按钮
            sleep(3000);
        }else{
            toastLog('等待宝箱倒计时');
            return;
        }
        //如果进入看视频赚金币则观看视频
        //if(currentActivity()=='com.ss.android.excitingvideo.ExcitingVideoActivity')
        if (className("com.lynx.tasm.behavior.ui.text.FlattenUIText").desc("反馈").boundsInside(0, 0, device.width, 300).visibleToUser(true).findOne(1000)) {
            playvideo();
            console.error('宝箱任务结束moneybox');
        } else {
            toastLog('未进入宝箱视频');
        }
    } else {
        console.info('【截图】moneybox_');
        //截图保存界面,以备后续查看
        captureScreen(files.getSdcardPath() + '/脚本/moneybox_' + currentActivity() + '.png');
    }
}

//主程序函数===============================================================
function Main() {
    var loopTimes = random(3,5); //work循环次数
    function work() {
        toastLog("开始工作work");
        var homebtn = getHomeBtn();
        if (!homebtn) {
            toastLog("当前不在首页");
            homebtn = gohome();
        }
        if (homebtn) {
            toastLog("点赚钱图标进任务页");
            if (!ifcarmer(homebtn)) {
                sleep(5000);
                //检测签到提示是否弹出到【任务页】
                singlecheck();
                sleep(2000);
                //点击看广告视频赚金币
                takelist();
                sleep(3000);
                //点击右下角宝箱
                if(loopTimes==1){
                    moneybox();
                    sleep(3000);
                }
            }
        }
        //回到首页准备刷视频
        console.error("开始刷视频模式+++++++++++++");
        gohome(); startSec = Date.now(); gogogo(50);
        console.error("刷视频模式结束+++++++++++++");
    }
    //打开抖音App
    if (getPackageName(AppName)) {
        openApp(AppName);
        //等待进入主界面成功
        toastLog('进入主函数'+ver);
        gohome();
        sleep(3000);
        //先检测签到提示是否自动弹出在【首页】
        //singlecheck();
        //sleep(3000);
        toastLog("刚启动先刷视频提高活跃度");
        gogogo(20);

        while (loopTimes > 0) {
            work();//开始工作
            sleep(5000);
            loopTimes--;
        }

        Samelogin = true;
        chengaccound();//切换账号

        if (Samelogin) { 
            loopTimes = 0; 
            console.warn('账号相同挂机等待'); 
        } else {
            console.warn('用新账号继续循环');
            storages.create("tiktok").put('singlecheck', 0);//新账号就需要再次检测签到
            //切换了新账号需要重启应用
            closeApp(AppName);
            sleep(5000);
            openApp(AppName);
            sleep(5000);
            Main();
            return;
        }

        console.clear();
        console.warn('运行结束关闭应用');
    } else {
        console.warn("未安装:" + AppName);
        work_thread.interrupt();
        device.cancelKeepingAwake();
        engines.myEngine().forceStop();
        return;
    }
    console.show();
    console.warn('执行完成用时' + SecondsToHMS((Date.now() - starttime) / 1000));
    cutDownBySleep(5,'5秒后进入息屏挂机模式');
    console.hide();
    closeApp(AppName);
    sleep(3000);
    oled(random(600,900));//熄屏挂机约10~15分钟左右
}
function getHomeBtn(n){
    return className("TextView").desc("首页,按钮").boundsInside(0, device.height-300, 300, device.height).visibleToUser(true).findOne(n?n:1000);
}
function getOKBtn(n){
    return className("com.lynx.tasm.behavior.ui.view.UIView").descMatches(/领取成功.*|\d{1,2}秒后可领奖励.*/).boundsInside(device.width / 2, 0, device.width, 300).visibleToUser(true).findOne(n?n:1000);
}
function toActivePage(page) {
    page=page||'';
    try{
        let intent = new Intent();
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        let package = packageName;//这个参数是包名必须定义
        let className = page;//这个必须准确
        let componentName = new android.content.ComponentName(package, className);
        intent.setComponent(componentName);
        context.startActivity(intent);
    }catch(e){
        console.warn('跳转失败,尝试重启软件并开启无障碍服务');
    }
}
function gohome() {
    toastLog('回到首页gohome');
    openApp(AppName);
    toActivePage('com.ss.android.ugc.aweme.main.MainActivity');
    sleep(3000);
    //var homepage = className("Button").descStartsWith("侧边栏").clickable(true).boundsInside(0, 0, 500, 500).visibleToUser(true).findOne(1000);
    //if(!homepage){
    //    console.log('gohome:',currentActivity());//????????????????????
    //}
    var MaxLoop = 5;
    var homebtn = getHomeBtn();
    while (!homebtn && MaxLoop > 0) {
        MaxLoop--;
        back(); sleep(3000);
        homebtn = getHomeBtn();
    }
    if (!homebtn) {
        toastLog('需要重启软件【截图】');
        //截图保存界面,以备后续查看
        captureScreen(files.getSdcardPath() + '/脚本/gohome1_' + currentActivity() + '.png');
        sleep(1000);
        device.wakeUp();//唤醒设备
        sleep(1000);
        left2right(1);
        sleep(1000);
        closeApp(AppName);
        sleep(5000);
        openApp(AppName);
        homebtn = getHomeBtn();
    }
    back(); sleep(1000);
    randomHeart(7);
    return homebtn;
}
function randomHeart(num) {
    if (7!=num&&text('当前无新视频').visibleToUser(true).findOne(1000)) {
        console.log("当前无新视频");
        click(device.right - 100, device.top - 100);
        randomHeart(7);//切换频道
        sleep(1000);
        return;
    }
    let randomIndex = num ? num : random(1, 30);
    //随机切换频道
    if (randomIndex == 7) {
        var idList = [];
        className('TextView').clickable(true).descEndsWith(',按钮').boundsInside(0, 0, device.width, 300).find().forEach(function(tv){
            if(!tv.desc().match(/.*已选中.*|.*团购.*|.*商城.*|.*热点.*|.*直播中.*/)){
                //log(tv.desc());
                idList.push(tv);
            }
        });
        if(idList.length>0){
            let index = random(1, idList.length) - 1;
            console.log('拟人:切换频道:' + idList[index].desc());
            idList[index].click();
            sleep(2000);
            if (text('发现通讯录朋友').visibleToUser(true).findOne(1000)) {
                console.log("关注中没有视频");
                randomHeart(7);//再次切换频道
                slidingByCurve();
            }
            return;
        }
    }
    //随机恢复到首页
    if (randomIndex % 5 == 0) {
        console.log('拟人:随机回首页');
        gohome();
        return;
    }
    //随机点赞
    if (randomIndex % 6 == 0) {
        var like = className("android.widget.LinearLayout").descStartsWith('未点赞').clickable(true).boundsInside(device.width-300, device.height/2, device.width, device.height).visibleToUser(true).findOnce();
        if (like) {
            console.log('拟人:随机点赞');
            click(like.bounds().centerX(),like.bounds().centerY());
            return;
        }
    }
    //随机评论
    if(randomIndex % 7 == 0){
        var plug = className("android.widget.LinearLayout").descStartsWith('评论').clickable(true).boundsInside(device.width-300, device.height/2, device.width, device.height).visibleToUser(true).findOne(1000);
        if(plug){
            console.log('拟人:随机评论');
            //长按评论按钮
            longClick(plug.bounds().centerX(),plug.bounds().centerY());
            sleep(1000);
            var plugdiv=id('root_view').className('android.view.ViewGroup').visibleToUser(true).findOne(1000);
            if(plugdiv){
                let icoY=plugdiv.bounds().bottom-90;
                let icoX=[
                    plugdiv.bounds().left+90,
                    plugdiv.bounds().left+220,
                    plugdiv.bounds().left+350,
                    plugdiv.bounds().left+500
                ];
                let index = random(1, icoX.length) - 1;
                //console.log(icoX[index],icoY);
                click(icoX[index],icoY);
                sleep(3000);
            }
        }
    }
    //随机下滑
    if (randomIndex % 8 == 0) {
        console.log('拟人:随机下滑');
        swipe(device.width / 2, device.height * 0.1 + randomIndex, device.width / 2, device.height * 0.9 - randomIndex, random(500, 1500));
        return;
    }
    //随机左右划
    if (randomIndex % 9 == 0) {
        left2right(2);
        return;
    }
    //连续上滑
    if (randomIndex % 10 == 0) {
        console.log('拟人:连续上滑');
        var k = random(2, 4);
        for (var i = 0; i < k; i++) {
            var j = random(2, 5);
            if (j == 3) {
                swipe(device.width / j, device.height * 0.1 + j * k, device.width / j, device.height * 0.9 - j * k, j * 50);
            } else {
                swipe(device.width / j, device.height * 0.9 - j * k, device.width / j, device.height * 0.1 + j * k, j * 50);
            }
            sleep(j * 250);
        }
        return;
    }
    //向上滑
    slidingByCurve();
}
function isvideoPage() {
    var isvideo = false;
    //var taskpage = FindPicture('img_box');
    var taskpage = className('android.view.View').descMatches(/.*分钟完成一次广告任务.*/).visibleToUser(true).findOne(1000);
    if (!taskpage) {
        console.verbose("检测是否视频播放中isvideoPage");
        //关闭自动弹出的层
        if (currentActivity().match(/.*BulletContainerActivity|.*NoMarginSheetBaseDialog|.*DuxBasePanelDialog/)) { 
            console.log('点左上角关闭弹出层isvideoPage');click(80, 150);left2right(2);sleep(1000);left2right(2);sleep(1000);
        }
        if(currentActivity() =='com.ss.android.ugc.aweme.main.MainActivity'){
            isvideo = true;
        }else if(getHomeBtn()){
            isvideo = true;
        }else{
            var div0=textMatches(/分享给|不感兴趣|建群分享|返回/).visibleToUser(true).findOne(1000);
            var div1=descMatches(/分享给|不感兴趣|建群分享|返回/).visibleToUser(true).findOne(1000);
            if (div0||div1) {
                isvideo = true;
                toastLog('0.关闭弹出层');
                //captureScreen(files.getSdcardPath() + '/脚本/isvideoPage_' + currentActivity() + '.png');
                //sleep(1000);
                click(80, 150);
                sleep(3000);
            }
        }
        var view = descMatches(/播放视频.*|暂停视频.*|.*进入直播间.*/).visibleToUser(true).findOne(1000);
        if (view) {
            isvideo = true;
            console.verbose("正在播放视频:" + view.desc());
        }
        var living = id("root").desc("关闭").clickable(true).boundsInside(device.width-300, 0, device.width, 300).visibleToUser(true).findOne(1000);//直播间
        if (living) {
            isvideo = true;
            toastLog("1.退出直播间");
            click(living.bounds().centerX(), living.bounds().centerY());
            sleep(2000);
        }
        if (currentActivity() == 'com.ss.android.ugc.aweme.live.LivePlayActivity') {
            toastLog("2.退出直播间");
            isvideo = true;
            back();
        }
    }
    return isvideo;
}
function gogogo(n) {
    let gotime = random(3,6); //刷视频每n分钟结束一次
    for (var i = 1; i <= n; i++) {
        let flashtime=parseInt((Date.now() - startSec) / 1000);
        console.log('第'+i+'次刷视频,累计用时:',flashtime,'秒');
        if( flashtime > gotime*60){console.warn(gotime+'分种超时,停止刷视频'); running = false; floaty.closeAll(); break;}
        if (isvideoPage()) {
            running = true;
            var adbutton = className('com.lynx.tasm.behavior.ui.view.UIView').descMatches(/提交,按钮.*/).boundsInside(0, 2*device.height/3, device.width, device.height).visibleToUser(true).findOne(1000);
            if (adbutton) {
                //广告视频则多停留一个周期
                left2right(2);
                cutDownBySleep(random(5, 9),'广告停留:');
                //进入广告看详情
                if (!descMatches(/立即下载|立即领取/).boundsInside(0, 2*device.height/3, device.width, device.height).visibleToUser(true).findOnce()) {
                    click(adbutton.bounds().centerX(), adbutton.bounds().centerY());
                    cutDownBySleep(random(5, 9),'广告详情:');
                    back();
                }
                cutDownBySleep(random(5, 9),'观看广告:');
            }
            randomHeart();//拟人化
            cutDownBySleep(random(6, 30),'观看视频:');//每个视频随机时间 6-30s
        } else {
            floaty.closeAll();
            running = false;
            toastLog('not at the video page');
            var dialog = currentActivity();
            if (!dialog.match(/android\.app\.Dialog|android\.widget\.FrameLayout/)) {
                //截图保存界面,以备后续查看
                console.info('【gogogo截图】',dialog);
                captureScreen(files.getSdcardPath() + '/脚本/gogogo_' + dialog + '.png');
                gohome();
            }
            sleep(3000);
        }
    }
    running = false;
}
function cutDownBySleep(lasterSecend, message) {
    message = message || "";
    floaty.closeAll();
    var fwin = floaty.rawWindow(
        `<frame id="frame" alpha="0" w="{{device.width-500}}px" h="150px" marginTop="{{device.height-550}}px" marginLeft="250px" marginRight="250px">
            <card w="auto" h="auto" layout_gravity="center" cardCornerRadius="5dp" cardBackgroundColor="#eeeeee" >
                <text id="title" text="" textColor="#333333" textSize="13sp" padding="12 8" />
            </card>
        </frame>`
    );
    fwin.setTouchable(false);
    sleep(300);
    for (let i = lasterSecend; i >= 0; i--) {
        if (!running && !working) { break; }
        if (!fwin || !fwin.title) { break; }
        ui.run(() => { fwin.title.setText(message + "剩余" + i + "秒"); fwin.frame.attr("alpha", 0.8); });
        sleep(1000);
    }
    fwin=null;
    floaty.closeAll();
}
function slidingByLine() {
    // top X,Y范围
    tx = randomPointLoc(parseInt(device.width / 3), parseInt(device.width / 2));
    ty = randomPointLoc(parseInt(device.height / 5), parseInt(device.height / 4));
    // bottom X,Y 范围
    bx = randomPointLoc(parseInt(device.width / 3), parseInt(device.width / 2));
    by = randomPointLoc(parseInt(3 * device.height / 4), parseInt(4 * device.height / 5));

    slidingTime = randomRangeTime(0.8, 1.3);
    log("上滑:随机直线");
    //log("X: "+ Math.abs(bx-tx) + " Y: "+ Math.abs(by - ty));
    swipe(bx, by, tx, ty, slidingTime);
}
function left2right(direction) {
    var intX=parseInt(Math.random()*200+400);
    var intY=parseInt(Math.random()*200+200);
    var distance=parseInt(Math.random()*100+device.height/4);
    switch (direction) {
        case 1:
            //向上小距离
            sml_move(intX, intY + distance, intX, intY, 400);
            break;
        case 2:
            //向下小距离
            sml_move(intX, intY, intX, intY + distance, 400);
            break;
        case 3:
            //向左翻屏
            sml_move(
                device.width / 2 + parseInt(Math.random() * 100) + 300,
                device.height / 4 - parseInt(Math.random() * 200) + 100,
                0 + parseInt(Math.random() * 100),
                device.height / 5 + parseInt(Math.random() * 100),
                500
            );
            break;
        case 4:
            //向右翻屏
            sml_move(
                device.width / 2 - parseInt(Math.random() * 100) - 300,
                device.height / 5 - parseInt(Math.random() * 200) + 100,
                device.width - parseInt(Math.random() * 100),
                device.height / 4 + parseInt(Math.random() * 100),
                500
            );
            break;
    }
    sleep(1000);
}
function slidingByCurve() {
    // top X,Y范围
    tx = randomPointLoc(parseInt(device.width / 3), parseInt(device.width / 2));
    ty = randomPointLoc(parseInt(device.height / 5), parseInt(device.height / 4));
    // bottom X,Y 范围
    bx = randomPointLoc(parseInt(device.width / 3), parseInt(device.width / 2));
    by = randomPointLoc(parseInt(2 * device.height / 3), parseInt(3 * device.height / 4));

    slidingTime = randomRangeTime(0.2, 0.6);
    log("上滑:仿真曲线");
    //log("X: "+ Math.abs(bx-tx) + " Y: "+ Math.abs(by - ty));
    sml_move(bx, by, tx, ty, slidingTime);
}
function randomPointLoc(start, end) {
    len = Math.abs(end - start);
    loc = Math.floor(Math.random() * len) + start;
    return loc;
}
function randomRangeTime(start, end) {
    len = Math.abs(end - start) * 1000;
    ms = Math.floor(Math.random() * len) + start * 1000;
    return ms;
}
function sml_move(qx, qy, zx, zy, time) {
    var xxy = [time];
    var point = [];
    var dx0 = {
        "x": qx,
        "y": qy
    };
    var dx1 = {
        "x": random(qx - 100, qx + 100),
        "y": random(qy, qy + 50)
    };
    var dx2 = {
        "x": random(zx - 100, zx + 100),
        "y": random(zy, zy + 50),
    };
    var dx3 = {
        "x": zx,
        "y": zy
    };
    for (var i = 0; i < 4; i++) {
        eval("point.push(dx" + i + ")");
    }
    // log(point[3].x)
    for (let i = 0; i < 1; i += 0.08) {
        let newPoint=bezier_curves(point, i);
        xxyy = [parseInt(newPoint.x), parseInt(newPoint.y)]
        xxy.push(xxyy);
    }
    try {
        gesture.apply(null, xxy);
    } catch (e) {
        log('error:',xxy);
    }
}
function bezier_curves(cp, t) {
    cx = 3.0 * (cp[1].x - cp[0].x);
    bx = 3.0 * (cp[2].x - cp[1].x) - cx;
    ax = cp[3].x - cp[0].x - cx - bx;
    cy = 3.0 * (cp[1].y - cp[0].y);
    by = 3.0 * (cp[2].y - cp[1].y) - cy;
    ay = cp[3].y - cp[0].y - cy - by;

    tSquared = t * t;
    tCubed = tSquared * t;
    result = {
        "x": 0,
        "y": 0
    };
    result.x = (ax * tCubed) + (bx * tSquared) + (cx * t) + cp[0].x;
    result.y = (ay * tCubed) + (by * tSquared) + (cy * t) + cp[0].y;
    return result;
}
function SecondsToHMS(seconds) {
    const hours = Math.floor(seconds / 3600);
    const minutes = Math.floor((seconds % 3600) / 60);
    const remainingSeconds = Math.floor(seconds % 60);
    return (hours > 0 ? hours + "时" : "") + (minutes > 0 ? minutes + "分" : "") + remainingSeconds + "秒";
}
function getindexInParent(child) {
    var parent = child.parent();
    for (var i = 0; i < parent.childCount(); i++) {
        if (parent.child(i).find(className('CheckBox').checked(true).visibleToUser(true)).length > 0) {
            return i;
        }
    }
    return -1; // 如果找不到子元素,则返回-1
}
function isRectInScreen(bounds) {
    var x = bounds.left, y = bounds.top,
        a = bounds.right, b = bounds.bottom;
    return (
      x >= 0 && x <= device.width &&
      y >= 0 && y <= device.height &&
      a > 0 && a <= device.width &&
      b > 0 && b <=device.height
    );
}
function openApp(appname){
    console.warn('启动应用:' + appname);
    var appstate = launchApp(appname);
    sleep(5000);
    if (appstate) {
        toastLog("应用正在运行");
    } else {
        toastLog("无法自启动,需模拟点击");
        home();//要启动的APP必须放在第一页中
        sleep(3000);
        var app = id("item_title").text(appname).visibleToUser(true).findOne(2000);
        if (app) {
            click(app.bounds().centerX(), app.bounds().top - 50);
            sleep(8000);
        }else{
            toastLog('要启动的APP必须放在首页,即按Home能看到的那一页');
            work_thread.interrupt();
            engines.myEngine().forceStop();
            exit();
        }
    }
}
function closeApp(appname) {
    let packageName = getPackageName(appname);
    // 使用ADB命令强行结束进程
    //shell("adb shell am force-stop " + packageName);
    console.warn('关闭应用:' + appname);
    app.openAppSetting(packageName);
    text(app.getAppName(packageName)).waitFor();
    let is_sure = textMatches(/.*强行停止.*/).visibleToUser(true).findOnce();
    if (is_sure&&is_sure.enabled()) {
        try {
            var btn = className("Button").text('强行停止').visibleToUser(true).findOnce();
            if (btn) btn.click();
            sleep(1000);
            btn = className("Button").text('强行停止').visibleToUser(true).findOnce();
            if (btn) btn.click();
            sleep(1000);
            btn = className("Button").text('确定').visibleToUser(true).findOnce();
            if (btn) btn.click();
            back(); back(); back();
            home();
        } catch (e) {
            log(app.getAppName(packageName) + "应用已被关闭");
            sleep(1000);
            back(); back(); back();
            home();
        }
    } else {
        log(app.getAppName(packageName) + "应用不能被正常关闭");
        back(); back(); back();
        home();
    }
}
function chengaccound() {
    console.warn("【准备切换登录账号】");
    //首先需要进入我的页面中
    if (!className("android.widget.TextView").desc('我,按钮').visibleToUser(true).findOnce()) {
        gohome();
        sleep(1000);
    }
    try {
        var me = className("android.widget.TextView").desc('我,按钮').visibleToUser(true).findOnce();
        if (me) click(me.bounds().centerX(), me.bounds().centerY());
        sleep(2000);
        var more = className("android.view.ViewGroup").desc('更多').findOnce();
        if (more) click(more.bounds().centerX(), more.bounds().centerY());
        sleep(2000);
        var setting = className("android.view.ViewGroup").desc('设置,按钮').findOnce().click();
        //if(setting)click(setting.bounds().centerX(),setting.bounds().centerY());
        sleep(2000);
        var account = className("android.widget.RelativeLayout").desc('切换账号').findOnce().click();
        //if(account)click(account.bounds().centerX(),account.bounds().centerY());
        sleep(2000);

        var checkbox = className("android.widget.CheckBox").checked(true).visibleToUser(true).findOnce().parent();
        var loginID = checkbox.child(0).text();//切换前登录的账户名
        //找到当前为选择状态的下一个兄弟节点并点击选择,如果没有下一个兄弟,则选择第一个兄弟
        var checknext = checkbox.parent().child(getindexInParent(checkbox) + 1);
        if (checknext && checknext.className() == checkbox.className()) {
            Samelogin=false;
            toastLog("选择下一个账号");
            checknext.click();
        } else {
            //切换前后是否为相同账户
            Samelogin=loginID==checkbox.child(0).text();
            toastLog("选择第一个账号:"+Samelogin);
            checkbox.parent().child(0).click();
        }
        sleep(1000);

        var j = 0;
        var backbtn = id("back_btn").desc('返回').findOnce();
        while (backbtn) {
            backbtn.click();
            sleep(1000);
            backbtn = id("back_btn").desc('返回').findOnce();
            if (text('更多功能').findOnce()) {
                back();
                break;
            }
            if (j > 5) break;
        }
        back();
    } catch (e) {
        console.warn("切换登录账号失败");
    }
}
function isDeviceLocked() {
    importClass(android.app.KeyguardManager);
    importClass(android.content.Context);
    var km = context.getSystemService(Context.KEYGUARD_SERVICE);
    return { 'isScreenOn': device.isScreenOn(), 'isLocked': km.isKeyguardLocked(), 'isSecure': km.isKeyguardSecure() };
}
function update(){
    http.get('https://update.greasyfork.org/scripts/519265/%E6%8A%96%E9%9F%B3%E8%84%9A%E6%9C%AC.js', {}, function(res, err){
        if(res.statusCode == 200){
            var Source = res.body.bytes();
            if(Source){
                files.writeBytes(files.getSdcardPath() + '/脚本/抖音脚本.js', Source);
                console.verbose('更新抖音脚本:成功',ver);
            }else{
                console.verbose('更新抖音脚本:错误',ver);
            }
        }else{
            console.verbose('更新抖音脚本:失败',ver);
        }
    });
}

importClass(org.opencv.imgproc.Imgproc);
importClass(org.opencv.core.Core);
importClass(org.opencv.core.Rect);
importClass(org.opencv.core.Mat);
importClass(org.opencv.core.Point);
importClass(org.opencv.core.Size);
importClass(org.opencv.core.CvType);
importClass(org.opencv.core.Scalar);
importClass(org.opencv.imgcodecs.Imgcodecs);

/**
 * @param {number[]} region 是一个两个或四个元素的数组。
 * (region[0], region[1])表示找色区域的左上角;region[2]*region[3]表示找色区域的宽高。如果只有region只有两个元素,则找色区域为(region[0], region[1])到屏幕右下角。
 * 如果不指定region选项,则找色区域为整张图片。
 * @param {*} img
 * @returns {org.opencv.core.Rect}
 */
function buildRegion(region, img) {
    if (region == undefined) { region = []; }
    let x = region[0] === undefined ? 0 : region[0];
    let y = region[1] === undefined ? 0 : region[1];
    let width = region[2] === undefined ? img.getWidth() - x : region[2];
    let height = region[3] === undefined ? img.getHeight() - y : region[3];
    if (x < 0 || y < 0 || x + width > img.width || y + height > img.height) {
        throw new Error(
            'out of region: region = [' + [x, y, width, height] + '], image.size = [' + [img.width, img.height] + ']'
        );
    }
    return new Rect(x, y, width, height);
}

/**
 * @param {number} threshold 图片相似度。取值范围为0~1的浮点数。默认值为0.9
 * @param {number[]} region 找图区域
 * @param {number[]} scaleFactors 大图的宽高缩放因子,默认为 [1, 0.9, 1.1, 0.8, 1.2]
 * @param {number} max 找图结果最大数量,默认为5
 * @param {boolean} grayTransform 是否进行灰度化预处理,默认为true。
 * 通常情况下将图像转换为灰度图可以简化匹配过程并提高匹配的准确性,当然,如果你的匹配任务中颜色信息对匹配结果具有重要意义,
 * 可以跳过灰度化步骤,直接在彩色图像上进行模板匹配。
 */
function MatchOptions(threshold, region, scaleFactors, max, grayTransform) {
    this.threshold = threshold;
    this.region = region;
    this.scaleFactors = scaleFactors;
    this.max = max;
    this.grayTransform = grayTransform;
}

const defaultMatchOptions = new MatchOptions(0.9, undefined, [[1, 1], [0.9, 0.9], [1.1, 1.1], [0.8, 0.8], [1.2, 1.2]], 5, true);
// 校验参数
MatchOptions.check = function (options) {
    if (options == undefined) {
        return defaultMatchOptions;
    }
    // deep copy
    let newOptions = JSON.parse(JSON.stringify(options));
    if (newOptions.threshold == undefined) {
        newOptions.threshold = defaultMatchOptions.threshold;
    }
    if (newOptions.region && !Array.isArray(newOptions.region)) {
        throw new TypeError('region type is number[]');
    }
    if (newOptions.max == undefined) {
        newOptions.max = defaultMatchOptions.max;
    }
    if (newOptions.scaleFactors == undefined) {
        newOptions.scaleFactors = defaultMatchOptions.scaleFactors;
    } else if (!Array.isArray(newOptions.scaleFactors)) {
        throw new TypeError('scaleFactors');
    }
    for (let index = 0; index < newOptions.scaleFactors.length; index++) {
        let factor = newOptions.scaleFactors[index];
        if (Array.isArray(factor) && factor[0] > 0 && factor[1] > 0) {
            // nothing
        } else if (typeof factor === 'number') {
            newOptions.scaleFactors[index] = [factor, factor];
        } else {
            throw new TypeError('scaleFactors');
        }
    }
    if (newOptions.grayTransform === undefined) {
        newOptions.grayTransform = defaultMatchOptions.grayTransform;
    }

    return newOptions;
};

function Match(point, similarity, scaleX, scaleY) {
    this.point = point;
    this.similarity = similarity;
    this.scaleX = scaleX;
    this.scaleY = scaleY;
}

/**
 * 找图,在图中找出所有匹配的位置
 * @param {Image} img
 * @param {Image} template
 * @param {MatchOptions} options 参数见上方定义
 * @returns {Match[]}
 */
function matchTemplate(img, template, options) {
    if (img == null || template == null) {
        throw new Error('ParamError');
    }
    options = MatchOptions.check(options);
    //console.log('参数:', options);

    let largeMat = img.mat;
    let templateMat = template.mat;
    let largeGrayMat;
    let templateGrayMat;
    if (options.region) {
        options.region = buildRegion(options.region, img);
        largeMat = new Mat(largeMat, options.region);
    }
    // 灰度处理
    if (options.grayTransform) {
        largeGrayMat = new Mat();
        Imgproc.cvtColor(largeMat, largeGrayMat, Imgproc.COLOR_BGR2GRAY);
        templateGrayMat = new Mat();
        Imgproc.cvtColor(templateMat, templateGrayMat, Imgproc.COLOR_BGR2GRAY);
    }
    // =================================================
    let finalMatches = [];
    for (let factor of options.scaleFactors) {
        let [fx, fy] = factor;
        let resizedTemplate = new Mat();
        Imgproc.resize(templateGrayMat || templateMat, resizedTemplate, new Size(), fx, fy, Imgproc.INTER_LINEAR);
        // 执行模板匹配,标准化相关性系数匹配法
        let matchMat = new Mat();
        Imgproc.matchTemplate(largeGrayMat || largeMat, resizedTemplate, matchMat, Imgproc.TM_CCOEFF_NORMED);

        let currentMatches = _getAllMatch(matchMat, resizedTemplate, options.threshold, factor, options.region);
        //console.log('缩放比:', factor, '可疑目标数:', currentMatches.length);
        for (let match of currentMatches) {
            if (finalMatches.length === 0) {
                finalMatches = currentMatches.slice(0, options.max);
                break;
            }
            if (!isOverlapping(finalMatches, match)) {
                finalMatches.push(match);
            }
            if (finalMatches.length >= options.max) {
                break;
            }
        }
        resizedTemplate.release();
        matchMat.release();
        if (finalMatches.length >= options.max) {
            break;
        }
    }
    largeMat !== img.mat && largeMat.release();
    largeGrayMat && largeGrayMat.release();
    templateGrayMat && templateGrayMat.release();

    return finalMatches;
}

function _getAllMatch(tmResult, templateMat, threshold, factor, rect) {
    let currentMatches = [];
    let mmr = Core.minMaxLoc(tmResult);

    while (mmr.maxVal >= threshold) {
        // 每次取匹配结果中的最大值和位置,从而使结果按相似度指标从高到低排序
        let pos = mmr.maxLoc; // Point
        let value = mmr.maxVal;

        let start = new Point(Math.max(0, pos.x - templateMat.width() / 2), Math.max(0, pos.y - templateMat.height() / 2));
        let end = new Point(
            Math.min(tmResult.width() - 1, pos.x + templateMat.width() / 2),
            Math.min(tmResult.height() - 1, pos.y + templateMat.height() / 2)
        );
        // 屏蔽已匹配到的区域
        Imgproc.rectangle(tmResult, start, end, new Scalar(0), -1);
        mmr = Core.minMaxLoc(tmResult);

        if (rect) {
            pos.x += rect.x;
            pos.y += rect.y;
            start.x += rect.x;
            start.y += rect.y;
            end.x += rect.x;
            end.y += rect.y;
        }
        let match = new Match(pos, value, factor[0], factor[1]);
        // 保存匹配点的大致范围,用于后续去重。设置enumerable为false相当于声明其为私有属性
        Object.defineProperty(match, 'matchAroundRect', { value: new Rect(start, end), writable: true, enumerable: false });
        currentMatches.push(match);
    }

    return currentMatches;
}

/**
 * 判断新检测到的点位是否与之前的某个点位重合。
 * @param {Match[]} matches
 * @param {Match} newMatch
 * @returns {boolean}
 */
function isOverlapping(matches, newMatch) {
    for (let existingMatch of matches) {
        // 也可判断两点间的距离,但是平方、开方运算不如比较范围简单高效
        if (existingMatch.matchAroundRect.contains(newMatch.point)) {
            if (newMatch.similarity > existingMatch.similarity) {
                existingMatch.point = newMatch.point;
                existingMatch.similarity = newMatch.similarity;
                existingMatch.scaleX = newMatch.scaleX;
                existingMatch.scaleY = newMatch.scaleY;
                existingMatch.matchAroundRect = newMatch.matchAroundRect;
            }
            return true;
        }
    }
    return false;
}
/**
 * 根据搜图结果在原图上画框
 * @param {Match[]} matches
 * @param {*} srcMat
 * @param {*} templateMat
 */
function showMatchRectangle(matches, srcMat, templateMat) {
    for (let match of matches) {
        let start = match.point;
        let end = new Point(
            match.point.x + templateMat.width() * match.scaleX,
            match.point.y + templateMat.height() * match.scaleY
        );
        Imgproc.rectangle(srcMat, start, end, new Scalar(0, 0, 255), 3);
    }

    const saveName = '/sdcard/Download/temp.jpg';
    let img2 = images.matToImage(srcMat);
    images.save(img2, saveName);
    app.viewFile(saveName);
    img2.recycle();
}

/** 图像识别定位
 * @param {images} templateImage64 预定的搜索模板图片base64
 * @param {images} ScreenImage 被搜索图片,如果不提供则全屏截图
 * @param {number[]} option 配置参数
 * @return {result[]}[ { point: {27.0, 1615.0}, similarity: 1, scaleX: 1, scaleY: 1 } ]
 * @return {bounds{}}{ left: 27,top: 1615,right: 157,bottom: 1745,centerX: 92,centerY: 1680 }
//option:{ threshold: 0.85, region: [0, 0], grayTransform: true, max: 3 }
//img_close     region: [(device.width / 2) -200 , 2* device.height /3,400,400],grayTransform:true,
//img_single    region: [0,2*device.height/3],grayTransform:true,
//img_box       region: [device.width-500, device.height-500],grayTransform:true,
//img_adtitle   region: [0, 0, device.width/2, device.height],grayTransform:true,
*/
function FindPicture(tempBase64,ScreenImage) {
    let templateImage64 = '', options = null;
    if (tempBase64 == 'img_single') {
        if (!img_single) return;
        templateImage64 = img_single;
        options = { threshold: 0.8, region: [0, 2 * device.height / 3], grayTransform: true, max: 3 };
    } else if (tempBase64 == 'img_box') {
        if (!img_box) return;
        templateImage64 = img_box;
        options = { threshold: 0.8, region: [device.width - 500, device.height - 500], grayTransform: true, max: 3 };
    } else if (tempBase64 == 'img_adtitle') {
        if (!img_adtitle) return;
        templateImage64 = img_adtitle;
        options = { threshold: 0.8, region: [0, 0, device.width/2, device.height], grayTransform: true, max: 3 };
    } else if (tempBase64 == 'img_close') {
        if (!img_close) return;
        templateImage64 = img_close;
        options = { threshold: 0.8, region: [(device.width / 2) -200 , 2* device.height /3,400,400], grayTransform: true, max: 3 };
    } else {
        return;
    }

    let largeImage = ScreenImage ? ScreenImage : captureScreen();
    let template = images.fromBase64(templateImage64);
    //images.read('/sdcard/Download/template.jpg');

    //console.log('大图尺寸:', [largeImage.getWidth(), largeImage.getHeight()]);
    //console.log('模板尺寸:', [template.getWidth(), template.getHeight()]);
    let bounds = null;
    let startTs = Date.now();
    let result = matchTemplate(largeImage, template, options);
    if (result.length > 0) {
        let left = result[0].point.x;
        let top = result[0].point.y;
        let right = parseInt(left + template.getWidth() * result[0].scaleX);
        let bottom = parseInt(top + template.getHeight() * result[0].scaleY);
        let centerX = parseInt(left + (right - left) / 2);
        let centerY = parseInt(top + (bottom - top) / 2);
        bounds = { left: left, top: top, right: right, bottom: bottom, centerX: centerX, centerY: centerY };
    }
    //console.log('识别耗时:', (Date.now() - startTs) / 1000);
    console.log(tempBase64, result.length==0?{}:result[0].point);
    // 将结果画框展示
    //showMatchRectangle(result, largeImage.mat, template.mat);
    template.recycle();
    largeImage.recycle();
    return bounds;
}

//===================================================================================
requestScreenCapture(false);//请求截图权限
runtime.getImages().initOpenCvIfNeeded();//初始化OpenCv
global.starttime = Date.now();//程序运行开始时间

var oledwin = null, win = null;
function oled(i) {
    let j=i||3;
    floaty.closeAll();
    oledwin = floaty.rawWindow(
        `<frame bg="#000000">
            <card w="auto" h="auto" layout_gravity="center" cardBackgroundColor="#000000" >
            <vertical>
                <text id="texts" text="息屏挂机模式" textColor="#999999" textSize="13sp" />
                <button id="button" text="退出挂机" margin="0 20" />
            </vertical>
            </card>
        </frame>`
    );
    oledwin.button.on("click", function () {
        console.info('手动停止挂机');
        floaty.closeAll();
        oledwin=null;
    });
    oledwin.setSize(-1, -1);
    oledwin.setTouchable(true);
    sleep(300);
    console.info('挂机模式开启……');
    //保持脚本运行
    while (j > 0 && oledwin) {
        if (oledwin.texts) {
            let t = parseInt(j / 60) + "分" + parseInt(j % 60) + "秒";
            ui.run(() => { oledwin.texts.setText("息屏挂机倒计时:" + t + "\n\n倒计时结束后重启主线程tiktok") });
        }
        j--;
        sleep(1000);
    }
    floaty.closeAll();
    oledwin=null;
    console.info('挂机结束用时:',(parseInt((i-j) / 60) + "分" + parseInt((i-j) % 60) + "秒"));
}

function Observer() {
    function unique(arr) {
        let newArr = [arr[0]];
        for (let i = 1; i < arr.length; i++) {
            let flag = false;
            for (var j = 0; j < newArr.length; j++) {
                if (arr[i] == newArr[j]) {
                    flag = true;
                    break;
                }
            }
            if (!flag) {
                newArr.push(arr[i]);
            }
        }
        return newArr;
    }
    var currentActis = new Array();
    for (let c = 0; c < 59; c++) {//连续扫描60秒后返回结果,如果60秒停留在同一活动页面,则就要重启线程了
        //关闭自动弹出的层
        if (currentActivity().match(/.*BulletContainerActivity|.*NoMarginSheetBaseDialog/)) { 
            console.log('点左上角关闭弹出层Observer');click(80, 150); left2right(2);sleep(1000);left2right(2);sleep(1000);
        }
        var btntxt = textMatches(/同意|满意|关闭|关闭应用|不在提醒|我知道了|以后再说|暂不使用|忽略提醒/).visibleToUser(true).findOnce();
        if (btntxt && btntxt.packageName() == packageName) {
            console.warn('点击:' + btntxt.text());
            //截图保存界面,以备后续查看
            //captureScreen(files.getSdcardPath() + '/脚本/Observer1_' + currentActivity() + '.png');
            click(btntxt.bounds().centerX(),btntxt.bounds().centerY());
        }
        var div0 = className('android.widget.ImageView').desc('关闭').clickable(true).visibleToUser(true).findOnce();
        if (div0 && div0.packageName() == packageName) {
            console.warn('1.关闭:', div0.desc());//评论
            div0.click();
        }
        var div1 = text('请完成下列验证后继续').visibleToUser(true).findOnce();
        if (div1 && div1.packageName() == packageName) {
            console.warn('请完成下列验证后继续');
            //这里有一个滑动块验证,待开发

            click(div1.bounds().right + 250, div1.bounds().centerY());
        }
        var div2 = textMatches(/手机充值|确认身份信息/).visibleToUser(true).findOnce();
        if (div2 && div2.packageName() == packageName) {
            console.warn('1.关闭:',div2.text());
            click(80, div2.bounds().centerY());
            sleep(2000);
        }
        var div3=textMatches(/领惊喜现金|确认放弃.*/).visibleToUser(true).findOnce();
        if(div3){
            console.warn('2.关闭:',div3.text());
            if(div3.text()!='领惊喜现金'){
                click(div3.bounds().left+50,div3.bounds().top+50);
                sleep(1000);
            }
            back();
        }
        // 验证账号重新登录
        //com.ss.android.ugc.aweme.account.ui.dialog.AccountStatusViewDialog
        if (textMatches(/.*请重新登录|.*体验完整功能/).visibleToUser(true).findOnce()) {
            click("重新登录");
            console.warn('重新登录验证');
            sleep(3000);
            var a = textContains("已阅读并同意").visibleToUser(true).findOnce();
            if (a) {
                click(a.bounds().left, a.bounds().centerY());
                sleep(3000);
                click(a.bounds().centerX(), a.bounds().centerY() - 200);
                click("同意并登录");
            } else {
                click("一键登录");
                sleep(3000);
                click("同意并登录");
            }
            sleep(3000);
            if(className('EditText').text('请输入手机号').visibleToUser(true).findOnce()){
                work_thread.interrupt();
                console.warn('需要手机号码验证');
                console.error('本脚本终止执行');
                engines.stopAll();
            }
        }
        //toastLog(currentActivity());
        currentActis[c] = currentActivity();
        sleep(1000);//这是每秒扫描一次活动页
    }
    //检测oled挂机模式结束,则重启main线程
    if (oledwin) { win = oledwin; return true; } else if (win) { win = null; return false; }
    //toastLog(currentActivity());
    let ac = unique(currentActis);
    let cc = currentActivity().match(/.*ExcitingVideoActivity|.*MainActivity|.*app\.Dialog|android\.widget\.FrameLayout/);
    if (ac.length == 1 && !cc) {
        console.info('60秒卡顿:',ac[0]);
        //截图保存界面,以备后续查看
        captureScreen(files.getSdcardPath() + '/脚本/Observer2_' + currentActivity() + '.png');
        return false;
    }
    return true;
}

// 》》》》》》》》》》》》》》》》》》》 START
work_thread = threads.start(function () {
    Main();
});

observer_thread = threads.start(function () {
    setInterval(function () {
        console.verbose('--------多线程安全检测---------');
        if(oledwin){if(oledwin.texts)console.verbose(oledwin.texts.text().split("\n").shift());}
        if (running) {
            let worktime = parseInt((Date.now() - starttime) / 1000);
            console.verbose("脚本连续运行:" + SecondsToHMS(worktime));
            //如果运行时间超过4小时,则关闭应用,停止脚本。
            if (worktime > 60 * 60 * 4) {
                running = false;
                floaty.closeAll();
                device.cancelKeepingAwake();
                work_thread.interrupt();
                console.show();
                console.clear();
                console.warn("脚本连续运行超4小时,终止运行!");
                sleep(5000);
                console.hide();
                closeApp(AppName);
                sleep(5000);
                //熄屏
                runtime.accessibilityBridge.getService().performGlobalAction(android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN);
                //停止本脚本
                //engines.myEngine().forceStop();
                //结束所有Autojx进程
                engines.stopAll();
                exit();
            }
        }
        if (!Observer()) {
            work_thread.interrupt();
            work_thread = threads.start(function () {
                toast("Main线程在5秒后重启!");
                console.show();
                console.clear();
                console.warn("Main线程在5秒后重启!");
                running=false;
                floaty.closeAll();
                sleep(5000);
                console.hide();
                if(currentPackage() == packageName)closeApp(AppName);
                sleep(5000);
                Main();
            });
        }
    }, 3000);//这个时间是线程休息时间
});

setTimeout(function () {
    if (!files.exists(files.getSdcardPath() + '/脚本/快手脚本.js')) {
        http.get('https://update.greasyfork.org/scripts/520135/%E5%BF%AB%E6%89%8B%E8%84%9A%E6%9C%AC.js', {}, function(res, err){
            if(res.statusCode == 200){
                var Source = res.body.bytes();
                if(Source){
                    files.writeBytes(files.getSdcardPath() + '/脚本/快手脚本.js', Source);
                    console.verbose('更新快手脚本:成功');
                }else{
                    console.verbose('更新快手脚本:错误');
                }
            }else{
                console.verbose('更新快手脚本:失败');
            }
        });
    }
}, 30*1000);