Bilibili Ban Kit

批量ban掉你讨厌的up主

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Bilibili Ban Kit
// @namespace    https://github.com/linyanm
// @version      2024-10-24--01
// @description  批量ban掉你讨厌的up主
// @author       Noir
// @match        *://www.bilibili.com/
// @match        *://www.bilibili.com/?*
// @match        *://www.bilibili.com/index.html
// @match        *://www.bilibili.com/index.html?*
// @match        *://www.bilibili.com/video/*
// @match        *://www.bilibili.com/list/watchlater?*
// @match        *://www.bilibili.com/bangumi/play/*
// @match        *://space.bilibili.com/*
// @icon         https://i0.hdslb.com/bfs/static/jinkela/long/images/favicon.ico
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @run-at       document-end
// @license      MIT
// ==/UserScript==

(function() {
	'use strict';



	const CACHE = {
			陈三岁恐怖游戏: 3537112840275980,
			吧主甲: 434615,
			晚晚恶霸和小跟班: 253212392,
			电影迷小雅: 395991094,
			真探唐仁杰: 544336675,
			润州镜: 2229752,
			龙崎棒棒糖: 4564056,
			英大吉: 638816489,
			steam情报局: 473519710,
			勾手老大爷邓肯: 24490535,
			红警HBK08: 1629347259,
			小片片说大片: 10119428,
			正说清代十二朝: 1457639978,
			井号5467: 4305299,
			培根悖论唠唠嗑: 386869863,
			南国小豆豆: 10518076,
			胶片看电影: 46545652,
			机智帅气的一炫君: 309134148,
			不搞博士Dcy: 442229,
			黝黑蜗壳天眼老师: 14068111,
			光影笔墨: 269319344,
			迷影至下Filmlast: 942755,
			河畔的伯爵: 1596926736,
			老野心家: 2043926679,
			一碗龙虾汤: 98684196,
			渤海小吏: 504934876,
			风都獭书馆: 145544,
			The梁某人: 1405515989,
			江湖弃子: 1117551831,
			墨染玄黎: 406999290,
			大聪看电影: 253350665,
			抽风crazy: 2728123,
			悬疑MOVIE: 452600545,
			西葛西: 2035005110,
			文西与阿漆: 94281836,
			硬核的HeyMatt: 239688446,
			发光的三极管yoyo: 1135981288,
			南条鸡腿子: 2574869,
			老大南: 483052036,
			老坚果然翁: 284845773,
			影Man未知档案: 13743667,
			AYCS2: 203680252,
			地上足球888: 550674844,
			瓦岗寨主坑爹李: 3948019,
	};
	const NAMES = Object.keys(CACHE).join(',');


	const cache = GM_getValue('cache', CACHE);
	const block = GM_getValue('block', {});

	let blacklist = GM_getValue('blacklist', []);

	GM_registerMenuCommand('配置黑名单', showConfigDialog);

	let container = null;

	function showConfigDialog() {
			if (container) {
					container.remove();
			}
			container = document.createElement('div');
			container.innerHTML = `
					<div style="position: fixed; top: 10%; left: 50%; transform: translateX(-50%);
											background: #fff; border-radius: 10px; box-shadow: 0 0 15px rgba(0,0,0,0.3);
											padding: 20px; z-index: 10000; width: 400px; font-family: Arial;">
							<h3 style="text-align: center; margin-bottom: 10px;">配置拉黑名单</h3>
							<textarea id="batchInput" style="width: 100%; height: 50px; border: 1px solid #ddd;
									border-radius: 5px; padding: 5px;" placeholder="批量输入昵称,用逗号分隔"></textarea>
							<button id="batchAdd" style="margin-top: 10px; width: 100%; padding: 10px;
									background-color: #00a1d6; color: white; border: none; border-radius: 5px;">批量添加</button>
							<button id="initialize" style="margin-top: 10px; width: 100%; padding: 10px;
									background-color: #00a1d6; color: white; border: none; border-radius: 5px;">一键添加畜生名单</button>
							<ul id="blacklist" style="list-style: none; padding: 0; margin-top: 15px; max-height: 200px;
									overflow-y: auto; border: 1px solid #ddd; border-radius: 5px;">
									${blacklist.map((name, i) => `
											<li style="display: flex; justify-content: space-between; align-items: center; padding: 5px;
													border-bottom: 1px solid #f0f0f0;">
													<span id="user-${i}" class="user-name" style="flex: 1;">${name}</span>
													<button data-index="${i}" class="remove-btn" style="color: red; border: none;
															background: none;">删除</button>
											</li>
									`).join('')}
							</ul>
							<!-- <button id="execute" style="margin-top: 10px; width: 100%; padding: 10px; background-color: #00a1d6;
									color: white; border: none; border-radius: 5px;">正义执行</button> -->
							<button id="close" style="margin-top: 5px; width: 100%; padding: 10px; background-color: #ccc;
									color: black; border: none; border-radius: 5px;">关闭</button>
					</div>
			`;
			document.body.appendChild(container);

			// container.querySelector('#execute').addEventListener('click', main);
			container.querySelector('#close').addEventListener('click', () => container.remove());
			container.querySelector('#batchAdd').addEventListener('click', addBatchNames);
			container.querySelector('#initialize').addEventListener('click', initialize);

			container.querySelectorAll('.remove-btn').forEach(btn => {
					btn.addEventListener('click', (e) => {
							const index = e.target.dataset.index;
							const name = blacklist[index];
							delete block[name];
							GM_setValue('block', block);
							blacklist.splice(index, 1);
							showConfigDialog(); // 重新渲染
							saveBlacklist();
					});
			});

			// 检查每个用户的拉黑状态
			blacklist.forEach((name, i) => {
					fetchUID(name).then(uid => {
							if (uid) {
									isBlocked(uid).then(isBlocked => {
											const userElement = document.getElementById(`user-${i}`);
											userElement.style.color = isBlocked ? 'green' : 'red';
											userElement.textContent += isBlocked ? '(已拉黑)' : '(未拉黑)';
									});
							}
					});
			});
	}

	function addBatchNames() {
			const input = document.querySelector('#batchInput').value.trim();
			if (input) {
					const names = input.split(',').map(name => name.trim()).filter(name => name);
					blacklist.push(...names);
					showConfigDialog();
					saveBlacklist();
			}
	}

	function initialize() {
			const names = NAMES.split(',').map(name => name.trim()).filter(name => name);
			blacklist.push(...names);
			showConfigDialog();
			saveBlacklist();
	}

	function saveBlacklist() {
			blacklist = Array.from(new Set(blacklist));
			GM_setValue('blacklist', blacklist);
			main();
	}

	function fetchUID(name) {
			const url = `https://api.bilibili.com/x/web-interface/search/type?search_type=bili_user&keyword=${encodeURIComponent(name)}`;
			return new Promise((resolve, reject) => {
					if (cache[name]) {
							resolve(cache[name]);
							return;
					}
					GM_xmlhttpRequest({
							method: 'GET',
							url: url,
							onload: (response) => {
									try {
											const result = JSON.parse(response.responseText);
											if (result.data && result.data.result.length > 0) {
													const uid = result.data.result[0].mid;
													if (name && uid) {
															cache[name] = uid;
															GM_setValue('cache', cache);
													}
													resolve(uid);
											} else {
													resolve(null);
											}
									} catch (e) {
											reject(e);
									}
							},
							onerror: reject
					});
			});
	}

	function isBlocked(uid) {
			const url = `https://api.bilibili.com/x/relation?fid=${uid}`;
			return new Promise((resolve, reject) => {
					if (block[uid]) {
							resolve(true);
							return;
					}
					GM_xmlhttpRequest({
							method: 'GET',
							url: url,
							onload: (response) => {
									try {
											const result = JSON.parse(response.responseText);
											const blocked = result.data.attribute === 128; // 128 表示已拉黑
											block[uid] = blocked;
											GM_setValue('block', block);
											resolve(blocked)
									} catch (e) {
											reject(e);
									}
							},
							onerror: reject
					});
			});
	}

	async function getUIDsToBlock() {
			const uids = [];
			for (const name of blacklist) {
					const uid = await fetchUID(name);
					if (uid && !(await isBlocked(uid))) {
							uids.push(uid);
					} else {
							// console.log(`用户 ${name} 已经拉黑或未找到,跳过。`);
					}
			}
			return uids;
	}

	function blockUser(uid) {
			const csrfToken = getCsrfToken();
			const extendContent = JSON.stringify({
					entity: "user",
					entity_id: uid,
					fp: `0\u0001900,,1440\u0001Win32\u000120\u00018\u000124\u00011\u0001zh-CN\u00010\u00010,,0,,0\u0001${navigator.userAgent}`
			});

			const formData = new URLSearchParams({
					fid: uid,
					act: "5", // act=5 拉黑
					// act=6 解除拉黑
					re_src: "11",
					gaia_source: "web_main",
					spmid: "333.999.0.0",
					extend_content: extendContent,
					csrf: csrfToken
			}).toString();

			GM_xmlhttpRequest({
					method: 'POST',
					url: 'https://api.bilibili.com/x/relation/modify',
					headers: {
							'Content-Type': 'application/x-www-form-urlencoded',
							'Origin': 'https://www.bilibili.com',
							'Referer': 'https://www.bilibili.com/',
							'Cookie': document.cookie,
					},
					data: formData,
					onload: (response) => {
							if (response.status === 200) {
									console.log(`成功拉黑用户:${uid}`);
							} else {
									console.error(`拉黑失败:${uid}`, response);
							}
					},
					onerror: (error) => console.error(`请求错误:${uid}`, error)
			});
	}

	function getCsrfToken() {
			const match = document.cookie.match(/bili_jct=([a-zA-Z0-9]{32})/);
			return match ? match[1] : '';
	}

	// 主逻辑:过滤后执行批量拉黑
	async function main () {
			const uidsToBlock = await getUIDsToBlock();
			uidsToBlock.forEach((uid, index) => {
					setTimeout(() => {
							blockUser(uid);
							if (container) {
									showConfigDialog();
							}
					}, index * 5000); // 间隔 5 秒避免风控
			});
	}

	main();
})();