pseudo-Benben - tiger0132

qwq

  1. // ==UserScript==
  2. // @name pseudo-Benben - tiger0132
  3. // @namespace https://oj.akioi.ml:8200/
  4. // @version 1.2.7
  5. // @description qwq
  6. // @author tiger0132
  7. // @match https://*.luogu.com.cn/
  8. // @grant unsafeWindow
  9. // @grant GM_setValue
  10. // @grant GM_getValue
  11. // @grant GM_deleteValue
  12. // @grant GM_addStyle
  13. // ==/UserScript==
  14.  
  15. (function () {
  16. 'use strict';
  17.  
  18. const $ = unsafeWindow.$, host = GM_getValue('benben_host', 'https://pbb.akioi.ml');
  19.  
  20. const request = ({ url, data }) => new Promise((resolve, reject) => {
  21. try {
  22. data.img = 1;
  23. data._ = +new Date;
  24. var img = new Image();
  25. img.setAttribute('crossOrigin', 'anonymous');
  26. img.onload = function () {
  27. var canvas = document.createElement('canvas');
  28. canvas.width = this.width;
  29. canvas.height = this.height;
  30. var ctx = canvas.getContext('2d');
  31. ctx.drawImage(this, 0, 0);
  32. resolve(JSON.parse(new TextDecoder().decode(ctx.getImageData(0, 0, this.width, 1).data.filter((_, i) => i % 4 !== 3))));
  33. };
  34. img.onerror = reject;
  35. img.src = url + '?' + new URLSearchParams(data);
  36. } catch (e) {
  37. reject(e);
  38. }
  39. });
  40. function verifyToken(cur_token, secret) {
  41. console.log(`token: ${cur_token}`);
  42. console.log(`secret: ${secret}`);
  43. var slogan = _feInstance.currentUser.slogan;
  44. show_prompt('提示', '脚本可能会覆盖您的签名,请妥善备份。\n原签名:' + slogan, () => {
  45. fetch('https://www.luogu.com.cn/api/user/updateSlogan', {
  46. 'headers': {
  47. 'content-type': 'application/json;charset=UTF-8',
  48. 'x-csrf-token': document.getElementsByName('csrf-token')[0].content
  49. },
  50. 'body': `{"slogan":"${secret}"}`,
  51. 'method': 'POST',
  52. }).then(resp => resp.json()).then(resp => {
  53. request({
  54. url: host + '/api/auth/verifyToken',
  55. data: { token: cur_token }
  56. }).then(res => {
  57. if (res.status !== 200) {
  58. console.error(res.data);
  59. return show_alert('提示', '[Benben\'] 出了一点问题 >_<', res.data);
  60. }
  61. GM_setValue('benben_token', token = cur_token);
  62. fetch('https://www.luogu.com.cn/api/user/updateSlogan', {
  63. 'headers': {
  64. 'content-type': 'application/json;charset=UTF-8',
  65. 'x-csrf-token': document.getElementsByName('csrf-token')[0].content
  66. },
  67. 'body': `{"slogan":"${slogan}"}`,
  68. 'method': 'POST',
  69. }).then(res => res.json()).then(res => {
  70. show_alert('提示', '[Benben\'] 验证成功');
  71. checkStatus();
  72. });
  73. });
  74. });
  75. });
  76. }
  77. function genNewToken() {
  78. request({
  79. url: host + '/api/auth/getToken',
  80. data: { uid: _feInstance.currentUser.uid }
  81. }).then(res => {
  82. if (res.status !== 200) {
  83. console.error(res.data);
  84. return alert('[Benben\'] 出了一点问题 >_<');
  85. }
  86. verifyToken(res.data.token, res.data.secret);
  87. });
  88. }
  89.  
  90. var token = GM_getValue('benben_token'), cur_uid;
  91. var sendMode = 0; // 1 是伪犇,0 是原
  92.  
  93. function injectPostFeed() {
  94. $('#feed-submit').unbind();
  95. $('#feed-submit').click(function () {
  96. if (sendMode) {
  97. this.classList.add('am-disabled');
  98. var content = document.getElementById('feed-content').value;
  99. request({
  100. url: host + `/api/feed/${feedMode.substr(8)}/post`,
  101. data: { content: content, token: token }
  102. }).then(resp => {
  103. if (resp.status !== 200) {
  104. show_alert('好像哪里有点问题', resp.data);
  105. } else {
  106. $("#feed-content").val('');
  107. switchMode(feedMode);
  108. }
  109. this.classList.remove('am-disabled');
  110. });
  111. } else {
  112. $(this).addClass("am-disabled");
  113. var content = $('#feed-content').val(), e = this;
  114. $.post("/api/feed/postBenben", { content: content }, function (resp) {
  115. if (resp.status !== 200) {
  116. show_alert("好像哪里有点问题", resp.data);
  117. } else {
  118. $(e).removeClass("am-disabled");
  119. $("#feed-content").val('');
  120. switchMode('watching');
  121. }
  122. });
  123. }
  124. });
  125. }
  126.  
  127. const selector_html = `<a style="cursor: pointer">伪犇犇</a>`;
  128.  
  129. function origLoadFeed() {
  130. $.get('/feed/' + feedMode + '?page=' + feedPage, function (resp) {
  131. console.log('[pLIE v2] loadFeed()');
  132. var l = $(resp), res;
  133. for (var i = 0; i < l.length; i++) { // 在获取犇犇的时候就直接过滤
  134. (function (node) {
  135. if (node.tagName == 'LI') {
  136. var uid = node.querySelector('div.lg-left > a').href.match(/\/user\/(\d+)/)[1];
  137. if (ignQuery(uid)) {
  138. console.log(`[pLIE v2] ignored a feed from uid=${uid}`); // debug
  139. return;
  140. }
  141. var ignAddButton = $(`<span class="am-btn am-btn-danger am-btn-sm am-radius am-badge lg-bg-red ign-btn" data-uid="${uid}">屏蔽</span>`);
  142. var ignDelButton = $(`<span class="am-btn am-btn-success am-btn-sm am-radius am-badge lg-bg-green ign-btn" data-uid="${uid}">解除</span>`);
  143. ignAddButton.click(() => { ignAdd(uid); });
  144. ignDelButton.click(() => { ignDel(uid); });
  145. $('div.am-comment-main > header > div', node).append(ignAddButton).append('&nbsp;').append(ignDelButton);
  146. $feed.append(node);
  147. } else
  148. $feed.append(node);
  149. })(l[i]);
  150. }
  151.  
  152. $('#feed-more').children('a').text('点击查看更多...')
  153. $('[name=feed-delete]').click(function () {
  154. $.post('/api/feed/delete/' + $(this).attr('data-feed-id'), () => {
  155. switchMode(feedMode);
  156. })
  157. })
  158. $('[name=feed-reply]').click(function () {
  159. var content = $(this).parents('li.feed-li').find('.feed-comment').text();
  160. $('#feed-content').val(' || @' + $(this).attr('data-username') + ' : ' + content);
  161. })
  162. $('[name=feed-report]').click(function () {
  163. var reportType = $(this).attr('data-report-type'), reportID = $(this).attr('data-report-id');
  164. $('#report').modal({
  165. relatedTarget: this,
  166. onConfirm: (e) => {
  167. var reason = $('[name=reason]').val();
  168. var detail = $('[name=content]').val();
  169.  
  170. $.post('/api/report/' + reportType, {
  171. relevantID: reportID,
  172. reason: reason + ' ' + detail
  173. }, function (data) {
  174. show_alert('提示', data.data);
  175. });
  176. }
  177. });
  178. });
  179. });
  180. feedPage++;
  181. }
  182. unsafeWindow.loadFeed = () => {
  183. if (unsafeWindow.feedMode.indexOf('pbenben') != -1) p_loadFeed();
  184. else origLoadFeed();
  185. }
  186. var origSwitchMode = unsafeWindow.switchMode;
  187. unsafeWindow.switchMode = mode => {
  188. if (mode.indexOf('pbenben') != -1) sendMode = 1;
  189. else sendMode = 0;
  190. origSwitchMode(mode);
  191. }
  192. function p_loadFeed() {
  193. request({
  194. url: host + '/api/feed/' + unsafeWindow.feedMode.substr(8),
  195. data: {
  196. page: unsafeWindow.feedPage,
  197. token: token
  198. }
  199. }).then(resp => {
  200. if (resp.status !== 200) return show_alert('提示', resp.data);
  201.  
  202. const rootUsers = [ // 后端会检验的,如果没有权限,加进去也没用
  203. 28762
  204. ];
  205.  
  206. var feedMd = [];
  207. var html = resp.data.reduce((last, i) => {
  208. var feed_del = '', badge_html = '';
  209. if (i.user.uid == cur_uid || rootUsers.includes(cur_uid))
  210. feed_del = `<a name="feed-delete" data-feed-id="${i.id}">删除</a>`;
  211. if (i.user.badge)
  212. badge_html = `&nbsp;<span class="am-badge am-radius" style="background-color: ${i.user.color};">${escapeHtml(i.user.badge)}</span>`;
  213. return last + `
  214. <li class="am-comment am-comment-primary feed-li"}>
  215. <div class="lg-left"><a href="/user/${i.user.uid}" class="center">
  216. <img src="https://cdn.luogu.com.cn/upload/usericon/${i.user.uid}.png" class="am-comment-avatar">
  217. </a></div>
  218. <div class="am-comment-main">
  219. <header class="am-comment-hd">
  220. <div class="am-comment-meta">
  221. <span class="feed-username"><a class${i.user.bold ? '="lg-bold"' : ''} style="color: ${i.user.color} !important;" href="/user/${i.user.uid}" target="_blank">${i.user.name}</a>${badge_html}</span> ${fmtDate(new Date(i.time * 1000))}
  222. ${feed_del}
  223. <a name="feed-reply" href="javascript: scrollToId('feed-content')" data-username="${i.user.name}">回复</a>
  224. </header>
  225. <div class="am-comment-bd">
  226. <span class="feed-comment">${i.content_html}</span>
  227. <span class="feed-markdown">${encodeURIComponent(i.content_markdown)}</span>
  228. </div>
  229. </div>
  230. </li>`
  231. }, '');
  232.  
  233. $feed.append(html);
  234. $('#feed-more').children('a').text('点击查看更多...');
  235. $('[name=feed-delete]').click(function () {
  236. request({
  237. url: host + '/api/feed/action/delete',
  238. data: {
  239. id: $(this).attr('data-feed-id'),
  240. token: token
  241. }
  242. }).then(resp => {
  243. if (resp.status != 200) show_alert(resp.data);
  244. else switchMode(feedMode);
  245. });
  246. });
  247. $('[name=feed-reply]').click(function () {
  248. var content_markdown = $(this).parents('li.feed-li').find('.feed-markdown').text();
  249. var content_html = $(this).parents('li.feed-li').find('.feed-comment').text();
  250. $('#feed-content').val(' || @' + $(this).attr('data-username') + ' : \n' + (decodeURIComponent(content_markdown) || content_html));
  251. });
  252. feedPage++;
  253. });
  254. }
  255.  
  256. unsafeWindow.getFeedMode = () => {
  257. if (feedMode.startsWith('pbenben-'))
  258. return feedMode.substr(8);
  259. else
  260. return feedMode;
  261. };
  262.  
  263. unsafeWindow.changeColor = (color, bold) =>
  264. request({
  265. url: host + '/api/user/changeColor',
  266. data: {
  267. token: token,
  268. color: color,
  269. bold: bold
  270. }
  271. }).then(data => {
  272. console.log(data.data);
  273. show_alert('提示', data.data)
  274. });
  275. unsafeWindow.changeTag = tag =>
  276. request({
  277. url: host + '/api/user/changeTag',
  278. data: { token: token, tag: tag }
  279. }).then(data => {
  280. console.log(data.data);
  281. show_alert('提示', data.data)
  282. });
  283.  
  284. unsafeWindow.newRoom = name =>
  285. request({
  286. url: host + '/api/room/new',
  287. data: { token: token, name: name }
  288. }).then(data => {
  289. if (data.status === 200)
  290. show_alert('提示', '创建成功。分区 id:' + data.data.id);
  291. else
  292. show_alert('提示', data.data);
  293. });
  294. unsafeWindow.addUser = (uid, id) =>
  295. request({
  296. url: host + `/api/room/${id || getFeedMode()}/addUser`,
  297. data: { token: token, uid: uid }
  298. }).then(data => {
  299. console.log(data.data);
  300. show_alert('提示', data.data)
  301. });
  302. unsafeWindow.delUser = (uid, id) =>
  303. request({
  304. url: host + `/api/room/${id || getFeedMode()}/delUser`,
  305. data: { token: token, uid: uid }
  306. }).then(data => {
  307. console.log(data.data);
  308. show_alert('提示', data.data)
  309. });
  310. unsafeWindow.userList = id =>
  311. request({
  312. url: host + `/api/room/${id || getFeedMode()}/info`,
  313. data: { token: token }
  314. }).then(data => {
  315. console.log(data.data.users.map(x => x.name));
  316. console.log(data.data.users);
  317. });
  318.  
  319. function checkStatus() {
  320. request({
  321. url: host + '/api/auth/tokenStatus',
  322. data: { token: token }
  323. }).then(resp => {
  324. console.logresp;
  325. var statusNode = document.getElementById('pbenben-status');
  326. var btn = document.getElementById('benben-btn');
  327. btn.textContent = '';
  328. statusNode.textContent = '';
  329. if (resp.status !== 200) {
  330. statusNode.style.color = '#e74c3c';
  331. statusNode.textContent = `错误:${resp.data}`;
  332. btn.textContent = '生成 token';
  333. btn.style.visibility = 'visible';
  334. btn.onclick = function () {
  335. this.classList.add('am-disabled');
  336. genNewToken();
  337. };
  338. } else if (resp.data.verified/* && resp.data.uid == _feInstance.currentUser.uid*/) {
  339. btn.style.visibility = 'hidden';
  340. statusNode.style.color = '#5eb95e';
  341. statusNode.textContent = `正常:uid = ${cur_uid = resp.data.user.uid}`;
  342. } else {
  343. btn.style.visibility = 'visible';
  344. statusNode.style.color = '#e67e22';
  345. if (!resp.data.verified) {
  346. statusNode.textContent = 'token 未验证&nbsp;';
  347. btn.textContent = '验证 token';
  348. btn.onclick = function () {
  349. this.classList.add('am-disabled');
  350. verifyToken(token);
  351. };
  352. }
  353. }
  354. });
  355. }
  356. function addTagChanger() {
  357. $('#change-tag').click(() => {
  358. var tag = $('#tag-content')[0].value;
  359. changeTag(tag);
  360. });
  361. }
  362. async function addVersionChecker() {
  363. try {
  364. $('#version-info')[0].textContent = `当前版本:${GM_info.script.version} `;
  365.  
  366. var latestScriptVersion = await request({
  367. url: host + '/api/misc/latestScriptVersion',
  368. data: {}
  369. });
  370. $('#version-info')[0].textContent += `最新版本:${latestScriptVersion.data} `;
  371.  
  372. var backendVersion = await request({
  373. url: host + '/api/misc/backendVersion',
  374. data: {}
  375. });
  376. $('#version-info')[0].textContent += `后端版本:${backendVersion.data}`;
  377. } catch (e) { console.error(e); }
  378. }
  379. async function addFeedModeButton() {
  380. request({
  381. url: host + '/api/room/list',
  382. data: { token: token, mode: 'public' }
  383. }).then(data => {
  384. var node = $('#feedmode-selector')[0];
  385. if (data.status != 200) node.append(data.data);
  386. else
  387. for (var i of data.data) {
  388. node.append($(`
  389. <button class="am-btn am-btn-primary am-btn-sm" onclick="switchMode('pbenben-${i.id}')">${escapeHtml(i.name)}</button>`)[0]);
  390. node.append('\u00a0');
  391. }
  392. });
  393. var ownedRooms = new Set();
  394. await request({
  395. url: host + '/api/room/list',
  396. data: { token: token, mode: 'my' }
  397. }).then(data => {
  398. if (data.status === 200)
  399. for (var i of data.data)
  400. ownedRooms.add(JSON.stringify({ name: i.name, id: i.id }));
  401. });
  402. request({
  403. url: host + '/api/room/list',
  404. data: { token: token, mode: 'joined' }
  405. }).then(data => {
  406. var node = $('#joined-selector')[0];
  407. if (data.status != 200) node.append(data.data);
  408. else
  409. for (var i of data.data) {
  410. node.append($(`
  411. <button class="am-btn ${ownedRooms.has(JSON.stringify({ name: i.name, id: i.id })) ? 'am-btn-success' : 'am-btn-secondary'} am-btn-sm" onclick="switchMode('pbenben-${i.id}')">${escapeHtml(i.name)}</button>`)[0]);
  412. node.append('\u00a0');
  413. }
  414. });
  415. }
  416.  
  417. const entityMap = {
  418. '&': '&amp;',
  419. '<': '&lt;',
  420. '>': '&gt;',
  421. '"': '&quot;',
  422. "'": '&#39;',
  423. '/': '&#x2F;',
  424. '`': '&#x60;',
  425. '=': '&#x3D;'
  426. };
  427. function escapeHtml(string) {
  428. return String(string).replace(/[&<>"'`=\/]/g, s => entityMap[s]);
  429. }
  430. function fmtDate(dt) {
  431. return `${dt.getFullYear().toString().padStart(4, '0')}-${(dt.getMonth() + 1).toString().padStart(2, '0')}-${dt.getDate().toString().padStart(2, '0')} ${dt.getHours().toString().padStart(2, '0')}:${dt.getMinutes().toString().padStart(2, '0')}:${dt.getSeconds().toString().padStart(2, '0')}`;
  432. }
  433.  
  434. const modal_html = `
  435. <div class="am-modal am-modal-prompt" tabindex="-1" id="pbenben-prompt">
  436. <div class="am-modal-dialog">
  437. <div class="am-modal-hd" id="pbenben-alert-title"></div>
  438. <div class="am-modal-bd" id="pbenben-alert-message"></div>
  439. <div class="am-modal-footer">
  440. <span class="am-modal-btn" data-am-modal-cancel>取消</span>
  441. <span class="am-modal-btn" data-am-modal-confirm>确定</span>
  442. </div>
  443. </div>
  444. </div>`;
  445. unsafeWindow.show_prompt = function show_prompt(title, message, callback) {
  446. $('#pbenben-alert-title').html(title);
  447. $('#pbenben-alert-message').html(message);
  448. $('#pbenben-prompt').modal({
  449. onConfirm: callback
  450. });
  451. }
  452. function addConfirmModal() {
  453. document.body.append($(modal_html)[0]);
  454. }
  455.  
  456. const status_html = `
  457. <h2>伪犇犇</h2>
  458. <span id="version-info">版本信息:N/A</span>&nbsp;<a href="https://gf.qytechs.cn/zh-CN/scripts/402550-pseudo-benben-tiger0132">项目链接</a>&nbsp;
  459. <a href="/paste/b22sy2nu">FAQ</a>&nbsp;
  460. <a href="https://pbb.akioi.ml/">独立站</a><br>
  461. <span>状态:<span id="pbenben-status">N/A</span>&nbsp;
  462. <button class="am-btn am-btn-primary am-btn-sm" id="benben-btn" style="visibility: hidden !important"></button></span><br>
  463. <div class="am-input-group am-input-group-primary am-input-group-sm"><input type="text" class="am-form-field" placeholder="tag 内容" id="tag-content"></div>
  464. <button class="am-btn am-btn-danger am-btn-sm" id="change-tag">修改</button><br>
  465. <div id="feedmode-selector">
  466. <button class="am-btn am-btn-primary am-btn-sm" onclick="switchMode('watching')">我关注的</button>
  467. <!--<button class="am-btn am-btn-primary am-btn-sm" onclick="switchMode('all')">全网动态</button>-->
  468. <button class="am-btn am-btn-primary am-btn-sm" onclick="switchMode('my')">我发布的</button>
  469. </div>
  470. <div id="joined-selector">
  471. </div>
  472. <small>
  473. 提示:第一次使用请按「生成 token」<br>
  474. 注:如果选中的是「我关注的」、「全网动态」(和「我发布的」),那么发送的内容就在原犇犇,反之亦然<br>
  475. 以及,tag 长度最多 10 个字,特殊字符会被过滤</small>
  476. `;
  477. function init() {
  478. if (!_feInstance.currentUser) {
  479. console.error('[Benben\'] Not logined!');
  480. return;
  481. }
  482.  
  483. var node = document.createElement('div');
  484. node.className = 'lg-article';
  485. node.id = 'benben-status';
  486. node.innerHTML = status_html;
  487.  
  488. document.querySelector('div.lg-index-benben > div:nth-child(2)').insertAdjacentElement('afterend', node);
  489.  
  490. GM_addStyle(`.feed-comment p {margin-bottom: 0;} .feed-markdown {display: none;}`);
  491.  
  492. checkStatus();
  493. injectPostFeed();
  494. // addColorSelector(); disabled
  495. // addFeedModeSelector(); deprecated
  496. addTagChanger();
  497. addVersionChecker();
  498. addFeedModeButton();
  499. addConfirmModal();
  500. }
  501. init();
  502.  
  503. unsafeWindow.getToken = () => GM_getValue('benben_token');
  504. unsafeWindow.setToken = data => {
  505. GM_setValue('benben_token', token = data);
  506. checkStatus();
  507. };
  508. unsafeWindow.rmToken = () => {
  509. GM_deleteValue('benben_token');
  510. token = undefined;
  511. checkStatus();
  512. };
  513. unsafeWindow.setHost = data => GM_setValue('benben_host', data);
  514. unsafeWindow.getHost = () => GM_getValue('benben_host');
  515. unsafeWindow.rmHost = () => GM_deleteValue('benben_host');
  516.  
  517. // 屏蔽器相关
  518. var ignoreList = GM_getValue('LuoguIgnoreList_v2', {});
  519.  
  520. function getUid(name) { // 根据用户名反查 uid
  521. return new Promise((resolve, reject) => {
  522. $.get('/api/user/search?keyword=' + name, function (resp) {
  523. resolve(resp.users[0].uid);
  524. });
  525. });
  526. }
  527.  
  528. function ignAdd(uid) {
  529. ignoreList[uid] = true;
  530. console.log(`[pLIE v2] added ${uid}`);
  531. GM_setValue('LuoguIgnoreList_v2', ignoreList);
  532. }
  533. function ignSet(data) {
  534. ignoreList = data;
  535. console.log(`[pLIE v2] success`);
  536. GM_setValue('LuoguIgnoreList_v2', ignoreList);
  537. }
  538. function ignDel(uid) {
  539. delete ignoreList[uid];
  540. console.log(`[pLIE v2] deleted ${uid}`);
  541. GM_setValue('LuoguIgnoreList_v2', ignoreList);
  542. }
  543. function ignQuery(uid) {
  544. return ignoreList[uid] || false;
  545. }
  546. function ignTog(uid) {
  547. console.log(`[pLIE v2] toggled ${uid}`);
  548. ignQuery(uid) ? ignDel(uid) : ignAdd(uid);
  549. }
  550. function ignShow() {
  551. return ignoreList;
  552. }
  553. function ignClear() {
  554. ignoreList = {};
  555. GM_setValue('LuoguIgnoreList_v2', ignoreList);
  556. }
  557. function id2uid_pack(callback) {
  558. return function (uid) {
  559. getUid(uid).then((resp) => console.log(callback(resp)));
  560. };
  561. }
  562.  
  563. unsafeWindow.ignAdd = id2uid_pack(ignAdd);
  564. unsafeWindow.ignDel = id2uid_pack(ignDel);
  565. unsafeWindow.ignSet = id2uid_pack(ignSet);
  566. unsafeWindow.ignQuery = id2uid_pack(ignQuery);
  567. unsafeWindow.ignTog = id2uid_pack(ignTog);
  568. unsafeWindow.ignShow = ignShow;
  569. unsafeWindow.ignClear = ignClear;
  570. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址