pseudo-Benben - tiger0132

qwq

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         pseudo-Benben - tiger0132
// @namespace    https://oj.akioi.ml:8200/
// @version      1.2.7
// @description  qwq
// @author       tiger0132
// @match        https://*.luogu.com.cn/
// @grant        unsafeWindow
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_addStyle
// ==/UserScript==

(function () {
	'use strict';

	const $ = unsafeWindow.$, host = GM_getValue('benben_host', 'https://pbb.akioi.ml');

	const request = ({ url, data }) => new Promise((resolve, reject) => {
		try {
			data.img = 1;
			data._ = +new Date;
			var img = new Image();
			img.setAttribute('crossOrigin', 'anonymous');
			img.onload = function () {
				var canvas = document.createElement('canvas');
				canvas.width = this.width;
				canvas.height = this.height;
				var ctx = canvas.getContext('2d');
				ctx.drawImage(this, 0, 0);
				resolve(JSON.parse(new TextDecoder().decode(ctx.getImageData(0, 0, this.width, 1).data.filter((_, i) => i % 4 !== 3))));
			};
			img.onerror = reject;
			img.src = url + '?' + new URLSearchParams(data);
		} catch (e) {
			reject(e);
		}
	});
	function verifyToken(cur_token, secret) {
		console.log(`token: ${cur_token}`);
		console.log(`secret: ${secret}`);
		var slogan = _feInstance.currentUser.slogan;
		show_prompt('提示', '脚本可能会覆盖您的签名,请妥善备份。\n原签名:' + slogan, () => {
			fetch('https://www.luogu.com.cn/api/user/updateSlogan', {
				'headers': {
					'content-type': 'application/json;charset=UTF-8',
					'x-csrf-token': document.getElementsByName('csrf-token')[0].content
				},
				'body': `{"slogan":"${secret}"}`,
				'method': 'POST',
			}).then(resp => resp.json()).then(resp => {
				request({
					url: host + '/api/auth/verifyToken',
					data: { token: cur_token }
				}).then(res => {
					if (res.status !== 200) {
						console.error(res.data);
						return show_alert('提示', '[Benben\'] 出了一点问题 >_<', res.data);
					}
					GM_setValue('benben_token', token = cur_token);
					fetch('https://www.luogu.com.cn/api/user/updateSlogan', {
						'headers': {
							'content-type': 'application/json;charset=UTF-8',
							'x-csrf-token': document.getElementsByName('csrf-token')[0].content
						},
						'body': `{"slogan":"${slogan}"}`,
						'method': 'POST',
					}).then(res => res.json()).then(res => {
						show_alert('提示', '[Benben\'] 验证成功');
						checkStatus();
					});
				});
			});
		});
	}
	function genNewToken() {
		request({
			url: host + '/api/auth/getToken',
			data: { uid: _feInstance.currentUser.uid }
		}).then(res => {
			if (res.status !== 200) {
				console.error(res.data);
				return alert('[Benben\'] 出了一点问题 >_<');
			}
			verifyToken(res.data.token, res.data.secret);
		});
	}

	var token = GM_getValue('benben_token'), cur_uid;
	var sendMode = 0; // 1 是伪犇,0 是原

	function injectPostFeed() {
		$('#feed-submit').unbind();
		$('#feed-submit').click(function () {
			if (sendMode) {
				this.classList.add('am-disabled');
				var content = document.getElementById('feed-content').value;
				request({
					url: host + `/api/feed/${feedMode.substr(8)}/post`,
					data: { content: content, token: token }
				}).then(resp => {
					if (resp.status !== 200) {
						show_alert('好像哪里有点问题', resp.data);
					} else {
						$("#feed-content").val('');
						switchMode(feedMode);
					}
					this.classList.remove('am-disabled');
				});
			} else {
				$(this).addClass("am-disabled");
				var content = $('#feed-content').val(), e = this;
				$.post("/api/feed/postBenben", { content: content }, function (resp) {
					if (resp.status !== 200) {
						show_alert("好像哪里有点问题", resp.data);
					} else {
						$(e).removeClass("am-disabled");
						$("#feed-content").val('');
						switchMode('watching');
					}
				});
			}
		});
	}

	const selector_html = `<a style="cursor: pointer">伪犇犇</a>`;

	function origLoadFeed() {
		$.get('/feed/' + feedMode + '?page=' + feedPage, function (resp) {
			console.log('[pLIE v2] loadFeed()');
			var l = $(resp), res;
			for (var i = 0; i < l.length; i++) { // 在获取犇犇的时候就直接过滤
				(function (node) {
					if (node.tagName == 'LI') {
						var uid = node.querySelector('div.lg-left > a').href.match(/\/user\/(\d+)/)[1];
						if (ignQuery(uid)) {
							console.log(`[pLIE v2] ignored a feed from uid=${uid}`); // debug
							return;
						}
						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>`);
						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>`);
						ignAddButton.click(() => { ignAdd(uid); });
						ignDelButton.click(() => { ignDel(uid); });
						$('div.am-comment-main > header > div', node).append(ignAddButton).append('&nbsp;').append(ignDelButton);
						$feed.append(node);
					} else
						$feed.append(node);
				})(l[i]);
			}

			$('#feed-more').children('a').text('点击查看更多...')
			$('[name=feed-delete]').click(function () {
				$.post('/api/feed/delete/' + $(this).attr('data-feed-id'), () => {
					switchMode(feedMode);
				})
			})
			$('[name=feed-reply]').click(function () {
				var content = $(this).parents('li.feed-li').find('.feed-comment').text();
				$('#feed-content').val(' || @' + $(this).attr('data-username') + ' : ' + content);
			})
			$('[name=feed-report]').click(function () {
				var reportType = $(this).attr('data-report-type'), reportID = $(this).attr('data-report-id');
				$('#report').modal({
					relatedTarget: this,
					onConfirm: (e) => {
						var reason = $('[name=reason]').val();
						var detail = $('[name=content]').val();

						$.post('/api/report/' + reportType, {
							relevantID: reportID,
							reason: reason + ' ' + detail
						}, function (data) {
							show_alert('提示', data.data);
						});
					}
				});
			});
		});
		feedPage++;
	}
	unsafeWindow.loadFeed = () => {
		if (unsafeWindow.feedMode.indexOf('pbenben') != -1) p_loadFeed();
		else origLoadFeed();
	}
	var origSwitchMode = unsafeWindow.switchMode;
	unsafeWindow.switchMode = mode => {
		if (mode.indexOf('pbenben') != -1) sendMode = 1;
		else sendMode = 0;
		origSwitchMode(mode);
	}
	function p_loadFeed() {
		request({
			url: host + '/api/feed/' + unsafeWindow.feedMode.substr(8),
			data: {
				page: unsafeWindow.feedPage,
				token: token
			}
		}).then(resp => {
			if (resp.status !== 200) return show_alert('提示', resp.data);

			const rootUsers = [ // 后端会检验的,如果没有权限,加进去也没用
				28762
			];

			var feedMd = [];
			var html = resp.data.reduce((last, i) => {
				var feed_del = '', badge_html = '';
				if (i.user.uid == cur_uid || rootUsers.includes(cur_uid))
					feed_del = `<a name="feed-delete" data-feed-id="${i.id}">删除</a>`;
				if (i.user.badge)
					badge_html = `&nbsp;<span class="am-badge am-radius" style="background-color: ${i.user.color};">${escapeHtml(i.user.badge)}</span>`;
				return last + `
<li class="am-comment am-comment-primary feed-li"}>
<div class="lg-left"><a href="/user/${i.user.uid}" class="center">
<img src="https://cdn.luogu.com.cn/upload/usericon/${i.user.uid}.png" class="am-comment-avatar">
</a></div>
<div class="am-comment-main">
<header class="am-comment-hd">
<div class="am-comment-meta">
<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))}
${feed_del}
<a name="feed-reply" href="javascript: scrollToId('feed-content')" data-username="${i.user.name}">回复</a>
</header>
<div class="am-comment-bd">
<span class="feed-comment">${i.content_html}</span>
<span class="feed-markdown">${encodeURIComponent(i.content_markdown)}</span>
</div>
</div>
</li>`
			}, '');

			$feed.append(html);
			$('#feed-more').children('a').text('点击查看更多...');
			$('[name=feed-delete]').click(function () {
				request({
					url: host + '/api/feed/action/delete',
					data: {
						id: $(this).attr('data-feed-id'),
						token: token
					}
				}).then(resp => {
					if (resp.status != 200) show_alert(resp.data);
					else switchMode(feedMode);
				});
			});
			$('[name=feed-reply]').click(function () {
				var content_markdown = $(this).parents('li.feed-li').find('.feed-markdown').text();
				var content_html = $(this).parents('li.feed-li').find('.feed-comment').text();
				$('#feed-content').val(' || @' + $(this).attr('data-username') + ' :  \n' + (decodeURIComponent(content_markdown) || content_html));
			});
			feedPage++;
		});
	}

	unsafeWindow.getFeedMode = () => {
		if (feedMode.startsWith('pbenben-'))
			return feedMode.substr(8);
		else
			return feedMode;
	};

	unsafeWindow.changeColor = (color, bold) =>
		request({
			url: host + '/api/user/changeColor',
			data: {
				token: token,
				color: color,
				bold: bold
			}
		}).then(data => {
			console.log(data.data);
			show_alert('提示', data.data)
		});
	unsafeWindow.changeTag = tag =>
		request({
			url: host + '/api/user/changeTag',
			data: { token: token, tag: tag }
		}).then(data => {
			console.log(data.data);
			show_alert('提示', data.data)
		});

	unsafeWindow.newRoom = name =>
		request({
			url: host + '/api/room/new',
			data: { token: token, name: name }
		}).then(data => {
			if (data.status === 200)
				show_alert('提示', '创建成功。分区 id:' + data.data.id);
			else
				show_alert('提示', data.data);
		});
	unsafeWindow.addUser = (uid, id) =>
		request({
			url: host + `/api/room/${id || getFeedMode()}/addUser`,
			data: { token: token, uid: uid }
		}).then(data => {
			console.log(data.data);
			show_alert('提示', data.data)
		});
	unsafeWindow.delUser = (uid, id) =>
		request({
			url: host + `/api/room/${id || getFeedMode()}/delUser`,
			data: { token: token, uid: uid }
		}).then(data => {
			console.log(data.data);
			show_alert('提示', data.data)
		});
	unsafeWindow.userList = id =>
		request({
			url: host + `/api/room/${id || getFeedMode()}/info`,
			data: { token: token }
		}).then(data => {
			console.log(data.data.users.map(x => x.name));
			console.log(data.data.users);
		});

	function checkStatus() {
		request({
			url: host + '/api/auth/tokenStatus',
			data: { token: token }
		}).then(resp => {
			console.logresp;
			var statusNode = document.getElementById('pbenben-status');
			var btn = document.getElementById('benben-btn');
			btn.textContent = '';
			statusNode.textContent = '';
			if (resp.status !== 200) {
				statusNode.style.color = '#e74c3c';
				statusNode.textContent = `错误:${resp.data}`;
				btn.textContent = '生成 token';
				btn.style.visibility = 'visible';
				btn.onclick = function () {
					this.classList.add('am-disabled');
					genNewToken();
				};
			} else if (resp.data.verified/* && resp.data.uid == _feInstance.currentUser.uid*/) {
				btn.style.visibility = 'hidden';
				statusNode.style.color = '#5eb95e';
				statusNode.textContent = `正常:uid = ${cur_uid = resp.data.user.uid}`;
			} else {
				btn.style.visibility = 'visible';
				statusNode.style.color = '#e67e22';
				if (!resp.data.verified) {
					statusNode.textContent = 'token 未验证&nbsp;';
					btn.textContent = '验证 token';
					btn.onclick = function () {
						this.classList.add('am-disabled');
						verifyToken(token);
					};
				}
			}
		});
	}
	function addTagChanger() {
		$('#change-tag').click(() => {
			var tag = $('#tag-content')[0].value;
			changeTag(tag);
		});
	}
	async function addVersionChecker() {
		try {
			$('#version-info')[0].textContent = `当前版本:${GM_info.script.version} `;

			var latestScriptVersion = await request({
				url: host + '/api/misc/latestScriptVersion',
				data: {}
			});
			$('#version-info')[0].textContent += `最新版本:${latestScriptVersion.data} `;

			var backendVersion = await request({
				url: host + '/api/misc/backendVersion',
				data: {}
			});
			$('#version-info')[0].textContent += `后端版本:${backendVersion.data}`;
		} catch (e) { console.error(e); }
	}
	async function addFeedModeButton() {
		request({
			url: host + '/api/room/list',
			data: { token: token, mode: 'public' }
		}).then(data => {
			var node = $('#feedmode-selector')[0];
			if (data.status != 200) node.append(data.data);
			else
				for (var i of data.data) {
					node.append($(`
<button class="am-btn am-btn-primary am-btn-sm" onclick="switchMode('pbenben-${i.id}')">${escapeHtml(i.name)}</button>`)[0]);
					node.append('\u00a0');
				}
		});
		var ownedRooms = new Set();
		await request({
			url: host + '/api/room/list',
			data: { token: token, mode: 'my' }
		}).then(data => {
			if (data.status === 200)
				for (var i of data.data)
					ownedRooms.add(JSON.stringify({ name: i.name, id: i.id }));
		});
		request({
			url: host + '/api/room/list',
			data: { token: token, mode: 'joined' }
		}).then(data => {
			var node = $('#joined-selector')[0];
			if (data.status != 200) node.append(data.data);
			else
				for (var i of data.data) {
					node.append($(`
<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]);
					node.append('\u00a0');
				}
		});
	}

	const entityMap = {
		'&': '&amp;',
		'<': '&lt;',
		'>': '&gt;',
		'"': '&quot;',
		"'": '&#39;',
		'/': '&#x2F;',
		'`': '&#x60;',
		'=': '&#x3D;'
	};
	function escapeHtml(string) {
		return String(string).replace(/[&<>"'`=\/]/g, s => entityMap[s]);
	}
	function fmtDate(dt) {
		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')}`;
	}

	const modal_html = `
<div class="am-modal am-modal-prompt" tabindex="-1" id="pbenben-prompt">
<div class="am-modal-dialog">
<div class="am-modal-hd" id="pbenben-alert-title"></div>
<div class="am-modal-bd" id="pbenben-alert-message"></div>
<div class="am-modal-footer">
<span class="am-modal-btn" data-am-modal-cancel>取消</span>
<span class="am-modal-btn" data-am-modal-confirm>确定</span>
</div>
</div>
</div>`;
	unsafeWindow.show_prompt = function show_prompt(title, message, callback) {
		$('#pbenben-alert-title').html(title);
		$('#pbenben-alert-message').html(message);
		$('#pbenben-prompt').modal({
			onConfirm: callback
		});
	}
	function addConfirmModal() {
		document.body.append($(modal_html)[0]);
	}

	const status_html = `
<h2>伪犇犇</h2>
<span id="version-info">版本信息:N/A</span>&nbsp;<a href="https://greasyfork.org/zh-CN/scripts/402550-pseudo-benben-tiger0132">项目链接</a>&nbsp;
<a href="/paste/b22sy2nu">FAQ</a>&nbsp;
<a href="https://pbb.akioi.ml/">独立站</a><br>
<span>状态:<span id="pbenben-status">N/A</span>&nbsp;
<button class="am-btn am-btn-primary am-btn-sm" id="benben-btn" style="visibility: hidden !important"></button></span><br>
<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>
<button class="am-btn am-btn-danger am-btn-sm" id="change-tag">修改</button><br>
<div id="feedmode-selector">
<button class="am-btn am-btn-primary am-btn-sm" onclick="switchMode('watching')">我关注的</button>
<!--<button class="am-btn am-btn-primary am-btn-sm" onclick="switchMode('all')">全网动态</button>-->
<button class="am-btn am-btn-primary am-btn-sm" onclick="switchMode('my')">我发布的</button>
</div>
<div id="joined-selector">
</div>
<small>
提示:第一次使用请按「生成 token」<br>
注:如果选中的是「我关注的」、「全网动态」(和「我发布的」),那么发送的内容就在原犇犇,反之亦然<br>
以及,tag 长度最多 10 个字,特殊字符会被过滤</small>
`;
	function init() {
		if (!_feInstance.currentUser) {
			console.error('[Benben\'] Not logined!');
			return;
		}

		var node = document.createElement('div');
		node.className = 'lg-article';
		node.id = 'benben-status';
		node.innerHTML = status_html;

		document.querySelector('div.lg-index-benben > div:nth-child(2)').insertAdjacentElement('afterend', node);

		GM_addStyle(`.feed-comment p {margin-bottom: 0;} .feed-markdown {display: none;}`);

		checkStatus();
		injectPostFeed();
		// addColorSelector(); disabled
		// addFeedModeSelector(); deprecated
		addTagChanger();
		addVersionChecker();
		addFeedModeButton();
		addConfirmModal();
	}
	init();

	unsafeWindow.getToken = () => GM_getValue('benben_token');
	unsafeWindow.setToken = data => {
		GM_setValue('benben_token', token = data);
		checkStatus();
	};
	unsafeWindow.rmToken = () => {
		GM_deleteValue('benben_token');
		token = undefined;
		checkStatus();
	};
	unsafeWindow.setHost = data => GM_setValue('benben_host', data);
	unsafeWindow.getHost = () => GM_getValue('benben_host');
	unsafeWindow.rmHost = () => GM_deleteValue('benben_host');

	// 屏蔽器相关
	var ignoreList = GM_getValue('LuoguIgnoreList_v2', {});

	function getUid(name) { // 根据用户名反查 uid
		return new Promise((resolve, reject) => {
			$.get('/api/user/search?keyword=' + name, function (resp) {
				resolve(resp.users[0].uid);
			});
		});
	}

	function ignAdd(uid) {
		ignoreList[uid] = true;
		console.log(`[pLIE v2] added ${uid}`);
		GM_setValue('LuoguIgnoreList_v2', ignoreList);
	}
	function ignSet(data) {
		ignoreList = data;
		console.log(`[pLIE v2] success`);
		GM_setValue('LuoguIgnoreList_v2', ignoreList);
	}
	function ignDel(uid) {
		delete ignoreList[uid];
		console.log(`[pLIE v2] deleted ${uid}`);
		GM_setValue('LuoguIgnoreList_v2', ignoreList);
	}
	function ignQuery(uid) {
		return ignoreList[uid] || false;
	}
	function ignTog(uid) {
		console.log(`[pLIE v2] toggled ${uid}`);
		ignQuery(uid) ? ignDel(uid) : ignAdd(uid);
	}
	function ignShow() {
		return ignoreList;
	}
	function ignClear() {
		ignoreList = {};
		GM_setValue('LuoguIgnoreList_v2', ignoreList);
	}
	function id2uid_pack(callback) {
		return function (uid) {
			getUid(uid).then((resp) => console.log(callback(resp)));
		};
	}

	unsafeWindow.ignAdd = id2uid_pack(ignAdd);
	unsafeWindow.ignDel = id2uid_pack(ignDel);
	unsafeWindow.ignSet = id2uid_pack(ignSet);
	unsafeWindow.ignQuery = id2uid_pack(ignQuery);
	unsafeWindow.ignTog = id2uid_pack(ignTog);
	unsafeWindow.ignShow = ignShow;
	unsafeWindow.ignClear = ignClear;
})();