@grammyjs/commands 1.0.3 → 1.0.5

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,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.commandNotFound = exports.CommandGroup = void 0;
3
+ exports.CommandGroup = void 0;
4
+ exports.commandNotFound = commandNotFound;
4
5
  const mod_js_1 = require("./mod.js");
5
6
  const deps_node_js_1 = require("./deps.node.js");
6
7
  const array_js_1 = require("./utils/array.js");
@@ -249,7 +250,6 @@ function commandNotFound(commands, opts = {}) {
249
250
  return false;
250
251
  };
251
252
  }
252
- exports.commandNotFound = commandNotFound;
253
253
  function containsCommands(ctx, commands) {
254
254
  let allPrefixes = [
255
255
  ...new Set((0, array_js_1.ensureArray)(commands).flatMap((cmds) => cmds.prefixes)),
package/out/command.d.ts CHANGED
@@ -2,6 +2,25 @@ import { type BotCommand, type BotCommandScope, type BotCommandScopeAllChatAdmin
2
2
  import type { CommandOptions } from "./types.js";
3
3
  import { type MaybeArray } from "./utils/array.js";
4
4
  type BotCommandGroupsScope = BotCommandScopeAllGroupChats | BotCommandScopeAllChatAdministrators;
5
+ /**
6
+ * Represents a matched command, the result of the RegExp match, and the rest of the input.
7
+ */
8
+ export interface CommandMatch {
9
+ /**
10
+ * The matched command.
11
+ */
12
+ command: string | RegExp;
13
+ /**
14
+ * The rest of the input after the command.
15
+ */
16
+ rest: string;
17
+ /**
18
+ * The result of the RegExp match.
19
+ *
20
+ * Only defined if the command is a RegExp.
21
+ */
22
+ match?: RegExpExecArray | null;
23
+ }
5
24
  /**
6
25
  * Class that represents a single command and allows you to configure it.
7
26
  */
@@ -117,6 +136,17 @@ export declare class Command<C extends Context = Context> implements MiddlewareO
117
136
  addToScope(scope: BotCommandGroupsScope, middleware?: MaybeArray<ChatTypeMiddleware<C, "group" | "supergroup">>, options?: Partial<CommandOptions>): this;
118
137
  addToScope(scope: BotCommandScopeAllPrivateChats, middleware?: MaybeArray<ChatTypeMiddleware<C, "private">>, options?: Partial<CommandOptions>): this;
119
138
  addToScope(scope: BotCommandScope, middleware?: MaybeArray<Middleware<C>>, options?: Partial<CommandOptions>): this;
139
+ /**
140
+ * Finds the matching command in the given context
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * // ctx.msg.text = "/delete_123 something"
145
+ * const match = Command.findMatchingCommand(/delete_(.*)/, { prefix: "/", ignoreCase: true }, ctx)
146
+ * // match is { command: /delete_(.*)/, rest: ["something"], match: ["delete_123"] }
147
+ * ```
148
+ */
149
+ static findMatchingCommand(command: MaybeArray<string | RegExp>, options: CommandOptions, ctx: Context): CommandMatch | null;
120
150
  /**
121
151
  * Creates a matcher for the given command that can be used in filtering operations
122
152
  *
package/out/command.js CHANGED
@@ -2,9 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Command = void 0;
4
4
  const deps_node_js_1 = require("./deps.node.js");
5
- const errors_js_1 = require("./utils/errors.js");
6
5
  const array_js_1 = require("./utils/array.js");
7
6
  const checks_js_1 = require("./utils/checks.js");
7
+ const errors_js_1 = require("./utils/errors.js");
8
8
  const NOCASE_COMMAND_NAME_REGEX = /^[0-9a-z_]+$/i;
9
9
  /**
10
10
  * Class that represents a single command and allows you to configure it.
@@ -165,6 +165,58 @@ class Command {
165
165
  this._scopes.push(scope);
166
166
  return this;
167
167
  }
168
+ /**
169
+ * Finds the matching command in the given context
170
+ *
171
+ * @example
172
+ * ```ts
173
+ * // ctx.msg.text = "/delete_123 something"
174
+ * const match = Command.findMatchingCommand(/delete_(.*)/, { prefix: "/", ignoreCase: true }, ctx)
175
+ * // match is { command: /delete_(.*)/, rest: ["something"], match: ["delete_123"] }
176
+ * ```
177
+ */
178
+ static findMatchingCommand(command, options, ctx) {
179
+ var _a;
180
+ const { matchOnlyAtStart, prefix, targetedCommands } = options;
181
+ if (!ctx.has(":text"))
182
+ return null;
183
+ if (matchOnlyAtStart && !ctx.msg.text.startsWith(prefix)) {
184
+ return null;
185
+ }
186
+ const commandNames = (0, array_js_1.ensureArray)(command);
187
+ const escapedPrefix = prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
188
+ 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;
190
+ if (!firstCommand)
191
+ return null;
192
+ if (!firstCommand.username && targetedCommands === "required")
193
+ return null;
194
+ if (firstCommand.username && firstCommand.username !== ctx.me.username) {
195
+ return null;
196
+ }
197
+ if (firstCommand.username && targetedCommands === "ignored")
198
+ return null;
199
+ const matchingCommand = commandNames.find((name) => {
200
+ const matches = (0, checks_js_1.matchesPattern)(name instanceof RegExp
201
+ ? firstCommand.command + firstCommand.rest
202
+ : firstCommand.command, name, options.ignoreCase);
203
+ return matches;
204
+ });
205
+ if (matchingCommand instanceof RegExp) {
206
+ return {
207
+ command: matchingCommand,
208
+ rest: firstCommand.rest.trim(),
209
+ match: matchingCommand.exec(ctx.msg.text),
210
+ };
211
+ }
212
+ if (matchingCommand) {
213
+ return {
214
+ command: matchingCommand,
215
+ rest: firstCommand.rest.trim(),
216
+ };
217
+ }
218
+ return null;
219
+ }
168
220
  /**
169
221
  * Creates a matcher for the given command that can be used in filtering operations
170
222
  *
@@ -182,30 +234,14 @@ class Command {
182
234
  * @returns A predicate that matches the given command
183
235
  */
184
236
  static hasCommand(command, options) {
185
- const { matchOnlyAtStart, prefix, targetedCommands } = options;
186
237
  return (ctx) => {
187
- if (!ctx.has(":text"))
188
- return false;
189
- if (matchOnlyAtStart && !ctx.msg.text.startsWith(prefix)) {
238
+ const matchingCommand = Command.findMatchingCommand(command, options, ctx);
239
+ if (!matchingCommand)
190
240
  return false;
191
- }
192
- const commandNames = (0, array_js_1.ensureArray)(command);
193
- const commands = prefix === "/"
194
- ? ctx.entities("bot_command")
195
- : ctx.msg.text.split(prefix).map((text) => ({ text }));
196
- for (const { text } of commands) {
197
- const [command, username] = text.split("@");
198
- if (targetedCommands === "ignored" && username)
199
- continue;
200
- if (targetedCommands === "required" && !username)
201
- continue;
202
- if (username && username !== ctx.me.username)
203
- continue;
204
- if (commandNames.some((name) => (0, checks_js_1.matchesPattern)(command.replace(prefix, "").split(" ")[0], name, options.ignoreCase))) {
205
- return true;
206
- }
207
- }
208
- return false;
241
+ ctx.match = matchingCommand.rest;
242
+ // TODO: Clean this up. But how to do it without requiring the user to install the commands flavor?
243
+ ctx.commandMatch = matchingCommand;
244
+ return true;
209
245
  };
210
246
  }
211
247
  /**
package/out/context.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { CommandGroup } from "./command-group.js";
2
+ import { CommandMatch } from "./command.js";
2
3
  import { BotCommandScopeChat, Context, NextFunction } from "./deps.node.js";
3
4
  import { SetMyCommandsParams } from "./mod.js";
4
5
  import { BotCommandEntity } from "./types.js";
@@ -42,6 +43,12 @@ export interface CommandsFlavor<C extends Context = Context> extends Context {
42
43
  * @returns command entities hydrated with the custom prefixes
43
44
  */
44
45
  getCommandEntities: (commands: CommandGroup<C> | CommandGroup<C>[]) => BotCommandEntity[];
46
+ /**
47
+ * The matched command and the rest of the input.
48
+ *
49
+ * When matched command is a RegExp, a `match` property exposes the result of the RegExp match.
50
+ */
51
+ commandMatch: CommandMatch;
45
52
  }
46
53
  /**
47
54
  * Installs the commands flavor into the context.
package/out/context.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MyCommandParams = exports.commands = void 0;
3
+ exports.MyCommandParams = void 0;
4
+ exports.commands = commands;
4
5
  const array_js_1 = require("./utils/array.js");
5
6
  const jaro_winkler_js_1 = require("./utils/jaro-winkler.js");
6
7
  const set_bot_commands_js_1 = require("./utils/set-bot-commands.js");
@@ -70,7 +71,6 @@ function commands() {
70
71
  return next();
71
72
  };
72
73
  }
73
- exports.commands = commands;
74
74
  /**
75
75
  * Static class for getting and manipulating {@link SetMyCommandsParams}.
76
76
  * The main function is {@link from}
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; } });
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getCommandsRegex = exports.escapeSpecial = exports.ensureArray = void 0;
3
+ exports.ensureArray = void 0;
4
+ exports.escapeSpecial = escapeSpecial;
5
+ exports.getCommandsRegex = getCommandsRegex;
4
6
  const ensureArray = (value) => Array.isArray(value) ? value : [value];
5
7
  exports.ensureArray = ensureArray;
6
8
  const specialChars = "\\.^$|?*+()[]{}-".split("");
@@ -8,8 +10,6 @@ const replaceAll = (s, find, replace) => s.replace(new RegExp(`\\${find}`, "g"),
8
10
  function escapeSpecial(str) {
9
11
  return specialChars.reduce((acc, char) => replaceAll(acc, char, `\\${char}`), str);
10
12
  }
11
- exports.escapeSpecial = escapeSpecial;
12
13
  function getCommandsRegex(prefix) {
13
14
  return new RegExp(`(\?\<\!\\S)(\?<prefix>${escapeSpecial(prefix)})\\S+(\\s|$)`, "g");
14
15
  }
15
- exports.getCommandsRegex = getCommandsRegex;
@@ -1,15 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.matchesPattern = exports.isCommandOptions = exports.isMiddleware = exports.isAdmin = void 0;
3
+ exports.isAdmin = isAdmin;
4
+ exports.isMiddleware = isMiddleware;
5
+ exports.isCommandOptions = isCommandOptions;
6
+ exports.matchesPattern = matchesPattern;
7
+ const deps_node_js_1 = require("../deps.node.js");
4
8
  function isAdmin(ctx) {
5
9
  return ctx
6
10
  .getAuthor()
7
11
  .then((author) => ["administrator", "creator"].includes(author.status));
8
12
  }
9
- exports.isAdmin = isAdmin;
10
13
  function isMiddleware(obj) {
11
14
  if (!obj)
12
15
  return false;
16
+ if (obj instanceof deps_node_js_1.Composer)
17
+ return true;
13
18
  if (Array.isArray(obj))
14
19
  return obj.every(isMiddleware);
15
20
  const objType = typeof obj;
@@ -21,7 +26,6 @@ function isMiddleware(obj) {
21
26
  }
22
27
  return false;
23
28
  }
24
- exports.isMiddleware = isMiddleware;
25
29
  function isCommandOptions(obj) {
26
30
  if (typeof obj !== "object" || !obj)
27
31
  return false;
@@ -37,7 +41,6 @@ function isCommandOptions(obj) {
37
41
  return true;
38
42
  return false;
39
43
  }
40
- exports.isCommandOptions = isCommandOptions;
41
44
  function matchesPattern(value, pattern, ignoreCase = false) {
42
45
  const transformedValue = ignoreCase ? value.toLowerCase() : value;
43
46
  const transformedPattern = pattern instanceof RegExp && ignoreCase && !pattern.flags.includes("i")
@@ -47,4 +50,3 @@ function matchesPattern(value, pattern, ignoreCase = false) {
47
50
  ? transformedValue === transformedPattern
48
51
  : transformedPattern.test(transformedValue);
49
52
  }
50
- exports.matchesPattern = matchesPattern;
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fuzzyMatch = exports.isLanguageCode = exports.JaroWinklerDistance = exports.distance = void 0;
3
+ exports.distance = distance;
4
+ exports.JaroWinklerDistance = JaroWinklerDistance;
5
+ exports.isLanguageCode = isLanguageCode;
6
+ exports.fuzzyMatch = fuzzyMatch;
4
7
  const language_codes_js_1 = require("../language-codes.js");
5
8
  function distance(s1, s2) {
6
9
  if (s1.length === 0 || s2.length === 0) {
@@ -61,7 +64,6 @@ function distance(s1, s2) {
61
64
  return (m / s1.length + m / s2.length + (m - t) / m) / 3.0; // HtD: therefore, m - t > 0, and m - t < m
62
65
  // HtD: => return value is between 0 and 1
63
66
  }
64
- exports.distance = distance;
65
67
  // Computes the Winkler distance between two string -- intrepreted from:
66
68
  // http://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance
67
69
  // s1 is the first string to compare
@@ -87,11 +89,9 @@ function JaroWinklerDistance(s1, s2, options) {
87
89
  return jaro + l * p * (1 - jaro);
88
90
  }
89
91
  }
90
- exports.JaroWinklerDistance = JaroWinklerDistance;
91
92
  function isLanguageCode(value) {
92
93
  return Object.values(language_codes_js_1.LanguageCodes).includes(value);
93
94
  }
94
- exports.isLanguageCode = isLanguageCode;
95
95
  function fuzzyMatch(userInput, commands, options) {
96
96
  var _a;
97
97
  const defaultSimilarityThreshold = 0.82;
@@ -114,4 +114,3 @@ function fuzzyMatch(userInput, commands, options) {
114
114
  }, { command: null, similarity: 0 });
115
115
  return bestMatch.similarity > similarityThreshold ? bestMatch : null;
116
116
  }
117
- exports.fuzzyMatch = fuzzyMatch;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setBotCommands = void 0;
3
+ exports.setBotCommands = setBotCommands;
4
4
  const errors_js_1 = require("./errors.js");
5
5
  /**
6
6
  * Performs validation and sets the provided commands for the bot.
@@ -16,4 +16,3 @@ async function setBotCommands(api, commandParams, uncompliantCommands, options)
16
16
  }
17
17
  await Promise.all(commandParams.map((args) => api.raw.setMyCommands(args)));
18
18
  }
19
- exports.setBotCommands = setBotCommands;
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@grammyjs/commands",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "grammY Commands Plugin",
5
5
  "main": "out/mod.js",
6
6
  "scripts": {
7
- "backport": "deno task backport",
8
- "prepare": "deno task backport"
7
+ "backport": "deno2node tsconfig.json",
8
+ "prepare": "npm run backport"
9
9
  },
10
10
  "keywords": [
11
11
  "grammY",
@@ -27,7 +27,7 @@
27
27
  "grammy": "^1.17.1"
28
28
  },
29
29
  "devDependencies": {
30
- "deno-bin": "^2.0.6",
30
+ "deno2node": "^1.14.0",
31
31
  "typescript": "^5.6.3"
32
32
  },
33
33
  "files": [