@grammyjs/commands 0.8.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,22 +16,22 @@ The main functionality of this plugin is to define your commands, localize them,
16
16
 
17
17
  ```typescript
18
18
  import { Bot } from "grammy";
19
- import { Commands } from "@grammyjs/commands";
19
+ import { CommandGroup } from "@grammyjs/commands";
20
20
 
21
21
  const bot = new Bot("<telegram token>");
22
22
 
23
- const myCommands = new Commands();
23
+ const myCommands = new CommandGroup();
24
24
 
25
25
  myCommands.command("start", "Initializes bot configuration")
26
- .localize("pt", "start", "Inicializa as configurações do bot")
27
- .addToScope(
28
- { type: "all_private_chats" },
29
- (ctx) => ctx.reply(`Hello, ${ctx.chat.first_name}!`),
30
- )
31
- .addToScope(
32
- { type: "all_group_chats" },
33
- (ctx) => ctx.reply(`Hello, members of ${ctx.chat.title}!`),
34
- );
26
+ .localize("pt", "start", "Inicializa as configurações do bot")
27
+ .addToScope(
28
+ { type: "all_private_chats" },
29
+ (ctx) => ctx.reply(`Hello, ${ctx.chat.first_name}!`),
30
+ )
31
+ .addToScope(
32
+ { type: "all_group_chats" },
33
+ (ctx) => ctx.reply(`Hello, members of ${ctx.chat.title}!`),
34
+ );
35
35
 
36
36
  // Calls `setMyCommands`
37
37
  await myCommands.setCommands(bot);
@@ -52,7 +52,7 @@ commands flavor and the plugin itself, like so:
52
52
 
53
53
  ```typescript
54
54
  import { Bot, Context } from "grammy";
55
- import { Commands, commands, CommandsFlavor } from "@grammyjs/commands";
55
+ import { CommandGroup, commands, CommandsFlavor } from "@grammyjs/commands";
56
56
 
57
57
  type BotContext = CommandsFlavor;
58
58
 
@@ -60,14 +60,14 @@ const bot = new Bot<BotContext>("<telegram_token>");
60
60
  bot.use(commands());
61
61
 
62
62
  bot.on("message", async (ctx) => {
63
- const cmds = new Commands();
63
+ const cmds = new CommandGroup();
64
64
 
65
- cmds.command("start", "Initializes bot configuration")
66
- .localize("pt", "start", "Inicializa as configurações do bot");
65
+ cmds.command("start", "Initializes bot configuration")
66
+ .localize("pt", "start", "Inicializa as configurações do bot");
67
67
 
68
- await ctx.setMyCommands(cmds);
68
+ await ctx.setMyCommands(cmds);
69
69
 
70
- return ctx.reply("Commands set!");
70
+ return ctx.reply("Commands set!");
71
71
  });
72
72
 
73
73
  bot.start();
@@ -1,12 +1,14 @@
1
- import { Command } from "./command.js";
1
+ import { Command, CommandsFlavor } from "./mod.js";
2
2
  import { Api, BotCommand, BotCommandScope, CommandContext, Context, type LanguageCode, Middleware } from "./deps.node.js";
3
3
  import type { CommandElementals, CommandOptions } from "./types.js";
4
- import { type MaybeArray } from "./utils.js";
4
+ import { type MaybeArray } from "./utils/array.js";
5
+ import { SetBotCommandsOptions } from "./utils/set-bot-commands.js";
6
+ import { JaroWinklerOptions } from "./utils/jaro-winkler.js";
5
7
  /**
6
8
  * Interface for grouping {@link BotCommand}s that might (or not)
7
9
  * be related to each other by scope and/or language.
8
10
  */
9
- export type SetMyCommandsParams = {
11
+ export interface SetMyCommandsParams {
10
12
  /** If defined: scope on which the commands will take effect */
11
13
  scope?: BotCommandScope;
12
14
  /** If defined: Language on which the commands will take effect.
@@ -15,21 +17,33 @@ export type SetMyCommandsParams = {
15
17
  language_code?: LanguageCode;
16
18
  /** Commands that can be each one passed to a SetMyCommands Call */
17
19
  commands: BotCommand[];
18
- };
20
+ }
21
+ /**
22
+ * Interface to represent uncompliance of a command
23
+ * with the Bot API
24
+ */
25
+ export interface UncompliantCommand {
26
+ /** Name of the uncompliant command */
27
+ name: string;
28
+ /** Reason why the command was considered uncompliant */
29
+ reasons: string[];
30
+ /** Language in which the command is uncompliant */
31
+ language: LanguageCode | "default";
32
+ }
19
33
  /**
20
34
  * Central class that manages all registered commands.
21
35
  * This is the starting point for the plugin, and this is what you should pass to `bot.use` so your commands get properly registered.
22
36
  *
23
37
  * @example
24
38
  * ```typescript
25
- * const myCommands = new Commands()
39
+ * const myCommands = new CommandGroup()
26
40
  * commands.command("start", "start the bot configuration", (ctx) => ctx.reply("Hello there!"))
27
41
  *
28
42
  * // Registers the commands with the bot instance.
29
43
  * bot.use(myCommands)
30
44
  * ```
31
45
  */
32
- export declare class Commands<C extends Context> {
46
+ export declare class CommandGroup<C extends Context> {
33
47
  private _languages;
34
48
  private _scopes;
35
49
  private _commands;
@@ -58,16 +72,23 @@ export declare class Commands<C extends Context> {
58
72
  command(name: string | RegExp, description: string, options?: Partial<CommandOptions>): Command<C>;
59
73
  /**
60
74
  * Serializes the commands into multiple objects that can each be passed to a `setMyCommands` call.
75
+ *
61
76
  * @returns One item for each combination of command + scope + language
62
77
  */
63
- toArgs(): SetMyCommandsParams[];
78
+ toArgs(): {
79
+ scopes: SetMyCommandsParams[];
80
+ uncompliantCommands: UncompliantCommand[];
81
+ };
64
82
  /**
65
83
  * Serializes the commands of a single scope into objects that can each be passed to a `setMyCommands` call.
66
84
  *
67
85
  * @param scope Selected scope to be serialized
68
86
  * @returns One item per command per language
69
87
  */
70
- toSingleScopeArgs(scope: BotCommandScope): SetMyCommandsParams[];
88
+ toSingleScopeArgs(scope: BotCommandScope): {
89
+ commandParams: SetMyCommandsParams[];
90
+ uncompliantCommands: UncompliantCommand[];
91
+ };
71
92
  /**
72
93
  * Registers all commands to be displayed by clients according to their scopes and languages
73
94
  * Calls `setMyCommands` for each language of each scope of each command.
@@ -81,7 +102,7 @@ export declare class Commands<C extends Context> {
81
102
  */
82
103
  setCommands({ api }: {
83
104
  api: Api;
84
- }): Promise<void>;
105
+ }, options?: Partial<SetBotCommandsOptions>): Promise<void>;
85
106
  /**
86
107
  * Serialize all register commands into it's name, prefix and language
87
108
  *
@@ -99,4 +120,17 @@ export declare class Commands<C extends Context> {
99
120
  */
100
121
  toString(): string;
101
122
  middleware(): import("grammy").MiddlewareFn<C>;
123
+ /**
124
+ * @returns all {@link Command}s contained in the instance
125
+ */
126
+ get commands(): Command<C>[];
127
+ /**
128
+ * @returns all prefixes registered in this instance
129
+ */
130
+ get prefixes(): string[];
102
131
  }
132
+ type HaveCommandLike<C extends Context = Context, CF extends CommandsFlavor<C> = CommandsFlavor<C>> = C & CF & {
133
+ commandSuggestion: string | null;
134
+ };
135
+ export declare function commandNotFound<CF extends CommandsFlavor<C>, C extends Context = Context>(commands: CommandGroup<C> | CommandGroup<C>[], opts?: Omit<Partial<JaroWinklerOptions>, "language">): (ctx: C) => ctx is HaveCommandLike<C, CF>;
136
+ export {};
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Commands = void 0;
4
- const command_js_1 = require("./command.js");
3
+ exports.commandNotFound = exports.CommandGroup = void 0;
4
+ const mod_js_1 = require("./mod.js");
5
5
  const deps_node_js_1 = require("./deps.node.js");
6
+ const array_js_1 = require("./utils/array.js");
7
+ const set_bot_commands_js_1 = require("./utils/set-bot-commands.js");
6
8
  const isMiddleware = (obj) => {
7
9
  if (!obj)
8
10
  return false;
@@ -23,14 +25,14 @@ const isMiddleware = (obj) => {
23
25
  *
24
26
  * @example
25
27
  * ```typescript
26
- * const myCommands = new Commands()
28
+ * const myCommands = new CommandGroup()
27
29
  * commands.command("start", "start the bot configuration", (ctx) => ctx.reply("Hello there!"))
28
30
  *
29
31
  * // Registers the commands with the bot instance.
30
32
  * bot.use(myCommands)
31
33
  * ```
32
34
  */
33
- class Commands {
35
+ class CommandGroup {
34
36
  constructor(options = {}) {
35
37
  this._languages = new Set();
36
38
  this._scopes = new Map();
@@ -65,7 +67,7 @@ class Commands {
65
67
  const options = handler
66
68
  ? _options !== null && _options !== void 0 ? _options : this._commandOptions
67
69
  : (_a = handlerOrOptions) !== null && _a !== void 0 ? _a : this._commandOptions;
68
- const command = new command_js_1.Command(name, description, options);
70
+ const command = new mod_js_1.Command(name, description, options);
69
71
  if (handler)
70
72
  command.addToScope({ type: "default" }, handler);
71
73
  this._commands.push(command);
@@ -74,25 +76,40 @@ class Commands {
74
76
  }
75
77
  /**
76
78
  * Serializes the commands into multiple objects that can each be passed to a `setMyCommands` call.
79
+ *
77
80
  * @returns One item for each combination of command + scope + language
78
81
  */
79
82
  toArgs() {
80
83
  this._populateMetadata();
81
- const params = [];
84
+ const scopes = [];
85
+ const uncompliantCommands = [];
82
86
  for (const [scope, commands] of this._scopes.entries()) {
83
87
  for (const language of this._languages) {
84
- params.push({
85
- scope: JSON.parse(scope),
86
- language_code: language === "default"
87
- ? undefined
88
- : language,
89
- commands: commands
90
- .filter((command) => typeof command.name === "string")
91
- .map((command) => command.toObject(language)),
88
+ const compliantScopedCommands = [];
89
+ commands.forEach((command) => {
90
+ const [isApiCompliant, ...reasons] = command.isApiCompliant(language);
91
+ if (isApiCompliant) {
92
+ return compliantScopedCommands.push(command);
93
+ }
94
+ uncompliantCommands.push({
95
+ name: command.stringName,
96
+ reasons: reasons,
97
+ language,
98
+ });
92
99
  });
100
+ if (compliantScopedCommands.length) {
101
+ scopes.push({
102
+ scope: JSON.parse(scope),
103
+ language_code: language === "default" ? undefined : language,
104
+ commands: compliantScopedCommands.map((command) => command.toObject(language)),
105
+ });
106
+ }
93
107
  }
94
108
  }
95
- return params.filter((params) => params.commands.length > 0);
109
+ return {
110
+ scopes,
111
+ uncompliantCommands,
112
+ };
96
113
  }
97
114
  /**
98
115
  * Serializes the commands of a single scope into objects that can each be passed to a `setMyCommands` call.
@@ -102,18 +119,29 @@ class Commands {
102
119
  */
103
120
  toSingleScopeArgs(scope) {
104
121
  this._populateMetadata();
105
- const params = [];
122
+ const commandParams = [];
123
+ const uncompliantCommands = [];
106
124
  for (const language of this._languages) {
107
- params.push({
125
+ const compliantCommands = [];
126
+ this._commands.forEach((command) => {
127
+ const [isApiCompliant, ...reasons] = command.isApiCompliant(language);
128
+ if (!isApiCompliant) {
129
+ return uncompliantCommands.push({
130
+ name: command.stringName,
131
+ reasons: reasons,
132
+ language,
133
+ });
134
+ }
135
+ if (command.scopes.length)
136
+ compliantCommands.push(command);
137
+ });
138
+ commandParams.push({
108
139
  scope,
109
140
  language_code: language === "default" ? undefined : language,
110
- commands: this._commands
111
- .filter((command) => command.scopes.length)
112
- .filter((command) => typeof command.name === "string")
113
- .map((command) => command.toObject(language)),
141
+ commands: compliantCommands.map((command) => command.toObject(language)),
114
142
  });
115
143
  }
116
- return params;
144
+ return { commandParams, uncompliantCommands };
117
145
  }
118
146
  /**
119
147
  * Registers all commands to be displayed by clients according to their scopes and languages
@@ -126,8 +154,9 @@ class Commands {
126
154
  *
127
155
  * @param Instance of `bot` or { api: bot.api }
128
156
  */
129
- async setCommands({ api }) {
130
- await Promise.all(this.toArgs().map((args) => api.raw.setMyCommands(args)));
157
+ async setCommands({ api }, options) {
158
+ const { scopes, uncompliantCommands } = this.toArgs();
159
+ await (0, set_bot_commands_js_1.setBotCommands)(api, scopes, uncompliantCommands, options);
131
160
  }
132
161
  /**
133
162
  * Serialize all register commands into it's name, prefix and language
@@ -180,7 +209,21 @@ class Commands {
180
209
  return this._cachedComposer.middleware();
181
210
  }
182
211
  /**
183
- * Replaces the `toString` method on node.js
212
+ * @returns all {@link Command}s contained in the instance
213
+ */
214
+ get commands() {
215
+ return this._commands;
216
+ }
217
+ /**
218
+ * @returns all prefixes registered in this instance
219
+ */
220
+ get prefixes() {
221
+ return [
222
+ ...new Set(this._commands.flatMap((command) => command.prefix)),
223
+ ];
224
+ }
225
+ /**
226
+ * Replaces the `toString` method on Deno
184
227
  *
185
228
  * @see toString
186
229
  */
@@ -188,7 +231,7 @@ class Commands {
188
231
  return this.toString();
189
232
  }
190
233
  /**
191
- * Replaces the `toString` method on Deno
234
+ * Replaces the `toString` method on Node.js
192
235
  *
193
236
  * @see toString
194
237
  */
@@ -196,4 +239,30 @@ class Commands {
196
239
  return this.toString();
197
240
  }
198
241
  }
199
- exports.Commands = Commands;
242
+ exports.CommandGroup = CommandGroup;
243
+ function commandNotFound(commands, opts = {}) {
244
+ return function (ctx) {
245
+ if (containsCommands(ctx, commands)) {
246
+ ctx
247
+ .commandSuggestion = ctx
248
+ .getNearestCommand(commands, opts);
249
+ return true;
250
+ }
251
+ return false;
252
+ };
253
+ }
254
+ exports.commandNotFound = commandNotFound;
255
+ function containsCommands(ctx, commands) {
256
+ let allPrefixes = [
257
+ ...new Set((0, array_js_1.ensureArray)(commands).flatMap((cmds) => cmds.prefixes)),
258
+ ];
259
+ if (allPrefixes.length < 1) {
260
+ allPrefixes = ["/"];
261
+ }
262
+ for (const prefix of allPrefixes) {
263
+ const regex = (0, array_js_1.getCommandsRegex)(prefix);
264
+ if (ctx.hasText(regex))
265
+ return true;
266
+ }
267
+ return false;
268
+ }
package/out/command.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { type BotCommand, type BotCommandScope, type BotCommandScopeAllChatAdministrators, type BotCommandScopeAllGroupChats, type BotCommandScopeAllPrivateChats, type ChatTypeMiddleware, type Context, type LanguageCode, type Middleware, type MiddlewareObj } from "./deps.node.js";
2
2
  import type { CommandOptions } from "./types.js";
3
- import { type MaybeArray } from "./utils.js";
3
+ import { type MaybeArray } from "./utils/array.js";
4
4
  type BotCommandGroupsScope = BotCommandScopeAllGroupChats | BotCommandScopeAllChatAdministrators;
5
- export declare const matchesPattern: (value: string, pattern: string | RegExp) => boolean;
5
+ export declare const matchesPattern: (value: string, pattern: string | RegExp, ignoreCase?: boolean) => boolean;
6
6
  /**
7
7
  * Class that represents a single command and allows you to configure it.
8
8
  */
@@ -21,6 +21,22 @@ export declare class Command<C extends Context = Context> implements MiddlewareO
21
21
  * @access package
22
22
  */
23
23
  constructor(name: string | RegExp, description: string, options?: Partial<CommandOptions>);
24
+ /**
25
+ * Whether the command has a custom prefix
26
+ */
27
+ get hasCustomPrefix(): boolean | "";
28
+ /**
29
+ * Gets the command name as string
30
+ */
31
+ get stringName(): string;
32
+ /**
33
+ * Whether the command can be passed to a `setMyCommands` API call
34
+ * and, if not, the reason.
35
+ */
36
+ isApiCompliant(language?: LanguageCode | "default"): [result: true] | [
37
+ result: false,
38
+ ...reasons: string[]
39
+ ];
24
40
  /**
25
41
  * Get registered scopes for this command
26
42
  */
@@ -54,7 +70,7 @@ export declare class Command<C extends Context = Context> implements MiddlewareO
54
70
  *
55
71
  * @example
56
72
  * ```ts
57
- * const myCommands = new Commands();
73
+ * const myCommands = new CommandGroup();
58
74
  * myCommands.command("start", "Initializes bot configuration")
59
75
  * .addToScope(
60
76
  * { type: "all_private_chats" },
package/out/command.js CHANGED
@@ -2,13 +2,22 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Command = exports.matchesPattern = void 0;
4
4
  const deps_node_js_1 = require("./deps.node.js");
5
- const errors_js_1 = require("./errors.js");
6
- const utils_js_1 = require("./utils.js");
5
+ const errors_js_1 = require("./utils/errors.js");
6
+ const array_js_1 = require("./utils/array.js");
7
7
  const isAdmin = (ctx) => ctx
8
8
  .getAuthor()
9
9
  .then((author) => ["administrator", "creator"].includes(author.status));
10
- const matchesPattern = (value, pattern) => typeof pattern === "string" ? value === pattern : pattern.test(value);
10
+ const matchesPattern = (value, pattern, ignoreCase = false) => {
11
+ const transformedValue = ignoreCase ? value.toLowerCase() : value;
12
+ const transformedPattern = pattern instanceof RegExp && ignoreCase && !pattern.flags.includes("i")
13
+ ? new RegExp(pattern, pattern.flags + "i")
14
+ : pattern;
15
+ return typeof transformedPattern === "string"
16
+ ? transformedValue === transformedPattern
17
+ : transformedPattern.test(transformedValue);
18
+ };
11
19
  exports.matchesPattern = matchesPattern;
20
+ const NOCASE_COMMAND_NAME_REGEX = /^[0-9a-z_]+$/i;
12
21
  /**
13
22
  * Class that represents a single command and allows you to configure it.
14
23
  */
@@ -30,12 +39,51 @@ class Command {
30
39
  prefix: "/",
31
40
  matchOnlyAtStart: true,
32
41
  targetedCommands: "optional",
42
+ ignoreCase: false,
33
43
  };
34
44
  this._options = { ...this._options, ...options };
35
45
  if (this._options.prefix === "")
36
46
  this._options.prefix = "/";
37
47
  this._languages.set("default", { name: name, description });
38
48
  }
49
+ /**
50
+ * Whether the command has a custom prefix
51
+ */
52
+ get hasCustomPrefix() {
53
+ return this.prefix && this.prefix !== "/";
54
+ }
55
+ /**
56
+ * Gets the command name as string
57
+ */
58
+ get stringName() {
59
+ return typeof this.name === "string" ? this.name : this.name.source;
60
+ }
61
+ /**
62
+ * Whether the command can be passed to a `setMyCommands` API call
63
+ * and, if not, the reason.
64
+ */
65
+ isApiCompliant(language) {
66
+ const problems = [];
67
+ if (this.hasCustomPrefix) {
68
+ problems.push(`Command has custom prefix: ${this._options.prefix}`);
69
+ }
70
+ const name = language ? this.getLocalizedName(language) : this.name;
71
+ if (typeof name !== "string") {
72
+ problems.push("Command has a regular expression name");
73
+ }
74
+ if (typeof name === "string") {
75
+ if (name.toLowerCase() !== name) {
76
+ problems.push("Command name has uppercase characters");
77
+ }
78
+ if (name.length > 32) {
79
+ problems.push(`Command name is too long (${name.length} characters). Maximum allowed is 32 characters`);
80
+ }
81
+ if (!NOCASE_COMMAND_NAME_REGEX.test(name)) {
82
+ problems.push(`Command name has special characters (${name.replace(/[0-9a-z_]/ig, "")}). Only letters, digits and _ are allowed`);
83
+ }
84
+ }
85
+ return problems.length ? [false, ...problems] : [true];
86
+ }
39
87
  /**
40
88
  * Get registered scopes for this command
41
89
  */
@@ -73,7 +121,7 @@ class Command {
73
121
  return this._options.prefix;
74
122
  }
75
123
  addToScope(scope, middleware, options = this._options) {
76
- const middlewareArray = (0, utils_js_1.ensureArray)(middleware);
124
+ const middlewareArray = (0, array_js_1.ensureArray)(middleware);
77
125
  const optionsObject = { ...this._options, ...options };
78
126
  switch (scope.type) {
79
127
  case "default":
@@ -148,7 +196,7 @@ class Command {
148
196
  if (matchOnlyAtStart && !ctx.msg.text.startsWith(prefix)) {
149
197
  return false;
150
198
  }
151
- const commandNames = (0, utils_js_1.ensureArray)(command);
199
+ const commandNames = (0, array_js_1.ensureArray)(command);
152
200
  const commands = prefix === "/"
153
201
  ? ctx.entities("bot_command")
154
202
  : ctx.msg.text.split(prefix).map((text) => ({ text }));
@@ -160,7 +208,7 @@ class Command {
160
208
  continue;
161
209
  if (username && username !== ctx.me.username)
162
210
  continue;
163
- if (commandNames.some((name) => (0, exports.matchesPattern)(command.replace(prefix, ""), name))) {
211
+ if (commandNames.some((name) => (0, exports.matchesPattern)(command.replace(prefix, ""), name, options.ignoreCase))) {
164
212
  return true;
165
213
  }
166
214
  }
package/out/context.d.ts CHANGED
@@ -1,7 +1,9 @@
1
- import { Commands } from "./commands.js";
1
+ import { CommandGroup } from "./command-group.js";
2
2
  import { BotCommandScopeChat, Context, NextFunction } from "./deps.node.js";
3
- import { JaroWinklerOptions } from "./jaro-winkler.js";
4
3
  import { SetMyCommandsParams } from "./mod.js";
4
+ import { BotCommandEntity } from "./types.js";
5
+ import { JaroWinklerOptions } from "./utils/jaro-winkler.js";
6
+ import { SetBotCommandsOptions } from "./utils/set-bot-commands.js";
5
7
  export interface CommandsFlavor<C extends Context = Context> extends Context {
6
8
  /**
7
9
  * Sets the provided commands for the current chat.
@@ -25,7 +27,7 @@ export interface CommandsFlavor<C extends Context = Context> extends Context {
25
27
  * @param commands List of available commands
26
28
  * @returns Promise with the result of the operations
27
29
  */
28
- setMyCommands: (commands: Commands<C> | Commands<C>[], ...moreCommands: Commands<C>[]) => Promise<void>;
30
+ setMyCommands: (commands: CommandGroup<C> | CommandGroup<C>[], options?: SetBotCommandsOptions) => Promise<void>;
29
31
  /**
30
32
  * Returns the nearest command to the user input.
31
33
  * If no command is found, returns `null`.
@@ -34,7 +36,12 @@ export interface CommandsFlavor<C extends Context = Context> extends Context {
34
36
  * @param options Options for the Jaro-Winkler algorithm
35
37
  * @returns The nearest command or `null`
36
38
  */
37
- getNearestCommand: (commands: Commands<C> | Commands<C>[], options?: Omit<Partial<JaroWinklerOptions>, "language">) => string | null;
39
+ getNearestCommand: (commands: CommandGroup<C> | CommandGroup<C>[], options?: Omit<Partial<JaroWinklerOptions>, "language">) => string | null;
40
+ /**
41
+ * @param commands
42
+ * @returns command entities hydrated with the custom prefixes
43
+ */
44
+ getCommandEntities: (commands: CommandGroup<C> | CommandGroup<C>[]) => BotCommandEntity[];
38
45
  }
39
46
  /**
40
47
  * Installs the commands flavor into the context.
@@ -46,46 +53,52 @@ export declare function commands<C extends Context>(): (ctx: CommandsFlavor<C>,
46
53
  */
47
54
  export declare class MyCommandParams {
48
55
  /**
49
- * Merges and serialize one or more Commands instances into a single array
50
- * of commands params that can be used to set the commands menu displayed to the user.
51
- * @example
52
- ```ts
53
- const adminCommands = new Commands();
54
- const userCommands = new Commands();
55
- adminCommands
56
- .command("do a",
57
- "a description",
58
- (ctx) => ctx.doA());
59
- userCommands
60
- .command("do b",
61
- "b description",
62
- (ctx) => ctx.doB());
63
- const mergedParams =
64
- MyCommandParams.from([a, b], someChatId);
65
- ```
66
- * @param commands An array of one or more Commands instances.
67
- * @returns an array of {@link SetMyCommandsParams} grouped by language
68
- */
69
- static from<C extends Context>(commands: Commands<C>[], chat_id: BotCommandScopeChat["chat_id"]): SetMyCommandsParams[];
56
+ * Merges and serialize one or more Commands instances into a single array
57
+ * of commands params that can be used to set the commands menu displayed to the user.
58
+ * @example
59
+ ```ts
60
+ const adminCommands = new CommandGroup();
61
+ const userCommands = new CommandGroup();
62
+ adminCommands
63
+ .command("do a",
64
+ "a description",
65
+ (ctx) => ctx.doA());
66
+ userCommands
67
+ .command("do b",
68
+ "b description",
69
+ (ctx) => ctx.doB());
70
+ const mergedParams =
71
+ MyCommandParams.from([a, b], someChatId);
72
+ ```
73
+ * @param commands An array of one or more Commands instances.
74
+ * @returns an array of {@link SetMyCommandsParams} grouped by language
75
+ */
76
+ static from<C extends Context>(commands: CommandGroup<C>[], chat_id: BotCommandScopeChat["chat_id"]): {
77
+ commandsParams: SetMyCommandsParams[];
78
+ uncompliantCommands: import("./command-group.js").UncompliantCommand[];
79
+ };
70
80
  /**
71
- * Serializes one or multiple {@link Commands} instances, each one into their respective
72
- * single scoped SetMyCommandsParams version.
73
- * @example
74
- ```ts
75
- const adminCommands = new Commands();
76
- // add to scope, localize, etc
77
- const userCommands = new Commands();
78
- // add to scope, localize, etc
79
- const [
80
- singleScopedAdminParams,
81
- singleScopedUserParams
82
- ] = MyCommandsParams.serialize([adminCommands,userCommands])
83
- ```
84
- * @param commandsArr an array of one or more commands instances
85
- * @param chat_id the chat id relative to the message update, coming from the ctx object.
86
- * @returns an array of scoped {@link SetMyCommandsParams} mapped from their respective Commands instances
87
- */
88
- static _serialize<C extends Context>(commandsArr: Commands<C>[], chat_id: BotCommandScopeChat["chat_id"]): SetMyCommandsParams[][];
81
+ * Serializes one or multiple {@link CommandGroup} instances, each one into their respective
82
+ * single scoped SetMyCommandsParams version.
83
+ * @example
84
+ ```ts
85
+ const adminCommands = new CommandGroup();
86
+ // add to scope, localize, etc
87
+ const userCommands = new CommandGroup();
88
+ // add to scope, localize, etc
89
+ const [
90
+ singleScopedAdminParams,
91
+ singleScopedUserParams
92
+ ] = MyCommandsParams.serialize([adminCommands,userCommands])
93
+ ```
94
+ * @param commandsArr an array of one or more commands instances
95
+ * @param chat_id the chat id relative to the message update, coming from the ctx object.
96
+ * @returns an array of scoped {@link SetMyCommandsParams} mapped from their respective Commands instances
97
+ */
98
+ static _serialize<C extends Context>(commandsArr: CommandGroup<C>[], chat_id: BotCommandScopeChat["chat_id"]): {
99
+ commandParams: SetMyCommandsParams[];
100
+ uncompliantCommands: import("./command-group.js").UncompliantCommand[];
101
+ }[];
89
102
  /**
90
103
  * Lexicographically sorts commandParams based on their language code.
91
104
  * @returns the sorted array
package/out/context.js CHANGED
@@ -1,42 +1,71 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MyCommandParams = exports.commands = void 0;
4
- const jaro_winkler_js_1 = require("./jaro-winkler.js");
5
- const utils_js_1 = require("./utils.js");
4
+ const array_js_1 = require("./utils/array.js");
5
+ const jaro_winkler_js_1 = require("./utils/jaro-winkler.js");
6
+ const set_bot_commands_js_1 = require("./utils/set-bot-commands.js");
6
7
  /**
7
8
  * Installs the commands flavor into the context.
8
9
  */
9
10
  function commands() {
10
11
  return (ctx, next) => {
11
- ctx.setMyCommands = async (commands, ...moreCommands) => {
12
+ ctx.setMyCommands = async (commands, options) => {
12
13
  if (!ctx.chat) {
13
14
  throw new Error("cannot call `ctx.setMyCommands` on an update with no `chat` property");
14
15
  }
15
- commands = (0, utils_js_1.ensureArray)(commands).concat(moreCommands);
16
- await Promise.all(MyCommandParams.from(commands, ctx.chat.id)
17
- .map((args) => ctx.api.raw.setMyCommands(args)));
16
+ const { uncompliantCommands, commandsParams: currentChatCommandParams, } = MyCommandParams.from((0, array_js_1.ensureArray)(commands), ctx.chat.id);
17
+ await (0, set_bot_commands_js_1.setBotCommands)(ctx.api, currentChatCommandParams, uncompliantCommands, options);
18
18
  };
19
19
  ctx.getNearestCommand = (commands, options) => {
20
- var _a;
21
- if ((_a = ctx.msg) === null || _a === void 0 ? void 0 : _a.text) {
22
- commands = (0, utils_js_1.ensureArray)(commands);
23
- const results = commands.map((commands) => {
24
- var _a;
25
- const userInput = ctx.msg.text.substring(1);
26
- const result = (0, jaro_winkler_js_1.fuzzyMatch)(userInput, commands, {
27
- ...options,
28
- language: !(options === null || options === void 0 ? void 0 : options.ignoreLocalization)
29
- ? (_a = ctx.from) === null || _a === void 0 ? void 0 : _a.language_code
30
- : undefined,
31
- });
32
- return result;
33
- }).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); });
34
- const result = results[0];
35
- if (!result || !result.command)
36
- return null;
37
- return result.command.prefix + result.command.name;
20
+ if (!ctx.has(":text")) {
21
+ throw new Error("cannot call `ctx.getNearestCommand` on an update with no `text`");
38
22
  }
39
- return null;
23
+ const results = (0, array_js_1.ensureArray)(commands)
24
+ .map((commands) => {
25
+ var _a;
26
+ const firstMatch = ctx.getCommandEntities(commands)[0];
27
+ const commandLike = (firstMatch === null || firstMatch === void 0 ? void 0 : firstMatch.text.replace(firstMatch.prefix, "")) ||
28
+ "";
29
+ const result = (0, jaro_winkler_js_1.fuzzyMatch)(commandLike, commands, {
30
+ ...options,
31
+ language: !(options === null || options === void 0 ? void 0 : options.ignoreLocalization)
32
+ ? (_a = ctx.from) === null || _a === void 0 ? void 0 : _a.language_code
33
+ : undefined,
34
+ });
35
+ return result;
36
+ }).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); });
37
+ const result = results[0];
38
+ if (!result || !result.command)
39
+ return null;
40
+ return result.command.prefix + result.command.name;
41
+ };
42
+ ctx.getCommandEntities = (commands) => {
43
+ if (!ctx.has(":text")) {
44
+ throw new Error("cannot call `ctx.commandEntities` on an update with no `text`");
45
+ }
46
+ const text = ctx.msg.text;
47
+ if (!text)
48
+ return [];
49
+ const prefixes = (0, array_js_1.ensureArray)(commands).flatMap((cmds) => cmds.prefixes);
50
+ if (!prefixes.length)
51
+ return [];
52
+ const regexes = prefixes.map((prefix) => (0, array_js_1.getCommandsRegex)(prefix));
53
+ const entities = regexes.flatMap((regex) => {
54
+ let match;
55
+ const matches = [];
56
+ while ((match = regex.exec(text)) !== null) {
57
+ const text = match[0].trim();
58
+ matches.push({
59
+ text,
60
+ offset: match.index,
61
+ prefix: match.groups.prefix,
62
+ type: "bot_command",
63
+ length: text.length,
64
+ });
65
+ }
66
+ return matches;
67
+ });
68
+ return entities;
40
69
  };
41
70
  return next();
42
71
  };
@@ -48,50 +77,57 @@ exports.commands = commands;
48
77
  */
49
78
  class MyCommandParams {
50
79
  /**
51
- * Merges and serialize one or more Commands instances into a single array
52
- * of commands params that can be used to set the commands menu displayed to the user.
53
- * @example
54
- ```ts
55
- const adminCommands = new Commands();
56
- const userCommands = new Commands();
57
- adminCommands
58
- .command("do a",
59
- "a description",
60
- (ctx) => ctx.doA());
61
- userCommands
62
- .command("do b",
63
- "b description",
64
- (ctx) => ctx.doB());
65
- const mergedParams =
66
- MyCommandParams.from([a, b], someChatId);
67
- ```
68
- * @param commands An array of one or more Commands instances.
69
- * @returns an array of {@link SetMyCommandsParams} grouped by language
70
- */
80
+ * Merges and serialize one or more Commands instances into a single array
81
+ * of commands params that can be used to set the commands menu displayed to the user.
82
+ * @example
83
+ ```ts
84
+ const adminCommands = new CommandGroup();
85
+ const userCommands = new CommandGroup();
86
+ adminCommands
87
+ .command("do a",
88
+ "a description",
89
+ (ctx) => ctx.doA());
90
+ userCommands
91
+ .command("do b",
92
+ "b description",
93
+ (ctx) => ctx.doB());
94
+ const mergedParams =
95
+ MyCommandParams.from([a, b], someChatId);
96
+ ```
97
+ * @param commands An array of one or more Commands instances.
98
+ * @returns an array of {@link SetMyCommandsParams} grouped by language
99
+ */
71
100
  static from(commands, chat_id) {
72
- const commandsParams = this._serialize(commands, chat_id).flat();
73
- if (!commandsParams.length)
74
- return [];
75
- return this.mergeByLanguage(commandsParams);
101
+ const serializedCommands = this._serialize(commands, chat_id);
102
+ const commandsParams = serializedCommands
103
+ .map(({ commandParams }) => commandParams)
104
+ .flat();
105
+ const uncompliantCommands = serializedCommands
106
+ .map(({ uncompliantCommands }) => uncompliantCommands)
107
+ .flat();
108
+ return {
109
+ commandsParams: this.mergeByLanguage(commandsParams),
110
+ uncompliantCommands,
111
+ };
76
112
  }
77
113
  /**
78
- * Serializes one or multiple {@link Commands} instances, each one into their respective
79
- * single scoped SetMyCommandsParams version.
80
- * @example
81
- ```ts
82
- const adminCommands = new Commands();
83
- // add to scope, localize, etc
84
- const userCommands = new Commands();
85
- // add to scope, localize, etc
86
- const [
87
- singleScopedAdminParams,
88
- singleScopedUserParams
89
- ] = MyCommandsParams.serialize([adminCommands,userCommands])
90
- ```
91
- * @param commandsArr an array of one or more commands instances
92
- * @param chat_id the chat id relative to the message update, coming from the ctx object.
93
- * @returns an array of scoped {@link SetMyCommandsParams} mapped from their respective Commands instances
94
- */
114
+ * Serializes one or multiple {@link CommandGroup} instances, each one into their respective
115
+ * single scoped SetMyCommandsParams version.
116
+ * @example
117
+ ```ts
118
+ const adminCommands = new CommandGroup();
119
+ // add to scope, localize, etc
120
+ const userCommands = new CommandGroup();
121
+ // add to scope, localize, etc
122
+ const [
123
+ singleScopedAdminParams,
124
+ singleScopedUserParams
125
+ ] = MyCommandsParams.serialize([adminCommands,userCommands])
126
+ ```
127
+ * @param commandsArr an array of one or more commands instances
128
+ * @param chat_id the chat id relative to the message update, coming from the ctx object.
129
+ * @returns an array of scoped {@link SetMyCommandsParams} mapped from their respective Commands instances
130
+ */
95
131
  static _serialize(commandsArr, chat_id) {
96
132
  return commandsArr.map((commands) => commands.toSingleScopeArgs({
97
133
  type: "chat",
@@ -120,6 +156,8 @@ class MyCommandParams {
120
156
  * @returns an array containing all commands grouped by language
121
157
  */
122
158
  static mergeByLanguage(params) {
159
+ if (!params.length)
160
+ return [];
123
161
  const sorted = this._sortByLanguage(params);
124
162
  return sorted.reduce((result, current, i, arr) => {
125
163
  if (i === 0 || current.language_code !== arr[i - 1].language_code) {
@@ -1,3 +1,3 @@
1
1
  export { Api, Bot, type ChatTypeContext, type ChatTypeMiddleware, type CommandContext, type CommandMiddleware, Composer, Context, type Middleware, type MiddlewareObj, type NextFunction, } from "grammy";
2
- export type { BotCommand, BotCommandScope, BotCommandScopeAllChatAdministrators, BotCommandScopeAllGroupChats, BotCommandScopeAllPrivateChats, BotCommandScopeChat, Chat, LanguageCode, } from "@grammyjs/types";
2
+ export type { BotCommand, BotCommandScope, BotCommandScopeAllChatAdministrators, BotCommandScopeAllGroupChats, BotCommandScopeAllPrivateChats, BotCommandScopeChat, Chat, LanguageCode, MessageEntity } from "@grammyjs/types";
3
3
  export { LanguageCodes } from "@grammyjs/types";
package/out/mod.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { Command } from "./command.js";
2
- export * from "./commands.js";
2
+ export * from "./command-group.js";
3
3
  export * from "./context.js";
4
4
  export type { CommandOptions } from "./types.js";
package/out/mod.js CHANGED
@@ -17,5 +17,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.Command = void 0;
18
18
  var command_js_1 = require("./command.js");
19
19
  Object.defineProperty(exports, "Command", { enumerable: true, get: function () { return command_js_1.Command; } });
20
- __exportStar(require("./commands.js"), exports);
20
+ __exportStar(require("./command-group.js"), exports);
21
21
  __exportStar(require("./context.js"), exports);
package/out/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { BotCommandScope, LanguageCode } from "./deps.node.js";
1
+ import type { BotCommandScope, LanguageCode, MessageEntity } from "./deps.node.js";
2
2
  /**
3
3
  * Supported command options
4
4
  */
@@ -22,6 +22,11 @@ export interface CommandOptions {
22
22
  * - `"required"`: only targeted commands are matched
23
23
  */
24
24
  targetedCommands: "ignored" | "optional" | "required";
25
+ /**
26
+ * Whether match against commands in a case-insensitive manner.
27
+ * Defaults to `false`.
28
+ */
29
+ ignoreCase: boolean;
25
30
  }
26
31
  export interface CommandElementals {
27
32
  name: string;
@@ -30,3 +35,9 @@ export interface CommandElementals {
30
35
  scopes: BotCommandScope[];
31
36
  description: string;
32
37
  }
38
+ /** represents a bot__command entity inside a text message */
39
+ export interface BotCommandEntity extends MessageEntity.CommonMessageEntity {
40
+ type: "bot_command";
41
+ text: string;
42
+ prefix: string;
43
+ }
@@ -0,0 +1,4 @@
1
+ export type MaybeArray<T> = T | T[];
2
+ export declare const ensureArray: <T>(value: MaybeArray<T>) => T[];
3
+ export declare function escapeSpecial(str: string): string;
4
+ export declare function getCommandsRegex(prefix: string): RegExp;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getCommandsRegex = exports.escapeSpecial = exports.ensureArray = void 0;
4
+ const ensureArray = (value) => Array.isArray(value) ? value : [value];
5
+ exports.ensureArray = ensureArray;
6
+ const specialChars = "\\.^$|?*+()[]{}-".split("");
7
+ const replaceAll = (s, find, replace) => s.replace(new RegExp(`\\${find}`, "g"), replace);
8
+ function escapeSpecial(str) {
9
+ return specialChars.reduce((acc, char) => replaceAll(acc, char, `\\${char}`), str);
10
+ }
11
+ exports.escapeSpecial = escapeSpecial;
12
+ function getCommandsRegex(prefix) {
13
+ return new RegExp(`(\?\<\!\\S)(\?<prefix>${escapeSpecial(prefix)})\\S+(\\s|$)`, "g");
14
+ }
15
+ exports.getCommandsRegex = getCommandsRegex;
@@ -0,0 +1,12 @@
1
+ import { UncompliantCommand } from "../command-group.js";
2
+ import { BotCommandScope } from "../deps.node.js";
3
+ export declare class InvalidScopeError extends Error {
4
+ constructor(scope: BotCommandScope);
5
+ }
6
+ export declare class CustomPrefixNotSupportedError extends Error {
7
+ readonly offendingCommands: string[];
8
+ constructor(message: string, offendingCommands: string[]);
9
+ }
10
+ export declare class UncompliantCommandsError extends Error {
11
+ constructor(commands: Array<UncompliantCommand>);
12
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UncompliantCommandsError = exports.CustomPrefixNotSupportedError = exports.InvalidScopeError = void 0;
4
+ class InvalidScopeError extends Error {
5
+ constructor(scope) {
6
+ super(`Invalid scope: ${scope}`);
7
+ this.name = "InvalidScopeError";
8
+ }
9
+ }
10
+ exports.InvalidScopeError = InvalidScopeError;
11
+ class CustomPrefixNotSupportedError extends Error {
12
+ constructor(message, offendingCommands) {
13
+ super(message);
14
+ this.offendingCommands = offendingCommands;
15
+ this.name = "CustomPrefixNotSupportedError";
16
+ }
17
+ }
18
+ exports.CustomPrefixNotSupportedError = CustomPrefixNotSupportedError;
19
+ class UncompliantCommandsError extends Error {
20
+ constructor(commands) {
21
+ const message = [
22
+ `Tried to set bot commands with one or more commands that do not comply with the Bot API requirements for command names. Offending command(s):`,
23
+ commands.map(({ name, reasons, language }) => `- (language: ${language}) ${name}: ${reasons.join(", ")}`)
24
+ .join("\n"),
25
+ "If you want to filter these commands out automatically, set `ignoreUncompliantCommands` to `true`",
26
+ ].join("\n");
27
+ super(message);
28
+ }
29
+ }
30
+ exports.UncompliantCommandsError = UncompliantCommandsError;
@@ -1,6 +1,6 @@
1
- import { Commands } from "./commands.js";
2
- import { Context, LanguageCode } from "./deps.node.js";
3
- import type { CommandElementals } from "./types.js";
1
+ import { CommandGroup } from "../command-group.js";
2
+ import { Context, LanguageCode } from "../deps.node.js";
3
+ import type { CommandElementals } from "../types.js";
4
4
  export declare function distance(s1: string, s2: string): number;
5
5
  export type JaroWinklerOptions = {
6
6
  ignoreCase?: boolean;
@@ -14,5 +14,5 @@ type CommandSimilarity = {
14
14
  };
15
15
  export declare function JaroWinklerDistance(s1: string, s2: string, options: Pick<Partial<JaroWinklerOptions>, "ignoreCase">): number;
16
16
  export declare function isLanguageCode(value: string | undefined): value is LanguageCode;
17
- export declare function fuzzyMatch<C extends Context>(userInput: string, commands: Commands<C>, options: Partial<JaroWinklerOptions>): CommandSimilarity | null;
17
+ export declare function fuzzyMatch<C extends Context>(userInput: string, commands: CommandGroup<C>, options: Partial<JaroWinklerOptions>): CommandSimilarity | null;
18
18
  export {};
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.fuzzyMatch = exports.isLanguageCode = exports.JaroWinklerDistance = exports.distance = void 0;
4
- const deps_node_js_1 = require("./deps.node.js");
4
+ const deps_node_js_1 = require("../deps.node.js");
5
5
  function distance(s1, s2) {
6
6
  if (s1.length === 0 || s2.length === 0) {
7
7
  return 0;
@@ -102,9 +102,7 @@ function fuzzyMatch(userInput, commands, options) {
102
102
  * https://en.wikipedia.org/wiki/IETF_language_tag
103
103
  */
104
104
  const possiblyISO639 = (_a = options.language) === null || _a === void 0 ? void 0 : _a.split("-")[0];
105
- const language = isLanguageCode(possiblyISO639)
106
- ? possiblyISO639
107
- : undefined;
105
+ const language = isLanguageCode(possiblyISO639) ? possiblyISO639 : undefined;
108
106
  const cmds = options.ignoreLocalization
109
107
  ? commands.toElementals()
110
108
  : commands.toElementals(language);
@@ -112,9 +110,7 @@ function fuzzyMatch(userInput, commands, options) {
112
110
  const similarity = JaroWinklerDistance(userInput, command.name, {
113
111
  ...options,
114
112
  });
115
- return similarity > best.similarity
116
- ? { command, similarity }
117
- : best;
113
+ return similarity > best.similarity ? { command, similarity } : best;
118
114
  }, { command: null, similarity: 0 });
119
115
  return bestMatch.similarity > similarityThreshold ? bestMatch : null;
120
116
  }
@@ -0,0 +1,24 @@
1
+ import { SetMyCommandsParams, UncompliantCommand } from "../command-group.js";
2
+ import { Api } from "../deps.node.js";
3
+ /**
4
+ * Options for the `setBotCommands` function.
5
+ */
6
+ export interface SetBotCommandsOptions {
7
+ /**
8
+ * Whether to remove invalid commands from the list of calls to the Bot API.
9
+ *
10
+ * If set to `false`, the method will throw an error if any of the commands
11
+ * is invalid according to the {@link https://core.telegram.org/bots/api#botcommand|official Bot API documentation}.
12
+ *
13
+ * Defaults to `false`.
14
+ */
15
+ ignoreUncompliantCommands?: boolean;
16
+ }
17
+ /**
18
+ * Performs validation and sets the provided commands for the bot.
19
+ * @param api Instance of the Api class for the bot the commands are being set for.
20
+ * @param commandParams List of commands to set.
21
+ * @param uncompliantCommands List of commands that do not comply with the Bot API rules.
22
+ * @param options Options object`
23
+ */
24
+ export declare function setBotCommands(api: Api, commandParams: SetMyCommandsParams[], uncompliantCommands: UncompliantCommand[], options?: Partial<SetBotCommandsOptions>): Promise<void>;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setBotCommands = void 0;
4
+ const errors_js_1 = require("./errors.js");
5
+ /**
6
+ * Performs validation and sets the provided commands for the bot.
7
+ * @param api Instance of the Api class for the bot the commands are being set for.
8
+ * @param commandParams List of commands to set.
9
+ * @param uncompliantCommands List of commands that do not comply with the Bot API rules.
10
+ * @param options Options object`
11
+ */
12
+ async function setBotCommands(api, commandParams, uncompliantCommands, options) {
13
+ const { ignoreUncompliantCommands = false } = options !== null && options !== void 0 ? options : {};
14
+ if (uncompliantCommands.length && !ignoreUncompliantCommands) {
15
+ throw new errors_js_1.UncompliantCommandsError(uncompliantCommands);
16
+ }
17
+ await Promise.all(commandParams.map((args) => api.raw.setMyCommands(args)));
18
+ }
19
+ exports.setBotCommands = setBotCommands;
package/package.json CHANGED
@@ -1,30 +1,30 @@
1
1
  {
2
- "name": "@grammyjs/commands",
3
- "version": "0.8.1",
4
- "description": "grammY Commands Plugin",
5
- "main": "out/mod.js",
6
- "scripts": {
7
- "backport": "deno task backport",
8
- "prepare": "deno task backport"
9
- },
10
- "keywords": [
11
- "grammY",
12
- "telegram",
13
- "bot",
14
- "commands"
15
- ],
16
- "author": "Roz <roz@rjmunhoz.me>",
17
- "license": "MIT",
18
- "dependencies": {
19
- "@grammyjs/types": "^3.8.1",
20
- "grammy": "^1.17.1",
21
- "ts-pattern": "^5.0.1"
22
- },
23
- "devDependencies": {
24
- "deno-bin": "^1.45.2",
25
- "typescript": "^5.1.6"
26
- },
27
- "files": [
28
- "out"
29
- ]
2
+ "name": "@grammyjs/commands",
3
+ "version": "0.10.0",
4
+ "description": "grammY Commands Plugin",
5
+ "main": "out/mod.js",
6
+ "scripts": {
7
+ "backport": "deno task backport",
8
+ "prepare": "deno task backport"
9
+ },
10
+ "keywords": [
11
+ "grammY",
12
+ "telegram",
13
+ "bot",
14
+ "commands"
15
+ ],
16
+ "author": "Roz <roz@rjmunhoz.me>",
17
+ "license": "MIT",
18
+ "dependencies": {
19
+ "@grammyjs/types": "^3.8.1",
20
+ "grammy": "^1.17.1",
21
+ "ts-pattern": "^5.0.1"
22
+ },
23
+ "devDependencies": {
24
+ "deno-bin": "^1.45.2",
25
+ "typescript": "^5.1.6"
26
+ },
27
+ "files": [
28
+ "out"
29
+ ]
30
30
  }
package/out/errors.d.ts DELETED
@@ -1,4 +0,0 @@
1
- import { BotCommandScope } from "./deps.node.js";
2
- export declare class InvalidScopeError extends Error {
3
- constructor(scope: BotCommandScope);
4
- }
package/out/errors.js DELETED
@@ -1,10 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.InvalidScopeError = void 0;
4
- class InvalidScopeError extends Error {
5
- constructor(scope) {
6
- super(`Invalid scope: ${scope}`);
7
- this.name = "InvalidScopeError";
8
- }
9
- }
10
- exports.InvalidScopeError = InvalidScopeError;
package/out/utils.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export type MaybeArray<T> = T | T[];
2
- export declare const ensureArray: <T>(value: MaybeArray<T>) => T[];
package/out/utils.js DELETED
@@ -1,5 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ensureArray = void 0;
4
- const ensureArray = (value) => Array.isArray(value) ? value : [value];
5
- exports.ensureArray = ensureArray;