// ==UserScript== // @name B站油管showroom简易打尻装置 // @namespace http://tampermonkey.net/ // @version 0.5.1 // @description 啊B、油管或showroom打尻,需要用户已登录。若有滥用等问题概不负责,诶嘿。顺便关注一下小东人鱼和noworld吧~ // @author 太陽闇の力 // @include /https?:\/\/live\.bilibili\.com\/(blanc\/)?\d+\??.*/ // @match https://www.youtube.com/live_chat* // @exclude https://www.youtube.com/live_chat_replay* // @match https://www.showroom-live.com/* // @exclude https://www.showroom-live.com/room/* // @grant none // @license MIT // @downloadURL none // ==/UserScript== //1.界面参考自小东人鱼午安社五更耗纸 https://github.com/gokoururi-git/gachihelper/ //2.搜索找到代码中let countdown = "220"; let intervaltime = "6";这两句可以改开局的倒计时时间和发送间隔时间(油管和showroom的发送间隔在let intervaltime = "6";下面的代码中进行修改)。 //3.interval.min、interval.max、interval.step分别是滑动条的最小值、最大值和滑动间隔,已根据平台区别设定了这三个值和interval的关系,尽量避免发送太猛吓到主播。 //4.B站直播五秒内同一句打call的话,会显示发送频率太快而无法发送成功。建议写多几句不一样的。 //6.showroom的50 count中,搜索找到下面这两行代码可以更改设置 //const kankaku = 2; //50 count的时间间隔 。网络不好的时候不宜进行50 count,会自动停止 //countDownButton.innerText = kankaku*(50-1)+1;//50 count所用总时间。这个值是我乱猜的,不过也有实验过姑且能用的样子 (function() { let waitTime = 1;//等待1秒,如果加载比较慢的话,不等待可能获取不到元素。 let times = 10;//尝试查询的次数 let timescopy = times; let maint; let isOnLive;//showroom是否在直播 const roomID = /\d+/.exec(location.pathname)[0]; const main = () => { try { //-----------配置区------------- //0默认收起,1默认展开 let isunfold = 0; let unfold = ["展开","收起"]; //输入框选择符 let inputSelector = 'textarea'; //发送按钮选择符 let sendSelector = '.bl-button'; const urlCheck = (s)=>window.location.host == s; const isShowroom = urlCheck("www.showroom-live.com"); const isBilibili = urlCheck("live.bilibili.com"); const isYoutube = urlCheck("www.youtube.com"); let countdown = "220"; //倒计时 let intervaltime = "6";//发送间隔 let biliTextArea; let biliTextSender; if(isYoutube){//如果在油管的话设置初始发送间隔时间为20秒 intervaltime="20"; } if(isShowroom){//判断是不是showroom的直播间 intervaltime="50";//如果在showroom的话设置初始发送间隔时间为20秒 const showroomURL = "https://www.showroom-live.com"; let ogURL = window.document.querySelector("meta[property='og:url']"); ogURL = ogURL?.content; if(ogURL==showroomURL) { clearInterval(maint); return; } const liveURL = "https://www.showroom-live.com/api/live/live_info?room_id="+roomID; const req = new XMLHttpRequest(); let live_status; req.onload = function(){ if(req.status == 200){ live_status = JSON.parse(req.responseText).live_status; isOnLive = live_status == 2; } } req.open("GET",liveURL,false); req.send(null); if(!isOnLive) { clearInterval(maint); return; } //免费礼物点一下就能十连发 const comboInterval=25; const list = window.document.querySelector("#room-gift-item-list"); const gift = list.childNodes; for(let i = 0;i<5;i++){ gift[i].addEventListener('mousedown',(e)=>{ if(e.which ==1){//左键点下 const num = e.target.parentNode.parentNode.querySelector("div").innerText.split(" ")[1]-1; const comboNum = num > 9 ? 9 : num; for(let i = 1;i<=comboNum;i++){ setTimeout(()=>{e.target.click();},i*comboInterval) } } }) } //评论栏和发送按钮 inputSelector ='.comment-input-text'; sendSelector = '.js-room-comment-btn'; } //iframe,生日直播间之类的会成嵌套框架的结构,那个时候只看框架代码 if(isBilibili&&!(window.document.firstChild instanceof window.Comment)){//「あなたに逢えなくなって、錆びた時計と泣いたけど…」 clearInterval(maint); return; } clearInterval(maint); const callDealler = (call) => { let tempcallResult = []; call = call.trim(); call = call.replace(/ /g, ''); call = call.replace(/ /g, ''); call = call.replace(/\n{2,}/g, '\n'); tempcallResult = call.split('\n'); return tempcallResult; } let callResult = []; let currIndex = 0; let t = null; let ct = null; const inputEvent = document.createEvent("Event"); inputEvent.initEvent("input",true, true); const send = function(){ if(isYoutube){ biliTextArea.innerText = callResult[currIndex++]; }else{ biliTextArea.value = callResult[currIndex++]; } biliTextArea.dispatchEvent(inputEvent); biliTextSender.click(); } const next = function() { if(isShowroom&&!biliTextSender.classList.contains('is-disabled')){ biliTextArea.value = ""; pause(); return; } currIndex %= callResult.length; send(); } const init = function() { biliTextArea = window.document.querySelector(inputSelector); biliTextSender = window.document.querySelector(sendSelector); if(isYoutube){ biliTextArea = window.document.querySelector("#input").querySelector("#input"); biliTextSender = window.document.querySelector("#send-button").querySelector("#button"); } currIndex = 0; send(); const intervalChoose = intervalValBox&&parseFloat(intervalValBox.value) > 0 ? intervalValBox.value : interval.value; timeLabel.innerText = intervalChoose; t = setInterval(next, intervalChoose * 1000); } // ------------------GUI设计开始--------------- // 总容器 const container = window.document.createElement('div'); container.style.cssText = 'width:260px;position:fixed;bottom:5px;left:5px;z-index:999;box-sizing:border-box;'; // 工具名称 const topTool = window.document.createElement('div'); topTool.innerText = 'call'; topTool.style.cssText = 'text-align:center;line-height:20px;height:20px;width:100%;color:rgb(210,143,166);font-size:14px;'; // 最小化按钮 const collapseButton = window.document.createElement('button'); collapseButton.innerText = unfold[isunfold]; collapseButton.style.cssText = 'float:right;width:40px;height:20px;border:none;cursor:pointer;background-color:#1890ff;border-radius:1px;color:#ffffff;'; // 主窗口 const mainWindow = window.document.createElement('div'); mainWindow.style.cssText = 'width:100%;background-color:rgba(220, 192, 221, .5);padding:10px;box-sizing:border-box;'; if(isunfold==0){ mainWindow.style.display = "none"; } // call框 const textArea = window.document.createElement('textarea'); textArea.style.cssText = 'width:100%;height:50px;resize:none;outline:none;background-color:rgba(255,255,255,.5);border-radius:2px'; // 按钮区容器 const buttonArea = window.document.createElement('div'); buttonArea.style.cssText = 'width:100%;height:30px;box-sizing:border-box;display:flex; justify-content: center;'; // 按钮区容器2 const buttonArea2 = window.document.createElement('div'); buttonArea2.style.cssText = 'width:100%;height:30px;box-sizing:border-box;display:flex;justify-content: space-around;'; // 开始按钮 const goButton = window.document.createElement('button'); goButton.innerText = '开始'; goButton.style.cssText = 'width:max-content;height:28px;padding:0 5px;margin-left:5px;'; // 发送间隔提示文本 const intervalLabel = window.document.createElement('div'); intervalLabel.innerText = '发送间隔:' intervalLabel.style.cssText = 'width:70px;height:28px;line-height:28px;'; // 选择延迟 const interval = window.document.createElement('input'); interval.type = "range"; interval.step = "0.1"; interval.min = (intervaltime-2)/2; interval.value = intervaltime; interval.max = (+intervaltime+14)*1.5; interval.style.cssText = 'width:max-content;padding:0 5px;height:28px;margin-left:5px;'; const timeLabel = window.document.createElement('div'); timeLabel.innerText = intervaltime; timeLabel.style.cssText = 'width:24px;height:28px;line-height:28px;'; const secondLabel = window.document.createElement('div'); secondLabel.innerText = '秒'; secondLabel.style.cssText = 'width:max-content;height:28px;line-height:28px;'; // 倒计时 const countDownButton = window.document.createElement('button'); countDownButton.setAttribute("contenteditable", "true"); countDownButton.innerText = countdown; countDownButton.style.cssText = 'width:50px;height:28px;margin-left:5px;padding:0 5px;'; // 组装 topTool.appendChild(collapseButton); container.appendChild(topTool); mainWindow.appendChild(textArea); buttonArea.appendChild(intervalLabel); buttonArea.appendChild(interval); buttonArea.appendChild(timeLabel); buttonArea.appendChild(secondLabel); buttonArea2.appendChild(goButton); buttonArea2.appendChild(countDownButton); mainWindow.appendChild(buttonArea); mainWindow.appendChild(buttonArea2); container.appendChild(mainWindow); window.document.body.appendChild(container); // 显示逻辑控制 collapseButton.addEventListener('click', () => { if (collapseButton.innerText === '收起') { mainWindow.style.display = 'none'; collapseButton.innerText = '展开'; return; } if (collapseButton.innerText === '展开') { mainWindow.style.display = 'block'; collapseButton.innerText = '收起'; return; } }, false); //显示滑动条数字 interval.oninput = function() { timeLabel.innerText = interval.value; } if(isShowroom){ container.style.width = "282px"; timeLabel.style.width = "32px"; } if(isBilibili){ //填充上次所写的打call语句 const tsc = localStorage.getItem('tampermonkey_script_call'); if(tsc){ const tscJson = JSON.parse(tsc); textArea.value = tscJson[roomID]||``; }else{ localStorage.setItem('tampermonkey_script_call',JSON.stringify({})); } } //-------------------gui设计结束------------------ let intervalValBox ; function createInput(){ intervalLabel.innerText = ""; intervalValBox = document.createElement('input'); intervalValBox.style.width = "100%"; intervalValBox.placeholder = "输入数值"; intervalLabel.appendChild(intervalValBox); intervalLabel.onclick = null; } intervalLabel.onclick =createInput; function pause(){ clearInterval(t); clearInterval(ct); goButton.innerText = '开始'; countDownButton.innerText = countDownButton.innerText==0?countdown:220; countDownButton.setAttribute("contenteditable", "true"); if(isShowroom&&textArea.value.trim() === ''){ //showroom中如果输入为空,则进行50 count。 interval.min = (intervaltime-2)/2; interval.value = intervaltime; timeLabel.innerText = intervaltime; } } const countdownfunc = function() { if (countDownButton.innerText > 0) { countDownButton.innerText -= 1; } else { pause(); } } goButton.addEventListener('click', () => { if (goButton.innerText == '暂停') { pause() return; } if(intervalValBox){ intervalValBox.remove(); intervalLabel.innerText = "发送间隔:"; intervalLabel.onclick = createInput; } const value = textArea.value; callResult = callDealler(value); if (value.trim() === '') { if(!isShowroom){ window.alert('打尻:您还没有输入call语句'); return; }else{ //设定50 count const kankaku = 2; //50 count的时间间隔 countDownButton.innerText = kankaku*(50-1)+1;//这个值是我乱猜的 interval.min = kankaku; interval.value = kankaku; timeLabel.innerText = kankaku; callResult = Array.from({length:50}, (v,k) => k+1); } } if(isBilibili){ const tsc_temp = localStorage.getItem('tampermonkey_script_call'); const tscJson_temp = JSON.parse(tsc); tscJson_temp[roomID] = textArea.value; localStorage.setItem('tampermonkey_script_call',JSON.stringify(tscJson_temp)); } ct = setInterval(countdownfunc, 1000); goButton.innerText = '暂停'; countDownButton.setAttribute("contenteditable", "false"); if (!isNaN(parseFloat(countDownButton.innerText))&&!(isShowroom&&textArea.value.trim() === '')) { countdown = countDownButton.innerText; } init(); }, false); } catch (e) { times-=1; if(times==0){ times = timescopy; clearInterval(maint); if(window.confirm('打尻:发生未知错误\n可能是在加载中无法获取元素\n' + e+"\n是否重新尝试打尻?")){ maint= setInterval(main, 1000 * waitTime); } }; } } maint= setInterval(main, 1000 * waitTime); })();