Weibo All Hidden

批量修改微博可见范围

  1. // ==UserScript==
  2. // @name Weibo All Hidden
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1.5
  5. // @description 批量修改微博可见范围
  6. // @author Wei
  7. // @match http*://*weibo.com*
  8. // @match https://weibo.com/comment/outbox
  9. // @match https://weibo.com/u/*
  10. // @icon https://www.google.com/s2/favicons?sz=64&domain=weibo.com
  11. // @grant none
  12. // @license MIT
  13. // ==/UserScript==
  14.  
  15. class W {
  16. privateAttributes = [// 0: ref; 1: original event; 2: css; 3: w label
  17. {name: 'w-group', typ: 3},
  18. {name: 'w-verify', typ: 3},
  19. {name: 'w-ref', typ: 0},
  20. {name: 'w-click', typ: 1},
  21. {name: 'w-display', typ: 2},
  22. {name: 'w-weight', typ: 2},
  23. {name: 'w-height', typ: 2},
  24. ]
  25.  
  26. constructor() {
  27. this.ref = {}
  28. this.v2H = []
  29. this.variable = (v) => {
  30. setTimeout(() => {
  31. this.updateHtml();
  32. }, 1)
  33. return v;
  34. }
  35. }
  36.  
  37. init() {
  38. let wEle = document.getElementsByClassName('w')
  39. // 判断元素中是否有privateAttributes
  40. for (let i = 0; i < wEle.length; i++) {
  41. let ele = wEle[i]
  42. let group = null
  43. let verify = false
  44. this.privateAttributes.forEach((att) => {
  45. let attValue = ele.getAttribute(att.name)
  46. if (attValue !== null) {
  47. const name = att.name.replace('w-', '')
  48. if (att.typ === 0) { // w-ref
  49. this.ref[attValue] = ele
  50. } else if (att.typ === 1) { // event
  51. let f = eval(attValue)
  52. if (group) {
  53. group.forEach((item) => {
  54. item.addEventListener(name, () => {
  55. f(item.value)
  56. }, false)
  57. })
  58. } else {
  59. ele.addEventListener(name, () => {
  60. verify ? (window.confirm("确定执行么?") ? f(ele.id) : '') : f(ele.id)
  61. }, false)
  62. }
  63. } else if (att.typ === 2) { // css
  64. ele.style[name] = attValue
  65. } else if (att.typ === 3) { // group
  66. if (name === 'group') group = Array.from(ele.getElementsByClassName(attValue))
  67. if (name === 'verify') verify = true;
  68. }
  69. }
  70. //正则匹配 ele.innerHTML 中是否含有 {{xxx}} 并且ele要为最小元素 匹配所有结果
  71. let reg = /{{(.*?)}}/g
  72. let matches;
  73. let v2HInfo = {
  74. element: ele,
  75. orgHTML: ele.innerHTML,
  76. mv: []
  77. }
  78. let matchFlag = false
  79. while ((matches = reg.exec(ele.innerHTML)) !== null && ele.childElementCount === 0) {
  80. matchFlag = true;
  81. let match = matches[0]; // 匹配的完整字符串,例如 "{{var1}}"
  82. let variable = matches[1];
  83. v2HInfo.mv.push({
  84. match: match,
  85. variable: variable,
  86. })
  87. }
  88. if (matchFlag) {
  89. //console.log(v2HInfo)
  90. this.v2H.push(v2HInfo)
  91. }
  92. })
  93. }
  94. this.updateHtml()
  95. }
  96.  
  97. updateHtml() {
  98. this.v2H.forEach((v2HInfo) => {
  99. let orgHTML = v2HInfo.orgHTML
  100. v2HInfo.mv.forEach((item) => {
  101. orgHTML = orgHTML.replace(item.match, eval(item.variable))
  102. })
  103. v2HInfo.element.innerHTML !== orgHTML ? (v2HInfo.element.innerHTML = orgHTML): ''
  104. })
  105. }
  106. }
  107. let w = new W()
  108. let f1 = true
  109. let f2 = true
  110. let userInfo = {
  111. X_XSRF_TOKEN: null,
  112. uid: null,
  113. status: null,
  114. name: null,
  115. count: null,
  116. wbInfo: [],
  117. wbVisibleInfo: {
  118. 0: 0,
  119. 10: 0,
  120. 2: 0,
  121. 1: 0
  122. },
  123. processGet: 0,
  124. processSkip: 0,
  125. modifyVisibleType: null,
  126. modifyVisibleTextList: {
  127. 0: "公开",
  128. 10: "粉丝",
  129. 2: "朋友",
  130. 1: "仅自己",
  131. },
  132. processModifyVisible: 0,
  133. error: "点击查看错误",
  134. hasWWW: false,
  135.  
  136.  
  137. cmtTotal: 0,
  138. cmtPublicTotal: 0,
  139. cmtGet: 0,
  140. };
  141.  
  142. function get(url, data, callback){
  143. //创建异步对象
  144. var xhr = null
  145. if (window.XMLHttpRequest) {
  146. xhr = new XMLHttpRequest();
  147. } else if(window.ActiveXObject) {//IE6及以下
  148. xhr = new ActiveXObject('Microsoft.XMLHTTP');
  149. }
  150. //判断data是否为空
  151. if(data){
  152. url=url+'?'+params(data);
  153. }
  154. //设置请求行
  155. xhr.open('get',url);
  156. //设置请求头(get可以省略)
  157. xhr.setRequestHeader("x-xsrf-token",userInfo.X_XSRF_TOKEN);
  158. //注册回调函数
  159. xhr.onreadystatechange = function(){
  160. if(xhr.readyState==4&&xhr.status==200){
  161. //调用传递的回调函数
  162. callback(xhr.responseText);
  163. }
  164. }
  165. //发送请求主体
  166. xhr.send(null);
  167. }
  168.  
  169. function post(url, data, callback){
  170. //创建异步对象
  171. var xhr = null
  172. if (window.XMLHttpRequest) {
  173. xhr = new XMLHttpRequest();
  174. } else if(window.ActiveXObject) {//IE6及以下
  175. xhr = new ActiveXObject('Microsoft.XMLHTTP');
  176. }
  177. //设置请求行
  178. xhr.open('post',url);
  179. //设置请求头(post有数据发送才需要设置请求头)
  180. //判断是否有数据发送
  181. xhr.setRequestHeader("x-xsrf-token",userInfo.X_XSRF_TOKEN);
  182. if(data){
  183. xhr.setRequestHeader("Content-type","application/json; charset=utf-8");
  184. }
  185. //注册回调函数
  186. xhr.onreadystatechange = function(){
  187. if(xhr.readyState==4&&xhr.status==200){
  188. //调用传递的回调函数
  189. callback(xhr.responseText);
  190. } else {
  191. throw new Error('error');
  192. }
  193. }
  194. //发送请求主体
  195. xhr.send(JSON.stringify(data));
  196. }
  197.  
  198. let getWb = () => {
  199. const paramsStr = window.location.href
  200. userInfo.hasWWW = paramsStr.search('www') === -1 ? '' : 'www.';
  201. userInfo.uid = paramsStr.split("/").pop();
  202. get(`https://${userInfo.hasWWW}weibo.com/ajax/profile/info?uid=${userInfo.uid}`, null, (e) => {
  203. let recvJson = JSON.parse(e);
  204. console.log(recvJson, recvJson.ok);
  205. userInfo.status = recvJson.ok;
  206. userInfo.name = recvJson.data.user.screen_name;
  207. userInfo.count = recvJson.data.user.statuses_count;
  208. w.updateHtml();
  209. })
  210. }
  211.  
  212. let getAllWbID = () => {
  213. if (!userInfo.count || !userInfo.status) return;
  214. const pageCount = Math.ceil(userInfo.count/20);
  215. userInfo.wbInfo = [];
  216. userInfo.wbVisibleInfo = {
  217. 0:0,
  218. 10:0,
  219. 2:0,
  220. 1:0
  221. }
  222. userInfo.processGet = w.variable(0);
  223.  
  224.  
  225. for (let i = 0; i < pageCount; i++) {
  226. //for (let i = 0; i < 1; i++) {
  227. setTimeout(() => {
  228. get(`https://${userInfo.hasWWW}weibo.com/ajax/statuses/mymblog?uid=${userInfo.uid}&page=${i+1}&feature=0`, null, (e) => {
  229. let recvJson = JSON.parse(e);
  230. let dataList = recvJson.data.list;
  231. for (let j =0; j<dataList.length;j++ ){
  232. let item = dataList[j];
  233. userInfo.wbInfo.push({
  234. id: item.id,
  235. visible: item.visible.type,
  236. mblogid: item.mblogid
  237. })
  238.  
  239. userInfo.wbVisibleInfo[item.visible.type] += 1;
  240. userInfo.processGet += 1;
  241. w.updateHtml();
  242. }
  243. });
  244. }, 500 * i)
  245. }
  246. }
  247.  
  248. let modifyVisible = () => {
  249. if(userInfo.modifyVisibleType === null) return;
  250. userInfo.processSkip = 0;
  251. userInfo.processModifyVisible = 0;
  252. let wbInfoNeedModify = [];
  253. let errorIndexList = [];
  254. for (let i = 0; i <userInfo.count; i++) {
  255. //for (let i = 0; i < 1; i++) {
  256. if(String(userInfo.wbInfo[i].visible) === userInfo.modifyVisibleType) {
  257. userInfo.processSkip += 1;
  258. userInfo.processModifyVisible += 1;
  259. w.updateHtml();
  260. continue;
  261. } else {
  262. wbInfoNeedModify.push(i);
  263. errorIndexList.push(i);
  264. }
  265. }
  266. for (let i = 0; i <wbInfoNeedModify.length; i++) {
  267. setTimeout(() => {
  268. try{
  269. post(`https://${userInfo.hasWWW}weibo.com/ajax/statuses/modifyVisible`, {
  270. ids:String(userInfo.wbInfo[wbInfoNeedModify[i]].id),
  271. visible:String(userInfo.modifyVisibleType),
  272. }, (e)=>{
  273. userInfo.processModifyVisible += 1;
  274. errorIndexList.filter(item => item === wbInfoNeedModify[i]);
  275. w.updateHtml();
  276. })
  277. } catch (e) {
  278. userInfo.error += `<br>${e}<a href="https://weibo.com/${userInfo.uid}/${userInfo.wbInfo[errorIndexList[i]].mblogid}">${userInfo.wbInfo[errorIndexList[i]].mblogid}</a>`
  279. w.updateHtml();
  280. }
  281. }, 1000 * i)
  282. }
  283. w.updateHtml();
  284. }
  285.  
  286. let getMyCmt = () => {
  287. userInfo.cmtGet = 0;
  288. userInfo.cmtPublicTotal = 0;
  289. let i = 0
  290. let g = (nc) => {
  291. i += 1;
  292. if (i > 2) return
  293. get(`https://weibo.com/ajax/message/myCmt${nc ? '?max_id='+nc : ''}`, null, (e)=>{
  294. let recvJson = JSON.parse(e);
  295. userInfo.cmtGet += recvJson.data.comments.length;
  296. userInfo.cmtTotal = w.variable(recvJson.data.total_number);
  297. recvJson.data.comments.forEach((item)=>{
  298. // if (item.status.user.verified && item.status.user.verified_type in [0, 2]) {
  299. // userInfo.cmtPublicTotal += 1;
  300. // console.log(item.text)
  301. // }
  302. console.log(item.text, item.status.user.verified, item.status.user.verified_type)
  303. });
  304. if (recvJson.data.comments.length > 1) {
  305. let nextCursor = recvJson.data.next_cursor;
  306. g(nextCursor)
  307. }
  308. })
  309. };
  310. g()
  311.  
  312.  
  313. }
  314.  
  315.  
  316. let initHtml = () => {
  317. var htmlCode = `
  318. <style>
  319. .wb-tool {
  320. width: 300px;
  321. bottom: 20px;
  322. right: 20px;
  323. padding: 10px 10px 8px 10px;
  324. background-color: #eee;
  325. z-index: 9999;
  326. position: fixed;
  327. overflow: hidden;
  328. border-radius: 10px;
  329. transition: all 0.5s;
  330. box-shadow: 0 0 5px 2px rgba(0, 0, 0, .2);
  331. }
  332.  
  333. .title {
  334. font-size: 22px;
  335. font-weight: 600;
  336. text-align: center;
  337. margin-top: 5px;
  338. }
  339.  
  340. .note {
  341. font-size: 14px;
  342. font-weight: 600;
  343. color: #EE0000;
  344. }
  345.  
  346. .info {
  347. font-size: 14px;
  348. font-weight: 600;
  349. color: #000;
  350. margin: 3px 0;
  351. }
  352.  
  353. .btn {
  354. border: none;
  355. background-color: #fff;
  356. width: 100%;
  357. margin-top: 8px;
  358. padding: 8px;
  359. border-radius: 4px;
  360. transition: all 0.2s;
  361. display: inline-block;
  362. cursor: pointer;
  363. }
  364.  
  365. .btn:hover {
  366. box-shadow: 0 0 5px 2px rgba(0, 0, 255, .2);
  367. }
  368.  
  369. .btn-sm {
  370. width: 80%;
  371. margin: 0 10%;
  372. background-color: #f1f1f1;
  373. }
  374.  
  375. .card {
  376. width: 100%;
  377. /*min-height: 100px;*/
  378. background-color: #fff;
  379. border-radius: 10px;
  380. margin-top: 8px;
  381. overflow: hidden;
  382. transition: all 0.2s;
  383. }
  384.  
  385. .card-title {
  386. font-size: 14px;
  387. font-weight: 600;
  388. color: #222;
  389. margin: 5px;
  390. }
  391.  
  392. .card-info {
  393. margin: 5px;
  394. font-size: 14px;
  395. color: #000;
  396. }
  397.  
  398. .error {
  399. width: calc(100% - 10px);
  400. height: 92px;
  401. border-radius: 8px;
  402. color: red;
  403. background-color: antiquewhite;
  404. overflow: auto;
  405. }
  406.  
  407.  
  408. </style>
  409. <div class="w wb-tool">
  410. <div class="w title">微博工具</div>
  411. <p class="w note">
  412. 注意事项:
  413. <br>1. 请谨慎使用,部分操作无法恢复!
  414. <br>2. "仅自己可见"无法隐藏"快转"的内容!
  415. </p>
  416. <div class="w info" id="info-token">Token状态:{{userInfo.X_XSRF_TOKEN?'获取成功':'获取失败'}}</div>
  417. <div class="w info" id="info-status">状态:{{userInfo.status?'Success':'Error'}}</div>
  418. <div class="w info" id="info-name">昵称:{{userInfo.name}}</div>
  419. <div class="w info" id="info-count">总微博数:{{userInfo.count}}</div>
  420. <button class="w btn" id="showCardModifyVisibleWb" w-click="onCardClick">微博可见范围修改</button>
  421. <button class="w btn" id="showCardDeletePublicCmt" w-click="onCardClick">微博公开评论删除</button>
  422. <div class="w card" w-ref="cardMVW" w-height="0">
  423. <div class="w card-title">微博可见范围修改</div>
  424. <button class="w btn btn-sm" id="btnGetAllWb" w-verify w-click="getAllWbID">获取全部微博信息</button>
  425. <div class="w card-info">进度:{{userInfo.processGet}} / {{userInfo.count}}</div>
  426. <div class="w card-info" id="visibleInfo">
  427. 公开:{{userInfo.wbVisibleInfo[0]}},粉丝:{{userInfo.wbVisibleInfo[10]}},
  428. 朋友:{{userInfo.wbVisibleInfo[2]}},仅自己:{{userInfo.wbVisibleInfo[1]}}
  429. </div>
  430.  
  431. <div class="w card-info" w-group="radio" w-click="(value) => userInfo.modifyVisibleType = w.variable(value)">
  432. <input name="radio" type="radio" id="public" class="w radio" value="0"/>
  433. <label for="public">公开</label>
  434.  
  435. <input name="radio" type="radio" id="fans" class="w radio" value="10"/>
  436. <label for="fans">粉丝</label>
  437.  
  438. <input name="radio" type="radio" id="friends" class="w radio" value="2"/>
  439. <label for="friends">朋友</label>
  440.  
  441. <input name="radio" type="radio" id="me" class="w radio" value="1"/>
  442. <label for="me">仅自己</label>
  443. </div>
  444. <button class="w btn btn-sm" id="btnModifyVisible" w-verify w-click="modifyVisible">
  445. 全部转换到{{userInfo.modifyVisibleTextList[userInfo.modifyVisibleType]}}
  446. </button>
  447. <div class="w card-info" id="processModifyVisible">
  448. 进度:{{userInfo.processModifyVisible}} / {{userInfo.count}},
  449. 跳过:{{userInfo.processSkip}} / {{userInfo.count}}
  450. </div>
  451. <div class="w card-info error" id="error">{{userInfo.error}}</div>
  452. </div>
  453. <div class="w card" w-ref="cardDPC" w-height="0">
  454. <div class="w card-title">微博公开评论删除</div>
  455. <button class="w btn btn-sm" id="btnGetAllCmt" w-click="getMyCmt">获取公开评论信息</button>
  456. <div class="w card-info">总共发出评论:{{userInfo.cmtTotal}}</div>
  457. <div class="w card-info">已经获取评论:{{userInfo.cmtGet}}</div>
  458. <div class="w card-info">公开评论数:{{userInfo.cmtPublicTotal}}</div>
  459.  
  460. </div>
  461. </div>
  462. `;
  463. let insertElement = document.createElement("div");
  464. insertElement.innerHTML = htmlCode;
  465. document.body.append(insertElement);
  466. }
  467.  
  468. let onCardClick = (id) => {
  469. console.log(id)
  470. if (id === 'showCardModifyVisibleWb') {
  471. w.ref.cardMVW.style.height = (f1) ? '300px' : '0px'
  472. f1 = !f1
  473. }
  474. if (id === 'showCardDeletePublicCmt') {
  475. w.ref.cardDPC.style.height = (f2) ? '200px' : '0px';
  476. f2 = !f2
  477. }
  478. }
  479.  
  480. (function() {
  481. 'use strict';
  482. window.onload=function(){
  483. initHtml();
  484. w.init()
  485. getWb();
  486.  
  487.  
  488. // 劫持所有请求获取X__XSRF__TOKEN
  489. function addXMLRequestCallback(callback) {
  490. var oldSend, i;
  491. if (XMLHttpRequest.callbacks) {
  492. XMLHttpRequest.callbacks.push(callback);
  493. } else {
  494. XMLHttpRequest.callbacks = [callback];
  495. oldSend = XMLHttpRequest.prototype.send;
  496. XMLHttpRequest.prototype.send = function () {
  497. for (i = 0; i < XMLHttpRequest.callbacks.length; i++) {
  498. XMLHttpRequest.callbacks[i](this);
  499. }
  500. return oldSend.apply(this, arguments);
  501. };
  502. }
  503. }
  504. XMLHttpRequest.prototype.wrappedSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
  505. XMLHttpRequest.prototype.setRequestHeader = function(header, value) {
  506. this.wrappedSetRequestHeader(header, value);
  507. if(!this.headers) {
  508. this.headers = {};
  509. }
  510. header = header.replaceAll('-', '__')
  511. this.headers[header] = value;
  512. }
  513. let flag = false;
  514. addXMLRequestCallback(function (xhr) {
  515. xhr.addEventListener("load", function () {
  516. if (xhr.readyState == 4 && xhr.status == 200) {
  517. if(!flag && xhr.headers.hasOwnProperty('X__XSRF__TOKEN')) {
  518. userInfo.X_XSRF_TOKEN = w.variable(xhr.headers.X__XSRF__TOKEN)
  519. flag = true;
  520.  
  521. }
  522. }
  523. });
  524. });
  525. }
  526. })();