aardio

webview2抖音视频自动发布

2023-07-20  本文已影响0人  LCSan

aardio的webview2同步方式,通过js,cdp协同完成。几个重要点记录一下:
1、webview中异步、同步方法不能混用,同步会阻塞消息队列。这里要用cpd是同步方法,因此全部舍弃异步方法;
2、文件上传,用DOM.setFileInputFiles方法,不用模拟文件弹框,直接设置文件即可上传;
3、抖音的视频标题框是封装控件,必须模拟点击获取焦点。这里用Input发送键盘、鼠标、指令方式,模拟鼠键操作;
4、cdp默认是css选择器,通过Runtime.evaluate以xpath方式执行js获取nodeId,再执行其他cdp命令传参。

//自动化
import win.ui;
/*DSG{{*/
var winform = win.form(text="抖音自动发布";right=759;bottom=469;bgcolor=16777215;border="thin")
winform.add()
/*}}*/

import web.view;
import web.json;
import console;

function enableEdit(wb){
    // 获取编辑框坐标
    var rect = wb.eval(`JSON.stringify(document.querySelector("div#root div.editor-kit-root-container > div > div").getBoundingClientRect())`);
    rect = web.json.parse(rect);
    // 鼠标左键点击,[x,y]坐标
    wb.cdp("Input.dispatchMouseEvent",{type="mousePressed";x=rect["x"]+10;y=rect["y"]+10;button="left"})    
}


// 输入文本
function addSendMsg(wb,msg){
    // CDP:模拟输入文本
    wb.cdp("Input.insertText",{text=msg})
}

// 添加抖音用户
function addSendUser(wb,userId){
    if(string.startWith(userId,'@')){
        userId = string.right(userId,-2)
    }
    addSendMsg(wb, "@" ++ userId)
    // 等待选人
    wb.waitEle(`div.editor-kit-root-container div.mention-suggest-mount-dom img`);
    // 选人
    wb.eval(`document.evaluate('.//span[contains(string(),"抖音号 ` + userId + `")]',document).iterateNext().click()`)
}

// 获取输入总长度
function getMsgLength(wb){
    var data = wb.eval(`document.querySelector("div.toolbar > div:nth-child(2)").innerText.match(/\d+/ig)`); 
    if(tonumber(data[1]) > tonumber(data[2])){
        // 必须要点一下编辑框才能撤销
        enableEdit(wb)
        // CDP:模拟命令,撤销命令
        wb.cdp("Input.dispatchKeyEvent",{
            type="rawKeyDown";
            commands={"undo","undo"}
        })

        return false; 
    }
    return true; 
}


function uploadVideo(wb,mp4,msgs){
    
    //console.log("2222");
    // 等待上传框
    wb.waitEle(`input[type="file"]`);
    sleep(1000)
    // 获取控件
    var file = wb.cdpQuery(`input[type="file"]`);
    // 设置文件路径,上传
    wb.cdp("DOM.setFileInputFiles",{
        files = { mp4 };
        nodeId = file.nodeId;
    })
    
    // 等待编辑框
    wb.waitEle("div#root div.editor-kit-root-container");
    
    
    // 等待3秒
    sleep(1000)
    enableEdit(wb)
    var flag;   
    // 发消息
    for(k,v in msgs){
        if(string.startWith(v,'@')){
            addSendUser(wb,v)
        }else {
            addSendMsg(wb,v)
        }
        flag = getMsgLength(wb);
        //console.log(flag);
        if(!flag){
            break
        }
    }
    sleep(1000)
    // 勾选不允许下载
    wb.eval(`document.querySelector('div[class^="download-content"] input[type="checkbox"][value="0"]').click()`);
    
    // 等待重新上传框子,说明传完
    wb.waitEle(`#root > div > div > div[class^="content-body-"] > div[class^="preview-"] > div > div > div > div[class^="long-card-"]`);
    
    
    // 发布按钮,滚动到可视区域 
    var dd = wb.cdp("Runtime.evaluate",{
        expression = `document.evaluate(".//button[text()='发布']",document).iterateNext()`;
    });
    
    wb.cdp("DOM.scrollIntoViewIfNeeded",{
        objectId = dd.result.objectId;
    });


    // 发布视频
    wb.eval(`document.evaluate("//button[text()='发布']",document).iterateNext().click()`);
}

// 要发布的视频地址
var mp4 = "mp4路径";
// 视频title。文字,@抖音号。抖音号自动转成@人
var msgs = {`测试一段文字,测试@人:`,"@77440681","@77440682","@77440683","@77440684"}; 

// 初始化浏览器
var wb = web.view(winform,`\userdata`); 
winform.show();
// 设置禁用选项,关闭右键,开发工具,状态栏
wb.enableDefaultContextMenus(false);
wb.enableDevTools(false);
wb.enableStatusBar(false);

//添加网页默认加载脚本,禁止弹出新窗口
wb.preloadScript("window.open =  function(url){ window.location.href = url; } ")

// 打开上传页面
wb.go("https://creator.douyin.com/creator-micro/content/upload");
wb.wait("");

var flag = true;
while(flag){
    select(wb.location) {
        case "https://creator.douyin.com/creator-micro/content/upload" {
            uploadVideo(wb,mp4,msgs)
            flag = false;
        }
        case "https://creator.douyin.com/creator-micro/home" {
                        // 登录成功,跳转到上传页面
            wb.go("https://creator.douyin.com/creator-micro/content/upload");
            wb.wait("creator-micro/content/upload");
            //console.log("222")
        }
        case "https://creator.douyin.com/" {
                        // 没有登录情况下,人工登录
            wb.waitEle("span.login")
            wb.eval(`document.querySelector("span.login").click()`)              
            wb.wait("creator-micro/home");
            //console.log("3333")
        }
    }
}

win.loopMessage();

另外异步方式的实现,不稳定,记录一下。

//waitEle 用法演示
import win.ui;
/*DSG{{*/
var winform = win.form(text="抖音自动发帖";right=1097;bottom=759)
winform.add(
btn_video={cls="plus";text="视频目录";left=784;top=688;right=880;bottom=720;align="left";color=3947580;disabled=1;dr=1;dt=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=8}};iconText='\uF07C';notify=1;textPadding={left=25};z=6};
btn_xls={cls="plus";text="Excel文件";left=784;top=656;right=880;bottom=688;align="left";color=3947580;disabled=1;dr=1;dt=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=8}};iconText='\uF07C';notify=1;textPadding={left=25};z=8};
button_clean={cls="button";text="清空";left=1000;top=656;right=1096;bottom=680;disabled=1;z=4};
button_start={cls="button";text="开始";left=888;top=656;right=984;bottom=680;disabled=1;z=3};
edit_log={cls="edit";left=890;top=2;right=1096;bottom=648;color=128;disabled=1;edge=1;multiline=1;readonly=1;z=2};
edit_sleep={cls="edit";text="2000";left=336;top=680;right=448;bottom=720;disabled=1;edge=1;num=1;z=12};
edit_sn={cls="edit";left=72;top=728;right=328;bottom=752;edge=1;readonly=1;z=14};
edit_tips={cls="edit";left=16;top=680;right=328;bottom=720;disabled=1;edge=1;multiline=1;z=9};
edit_video={cls="plus";left=456;top=688;right=784;bottom=714;align="left";border={bottom=1;color=-6908266};disabled=1;dl=1;dr=1;dt=1;editable=1;font=LOGFONT(h=-13);textPadding={top=6;bottom=2};z=5};
edit_vip={cls="edit";left=392;top=728;right=872;bottom=752;edge=1;z=16};
edit_xls={cls="plus";left=456;top=656;right=784;bottom=682;align="left";border={bottom=1;color=-6908266};disabled=1;dl=1;dr=1;dt=1;editable=1;font=LOGFONT(h=-13);textPadding={top=6;bottom=2};z=7};
static={cls="static";left=0;top=0;right=889;bottom=651;transparent=1;z=1};
static2={cls="static";text="话术";left=16;top=656;right=80;bottom=672;color=3947580;font=LOGFONT(h=-14);group=1;transparent=1;z=10};
static3={cls="static";text="机器码:";left=16;top=736;right=72;bottom=760;transparent=1;z=13};
static4={cls="static";text="搜索间隔(毫秒)";left=336;top=656;right=432;bottom=672;color=3947580;font=LOGFONT(h=-14);group=1;transparent=1;z=11};
static6={cls="static";text="激活码:";left=336;top=736;right=392;bottom=760;transparent=1;z=15};
static7={cls="static";text="有效期至:";left=880;top=736;right=952;bottom=760;transparent=1;z=17};
static_tm={cls="static";text="-";left=944;top=736;right=1072;bottom=760;transparent=1;z=18}
)
/*}}*/

import winex;
import key;
import config;
import fsys;
import mouse;
import winex.mouse;
import winex.key;
import com.excel; 
import web.view;
import win.timer;
import myplu.vip;

// 自动保存配置文件
winform.bindConfig( config.winform,{
    edit = "text";
    radiobutton = "checked";
    checkbox = "checked";
    combobox = "selIndex";
    plus ={
        ["^chk"] = "checked";
        ["^edit"] = "text";
    };
} );

// 定时器
var timer = win.timer(winform); 
// 间隔1秒
timer.setInterval(1000);
// 更新计时
timer.onTimer = function(hwnd,msg,id,tick){
    winform.static_tm.text = "";
} 

// 网页控件
var wb = web.view(winform.static,`\userdata`);
var xls_data = null; 
var video_data = null; 
var start_flag = true; 
var wb_hwnd = null; 

// 是否离开的弹窗
function checkExit(){
    var hwnd = winex.findEx(,,"Chrome\_WidgetWin\_\x+","是否离开网站")
    if(hwnd){
        winex.key.click(hwnd,"ENTER") 
    }   
}

// 是否离开的弹窗
function checkMap(){
    var hwnd = winex.findEx(,,"Chrome\_WidgetWin\_\x+","")
    if(hwnd){
        winex.key.click(hwnd,"ENTER") 
    }   
}

// 浏览器窗口里面找窗口句柄,然后操作
function getWdHwnd(){
    if(not wb_hwnd){
        checkMap();
        wb_hwnd = winex.findEx(winform.static.hwnd,,"Chrome\_RenderWidgetHostHWND","Chrome Legacy Window");
    }
    return wb_hwnd; 
}

function checkDialog(){
    /*
    if(xls_data == null or #xls_data == 0){
        winform.edit_log.print("excel数据已发布完");
        return ; 
    }
    if(video_data == null or #video_data ==0 ){
        winform.edit_log.print("视频已发布完");
        return ; 
    }*/
    
    hwnd,hwnd_button,t_id,p_id = winex.wait(,"打开","#32770","Button")
    if(hwnd){
        winform.edit_log.print(hwnd,hwnd_button,t_id,p_id)
    
        hwnd_edit = winex.findEx(hwnd,,"Edit","")
        sleep(100);
        winex.mouse.click(hwnd_edit,32,18)
        winform.edit_log.print(hwnd_edit)
        sleep(100);
        var txt = table.pop(video_data,1);
        if(txt && #txt > 0){
            txt = winform.edit_video.text + "\" + txt;
            winform.edit_log.print(txt);
            winex.sayIme(txt,hwnd_edit)
            sleep(100);
            winform.edit_log.print(hwnd_button)
            winex.key.click(hwnd_button,"ENTER") 
        }else {
            winform.edit_log.print("没有更多上传视频");
        }
        return true; 
    }
    sleep(500);
    var hwnd = winex.findEx(,,"#32770","打开");
    retun (hwnd == null);
}



// 异步调用
wb.external = {
    uploadVideo = function(){   
        checkDialog();      
    };
    sendMsg = function(data,flag){
        if(start_flag==false){
            winform.edit_log.print("停止");
            return ; 
        }   
        
        x,y = data["x"]+data["width"]-5,data["y"]+data["height"]-5;     
        winform.edit_log.print(x,y);
        // 点击输入框位置
        winex.mouse.clickDB(getWdHwnd(),x,y);
        // 输入文案
        if(#winform.edit_tips.text > 0 and flag){
            sleep(500);
            winex.say(winform.edit_tips.text,hwnd);
        }       
        handleMsg();        
    };
    nextMsg = function(data){
        if(start_flag==false){
            winform.edit_log.print("停止");
            return ; 
        }
        winform.edit_log.print("成功:" + data);
        // 成功,可以接续下一个
        handleMsg();
    };
    exitMsg = function(data){
        winform.edit_log.print("成功:" + data);
        winform.edit_log.print("文字超长,回删");
        // 数量大于500,说明可以停止   
        winex.key.combine(getWdHwnd(),"CTRL","Z");
        winex.key.combine(getWdHwnd(),"CTRL","Z");
        if(start_flag==false){
            winform.edit_log.print("停止");
            return ; 
        }
        commitMsg();
    };
    errorMsg = function(){
        winform.edit_log.print("用户未找到");
        // 没有找到id,下一个
        winex.key.combine(getWdHwnd(),"CTRL","Z");
        if(start_flag==false){
            winform.edit_log.print("停止");
            return ; 
        }
        handleMsg();
    };
    submitMsg = function(data){
        if(start_flag==false){
            winform.edit_log.print("停止");
            return ; 
        }
        winform.edit_log.print("发布");
        sleep(500);
        
        x,y,cx,cy = winform.static.getPos(true);
        
        x,y = x+data["x"]+data["width"]/2,y+data["y"]+data["height"]/2;
        
        winform.edit_log.print(x,y);
        mouse.click(x,y,true)
        //winex.mouse.clickDB(getWdHwnd(),x,y);
        sleep(5000);
        // 进行下一页
        wb.go("https://creator.douyin.com/creator-micro/content/upload");
        //checkExit();
    };
    
}

// 网页加载监听
wb.onDocumentInit = function(url){
    winform.edit_log.print(url);
    if(url="https://creator.douyin.com/creator-micro/content/upload"){
        
        // 用法三:异步等待参数@1指定CSS选择器的节点,回调 Javascript 代码
        // 第一个页面,上传视频
        wb.waitEle("label",`
            document.querySelector("label").click();
            // 这里至关重要,文件弹框跟uploadVideo方法里面等待阻塞,导致可能模拟操作失败。因此这里异步等待弹框先出来。
            setTimeout(function () {
                aardio.uploadVideo();
            },3000);
        `);
        
        // 第二个页面,发送文本
        wb.waitEle("div#root div.editor-kit-root-container",`
            setTimeout(function () {
                aardio.sendMsg(document.querySelector("div#root div.editor-kit-root-container > div > div").getBoundingClientRect(),true);
            },1000);
        `);
        
        // 发布后,取消同步到西瓜
        wb.waitEle("button.semi-button.semi-button-tertiary.semi-button-light > span.semi-button-content",`
            document.evaluate("//span[@class='semi-button-content' and text()='暂不同步']",document).iterateNext().click();
        `);
    }
}

// 读取excel数据
function readExcel(xlsPath,sheetName=1){
    var excel,err = com.excel();
    excel.alerts = false;
    
    var book = excel.Open(xlsPath);
 
    var sheet = book.Sheets(sheetName);
    var count = sheet.UsedRange.Rows.Count;
    var res = {}; 
    for(i=2;count;1){
        table.push(res,sheet.Cells(i,2).Text);
    }
    excel.Quit();
    return res; 
}

// 锁定控件
function clockCtrl(flag){
    winform.edit_sn.text = myplu.vip.keyCode();
    winform.edit_xls.disabled = flag;
    winform.edit_video.disabled = flag;
    winform.edit_sleep.disabled = flag;
    winform.edit_tips.disabled = flag;
    winform.btn_xls.disabled = flag;
    winform.btn_video.disabled = flag;
    winform.button_start.disabled = flag;
    winform.button_clean.disabled = flag;
    if(flag){
        timer.enable();
    }else{
        timer.disable();
    }
}

// 检测时间
function checkVip(){
    var res = false; 
    if(winform.edit_vip.text == ""){
        win.msgbox("请联系运营申请激活码!","运行提示");
        clockCtrl(true);
        return res; 
    }
    try{
        var tm = myplu.vip.decode("xxxxx",winform.edit_vip.text);
        if(tm){
            if(time.now().diffsecond(tm) > 0){
                clockCtrl(true);
                winform.static_tm.text=tostring(tm,"%Y-%m-%d %H:%M:%S","chs");
                winform.edit_log.print("激活码已过期");
            }else {
                clockCtrl(false);
                winform.static_tm.text=tostring(tm,"%Y-%m-%d %H:%M:%S","chs");
                winform.edit_log.print("已激活");
                res = true; 
            }
        }else {
            clockCtrl(true);
            winform.edit_log.print("激活失败");
        }
    }
    catch(e){
        clockCtrl(true);
        win.msgbox("请输入有效的激活码!","运行提示");    
    }   
    return res; 
}

// 模拟发送
function handleMsg(){
    var res;
    do{ 
        // 成功,可以接续下一个
        if(#xls_data > 0){
            res = addUser();
        }else {
            commitMsg();
            res = true;
        }
    }while(res == false)
}


// 添加配置操作
function addUser(){
    var userId = table.pop(xls_data,1);             
    if(userId and #userId>0){
        winform.edit_log.print(userId);
        sleep(500)
        winex.say("@" + userId) 
        sleep(tonumber(winform.edit_sleep.text))        
        wb.doScript(`
            (function(){
                var node = document.evaluate('.//span[contains(string(),"抖音号 ` + userId + `")]',document).iterateNext();
                if(node){
                    node.click();
                    // 之后执行
                    setTimeout(function () {
                        var psize = document.querySelector("#root div.editor-kit-editor-container > div:nth-child(2)").innerText.match(/\d+/ig);
                        if(parseInt(psize[0]) < parseInt(psize[1])){
                            // 说明可以继续填写
                            aardio.nextMsg(node.innerText);
                        }else{
                            // 说明不能继续填写
                            aardio.exitMsg(node.innerText);
                        }
                    }, 200)
                }else{
                    // 不存在,说明失败
                    aardio.errorMsg();
                } 
            })();
            `,function(result,err){
                
        }); 
        return true;
    }else{
        return false; 
    }    
}

// 发布动作
function commitMsg(){
    wb.doScript(`
        (function(){
            document.evaluate("//label[span/text()='不允许']/input[@type='checkbox']",document).iterateNext().click();
            document.evaluate("//button[text()='发布']",document).iterateNext().scrollIntoView();
            setTimeout(function () {
                aardio.submitMsg(document.evaluate("//button[text()='发布']",document).iterateNext().getBoundingClientRect());
            }, 1000)
        })();
        `,function(result,err){
            
    }); 
}

// 开始按钮
winform.button_start.oncommand = function(id,event){
    if(winform.button_start.text == "停止"){
        start_flag = false;     
        winform.button_start.text = "继续";
        return ;
    }
    
    var res = checkVip();
    if(res == null){
        return ; 
    }
    // 检查excel
    if(winform.edit_xls.text == ""){
        winform.edit_log.print("请选择Excel文件");
        winform.btn_xls.oncommand();
        return;
    }elseif(io.exist(winform.edit_xls.text) == false) {
        winform.edit_log.print("Excel文件不存在,重新选择!");
        winform.btn_xls.oncommand();
        return;
    }
    // 检查视频
    if(winform.edit_video.text == ""){
        winform.edit_log.print("请选择要上传视频的目录");
        winform.btn_video.oncommand();
        return;
    }elseif(io.exist(winform.edit_video.text) == false) {       
        winform.edit_log.print("视频目录不存在,重新选择!");
        winform.btn_video.oncommand();
        return;
    }
    
    // 构造excel数据
    if(xls_data == null){
        winform.edit_log.print("读取excel数据");
        xls_data = readExcel(winform.edit_xls.text);
        table.reverse(xls_data);
    }
    
    // 查找视频文件名
    if(video_data == null){
        winform.edit_log.print("扫描上传视频文件");
        video_data = fsys.list(winform.edit_video.text)
    }
    
    winform.button_start.text = "停止";
    if(start_flag){     
        winform.edit_log.print("开始");
        winform.edit_log.print("打开抖音上传页面");
        wb.go("https://creator.douyin.com/creator-micro/content/upload");
    }else {
        start_flag = true;
        winform.edit_log.print("继续");       
        // 第二个页面,发送文本
        wb.waitEle("div#root div.editor-kit-root-container",`
            aardio.sendMsg(document.querySelector("div#root div.editor-kit-root-container > div > div").getBoundingClientRect(),false);
        `);
    }       
}

// 清空按钮
winform.button_clean.oncommand = function(id,event){
    winform.edit_log.text = "";
}

// 视频目录
import fsys.dlg.dir;
winform.btn_video.oncommand = function(id,event){
    var path = fsys.dlg.dir(,winform,'请选择视频目录')
    if(path){
        winform.edit_video.text = path;
        video_data = null;
    }   
    // 保持配置
    config.saveAll();
}
winform.btn_video.skin({
    color={
        active=0xFF00FF00;
        default=0xFF3C3C3C;
        disabled=0xFF6D6D6D;
        hover=0xFFFF0000
    }
})

// excel 文件
import fsys.dlg;
winform.btn_xls.oncommand = function(id,event){
    var path = fsys.dlg.open('Excel文件|*.elx;*.xlsx||',,,winform);
    if(path){
        winform.edit_xls.text = path;
        xls_data = null;
    }
    // 保持配置
    config.saveAll();
}
winform.btn_xls.skin({
    color={
        active=0xFF00FF00;
        default=0xFF3C3C3C;
        disabled=0xFF6D6D6D;
        hover=0xFFFF0000
    }
})

winform.show();

// 话术编辑后保存
winform.edit_tips.onFocusLost = function(id,event){ 
    // 保持配置
    config.saveAll();
}

// 间隔时间
winform.edit_sleep.onFocusLost = function(id,event){
    // 保持配置
    config.saveAll();
}
// 清空日志框
winform.edit_log.text = "请自行登录抖音账号";
// 打开抖音
wb.go("https://www.douyin.com/");

winform.edit_vip.onFocusLost = function(id,event){
    checkVip();
}

checkVip();
checkExit();

win.loopMessage();
上一篇下一篇

猜你喜欢

热点阅读