UNPKG

slash-create-modify

Version:

Create and sync Discord slash commands!

133 lines (132 loc) 6.14 kB
"use strict"; 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;