Greasy Fork

Greasy Fork is available in English.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         B站直播间添加个人主页连结到用户名
// @namespace    http://tampermonkey.net/
// @version      0.0.4
// @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 = true // 是否启用悬浮卡片

    const map = new Map()
    const infoMap = new Map()

    const config = { attributes: false, childList: true, subtree: true }

    function putToMap(uid, uname){
         if (map.has(uname)) return
         map.set(uname, uid)
    }

    async function fetchInfo(uid, uname){
         if (infoMap.has(uid)) return infoMap.get(uid)
         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`)
         const data = {following, follower, name, level, face}
         infoMap.set(uid, data)
         return data
    }

    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 uid = map.get(uname)
        if (!uid) {
            console.warn(`找不到 ${uname} 用户的 uid`)
            return
        }
        target[0].innerHTML = `<a id="a-${uid}" href="https://space.bilibili.com/${uid}" target="_blank" style="color: inherit;text-decoration: inherit;">${uname}</a>`

        if (hoverEnable){
           $(`#a-${uid}`).on('mouseenter', () => {
            $(document.body).one('click', () => $(`#hover-content-${uid}`).css('display', 'none'))
            // 如果已有卡片
            if ($(`#hover-content-${uid}`).length > 0 ){
              $(`#hover-content-${uid}`).css('display', 'block')
              return
            }
            // 如果没有卡片
            // 悬浮卡片在进入讯息时大部分都没办法来得及获取,所以无法获取很正常
            fetchInfo(uid, uname).then(info => {
               const {following, follower, name, level, face} = info
               const 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>
                `
               $('#chat-control-panel-vm').append(html)
            }).catch(err => {
               console.warn(`索取用户资讯(${uname}: ${uid})时出现错误`)
               console.error(err)
               const 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)
            }).finally(() => {
               //setTimeout(() => $(`#hover-content-${uid}`).remove(), 1000 * 60 * 5) // 五分钟后删除悬浮卡片
               $(`#hover-content-${uid}`).css('display', 'block')
            })

           })

           $(`#a-${uid}`).on('mouseleave', () => {
              $(`#hover-content-${uid}`).css('display', 'none')
           })

        }
    }

    const sc = launchSuperChat()

    const gift = launchSendGift()

    const enter = launchEnterMessage()

    if (hoverEnable){
     $(document.body).append(`
      <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))
}