Greasy Fork

Greasy Fork is available in English.

B站直播间添加个人主页连结到用户名

B站直播间添加个人主页连结到礼物/醒目留言/观众进入讯息的用户名上

当前为 2021-01-26 提交的版本,查看 最新版本

// ==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))
}