A library to simplify userscript creation for Multiplayer Piano.
Tento skript by nemal byť nainštalovaný priamo. Je to knižnica pre ďalšie skripty, ktorú by mali používať cez meta príkaz // @require https://update.greatest.deepsurf.us/scripts/582107/1849133/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 dm(target, msg) {
if (target == null)
throw new Error('Target must be provided.');
if (typeof target !== 'string')
throw new TypeError('Target must be a string.');
if (msg == null)
throw new Error('Message must be provided.');
if (typeof msg !== 'string')
throw new TypeError('Message must be a string.');
MPP.client.sendArray([{
m: 'dm',
message: msg,
_id: target
}]);
}
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 Object.entries(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 Object.entries(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 utils */
function setPrefix(newPrefix) {
if (prefix == null)
throw new Error('Prefix must be specified.');
if (typeof prefix !== 'string')
throw new TypeError('Prefix must be a string.');
return prefix = newPrefix;
}
function setPrivate(privacy) {
if (privacy == null)
throw new Error('Privacy must be specified.');
if (typeof prefix !== 'boolean')
throw new TypeError('Privacy must be a boolean.');
return private = privacy;
}
/* 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) {
dm(user._id, 'You do not have permission to use this bot.');
return;
}
// find command
let targetCommand = null;
for (const [name, info] of cmds) {
if (
name === cmd
||
info.aliases.includes(cmd)
) targetCommand = name;
}
// 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, prefix, private
});
} 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)}`);
}
});