Weibo All Hidden

批量修改微博可见范围

// ==UserScript==
// @name         Weibo All Hidden
// @namespace    http://tampermonkey.net/
// @version      0.1.5
// @description  批量修改微博可见范围
// @author       Wei
// @match        http*://*weibo.com*
// @match        https://weibo.com/comment/outbox
// @match        https://weibo.com/u/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=weibo.com
// @grant        none
// @license MIT
// ==/UserScript==

class W {
    privateAttributes = [// 0: ref; 1: original event; 2: css; 3: w label
        {name: 'w-group', typ: 3},
        {name: 'w-verify', typ: 3},
        {name: 'w-ref', typ: 0},
        {name: 'w-click', typ: 1},
        {name: 'w-display', typ: 2},
        {name: 'w-weight', typ: 2},
        {name: 'w-height', typ: 2},
    ]

    constructor() {
        this.ref = {}
        this.v2H = []
        this.variable = (v) => {
            setTimeout(() => {
                this.updateHtml();
            }, 1)
            return v;
        }
    }

    init() {
        let wEle = document.getElementsByClassName('w')
        // 判断元素中是否有privateAttributes
        for (let i = 0; i < wEle.length; i++) {
            let ele = wEle[i]
            let group = null
            let verify = false
            this.privateAttributes.forEach((att) => {
                let attValue = ele.getAttribute(att.name)
                if (attValue !== null) {
                    const name = att.name.replace('w-', '')
                    if (att.typ === 0) {        // w-ref
                        this.ref[attValue] = ele
                    } else if (att.typ === 1) { // event
                        let f = eval(attValue)
                        if (group) {
                            group.forEach((item) => {
                                item.addEventListener(name, () => {
                                    f(item.value)
                                }, false)
                            })
                        } else {
                            ele.addEventListener(name, () => {
                                verify ? (window.confirm("确定执行么?") ? f(ele.id) : '') : f(ele.id)
                            }, false)
                        }
                    } else if (att.typ === 2) { // css
                        ele.style[name] = attValue
                    } else if (att.typ === 3) { // group
                        if (name === 'group') group = Array.from(ele.getElementsByClassName(attValue))
                        if (name === 'verify') verify = true;
                    }
                }
                //正则匹配 ele.innerHTML 中是否含有 {{xxx}} 并且ele要为最小元素 匹配所有结果
                let reg = /{{(.*?)}}/g
                let matches;
                let v2HInfo = {
                    element: ele,
                    orgHTML: ele.innerHTML,
                    mv: []
                }
                let matchFlag = false
                while ((matches = reg.exec(ele.innerHTML)) !== null && ele.childElementCount === 0) {
                    matchFlag = true;
                    let match = matches[0]; // 匹配的完整字符串,例如 "{{var1}}"
                    let variable = matches[1];
                    v2HInfo.mv.push({
                        match: match,
                        variable: variable,
                    })
                }
                if (matchFlag) {
                    //console.log(v2HInfo)
                    this.v2H.push(v2HInfo)
                }
            })
        }
        this.updateHtml()
    }

    updateHtml() {
        this.v2H.forEach((v2HInfo) => {
            let orgHTML = v2HInfo.orgHTML
            v2HInfo.mv.forEach((item) => {
                orgHTML = orgHTML.replace(item.match, eval(item.variable))
            })
            v2HInfo.element.innerHTML !== orgHTML ? (v2HInfo.element.innerHTML = orgHTML): ''
        })
    }
}
let w = new W()
let f1 = true
let f2 = true
let userInfo = {
    X_XSRF_TOKEN: null,
    uid: null,
    status: null,
    name: null,
    count: null,
    wbInfo: [],
    wbVisibleInfo: {
        0: 0,
        10: 0,
        2: 0,
        1: 0
    },
    processGet: 0,
    processSkip: 0,
    modifyVisibleType: null,
    modifyVisibleTextList: {
        0: "公开",
        10: "粉丝",
        2: "朋友",
        1: "仅自己",
    },
    processModifyVisible: 0,
    error: "点击查看错误",
    hasWWW: false,


    cmtTotal: 0,
    cmtPublicTotal: 0,
    cmtGet: 0,
};

function get(url, data, callback){
    //创建异步对象
    var xhr = null
    if (window.XMLHttpRequest) {
        xhr = new XMLHttpRequest();
    } else if(window.ActiveXObject) {//IE6及以下
        xhr = new ActiveXObject('Microsoft.XMLHTTP');
    }
    //判断data是否为空
    if(data){
        url=url+'?'+params(data);
    }
    //设置请求行
    xhr.open('get',url);
    //设置请求头(get可以省略)
    xhr.setRequestHeader("x-xsrf-token",userInfo.X_XSRF_TOKEN);
    //注册回调函数
    xhr.onreadystatechange = function(){
        if(xhr.readyState==4&&xhr.status==200){
            //调用传递的回调函数
            callback(xhr.responseText);
        }
    }
    //发送请求主体
    xhr.send(null);
}

function post(url, data, callback){
    //创建异步对象
    var xhr = null
    if (window.XMLHttpRequest) {
        xhr = new XMLHttpRequest();
    } else if(window.ActiveXObject) {//IE6及以下
        xhr = new ActiveXObject('Microsoft.XMLHTTP');
    }
    //设置请求行
    xhr.open('post',url);
    //设置请求头(post有数据发送才需要设置请求头)
    //判断是否有数据发送
    xhr.setRequestHeader("x-xsrf-token",userInfo.X_XSRF_TOKEN);
    if(data){
        xhr.setRequestHeader("Content-type","application/json; charset=utf-8");
    }
    //注册回调函数
    xhr.onreadystatechange = function(){
        if(xhr.readyState==4&&xhr.status==200){
            //调用传递的回调函数
            callback(xhr.responseText);
        } else {
            throw new Error('error');
        }
    }
    //发送请求主体
    xhr.send(JSON.stringify(data));
}

let getWb = () => {
    const paramsStr = window.location.href
    userInfo.hasWWW = paramsStr.search('www') === -1 ? '' : 'www.';
    userInfo.uid = paramsStr.split("/").pop();
    get(`https://${userInfo.hasWWW}weibo.com/ajax/profile/info?uid=${userInfo.uid}`, null, (e) => {
        let recvJson = JSON.parse(e);
        console.log(recvJson, recvJson.ok);
        userInfo.status = recvJson.ok;
        userInfo.name = recvJson.data.user.screen_name;
        userInfo.count = recvJson.data.user.statuses_count;
        w.updateHtml();
    })
}

let getAllWbID = () => {
    if (!userInfo.count || !userInfo.status) return;
    const pageCount = Math.ceil(userInfo.count/20);
    userInfo.wbInfo = [];
    userInfo.wbVisibleInfo = {
        0:0,
        10:0,
        2:0,
        1:0
    }
    userInfo.processGet = w.variable(0);


    for (let i = 0; i < pageCount; i++) {
        //for (let i = 0; i < 1; i++) {
        setTimeout(() => {
            get(`https://${userInfo.hasWWW}weibo.com/ajax/statuses/mymblog?uid=${userInfo.uid}&page=${i+1}&feature=0`, null, (e) => {
                let recvJson = JSON.parse(e);
                let dataList = recvJson.data.list;
                for (let j =0; j<dataList.length;j++ ){
                    let item = dataList[j];
                    userInfo.wbInfo.push({
                        id: item.id,
                        visible: item.visible.type,
                        mblogid: item.mblogid
                    })

                    userInfo.wbVisibleInfo[item.visible.type] += 1;
                    userInfo.processGet += 1;
                    w.updateHtml();
                }
            });
        }, 500 * i)
    }
}

let modifyVisible = () => {
    if(userInfo.modifyVisibleType === null) return;
    userInfo.processSkip = 0;
    userInfo.processModifyVisible = 0;
    let wbInfoNeedModify = [];
    let errorIndexList = [];
    for (let i = 0; i <userInfo.count; i++) {
        //for (let i = 0; i < 1; i++) {
        if(String(userInfo.wbInfo[i].visible) === userInfo.modifyVisibleType) {
            userInfo.processSkip += 1;
            userInfo.processModifyVisible += 1;
            w.updateHtml();
            continue;
        } else {
            wbInfoNeedModify.push(i);
            errorIndexList.push(i);
        }
    }
    for (let i = 0; i <wbInfoNeedModify.length; i++) {
        setTimeout(() => {
            try{
                post(`https://${userInfo.hasWWW}weibo.com/ajax/statuses/modifyVisible`, {
                    ids:String(userInfo.wbInfo[wbInfoNeedModify[i]].id),
                    visible:String(userInfo.modifyVisibleType),
                }, (e)=>{
                    userInfo.processModifyVisible += 1;
                    errorIndexList.filter(item => item === wbInfoNeedModify[i]);
                    w.updateHtml();
                })
            } catch (e) {
                userInfo.error += `<br>${e}<a href="https://weibo.com/${userInfo.uid}/${userInfo.wbInfo[errorIndexList[i]].mblogid}">${userInfo.wbInfo[errorIndexList[i]].mblogid}</a>`
                w.updateHtml();
            }
        }, 1000 * i)
    }
    w.updateHtml();
}

let getMyCmt = () => {
    userInfo.cmtGet = 0;
    userInfo.cmtPublicTotal = 0;
    let i = 0
    let g = (nc) => {
        i += 1;
        if (i > 2) return
        get(`https://weibo.com/ajax/message/myCmt${nc ? '?max_id='+nc : ''}`, null, (e)=>{
            let recvJson = JSON.parse(e);
            userInfo.cmtGet += recvJson.data.comments.length;
            userInfo.cmtTotal = w.variable(recvJson.data.total_number);
            recvJson.data.comments.forEach((item)=>{
                // if (item.status.user.verified && item.status.user.verified_type in [0, 2]) {
                //     userInfo.cmtPublicTotal += 1;
                //     console.log(item.text)
                // }
                console.log(item.text, item.status.user.verified, item.status.user.verified_type)
            });
            if (recvJson.data.comments.length > 1) {
                let nextCursor = recvJson.data.next_cursor;
                g(nextCursor)
            }
        })
    };
    g()


}


let initHtml = () => {
    var htmlCode = `
<style>
    .wb-tool {
        width: 300px;
        bottom: 20px;
        right: 20px;
        padding: 10px 10px 8px 10px;
        background-color: #eee;
        z-index: 9999;
        position: fixed;
        overflow: hidden;
        border-radius: 10px;
        transition: all 0.5s;
        box-shadow: 0 0 5px 2px rgba(0, 0, 0, .2);
    }

    .title {
        font-size: 22px;
        font-weight: 600;
        text-align: center;
        margin-top: 5px;
    }

    .note {
        font-size: 14px;
        font-weight: 600;
        color: #EE0000;
    }

    .info {
        font-size: 14px;
        font-weight: 600;
        color: #000;
        margin: 3px 0;
    }

    .btn {
        border: none;
        background-color: #fff;
        width: 100%;
        margin-top: 8px;
        padding: 8px;
        border-radius: 4px;
        transition: all 0.2s;
        display: inline-block;
        cursor: pointer;
    }

    .btn:hover {
        box-shadow: 0 0 5px 2px rgba(0, 0, 255, .2);
    }

    .btn-sm {
        width: 80%;
        margin: 0 10%;
        background-color: #f1f1f1;
    }

    .card {
        width: 100%;
        /*min-height: 100px;*/
        background-color: #fff;
        border-radius: 10px;
        margin-top: 8px;
        overflow: hidden;
        transition: all 0.2s;
    }

    .card-title {
        font-size: 14px;
        font-weight: 600;
        color: #222;
        margin: 5px;
    }

    .card-info {
        margin: 5px;
        font-size: 14px;
        color: #000;
    }

    .error {
        width: calc(100% - 10px);
        height: 92px;
        border-radius: 8px;
        color: red;
        background-color: antiquewhite;
        overflow: auto;
    }


</style>
<div class="w wb-tool">
    <div class="w title">微博工具</div>
    <p class="w note">
        注意事项:
        <br>1. 请谨慎使用,部分操作无法恢复!
        <br>2. "仅自己可见"无法隐藏"快转"的内容!
    </p>
    <div class="w info" id="info-token">Token状态:{{userInfo.X_XSRF_TOKEN?'获取成功':'获取失败'}}</div>
    <div class="w info" id="info-status">状态:{{userInfo.status?'Success':'Error'}}</div>
    <div class="w info" id="info-name">昵称:{{userInfo.name}}</div>
    <div class="w info" id="info-count">总微博数:{{userInfo.count}}</div>
    <button class="w btn" id="showCardModifyVisibleWb" w-click="onCardClick">微博可见范围修改</button>
    <button class="w btn" id="showCardDeletePublicCmt" w-click="onCardClick">微博公开评论删除</button>
    <div class="w card" w-ref="cardMVW" w-height="0">
        <div class="w card-title">微博可见范围修改</div>
        <button class="w btn btn-sm" id="btnGetAllWb" w-verify w-click="getAllWbID">获取全部微博信息</button>
        <div class="w card-info">进度:{{userInfo.processGet}} / {{userInfo.count}}</div>
        <div class="w card-info" id="visibleInfo">
            公开:{{userInfo.wbVisibleInfo[0]}},粉丝:{{userInfo.wbVisibleInfo[10]}},
            朋友:{{userInfo.wbVisibleInfo[2]}},仅自己:{{userInfo.wbVisibleInfo[1]}}
        </div>

        <div class="w card-info" w-group="radio" w-click="(value) => userInfo.modifyVisibleType = w.variable(value)">
            <input name="radio" type="radio" id="public" class="w radio" value="0"/>
            <label for="public">公开</label>

            <input name="radio" type="radio" id="fans" class="w radio" value="10"/>
            <label for="fans">粉丝</label>

            <input name="radio" type="radio" id="friends" class="w radio" value="2"/>
            <label for="friends">朋友</label>

            <input name="radio" type="radio" id="me" class="w radio" value="1"/>
            <label for="me">仅自己</label>
        </div>
        <button class="w btn btn-sm" id="btnModifyVisible" w-verify w-click="modifyVisible">
            全部转换到{{userInfo.modifyVisibleTextList[userInfo.modifyVisibleType]}}
        </button>
        <div class="w card-info" id="processModifyVisible">
            进度:{{userInfo.processModifyVisible}} / {{userInfo.count}},
            跳过:{{userInfo.processSkip}} / {{userInfo.count}}
        </div>
        <div class="w card-info error" id="error">{{userInfo.error}}</div>
    </div>
    <div class="w card" w-ref="cardDPC" w-height="0">
        <div class="w card-title">微博公开评论删除</div>
        <button class="w btn btn-sm" id="btnGetAllCmt" w-click="getMyCmt">获取公开评论信息</button>
        <div class="w card-info">总共发出评论:{{userInfo.cmtTotal}}</div>
        <div class="w card-info">已经获取评论:{{userInfo.cmtGet}}</div>
        <div class="w card-info">公开评论数:{{userInfo.cmtPublicTotal}}</div>

    </div>
</div>
`;
    let insertElement = document.createElement("div");
    insertElement.innerHTML = htmlCode;
    document.body.append(insertElement);
}

let onCardClick = (id) => {
    console.log(id)
    if (id === 'showCardModifyVisibleWb') {
        w.ref.cardMVW.style.height = (f1) ? '300px' : '0px'
        f1 = !f1
    }
    if (id === 'showCardDeletePublicCmt') {
        w.ref.cardDPC.style.height = (f2) ? '200px' : '0px';
        f2 = !f2
    }
}

(function() {
    'use strict';
    window.onload=function(){
        initHtml();
        w.init()
        getWb();


        // 劫持所有请求获取X__XSRF__TOKEN
        function addXMLRequestCallback(callback) {
            var oldSend, i;
            if (XMLHttpRequest.callbacks) {
                XMLHttpRequest.callbacks.push(callback);
            } else {
                XMLHttpRequest.callbacks = [callback];
                oldSend = XMLHttpRequest.prototype.send;
                XMLHttpRequest.prototype.send = function () {
                    for (i = 0; i < XMLHttpRequest.callbacks.length; i++) {
                        XMLHttpRequest.callbacks[i](this);
                    }
                    return oldSend.apply(this, arguments);
                };
            }
        }
        XMLHttpRequest.prototype.wrappedSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
        XMLHttpRequest.prototype.setRequestHeader = function(header, value) {
            this.wrappedSetRequestHeader(header, value);
            if(!this.headers) {
                this.headers = {};
            }
            header = header.replaceAll('-', '__')
            this.headers[header] = value;
        }
        let flag = false;
        addXMLRequestCallback(function (xhr) {
            xhr.addEventListener("load", function () {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    if(!flag && xhr.headers.hasOwnProperty('X__XSRF__TOKEN')) {
                        userInfo.X_XSRF_TOKEN = w.variable(xhr.headers.X__XSRF__TOKEN)
                        flag = true;

                    }
                }
            });
        });
    }
})();