您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
try to take over the world!
当前为
// ==UserScript== // @name b站直播间房管辅助直播 bilibili 哔哩哔哩 // @namespace http://tampermonkey.net/ // @version 0.4 // @description try to take over the world! // @author You // @match *://live.bilibili.com/* // @exclude *://live.bilibili.com/p/* // @grant none // ==/UserScript== (function() { const hour = '1' 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([data],{type:'text/csv'}); 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 wholeScreen(child,height= '200px',width='200px',top='0px',left='0px'){ const background = document.createElement('div') 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%;' background.onclick = function(){ this.parentElement.removeChild(this) } 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 = function (e){ e.stopPropagation() } background.appendChild(dialog) document.body.appendChild(background) return {background,dialog} } } class AdminConsole{ constructor(){ this.injectStyle() this.initConsole() } injectStyle(){ let style = '' style += '.mana-fontbutton{cursor: pointer;margin-left: 4px;}' style += '.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; height: 600px; }' style += '.mana-logline{ border-bottom: 1px solid #e5e5e5; margin: 3px 0px 3px 0px; }' const styleDOM = document.createElement('style') styleDOM.innerHTML = style styleDOM.setAttribute('mana-style','') document.body.appendChild(styleDOM) } initConsole(){ const injectMenu = () =>{ const menu = document.getElementsByClassName('admin-drop-ctnr')[0] if(menu){ const openLog = document.createElement('p') openLog.className = 'drop-menu-item ts-dot-4' openLog.innerText = '一键禁言记录' openLog.style = 'white-space: nowrap;font-size: 12px;color: #333;line-height: 19px;margin: 0 0 8px;' openLog.onmouseover = BasicAction.fontButtonOver openLog.onmouseout = BasicAction.fontButtonOut openLog.onclick = this.openLog menu.appendChild(openLog) return } requestAnimationFrame(injectMenu) } injectMenu() 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' 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() } openLog = () => { let logFile = new Array() for(let key in localStorage){ if(key.startsWith('blockLog-')){ logFile.push(key) } } logFile.sort() 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 = this.deleteLog del.onmouseover = BasicAction.fontButtonOver del.onmouseout = BasicAction.fontButtonOut del.className = 'mana-fontbutton' const dowl = document.createElement('span') dowl.innerText = '下载' dowl.onclick = this.downloadLog dowl.onmouseover = BasicAction.fontButtonOver dowl.onmouseout = BasicAction.fontButtonOut dowl.className = 'mana-fontbutton' line.appendChild(del) line.appendChild(dowl) box.appendChild(line) } const d = Dialog.wholeScreen(box) } } downloadLog(e){ try{ const logName = e.target.parentElement.getAttribute('log') const log = localStorage.getItem(logName) FileUtils.saveFile(logName+'.csv',log) }catch(e){console.log('导出日志异常')} } deleteLog(e){ try{ const logName = e.target.parentElement.getAttribute('log') if(confirm(`是否要删除日志: ${logName} ?`)){ localStorage.removeItem(logName) const container = e.target.parentElement.parentElement for(let line of container.children){ if(line.getAttribute('log') === logName){ container.removeChild(line) } } } }catch(e){console.log('删除日志异常')} } } class Blocker{ constructor(){ 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' 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 += `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()},${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-${date.getFullYear()}-${zfill(date.getMonth()+1,2)}-${zfill(date.getDate(),2)}` const cookie = new WebCookie() const data = { roomid:cookie.roomid || '1', block_uid, 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 += `${zfill(date.getHours(),2)}:${zfill(date.getMinutes(),2)}:${zfill(date.getSeconds(),2)},${uid},${uname},${danmaku}\n` window.localStorage.setItem(logTitle,log) const button = container.children[0] button.innerText = '撤销' const data = { 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,data,logTitle,uid,uname) } } } }) } } 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 } } const adminConsole = new AdminConsole() const blocker = new Blocker() })();