Weibo All Hidden

批量修改微博可见范围

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==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;

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