@grammyjs/commands 0.7.0 → 0.8.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/out/command.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { type BotCommand, type BotCommandScope, type BotCommandScopeAllChatAdministrators, type BotCommandScopeAllGroupChats, type BotCommandScopeAllPrivateChats, type ChatTypeMiddleware, type Context, type Middleware, type MiddlewareObj } from "./deps.node.js";
2
- import { CommandOptions } from "./types.js";
3
- export type MaybeArray<T> = T | T[];
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
+ import type { CommandOptions } from "./types.js";
3
+ import { type MaybeArray } from "./utils.js";
4
4
  type BotCommandGroupsScope = BotCommandScopeAllGroupChats | BotCommandScopeAllChatAdministrators;
5
5
  export declare const matchesPattern: (value: string, pattern: string | RegExp) => boolean;
6
6
  /**
@@ -28,7 +28,7 @@ export declare class Command<C extends Context = Context> implements MiddlewareO
28
28
  /**
29
29
  * Get registered languages for this command
30
30
  */
31
- get languages(): Map<string, {
31
+ get languages(): Map<LanguageCode | "default", {
32
32
  name: string | RegExp;
33
33
  description: string;
34
34
  }>;
@@ -44,6 +44,10 @@ export declare class Command<C extends Context = Context> implements MiddlewareO
44
44
  * Get the default description for this command
45
45
  */
46
46
  get description(): string;
47
+ /**
48
+ * Get the prefix for this command
49
+ */
50
+ get prefix(): string;
47
51
  /**
48
52
  * Registers the command to a scope to allow it to be handled and used with `setMyCommands`.
49
53
  * This will automatically apply filtering middlewares for you, so the handler only runs on the specified scope.
@@ -100,19 +104,19 @@ export declare class Command<C extends Context = Context> implements MiddlewareO
100
104
  * @param name Localized command name
101
105
  * @param description Localized command description
102
106
  */
103
- localize(languageCode: string, name: string | RegExp, description: string): this;
107
+ localize(languageCode: LanguageCode, name: string | RegExp, description: string): this;
104
108
  /**
105
109
  * Gets the localized command name of an existing translation
106
110
  * @param languageCode Language to get the name for
107
111
  * @returns Localized command name
108
112
  */
109
- getLocalizedName(languageCode: string): string | RegExp;
113
+ getLocalizedName(languageCode: LanguageCode | "default"): string | RegExp;
110
114
  /**
111
115
  * Gets the localized command name of an existing translation
112
116
  * @param languageCode Language to get the name for
113
117
  * @returns Localized command name
114
118
  */
115
- getLocalizedDescription(languageCode: string): string;
119
+ getLocalizedDescription(languageCode: LanguageCode | "default"): string;
116
120
  /**
117
121
  * Converts command to an object representation.
118
122
  * Useful for JSON serialization.
@@ -120,7 +124,7 @@ export declare class Command<C extends Context = Context> implements MiddlewareO
120
124
  * @param languageCode If specified, uses localized versions of the command name and description
121
125
  * @returns Object representation of this command
122
126
  */
123
- toObject(languageCode?: string): BotCommand;
127
+ toObject(languageCode?: LanguageCode | "default"): BotCommand;
124
128
  middleware(): import("grammy").MiddlewareFn<C>;
125
129
  }
126
130
  export {};
package/out/command.js CHANGED
@@ -3,7 +3,7 @@ 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
5
  const errors_js_1 = require("./errors.js");
6
- const ensureArray = (value) => Array.isArray(value) ? value : [value];
6
+ const utils_js_1 = require("./utils.js");
7
7
  const isAdmin = (ctx) => ctx
8
8
  .getAuthor()
9
9
  .then((author) => ["administrator", "creator"].includes(author.status));
@@ -66,8 +66,14 @@ class Command {
66
66
  get description() {
67
67
  return this._languages.get("default").description;
68
68
  }
69
+ /**
70
+ * Get the prefix for this command
71
+ */
72
+ get prefix() {
73
+ return this._options.prefix;
74
+ }
69
75
  addToScope(scope, middleware, options = this._options) {
70
- const middlewareArray = ensureArray(middleware);
76
+ const middlewareArray = (0, utils_js_1.ensureArray)(middleware);
71
77
  const optionsObject = { ...this._options, ...options };
72
78
  switch (scope.type) {
73
79
  case "default":
@@ -142,7 +148,7 @@ class Command {
142
148
  if (matchOnlyAtStart && !ctx.msg.text.startsWith(prefix)) {
143
149
  return false;
144
150
  }
145
- const commandNames = ensureArray(command);
151
+ const commandNames = (0, utils_js_1.ensureArray)(command);
146
152
  const commands = prefix === "/"
147
153
  ? ctx.entities("bot_command")
148
154
  : ctx.msg.text.split(prefix).map((text) => ({ text }));
package/out/commands.d.ts CHANGED
@@ -1,13 +1,19 @@
1
- import { Command, MaybeArray } from "./command.js";
2
- import { Api, BotCommand, BotCommandScope, CommandContext, Context, Middleware } from "./deps.node.js";
3
- import { CommandOptions } from "./types.js";
1
+ import { Command } from "./command.js";
2
+ import { Api, BotCommand, BotCommandScope, CommandContext, Context, type LanguageCode, Middleware } from "./deps.node.js";
3
+ import type { CommandElementals, CommandOptions } from "./types.js";
4
+ import { type MaybeArray } from "./utils.js";
5
+ /**
6
+ * Interface for grouping {@link BotCommand}s that might (or not)
7
+ * be related to each other by scope and/or language.
8
+ */
4
9
  export type SetMyCommandsParams = {
5
- /**
6
- * Scope
7
- * @param language_code two letter abbreviation in ISO_639 standard: https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes
8
- */
10
+ /** If defined: scope on which the commands will take effect */
9
11
  scope?: BotCommandScope;
10
- language_code?: string;
12
+ /** If defined: Language on which the commands will take effect.
13
+ * Two letter abbreviation in ISO_639 standard: https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes
14
+ */
15
+ language_code?: LanguageCode;
16
+ /** Commands that can be each one passed to a SetMyCommands Call */
11
17
  commands: BotCommand[];
12
18
  };
13
19
  /**
@@ -66,15 +72,28 @@ export declare class Commands<C extends Context> {
66
72
  * Registers all commands to be displayed by clients according to their scopes and languages
67
73
  * Calls `setMyCommands` for each language of each scope of each command.
68
74
  *
75
+ * [!IMPORTANT]
76
+ * Calling this method with upperCased command names registered, will throw
77
+ * @see https://core.telegram.org/bots/api#botcommand
78
+ * @see https://core.telegram.org/method/bots.setBotCommands
79
+ *
69
80
  * @param Instance of `bot` or { api: bot.api }
70
81
  */
71
82
  setCommands({ api }: {
72
83
  api: Api;
73
84
  }): Promise<void>;
74
85
  /**
75
- * Alias for {@link toArgs}
86
+ * Serialize all register commands into it's name, prefix and language
87
+ *
88
+ * @param filterLanguage if undefined, it returns all names
89
+ * else get only the locales for the given filterLanguage
90
+ * fallbacks to "default"
91
+ *
92
+ * @returns an array of {@link CommandElementals}
93
+ *
94
+ * Note: mainly used to serialize for {@link FuzzyMatch}
76
95
  */
77
- toJSON(): SetMyCommandsParams[];
96
+ toElementals(filterLanguage?: LanguageCode | "default"): CommandElementals[];
78
97
  /**
79
98
  * @returns A JSON serialized version of all the currently registered commands
80
99
  */
package/out/commands.js CHANGED
@@ -89,8 +89,9 @@ class Commands {
89
89
  language_code: language === "default"
90
90
  ? undefined
91
91
  : language,
92
- commands: commands.map((command) => command.toObject(language))
93
- .filter((args) => args.command.length > 0),
92
+ commands: commands
93
+ .filter((command) => typeof command.name === "string")
94
+ .map((command) => command.toObject(language)),
94
95
  });
95
96
  }
96
97
  }
@@ -121,16 +122,52 @@ class Commands {
121
122
  * Registers all commands to be displayed by clients according to their scopes and languages
122
123
  * Calls `setMyCommands` for each language of each scope of each command.
123
124
  *
125
+ * [!IMPORTANT]
126
+ * Calling this method with upperCased command names registered, will throw
127
+ * @see https://core.telegram.org/bots/api#botcommand
128
+ * @see https://core.telegram.org/method/bots.setBotCommands
129
+ *
124
130
  * @param Instance of `bot` or { api: bot.api }
125
131
  */
126
132
  async setCommands({ api }) {
127
133
  await Promise.all(this.toArgs().map((args) => api.raw.setMyCommands(args)));
128
134
  }
129
135
  /**
130
- * Alias for {@link toArgs}
136
+ * Serialize all register commands into it's name, prefix and language
137
+ *
138
+ * @param filterLanguage if undefined, it returns all names
139
+ * else get only the locales for the given filterLanguage
140
+ * fallbacks to "default"
141
+ *
142
+ * @returns an array of {@link CommandElementals}
143
+ *
144
+ * Note: mainly used to serialize for {@link FuzzyMatch}
131
145
  */
132
- toJSON() {
133
- return this.toArgs();
146
+ toElementals(filterLanguage) {
147
+ this._populateMetadata();
148
+ return Array.from(this._scopes.values())
149
+ .flat()
150
+ .flatMap((command) => {
151
+ const elements = [];
152
+ for (const [language, local] of command.languages.entries()) {
153
+ elements.push({
154
+ name: local.name instanceof RegExp
155
+ ? local.name.source
156
+ : local.name,
157
+ language,
158
+ prefix: command.prefix,
159
+ scopes: command.scopes,
160
+ description: command.getLocalizedDescription(language),
161
+ });
162
+ }
163
+ if (filterLanguage) {
164
+ const filtered = elements.filter((command) => command.language === filterLanguage);
165
+ const defaulted = elements.filter((command) => command.language === "default");
166
+ return filtered.length ? filtered[0] : defaulted[0];
167
+ }
168
+ else
169
+ return elements;
170
+ });
134
171
  }
135
172
  /**
136
173
  * @returns A JSON serialized version of all the currently registered commands
package/out/context.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Commands } from "./commands.js";
2
- import { Context, NextFunction } from "./deps.node.js";
2
+ import { BotCommandScopeChat, Context, NextFunction } from "./deps.node.js";
3
3
  import { JaroWinklerOptions } from "./jaro-winkler.js";
4
4
  import { SetMyCommandsParams } from "./mod.js";
5
5
  export interface CommandsFlavor<C extends Context = Context> extends Context {
@@ -7,10 +7,25 @@ export interface CommandsFlavor<C extends Context = Context> extends Context {
7
7
  * Sets the provided commands for the current chat.
8
8
  * Cannot be called on updates that don't have a `chat` property.
9
9
  *
10
+ * [!IMPORTANT]
11
+ * Calling this method with upperCased command names registered, will throw
12
+ * @see https://core.telegram.org/bots/api#botcommand
13
+ * @see https://core.telegram.org/method/bots.setBotCommands
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * bot.hears("sudo", (ctx) =>
18
+ * ctx.setMyCommands(userCommands, adminCommands));
19
+ * bot.hears("logout", (ctx) =>
20
+ * ctx.setMyCommands(userCommands));
21
+ * bot.hears("example", (ctx) =>
22
+ * ctx.setMyCommands([aCommands, bCommands, cCommands]));
23
+ * ```
24
+ *
10
25
  * @param commands List of available commands
11
26
  * @returns Promise with the result of the operations
12
27
  */
13
- setMyCommands: (commands: Commands<C>, ...moreCommands: Commands<C>[]) => Promise<void>;
28
+ setMyCommands: (commands: Commands<C> | Commands<C>[], ...moreCommands: Commands<C>[]) => Promise<void>;
14
29
  /**
15
30
  * Returns the nearest command to the user input.
16
31
  * If no command is found, returns `null`.
@@ -19,17 +34,70 @@ export interface CommandsFlavor<C extends Context = Context> extends Context {
19
34
  * @param options Options for the Jaro-Winkler algorithm
20
35
  * @returns The nearest command or `null`
21
36
  */
22
- getNearestCommand: (commands: Commands<C>, options?: Partial<JaroWinklerOptions>) => string | null;
37
+ getNearestCommand: (commands: Commands<C> | Commands<C>[], options?: Omit<Partial<JaroWinklerOptions>, "language">) => string | null;
23
38
  }
24
39
  /**
25
40
  * Installs the commands flavor into the context.
26
41
  */
27
42
  export declare function commands<C extends Context>(): (ctx: CommandsFlavor<C>, next: NextFunction) => Promise<void>;
28
43
  /**
29
- * Iterates over an array of commands params, merging commands when two commandsParams
30
- * are from the same language.
31
- *
32
- * @param commandParams an array of commands params coming from multiple Commands instances
33
- * @returns an array containing all commands to be set on ctx
44
+ * Static class for getting and manipulating {@link SetMyCommandsParams}.
45
+ * The main function is {@link from}
34
46
  */
35
- export declare function _mergeMyCommandsParams(commandParams: SetMyCommandsParams[][]): SetMyCommandsParams[];
47
+ export declare class MyCommandParams {
48
+ /**
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[];
70
+ /**
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[][];
89
+ /**
90
+ * Lexicographically sorts commandParams based on their language code.
91
+ * @returns the sorted array
92
+ */
93
+ static _sortByLanguage(params: SetMyCommandsParams[]): SetMyCommandsParams[];
94
+ /**
95
+ * Iterates over an array of CommandsParams
96
+ * merging their respective {@link SetMyCommandsParams.commands}
97
+ * when they are from the same language, separating when they are not.
98
+ *
99
+ * @param params a flattened array of commands params coming from one or more Commands instances
100
+ * @returns an array containing all commands grouped by language
101
+ */
102
+ private static mergeByLanguage;
103
+ }
package/out/context.js CHANGED
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports._mergeMyCommandsParams = exports.commands = void 0;
3
+ exports.MyCommandParams = exports.commands = void 0;
4
4
  const jaro_winkler_js_1 = require("./jaro-winkler.js");
5
+ const utils_js_1 = require("./utils.js");
5
6
  /**
6
7
  * Installs the commands flavor into the context.
7
8
  */
@@ -11,19 +12,29 @@ function commands() {
11
12
  if (!ctx.chat) {
12
13
  throw new Error("cannot call `ctx.setMyCommands` on an update with no `chat` property");
13
14
  }
14
- const commandsParams = [commands].concat(moreCommands).map((commands) => commands.toSingleScopeArgs({
15
- type: "chat",
16
- chat_id: ctx.chat.id,
17
- }));
18
- const mergedCommands = _mergeMyCommandsParams(commandsParams);
19
- await Promise.all(mergedCommands
15
+ commands = (0, utils_js_1.ensureArray)(commands).concat(moreCommands);
16
+ await Promise.all(MyCommandParams.from(commands, ctx.chat.id)
20
17
  .map((args) => ctx.api.raw.setMyCommands(args)));
21
18
  };
22
19
  ctx.getNearestCommand = (commands, options) => {
23
20
  var _a;
24
21
  if ((_a = ctx.msg) === null || _a === void 0 ? void 0 : _a.text) {
25
- const userInput = ctx.msg.text.substring(1);
26
- return (0, jaro_winkler_js_1.fuzzyMatch)(userInput, commands, { ...options });
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;
27
38
  }
28
39
  return null;
29
40
  };
@@ -32,35 +43,96 @@ function commands() {
32
43
  }
33
44
  exports.commands = commands;
34
45
  /**
35
- * Iterates over an array of commands params, merging commands when two commandsParams
36
- * are from the same language.
37
- *
38
- * @param commandParams an array of commands params coming from multiple Commands instances
39
- * @returns an array containing all commands to be set on ctx
46
+ * Static class for getting and manipulating {@link SetMyCommandsParams}.
47
+ * The main function is {@link from}
40
48
  */
41
- function _mergeMyCommandsParams(commandParams) {
42
- if (!commandParams.flat().length)
43
- return [];
44
- return commandParams
45
- .flat()
46
- .sort((a, b) => {
47
- if (!a.language_code)
48
- return -1;
49
- if (!b.language_code)
50
- return 1;
51
- return a.language_code.localeCompare(b.language_code);
52
- })
53
- .reduce((result, current, i, arr) => {
54
- if (i === 0 || current.language_code !== arr[i - 1].language_code) {
55
- result.push(current);
56
- return result;
57
- }
58
- else {
59
- result[result.length - 1].commands = result[result.length - 1]
60
- .commands
61
- .concat(current.commands);
62
- return result;
63
- }
64
- }, []);
49
+ class MyCommandParams {
50
+ /**
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
+ */
71
+ 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);
76
+ }
77
+ /**
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
+ */
95
+ static _serialize(commandsArr, chat_id) {
96
+ return commandsArr.map((commands) => commands.toSingleScopeArgs({
97
+ type: "chat",
98
+ chat_id,
99
+ }));
100
+ }
101
+ /**
102
+ * Lexicographically sorts commandParams based on their language code.
103
+ * @returns the sorted array
104
+ */
105
+ static _sortByLanguage(params) {
106
+ return params.sort((a, b) => {
107
+ if (!a.language_code)
108
+ return -1;
109
+ if (!b.language_code)
110
+ return 1;
111
+ return a.language_code.localeCompare(b.language_code);
112
+ });
113
+ }
114
+ /**
115
+ * Iterates over an array of CommandsParams
116
+ * merging their respective {@link SetMyCommandsParams.commands}
117
+ * when they are from the same language, separating when they are not.
118
+ *
119
+ * @param params a flattened array of commands params coming from one or more Commands instances
120
+ * @returns an array containing all commands grouped by language
121
+ */
122
+ static mergeByLanguage(params) {
123
+ const sorted = this._sortByLanguage(params);
124
+ return sorted.reduce((result, current, i, arr) => {
125
+ if (i === 0 || current.language_code !== arr[i - 1].language_code) {
126
+ result.push(current);
127
+ return result;
128
+ }
129
+ else {
130
+ result[result.length - 1].commands = result[result.length - 1]
131
+ .commands
132
+ .concat(current.commands);
133
+ return result;
134
+ }
135
+ }, []);
136
+ }
65
137
  }
66
- exports._mergeMyCommandsParams = _mergeMyCommandsParams;
138
+ exports.MyCommandParams = MyCommandParams;
@@ -1,2 +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, Chat, } from "grammy/types";
2
+ export type { BotCommand, BotCommandScope, BotCommandScopeAllChatAdministrators, BotCommandScopeAllGroupChats, BotCommandScopeAllPrivateChats, BotCommandScopeChat, Chat, LanguageCode, } from "@grammyjs/types";
3
+ export { LanguageCodes } from "@grammyjs/types";
package/out/deps.node.js CHANGED
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Context = exports.Composer = exports.Bot = exports.Api = void 0;
3
+ exports.LanguageCodes = exports.Context = exports.Composer = exports.Bot = exports.Api = void 0;
4
4
  var grammy_1 = require("grammy");
5
5
  Object.defineProperty(exports, "Api", { enumerable: true, get: function () { return grammy_1.Api; } });
6
6
  Object.defineProperty(exports, "Bot", { enumerable: true, get: function () { return grammy_1.Bot; } });
7
7
  Object.defineProperty(exports, "Composer", { enumerable: true, get: function () { return grammy_1.Composer; } });
8
8
  Object.defineProperty(exports, "Context", { enumerable: true, get: function () { return grammy_1.Context; } });
9
+ var types_1 = require("@grammyjs/types");
10
+ Object.defineProperty(exports, "LanguageCodes", { enumerable: true, get: function () { return types_1.LanguageCodes; } });
@@ -1,9 +1,18 @@
1
1
  import { Commands } from "./commands.js";
2
- import { Context } from "./deps.node.js";
2
+ import { Context, LanguageCode } from "./deps.node.js";
3
+ import type { CommandElementals } from "./types.js";
3
4
  export declare function distance(s1: string, s2: string): number;
4
5
  export type JaroWinklerOptions = {
5
6
  ignoreCase?: boolean;
6
7
  similarityThreshold?: number;
8
+ language?: LanguageCode | string;
9
+ ignoreLocalization?: boolean;
7
10
  };
8
- export declare function JaroWinklerDistance(s1: string, s2: string, options: Partial<JaroWinklerOptions>): number;
9
- export declare function fuzzyMatch<C extends Context>(userInput: string, commands: Commands<C>, options: Partial<JaroWinklerOptions>): string | null;
11
+ type CommandSimilarity = {
12
+ command: CommandElementals | null;
13
+ similarity: number;
14
+ };
15
+ export declare function JaroWinklerDistance(s1: string, s2: string, options: Pick<Partial<JaroWinklerOptions>, "ignoreCase">): number;
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;
18
+ export {};
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fuzzyMatch = exports.JaroWinklerDistance = exports.distance = void 0;
3
+ exports.fuzzyMatch = exports.isLanguageCode = exports.JaroWinklerDistance = exports.distance = void 0;
4
+ const deps_node_js_1 = require("./deps.node.js");
4
5
  function distance(s1, s2) {
5
6
  if (s1.length === 0 || s2.length === 0) {
6
7
  return 0;
@@ -87,23 +88,34 @@ function JaroWinklerDistance(s1, s2, options) {
87
88
  }
88
89
  }
89
90
  exports.JaroWinklerDistance = JaroWinklerDistance;
91
+ function isLanguageCode(value) {
92
+ return Object.values(deps_node_js_1.LanguageCodes).includes(value);
93
+ }
94
+ exports.isLanguageCode = isLanguageCode;
90
95
  function fuzzyMatch(userInput, commands, options) {
91
- const defaultSimilarityThreshold = 0.85;
96
+ var _a;
97
+ const defaultSimilarityThreshold = 0.82;
92
98
  const similarityThreshold = options.similarityThreshold ||
93
99
  defaultSimilarityThreshold;
94
- const commandsSet = new Set(commands
95
- .toJSON()
96
- .flatMap((item) => item.commands.map((command) => command.command)));
97
- const bestMatch = Array.from(commandsSet).reduce((best, command) => {
98
- const similarity = JaroWinklerDistance(userInput, command, {
100
+ /**
101
+ * ctx.from.id is IETF
102
+ * https://en.wikipedia.org/wiki/IETF_language_tag
103
+ */
104
+ const possiblyISO639 = (_a = options.language) === null || _a === void 0 ? void 0 : _a.split("-")[0];
105
+ const language = isLanguageCode(possiblyISO639)
106
+ ? possiblyISO639
107
+ : undefined;
108
+ const cmds = options.ignoreLocalization
109
+ ? commands.toElementals()
110
+ : commands.toElementals(language);
111
+ const bestMatch = cmds.reduce((best, command) => {
112
+ const similarity = JaroWinklerDistance(userInput, command.name, {
99
113
  ...options,
100
114
  });
101
115
  return similarity > best.similarity
102
116
  ? { command, similarity }
103
117
  : best;
104
118
  }, { command: null, similarity: 0 });
105
- return bestMatch.similarity > similarityThreshold
106
- ? bestMatch.command
107
- : null;
119
+ return bestMatch.similarity > similarityThreshold ? bestMatch : null;
108
120
  }
109
121
  exports.fuzzyMatch = fuzzyMatch;
package/out/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { BotCommandScope, LanguageCode } from "./deps.node.js";
1
2
  /**
2
3
  * Supported command options
3
4
  */
@@ -22,3 +23,10 @@ export interface CommandOptions {
22
23
  */
23
24
  targetedCommands: "ignored" | "optional" | "required";
24
25
  }
26
+ export interface CommandElementals {
27
+ name: string;
28
+ prefix: string;
29
+ language: LanguageCode | "default";
30
+ scopes: BotCommandScope[];
31
+ description: string;
32
+ }
package/out/utils.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export type MaybeArray<T> = T | T[];
2
+ export declare const ensureArray: <T>(value: MaybeArray<T>) => T[];
package/out/utils.js ADDED
@@ -0,0 +1,5 @@
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grammyjs/commands",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "grammY Commands Plugin",
5
5
  "main": "out/mod.js",
6
6
  "scripts": {
@@ -16,6 +16,7 @@
16
16
  "author": "Roz <roz@rjmunhoz.me>",
17
17
  "license": "MIT",
18
18
  "dependencies": {
19
+ "@grammyjs/types": "^3.8.1",
19
20
  "grammy": "^1.17.1",
20
21
  "ts-pattern": "^5.0.1"
21
22
  },