// ==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;
}
}
});
});
}
})();