B站直播间发送快捷弹幕
// ==UserScript== // @name 快捷弹幕 // @namespace http://tampermonkey.net/ // @version 0.5.5 // @description B站直播间发送快捷弹幕 // @author mianju // @include /https?:\/\/live\.bilibili\.com\/(blanc\/)?\d+\??.*/ // @require https://cdn.staticfile.org/axios/0.27.2/axios.min.js // @grant none // ==/UserScript== (function () { function main() { initLib() initCss() waitForLoaded(() => { initUi() }) } function initLib() { let scriptElement = document.createElement('script') scriptElement.src = 'https://cdn.staticfile.org/vue/2.6.9/vue.min.js' document.head.appendChild(scriptElement) let linkElement = document.createElement('link') linkElement.rel = 'stylesheet' linkElement.href = 'https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.14/theme-chalk/index.css' document.head.appendChild(linkElement) scriptElement = document.createElement('script') scriptElement.src = 'https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.14/index.js' document.head.appendChild(scriptElement) } function initCss() { let css = ` .el-select .el-input { width: 80px; } .input-with-select .el-input-group__prepend { background-color: #fff; } ` let styleElement = document.createElement('style') styleElement.innerText = css document.head.appendChild(styleElement) } function waitForLoaded(callback, timeout = 10 * 1000) { let startTime = new Date() function poll() { if (isLoaded()) { callback() return } if (new Date() - startTime > timeout) { return } setTimeout(poll, 1000) } poll() } function isLoaded() { if (window.ELEMENT === undefined) { return false } if (document.querySelector('#control-panel-ctnr-box') === null) { return false } return true } function initUi() { var father = document.getElementsByClassName('icon-left-part')[0]; let quickDanmakuElement = document.createElement('div'); father.appendChild(quickDanmakuElement); new Vue({ el: quickDanmakuElement, template: ` <span> <template style="width: 200px" > <el-select v-model="value" placeholder="选择" @change="sendDanmaku" style="width: 80px;height: 24px" size="mini"> <el-option-group v-for="group in options" :key="group.label" :label="group.label"> <el-option v-for="danmaku in group.danmakus" :key="danmaku.label" :label="danmaku.label" :value="danmaku.label"> </el-option> </el-option-group> </el-select> <el-button icon="el-icon-message" size="mini" circle @click="sendDanmaku(value)"></el-button> <el-popover placement="bottom" title="快捷弹幕设置" width="300px" trigger="click" > <span class="demonstration">添加: </span> <el-tooltip class="item" effect="dark" content="添加表情包教程:https://bbs.nga.cn/read.php?pid=594434351&opt=128" placement="top"> <el-input placeholder="请输入内容" v-model="danmaku4add" class="input-with-select" maxlength="20" size="mini" style="width: 300px"> <el-select v-model="select" slot="prepend" placeholder="请选择"> <el-option label="当前" value="cur"></el-option> <el-option label="所有" value="all"></el-option> </el-select> <el-button slot="append" icon="el-icon-check" @click="addDanmaku" size="mini"></el-button> </el-input> </el-tooltip> <div class="block" style="margin-top: 10px"> <span class="demonstration">删除: </span> <el-cascader size="mini" v-model="danmakus4del" :options="options" :props="{ expandTrigger: 'hover','children': 'danmakus','multiple': 'true','emitPath':'false' }" ></el-cascader> <el-button type="danger" icon="el-icon-delete" size="mini" @click="delDanmakus"></el-button> </div> <el-button slot="reference" icon="el-icon-setting" size="mini" circle></el-button> <div style="margin-top: 10px"> 自动发送: <el-radio-group v-model="freq" size="mini"> <el-radio-button label="高频"></el-radio-button> <el-radio-button label="正常"></el-radio-button> <el-radio-button label="低频"></el-radio-button> </el-radio-group> <el-switch size="mini" v-model="value4switch" :disabled="flag4switch" @change="autoSend"> </el-switch> </div> </el-popover> </template> </span> `, data: { options: [ { label: '所有直播间', value: 'all', danmakus: [] }, { label: '当前直播间', value: 'cur', danmakus: [] } ], value: '', danmaku4add: '', select: 'cur', danmakus4del: [], roomId: 0, allRoomDanmakus: [], curRoomDanmakus: [], key4all: 'allRoomDanmukus', key4cur: '', value4switch: false, interval: null, freq: '正常', freqopts: {'高频':[30,6000],'正常':[15,12000],'低频':[10,18000]}, chatInput: null }, methods: { "delDanmakus": function () { function del(val,danmakus) { let index = danmakus.indexOf(val); danmakus.splice(index,1); }; for (let i=0;i<this.danmakus4del.length;i++){ let danmaku = this.danmakus4del[i]; if (danmaku[0] == "cur") { del(danmaku[1],this.curRoomDanmakus); } else { del(danmaku[1],this.allRoomDanmakus); } } localStorage.setItem(this.key4cur,JSON.stringify(this.curRoomDanmakus)); localStorage.setItem(this.key4all,JSON.stringify(this.allRoomDanmakus)); this.danmakus4del = []; this.$message.success("删除成功"); this.handleDanmakus(); }, "getRoomId": function () { if (window.__NEPTUNE_IS_MY_WAIFU__) { this.roomId = window.__NEPTUNE_IS_MY_WAIFU__.roomInfoRes.data.room_info.room_id; } else { let url = document.URL; var re = /\/\d+/.exec(url); this.roomId = re[0].substr(1); } }, "getDanmakus": function (key) { if (localStorage.getItem(key)){ return JSON.parse(localStorage.getItem(key)); } return []; }, "handleDanmakus": function () { function handle(danmakus4opt,danmakus4room){ danmakus4opt.splice(0); for (let i=0;i<danmakus4room.length;i++) { danmakus4opt.push({label:danmakus4room[i],value:danmakus4room[i]}); } } handle(this.options[0].danmakus,this.allRoomDanmakus); handle(this.options[1].danmakus,this.curRoomDanmakus); }, "addDanmaku": function () { function add(key,danmakus,danmaku){ danmakus.push(danmaku); localStorage.setItem(key,JSON.stringify(danmakus)); }; if (this.select == "cur") { add(this.key4cur,this.curRoomDanmakus,this.danmaku4add); } else { add(this.key4all,this.allRoomDanmakus,this.danmaku4add); } this.danmaku4add = ''; this.$message.success("添加成功"); this.handleDanmakus(); }, "sendDanmaku": function (danmaku4send) { var patt = /(room|official)(_\d+){1,2}/ var jct = getCookie('bili_jct'); var date = new Date(); var data = new FormData(); data.append('bubble',0); data.append('color',16777215); data.append('fontsize',25); data.append('mode',1); data.append('msg',danmaku4send); data.append('rnd',parseInt(date.getTime()/1000)); data.append('roomid',this.roomId); data.append('csrf',jct); data.append('csrf_token',jct); if (patt.test(danmaku4send)) { data.set('msg',patt.exec(danmaku4send)[0]) data.append('dm_type',1); } let apiClient = axios.create({ baseURL: 'https://api.live.bilibili.com', withCredentials: true }); apiClient.post('/msg/send',data).then((res)=>{ if (res.data.code == 0) { switch (res.data.msg) { case '': this.$message.success('发送成功 - ' + danmaku4send); break; /*case 'f': this.$message.error('发送失败 - 包含B站屏蔽词: ' + danmaku4send); break; case 'k': this.$message.error('发送失败 - 包含直播间屏蔽词: ' + danmaku4send); break;*/ case 'same restriction': this.$message.error('发送失败,该弹幕已被限制,请选择其它弹幕!'); break; case 'max limit exceeded': this.$message.error('发送失败,弹幕池达到上限!'); break; default: this.$message.error('发送失败 - ' + res.data.message); } } else { this.$message.error('发送失败 - ' + res.data.message); } }).catch(()=>{ this.$message.error('发送失败 - ' + danmaku4send); }); }, "autoSend": function(flag) { if (flag) { this.$message.info('自动发送已开启'); let count = 0; let _this = this let opt = this.freqopts[this.freq] this.interval = setInterval( ()=>{ count++; if (count == opt[0]) { _this.value4switch = false; clearInterval(_this.interval) _this.$message.info('自动发送已关闭'); } _this.sendDanmaku(_this.value); },opt[1] ); } else { this.$message.info('自动发送已关闭'); clearInterval(this.interval); } }, "sendListener": function() { var _this = this; function keydown(event) { if (event.keyCode == 13) { if (_this.chatInput.chatInput != '') { _this.sendDanmaku(_this.chatInput.chatInput); } setTimeout(_this.chatInput.clearInput, 10); } }; this.chatInput.onKeyDown = keydown; } } , created: function () { this.getRoomId(); this.key4cur = this.roomId + '-danmukus'; this.curRoomDanmakus = this.getDanmakus(this.key4cur); this.allRoomDanmakus = this.getDanmakus(this.key4all); this.handleDanmakus(); this.chatInput = document.querySelector('#control-panel-ctnr-box').__vue__; this.sendListener(); }, computed: { flag4switch: function() { return (this.value == '') } } }); } function getCookie(cname){ var name = cname + "="; var ca = document.cookie.split(';'); for(var i=0; i<ca.length; i++) { var c = ca[i].trim(); if (c.indexOf(name)==0) { return c.substring(name.length,c.length); } } return ""; } main() })();