@grammyjs/commands
Version:
grammY Commands Plugin
139 lines (138 loc) • 5.95 kB
JavaScript
;
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.command;
};
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 = commands.map((cmds) => cmds.toArgs(chat_id));
const commandsParams = serializedCommands
.map(({ scopes }) => scopes)
.flat();
const uncompliantCommands = serializedCommands
.map(({ uncompliantCommands }) => uncompliantCommands)
.flat();
return {
commandsParams: this.merge(commandsParams),
uncompliantCommands,
};
}
/**
* Iterates over an array of CommandsParams
* merging their respective {@link SetMyCommandsParams.commands}
* when they are from the same language and scope
*
* @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 merge(params) {
if (!params.length)
return [];
const map = new Map();
params.forEach((curr) => {
if (!curr.scope)
return;
const key = `${curr.scope.type},${curr.language_code}`;
const old = map.get(key);
if (old) {
curr.commands = curr.commands.concat(old.commands);
}
map.set(key, curr);
});
return [...map.values()];
}
}
exports.MyCommandParams = MyCommandParams;