您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
B站直播间添加个人主页连结到礼物/醒目留言/观众进入讯息的用户名上
当前为
// ==UserScript== // @name B站直播间添加个人主页连结到用户名 // @namespace http://tampermonkey.net/ // @version 0.0.2 // @description B站直播间添加个人主页连结到礼物/醒目留言/观众进入讯息的用户名上 // @author Eric Lam // @include /https?:\/\/live\.bilibili\.com\/(blanc\/)?\d+\??.*/ // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/pako.min.js // @require http://greasyfork.icu/scripts/417560-bliveproxy/code/bliveproxy.js?version=875812 // @grant GM.xmlHttpRequest // ==/UserScript== (async function() { 'use strict'; const hoverEnable = false // 是否启用悬浮卡片 const map = new Map() const config = { attributes: false, childList: true, subtree: true } function putToMap(uid, uname){ if (map.has(uname)) return map.set(uname, { uid }) if (!hoverEnable) return fetchToMap(uid, uname) .then(info => map.set(uname, {uid, info})) .catch(err => { console.warn(`索取用户资讯(${uname}: ${uid})时出现错误`) console.error(err) }) } async function fetchToMap(uid, uname){ const { following, follower } = await webRequest(`https://api.bilibili.com/x/relation/stat?vmid=${uid}&jsonp=jsonp`) const { name, level, face } = await webRequest(`https://api.bilibili.com/x/space/acc/info?mid=${uid}&jsonp=jsonp`) console.debug(`successfully fetched ${uname} info`) return {following, follower, name, level, face} } async function launchEnterMessage(){ unsafeWindow.bliveproxy.addCommandHandler('INTERACT_WORD', ({data}) => { const {uid, uname} = data console.debug(`[from enter] name: ${uname}; uid: ${uid}`) putToMap(uid, uname) }) const observer = function(list, o){ for(const mu of list){ for (const node of mu.addedNodes){ handle(node, 'span.interact-name') } } } // observe for mutation link new MutationObserver(observer).observe($('#brush-prompt')[0], config) console.log('started enter message observing') // } async function launchSendGift(){ unsafeWindow.bliveproxy.addCommandHandler('SEND_GIFT', ({data}) => { const {uid, uname} = data console.debug(`[from gift] name: ${uname}; uid: ${uid}`) putToMap(uid, uname) }) const observer = function(list, o){ for(const mu of list){ for (const node of mu.addedNodes){ handle(node, 'span.username') } } } const observerBubble = function(list, o){ for(const mu of list){ for (const node of mu.addedNodes){ handle(node, 'div.user-name') } } } // observe for mutation link new MutationObserver(observer).observe($('#chat-items')[0], config) new MutationObserver(observer).observe($('#penury-gift-msg')[0], config) while($('.bubble-list').length == 0){ await sleep(500) } new MutationObserver(observerBubble).observe($('.bubble-list')[0], config) console.log('started gift observing') // } async function launchSuperChat(){ // websocket unsafeWindow.bliveproxy.addCommandHandler('SUPER_CHAT_MESSAGE', ({data}) => { const uid = data.uid const name = data.user_info.uname console.debug(`[from superchat] name: ${name}; uid: ${uid}`) putToMap(uid, name) }) // // get current superchat const scList = unsafeWindow.__NEPTUNE_IS_MY_WAIFU__.roomInfoRes.data.super_chat_info.message_list for (const data of scList){ const uid = data.uid const name = data.user_info.uname console.debug(`[from old sc list] name: ${name}; uid: ${uid}`) putToMap(uid, name) } // const observer = function(list, o){ for(const mu of list){ for (const node of mu.addedNodes){ handle(node, '.name') } } } const superChatPanelO = new MutationObserver(observer) const panelExist = () => $('.pay-note-panel').length > 0 let launched = false while (!panelExist()){ await sleep(500) } // observe for mutation link setInterval(() => { if (panelExist()){ if (launched) return superChatPanelO.observe($('.pay-note-panel')[0], config) console.log('started superchat observing') launched = true }else{ if (!launched) return superChatPanelO.disconnect() console.log('stopped superchat observing') launched = false } }, 1000) } function handle(node, element){ const target = $(node).find(element) if (target.length == 0) return const uname = target[0].innerText const data = map.get(uname) if (!data) { console.warn(`找不到 ${uname} 用户的 资讯`) return } const uid = data.uid target[0].innerHTML = `<a href="https://space.bilibili.com/${uid}" target="_blank" style="color: inherit;text-decoration: inherit;" ${hoverEnable ? `onmouseover="onHover(${uid}, true)" onmouseout="onHover(${uid}, false)` : ''}">${uname}</a>` if (hoverEnable){ // 悬浮卡片在进入讯息时大部分都没办法来得及获取,所以无法获取很正常 let html; if (data.info) { const {following, follower, name, level, face} = data.info html = ` <div id="hover-content-${uid}" class="hover"> <img src="${face}" alt="${name}" style="border-radius: 20px;float: right;" height="50" width="50"> <p style="margin: 0px;font-size: 13px">名称: ${name} (Lv${level})</p> <p style="margin: 0px; font-size: 13px">粉丝数: ${follower}</p> <p style="margin: 0px;font-size: 13px">关注数: ${following}</p> </div> ` }else{ console.debug(`无法索取 ${uname} 用户的悬浮卡片`) html = ` <div id="hover-content-${uid}" class="hover"> <p style="margin: 0px;font-size: 13px">无法索取 ${uname}(${uid}) 的卡片资讯</p> </div> ` } $('#chat-control-panel-vm').append(html) //setTimeout(() => $(`#hover-content-${uid}`).remove(), 1000 * 60 * 5) // 五分钟后删除悬浮卡片 } } const sc = launchSuperChat() const gift = launchSendGift() const enter = launchEnterMessage() if (hoverEnable){ $(document.body).append(` <script> function onHover(uid, show){ $('#hover-content-'+uid).css('display',show ? 'block' : 'none') if (show) $(document.body).one('click', () => $('#hover-content-'+uid).remove()) } </script> <style> .hover { position: absolute; z-index: 99; display: none; height: 50px; width: 93%; background-color: gray; padding: 10px; color: white; border-radius: 2px; } </style> `) } await Promise.all([sc, gift, enter]) })(); async function webRequest(url){ const data = await GM.xmlHttpRequest({ method: "GET", url }) const res = JSON.parse(data.response) if (res.code !== 0) throw new Error(res.message) return res.data } async function sleep(ms){ return new Promise((res,) => setInterval(res,ms)) }