您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Play sounds when users join, leave, or mention you in Multiplayer Piano
当前为
// ==UserScript== // @name Multiplayer Piano Optimizations [Sounds] // @namespace https://tampermonkey.net/ // @version 1.2.8 // @description Play sounds when users join, leave, or mention you in Multiplayer Piano // @author zackiboiz, cheezburger0, ccjit // @match *://multiplayerpiano.com/* // @match *://multiplayerpiano.net/* // @match *://multiplayerpiano.org/* // @match *://piano.mpp.community/* // @match *://mpp.7458.space/* // @match *://qmppv2.qwerty0301.repl.co/* // @match *://mpp.8448.space/* // @match *://mpp.autoplayer.xyz/* // @match *://mpp.hyye.xyz/* // @icon https://www.google.com/s2/favicons?sz=64&domain=multiplayerpiano.net // @grant GM_info // @license MIT // ==/UserScript== (async () => { function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } await sleep(1000); const builtin = [ { NAME: "Default", AUTHOR: "Zacki", MENTION: "https://files.catbox.moe/f5tzag.mp3", JOIN: "https://files.catbox.moe/t3ztlz.mp3", LEAVE: "https://files.catbox.moe/kmpz7e.mp3" }, { NAME: "PRISM", AUTHOR: "ccjit", MENTION: "https://file.garden/aHFDYCLeNBLSceNi/Swoosh.wav", JOIN: "https://file.garden/aHFDYCLeNBLSceNi/Plug%20In.wav", LEAVE: "https://file.garden/aHFDYCLeNBLSceNi/Plug%20Out.wav" }, { NAME: "Win7", AUTHOR: "cheezburger0", MENTION: "https://file.garden/ZXdl6GMYuz15ftGp/Windows%20Notify.wav", JOIN: "https://file.garden/ZXdl6GMYuz15ftGp/Windows%20Logon%20Sound.wav", LEAVE: "https://file.garden/ZXdl6GMYuz15ftGp/Windows%20Recycle.wav" }, { NAME: "Win11", AUTHOR: "ccjit", MENTION: "https://file.garden/aHFDYCLeNBLSceNi/Win11%20Notify.wav", JOIN: "https://file.garden/aHFDYCLeNBLSceNi/Win11%20Plug%20In.wav", LEAVE: "https://file.garden/aHFDYCLeNBLSceNi/Win11%20Plug%20Out.wav" }, { NAME: "Discord", AUTHOR: "ccjit", MENTION: "https://file.garden/aHFDYCLeNBLSceNi/Discord%20Ping.mp3", JOIN: "https://file.garden/aHFDYCLeNBLSceNi/Discord%20Join.mp3", LEAVE: "https://file.garden/aHFDYCLeNBLSceNi/Discord%20Leave.mp3" }, { NAME: "Xylo", AUTHOR: "cheezburger0", MENTION: "https://file.garden/ZXdl6GMYuz15ftGp/mention.wav", JOIN: "https://file.garden/ZXdl6GMYuz15ftGp/join.wav", LEAVE: "https://file.garden/ZXdl6GMYuz15ftGp/leave.wav" }, { NAME: "Skype 1", AUTHOR: "ccjit", MENTION: "https://file.garden/aHFDYCLeNBLSceNi/SKYPE_IM_ACC_MENTION.flac", JOIN: "https://file.garden/aHFDYCLeNBLSceNi/SKYPE_USER_ADDED.flac", LEAVE: "https://file.garden/aHFDYCLeNBLSceNi/SKYPE_USER_LEFT.flac" }, { NAME: "Piano", AUTHOR: "cheezburger0", MENTION: "https://file.garden/ZXdl6GMYuz15ftGp/mention-2.wav", JOIN: "https://file.garden/ZXdl6GMYuz15ftGp/join-2.wav", LEAVE: "https://file.garden/ZXdl6GMYuz15ftGp/leave-2.wav" } ]; const defaultName = builtin[0].NAME; class SoundManager { constructor(version) { this.version = version; this.GAP_MS = 200; this.volume = 1.0; this.lastPlayed = {}; this.audioCache = {}; this._loadSoundpacks(); const stored = localStorage.currentSoundpack || defaultName; this.currentSoundpack = this.soundpacks[stored] ? stored : defaultName; this.SOUNDS = this.soundpacks[this.currentSoundpack]; this._loadAssetsForCurrentPack(); } _loadSoundpacks() { let saved = {}; try { saved = JSON.parse(localStorage.savedSoundpacks) || {}; } catch { console.warn( "Invalid savedSoundpacks JSON, reverting to builtin only." ); saved = {}; } this.soundpacks = { ...saved }; builtin.forEach((sp) => this.saveSoundpack(sp, true)); localStorage.savedSoundpacks = JSON.stringify(this.soundpacks); } setCurrentSoundpack(name) { if (!this.soundpacks[name]) { console.warn(`Soundpack "${name}" does not exist.`); return; } this.currentSoundpack = name; this.SOUNDS = this.soundpacks[name]; localStorage.currentSoundpack = name; this._refreshDropdown(); this._loadAssetsForCurrentPack(); } saveSoundpack(obj, loading = false) { const { NAME, AUTHOR, MENTION, JOIN, LEAVE } = obj; if (!NAME || !AUTHOR || !MENTION || !JOIN || !LEAVE) { if (!loading) alert("All fields (NAME, AUTHOR, MENTION, JOIN, LEAVE) are required."); return; } for (const [existingName, sp] of Object.entries(this.soundpacks)) { if (sp.MENTION === MENTION && sp.JOIN === JOIN && sp.LEAVE === LEAVE) { if (!loading) alert(`This soundpack is identical to "${existingName}".`); return; } } let unique = NAME; let counter = 1; while (this.soundpacks[unique]) { unique = `${NAME} (${counter++})`; } this.soundpacks[unique] = { NAME: unique, AUTHOR, MENTION, JOIN, LEAVE }; localStorage.savedSoundpacks = JSON.stringify(this.soundpacks); if (!loading) alert(`Imported soundpack "${unique}".`); this._refreshDropdown?.(); } _loadAssetsForCurrentPack() { this.audioCache = {}; ["MENTION", "JOIN", "LEAVE"].forEach(key => { const baseSrc = this.SOUNDS[key]; const timestamp = Date.now(); const separator = baseSrc.includes("?") ? "&" : "?"; const bustedSrc = `${baseSrc + separator}_=${timestamp}`; const audio = new Audio(bustedSrc); audio.preload = "auto"; audio.volume = this.volume; this.audioCache[baseSrc] = audio; }); } play(src) { const now = Date.now(); if (!this.lastPlayed[src] || now - this.lastPlayed[src] >= this.GAP_MS) { this.lastPlayed[src] = now; const original = this.audioCache[src]; if (original) { const clone = original.cloneNode(); clone.volume = this.volume; clone.play().catch(() => { }); } else { const audio = new Audio(src); audio.volume = this.volume; audio.play().catch(() => { }); } } } _refreshDropdown() { const select = document.querySelector("#soundpack-select"); if (!select) return; select.innerHTML = ""; for (const [key, sp] of Object.entries(this.soundpacks)) { const opt = document.createElement("option"); opt.value = key; opt.textContent = `${sp.NAME} [${sp.AUTHOR}]`; if (key === this.currentSoundpack) opt.selected = true; select.appendChild(opt); } } } const soundManager = new SoundManager(GM_info.script.version); let replyTo = {}; let users = {}; function onMessage(msg) { const sender = msg.p ?? msg.sender; replyTo[msg.id] = sender._id; const me = MPP.client.user._id; const mention = msg.a.includes(`@${me}`); const replyMention = msg.r && replyTo[msg.r] === me; if ((mention || replyMention) && (!document.hasFocus() || MPP.client.getOwnParticipant().afk)) { soundManager.play(soundManager.SOUNDS.MENTION); } } MPP.client.on("a", onMessage); MPP.client.on("dm", onMessage); MPP.client.on("ch", (ch) => { users = {}; ch.ppl.forEach((u) => (users[u._id] = u)); }); MPP.client.on("p", (p) => { if (!users[p._id]) { soundManager.play(soundManager.SOUNDS.JOIN); } users[p._id] = p; }); MPP.client.on("bye", (u) => { soundManager.play(soundManager.SOUNDS.LEAVE); delete users[u.p]; }); const topOffset = document.getElementsByClassName("mpp-hats-button").length ? 84 : 58; const $btn = $(`<button id="soundpack-btn" class="top-button" style="position: fixed; right: 6px; top: ${topOffset}px; z-index: 100; padding: 5px;">MPP Sounds</button>`); $("body").append($btn); const $modal = $(` <div id="soundpack-modal" class="dialog" style="height: 240px; margin-top: -120px; display: none;"> <h3>MPP Sounds</h3><hr> <p> <label>Select soundpack: <select id="soundpack-select" class="text"></select> </label> </p> <p> <label>Import from JSON: <input type="file" id="soundpack-file" accept=".json"/> </label> </p> <p> <label>Reset Soundpacks: <button id="reset-soundpacks">Reset to default</button> </label> </p> <p><button id="soundpack-submit" class="submit">OK</button></p> </div> `); $("#modal #modals").append($modal); function hideAllModals() { $("#modal #modals > *").hide(); $("#modal").hide(); } function showModal() { if (MPP.chat) MPP.chat.blur(); hideAllModals(); soundManager._refreshDropdown(); $("#modal").fadeIn(250); $modal.show(); } $btn.on("click", showModal); $("#soundpack-file").on("change", function () { const file = this.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (e) => { try { const data = JSON.parse(e.target.result); soundManager.saveSoundpack(data); } catch { alert("Invalid JSON file."); } finally { this.value = ""; } }; reader.onerror = () => alert("Failed to read file."); reader.readAsText(file); }); $("#soundpack-submit").on("click", () => { const sel = $("#soundpack-select").val(); soundManager.setCurrentSoundpack(sel); hideAllModals(); }); $("#reset-soundpacks").on("click", () => { if ( confirm("Are you sure you want to reset your soundpacks?") && confirm("Are you absolutely sure you want to reset your soundpacks?") && confirm("ARE YOU TOTALLY ABSOLUTELY 100% SURE? THERE IS NO GOING BACK.") ) { soundManager.soundpacks = {}; builtin.forEach((sp) => soundManager.saveSoundpack(sp, true)); localStorage.savedSoundpacks = JSON.stringify(soundManager.soundpacks); alert("Successfully reset your soundpacks!"); soundManager.setCurrentSoundpack(defaultName); } }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址