// ==UserScript== // @name Multiplayer Piano Optimizations [Sounds] // @namespace https://tampermonkey.net/ // @version 1.1.0 // @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 // @downloadURL none // ==/UserScript== (async () => { function injectScript(src) { return new Promise((resolve, reject) => { const s = document.createElement("script"); s.src = src; s.async = false; s.onload = resolve; s.onerror = e => reject(e); document.head.appendChild(s); }); } await injectScript("https://code.jquery.com/ui/1.12.1/jquery-ui.js"); class SoundManager { constructor(version) { this.version = version; this.GAP_MS = 200; this.volume = 100; this.lastPlayed = {}; this.loadPacks(); } loadPacks() { const defaultPacks = { "Default": { MENTION: "https://files.catbox.moe/f5tzag.mp3", JOIN: "https://files.catbox.moe/t3ztlz.mp3", LEAVE: "https://files.catbox.moe/kmpz7e.mp3" }, "PRISM": { MENTION: "https://file.garden/aHFDYCLeNBLSceNi/Swoosh.wav", JOIN: "https://file.garden/aHFDYCLeNBLSceNi/Plug%20In.wav", LEAVE: "https://file.garden/aHFDYCLeNBLSceNi/Plug%20Out.wav" }, "Win11": { 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" }, "Discord": { MENTION: "https://file.garden/aHFDYCLeNBLSceNi/Discord%20Ping.mp3", JOIN: "https://file.garden/aHFDYCLeNBLSceNi/Discord%20Join.mp3", LEAVE: "https://file.garden/aHFDYCLeNBLSceNi/Discord%20Leave.mp3" } }; const stored = localStorage.getItem('savedSoundPacks'); if (stored) { try { this.packs = JSON.parse(stored); } catch (e) { console.error("Invalid saved sound packs, resetting to defaults.", e); this.packs = defaultPacks; } } else { this.packs = defaultPacks; } localStorage.setItem('savedSoundPacks', JSON.stringify(this.packs)); this.defaultPack = localStorage.getItem('defaultSoundPack') || 'Default'; if (!this.packs[this.defaultPack]) this.defaultPack = 'Default'; this.currentPack = this.defaultPack; this.SOUNDS = this.packs[this.currentPack]; } saveSoundPack({ NAME, MENTION, JOIN, LEAVE }) { if (!NAME || !MENTION || !JOIN || !LEAVE) { alert("All fields (NAME, MENTION, JOIN, LEAVE) are required."); return; } for (const [existingName, pack] of Object.entries(this.packs)) { if (pack.MENTION === MENTION && pack.JOIN === JOIN && pack.LEAVE === LEAVE) { alert(`Sound pack matches existing pack "${existingName}".`); return; } } let uniqueName = NAME; let i = 1; while (this.packs[uniqueName]) { uniqueName = `${NAME} (${i++})`; } this.packs[uniqueName] = { MENTION, JOIN, LEAVE }; localStorage.setItem('savedSoundPacks', JSON.stringify(this.packs)); alert(`Sound pack "${uniqueName}" imported successfully.`); this.updateDropdown(); } setSoundPack(name) { if (this.packs[name]) { this.SOUNDS = this.packs[name]; this.currentPack = name; } } setDefaultSoundPack(name) { if (this.packs[name]) { localStorage.setItem('defaultSoundPack', name); this.defaultPack = name; this.setSoundPack(name); } } play(src) { const now = Date.now(); if (!this.lastPlayed[src] || now - this.lastPlayed[src] >= this.GAP_MS) { this.lastPlayed[src] = now; const audio = new Audio(src); audio.volume = this.volume / 100; audio.play().catch(() => {}); } } updateDropdown() { const sel = $("#soundpack-select").empty(); Object.keys(this.packs).forEach(name => { const selAttr = name === this.currentPack ? ' selected' : ''; sel.append(``); }); } } const soundManager = new SoundManager(GM_info.script.version); let replyMap = {}; let users = {}; function handleMessage(msg) { const sender = msg.p ?? msg.sender; replyMap[msg.id] = sender._id; const you = MPP.client.user._id; const isMention = msg.a.includes(`@${you}`); const isReplyToYou = msg.r && replyMap[msg.r] === you; if ((isMention || isReplyToYou) && !document.hasFocus()) { soundManager.play(soundManager.SOUNDS.MENTION); } } MPP.client.on("a", handleMessage); MPP.client.on("dm", handleMessage); 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]; }); MPP.client.on("c", () => { MPP.chat.sendPrivate({ name: `[MPP Sounds] v${soundManager.version}`, color: "#ffaa00", message: "Sound alerts loaded." }); }); const $btn = $(`