A library to simplify userscript creation for Multiplayer Piano.
Version vom
Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greatest.deepsurf.us/scripts/582107/1848141/MPP%20Userscript%20Core.js
/* command registry */
const cmds = {};
function registerCommand(name, func, {...options} = {}) {
if (!name)
throw new Error('Command name must be provided.');
if (typeof name !== 'string')
throw new TypeError('Command name must be a string.');
if (func == null)
throw new Error('Command function must be provided.');
if (typeof func !== 'function');
throw new TypeError('Command function must be a function.');
cmds[name] = {
...options,
func
}
return cmds[name];
}
/* utilities */
function send(msg) {
MPP.chat.send(msg);
}
function receive(msg, user = MPP.client.getOwnParticipant(), date = Date.now()) {
MPP.chat.receive({
m: 'a',
a: msg,
p: user,
t: date
});
}
function storeItem(key, data) {
if (!key)
throw new Error('Item key must be provided.');
if (typeof key !== 'string')
throw new TypeError('Item key must be a string.');
return localStorage[key] = JSON.stringify(data);
}
function readItem(key, fallback = null) {
if (!key)
throw new Error('Item key must be provided.');
if (typeof key !== 'string')
throw new TypeError('Item key must be a string.');
return localStorage[key] != null ? JSON.parse(localStorage[key]) : fallback;
}
function findUsers(query) {
const normalize = text => text.toLowerCase().replace(/[^a-z0-9 -_]+/, '');
return Object.values(MPP.client.ppl).filter(
v => normalize(v.name).includes(normalize(query)) ||
normalize(v._id).includes(normalize(query))
);
}
function findUser(query) {
return findUsers(query)?.[0];
}
/* ranks */
const ranks = {
user: 0
};
const userRanks = readItem('userRanks', {});
function clearUserRank(targetUserID) {
for (const [name, rank] of ranks) {
userRanks[rank] =
userRanks[rank].filter(userID => userID !== targetUserID);
if (userRanks[rank].length === 0)
delete userRanks[rank];
}
storeItem('userRanks', userRanks);
}
function getUserRank(userID) {
let foundRank = -Infinity;
for (const [name, rank] of ranks) {
if (
userRanks.includes(userID)
&&
rank > foundRank
) foundRank = rank;
}
if (!Number.isFinite(foundRank)) foundRank = ranks.user;
return foundRank;
}
function setUserRank(userID, rank) {
// short-circuiting
if (rank == null)
throw new Error('Rank ID or name is required.');
if (typeof rank !== 'number' && typeof rank !== 'string')
throw new TypeError('Rank must be a string or a number.');
// coercion
if (typeof rank === 'string') ranks[rank];
if (!Object.values(ranks).includes(rank))
throw new ReferenceError(`Unknown rank ${JSON.stringify(rank)}`);
clearUserRank(userID);
userRanks[rank] ??= [];
userRanks[rank].push(userID);
storeItem('userRanks', userRanks);
}
/* command handler */
let prefix = '/';
let private = false;
MPP.client.on('a', async event => {
// event metadata
const args = event.a.split(' ');
const user = event.p;
const userRank = getUserRank(user._id);
const rawcmd = args[0];
const fullcmd = rawcmd.toLowerCase();
const cmd = fullcmd.substring(prefix.length);
args.shift();
// handler short-circuit cases
if (!fullcmd.startsWith(prefix)) return;
if (private && user._id !== MPP.client.getOwnParticipant()._id) return;
// find command
let targetCommand;
if (cmds[cmd] != null) targetCommand = cmds[cmd];
else for (const [name, info] of cmds) {
if (info.aliases.includes(cmd)) targetCommand = info;
}
// executor short-circuit cases
if (targetCommand == null) {
send(`The command \`${prefix + cmd}\` does not exist.`);
return;
}
if ((targetCommand?.rank ?? ranks.user) > userRank) {
send('You do not have permission to use this command.');
return;
}
// execute command
try {
await targetCommand.func({
user, args, cmd, fullcmd, rawcmd
});
} catch (error) {
if (Error.isError(error)) {
const errorLocation = [.../(\d+):(\d+)/.exec(new Error().stack)].slice(1,3);
const line = errorLocation[0];
const col = errorLocation[1];
send(`❌ Uncaught ${error.name} at line ${line} column ${col}: ${error.msg}`);
} else
send(`❌ Uncaught RawThrow: ${JSON.stringify(error)}`);
}
});