b站直播间房管辅助直播 bilibili 哔哩哔哩

如有任何问题、意见或需求,可以直接在greasyfork反馈

// ==UserScript==
// @name         b站直播间房管辅助直播 bilibili 哔哩哔哩
// @namespace    http://tampermonkey.net/
// @version      1.1.0
// @description  如有任何问题、意见或需求,可以直接在greasyfork反馈
// @author       You
// @match        *://live.bilibili.com/*
// @exclude      *://live.bilibili.com/p/*
// @grant        none
// ==/UserScript==

(function() {

    function arrayRemove (array,element){
        const index = array.indexOf(element)
        if(index > -1){
            return array.splice(index,1)
        }else{
            return array
        }
    }
    function zfill(num, n) {
        return (Array(n).join(0) + num).slice(-n);
    }
    class BasicAction{
        static fontButtonOver(){this.style.color = '#23ade5'}
        static fontButtonOut(){this.style.color = '#333'}

    }
    class FileUtils{
        static saveFile(filename,data){
            var urlObject = window.URL
            var export_blob = new Blob(['\ufeff'+data],{type: 'text/csv,charset=UTF-8'});
            var save_link = document.createElement('a')
            save_link.href = urlObject.createObjectURL(export_blob);
            save_link.download = filename;
            save_link.click()
        }
    }

    class Ajax{
        static post(url,data={},head={},callback=()=>{},credentials=true){
            const xhr = new XMLHttpRequest()
            xhr.open('POST',url)
            let urlData = new Array()
            for(let d in data){urlData.push(d+'='+data[d])}
            const urlParams = urlData.join('&')

            for(let h in head){xhr.setRequestHeader(h,head[h])}
            xhr.withCredentials = credentials
            xhr.send(urlParams)
            xhr.onreadystatechange = function(){
                callback(xhr)
            }
        }
    }
    class Dialog{
        static wholeScreenCount = 0
        static wholeScreen(child,height= '200px',width='200px',top='0px',left='0px'){
            const background = document.createElement('div')
            background.clicked = false
            background.style = 'top:0px;left:0px;z-index:9999;position: fixed; background-color: rgba(0, 0, 0, 0.5); display: flex; flex-flow: column nowrap; justify-content: start; align-items: center; height: 100%; width: 100%;'
            let dialog = child
            if(!child){
                dialog = document.createElement('div')
                dialog.style.backgroundColor = 'white'
                dialog.style.borderRadius = '5px'
                dialog.style.height = height
                dialog.style.width = width
                dialog.style.top = top
                dialog.style.left = left
            }
            dialog.onclick =  (e) =>{
                e.stopPropagation()
            }

            background.onmousedown = (e) =>{
                if(e.target === background){
                    background.clicked = true
                }else{
                    background.clicked = false
                }
            }
            background.onmouseup = (e)=>{
                if(e.target === background && background.clicked){
                    e.target.parentElement.removeChild(e.target)
                    this.wholeScreenCount -= 1
                    if(this.wholeScreenCount == 0){
                        document.body.classList.remove('mana-stopscroll')
                    }
                }
            }
            background.appendChild(dialog)
            document.body.classList.add('mana-stopscroll')
            this.wholeScreenCount += 1
            document.body.appendChild(background)
            return {background,dialog}
        }

    }
    class Config{
        constructor(){
            this.readSettings()
        }
        defaultSettings = {
            hour:'1',
            isShowShortcutBlock:true,
            danmakuWidth:302,
            autoLock:false,
            hiddenChatGift:false,
        }  
        readSettings(){
            this.settings = JSON.parse(localStorage.getItem('mana-settings')) || {}
            for(let setting in this.defaultSettings){
                if(!(setting in this.settings)){
                    this.settings[setting] = this.defaultSettings[setting]
                }
            }
        }
        saveSettings(){
            localStorage.setItem('mana-settings',JSON.stringify(this.settings))
        }

    }
    class Style{
        constructor(id='',_class=''){
            this._createStyle()
        }
        _createStyle(){
            this._style = document.createElement('style')
            this._style.innerText = ''
            this._style.setAttribute('mana-style','')
        }
        add(st){
            this._style.innerText += st
        }
        inject(){
            document.body.appendChild(this._style)
            this._createStyle()
        }
    }
    class AdminConsole{
        constructor(config,chatPanel){
            this.config = config
            this.chatPanel = chatPanel
            this.injectStyle()
            this.initConsole()
            this.initDanmaku()
        }

        injectStyle(){
            const style = new Style()

            style.add('.mana-stopscroll{overflow:hidden;}')
            style.add('.mana-fontbutton { cursor: pointer; margin-left: 3px; color: #333; } .mana-fontbutton:hover{ color: #23ade5; }')
            style.add('.mana-logbox { top: 200px; position: relative; display: flex; flex-flow: column nowrap; justify-content: start; align-items: center; padding: 20px 20px 20px 20px; background-color: white; border-radius: 5px; overflow: auto; width: 300px; min-height:100px;max-height: calc(100% - 400px);font-size: 12px; }')
            style.add('.mana-logline{ border-bottom: 1px solid #e5e5e5; margin: 3px 0px 3px 0px; }')
            style.add('.mana-menu-font{display: inline-block; white-space: nowrap;font-size: 12px;line-height: 19px;margin: 0 0 8px;color: #333; } .mana-menu-font:hover{ color: #23ade5; }')
            style.add('.mana-setting-box { position: relative; top: 200px; background-color: white; min-width: 200px; display: flex; flex-flow: column nowrap; justify-content: start; align-items: flex-start; padding: 20px; border-radius: 5px; }')
            style.add('.mana-setting-line{ vertical-align: middle; margin: 10px 0 10px 0; }')
            style.add('.mana-setting-line > input[type=checkbox]{ display: inline-block; vertical-align: middle; }')
            style.add('.mana-display-none{ display: none; }')
            style.add('.mana-table-data-content .mana-display-none{ display: none; }')
            style.add('.mana-shortcut-block{ margin-right: 3px; }')

            style.add('.mana-table{ position: relative;top: 80px; background-color: white; border-radius: 5px; padding: 20px;min-height: 110px; height: calc(100% - 160px);}')
            style.add('.mana-table-title{ font-size: 30px; padding: 0 0 8px; height: 35px;}')

            style.add('.mana-table-body{height: calc(100% - 43px);}')
            style.add('.mana-table-data-title{height: 63px;}')
            style.add('.mana-table-data{overflow-y: scroll;overflow-x:hidden;max-height: calc(100% - 63px);}')
            style.add('.mana-table-data::-webkit-scrollbar{display: none;}')
            style.add('.mana-table-data-content{position: absolute;}')
            style.add('.mana-table-data-container{position: relative;}')

            style.add('.mana-table-line{ display: flex; flex-flow: row nowrap; justify-content: start; align-items: center; transition: background-color .1s ease;}')
            style.add('.mana-table-data-background .mana-table-line:nth-child(even){ background-color: #fafafa;}')
            style.add('.mana-table-data-background .mana-table-line:nth-child(odd){ background-color: white;}')
            style.add('.mana-table-data-content .mana-table-line:hover{background-color: #f5f7fa;}')

            style.add('.mana-table-td{ width: 178px; padding: 10px; white-space: nowrap;}')
            style.add('.mana-table-line .mana-table-td:nth-last-child(1){width: 270px;}')

            style.add('.mana-table-line .mana-log-table-search-td{padding: 0 12px 0 8px;}')
            style.add('.mana-table-search{ outline: none; border: 1px solid #dcdfe6; border-radius: 4px; height: 20px;}')
            style.add('.mana-table-search:focus{ border-color: #409eff; }')


            style.inject()
        }
        initDanmaku(){
            const danmaku = document.getElementById('aside-area-vm')
            const appBody = document.getElementsByClassName('app-body')[0]
            const player = document.getElementsByClassName('player-ctnr')[0]
            if(danmaku && appBody && player){
                if(this.config.settings.danmakuWidth != this.config.defaultSettings.danmakuWidth){
                    this.setDanmakuWidth(this.config.settings.danmakuWidth)
                }
                return
            }
            requestAnimationFrame(this.initDanmaku)
        }
        initMenu(){
            const injectAdmin = () => {
                const container = document.getElementsByClassName('upper-right-ctnr')[0]
                if(container && container.childNodes){
                    if(container.childNodes[0].nodeName=='#comment'&& container.childNodes[1].nodeName == 'DIV'){
                        const cons = document.createElement('div')
                        cons.style.color = '#999'
                        cons.className = 'right-action-ctnr live-skin-normal-a-text pointer dp-i-block primary btn p-relative mana-mana'
                        cons.innerHTML = '<i class="icon-font icon-set-up v-middle"></i> <span data-v-f650ac18="" class="action-text v-middle dp-i-block">管理</span> <div class="drop-ctnr p-absolute" style="display: none"> <div class="common-popup-wrap arrow-top drop-bubble-ctnr"> <div  class="admin-drop-ctnr"> </div> </div> </div>'
                        container.replaceChild(cons,container.childNodes[0])
                        cons.onmouseover = function(){
                            this.style.color = '#23ade5'
                            this.children[2].style.display = 'block'
                        }
                        cons.onmouseout = function(){
                            cons.style.color = '#999'
                            this.children[2].style.display = 'none'
                        }
                        return
                    }else{
                        if(container.childNodes[0].nodeName=='DIV'){
                            return
                        }
                    }
                }
                requestAnimationFrame(injectAdmin)
            }
            injectAdmin()
        }

        initConsole(){
            this.initMenu()
            const selections = ['房管辅助设置','禁言记录']
            const selectionOnclick = {
                '禁言记录':this.openLog,
                '房管辅助设置':this.openSettingConsole
            }
            const injectMenu = () =>{
                const menu = document.getElementsByClassName('admin-drop-ctnr')[0]
                if(menu){
                    for(let selection of selections){
                        const button = document.createElement('span')
                        button.className = 'drop-menu-item ts-dot-4 mana-menu-font'
                        button.innerText = selection
                        if(selection in selectionOnclick){
                            button.onclick = selectionOnclick[selection]
                        }
                        menu.appendChild(button)

                    }
                    return
                }
                requestAnimationFrame(injectMenu)
            }
            injectMenu()
        }
        setDanmakuWidth = (width)=>{
            const currentWidth =  document.getElementById('aside-area-vm').offsetWidth
            document.getElementById('aside-area-vm').style.width = width + 'px'
            const appBody = document.getElementsByClassName('app-body')[0]
            if(appBody){
                const targetWidth = Number(appBody.offsetWidth) + Number(width) - Number(currentWidth)
                appBody.style.width = targetWidth.toString() + 'px'
                appBody.style.maxWidth = targetWidth.toString() + 'px'
            }
            const player = document.getElementsByClassName('player-ctnr')[0]
            if(player){
                player.style.width = `calc(100% - ${width}px - 12px)`
            }
        }
        openSettingConsole=()=>{
            const menus = [
                ['默认禁言时间','input', this.config.settings.hour],
                ['显示快捷禁言按钮','checkbox',this.config.settings.isShowShortcutBlock],
                ['鼠标悬停锁定弹幕','checkbox',this.config.settings.autoLock],
                ['隐藏礼物弹幕','checkbox',this.config.settings.hiddenChatGift],
                ['弹幕栏宽度','DOM']
            ]
            const onchangeFunction = {
                '默认禁言时间': (e) => {
                    const hour = e.target.value.match(/\d*/)[0] || '1'
                    this.config.settings.hour = hour
                    this.config.saveSettings()
                },
                '显示快捷禁言按钮':(e)=>{
                    const isShowShortcutBlock = e.target.checked
                    this.config.settings.isShowShortcutBlock = isShowShortcutBlock
                    this.config.saveSettings()
                    if(isShowShortcutBlock){
                        const danmakus = document.getElementsByClassName('mana-shortcut-block')
                        for(let danmaku of danmakus){
                            danmaku.classList.remove('mana-display-none')
                        }
                    }else{
                        const danmakus = document.getElementsByClassName('mana-shortcut-block')
                        for(let danmaku of danmakus){
                            danmaku.classList.add('mana-display-none')
                        }
                    }
                },
                '鼠标悬停锁定弹幕':(e)=>{
                    this.config.settings.autoLock = e.target.checked
                    this.config.saveSettings()
                    this.chatPanel.autoLockScrolling()
                },
                '隐藏礼物弹幕':(e)=>{
                    this.config.settings.hiddenChatGift = e.target.checked
                    this.config.saveSettings()
                    this.chatPanel.autoHideChatGift()
                },
                '弹幕栏宽度':(e)=>{
                    const width = e.target.value.match(/\d*/)[0] || this.config.settings.danmakuWidth
                    this.config.settings.danmakuWidth = width
                    this.config.saveSettings()
                    this.setDanmakuWidth(width)
                }
            }

            const DOMFunction = {
                '弹幕栏宽度':(name)=>{
                    const line = document.createElement('div')
                    line.className = 'mana-setting-line'
                    const title = document.createElement('span')
                    title.className = 'mana-setting-title'
                    title.innerText = name+' : '

                    const input = document.createElement('input')
                    input.value = this.config.settings.danmakuWidth
                    input.onchange = onchangeFunction[name]

                    const button = document.createElement('span')
                    button.innerText = '恢复默认'
                    button.className = 'mana-fontbutton'
                    button.onclick = (e) => {
                        e.target.parentElement.children[1].value = this.config.defaultSettings.danmakuWidth
                        e.target.parentElement.children[1].onchange({target:e.target.parentElement.children[1]})
                    }

                    line.appendChild(title)
                    line.appendChild(input)
                    line.appendChild(button)

                    return line
                }
            }

            let box = document.createElement('div')
            box.className = 'mana-setting-box'
            for(let menu of  menus){
                if(menu[1] === 'DOM'){
                    box.append(DOMFunction[menu[0]](menu[0]))
                    continue
                }
                const line = document.createElement('div')
                line.className = 'mana-setting-line'
                const title = document.createElement('span')
                title.className = 'mana-setting-title'
                title.innerText = menu[0]+' : '
                let value = ''
                switch(menu[1]){
                    case 'input':
                        value = document.createElement('input')
                        if(menu.length >= 3){
                            value.value = menu[2]
                        }
                        value.className = 'mana-setting-input'
                        if(menu[0] in onchangeFunction){
                            value.onchange = onchangeFunction[menu[0]]
                        }
                        break
                    case 'checkbox':
                        value = document.createElement('input')
                        value.type = 'checkbox'
                        if(menu.length >= 3){
                            value.checked = menu[2]
                        }
                        value.className = 'mana-setting-checkbox'
                        if(menu[0] in onchangeFunction){
                            value.onchange = onchangeFunction[menu[0]]
                        }
                        break
                    default:

                }
                line.appendChild(title)
                line.appendChild(value)
                box.appendChild(line)
            }
            const d = Dialog.wholeScreen(box)


        }
        openLog = () => {
            let logFile = new Array()
            for(let key in localStorage){
                if(key.startsWith('blockLog-')){
                    logFile.push(key)
                }
            }
            logFile.sort()
            logFile.reverse()
            this.logFile = logFile.slice()
            logFile.unshift('全部日志')
            if(logFile.length > 0){
                let box = document.createElement('div')
                box.className = 'mana-logbox'


                for(let file of logFile){
                    const line = document.createElement('div')
                    const title = document.createElement('span')
                    title.innerText = file
                    line.setAttribute('log',file)
                    line.className = 'mana-logline'
                    title.style.minWidth = '200px'
                    title.style.display = 'inline-block'
                    line.appendChild(title)

                    const del = document.createElement('span')
                    del.innerText = '删除'
                    del.onclick = (e) =>{
                        this.deleteLog.call(this,e)
                    }
                    del.className = 'mana-fontbutton'


                    const dowl = document.createElement('span')
                    dowl.innerText = '下载'
                    dowl.onclick = this.downloadLog
                    dowl.className = 'mana-fontbutton'

                    const show = document.createElement('span')
                    show.innerText = '查看'
                    show.onclick = this.showLog
                    show.className = 'mana-fontbutton'

                    line.appendChild(del)
                    line.appendChild(dowl)
                    line.appendChild(show)
                    box.appendChild(line)

                }


                box.children[0].setAttribute('log','All-Logs')
                box.children[0].children[1].style.visibility = 'hidden'

                const d = Dialog.wholeScreen(box)
            }
        }
        filterLogData (e) {
            const searchInputs = document.getElementsByClassName('mana-log-table-search')
            const keywords = new Array()
            for(let input of searchInputs){
                keywords.push(input.value)
            }
            let hiddenCount = 0
            for(let tableLine of document.getElementsByClassName('mana-table-data-content')[0].children){
                let datas = [...tableLine.children]
                for(let i in datas){
                    datas[i] = datas[i].innerText
                }
                let finded = true
                for(let i in keywords){
                    if(keywords[i] !=='' && datas[i].indexOf(keywords[i]) < 0){
                        finded = false
                        break
                    }
                }
                if(finded){
                    tableLine.classList.remove('mana-display-none')
                }else{
                    tableLine.classList.add('mana-display-none')
                }
            }
        }
        showLog = (e) =>{
            const logName = e.target.parentElement.getAttribute('log')
            let log = ''
            if(logName == 'All-Logs'){
                const logFile = this.logFile.slice()
                logFile.reverse()
                for(let file of logFile){
                    let logData = localStorage.getItem(file).trim().split('\n')
                    const date = '\n' + file.match(/[a-zA-Z]*-(.*)$/)[1]+'-'
                    log += date + logData.join(date)
                }
                log = log.trim()
            }else{
                log = localStorage.getItem(logName).trim()
            }
            log = '操作时间,禁言时间,UID,用户名,备注\n' + log


            const table = document.createElement('div')
            table.className = 'mana-table mana-log-table'
            const tableTitle = document.createElement('div')
            tableTitle.innerText = logName
            tableTitle.className = 'mana-table-title mana-log-table-title'
            const tableBody = document.createElement('div')
            tableBody.className = 'mana-table-body mana-log-table-body'

            table.appendChild(tableTitle)
            table.appendChild(tableBody)

            log = log.split('\n')
            const tableBodyTitle = document.createElement('div')
            const tableBodySearch = document.createElement('div')
            const tableBodyData = document.createElement('div')
            const dataContent = document.createElement('div')
            const dataBackground = document.createElement('div')
            const dataContainer = document.createElement('div')
            const tableBodyHead = document.createElement('div')

            dataContent.className = 'mana-table-data-content'
            dataBackground.className = 'mana-table-data-background'
            dataContainer.className = 'mana-table-data-container'
            tableBodyData.className = 'mana-table-data'
            tableBodyHead.className = 'mana-table-data-title'
            dataContainer.appendChild(dataContent)
            dataContainer.appendChild(dataBackground)
            tableBodyData.appendChild(dataContainer)

            const tline = document.createElement('div')
            const sline = document.createElement('div')
            tline.className = 'mana-table-line'
            sline.className = 'mana-table-line'

            tableBodyTitle.appendChild(tline)
            tableBodySearch.appendChild(sline)
            tableBodyHead.appendChild(tableBodyTitle)
            tableBodyHead.appendChild(tableBodySearch)

            let inputIndex = 0
            for(let title of log[0].split(',')){
                const ttd  = document.createElement('div')
                ttd.innerText = title
                ttd.className = 'mana-table-td mana-log-table-title-td'

                tline.appendChild(ttd)


                const std = document.createElement('div')
                const input = document.createElement('input')
                std.className = 'mana-table-td mana-log-table-search-td'
                input.className = 'mana-table-search mana-log-table-search'
                input.setAttribute('name',inputIndex)
                inputIndex += 1
                input.locked = false
                input.addEventListener('compositionstart',(e)=>{
                    e.target.locked = true
                })
                input.addEventListener('compositionend',(e)=>{
                    e.target.locked = false;
                    this.filterLogData.call(this,e)
                })
                input.oninput = (e) =>{
                    if(!e.target.locked){
                        this.filterLogData.call(this,e)
                    }
                }
                std.appendChild(input)
                sline.appendChild(std)
            }
            const countKey = log[0].split(',').length

            for(let i=1;i < log.length;i++){
                const data = log[i]
                const line = document.createElement('div')
                line.className = 'mana-table-line'

                for(let d of data.split(',')){
                    const td = document.createElement('div')
                    td.innerText = d
                    td.className = 'mana-table-td'
                    line.appendChild(td)
                }
                for(let i = line.children.length;i<countKey;i++){
                    const td = document.createElement('div')
                    td.innerText = '\u200b'
                    td.className = 'mana-table-td'
                    line.appendChild(td)
                }
                dataContent.appendChild(line)
            }

            for(let child of dataContent.children){
                const line = document.createElement('div')
                line.className = 'mana-table-line'
                const td = document.createElement('div')
                td.innerText = '\ufeff'
                td.className = 'mana-table-td'
                line.appendChild(td)
                dataBackground.appendChild(line)

            }
            tableBody.appendChild(tableBodyHead)
            tableBody.appendChild(tableBodyData)
            Dialog.wholeScreen(table)


        }
        downloadLog= (e) => {
                const logName = e.target.parentElement.getAttribute('log')
                let log = ''
                if(logName == 'All-Logs'){
                    const logFile = this.logFile.slice()
                    logFile.reverse()
                    for(let file of logFile){
                        let logData = localStorage.getItem(file).trim().split('\n')
                        const date = '\n' + file.match(/[a-zA-Z]*-(.*)$/)[1]+'-'
                        log += date + logData.join(date)
                    }
                    log = log.trim()
                }else{
                    log = localStorage.getItem(logName).trim()
                }
                log = '操作时间,禁言时间,UID,用户名,备注\n' + log
                FileUtils.saveFile(logName+'.csv',log)
        }
        deleteLog(e){
                const logName = e.target.parentElement.getAttribute('log')
                if(confirm(`是否要删除日志: ${logName} ?`)){
                    localStorage.removeItem(logName)
                    arrayRemove(this.logFile,logName)
                    const container = e.target.parentElement.parentElement
                    for(let line of container.children){
                        if(line.getAttribute('log') === logName){
                            container.removeChild(line)
                        }
                    }
                }

        }

    }

    class Blocker{
        constructor(config){
            this.config = config
            this.bindMutation()
        }
        bindMutation(){
            const observation = (mutations,observer) =>{
                for(let mutation of mutations){
                    if(mutation.addedNodes.length > 0){
                        for(let node of mutation.addedNodes){
                            if(node.hasAttribute('data-uid')){
                                const deleteButton = document.createElement('span')
                                deleteButton.innerText = '禁言'
                                deleteButton.className = 'v-middle mana-shortcut-block'
                                if(!this.config.settings.isShowShortcutBlock){deleteButton.className += ' mana-display-none'}
                                deleteButton.onclick = this.addBlockUser
                                deleteButton.style = 'color:#23ade5;cursor:pointer;line-height:20px'
                                node.insertBefore(deleteButton,node.children[0])

                            }
                        }
                    }
                }
            }
            this.chatList =  document.getElementById('chat-items')
            const config = {childList:true}
            const observer = new MutationObserver(observation)
            observer.observe(this.chatList,config)
        }
        removeBlockUser  (dom,data,logTitle,uid,uname)  {
            const head = {
                'accept': 'application/json, text/plain, */*',
                'content-type': 'application/x-www-form-urlencoded'
            }
            Ajax.post('//api.live.bilibili.com/banned_service/v1/Silent/del_room_block_user',data,head,(xhr)=>{
                        if(xhr.readyState == 4 && xhr.status == 200 ){
                            const res = JSON.parse(xhr.responseText)
                            if(res.code == 0){
                                let log = window.localStorage.getItem(logTitle) || ''
                                const date = new Date()
                                log += `${Clock.getFormatTime()},0,${uid},${uname},撤销禁言\n`
                                window.localStorage.setItem(logTitle,log)

                                dom.innerText = '禁言'
                                dom.onclick = this.addBlockUser
                            }
                        }
                    })
        }
        addBlockUser = (e) =>{
            const container = e.target.parentElement
            const uid = container.getAttribute('data-uid')
            const uname = container.getAttribute('data-uname')
            const danmaku = container.getAttribute('data-danmaku')
            const date = new Date()
            const block_uid = uid || '1'
            const logTitle = `blockLog-${Clock.getFormatDate()}`

            const cookie = new WebCookie()
            const data = {
                roomid:cookie.roomid || '1',
                block_uid,
                hour:this.config.settings.hour,
                csrf_token: cookie.bili_jct || '1',
                csrf: cookie.bili_jct || '1',
                visit_id:""
            }
            const head = {
                'accept': 'application/json, text/plain, */*',
                'content-type': 'application/x-www-form-urlencoded'
            }
            Ajax.post('//api.live.bilibili.com/banned_service/v2/Silent/add_block_user',data,head,(xhr)=>{
                if(xhr.readyState == 4 && xhr.status == 200 ){
                    const res = JSON.parse(xhr.responseText)
                    if(res.code == 0){
                        const id = res.data.id
                        let log = window.localStorage.getItem(logTitle) || ''
                        log += `${Clock.getFormatTime()},${data.hour},${uid},${uname},${danmaku}\n`
                        window.localStorage.setItem(logTitle,log)
                        const button = container.children[0]
                        button.innerText = '撤销'
                        const removedata = {
                            id,
                            roomid:cookie.roomid || '1',
                            csrf_token: cookie.bili_jct || '1',
                            csrf: cookie.bili_jct || '1',
                            visit_id:""
                        }
                        button.onclick = () =>{
                            this.removeBlockUser.call(this,button,removedata,logTitle,uid,uname)
                        }
                    }
                }
            })
        }
    }
    class ChatPanel{
        constructor(config){
            this.config = config
            this.autoLockScrolling()
            this.autoHideChatGift()
        }
        autoHideChatGift(){
            const chatPanel = document.getElementById('chat-history-list')
            if(chatPanel){
                const obs = (mutations,observer) =>{
                    if(this.config.settings.hiddenChatGift){
                        for(let mutation of mutations){
                            if(mutation.addedNodes.length > 0){
                                for(let node of mutation.addedNodes){
                                    if(node.classList.contains('gift-item')){
                                        node.classList.add('mana-display-none')
                                    }
                                }
                            }
                        }
                    }else{
                        observer.disconnect()
                    }
                }

                if(this.config.settings.hiddenChatGift){
                    this.observer = new MutationObserver(obs)
                    const obConfig = {childList:true,subtree:true}
                    this.observer.observe(chatPanel,obConfig)
                }else{
                    const giftItems = document.getElementsByClassName('gift-item')
                    for(let item of giftItems){
                        item.classList.remove('mana-display-none')
                    }
                }

            }else{
                requestAnimationFrame(this.autoHideChatGift)
            }

        }

        changeHinder(){
            for(let arg of arguments){
                if(arg instanceof HTMLElement){
                    if(this.config.settings.autoLock){
                        arg.style.display = 'none'
                    }else{
                        arg.style.display = ''
                    }
                }
            }
        }
        autoLockScrolling(){
            const chatPanel = document.getElementById('chat-history-list')
            const chatList = document.getElementById('chat-items')
            if(chatPanel){
                this.changeHinder(
                    document.getElementById('gift-screen-animation-vm'),
                    document.getElementById('chat-gift-bubble-vm'),
                    document.getElementById('welcome-area-bottom-vm'),
                )

                if(this.config.settings.autoLock){

                    chatPanel.onmouseover = function (e) {
                            const minHeight = Math.max(10,chatPanel.offsetHeight - chatList.offsetHeight)
                            const virtualChat = document.createElement('div')
                            const scrollTop = this.scrollTop
                            document.getElementById('penury-gift-msg').style.display = 'none'
                            document.getElementById('brush-prompt').style.display = 'none'
    
                            virtualChat.id = 'mana-virtual-chat'
                            virtualChat.style.height = `${minHeight}px`
    
                            const wheelEvent = new WheelEvent('wheel',{
                                deltaY:-10.0,
                                deltaX:0.0
                            })
                            
                            if(document.getElementById('mana-virtual-chat')){
                                if(document.getElementById('mana-virtual-chat') !== this.children[this.children.length-1]){
                                    this.removeChild(document.getElementById('mana-virtual-chat'))
                                }
                            }else{
                                this.appendChild(virtualChat)
                            }
                            this.dispatchEvent(wheelEvent)
                            this.scrollTop = scrollTop   
                    }  
                    chatPanel.onmouseout = function (e){
                        if(e.relatedTarget && !(this.compareDocumentPosition(e.relatedTarget)&16) && !(this.compareDocumentPosition(e.relatedTarget)&4) ){
                            console.log('out:',e.relatedTarget)
                            const unlock = document.getElementById('danmaku-buffer-prompt')
                            unlock && unlock.click()
                            document.getElementById('penury-gift-msg').style.display = 'block'
                            document.getElementById('brush-prompt').style.display = 'block'
                            if(document.getElementById('mana-virtual-chat')){
                                this.removeChild(document.getElementById('mana-virtual-chat'))
                            }
                        }
                    }
                }else{
                    chatPanel.onmouseover = function(){}
                    chatPanel.onmouseout = function(){}
                    if(document.getElementById('mana-virtual-chat')){
                        this.removeChild(document.getElementById('mana-virtual-chat'))
                    }
                }
            }else{
                requestAnimationFrame(autoLockScrolling)
            }
        }
    }

    class Clock{
        static getFormatTime(){
            const date = new Date()
            return `${zfill(date.getHours(),2)}:${zfill(date.getMinutes(),2)}:${zfill(date.getSeconds(),2)}`
        }
        static getFormatDate(){
            const date = new Date()
            return `${date.getFullYear()}-${zfill(date.getMonth()+1,2)}-${zfill(date.getDate(),2)}`
        }
    }

    class WebCookie{
        constructor(){
            const cookies = {}
            for(let cookie of document.cookie.split(';')){
                const [k,v] = cookie.trim().split('=')
                cookies[k] = v
            }
            cookies ['roomid'] = document.location.pathname.substr(1).split('?')[0]
            return cookies
        }
    }
    function doAdmin(){
        const info = document.getElementsByClassName('room-info-upper-row')[0].__vue__ || false

        if(debug){
            if(!didAdmin){
                didAdmin = true
                window.config = new Config()
                window.blocker = new Blocker(window.config)
                window.chatPanel = new ChatPanel(window.config)
                window.adminConsole = new AdminConsole(window.config,window.chatPanel)
                return
            }
        }
        if(!didAdmin && info && info.isAdmin){
            didAdmin = true
            const config = new Config()
            const blocker = new Blocker(config)
            const chatPanel = new ChatPanel(config)
            const adminConsole = new AdminConsole(config,chatPanel)

        }
    }

    function init(){
        const container =  document.getElementsByClassName('upper-right-ctnr')[0]

        if(container){
            function observation(mutations,observer){
                const info = document.getElementsByClassName('room-info-upper-row')[0].__vue__
                for(let mutation of mutations){
                    if(info.isAdmin){
                        if(!mutation.addedNodes[0]||!mutation.addedNodes[0].className.indexOf('mana-mana') < 0){
                            doAdmin()
                            break
                        }
                    }
                }
            }
            const config = {childList:true}
            const observer = new MutationObserver(observation)
            observer.observe(container,config)
            doAdmin()

        }else{
            requestAnimationFrame(init)
        }
    }
    var debug = false
    var didAdmin = false
    init()
})();