您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add a "Speak" button to Blaseball live games
// ==UserScript== // @name Blaseball TTS // @namespace https://freshbreath.zone // @version 2.1 // @description Add a "Speak" button to Blaseball live games // @author MOS Technology 6502 // @match https://*.blaseball.com/ // @icon https://www.google.com/s2/favicons?sz=64&domain=blaseball.com // @grant none // @license CC0; https://creativecommons.org/share-your-work/public-domain/cc0/ // ==/UserScript== (function() { "use strict"; // This variable tracks which log we are "listening" to currently. let audibleLog; // Speak a string function speak(message) { // Reformat some of the messages for better speech playback. // Fix inning ordinals (1, 2, 3 -> 1st, 2nd, 3rd...) function ordinal(n) { let s = ["th", "st", "nd", "rd"]; let v = n%100; return (s[(v-20)%10] || s[v] || s[0]); } let inning = message.match(/End of the (?:top|bottom) of the (\d+)\./); if (inning != null) { message = message.slice(0, -1) + ordinal(inning[1]); } // Make pitch counts more phonetic message = message.replace(' 0-1', ' oh-n-one'); message = message.replace(' 0-2', ' oh-n-two'); message = message.replace(' 1-0', ' one-n-oh'); if (Math.random() < 0.5) { message = message.replace(' 1-1', ' count\'s even at one'); } else { message = message.replace(' 1-1', ' one-n-one'); } message = message.replace(' 1-2', ' one-n-two'); message = message.replace(' 2-0', ' two-n-oh'); message = message.replace(' 2-1', ' two-n-one'); if (Math.random() < 0.5) { message = message.replace(' 2-2', ' count\'s even at two'); } else { message = message.replace(' 2-2', ' two-n-two'); } message = message.replace(' 3-0', ' three-n-oh'); message = message.replace(' 3-1', ' three-n-one'); message = message.replace(' 3-2', ' full count'); console.log("Blaseball TTS: Speaking '" + message + "'"); // Create the TTS object let msg = new SpeechSynthesisUtterance(); msg.text = message; // Other fun voice options // msg.lang to change language // msg.pitch to sets the pitch (tone) // msg.rate to set rate (how fast they talk) // msg.voice to change voice (should be one of window.SpeechSynthesis.getVoices()) // msg.volume to change volume // Send the msg to the speech engine window.speechSynthesis.speak(msg); } // Callback for when a button is clicked. // This should try to find the sibling game-widget__log, and set audibleLog to it. const buttonCallback = (event) => { let node = event.target.parentNode; // Reset the audible log audibleLog = undefined; // Try to set audible log to the game log, if it exists for (const subNode of node.childNodes) { if (subNode.classList?.contains("game-widget__log")) { audibleLog = subNode; break; } } console.log("Blaseball TTS: Updating audibleLog to " + audibleLog) }; // Observer added to document which looks for changes const callback = (mutationList, observer) => { for (const mutation of mutationList) { if (mutation.type === "childList") { for (const node of mutation.addedNodes) { // Checking for the appearance of the game-widget__status, so we can add our button if (node.classList?.contains("game-widget__status")) { // found it! create button and add to main list let btn = document.createElement("button"); btn.textContent = '🔊'; btn.classList.add('schedule__day'); btn.addEventListener("click", buttonCallback); node.appendChild(btn); } } } else if (mutation.type === "characterData") { // Verify that the owner of this changed text is the audibleLog. // If so, send the new text to TTS. if (audibleLog && (mutation.target.parentNode === audibleLog)) { speak(mutation.target.data); } } } }; // Finally, attach the observer to the HTML document, and we are ready to go! const observer = new MutationObserver(callback); observer.observe(document, { subtree: true, childList: true, characterData: true }); })();