Greasy Fork is available in English.
Play sounds when users join, leave, or mention you in Multiplayer Piano
当前为
// ==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
// ==/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(`<option value="${name}"${selAttr}>${name}</option>`);
});
}
}
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 = $(`<div id="sounds-btn" class="ugly-button">MPP Sounds</div>`);
$("#buttons").append($btn);
const $modal = $(`
<div id="sound-modal" class="dialog" style="height: 200px; margin-top: -100px;">
<h3>MPP Sounds</h3><hr>
<p><label>Select pack: <select id="soundpack-select" class="text"></select></label></p>
<p><label>Import JSON file: <input type="file" id="soundpack-file" accept=".json" /></label></p>
<p><button id="set-soundpack" class="submit">SUBMIT</button></p>
</div>
`);
$("#modal #modals").append($modal);
function hideModals() {
$("#modal #modals > *").hide();
$("#modal").hide();
}
function showModal() {
if (MPP.chat) MPP.chat.blur();
hideModals();
soundManager.updateDropdown();
$("#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 json = JSON.parse(e.target.result);
soundManager.saveSoundPack(json);
} catch (err) {
alert("Invalid JSON file.");
} finally {
this.value = "";
}
};
reader.onerror = () => alert("Failed to read file.");
reader.readAsText(file);
});
$("#set-soundpack").on("click", () => {
const name = $("#soundpack-select").val();
soundManager.setDefaultSoundPack(name);
hideModals();
});
})();