@grammyjs/commands 0.9.0 → 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 +17 -17
- package/out/{commands.d.ts → command-group.d.ts} +43 -9
- package/out/{commands.js → command-group.js} +94 -25
- package/out/command.d.ts +18 -2
- package/out/command.js +43 -4
- package/out/context.d.ts +56 -43
- package/out/context.js +104 -66
- package/out/deps.node.d.ts +1 -1
- package/out/mod.d.ts +1 -1
- package/out/mod.js +1 -1
- package/out/types.d.ts +7 -1
- package/out/utils/array.d.ts +4 -0
- package/out/utils/array.js +15 -0
- package/out/utils/errors.d.ts +12 -0
- package/out/utils/errors.js +30 -0
- package/out/{jaro-winkler.d.ts → utils/jaro-winkler.d.ts} +4 -4
- package/out/{jaro-winkler.js → utils/jaro-winkler.js} +3 -7
- package/out/utils/set-bot-commands.d.ts +24 -0
- package/out/utils/set-bot-commands.js +19 -0
- package/package.json +28 -28
- package/out/errors.d.ts +0 -4
- package/out/errors.js +0 -10
- package/out/utils.d.ts +0 -2
- package/out/utils.js +0 -5
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 {
|
|
19
|
+
import { CommandGroup } from "@grammyjs/commands";
|
|
20
20
|
|
|
21
21
|
const bot = new Bot("<telegram token>");
|
|
22
22
|
|
|
23
|
-
const myCommands = new
|
|
23
|
+
const myCommands = new CommandGroup();
|
|
24
24
|
|
|
25
25
|
myCommands.command("start", "Initializes bot configuration")
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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 {
|
|
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
|
-
|
|
63
|
+
const cmds = new CommandGroup();
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
cmds.command("start", "Initializes bot configuration")
|
|
66
|
+
.localize("pt", "start", "Inicializa as configurações do bot");
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
await ctx.setMyCommands(cmds);
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
return ctx.reply("Commands set!");
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
bot.start();
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { Command } from "./
|
|
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
|
|
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
|
|
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
|
|
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():
|
|
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):
|
|
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.
|
|
4
|
-
const
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
|
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
|
|
122
|
+
const commandParams = [];
|
|
123
|
+
const uncompliantCommands = [];
|
|
106
124
|
for (const language of this._languages) {
|
|
107
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
|
@@ -179,6 +208,20 @@ class Commands {
|
|
|
179
208
|
}
|
|
180
209
|
return this._cachedComposer.middleware();
|
|
181
210
|
}
|
|
211
|
+
/**
|
|
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
|
+
}
|
|
182
225
|
/**
|
|
183
226
|
* Replaces the `toString` method on Deno
|
|
184
227
|
*
|
|
@@ -196,4 +239,30 @@ class Commands {
|
|
|
196
239
|
return this.toString();
|
|
197
240
|
}
|
|
198
241
|
}
|
|
199
|
-
exports.
|
|
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,6 +1,6 @@
|
|
|
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
5
|
export declare const matchesPattern: (value: string, pattern: string | RegExp, ignoreCase?: boolean) => boolean;
|
|
6
6
|
/**
|
|
@@ -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
|
|
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,8 +2,8 @@
|
|
|
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
|
|
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));
|
|
@@ -17,6 +17,7 @@ const matchesPattern = (value, pattern, ignoreCase = false) => {
|
|
|
17
17
|
: transformedPattern.test(transformedValue);
|
|
18
18
|
};
|
|
19
19
|
exports.matchesPattern = matchesPattern;
|
|
20
|
+
const NOCASE_COMMAND_NAME_REGEX = /^[0-9a-z_]+$/i;
|
|
20
21
|
/**
|
|
21
22
|
* Class that represents a single command and allows you to configure it.
|
|
22
23
|
*/
|
|
@@ -45,6 +46,44 @@ class Command {
|
|
|
45
46
|
this._options.prefix = "/";
|
|
46
47
|
this._languages.set("default", { name: name, description });
|
|
47
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
|
+
}
|
|
48
87
|
/**
|
|
49
88
|
* Get registered scopes for this command
|
|
50
89
|
*/
|
|
@@ -82,7 +121,7 @@ class Command {
|
|
|
82
121
|
return this._options.prefix;
|
|
83
122
|
}
|
|
84
123
|
addToScope(scope, middleware, options = this._options) {
|
|
85
|
-
const middlewareArray = (0,
|
|
124
|
+
const middlewareArray = (0, array_js_1.ensureArray)(middleware);
|
|
86
125
|
const optionsObject = { ...this._options, ...options };
|
|
87
126
|
switch (scope.type) {
|
|
88
127
|
case "default":
|
|
@@ -157,7 +196,7 @@ class Command {
|
|
|
157
196
|
if (matchOnlyAtStart && !ctx.msg.text.startsWith(prefix)) {
|
|
158
197
|
return false;
|
|
159
198
|
}
|
|
160
|
-
const commandNames = (0,
|
|
199
|
+
const commandNames = (0, array_js_1.ensureArray)(command);
|
|
161
200
|
const commands = prefix === "/"
|
|
162
201
|
? ctx.entities("bot_command")
|
|
163
202
|
: ctx.msg.text.split(prefix).map((text) => ({ text }));
|
package/out/context.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
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:
|
|
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:
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
static from<C extends Context>(commands:
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
static _serialize<C extends Context>(commandsArr:
|
|
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
|
|
5
|
-
const
|
|
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,
|
|
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
|
-
|
|
16
|
-
await
|
|
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
|
-
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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) {
|
package/out/deps.node.d.ts
CHANGED
|
@@ -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
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("./
|
|
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
|
*/
|
|
@@ -35,3 +35,9 @@ export interface CommandElementals {
|
|
|
35
35
|
scopes: BotCommandScope[];
|
|
36
36
|
description: string;
|
|
37
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,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 {
|
|
2
|
-
import { Context, LanguageCode } from "
|
|
3
|
-
import type { CommandElementals } from "
|
|
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:
|
|
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("
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
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