您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在kekeke跑團用的骰子BOT,支援COC、NC、四則運算規則,以及下載跑團紀錄
// ==UserScript== // @name Kekeke Dice Bot // @namespace https://greasyfork.org // @version 1.2.1 // @description 在kekeke跑團用的骰子BOT,支援COC、NC、四則運算規則,以及下載跑團紀錄 // @author Pixmi // @icon http://www.google.com/s2/favicons?domain=https://kekeke.cc/ // @include https://kekeke.cc/* // @include https://www.kekeke.cc/* // @license MIT // @grant none // @require https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js // ==/UserScript== /* jshint esversion: 6 */ $(window).on('load',function () { 'use strict'; var config; // 檢查Local Storage的kekeke_dice_config是否存在 if (localStorage.getItem('kekeke_dice_config') === null) { // 對config做初次設定 config = { Bot_Name: 'BOT', Auto_Time: 5, Auto_Time_Check: true, Auto_Logs: false, Logs_Time: false, Download_Time: '1' }; localStorage.setItem('kekeke_dice_config', JSON.stringify(config)); } else { // 舊版本升級新版本時,預防Local Storage出錯的檢查與設定 config = JSON.parse(localStorage.getItem('kekeke_dice_config')); if (config.Auto_Time === undefined) { config.Auto_Time = 5; } if (config.Auto_Time_Check === undefined) { config.Auto_Time_Check = true; } if (config.Auto_Logs === undefined) { config.Auto_Logs = false; } if (config.Logs_Time === undefined) { config.Logs_Time = false; } if (config.Logs_Clear === undefined) { config.Logs_Clear = true; } if (config.Download_Time === undefined || config.Download_Time == '0') { config.Download_Time = '1'; } localStorage.setItem('kekeke_dice_config', JSON.stringify(config)); } // 發言時間限制 setInterval(function(){ config = JSON.parse(localStorage.getItem('kekeke_dice_config')); config.Auto_Time_Check = true; localStorage.setItem('kekeke_dice_config', JSON.stringify(config)); },config.Auto_Time*1000+1); setTimeout(function () { // 給部分物件加上ID $('table.SquareCssResource-chatRoom > tbody > tr:last').find('table').attr('id', 'ChatTable'); $('td.SquareCssResource-submitInputButton').find('button').attr('id','submit_btn'); // 增加dice bot設定的按鈕 $('<td><div style="padding: 0 1px;"><img id="DiceConfig" src=" data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAD9AAAA/QBJxjN1gAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAS4SURBVHic7ZtPTFRHHMc/83i7FHYFq64RhAQRaAQE8SLx0ANJa/rHtqFgUguKkZrWxHpo06Q3br303DaNFQNJG1dDa7S3pkn/xHiiIq1plD8aKBAFIQoL7L/pAdgm6ltmcXgOdT+X3cz8fjO/+b55s9+XvIU0adI8y4iVJra0fFdkRz3HpeB1kFuA5zXWpcIkiDEhuRS1I1+cOfPOrZUMsiIBjjYFTwohPpOQtZJ83UjBjEB8+E1Hw+lUc1MWoLX53EcSPgfIqymk6MUyfBv92FmeVId6IqKzEWbGpxn85QZjPUMAUiKOn+5s+CqVcVISoLX5/KsSeVEIrIr63RTUFqeSvmoMXRngelc3UhKVwnrldMfbP6nmWqqB7x0+XyGRZwGrdF+FMYsHKKwtpnRfBYAt4vHvj7wbLFfNVRLgWGMwNx6nC/Dn1RSyrW7HCktdPbbV7SCvphAEfssSXccag7kqecveAm1tbdZQf/kPIPY/eZluIi8Wbr/+VltbWzxZ1LI7YHig/JO1t3gAsX+4v/zTZaOSdbYeCjZIKc4Cll2cj7eyhIwNOZDp1VamVubDxO7dJ9zbR3RwBCAuBPWnOhovOKU4CnDkSDBgRUUfkJO5twrvzpJVqHj1CPfcZP5KL8C9iMgo6+ion3hcnOMtICLiBJBjF+evucUDeKtLsYvyADZ4iZ1winMWQPAmgLdy7S1+iaULJyVvOMUkOwSLAazAer1VuYgVSDyeOF7FZAL4AYRtayzJXYQnUfs6pxhlJ/h/xejLK6dDzF2+Rmx04QC3t2zEW1uJlevXNoexAsjpEDPnf0bOhxNtkVsjREfHyW6sw/Jla5nHWAHmLl9Dzoep2pXHweZdSAnfdv5Bb88Y85d7yXppj5Z5jD0DYsN3AGhqqWFTwEdgs4+mw7sX+v65o20eYwWQi59C/GdWhfVQpwaMFcDeGgCgs72bqclZJidn6WzvBiCjYLO+ebSNpJnMPZXERie4dnWUj0/+mGgXmV4y9+7UNo+xO8Bavw5fQx32tnyEx0Z4PdjF+Vp/AcDgHQAg/NlkvVy7qnMYuwPc4qnsADccniquC+CWw1PFdQHccniquH4GuOXwVHFdALccniquC+CWw1Oux+0J3XJ4qri+A9xyeKo8FR/ghsNTJe0EdQ5mksNTRZsApjk8VbQJYJrDU0XbGWCaw1NFmwCmOTxVtAlgmsNTRdsZYJrDU0XbDjDN4ami1QeY5PBUeeadYFqAVIJDF38ldOm3NdW2HMnOgGnAL6PRxFsisZHxR4JMbpOR6NLXB48EL5JMgAGgKn53ilCun5sj45Qudvz+50AiqNrAtu6+Ycq2BsiavL/U1ff4JSYRQEouCEFVuOcGt18oIjQXZibHz8O2zsS20FyYwbEJtv89mFiL0zqVXpSc2l7A7S2bnEKNpGhsgtz+IYCpiMgocXpRMsNpgKtXz4V2Vx/oBxqem7wvsmfniHo9RDw2iBX/02ZVEfE4vgchCgZHyF14AJMCmto7G7odc5Yb9Oih4EGk+BrwaazVDaaFkK2nOg6cTRakdCnfb+7aHJaxDxDyNYGoAEz1tiGJ/EvApbjNl+3tB+4+7YLSpEmTJo3J/AskbixTjeLTxgAAAABJRU5ErkJggg==" style="cursor: pointer;" title="開啟Kekeke Dice設定選項"></div></td>').insertBefore($('.SquareCssResource-submitInputButton')); Observer(); }, 1500); const observeConfig = { attributes: true, childList: true, characterData: true, subtree: true }; const dd_check = /^([1-9][0-9]?)[d]([1-9][0-9]{0,2})([\+\-\*\/])?([1-9][0-9]{0,1})?([?])?/i, cc_check = /^cc\(?([0-9]{1,2})\)?(([\+\-])([1-9]))?/i, na_check = /^1na([\+\-\*\/])?([1-9][0-9]*)?/i, nc_check = /^([1-9][0-9]?)[n][c]([\+\-\*\/])?([1-9][0-9]{0,1})?/i; const url = /<a class="GlobalCssResource-external" target="_blank" ref="noopener" href="((http|https)(:\/\/)[\w\+\-\*\/\.\&\:\(\)\<\>\?\=]*)">((http|https)(:\/\/)[\w\+\-\*\/\.\&\:\(\)\<\>\?\=]*)<\/a>/ig, emoji = /<img class="GlobalCssResource-smiley" src="\/com\.liquable\.hiroba\/emoji\/[\w]*\/[\w\+\-\*\/\^\.\&\:\(\)\<\>]*.[a-z]*" alt="([\w\+\-\*\/\^\.\&\:\(\)\<\>\@]*)">/ig, br = /<br>/ig; // 檢查名稱是否與BOT名稱一致 function BotNameCheck() { let name = $('.gwt-TextBox.SquareCssResource-nicknameField').val(), config = JSON.parse(localStorage.getItem('kekeke_dice_config')); if (config.Bot_Name == name) return true; return false; } // 日期+時間函數 function getDateTimes(get,mode) { let text = ''; if (get) { let time = new Date(), year = time.getFullYear(), month = (time.getMonth() < 10 ? "0" : "") + time.getMonth()+1, day = (time.getDate() < 10 ? "0" : "") + time.getDate(), hours = (time.getHours() < 10 ? "0" : "") + time.getHours(), minutes = (time.getMinutes() < 10 ? "0" : "") + time.getMinutes(), seconds = (time.getSeconds() < 10 ? "0" : "") + time.getSeconds(); if (mode === 'logs') { text = hours + ':' + minutes + ':' + seconds + " - "; } if (mode === 'files') { text = year + month + day + '-' + hours + minutes + seconds; } } return text; } // 擲骰 function getRandom(e) { return Math.floor(Math.random()*e+1); } // 四則運算 function Arithmetics(symbols,value,count) { let x = []; if (symbols === '+') { value.forEach(function(item,index){ x.push(value[index] + count); }); } if (symbols === '-') { value.forEach(function(item,index){ if (value[index] - count <= 0) { x.push(1); } else { x.push(value[index] - count); } }); } if (symbols === '*') { value.forEach(function(item,index){ x.push(value[index] * count); }); } if (symbols === '/') { value.forEach(function(item,index){ x.push(Math.ceil(value[index] / count)); }); } return x; } // 陣列中取最大值 Array.prototype.max = function (){ let max = this[0]; this.forEach (function(ele,index,arr){ if(ele > max) { max = ele; } }); return max; }; // 陣列中取最小值 Array.prototype.min = function (){ let min = this[0]; this.forEach (function(ele,index,arr){ if(ele < min) { min = ele; } }); return min; }; /* Array.prototype的參考資料 * https://gist.github.com/hoandang/5989980 */ // 回應function function DiceRespond(content) { config = JSON.parse(localStorage.getItem('kekeke_dice_config')); // 輸入框放入BOT內容 $('textarea.SquareCssResource-messageInputField').val(content); // 點擊發送按鈕送出訊息 document.getElementById("submit_btn").click(); // 發文時間檢查設為false config.Auto_Time_Check = false; // 儲存config localStorage.setItem('kekeke_dice_config', JSON.stringify(config)); } // 安排時間紀錄選項的選取值 function check_download_time(v,t) { if (v == t) return true; return false; } function Observer() { // 建立rootObserver監控 let rootObserver = new MutationObserver(function (mutations) { // 設定要監控的元素 let chatElement = document.body.querySelector('table#ChatTable'); // 確認chatElement已經生成 if (chatElement) { // 結束rootObserver監控 rootObserver.disconnect(); const title = document.title.split(' | ')[0]; console.log(`Kekeke Dice 1.2.1 已在 ${title} 啟動。`); // 清空kekeke_logs紀錄 if (config.Auto_Logs) { localStorage.setItem(`logs_${title}`, JSON.stringify([])); } // 檢查自動下載 if (BotNameCheck() && config.Download_Time !== '0' && config.Auto_Logs) { let s = config.Download_Time*60*60*1000; if (s < 5000 || s === null) { s = 60*60*1000; } // 避免local storage發生問題時(例如NaN、0值)造成瀏覽器崩潰 setInterval(function(){ console.save(JSON.parse(localStorage.getItem(`logs_${title}`)), `logs_${title}` + '_' + getDateTimes(true,'files') + '.log'); // 清空紀錄 localStorage.setItem(`logs_${title}`, JSON.stringify([])); },s); } // 建立chatObserver監控聊天頻道 let chatObserver = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { // 有新的發言近來 if (mutation.type == 'childList' && mutation.addedNodes.length >= 1 && mutation.addedNodes[0].nodeName === 'DIV') { if (mutation.addedNodes[0].classList.contains('SquareCssResource-chatContent') && BotNameCheck()) { let config = JSON.parse(localStorage.getItem('kekeke_dice_config')), Post_Name = $(mutation.addedNodes[0]).find('.GlobalCssResource-colorNickname'), Post_Content = $(mutation.addedNodes[0]).find('.SquareCssResource-message'), Message = Post_Content.text().split(' @ '), content; // 確認是否為DND骰 if (dd_check.test(Message[0])) { // 拆解骰子細節並分配變數 let match = dd_check.exec(Message[0]), unit = Number(match[1]), flat = Number(match[2]), symbols = typeof match[3] !== 'undefined' ? true : false, count = typeof match[4] !== 'undefined' ? Number(match[4]) : 0, sum = typeof match[5] !== 'undefined' ? true : false, value = []; // 擲骰 for (let i = 1; i <= unit; i++) { value.push(getRandom(flat)); } let sum_value = [value.reduce((a,b)=>a+b)], sum_result = Arithmetics(match[3],sum_value,count), result = Arithmetics(match[3],value,count); if (sum && symbols) { content = `[${Post_Name.text()}] 的 ${match[0]} 骰出了 [${value}](加總) ${match[3]} ${count} = [${sum_value}] ${match[3]} ${count} = ${sum_result}`; } else if (sum && !symbols) { content = `[${Post_Name.text()}] 的 ${match[0]} 骰出了 [${value}](加總) = ${sum_value}`; } else if (!sum && symbols) { content = `[${Post_Name.text()}] 的 ${match[0]} 骰出了 [${value}] ${match[3]} ${count} = [${result}]`; } else { content = `[${Post_Name.text()}] 的 ${match[0]} 骰出了 [${value}]`; } } // 確認是否為COC骰 if (cc_check.test(Message[0])) { let match = cc_check.exec(Message[0]), // 比對值 flat = match[1] === '00' ? 100 : Number(match[1]), // 是否有獎勵或懲罰骰 symbols = typeof match[3] !== 'undefined' ? true : false, // 獎勵或懲罰的追加數量 count = typeof match[4] !== 'undefined' ? Number(match[4]) : 0, // 要骰的總數 unit = (symbols) ? count + 1 : 1, // 十位數 first_value = [], // 個位數 last_value = getRandom(10); // 擲骰十位數 for (let i = 1; i <= unit; i++) { // -1讓十位數骰出結果為0-9 first_value.push(getRandom(10)-1); } // 確認是懲罰(取最大數)或獎勵(取最小數)當十位數 let value = (match[3] === '-') ? first_value.max() : first_value.min(), result = (symbols) ? value*10 + last_value : getRandom(100), judge = ''; if (result > flat) { // 失敗 judge = '失敗'; if (flat > 50 && result == 100) { judge = '大失敗'; } if (flat <= 50 && result >= 96) { judge = '大失敗'; } } else { // 成功 judge = '成功'; if (result <= Math.floor(flat*0.5)) { judge = '困難成功'; } if (result <= Math.floor(flat*0.2)) { judge = '極難成功'; } if (result == 1) { judge = '大成功'; } } if (symbols) { let bonus = (match[3] === '-') ? '大' : '小'; content = `[${Post_Name.text()}] 的 CC${flat} 骰出了 [${first_value}](取最${bonus}x10) + [${last_value}] = ${result},判定為 [${judge}]`; } else { content = `[${Post_Name.text()}] 的 CC${flat} 骰出了 [${result}],判定為 [${judge}]`; } } // 確認是否為NA骰 if (na_check.test(Message[0])) { let array = ['','大失敗','失敗','失敗','失敗','失敗','對方隨意選擇','腳','身體','手','頭','己方隨意選擇'], match = na_check.exec(Message[0]), symbols = typeof match[1] !== 'undefined' ? true : false, count = typeof match[2] !== 'undefined' ? Number(match[2]) : 0, value = [getRandom(10)], result = symbols ? Arithmetics(match[1],value,count) : value, judge = result > 10 ? array[11] : array[result]; if (symbols) { content = `[${Post_Name.text()}] 的 ${match[0]} 骰出了 [${value}] ${match[1]} ${count} = [${result}] 判定為 [${judge}]`; } else { content = `[${Post_Name.text()}] 的 ${match[0]} 骰出了 [${value}] 判定為 [${judge}]`; } } // 確認是否為NC骰 if (nc_check.test(Message[0])) { // 拆解骰子細節並分配變數 let match = nc_check.exec(Message[0]), unit = Number(match[1]), symbols = typeof match[2] !== 'undefined' ? true : false, count = typeof match[3] !== 'undefined' ? Number(match[3]) : 0, value = []; // 擲骰 for (let i = 1; i <= unit; i++) { value.push(getRandom(10)); } let result = symbols ? Arithmetics(match[2],value,count) : value, judge; if (result.max() >= 6 ) { judge = '成功'; } if (result.max() > 10 ) { judge = '大成功'; } if (result.min() > 1 && result.max() <= 5 ) { judge = '失敗'; } if (result.min() == 1 && result.max() <= 5 ) { judge = '大失敗'; } if (symbols) { content = `[${Post_Name.text()}] 的 ${match[0]} 骰出了 [${value}] ${match[2]} ${count} = [${result}] 判定為 [${judge}]`; } else { content = `[${Post_Name.text()}] 的 ${match[0]} 骰出了 [${value}] 判定為 [${judge}]`; } } if (content !== '' && config.Auto_Time_Check) { DiceRespond(content); } // 將聊天紀錄輸出到console.log if (config.Auto_Logs) { let arr = JSON.parse(localStorage.getItem(`logs_${title}`)), // 設置去除表情符號html與超連結html的regex text = Post_Content.html().split('<span class="SquareCssResource-chatDate">'), // 刪去掉無用的html,保留表情符號的alt與超連結的href logs_content = text[0].replace(url,'$1').replace(emoji,'$1').replace(br,' '), // 設定時間戳記 time = getDateTimes(config.Logs_Time,'logs'); // 略過空白的對話內容 if (logs_content !== '') { arr.push(time + Post_Name.text() + ' : ' + logs_content); localStorage.setItem(`logs_${title}`, JSON.stringify(arr)); } } } } }); }); chatObserver.observe(chatElement, observeConfig); // 進階設定 $('#DiceConfig').click(function () { $(this).toggleClass('init'); if ($(this).hasClass('init')) { config = JSON.parse(localStorage.getItem('kekeke_dice_config')); $(this).parent().css('position', 'relative'); $('<div id="DiceConfig_panel"></div>').css({'background-color':'#fff','border':'1px solid #bbb','color':'#000','width':'300px','padding':'5px','position':'absolute','top':'30px','z-index':'999'}).insertAfter('#DiceConfig'); $('<table></table>').css({'width':'100%','table-layout':'fixed'}).appendTo('#DiceConfig_panel'); $('<tr><td style="width:65px">BOT名稱</td><td><input type="text" id="cb_Dice_Name" style="width:100%"></td></tr>').appendTo('#DiceConfig_panel > table'); $('<tr><td></td><td>(暱稱與此項相同才啟用BOT功能)</td></tr>').appendTo('#DiceConfig_panel > table'); $('<tr><td>自動記錄</td><td><label><input type="checkbox" id="cb_Auto_Logs" checked="checked">啟用自動紀錄</label></td></tr>').appendTo('#DiceConfig_panel > table'); $('<tr><td></td><td><label><input type="checkbox" id="cb_Logs_Time" checked="checked">加入時間戳記</label></td></tr>').appendTo('#DiceConfig_panel > table'); $('<tr><td>下載紀錄</td><td><select id="cb_Download_Time" style="width:100%"></select></td></tr>').appendTo('#DiceConfig_panel > table'); $('<option></option>').attr({'value':'0','selected':check_download_time(0,config.Download_Time)}).text('手動').appendTo('#cb_Download_Time'); $('<option></option>').attr({'value':'1','selected':check_download_time(1,config.Download_Time)}).text('每1小時').appendTo('#cb_Download_Time'); $('<option></option>').attr({'value':'2','selected':check_download_time(2,config.Download_Time)}).text('每2小時').appendTo('#cb_Download_Time'); $('<option></option>').attr({'value':'4','selected':check_download_time(4,config.Download_Time)}).text('每4小時').appendTo('#cb_Download_Time'); $('<option></option>').attr({'value':'6','selected':check_download_time(6,config.Download_Time)}).text('每6小時').appendTo('#cb_Download_Time'); $('<div></div>').css({'padding-top':'3px'}).appendTo('#DiceConfig_panel'); $('<button></button>').attr({'id':'save_dice_config','type':'button','title':'儲存後將自動重新整理'}).addClass('button_setting').text('儲存設定').appendTo('#DiceConfig_panel > div'); $('<button></button>').attr({'id':'save_logs','type':'button'}).addClass('button_setting').text('下載對話紀錄').appendTo('#DiceConfig_panel > div'); $('.button_setting').css({'height':'20px','float':'right','margin':'0 2px','padding':'0','font-size':'10px','box-sizing':'border-box'}); // 開啟面板時設定狀態 $('#cb_Dice_Name').val(config.Bot_Name); if (BotNameCheck(name)) { $('#cb_Auto_Logs').attr('disabled',false); $('#cb_Logs_Time').attr('disabled',false); $('#cb_Download_Time').attr({'disabled':false}); } else { $('#cb_Auto_Logs').attr({'disabled':true}); $('#cb_Logs_Time').attr({'disabled':true}); $('#cb_Download_Time').attr({'disabled':true}); } if (!config.Auto_Logs) $('#cb_Auto_Logs').attr('checked',false); if (!config.Logs_Time) $('#cb_Logs_Time').attr('checked',false); $('#cb_Dice_Name').on('change', function () { config.Bot_Name = $('#cb_Dice_Name').val(); localStorage.setItem('kekeke_dice_config', JSON.stringify(config)); if (BotNameCheck(name)) { $('#cb_Auto_Logs').attr('disabled',false); $('#cb_Logs_Time').attr('disabled',false); $('#cb_Download_Time').attr({'disabled':false}); } else { $('#cb_Auto_Logs').attr({'disabled':true}); $('#cb_Logs_Time').attr({'disabled':true}); $('#cb_Download_Time').attr({'disabled':true}); } }); $('#cb_Download_Time').on('change', function () { config.Download_Time = $('#cb_Download_Time').val(); }); $('#cb_Auto_Logs').on('change', function () { if (this.checked) { config.Auto_Logs = true; } else { config.Auto_Logs = false; } }); $('#cb_Logs_Time').on('change', function () { if (this.checked) { config.Logs_Time = true; } else { config.Logs_Time = false; } }); $('#save_logs').click(function () { console.save(JSON.parse(localStorage.getItem(`logs_${title}`)), `logs_${title}` + '_' + getDateTimes(true,'files') + '.log'); // 清空紀錄 localStorage.setItem(`logs_${title}`, JSON.stringify([])); }); $('#save_dice_config').click(function () { localStorage.setItem('kekeke_dice_config', JSON.stringify(config)); $('#DiceConfig').removeClass('init'); $('#DiceConfig_panel').remove(); location.reload(); }); } else { $('#DiceConfig_panel').remove(); } }); } }); rootObserver.observe(document, observeConfig); } // 下載對話紀錄 (function(console){ console.save = function(data, filename){ if(!data) { console.error('沒有保存任何對話紀錄'); return; } if(!filename) filename = 'autosave.log'; if(typeof data === "object"){ data = JSON.stringify(data, undefined, 4); } let blob = new Blob([data], {type: 'text/json'}), e = document.createEvent('MouseEvents'), a = document.createElement('a'); a.download = filename; a.href = window.URL.createObjectURL(blob); a.dataset.downloadurl = ['text/json', a.download, a.href].join(':'); e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); a.dispatchEvent(e); }; })(console); /* console.save的參考資料 * https://bgrins.github.io/devtools-snippets/#console-save */ });