slash-create-modify
Version:
Create and sync Discord slash commands!
133 lines (132 loc) • 6.14 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateID = exports.getFiles = exports.validateOptions = exports.oneLine = exports.formatAllowedMentions = exports.verifyKey = void 0;
const constants_1 = require("./constants");
const tweetnacl_1 = __importDefault(require("tweetnacl"));
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
/**
* Validates a payload from Discord against its signature and key.
*
* @param rawBody The raw payload data
* @param signature The signature from the `X-Signature-Ed25519` header
* @param timestamp The timestamp from the `X-Signature-Timestamp` header
* @param clientPublicKey The public key from the Discord developer dashboard
* @returns Whether or not validation was successful
*/
async function verifyKey(body, signature, timestamp, clientPublicKey) {
try {
return tweetnacl_1.default.sign.detached.verify(Buffer.from(timestamp + body), Buffer.from(signature, 'hex'), Buffer.from(clientPublicKey, 'hex'));
}
catch {
return false;
}
}
exports.verifyKey = verifyKey;
function formatAllowedMentions(allowed, defaultMentions) {
if (!allowed && defaultMentions)
return defaultMentions;
if ('parse' in allowed)
return allowed;
const result = {
parse: []
};
if (allowed.everyone)
result.parse.push('everyone');
if (allowed.roles === true)
result.parse.push('roles');
else if (Array.isArray(allowed.roles)) {
if (allowed.roles.length > 100)
throw new Error('Allowed role mentions cannot exceed 100.');
result.roles = allowed.roles;
}
if (allowed.users === true)
result.parse.push('users');
else if (Array.isArray(allowed.users)) {
if (allowed.users.length > 100)
throw new Error('Allowed user mentions cannot exceed 100.');
result.users = allowed.users;
}
return result;
}
exports.formatAllowedMentions = formatAllowedMentions;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function oneLine(strings, ..._) {
const l = arguments.length;
const substitutions = Array(l > 1 ? l - 1 : 0);
for (let k = 1; k < l; k++)
substitutions[k - 1] = arguments[k];
if (typeof strings === 'string')
return strings.replace(/(?:\n(?:\s*))+/g, ' ').trim();
return strings
.reduce((res, p) => ''.concat(res, substitutions.shift(), p))
.replace(/(?:\n(?:\s*))+/g, ' ')
.trim();
}
exports.oneLine = oneLine;
function validateOptions(options, prefix = 'options') {
function throwError(error = Error, index, reason, suffix = '') {
throw new error(`Command ${prefix}[${index}]${suffix}: ${reason}`);
}
for (let i = 0; i < options.length; i++) {
const option = options[i];
if (!option.type || !constants_1.CommandOptionType[option.type])
throwError(Error, i, 'Option type is invalid.');
if (typeof option.name !== 'string')
throwError(TypeError, i, 'Option name must be a string.');
if (option.name !== option.name.toLowerCase())
throwError(Error, i, 'Option name must be lowercase.');
if (!/^[\p{L}_\d-]{1,32}$/u.test(option.name))
throwError(RangeError, i, 'Option name must must be under 32 characters, matching this regex: /^[\\w-]{1,32}$/');
if (typeof option.description !== 'string')
throwError(TypeError, i, 'Option description must be a string.');
if (option.description.length < 1 || option.description.length > 100)
throwError(RangeError, i, 'Option description must be under 100 characters.');
if ('options' in option && option.options) {
if (option.type !== constants_1.CommandOptionType.SUB_COMMAND && option.type !== constants_1.CommandOptionType.SUB_COMMAND_GROUP)
throwError(Error, i, 'You cannot use the `options` field in options that are not sub-commands or sub-command groups!');
if (option.options.length > 25)
throwError(Error, i, 'The sub-command (group) options exceed 25 commands/options!');
validateOptions(option.options, `options[${i}].options`);
}
if ('choices' in option && option.choices) {
if (
// @ts-ignore
option.type === constants_1.CommandOptionType.SUB_COMMAND ||
// @ts-ignore
option.type === constants_1.CommandOptionType.SUB_COMMAND_GROUP ||
// @ts-ignore
option.type === constants_1.CommandOptionType.BOOLEAN)
throwError(Error, i, 'You cannot use the `choices` field in options that are sub-commands, sub-command groups or booleans!');
if (option.choices.length > 25)
throwError(Error, i, 'The choices exceed 25 commands/options!');
for (let ii = 0; ii < option.choices.length; ii++) {
const choice = option.choices[ii];
if (!choice.name || choice.name.length > 100)
throwError(RangeError, i, 'The choice name must be not exceed 100 characters!', `.choices[${ii}]`);
}
}
}
}
exports.validateOptions = validateOptions;
function getFiles(folderPath) {
const fileList = fs_1.default.readdirSync(folderPath);
const files = [];
for (const file of fileList) {
const filePath = path_1.default.join(folderPath, file);
const stat = fs_1.default.lstatSync(filePath);
if (stat.isDirectory())
files.push(...getFiles(filePath));
else
files.push(filePath);
}
return files;
}
exports.getFiles = getFiles;
function generateID() {
return (Date.now() + Math.round(Math.random() * 1000)).toString(36);
}
exports.generateID = generateID;