腾讯文档助手(批量导出)

批量导出

// ==UserScript==
// @name         腾讯文档助手(批量导出)
// @namespace    http://tampermonkey.net/
// @version      2024-02-11
// @description  批量导出
// @author       zhengqinqian
// @match        https://doc.weixin.qq.com/home/recent
// @match        https://doc.weixin.qq.com/home/mydoc
// @match        http://doc.weixin.qq.com/home/recent
// @require      https://cdn.jsdelivr.net/npm/toastr@2.1.4/toastr.min.js
// @resource     toastrCss   https://cdn.jsdelivr.net/npm/toastr@2.1.4/build/toastr.min.css
// @icon         https://www.google.com/s2/favicons?sz=64&domain=qq.com
// @grant        GM_cookie
// @license      MIT
// @grant        GM_xmlhttpRequest
// ==/UserScript==
//本地vue存储的文档信息
let list_data;
//doc下载API
let doc_export_api="https://doc.weixin.qq.com/v1/export/export_office?sid="
//doc导出进度查询
let doc_export_query_api="https://doc.weixin.qq.com/v1/export/query_progress?"
var localHref = window.location.href;
var UA = navigator.userAgent;
//正在导出的作业
var exporting_operationId=[]
var docCookies = {
    getItem: function (sKey) {
      return (
        decodeURIComponent(
          document.cookie.replace(
            new RegExp(
              "(?:(?:^|.*;)\\s*" +
                encodeURIComponent(sKey).replace(/[-.+*]/g, "\\$&") +
                "\\s*\\=\\s*([^;]*).*$)|^.*$",
            ),
            "$1",
          ),
        ) || null
      );
    },
    setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) {
      if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) {
        return false;
      }
      var sExpires = "";
      if (vEnd) {
        switch (vEnd.constructor) {
          case Number:
            sExpires =
              vEnd === Infinity
                ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT"
                : "; max-age=" + vEnd;
            break;
          case String:
            sExpires = "; expires=" + vEnd;
            break;
          case Date:
            sExpires = "; expires=" + vEnd.toUTCString();
            break;
        }
      }
      document.cookie =
        encodeURIComponent(sKey) +
        "=" +
        encodeURIComponent(sValue) +
        sExpires +
        (sDomain ? "; domain=" + sDomain : "") +
        (sPath ? "; path=" + sPath : "") +
        (bSecure ? "; secure" : "");
      return true;
    },
    removeItem: function (sKey, sPath, sDomain) {
      if (!sKey || !this.hasItem(sKey)) {
        return false;
      }
      document.cookie =
        encodeURIComponent(sKey) +
        "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" +
        (sDomain ? "; domain=" + sDomain : "") +
        (sPath ? "; path=" + sPath : "");
      return true;
    },
    hasItem: function (sKey) {
      return new RegExp(
        "(?:^|;\\s*)" +
          encodeURIComponent(sKey).replace(/[-.+*]/g, "\\$&") +
          "\\s*\\=",
      ).test(document.cookie);
    },
    keys: /* optional method: you can safely remove it! */ function () {
      var aKeys = document.cookie
        .replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, "")
        .split(/\s*(?:\=[^;]*)?;\s*/);
      for (var nIdx = 0; nIdx < aKeys.length; nIdx++) {
        aKeys[nIdx] = decodeURIComponent(aKeys[nIdx]);
      }
      return aKeys;
    },
  };

//vue数据初始化
function setData(){
    list_data=document.querySelector(".fileList_item_wrapper").__vue__.$attrs.list;
    console.log(list_data.length)
}
//恢复console.log
function setConsole() {
    // var iframe = document.createElement('iframe');
    // iframe.style.display = 'none';
    // document.body.appendChild(iframe);
    // var console = iframe.contentWindow.console;
    // window.console = console;
    // window.__autoClearConsoleTimer && clearInterval(window.__autoClearConsoleTimer)
    setInterval(()=>{window.__autoClearConsoleTimer && clearInterval(window.__autoClearConsoleTimer)},3000)
    // window.__resetConsole()
    // setTimeout(window.__resetConsole(),1000)
    //清除定时器
    // clearInterval(window.__autoClearConsoleTimer)
}
function JSON_to_URLEncoded(element,key,list){
    var list = list || [];
    if(typeof(element)=='object'){
      for (var idx in element)
        JSON_to_URLEncoded(element[idx],key?key+'['+idx+']':idx,list);
    } else {
      list.push(key+'='+encodeURIComponent(element));
    }
    return list.join('&');
}
function AjaxCall(href,type,data,callback) {
    var encode_data="";
    if(data){
        encode_data=JSON_to_URLEncoded(data);
    }
    console.log("AjaxCall:"+href+","+type+","+encode_data+","+encode_data.length)
    GM_xmlhttpRequest({
        method: type,
        url: href,
        data: encode_data,
        headers: {
            "User-Agent": UA,
            "Origin": "https://doc.weixin.qq.com/home",
            "content-type":"application/x-www-form-urlencoded; charset=UTF-8",
            // "cookie": document.cookie,
            // "Content-Length":encode_data.length,
            // "Host":"doc.weixin.qq.com"
        },
        onload: function(data,status) {
            if(data.readyState==4 && data.status==200){
                var htmlTxt = data.responseText;
                callback(null,htmlTxt);
            };
        },
        onerror: function (error) {
            console.info("AjaxCall.onerror")
            callback(error);
        },
        ontimeout: function (error) {
            console.info("AjaxCall.ontimeout")
            callback(error);
        },
    });
};
// 下载文档
function downLoadDoc(url,data){
    return new Promise(function(reslove,reject){
        AjaxCall(url,"POST",data,(error,reponse)=>{
            if(error){
                console.log("downLoadDoc error")
                resolve([false]);
                return;
            }
            var json = JSON.parse(reponse);
            if(json.ret==0){
                console.log("downLoadDoc return :"+reponse)
                console.log(reponse)
                reslove([true,json.operationId])
            }else{
                console.warn("downLoadDoc return :"+reponse)
                console.warn(reponse)
                reslove([false])
            }
        })
    })
}
//查询进度
function queryProgress(url,itemId){
    return new Promise(function(reslove,reject){
        AjaxCall(url,"GET",null,(error,reponse)=>{
            if(error){
                console.log("queryProgress error")
                resolve([false,itemId]);
                return;
            }
            var json = JSON.parse(reponse);
            if(json.ret==0&&json.progress==100){
                console.log("queryProgress ret:"+json.ret)
                console.log(reponse)
                reslove([true,json.file_url])
            }else{
                console.warn("queryProgress ret "+json.ret)
                console.warn(reponse)
                reslove([false,itemId])
            }
        })
    })
}
// selector is optional - defaults to all elements including window and document
// Do not pass window / document objects. Instead use pseudoselectors: 'window' or 'document'
// eTypeArray is optional - defaults to clearing all event types
function removeAllEventListeners(selector = '*', eTypeArray = ['*']) {
    switch (selector.toLowerCase()) {
      case 'window':
        removeListenersFromElem(window);
        break;
      case 'document':
        removeListenersFromElem(document);
        break;
      case '*':
        removeListenersFromElem(window);
        removeListenersFromElem(document);
      default:
        document.querySelectorAll(selector).forEach(removeListenersFromElem);
    }
    
    function removeListenersFromElem(elem) {
      let eListeners = getEventListeners(elem);
      let eTypes = Object.keys(eListeners);
      for (let eType of inBoth(eTypes, eTypeArray)) {
        eListeners[eType].forEach((eListener)=>{
          let options = {};
          inBoth(Object.keys(eListener), ['capture', 'once', 'passive', 'signal'])
            .forEach((key)=>{ options[key] = eListener[key] });
          elem.removeEventListener(eType, eListener.listener, eListener.useCapture);
          elem.removeEventListener(eType, eListener.listener, options);
        });
      }
    }
  
    function inBoth(arrA, arrB) {
      setB = new Set(arrB);
      if (setB.has('*')) {
        return arrA;
      } else {
        return arrA.filter(a => setB.has(a));
      }
    }
  }
//阻止冒泡,去除框外取消勾选 todo
function cancelBubble(){
    document.querySelector("body").addEventListener("click",(e)=>{e.stopPropagation();})
    document.querySelector("body").addEventListener("mousedown",(e)=>{e.stopPropagation();})
    var home_topfile=document.querySelector(".home_topfile");
    // removeAllEventListeners(".home_topfile")
    // home_topfile.replaceWith(home_topfile.cloneNode(true))
    // getEventListeners(document.querySelector(".home_topfile"))
    var placeToReplace;
    if (window.EventTarget && EventTarget.prototype.addEventListener) {
      placeToReplace = EventTarget;
    } else {
      placeToReplace = Element;
    }

    placeToReplace.prototype.oldaddEventListener = placeToReplace.prototype.addEventListener;
    placeToReplace.prototype.addEventListener = function(event, handler, placeholder) {
    //   console.log("calling substitute");
      if (arguments.length < 3) {
        this.oldaddEventListener(event, handler, false);
      } else {
        this.oldaddEventListener(event, handler, placeholder);
      }
    }
    document.querySelector(".home_topfile").addEventListener("mousedown", function() {
      console.log("foo");
    });


}
//onLoad
function pageOnload(){
    // setData
    setData();
    // setConsole
    setConsole();
    // cancelBubble();
    //recent页面显示在tab-container
    if(localHref.includes("home/recent")){
        //批量导出按钮
        document.querySelector(".tab-container").insertAdjacentHTML('beforeend','<button id="batchExport" type="button" class="xd_btn fileToolbar_button xd_btn_Blue xd_btn_Supper" style="width: auto; ">批量导出</button>')
        //全选按钮
        document.querySelector(".tab-container").insertAdjacentHTML('beforeend','<button id="selectAllItem" type="button" class="xd_btn fileToolbar_button xd_btn_Supper" style="width: auto;color: white;background: seagreen;display:inline">全选</button>')
    }else{
        document.querySelector(".xd-web-header_toolbar").insertAdjacentHTML('beforeend','<button id="selectAllItem" type="button" class="xd_btn fileToolbar_button xd_btn_Supper" style="width: auto;color: white;background: seagreen;display:inline">全选</button>')
        document.querySelector(".xd-web-header_toolbar").insertAdjacentHTML('beforeend','<button id="batchExport" type="button" class="xd_btn fileToolbar_button xd_btn_Blue xd_btn_Supper" style="width: auto; ">批量导出</button>')
    }
    // document.querySelectorAll(".xd_checkbox").forEach(item=>{
    //     console.log("debug for fileList_item_checkbox")
    //     parentDiv=item.parentNode.parentNode;
    //     item.addEventListener("click",(e)=>{
    //         if(parentDiv.getAttribute("class").indexOf("fileList_item_Active")==-1){
    //             //勾选
    //             console.log("click select")
    //             parentDiv.classList.add("fileList_item_Active");  
    //             item.firstChild.classList.replace("xd_common_unselect-normal","xd_common_select-normal");
    //         }
    //         else{
    //             //取消勾选
    //             console.log("rever select")
    //             parentDiv.classList.remove("fileList_item_Active");  
    //             item.firstChild.classList.replace("xd_common_select-normal","xd_common_unselect-normal");
    //         }
    //     })
    // });
    document.querySelectorAll(".fileList_item_checkbox").forEach(item=>{item.style.display="flex";});
    //点击批量导出
    document.querySelector("#batchExport").addEventListener("click",(e)=>{
        console.log("batchExport")
        if(list_data.length<=0){
            console.warn("data is null")
            return
        }
        var docList=document.querySelectorAll(".fileList_item_wrapper");
        console.log(docList.length)
        for(var i=0;i<docList.length;i++){
            var docItem=docList[i];
            console.log("i:"+i+","+docItem.firstChild.className+","+list_data[i].doc_id)
            //选中
            if(docItem.firstChild.className.includes("fileList_item_Active")){
                // if(i>0){
                //     return;
                // }
                // 执行导出作业
                var url=doc_export_api+docCookies.getItem("wedoc_sid")
                console.log("docItem.doc_id:"+list_data[i].doc_id)
                downLoadDoc(url,{"docId":list_data[i].doc_id})
                    .then((resp)=>{
                        console.log("downLoadDoc then:"+resp)
                        if(resp[0]){
                            //加入到待查询列表中
                            exporting_operationId.push(resp[1])
                            //清除选中状态,默认会清除
                            // docItem.firstChild.className.remove("fileList_item_Active")

                        }
                })
            }
        }
    })
    //点击全选
    document.querySelector("#selectAllItem").addEventListener("click",()=>{
        //全选
        if(document.querySelector(".fileList_item_wrapper > div:first-child").getAttribute("class").indexOf("fileList_item_Active")==-1){
            console.log("click selectAll")
            document.querySelectorAll(".fileList_item_wrapper > div:first-child").forEach(item=>item.classList.add("fileList_item_Active"));  
            document.querySelectorAll(".xd_checkbox > i").forEach(item=>item.classList.replace("xd_common_unselect-normal","xd_common_select-normal"));
        }else{
            //取消全选
            console.log("rever selectAll")
            document.querySelectorAll(".fileList_item_wrapper > div:first-child").forEach(item=>item.classList.remove("fileList_item_Active"));  
            document.querySelectorAll(".xd_checkbox > i").forEach(item=>item.classList.replace("xd_common_select-normal","xd_common_unselect-normal"));
        }
    })
    console.log("pageOnload end")
}
let await_flag=false;
(function() {
    document.onreadystatechange = function () {
        if (document.readyState === "complete") {
            setTimeout(()=>pageOnload(),1000)
        }
    };
    //window.addEventListener("load", (event)=>pageOnload(event));
    var wedoc_skey=docCookies.getItem("wedoc_skey");
    var wedoc_ticket=docCookies.getItem("wedoc_ticket");

    setInterval(async()=>{
        if(await_flag){
            console.log("busy")
            return;
        }
        await_flag=true;
        var time_stmap=new Date().getTime();
        console.log(time_stmap+","+exporting_operationId.length)
        var new_exporting_operationId=[];
        for(var i=0 ;i<exporting_operationId.length;i++){
            var item=exporting_operationId[i];
            var url=doc_export_query_api+"operationId="+item+"&timestamp="+new Date().getTime()
             await queryProgress(url,item)
                .then((resp)=>{
                    console.log("downLoadDoc retuen:"+resp)
                    if(resp[0]){
                        //下载
                        window.open(resp[1])
                    }else{
                        new_exporting_operationId.push(resp[1])
                    }
                })
            
        }
        exporting_operationId=new_exporting_operationId;
        await_flag=false;
    },3000)
    
    // alert("wedoc_skey:"+wedoc_skey+"\n"+"wedoc_ticket"+wedoc_ticket);
})();