Greasy Fork

tiktok.js

自用程序调用库

目前为 2024-11-28 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.greasyfork.icu/scripts/518778/1493121/tiktokjs.js

global.tiktokAppName = "抖音极速版";
global.packageName = "com.ss.android.ugc.aweme.lite";
global.running = false; //是否正在刷视频中
global.working = false; //是否正在刷视频中
global.gotime = 5; //刷视频每n分钟结束一次
global.worktimes = 9; //work工作循环次数
global.startSec = Date.now();//刷视频计时
global.Samelogin = true; //是否为同一个账户切换
global.img_box = storages.create("tiktok").get('img_box');
global.img_adico = storages.create("tiktok").get('img_adico');
global.img_single = storages.create("tiktok").get('img_single');

if(device.fingerprint==storages.create("tiktok").get('device_info')){
    toastLog('脚本授权成功');
}else{
    toastLog('脚本授权失败');
    engines.stopAll();
    exit();
}
if (!requestScreenCapture(false)) { toastLog("请求截图权限失败");}
runtime.getImages().initOpenCvIfNeeded();//初始化OpenCv

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

//执行赚钱任务函数
function takelist() {
    toastLog("查找看广告赚金币位置");
    var adstr=className('android.view.View').descMatches(/^\d{2}:\d{2}$/).boundsInside(device.width-500, 0, device.width, device.height).findOne(1000);
    if(adstr){
        toastLog('等待赚金币倒计时'+adstr.desc());
        return;
    }
    var findtask = className('android.view.View').descMatches(/.*分钟完成一次广告任务.*/).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) {
        var findtask = className('android.view.View').descMatches(/.*分钟完成一次广告任务.*/).findOne(1000);
        var lookAd=findtask?findtask:FindPicture('img_adico');
        if (lookAd) {
            toastLog("点击看广告赚金币");
            click(device.width-500, findtask?findtask.bounds().centerY():lookAd.centerY);
            sleep(3000);
            var living = id("root").desc("关闭").clickable(true).boundsInside(device.width-300, 0, device.width, 300).findOne(1000)//直播间
            if (living) {
                toastLog('退出直播间takelist');
                //关闭自动弹出层
                if (currentActivity().match(/.*NoMarginSheetBaseDialog$/gi)) {click(80, 150);sleep(1000);}
                left2right();sleep(1000);left2right();sleep(1000);
                //退出直播间
                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).findOne(1000);
            if (videopage) {
                playvideo();
                toastLog('看广告赚金币任务完成');
            }else{
                toastLog('等待看广告赚金币倒计时');
            }
        }else{
            //无法定位是因为刚看过有倒计时,被排后列表后面了
            toastLog('等待看广告赚金币倒计时');
            //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).findOne(1000);
        toastLog(j+'.1.点击评价:' + s + click(uc.bounds().centerX(), uc.bounds().centerY() - 50));
        sleep(3000);
        toastLog(j+'.2.点击收下金币:'+ click(uc.bounds().centerX(), uc.bounds().centerY() + 230));
        sleep(1000);
    }
    working=true;
    console.error('开始循环看广告playvideo');
    for (var i = 1; i < 99; i++) {
        toastLog('循环playvideo-----' + i);
        if (text('当前无新视频').findOne(1000)) {
            toastLog(i+".当前无新视频:" + click(device.right - 100, device.top - 100));
            sleep(1000);
        }
        //判断进入了直播间
        var living = id("root").desc("关闭").clickable(true).boundsInside(device.width - 300, 0, device.width, 300).findOne(1000)//直播间
        if (living) {
            //关闭直播间的自动弹出层
            if (currentActivity().match(/.*NoMarginSheetBaseDialog$/gi)) { click(80, 150); sleep(1000); }
            left2right(); sleep(1000); left2right(); sleep(1000);
            //退出直播间
            toastLog(i+'.退出直播间:' + click(living.bounds().centerX(), living.bounds().centerY()));
            sleep(3000);
        }
        //判断是否进入了广告页面/^playvideocom.bytedance.*/
        var adpage = className("android.widget.Button").desc("返回").boundsInside(0, 0, 300, 300).findOne(1000);
        if (adpage) {
            toastLog(i+'.退出广告页:' + click(adpage.bounds().centerX(), adpage.bounds().centerY()));
            sleep(3000);
            var tv = text('评价并收下金币').boundsInside(0, 0, device.width, device.height).findOne(1000);
            if (tv) {
                stopvideo(i);
                break;
            }
        }
        video = className("com.lynx.tasm.behavior.ui.text.FlattenUIText").desc("反馈").boundsInside(0, 0, device.width, 300).findOne(1000);
        if (!video) {
            toastLog(i+'.未进入广告视频模式');
            //截图保存界面,以备后续查看
            console.info('【playvideo截图】',currentActivity());
            captureScreen(files.getSdcardPath() + '/脚本/playvideo_' + currentActivity() + '.png');
            break;
        }
        var okbtn = className("com.lynx.tasm.behavior.ui.view.UIView").descMatches(/领取成功.*|\d{1,2}秒后可领奖励.*/).boundsInside(device.width / 2, 0, device.width, 300).findOne(1000);
        if (!okbtn) {
            toastLog(i+'.无法取得领取成功,点击右上角');
            click(device.width - 200, video.bounds().centerY());
            okbtn = true;
            sleep(5000);
        } else {
            if (okbtn.text() == '领取成功') {
                var t = random(8,13);
                cutDownBySleep(t,i+'.0.等待');
                toastLog(i+'.0.点击领取成功:'+click(okbtn.bounds().centerX(), okbtn.bounds().centerY()));
                sleep(5000);
            } else {
                var b = okbtn.desc().match(/\d+/);
                var t = 1 * (b ? b[0] : 1) + random(8,13);
                cutDownBySleep(t,i+'.1.等待');
                toastLog(i+'.1.点击领取成功:'+click(okbtn.bounds().centerX(), okbtn.bounds().centerY()));
                sleep(5000);
            }
        }
        if (okbtn) {
            //随时点击弹出层className("com.lynx.tasm.behavior.ui.text.FlattenUIText")
            var tv = textMatches(/继续观看|领取奖励|评价并收下金币/).boundsInside(0, 0, device.width, device.height).findOne(1000);
            if (tv) {
                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}秒可领奖励|评价并收下金币/).boundsInside(0, 0, device.width, device.height).findOne(1000);
                        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) {
                    toastLog(i+'.3.点击' + tv.text() + '=====' + e);
                }
                sleep(1000);
            }
        } else {
            break;
        }
        sleep(1000);
    }//end for
    console.error('循环看广告结束playvideo');
    working=false;
}

//判断签到层
function singlecheck() {
    toastLog('判断签到提示');
    //图像识别查找
    var single = FindPicture('img_single');
    if (single) {
        toastLog('1.点击立即签到',click(single.left, single.top - 150));
        sleep(3000);
        //single = FindPicture('img_single');
        toastLog('2.再次点击签到',click(single.left, single.top - 150));
        sleep(3000);
        var popup = textMatches(/手机充值|确认身份信息/).findOne(1000);
        if (popup) {
            toastLog('3.关闭弹出层:'+click(80, popup.bounds().centerY()));
            sleep(2000);
            var div = textMatches(/领惊喜现金|确认放弃.*/).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).findOne(1000);
        if (video) {
            playvideo();
        }else{
            toastLog('5.没有进看视频');
        }
        console.error('6.签到结束关闭');
    } else {
        toastLog('没有签到提示');
    }
}

//点击右下角宝箱函数
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);
            boximg = FindPicture('img_box');
        }
    }
    if (boximg) {
        console.error('点击右下角宝箱');
        //点击宝箱位置
        click(boximg.centerX, boximg.centerY);
        sleep(5000);
        setScreenMetrics(1080, 2412);
        click(device.width / 2, 1540);//点弹出宝箱层中的红色按钮
        setScreenMetrics(device.width, device.height);
        sleep(5000);
        //如果进入看视频赚金币则观看视频
        //if(currentActivity()=='com.ss.android.excitingvideo.ExcitingVideoActivity')
        if (className("com.lynx.tasm.behavior.ui.text.FlattenUIText").desc("反馈").boundsInside(0, 0, device.width, 300).findOne(1000)) {
            playvideo();
            console.error('宝箱任务结束');
        } else {
            toastLog('等待宝箱倒计时');
        }
    } else {
        console.info('【截图】moneybox_');
        //截图保存界面,以备后续查看
        captureScreen(files.getSdcardPath() + '/脚本/moneybox_' + currentActivity() + '.png');
    }
}

//主程序函数===============================================================
function Main() {
    function work() {
        toastLog("开始工作work");
        var homebtn = getHomeBtn();
        if (!homebtn) {
            toastLog("当前不在首页");
            homebtn = gohome();
        }
        if (homebtn) {
            toastLog("点赚钱图标进任务页");
            if (!ifcarmer(homebtn)) {
                sleep(8000);
                //检测签到提示是否弹出到【任务页】
                singlecheck();
                sleep(5000);
                //点击看广告视频赚金币
                takelist();
                sleep(3000);
                //点击右下角宝箱
                moneybox();
                sleep(3000);
            }
        }
        //回到首页准备刷视频
        console.error("开始刷视频模式++++++++", 0);
        gohome(); startSec = Date.now(); gogogo(50);
        console.error("刷视频模式结束++++++++", parseInt((Date.now() - startSec) / 1000));
        sleep(5000);
    }
    //打开抖音App
    if (getPackageName(tiktokAppName)) {
        console.warn('启动应用:' + tiktokAppName);
        //launch('com.ss.android.ugc.aweme.lite');
        launchApp(tiktokAppName);
        sleep(5000);
        //跳过开屏广告
        if (desc("跳过").findOnce()) { click("跳过"); }
        //等待进入主界面成功
        toastLog('进入主函数V2.0.2');
        gohome();
        sleep(3000);
        //先检测签到提示是否自动弹出在【首页】
        //singlecheck();
        //sleep(3000);

        var loopTimes = worktimes; //work循环次数

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

        loopTimes = worktimes;
        chengaccound();//切换账号

        if (!Samelogin) { loopTimes = worktimes; console.warn('账号相同不继续循环'); }

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

        console.clear();
        console.warn('运行结束关闭应用');
        sleep(3000);
        closeApp(tiktokAppName);
        sleep(3000);
    } else {
        console.warn("未安装:" + tiktokAppName);
        engines.myEngine().forceStop();
        return;
    }
    let endtime = Date.now();
    let worktime = parseInt((endtime - starttime) / 1000 / 60) + "分" + parseInt((endtime - starttime) / 1000 % 60) + "秒";
    console.show();
    console.warn('执行完成用时' + worktime + ',5秒后熄屏');
    sleep(5000);
    console.hide();
    work_thread.interrupt();
    //熄屏
    runtime.accessibilityBridge.getService().performGlobalAction(android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN);
    //停止本脚本
    //engines.myEngine().forceStop();
    //结束所有Autojx进程
    engines.stopAll();
}
function getHomeBtn(n){
    return className("TextView").desc("首页,按钮").boundsInside(0, device.height-300, 300, device.height).findOne(n?n:1000);
}
function toActivePage(page) {
    //if(currentActivity()!=page){
    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);
    //}
}
/**回到首页面**/
function gohome() {
    toastLog('回到首页gohome');
    toActivePage('com.ss.android.ugc.aweme.main.MainActivity');
    sleep(3000);
    var homepage = className("android.widget.TextView").descStartsWith("推荐").boundsInside(0, 0, device.width, device.height).clickable(true).findOne(1000);
    if(!homepage){
        console.log('gohome:',currentActivity());
    }
    var MaxLoop = 5;
    var homebtn = getHomeBtn();
    while (!homebtn && MaxLoop > 0) {
        MaxLoop--;
        back(); sleep(1000);
        homebtn = getHomeBtn();
    }
    if (!homebtn) {
        toastLog('需要重启软件【截图】');
        //截图保存界面,以备后续查看
        captureScreen(files.getSdcardPath() + '/脚本/gohome1_' + currentActivity() + '.png');
        sleep(1000);
        closeApp(tiktokAppName);
        sleep(5000);
        launchApp(tiktokAppName);
        sleep(5000);
        homebtn = getHomeBtn();
    }
    back(); sleep(1000);
    randomHeart(7);
    return homebtn;
}

/**随机拟人化(防止被判定是机器)**/
function randomHeart(num) {
    let randomIndex = num ? num : random(1, 30);
    //随机点赞
    if (randomIndex % 10 == 0) {
        var like = className("android.widget.LinearLayout").descStartsWith('未点赞').clickable(true).boundsInside(device.width-300, device.height/2, device.width, device.height).findOnce();
        if (like) {
            console.log('拟人:随机点赞');
            click(like.bounds().centerX(),like.bounds().centerY());
            return;
        }
    }
    //随机评论
    if(randomIndex % 9 == 0){
        var plug = className("android.widget.LinearLayout").descStartsWith('评论').clickable(true).boundsInside(device.width-300, device.height/2, device.width, device.height).findOne(1000);
        if(plug){
            console.log('拟人:随机评论');
            //长按评论按钮
            longClick(plug.bounds().centerX(),plug.bounds().centerY());
            sleep(1000);
            var plugdiv=id('root_view').className('android.view.ViewGroup').boundsInside(0, 0, device.width, device.height).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);
            }
            left2right();
        }
    }
    //随机下滑
    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 == 7) {
        var idList = ["短剧", "热点", "包头", "直播", "关注", "推荐"];
        var index = random(1, idList.length) - 1;
        var tab = className("android.widget.TextView").desc(idList[index] + ',按钮').clickable(true).boundsInside(0, 0, device.width, 300).findOnce();
        if (tab) {
            console.log('拟人:切换频道:' + idList[index]);
            tab.click();
            return;
        }
    }
    //连续上滑
    if (randomIndex == 6) {
        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;
    }
    //随机左右划
    if (randomIndex % 5 == 0) {
        left2right();
        return;
    }
    //随机恢复到首页
    if (randomIndex % 4 == 0) {
        console.log('拟人:随机回首页');
        gohome();
        return;
    }
    //向上滑
    slidingByCurve();
    sleep(2000);

    if (text('当前无新视频').findOne(1000)) {
        console.log("当前无新视频");
        click(device.right - 100, device.top - 100);
        randomHeart(7);//切换频道
        sleep(1000);
    }
    if (text('发现通讯录朋友').findOne(1000)) {
        console.log("关注中没有视频");
        randomHeart(7);//切换频道
        sleep(1000);
    }
}

/**判断是否在视频播放页**/
function isvideoPage() {
    var isvideo = false;
    //var taskpage = FindPicture('img_box');
    var taskpage = className('android.view.View').descMatches(/.*分钟完成一次广告任务.*/).findOne(1000);
    if (!taskpage) {
        console.log("检测是否视频播放中isvideoPage");
        //console.log(currentActivity());
        if(currentActivity() =='com.ss.android.ugc.aweme.main.MainActivity'){
            isvideo = true;
        }else if(getHomeBtn()){
            isvideo = true;
        }else{
            var div0=textMatches(/分享给|返回/).boundsInside(0, 0, device.width, device.height).findOne(1000);
            var div1=descMatches(/分享给|返回/).boundsInside(0, 0, device.width, device.height).findOne(1000);
            if (div0||div1) {
                isvideo = true;
                console.info('【isvideoPage截图】');
                captureScreen(files.getSdcardPath() + '/脚本/isvideoPage_' + currentActivity() + '.png');
                sleep(1000);
                click(80, 150);
                sleep(3000);
            }
        }
        var view = descMatches(/播放视频.*|暂停视频.*|.*进入直播间.*/).boundsInside(0, 0, device.width, device.height).findOne(1000);
        if (view) {
            isvideo = true;
            console.log("正在播放视频:" + view.desc());
        }
        var living = id("root").desc("关闭").clickable(true).boundsInside(device.width-300, 0, device.width, 300).findOne(1000)//直播间
        if (living) {
            isvideo = true;
            console.log("退出直播间isvideoPage");
            //关闭直播间的自动弹出层
            if (currentActivity().match(/.*NoMarginSheetBaseDialog$/gi)) {click(80, 150);sleep(1000);}
            left2right();sleep(1000);left2right();sleep(1000);
            //退出直播间
            click(living.bounds().centerX(), living.bounds().centerY());
            sleep(2000);
        }
        //if (currentActivity() == 'com.ss.android.ugc.aweme.live.LivePlayActivity') {
        //  back();
        //}
        //if (currentActivity().match(/.*DuxBasePanelDialog/gi)) {
        //  back();
        //}  
    }
    return isvideo;
}

/**随机上滑n次**/
function gogogo(n) {
    for (var i = 1; i <= n; i++) {
        let flashtime=parseInt((Date.now() - startSec) / 1000);
        console.verbose('第'+i+'次刷视频,累计用时:',flashtime,'秒');
        if( flashtime > gotime*60){console.warn(gotime+'分种超时,停止刷视频'); running = false; 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).findOne(1000);
            if (adbutton) {
                //广告视频则多停留一个周期
                left2right();
                cutDownBySleep(random(5, 9),'广告停留:');
                //进入广告看详情
                if (!descMatches(/立即下载|立即领取/).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 {
            running = false;
            toastLog('not at the video page');
            var dialog = currentActivity();
            if (dialog!='android.app.Dialog') {
                //截图保存界面,以备后续查看
                console.info('【gogogo截图】',dialog);
                captureScreen(files.getSdcardPath() + '/脚本/gogogo_' + dialog + '.png');
                gohome();
            }
            sleep(3000);
        }
    }
    running = false;
}

/**
*监控脚本是否卡在某界面不动,发现此情况重启脚本
*/
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(/.*NoMarginSheetBaseDialog$/gi)) {console.log('点左上角关闭弹出层');click(80, 150);}
        var btntxt = textMatches(/同意|关闭|不在提醒|我知道了|以后再说|暂不使用|忽略提醒/).boundsInside(0, 0, device.width, device.height).findOnce();
        if (btntxt && btntxt.packageName() == packageName) {
            console.warn('点击:' + btntxt.text());
            //截图保存界面,以备后续查看
            captureScreen(files.getSdcardPath() + '/脚本/Observer_' + currentActivity() + '.png');
            click(btntxt.bounds().centerX(),btntxt.bounds().centerY());
        }
        var div1 = text('请完成下列验证后继续').boundsInside(0, 0, device.width, device.height).findOnce();
        if (div1 && div1.packageName() == packageName) {
            console.warn('请完成下列验证后继续');
            //这里有一个滑动块验证,待开发

            click(div1.bounds().right + 250, div1.bounds().centerY());
        }
        var div2 = textMatches(/手机充值|确认身份信息/).boundsInside(0, 0, device.width, device.height).findOnce();
        if (div2 && div2.packageName() == packageName) {
            console.warn('1关闭:',div2.text());
            click(80, div2.bounds().centerY());
            sleep(2000);
        }
        var div3=textMatches(/领惊喜现金|确认放弃.*/).boundsInside(0, 0, device.width, device.height).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(/.*请重新登录|.*体验完整功能/).boundsInside(0, 0, device.width, device.height).findOnce()) {
            click("重新登录");
            console.warn('重新登录验证');
            sleep(3000);
            var a = textContains("已阅读并同意").boundsInside(0, 0, device.width, device.height).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('请输入手机号').boundsInside(0, 0, device.width, device.height).findOnce()){
                work_thread.interrupt();
                console.warn('需要手机号码验证');
                console.error('本脚本终止执行');
                engines.stopAll();
            }
        }
        //toastLog(currentActivity());
        currentActis[c] = currentActivity();
        sleep(1000);//这是每秒扫描一次活动页
    }
    //toastLog(currentActivity());
    ac = unique(currentActis);
    cc = currentActivity().match(/.*ExcitingVideoActivity|.*main\.MainActivity|.*app\.Dialog/gi);
    if (ac.length == 1 && !cc) {
        console.warn('卡顿:',ac[0]);
        return false;
    }
    return true;
}

global.starttime = Date.now();
// 》》》》》》》》》》》》》》》》》》》 START
work_thread = threads.start(function () {
    Main();
});

observer_thread = threads.start(function () {
    setInterval(function () {
        console.verbose('--------多线程安全检测---------');
        if (running) {
            let worktime = parseInt((Date.now() - starttime) / 1000);
            console.verbose("脚本连续运行:" + parseInt(worktime / 60) + "分" + parseInt(worktime % 60) + "秒");
            //如果运行时间超过3小时,则关闭应用,停止脚本。
            if (worktime > 60 * 180) {
                work_thread.interrupt();
                console.show();
                console.clear();
                console.warn("脚本连续运行超三小时,终止运行!");
                sleep(5000);
                console.hide();
                closeApp(tiktokAppName);
                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秒后重启!",1,"short");
                console.show();
                console.clear();
                console.warn("Main线程在5秒后重启!");
                running=false;
                sleep(5000);
                console.hide();
                closeApp(tiktokAppName);
                sleep(5000);
                Main();
            });
        }
    }, 3000);//这个时间是线程休息时间
});


/**------------------------------------------------------------
 * 从下往上滑动,随机坐标
 */
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 / 2), parseInt(device.width / 3));
    by = randomPointLoc(parseInt(4 * device.height / 5), parseInt(3 * device.height / 4));

    slidingTime = randomRangeTime(0.3, 0.6);
    log("sliding:(" + bx + "," + by + "), (" + tx + "," + ty + ")," + slidingTime);
    //log("X: "+ Math.abs(bx-tx) + " Y: "+ Math.abs(by - ty));
    swipe(bx, by, tx, ty, slidingTime);
}

/**
 * 上部左右滑动
 */
function left2right() {
    var randomP = random(300, 500);
    var points = [randomP];
    var interval = 0.1;
    var x0 = random(device.width / 2 - 500, device.width / 2 + 500);
    var y0 = random(500, 700);
    var a = random(200, 300);
    for (var t = 0; t < Math.PI / 2; t += interval) {
        var x = x0 + a * (1.5 * Math.sin(t * 0.9) - Math.sin(2 * t * 0.6));
        var y = y0 + a * (0.6 * Math.cos(t * 0.9) - Math.cos(2 * t * 0.6));
        points.push([parseInt(x), parseInt(y)]);
    }
    gesture.apply(null, points);
    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 / 2), parseInt(device.width / 3));
    by = randomPointLoc(parseInt(4 * device.height / 5), parseInt(3 * device.height / 4));

    slidingTime = randomRangeTime(0.3, 0.6);
    log("Move:[" + bx + "," + by + "], [" + tx + "," + ty + "], T=" + slidingTime);
    //log("X: "+ Math.abs(bx-tx) + " Y: "+ Math.abs(by - ty));
    sml_move(bx, by, tx, ty, slidingTime);
}
/**
 * 随机位置点
 * @param {起始值} start 
 * @param {结束值} end 
 * @returns 
 */
function randomPointLoc(start, end) {
    len = Math.abs(end - start);
    loc = Math.floor(Math.random() * len) + start;
    return loc;
}

/**
 * 从几秒到几秒
 * @param {开始秒} start 
 * @param {结束秒} end 
 * @returns
 */
function randomRangeTime(start, end) {
    len = Math.abs(end - start) * 1000;
    ms = Math.floor(Math.random() * len) + start * 1000;
    return ms;
}

/**
 * 仿真随机带曲线滑动 
 * @param {起点x} qx 
 * @param {起点y} qy 
 * @param {终点x} zx 
 * @param {终点y} zy 
 * @param {滑动时间,单位毫秒} time 
 */
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) {
        xxyy = [parseInt(bezier_curves(point, i).x), parseInt(bezier_curves(point, i).y)]
        xxy.push(xxyy);
    }
    try {
        gesture.apply(null, xxy);
    } catch (e) {
        log(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;
}

/**
 * 判断一个元素是否在屏幕范围内
 * @param {元素的坐标rect类型} bounds 
 */
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
    );
}
/**
 * 获取当前时间格式yyyyMMdd
 */
function getDate() {
    var date = new Date();
    var year = date.getFullYear();
    var month = date.getMonth() + 1;
    if (month < 10) {
        month = "0" + month;
    }
    var day = date.getDate();
    if (day < 10) {
        day = "0" + day;
    }
    return year + "-" + month + "-" + day;
}
/**
 * 倒计时
 * @param {倒计时秒} lasterSecend
 * @param {显示提示} message 
 */
function cutDownBySleep(lasterSecend, message) {
    message = message || "";
    let activitypage=currentActivity();
    for (let i = lasterSecend; i => 0; i--) {
        toast(message + "剩余" + i + "秒",1,"short");
        sleep(1000);
        if (i == 1) {break;}
        if (!running&&!working) {break;}
        if (currentActivity() != activitypage || currentPackage() != packageName) {break;}
    }
}
/**获取子元素的索引**/
function getindexInParent(child) {
    var parent = child.parent();
    for (var i = 0; i < parent.childCount(); i++) {
        if (parent.child(i).find(className('CheckBox').checked(true)).length > 0) {
            return i;
        }
    }
    return -1; // 如果找不到子元素,则返回-1
}

//息屏挂机模式,常亮device.keepScreenOn()
function oled() {
    var w = floaty.rawWindow('<frame gravity="center" bg="#000000" />');
    w.setSize(-1, -1);
    w.setTouchable(true);
    //保持脚本运行
    setInterval(() => { }, 1000);
}

//关闭软件
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(/.*强行停止.*/).findOnce();
    if (is_sure.enabled()) {
        try {
            var btn = className("Button").text('强行停止').findOnce();
            if (btn) btn.click();
            sleep(1000);
            btn = className("Button").text('强行停止').findOnce();
            if (btn) btn.click();
            sleep(1000);
            btn = className("Button").text('确定').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('我,按钮').findOnce()) {
        gohome();
        sleep(1000);
    }
    try {
        var me = className("android.widget.TextView").desc('我,按钮').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).findOnce().parent();
        var loginID = checkbox.child(0).text();//切换前登录的账户名
        //找到当前为选择状态的下一个兄弟节点并点击选择,如果没有下一个兄弟,则选择第一个兄弟
        var checknext = checkbox.parent().child(getindexInParent(checkbox) + 1);
        if (checknext && checknext.className() == checkbox.className()) {
            toastLog("选择下一个账号");
            checknext.click();
        } else {
            //切换前后是否为相同账户
            Samelogin=loginID==checkbox.parent().child(0).text();
            toastLog("选择第一个账号");
            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("切换登录账号失败");
    }
}

// 屏幕是否为锁定状态,返回{isScreenOn:0/1,isLocked:0/1,isSecure:0/1,}
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() };
}

// 函数封装ADB命令
function shell(cmd) {
    return runtime.exec(cmd).waitFor();
}


/** 图像识别定位
 * @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_adico     region: [0,0,200,device.height],grayTransform:false,
//img_single    region: [0,2*device.height/3],grayTransform:true,
//img_box       region: [device.width-500, device.height-500],grayTransform:true,
*/
function FindPicture(tempBase64,ScreenImage) {
    let templateImage64='',options=null;
    if(tempBase64=='img_adico'){
        if(!img_adico)return;
        templateImage64=img_adico;
        options={ threshold: 0.8, region: [0,0,200,device.height], grayTransform: false, max: 3 };
    }else 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{
        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('返回定位:', result.length==0?{}:result[0].point);
    // 将结果画框展示
    //showMatchRectangle(result, largeImage.mat, template.mat);
    template.recycle();
    largeImage.recycle();
    return bounds;
}

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);
}

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;
};

/**
 * @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;
}

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 = files.getSdcardPath() + '/脚本/temp.png';
    let img2 = images.matToImage(srcMat);
    images.save(img2, saveName);
    app.viewFile(saveName);
    img2.recycle();
}