您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
融合了动态关键字、关注列表、粉丝牌列表, 并使用反作弊和权重系统与动态稳定的斜P成分查询
// ==UserScript== // @name changing的斜P成分查询器 // @namespace Changing // @version 0.1 // @description 融合了动态关键字、关注列表、粉丝牌列表, 并使用反作弊和权重系统与动态稳定的斜P成分查询 // @author Changing // @match https://www.bilibili.com/video/* // @match https://t.bilibili.com/* // @match https://space.bilibili.com/* // @match https://www.bilibili.com/read/* // @connect bilibili.com // @grant GM_xmlhttpRequest // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js // @license MIT // ==/UserScript== (function() { 'use strict'; console.log('【GD_Slime的终极成分指示器】加载成功...') // 自定义设置 const queryFollowSwitch = true // 是否开启查询关注(开启之后结果更加精准, 但不稳定, 非常容易被b站限制) const dynamicQueryTimes = 3 // 查询用户动态数, 1次是12条动态(多于3次会非常慢!) const antiCheatNum = 3 // 反作弊指数, 即当用户在同一动态中发送超过此数目的 不同成分的关键字 时, 将会被惩罚 const weightThreshold = 3 // 权重阈值, 计算用户权重后如果大于等于该值才会添加tag const niaoPiSwitch = false // 是否开启查询鸟批名人堂, 因为明星鸟批数量繁多, 开启后可能查询会变慢 // 成分, 可自定义 const match = [ { name: '【 原批⭕ 】', color: '#FF0000', keywords: ['#原神#', '刻晴', '丘丘人', '雷电将军', '派蒙', '胡桃', '神里绫华', '达拉丽娜', '钟离'], UIDs: [401742377, 450905062, 472729452, // 赫萝的苹果(原神大up), 莴苣某人(原神大up) 653768, 1773346] // 原神官方, 原神官方客服, 原神官方运营 }, // 添加斜P { name: '【 斜P👟 】', // 别看了,都是斜p color: '#D12533', // 眼睛的颜色 keywords: ['evil', 'cutevil', 'vedal','evil neuro','牛肉','neuro','王八',], // 都是斜p UIDs: [265601447, 5971855,1880487363,402129981,14229747,39353567,75417781] // 铁牛奶,西街,21,阿鉴,人类,十六夜,788 }, { name: '【 农批👨🌾 】', color: '#FF0000', keywords: ['#王者荣耀#', '王者', '元歌', '李信', '宫本武藏', '百里守约', '马可波罗', '娜可露露'], UIDs: [392836434, 57863910, // 哔哩哔哩王者荣耀赛事, 王者荣耀官方, 13221028, 108569350] //迷茫小树叶, 梦泪, }, { name: '【 A畜🅰️ 】', color: '#FF0000', keywords: ["想到晚的瞬间","晚晚","嘉晚饭","乃贝","贝极星空间站","乃琳夸夸群","顶碗人", "皇珈骑士","贝极星","乃宝","嘉心糖的手账本","嘉心糖","拉姐","然然","asoul", "A-SOUL","水母","来点然能量","奶淇琳","珈乐","贝拉拉的717片星空", "嘉然我想对你说", "嘉然今天吃什么", "向晚大魔王", "贝拉Kira", "乃琳Queen", "珈乐Carol"], UIDs: [703007996,672342685,672328094,672353429,672346917,351609538] }, { name: '【 鸟批🐤 】', color: '#FFD700', keywords: ['文静', '千鸟Official', '明前奶绿', '奶绿', '艾白', '一只修白勾', '修白勾', '艾瑞思', '思思', '凜凜蝶凜', '琳_千鸟Official', '王木木', 'CoCo_千鸟Official'], UIDs: [667526012, 334537711, 1090010845, 1620923329, 1891728206, 553771121, //文静, 艾白, 思思, 木木, Co宝 2132180406, 1960682407, 1220317431] //奶绿, 白勾, 大蝶 }, { name: '【 三畜🦶 】', color: '#009900', keywords: ["小狗说","玉桂幺幺340","三宝","3宝","巢友","巢畜","4畜","小狗生病","啵啵小狗341"], UIDs: [33605910] // 3姐本人 }, { name: '【 罕见🎌 】', color: '#FF0000', keywords: ["東雪蓮Official","东雪莲","莲宝"], UIDs: [1437582453] // 罕见本人 }, { name: '【 瞳畜🌟 】', color: '#FF0000', keywords: ["小星星","瞳宝","瞳子","瞳瞳","瞳星结","星瞳"], UIDs: [401315430, 2122506217] // 瞳子本人, 瞳子工具人 }, { name: '【 杰尼🐢 】', color: '#FF0000', keywords: ["脆鲨","娜娜米","海子姐"], UIDs: [434334701] // 海子姐本人 }, { name: '【 E畜🐛 】', color: '#FF0000', keywords: ["虞莫","柚恩","露早","莞儿","米诺"], UIDs: [2018113152, 1811071010, 1795147802, 1669777785, 1875044092, 1778026586] //eoe官方, 剩下和keywords对应 } ] const matchLength = match.length if(niaoPiSwitch) { let niaoPiHallOfFame = [7477307, 10797522, 1190365997, 758140, 5336308, 19268544, 6715117, 297285769, 56794789, 8834998, 1480514, 50025593, 37141, 29755625, 370160494, 213195775] // -~=$ 【鸟批名人堂】 $=~- // GD_Slime, snawm, 文静大总管, 亮猪, 可达鸭, 张三, 懒羊羊, 模仿者, 纱雾里看花, 前列腺勇士, 乌桃茶, 心烧, 御坂io, 9191, 白帝圣剑甘道夫, 萌白 for (let e of match) { if(e.name == '【 鸟批🐤 】') { e.UIDs.concat(niaoPiHallOfFame) } } } //三种方式的api const biliDynamicAPI = 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid=' // 找到了个更好的api // https://account.bilibili.com/api/member/getCardByMid?mid= const biliFollowAPI = 'https://account.bilibili.com/api/member/getCardByMid?mid=' // 老api 多次调用会被code 412限制 // const biliFollowAPI = 'https://api.bilibili.com/x/relation/followings?ps=50&pn=' const biliMedalAPI = 'https://api.live.bilibili.com/xlive/web-ucenter/user/MedalWall?target_id=' // 用户代理 const userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36' // 缓存已经检查过的用户UID和其对应的tag const checkedUID = [] const checkedTag = [] // 监听用户名字出现 waitForKeyElements(".user .name", keyToMain); waitForKeyElements(".user-name", keyToMain); waitForKeyElements(".sub-user-name", keyToMain); waitForKeyElements("#h-name", keyToMain); // 一个中间层, 用于连通查找元素函数和主函数, 防止async主函数返回promise类型造成重复查找元素 function keyToMain(elements) { main(elements) } // 主函数, 为符合条件的用户添加tag async function main(elements) { try { console.log('【GD_Slime的终极成分指示器】主函数开始工作...') let user = elements var UID = getUID(user) if (!checkedUID.includes(UID)) { let Weights = new Array(3).fill(new Array(matchLength).fill(1)) await getDynamicWeights(UID).then((w) => { Weights[0] = w; }).catch((error) => { console.log(`获取动态权重时出错:${error}`); Weights[0] = new Array(matchLength).fill(0); // 使用默认权重 }); if (queryFollowSwitch) { await getFollowWeight(UID).then((w) => { Weights[1] = w; }).catch((error) => { console.log(`获取关注权重时出错:${error}`); Weights[1] = new Array(matchLength).fill(0); // 使用默认权重 }); } await getMedalWeight(UID).then((w) => { Weights[2] = w }) //console.log(`${w1}, ${w2}, ${w3}`) for(let i = 0; i < matchLength; i++) { let totalWeight = 0 totalWeight += Weights[0][i] if(queryFollowSwitch) {totalWeight += Weights[1][i]} totalWeight += Weights[2][i] //console.log(`${Weights[0]} | ${Weights[1]} | ${Weights[2]} | ${totalWeight} | ${UID}`) if (totalWeight >= weightThreshold) { let tag = getTag(i, totalWeight); // 添加 totalWeight 参数 user[0].innerHTML += tag; console.log("用户 %s UID: %d 的Tag: %s 添加成功!", user[0].innerText, UID, match[i].name); checkedUID.push(UID); checkedTag.push(tag); } } } else { let index = checkedUID.indexOf(UID) user[0].innerHTML += checkedTag[index] console.log("用户 %s UID: %d 的Tag: %s 添加成功!", user[0].innerText, UID, match[i].name) } } catch (error) { console.log('用户tag添加失败!') } } // 判断浏览器类型, 0 - edge, 1 - firefox, 2 - chrome const browserType = () => { let agent = navigator.userAgent if (agent.indexOf("Edge") > -1) { return 0 } else if (agent.indexOf("Firefox") > -1) { return 1 } else { return 2 } } // 检测是不是新版 const is_new = () => { if (browserType() < 2) { return true } else { return document.getElementsByClassName('item goback').length != 0 } } // 获取指定用户的UID const getUID = (user) => { if (is_new) { return user[0].dataset['usercardMid'] || user[0].dataset['userId'] } else { return user.children[0]['href'].replace(/[^\d]/g, "") } } // 拼接tag const getTag = (i, totalWeight) => { return "<b style='color: " + match[i].color + "'>" + match[i].name + " Lv." + totalWeight + "</b>"; } // 请求 // 重试机制的封装函数 async function retryRequest(targetURL, maxAttempts) { for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { let response = await request(targetURL); if (response.status === 200) { return response; } console.log(`请求失败,正在尝试第 ${attempt} 次重试...`); } catch (error) { console.log(`请求过程中出现错误:${error}`); if (attempt === maxAttempts) throw error; } } } // 修改后的请求函数 function request(targetURL) { return new Promise((resolve, reject) => { let requestFunction = GM_xmlhttpRequest ? GM_xmlhttpRequest : GM.xmlHttpRequest; requestFunction({ method: 'GET', url: targetURL, timeout: 10000, // 设置超时时间,例如 10 秒 headers: { 'User-Agent': userAgent }, onload: (res) => { if (res.status === 200 && res.response) { resolve(res); } else { reject(`请求失败,状态码:${res.status}`); } }, onerror: (err) => { reject(`请求发生错误:${err}`); }, ontimeout: () => { reject('请求超时'); } }); }); } //判断给定字符串出现次数 function getStrCount(scrStr, armStr) { var count=0; while(scrStr.indexOf(armStr) != -1 ) { scrStr = scrStr.replace(armStr,"") count++; } return count; } // -=动态部分=- // 修改后的 getDynamicWeights 函数 async function getDynamicWeights(UID) { let Weight = new Array(matchLength).fill(0); try { let offset = 0; for(let count = dynamicQueryTimes; count > 0; count--) { let targetURL = offset === 0 ? biliDynamicAPI + UID : biliDynamicAPI + UID + '&offset=' + offset; let res = await retryRequest(targetURL, 3); // 使用重试机制 if (res.status === 200 && JSON.parse(res.response).code == 0) { let data = JSON.parse(res.response).data; if (!data.has_more) count = 0; offset = data.offset; data.items.forEach(item => { let dyn = JSON.stringify(item); let antiCheat = new Array(matchLength).fill(false); for(let i = 0; i < matchLength; i++) { let count = 0; match[i].keywords.forEach(keyword => { let strCount = getStrCount(dyn, keyword) / 3; if(strCount > 0) { Weight[i] += 1; antiCheat[i] = true; count += strCount; } }); if(count >= 7) { Weight[i] -= 1.5 * (count - 7); } } let cheatIndex = antiCheat.reduce((acc, val, i) => val ? acc.concat(i) : acc, []); if(cheatIndex.length >= antiCheatNum) { cheatIndex.forEach(i => Weight[i] -= 2); } }); } } return Weight; } catch (error) { console.log('获取动态权重失败: ', error); return new Array(matchLength).fill(0); } } // 修改后的 queryFollowSwitch 函数 async function getFollowWeight(UID) { let Weight = new Array(matchLength).fill(0); try { let res = await retryRequest(biliFollowAPI + UID, 3); if (res.status === 200 && JSON.parse(res.response).code == 0) { let lists = JSON.parse(res.response).card.attentions; lists.forEach(uid => { for(let i = 0; i < matchLength; i++) { if(match[i].UIDs.includes(uid)) { Weight[i] += 2; } } }); } return Weight; } catch (error) { console.log('获取关注权重失败: ', error); return new Array(matchLength).fill(0); } } // -=粉丝牌部分=- async function getMedalWeight (UID) { try { let Weight = new Array(matchLength).fill(0) // 查看是否需要使用缺省值 let weightModifiedFlag = false let res = await request(biliMedalAPI + UID) if (res.status == 200) { //console.log('获取UID: %d 粉丝牌成功!', UID) if (JSON.parse(res.response).code == 0) { let data = JSON.parse(res.response).data if (Object.keys(data.list).length != 0) { data.list.forEach(medal => { let upUID = medal.medal_info.target_id let level = medal.medal_info.level for(let i = 0; i < matchLength; i++) { if(match[i].UIDs.includes(upUID)) { if (level >= 1 && level <= 5) { Weight[i] += 2 console.log(`UID: ${UID} 粉丝牌等级: ${level}, +2`) } else if (level >= 6 && level <= 10) { Weight[i] += 4 console.log(`UID: ${UID} 粉丝牌等级: ${level}, +4`) } else if (level >= 11 && level <= 15) { Weight[i] += 6 console.log(`UID: ${UID} 粉丝牌等级: ${level}, +6`) } else if (level >= 16 && level <= 20) { Weight[i] += 8 console.log(`UID: ${UID} 粉丝牌等级: ${level}, +8`) } else if (level >= 21 && level <= 25) { Weight[i] += 100 //铁定是该成分的 console.log(`UID: ${UID} 粉丝牌等级: ${level}, +100`) } weightModifiedFlag = true } } }) } } } if(!weightModifiedFlag) { // 缺省, 每个match里的对象权重为1 for(let c of Weight) {c = 1} } return Weight } catch (error) { console.log('获取UID: %d 粉丝牌失败!', UID) } } /*--- waitForKeyElements(): A utility function, for Greasemonkey scripts, that detects and handles AJAXed content. Usage example: waitForKeyElements ( "div.comments" , commentCallbackFunction ); //--- Page-specific function to do what we want when the node is found. function commentCallbackFunction (jNode) { jNode.text ("This comment changed by waitForKeyElements()."); } IMPORTANT: This function requires your script to have loaded jQuery. */ function waitForKeyElements(selectorTxt, actionFunction, bWaitOnce, iframeSelector) { var targetNodes, btargetsFound if (typeof iframeSelector == "undefined") targetNodes = $(selectorTxt); else targetNodes = $(iframeSelector).contents() .find(selectorTxt); if (targetNodes && targetNodes.length > 0) { btargetsFound = true; targetNodes.each(function () { var jThis = $(this); var alreadyFound = jThis.data('alreadyFound') || false; if (!alreadyFound) { //--- Call the payload function. var cancelFound = actionFunction(jThis); if (cancelFound) btargetsFound = false; else jThis.data('alreadyFound', true); } }); } else { btargetsFound = false; } //--- Get the timer-control variable for this selector. var controlObj = waitForKeyElements.controlObj || {}; var controlKey = selectorTxt.replace(/[^\w]/g, "_"); var timeControl = controlObj[controlKey]; //--- Now set or clear the timer as appropriate. if (btargetsFound && bWaitOnce && timeControl) { //--- The only condition where we need to clear the timer. clearInterval(timeControl); delete controlObj[controlKey] } else { //--- Set a timer, if needed. if (!timeControl) { timeControl = setInterval(function () { waitForKeyElements(selectorTxt, actionFunction, bWaitOnce, iframeSelector); }, 300); controlObj[controlKey] = timeControl; } } waitForKeyElements.controlObj = controlObj; } } )();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址