@grammyjs/commands 1.0.4 → 1.0.8

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.
@@ -1,6 +1,6 @@
1
1
  import { Command, CommandsFlavor } from "./mod.js";
2
2
  import { Api, BotCommand, BotCommandScope, CommandContext, Context, type LanguageCode, Middleware } from "./deps.node.js";
3
- import type { CommandElementals, CommandOptions } from "./types.js";
3
+ import type { BotCommandX, CommandOptions } from "./types.js";
4
4
  import { type MaybeArray } from "./utils/array.js";
5
5
  import { SetBotCommandsOptions } from "./utils/set-bot-commands.js";
6
6
  import { JaroWinklerOptions } from "./utils/jaro-winkler.js";
@@ -16,7 +16,9 @@ export interface SetMyCommandsParams {
16
16
  */
17
17
  language_code?: LanguageCode;
18
18
  /** Commands that can be each one passed to a SetMyCommands Call */
19
- commands: BotCommand[];
19
+ commands: (BotCommand & {
20
+ hasHandler?: boolean;
21
+ })[];
20
22
  }
21
23
  /**
22
24
  * Interface to represent uncompliance of a command
@@ -110,17 +112,18 @@ export declare class CommandGroup<C extends Context> {
110
112
  api: Api;
111
113
  }, options?: Partial<SetBotCommandsOptions>): Promise<void>;
112
114
  /**
113
- * Serialize all register commands into it's name, prefix and language
115
+ * Serialize all register commands into a more detailed object
116
+ * including it's name, prefix and language, and more data
114
117
  *
115
118
  * @param filterLanguage if undefined, it returns all names
116
119
  * else get only the locales for the given filterLanguage
117
120
  * fallbacks to "default"
118
121
  *
119
- * @returns an array of {@link CommandElementals}
122
+ * @returns an array of {@link BotCommandX}
120
123
  *
121
124
  * Note: mainly used to serialize for {@link FuzzyMatch}
122
125
  */
123
- toElementals(filterLanguage?: LanguageCode | "default"): CommandElementals[];
126
+ toElementals(filterLanguage?: LanguageCode | "default"): BotCommandX[];
124
127
  /**
125
128
  * @returns A JSON serialized version of all the currently registered commands
126
129
  */
@@ -158,13 +158,14 @@ class CommandGroup {
158
158
  await (0, set_bot_commands_js_1.setBotCommands)(api, scopes, uncompliantCommands, options);
159
159
  }
160
160
  /**
161
- * Serialize all register commands into it's name, prefix and language
161
+ * Serialize all register commands into a more detailed object
162
+ * including it's name, prefix and language, and more data
162
163
  *
163
164
  * @param filterLanguage if undefined, it returns all names
164
165
  * else get only the locales for the given filterLanguage
165
166
  * fallbacks to "default"
166
167
  *
167
- * @returns an array of {@link CommandElementals}
168
+ * @returns an array of {@link BotCommandX}
168
169
  *
169
170
  * Note: mainly used to serialize for {@link FuzzyMatch}
170
171
  */
@@ -176,13 +177,16 @@ class CommandGroup {
176
177
  const elements = [];
177
178
  for (const [language, local] of command.languages.entries()) {
178
179
  elements.push({
179
- name: local.name instanceof RegExp
180
+ command: local.name instanceof RegExp
180
181
  ? local.name.source
181
182
  : local.name,
182
183
  language,
183
184
  prefix: command.prefix,
184
185
  scopes: command.scopes,
185
186
  description: command.getLocalizedDescription(language),
187
+ ...(command.hasHandler
188
+ ? { hasHandler: true }
189
+ : { hasHandler: false }),
186
190
  });
187
191
  }
188
192
  if (filterLanguage) {
package/out/command.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { type BotCommand, type BotCommandScope, type BotCommandScopeAllChatAdministrators, type BotCommandScopeAllGroupChats, type BotCommandScopeAllPrivateChats, type ChatTypeMiddleware, CommandContext, type Context, type LanguageCode, type Middleware, type MiddlewareObj } from "./deps.node.js";
2
- import type { CommandOptions } from "./types.js";
1
+ import { type BotCommandScope, type BotCommandScopeAllChatAdministrators, type BotCommandScopeAllGroupChats, type BotCommandScopeAllPrivateChats, type ChatTypeMiddleware, CommandContext, type Context, type LanguageCode, type Middleware, type MiddlewareObj } from "./deps.node.js";
2
+ import type { BotCommandX, CommandOptions } from "./types.js";
3
3
  import { type MaybeArray } from "./utils/array.js";
4
4
  type BotCommandGroupsScope = BotCommandScopeAllGroupChats | BotCommandScopeAllChatAdministrators;
5
5
  /**
@@ -27,9 +27,12 @@ export interface CommandMatch {
27
27
  export declare class Command<C extends Context = Context> implements MiddlewareObj<C> {
28
28
  private _scopes;
29
29
  private _languages;
30
- private _composer;
31
30
  private _defaultScopeComposer;
32
31
  private _options;
32
+ private _scopeHandlers;
33
+ private _cachedComposer;
34
+ private _cachedComposerInvalidated;
35
+ private _hasHandler;
33
36
  /**
34
37
  * Initialize a new command with a default handler.
35
38
  *
@@ -111,6 +114,10 @@ export declare class Command<C extends Context = Context> implements MiddlewareO
111
114
  * Get the prefix for this command
112
115
  */
113
116
  get prefix(): string;
117
+ /**
118
+ * Get if this command has a handler
119
+ */
120
+ get hasHandler(): boolean;
114
121
  /**
115
122
  * Registers the command to a scope to allow it to be handled and used with `setMyCommands`.
116
123
  * This will automatically apply filtering middlewares for you, so the handler only runs on the specified scope.
@@ -198,7 +205,8 @@ export declare class Command<C extends Context = Context> implements MiddlewareO
198
205
  * @param languageCode If specified, uses localized versions of the command name and description
199
206
  * @returns Object representation of this command
200
207
  */
201
- toObject(languageCode?: LanguageCode | "default"): BotCommand;
208
+ toObject(languageCode?: LanguageCode | "default"): Pick<BotCommandX, "command" | "description" | "hasHandler">;
209
+ private registerScopeHandlers;
202
210
  middleware(): import("grammy").MiddlewareFn<C>;
203
211
  }
204
212
  export {};
package/out/command.js CHANGED
@@ -14,7 +14,6 @@ class Command {
14
14
  var _a;
15
15
  this._scopes = [];
16
16
  this._languages = new Map();
17
- this._composer = new deps_node_js_1.Composer();
18
17
  this._defaultScopeComposer = new deps_node_js_1.Composer();
19
18
  this._options = {
20
19
  prefix: "/",
@@ -22,12 +21,19 @@ class Command {
22
21
  targetedCommands: "optional",
23
22
  ignoreCase: false,
24
23
  };
25
- const handler = (0, checks_js_1.isMiddleware)(handlerOrOptions)
26
- ? handlerOrOptions
27
- : undefined;
24
+ this._scopeHandlers = new Map();
25
+ this._cachedComposer = new deps_node_js_1.Composer();
26
+ this._cachedComposerInvalidated = false;
27
+ let handler = (0, checks_js_1.isMiddleware)(handlerOrOptions) ? handlerOrOptions : undefined;
28
28
  options = !handler && (0, checks_js_1.isCommandOptions)(handlerOrOptions)
29
29
  ? handlerOrOptions
30
30
  : options;
31
+ if (!handler) {
32
+ handler = async (_ctx, next) => await next();
33
+ this._hasHandler = false;
34
+ }
35
+ else
36
+ this._hasHandler = true;
31
37
  this._options = { ...this._options, ...options };
32
38
  if (((_a = this._options.prefix) === null || _a === void 0 ? void 0 : _a.trim()) === "")
33
39
  this._options.prefix = "/";
@@ -111,58 +117,20 @@ class Command {
111
117
  get prefix() {
112
118
  return this._options.prefix;
113
119
  }
120
+ /**
121
+ * Get if this command has a handler
122
+ */
123
+ get hasHandler() {
124
+ return this._hasHandler;
125
+ }
114
126
  addToScope(scope, middleware, options = this._options) {
115
127
  const middlewareArray = middleware ? (0, array_js_1.ensureArray)(middleware) : undefined;
116
128
  const optionsObject = { ...this._options, ...options };
117
- if (middlewareArray) {
118
- switch (scope.type) {
119
- case "default":
120
- this._defaultScopeComposer
121
- .filter(Command.hasCommand(this.names, optionsObject))
122
- .use(...middlewareArray);
123
- break;
124
- case "all_chat_administrators":
125
- this._composer
126
- .filter(Command.hasCommand(this.names, optionsObject))
127
- .filter(checks_js_1.isAdmin)
128
- .use(...middlewareArray);
129
- break;
130
- case "all_private_chats":
131
- this._composer
132
- .filter(Command.hasCommand(this.names, optionsObject))
133
- .chatType("private")
134
- .use(...middlewareArray);
135
- break;
136
- case "all_group_chats":
137
- this._composer
138
- .filter(Command.hasCommand(this.names, optionsObject))
139
- .chatType(["group", "supergroup"])
140
- .use(...middlewareArray);
141
- break;
142
- case "chat":
143
- case "chat_administrators":
144
- if (scope.chat_id) {
145
- this._composer
146
- .filter(Command.hasCommand(this.names, optionsObject))
147
- .filter((ctx) => { var _a; return ((_a = ctx.chat) === null || _a === void 0 ? void 0 : _a.id) === scope.chat_id; })
148
- .filter(checks_js_1.isAdmin)
149
- .use(...middlewareArray);
150
- }
151
- break;
152
- case "chat_member":
153
- if (scope.chat_id && scope.user_id) {
154
- this._composer
155
- .filter(Command.hasCommand(this.names, optionsObject))
156
- .filter((ctx) => { var _a; return ((_a = ctx.chat) === null || _a === void 0 ? void 0 : _a.id) === scope.chat_id; })
157
- .filter((ctx) => { var _a; return ((_a = ctx.from) === null || _a === void 0 ? void 0 : _a.id) === scope.user_id; })
158
- .use(...middlewareArray);
159
- }
160
- break;
161
- default:
162
- throw new errors_js_1.InvalidScopeError(scope);
163
- }
164
- }
165
129
  this._scopes.push(scope);
130
+ if (middlewareArray && middlewareArray.length) {
131
+ this._scopeHandlers.set(scope, [optionsObject, middlewareArray]);
132
+ this._cachedComposerInvalidated = true;
133
+ }
166
134
  return this;
167
135
  }
168
136
  /**
@@ -176,17 +144,18 @@ class Command {
176
144
  * ```
177
145
  */
178
146
  static findMatchingCommand(command, options, ctx) {
179
- var _a;
147
+ var _a, _b;
180
148
  const { matchOnlyAtStart, prefix, targetedCommands } = options;
181
- if (!ctx.has(":text"))
149
+ if (!ctx.has([":text", ":caption"]))
182
150
  return null;
183
- if (matchOnlyAtStart && !ctx.msg.text.startsWith(prefix)) {
151
+ const txt = (_a = ctx.msg.text) !== null && _a !== void 0 ? _a : ctx.msg.caption;
152
+ if (matchOnlyAtStart && !txt.startsWith(prefix)) {
184
153
  return null;
185
154
  }
186
155
  const commandNames = (0, array_js_1.ensureArray)(command);
187
156
  const escapedPrefix = prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
188
157
  const commandRegex = new RegExp(`${escapedPrefix}(?<command>[^@ ]+)(?:@(?<username>[^\\s]*))?(?<rest>.*)`, "g");
189
- const firstCommand = (_a = commandRegex.exec(ctx.msg.text)) === null || _a === void 0 ? void 0 : _a.groups;
158
+ const firstCommand = (_b = commandRegex.exec(txt)) === null || _b === void 0 ? void 0 : _b.groups;
190
159
  if (!firstCommand)
191
160
  return null;
192
161
  if (!firstCommand.username && targetedCommands === "required")
@@ -206,7 +175,7 @@ class Command {
206
175
  return {
207
176
  command: matchingCommand,
208
177
  rest: firstCommand.rest.trim(),
209
- match: matchingCommand.exec(ctx.msg.text),
178
+ match: matchingCommand.exec(txt),
210
179
  };
211
180
  }
212
181
  if (matchingCommand) {
@@ -263,6 +232,7 @@ class Command {
263
232
  name: name,
264
233
  description,
265
234
  });
235
+ this._cachedComposerInvalidated = true;
266
236
  return this;
267
237
  }
268
238
  /**
@@ -297,11 +267,80 @@ class Command {
297
267
  ? localizedName.source
298
268
  : localizedName,
299
269
  description: this.getLocalizedDescription(languageCode),
270
+ ...(this.hasHandler ? { hasHandler: true } : { hasHandler: false }),
300
271
  };
301
272
  }
273
+ registerScopeHandlers() {
274
+ const entries = this._scopeHandlers.entries();
275
+ for (const [scope, [optionsObject, middlewareArray]] of entries) {
276
+ if (middlewareArray) {
277
+ switch (scope.type) {
278
+ case "default":
279
+ this._defaultScopeComposer
280
+ .filter(Command.hasCommand(this.names, optionsObject))
281
+ .use(...middlewareArray);
282
+ break;
283
+ case "all_chat_administrators":
284
+ this._cachedComposer
285
+ .filter(Command.hasCommand(this.names, optionsObject))
286
+ .chatType(["group", "supergroup"])
287
+ .filter(checks_js_1.isAdmin)
288
+ .use(...middlewareArray);
289
+ break;
290
+ case "all_private_chats":
291
+ this._cachedComposer
292
+ .filter(Command.hasCommand(this.names, optionsObject))
293
+ .chatType("private")
294
+ .use(...middlewareArray);
295
+ break;
296
+ case "all_group_chats":
297
+ this._cachedComposer
298
+ .filter(Command.hasCommand(this.names, optionsObject))
299
+ .chatType(["group", "supergroup"])
300
+ .use(...middlewareArray);
301
+ break;
302
+ case "chat":
303
+ if (scope.chat_id) {
304
+ this._cachedComposer
305
+ .filter(Command.hasCommand(this.names, optionsObject))
306
+ .chatType(["group", "supergroup", "private"])
307
+ .filter((ctx) => ctx.chatId === scope.chat_id)
308
+ .use(...middlewareArray);
309
+ }
310
+ break;
311
+ case "chat_administrators":
312
+ if (scope.chat_id) {
313
+ this._cachedComposer
314
+ .filter(Command.hasCommand(this.names, optionsObject))
315
+ .chatType(["group", "supergroup"])
316
+ .filter((ctx) => ctx.chatId === scope.chat_id)
317
+ .filter(checks_js_1.isAdmin)
318
+ .use(...middlewareArray);
319
+ }
320
+ break;
321
+ case "chat_member":
322
+ if (scope.chat_id && scope.user_id) {
323
+ this._cachedComposer
324
+ .filter(Command.hasCommand(this.names, optionsObject))
325
+ .chatType(["group", "supergroup"])
326
+ .filter((ctx) => ctx.chatId === scope.chat_id)
327
+ .filter((ctx) => { var _a; return ((_a = ctx.from) === null || _a === void 0 ? void 0 : _a.id) === scope.user_id; })
328
+ .use(...middlewareArray);
329
+ }
330
+ break;
331
+ default:
332
+ throw new errors_js_1.InvalidScopeError(scope);
333
+ }
334
+ }
335
+ }
336
+ this._cachedComposer.use(this._defaultScopeComposer);
337
+ this._cachedComposerInvalidated = false;
338
+ }
302
339
  middleware() {
303
- return new deps_node_js_1.Composer(this._composer, this._defaultScopeComposer)
304
- .middleware();
340
+ if (this._cachedComposerInvalidated) {
341
+ this.registerScopeHandlers();
342
+ }
343
+ return this._cachedComposer.middleware();
305
344
  }
306
345
  }
307
346
  exports.Command = Command;
package/out/context.js CHANGED
@@ -38,7 +38,7 @@ function commands() {
38
38
  const result = results[0];
39
39
  if (!result || !result.command)
40
40
  return null;
41
- return result.command.prefix + result.command.name;
41
+ return result.command.prefix + result.command.command;
42
42
  };
43
43
  ctx.getCommandEntities = (commands) => {
44
44
  if (!ctx.has(":text")) {
package/out/mod.d.ts CHANGED
@@ -2,3 +2,4 @@ export { Command } from "./command.js";
2
2
  export * from "./command-group.js";
3
3
  export * from "./context.js";
4
4
  export type { CommandOptions } from "./types.js";
5
+ export { LanguageCodes } from "./language-codes.js";
package/out/mod.js CHANGED
@@ -14,8 +14,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.Command = void 0;
17
+ exports.LanguageCodes = 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
20
  __exportStar(require("./command-group.js"), exports);
21
21
  __exportStar(require("./context.js"), exports);
22
+ var language_codes_js_1 = require("./language-codes.js");
23
+ Object.defineProperty(exports, "LanguageCodes", { enumerable: true, get: function () { return language_codes_js_1.LanguageCodes; } });
package/out/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { BotCommandScope, LanguageCode, MessageEntity } from "./deps.node.js";
1
+ import type { BotCommand, BotCommandScope, LanguageCode, MessageEntity } from "./deps.node.js";
2
2
  /**
3
3
  * Supported command options
4
4
  */
@@ -28,12 +28,24 @@ export interface CommandOptions {
28
28
  */
29
29
  ignoreCase: boolean;
30
30
  }
31
- export interface CommandElementals {
32
- name: string;
31
+ /**
32
+ * BotCommand representation with more information about it.
33
+ * Specially in regards to the plugin manipulation of it
34
+ */
35
+ export interface BotCommandX extends BotCommand {
33
36
  prefix: string;
37
+ /**
38
+ * Language in which this command is localize
39
+ */
34
40
  language: LanguageCode | "default";
41
+ /**
42
+ * Scopes in which this command is registered
43
+ */
35
44
  scopes: BotCommandScope[];
36
- description: string;
45
+ /**
46
+ * True if this command has middleware attach to it. False if not.
47
+ */
48
+ hasHandler: boolean;
37
49
  }
38
50
  /** represents a bot__command entity inside a text message */
39
51
  export interface BotCommandEntity extends MessageEntity.CommonMessageEntity {
@@ -1,6 +1,6 @@
1
1
  import { CommandGroup } from "../command-group.js";
2
2
  import { Context, LanguageCode } from "../deps.node.js";
3
- import type { CommandElementals } from "../types.js";
3
+ import type { BotCommandX } from "../types.js";
4
4
  export declare function distance(s1: string, s2: string): number;
5
5
  export type JaroWinklerOptions = {
6
6
  ignoreCase?: boolean;
@@ -9,7 +9,7 @@ export type JaroWinklerOptions = {
9
9
  ignoreLocalization?: boolean;
10
10
  };
11
11
  type CommandSimilarity = {
12
- command: CommandElementals | null;
12
+ command: BotCommandX | null;
13
13
  similarity: number;
14
14
  };
15
15
  export declare function JaroWinklerDistance(s1: string, s2: string, options: Pick<Partial<JaroWinklerOptions>, "ignoreCase">): number;
@@ -107,7 +107,7 @@ function fuzzyMatch(userInput, commands, options) {
107
107
  ? commands.toElementals()
108
108
  : commands.toElementals(language);
109
109
  const bestMatch = cmds.reduce((best, command) => {
110
- const similarity = JaroWinklerDistance(userInput, command.name, {
110
+ const similarity = JaroWinklerDistance(userInput, command.command, {
111
111
  ...options,
112
112
  });
113
113
  return similarity > best.similarity ? { command, similarity } : best;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grammyjs/commands",
3
- "version": "1.0.4",
3
+ "version": "1.0.8",
4
4
  "description": "grammY Commands Plugin",
5
5
  "main": "out/mod.js",
6
6
  "scripts": {