UNPKG

@grammyjs/commands

Version:
177 lines (176 loc) 7.54 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MyCommandParams = void 0; exports.commands = commands; const array_js_1 = require("./utils/array.js"); const jaro_winkler_js_1 = require("./utils/jaro-winkler.js"); const set_bot_commands_js_1 = require("./utils/set-bot-commands.js"); /** * Installs the commands flavor into the context. */ function commands() { return (ctx, next) => { ctx.setMyCommands = async (commands, options) => { if (!ctx.chat) { throw new Error("cannot call `ctx.setMyCommands` on an update with no `chat` property"); } const { uncompliantCommands, commandsParams: currentChatCommandParams, } = MyCommandParams.from((0, array_js_1.ensureArray)(commands), ctx.chat.id); await (0, set_bot_commands_js_1.setBotCommands)(ctx.api, currentChatCommandParams, uncompliantCommands, options); }; ctx.getNearestCommand = (commands, options) => { if (!ctx.has(":text")) { throw new Error("cannot call `ctx.getNearestCommand` on an update with no `text`"); } const results = (0, array_js_1.ensureArray)(commands) .map((commands) => { var _a; const firstMatch = ctx.getCommandEntities(commands)[0]; const commandLike = (firstMatch === null || firstMatch === void 0 ? void 0 : firstMatch.text.replace(firstMatch.prefix, "")) || ""; const result = (0, jaro_winkler_js_1.fuzzyMatch)(commandLike, commands, { ...options, language: !(options === null || options === void 0 ? void 0 : options.ignoreLocalization) ? (_a = ctx.from) === null || _a === void 0 ? void 0 : _a.language_code : undefined, }); return result; }).sort((a, b) => { var _a, _b; return ((_a = b === null || b === void 0 ? void 0 : b.similarity) !== null && _a !== void 0 ? _a : 0) - ((_b = a === null || a === void 0 ? void 0 : a.similarity) !== null && _b !== void 0 ? _b : 0); }); const result = results[0]; if (!result || !result.command) return null; return result.command.prefix + result.command.name; }; ctx.getCommandEntities = (commands) => { if (!ctx.has(":text")) { throw new Error("cannot call `ctx.commandEntities` on an update with no `text`"); } const text = ctx.msg.text; if (!text) return []; const prefixes = (0, array_js_1.ensureArray)(commands).flatMap((cmds) => cmds.prefixes); if (!prefixes.length) return []; const regexes = prefixes.map((prefix) => (0, array_js_1.getCommandsRegex)(prefix)); const entities = regexes.flatMap((regex) => { let match; const matches = []; while ((match = regex.exec(text)) !== null) { const text = match[0].trim(); matches.push({ text, offset: match.index, prefix: match.groups.prefix, type: "bot_command", length: text.length, }); } return matches; }); return entities; }; return next(); }; } /** * Static class for getting and manipulating {@link SetMyCommandsParams}. * The main function is {@link from} */ class MyCommandParams { /** * Merges and serialize one or more Commands instances into a single array * of commands params that can be used to set the commands menu displayed to the user. * @example ```ts const adminCommands = new CommandGroup(); const userCommands = new CommandGroup(); adminCommands .command("do a", "a description", (ctx) => ctx.doA()); userCommands .command("do b", "b description", (ctx) => ctx.doB()); const mergedParams = MyCommandParams.from([a, b], someChatId); ``` * @param commands An array of one or more Commands instances. * @returns an array of {@link SetMyCommandsParams} grouped by language */ static from(commands, chat_id) { const serializedCommands = this._serialize(commands, chat_id); const commandsParams = serializedCommands .map(({ commandParams }) => commandParams) .flat(); const uncompliantCommands = serializedCommands .map(({ uncompliantCommands }) => uncompliantCommands) .flat(); return { commandsParams: this.mergeByLanguage(commandsParams), uncompliantCommands, }; } /** * Serializes one or multiple {@link CommandGroup} instances, each one into their respective * single scoped SetMyCommandsParams version. * @example ```ts const adminCommands = new CommandGroup(); // add to scope, localize, etc const userCommands = new CommandGroup(); // add to scope, localize, etc const [ singleScopedAdminParams, singleScopedUserParams ] = MyCommandsParams.serialize([adminCommands,userCommands]) ``` * @param commandsArr an array of one or more commands instances * @param chat_id the chat id relative to the message update, coming from the ctx object. * @returns an array of scoped {@link SetMyCommandsParams} mapped from their respective Commands instances */ static _serialize(commandsArr, chat_id) { return commandsArr.map((commands) => commands.toSingleScopeArgs({ type: "chat", chat_id, })); } /** * Lexicographically sorts commandParams based on their language code. * @returns the sorted array */ static _sortByLanguage(params) { return params.sort((a, b) => { if (!a.language_code) return -1; if (!b.language_code) return 1; return a.language_code.localeCompare(b.language_code); }); } /** * Iterates over an array of CommandsParams * merging their respective {@link SetMyCommandsParams.commands} * when they are from the same language, separating when they are not. * * @param params a flattened array of commands params coming from one or more Commands instances * @returns an array containing all commands grouped by language */ static mergeByLanguage(params) { if (!params.length) return []; const sorted = this._sortByLanguage(params); return sorted.reduce((result, current, i, arr) => { if (i === 0 || current.language_code !== arr[i - 1].language_code) { result.push(current); return result; } else { result[result.length - 1].commands = result[result.length - 1] .commands .concat(current.commands); return result; } }, []); } } exports.MyCommandParams = MyCommandParams;