tgplus 1.0.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.
Files changed (54) hide show
  1. package/LICENSE +52 -0
  2. package/README.md +1512 -0
  3. package/dist/adapters.d.ts +46 -0
  4. package/dist/adapters.js +83 -0
  5. package/dist/adapters.js.map +1 -0
  6. package/dist/bot.d.ts +50 -0
  7. package/dist/bot.js +180 -0
  8. package/dist/bot.js.map +1 -0
  9. package/dist/client.d.ts +1139 -0
  10. package/dist/client.js +529 -0
  11. package/dist/client.js.map +1 -0
  12. package/dist/composer.d.ts +88 -0
  13. package/dist/composer.js +233 -0
  14. package/dist/composer.js.map +1 -0
  15. package/dist/context.d.ts +72 -0
  16. package/dist/context.js +149 -0
  17. package/dist/context.js.map +1 -0
  18. package/dist/decorators.d.ts +33 -0
  19. package/dist/decorators.js +39 -0
  20. package/dist/decorators.js.map +1 -0
  21. package/dist/filters.d.ts +19 -0
  22. package/dist/filters.js +81 -0
  23. package/dist/filters.js.map +1 -0
  24. package/dist/i18n.d.ts +30 -0
  25. package/dist/i18n.js +41 -0
  26. package/dist/i18n.js.map +1 -0
  27. package/dist/index.d.ts +16 -0
  28. package/dist/index.js +95 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/keyboard.d.ts +62 -0
  31. package/dist/keyboard.js +96 -0
  32. package/dist/keyboard.js.map +1 -0
  33. package/dist/rateLimit.d.ts +19 -0
  34. package/dist/rateLimit.js +32 -0
  35. package/dist/rateLimit.js.map +1 -0
  36. package/dist/richMessage.d.ts +111 -0
  37. package/dist/richMessage.js +221 -0
  38. package/dist/richMessage.js.map +1 -0
  39. package/dist/scenes.d.ts +57 -0
  40. package/dist/scenes.js +105 -0
  41. package/dist/scenes.js.map +1 -0
  42. package/dist/session-redis.d.ts +27 -0
  43. package/dist/session-redis.js +36 -0
  44. package/dist/session-redis.js.map +1 -0
  45. package/dist/session-sqlite.d.ts +29 -0
  46. package/dist/session-sqlite.js +34 -0
  47. package/dist/session-sqlite.js.map +1 -0
  48. package/dist/session.d.ts +30 -0
  49. package/dist/session.js +35 -0
  50. package/dist/session.js.map +1 -0
  51. package/dist/types.d.ts +2485 -0
  52. package/dist/types.js +11 -0
  53. package/dist/types.js.map +1 -0
  54. package/package.json +42 -0
@@ -0,0 +1,88 @@
1
+ import { Context } from "./context";
2
+ import * as T from "./types";
3
+ export type Middleware<C extends Context = Context> = (ctx: C, next: () => Promise<void>) => unknown | Promise<unknown>;
4
+ export type Predicate<C extends Context = Context> = (ctx: C) => boolean;
5
+ /**
6
+ * Composer is the router/handler-chain layer: `bot.command(...)`, `bot.on(...)`,
7
+ * `bot.hears(...)`, `bot.action(...)` all boil down to "run this middleware if a
8
+ * predicate matches, otherwise fall through to the next one" — same mental model
9
+ * as telegraf/Express, but every helper here is a plain method, not a class you
10
+ * must extend, so a beginner never needs to know the word "middleware" to start.
11
+ *
12
+ * Every matching method below (filter/on/command/hears/action) also has a
13
+ * `static` counterpart that builds the same middleware WITHOUT registering it
14
+ * on a Composer — use those to nest matchers inside gates like admin()/
15
+ * privateChat(): `bot.privateChat(Composer.command("start", handler))`.
16
+ */
17
+ export declare class Composer<C extends Context = Context> {
18
+ private stack;
19
+ /** Register any middleware. Everything else on this class is sugar over `use`. */
20
+ use(...fns: Middleware<C>[]): this;
21
+ /** Mount a sub-composer (or another bot) as a nested module. */
22
+ mount(composer: Composer<C>): this;
23
+ /** Build a standalone predicate-gated middleware — falls through (calls next()) when the predicate doesn't match. Nestable inside gates like admin()/privateChat(). */
24
+ static filter<C extends Context = Context>(predicate: Predicate<C>, ...fns: Middleware<C>[]): Middleware<C>;
25
+ /** Only run `fn` when `predicate(ctx)` is true; otherwise continue down the stack. */
26
+ filter(predicate: Predicate<C>, ...fns: Middleware<C>[]): this;
27
+ static on<C extends Context = Context>(type: T.UpdateType | MessageSubType | Predicate<C>, ...fns: Middleware<C>[]): Middleware<C>;
28
+ /** Restrict to a specific update type: on('message'), on('photo' as a message sub-type), on(customPredicate), etc. */
29
+ on(type: T.UpdateType | MessageSubType | Predicate<C>, ...fns: Middleware<C>[]): this;
30
+ static command<C extends Context = Context>(cmd: string | string[], ...fns: Middleware<C>[]): Middleware<C>;
31
+ /** Match /command or /command@BotName, optionally with args captured in ctx.match. */
32
+ command(cmd: string | string[], ...fns: Middleware<C>[]): this;
33
+ static hears<C extends Context = Context>(trigger: string | RegExp, ...fns: Middleware<C>[]): Middleware<C>;
34
+ /** Match message text against a string (exact) or RegExp (captures land in ctx.match). */
35
+ hears(trigger: string | RegExp, ...fns: Middleware<C>[]): this;
36
+ static action<C extends Context = Context>(trigger: string | RegExp, ...fns: Middleware<C>[]): Middleware<C>;
37
+ /** Match callback_query data against a string (exact) or RegExp (captures land in ctx.match). */
38
+ action(trigger: string | RegExp, ...fns: Middleware<C>[]): this;
39
+ /** Catch-all fallback if nothing above matched — put this last. */
40
+ otherwise(...fns: Middleware<C>[]): this;
41
+ /** Only run fns when the update's chat is one of the given types — e.g. chatType('private'), chatType(['group', 'supergroup']). */
42
+ chatType(type: T.ChatType | T.ChatType[], ...fns: Middleware<C>[]): this;
43
+ /** Only run fns in a private (1:1) chat with the bot. */
44
+ privateChat(...fns: Middleware<C>[]): this;
45
+ /** Only run fns in a group or supergroup. */
46
+ groupChat(...fns: Middleware<C>[]): this;
47
+ /**
48
+ * Only run fns if the sender is an admin or the creator of the chat.
49
+ * Makes one getChatMember call per matching update — for high-traffic
50
+ * chats, consider caching admin lists yourself instead.
51
+ */
52
+ admin(...fns: Middleware<C>[]): this;
53
+ /** Only run fns if the sender is the creator (owner) of the chat. */
54
+ creator(...fns: Middleware<C>[]): this;
55
+ /** Alias for filter() matching telegraf's naming — run fns only when predicate(ctx) is true. */
56
+ optional(predicate: Predicate<C>, ...fns: Middleware<C>[]): this;
57
+ /** Route to one of two middleware branches depending on predicate(ctx), telegraf-style. */
58
+ branch(predicate: Predicate<C> | ((ctx: C) => Promise<boolean>), whenTrue: Middleware<C> | Middleware<C>[], whenFalse: Middleware<C> | Middleware<C>[]): this;
59
+ /** Resolve which middleware to run at dispatch time — e.g. per-tenant config, feature flags, A/B tests. */
60
+ lazy(factory: (ctx: C) => Middleware<C> | Promise<Middleware<C>>): this;
61
+ /** Combine several middlewares into one, without needing a Composer instance — matches telegraf's static Composer.compose. */
62
+ static compose<C extends Context = Context>(fns: Middleware<C>[]): Middleware<C>;
63
+ /** Run the whole stack for a context. Used internally by Bot; exposed for testing. */
64
+ handle(ctx: C, done?: () => Promise<void>): Promise<void>;
65
+ }
66
+ /**
67
+ * Routes to a different sub-middleware based on a key derived from ctx —
68
+ * e.g. multi-tenant bots, or dispatching by a stored "current step" value
69
+ * that doesn't fit the Scenes model. Falls through via otherwise() (or the
70
+ * outer chain) if no route matches.
71
+ *
72
+ * const router = new Router<Context>((ctx) => ctx.session?.lang);
73
+ * router.on('en', (ctx) => ctx.reply('Hi!'));
74
+ * router.on('fr', (ctx) => ctx.reply('Salut!'));
75
+ * router.otherwise((ctx) => ctx.reply('Hi!'));
76
+ * bot.use(router.middleware());
77
+ */
78
+ export declare class Router<C extends Context = Context> {
79
+ private keyFn;
80
+ private routes;
81
+ private fallback?;
82
+ constructor(keyFn: (ctx: C) => string | undefined | Promise<string | undefined>);
83
+ on(key: string, ...fns: Middleware<C>[]): this;
84
+ otherwise(...fns: Middleware<C>[]): this;
85
+ middleware(): Middleware<C>;
86
+ }
87
+ type MessageSubType = "text" | "photo" | "video" | "document" | "audio" | "voice" | "sticker" | "location" | "contact" | "poll" | "dice" | "video_note" | "rich_message";
88
+ export {};
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Router = exports.Composer = void 0;
4
+ /**
5
+ * Composer is the router/handler-chain layer: `bot.command(...)`, `bot.on(...)`,
6
+ * `bot.hears(...)`, `bot.action(...)` all boil down to "run this middleware if a
7
+ * predicate matches, otherwise fall through to the next one" — same mental model
8
+ * as telegraf/Express, but every helper here is a plain method, not a class you
9
+ * must extend, so a beginner never needs to know the word "middleware" to start.
10
+ *
11
+ * Every matching method below (filter/on/command/hears/action) also has a
12
+ * `static` counterpart that builds the same middleware WITHOUT registering it
13
+ * on a Composer — use those to nest matchers inside gates like admin()/
14
+ * privateChat(): `bot.privateChat(Composer.command("start", handler))`.
15
+ */
16
+ class Composer {
17
+ stack = [];
18
+ /** Register any middleware. Everything else on this class is sugar over `use`. */
19
+ use(...fns) {
20
+ this.stack.push(...fns);
21
+ return this;
22
+ }
23
+ /** Mount a sub-composer (or another bot) as a nested module. */
24
+ mount(composer) {
25
+ return this.use((ctx, next) => composer.handle(ctx, next));
26
+ }
27
+ /** Build a standalone predicate-gated middleware — falls through (calls next()) when the predicate doesn't match. Nestable inside gates like admin()/privateChat(). */
28
+ static filter(predicate, ...fns) {
29
+ const inner = Composer.compose(fns);
30
+ return async (ctx, next) => {
31
+ if (predicate(ctx))
32
+ return inner(ctx, next);
33
+ return next();
34
+ };
35
+ }
36
+ /** Only run `fn` when `predicate(ctx)` is true; otherwise continue down the stack. */
37
+ filter(predicate, ...fns) {
38
+ return this.use(Composer.filter(predicate, ...fns));
39
+ }
40
+ static on(type, ...fns) {
41
+ const predicate = typeof type === "function" ? type : (ctx) => matchesUpdateType(ctx, type);
42
+ return Composer.filter(predicate, ...fns);
43
+ }
44
+ /** Restrict to a specific update type: on('message'), on('photo' as a message sub-type), on(customPredicate), etc. */
45
+ on(type, ...fns) {
46
+ return this.use(Composer.on(type, ...fns));
47
+ }
48
+ static command(cmd, ...fns) {
49
+ const cmds = (Array.isArray(cmd) ? cmd : [cmd]).map((c) => c.replace(/^\//, ""));
50
+ return Composer.filter((ctx) => {
51
+ const text = ctx.message?.text;
52
+ if (!text?.startsWith("/"))
53
+ return false;
54
+ const [used, ...rest] = text.slice(1).split(/\s+/);
55
+ const [name] = used.split("@");
56
+ if (!cmds.includes(name))
57
+ return false;
58
+ ctx.match = [text, rest.join(" ")];
59
+ return true;
60
+ }, ...fns);
61
+ }
62
+ /** Match /command or /command@BotName, optionally with args captured in ctx.match. */
63
+ command(cmd, ...fns) {
64
+ return this.use(Composer.command(cmd, ...fns));
65
+ }
66
+ static hears(trigger, ...fns) {
67
+ return Composer.filter((ctx) => {
68
+ const text = ctx.message?.text;
69
+ if (text === undefined)
70
+ return false;
71
+ if (typeof trigger === "string")
72
+ return text === trigger;
73
+ const m = text.match(trigger);
74
+ if (m)
75
+ ctx.match = m;
76
+ return !!m;
77
+ }, ...fns);
78
+ }
79
+ /** Match message text against a string (exact) or RegExp (captures land in ctx.match). */
80
+ hears(trigger, ...fns) {
81
+ return this.use(Composer.hears(trigger, ...fns));
82
+ }
83
+ static action(trigger, ...fns) {
84
+ return Composer.filter((ctx) => {
85
+ const data = ctx.callbackQuery?.data;
86
+ if (data === undefined)
87
+ return false;
88
+ if (typeof trigger === "string")
89
+ return data === trigger;
90
+ const m = data.match(trigger);
91
+ if (m)
92
+ ctx.match = m;
93
+ return !!m;
94
+ }, ...fns);
95
+ }
96
+ /** Match callback_query data against a string (exact) or RegExp (captures land in ctx.match). */
97
+ action(trigger, ...fns) {
98
+ return this.use(Composer.action(trigger, ...fns));
99
+ }
100
+ /** Catch-all fallback if nothing above matched — put this last. */
101
+ otherwise(...fns) {
102
+ return this.use(...fns);
103
+ }
104
+ /** Only run fns when the update's chat is one of the given types — e.g. chatType('private'), chatType(['group', 'supergroup']). */
105
+ chatType(type, ...fns) {
106
+ const types = Array.isArray(type) ? type : [type];
107
+ return this.filter((ctx) => !!ctx.chat && types.includes(ctx.chat.type), ...fns);
108
+ }
109
+ /** Only run fns in a private (1:1) chat with the bot. */
110
+ privateChat(...fns) {
111
+ return this.chatType("private", ...fns);
112
+ }
113
+ /** Only run fns in a group or supergroup. */
114
+ groupChat(...fns) {
115
+ return this.chatType(["group", "supergroup"], ...fns);
116
+ }
117
+ /**
118
+ * Only run fns if the sender is an admin or the creator of the chat.
119
+ * Makes one getChatMember call per matching update — for high-traffic
120
+ * chats, consider caching admin lists yourself instead.
121
+ */
122
+ admin(...fns) {
123
+ const inner = Composer.compose(fns);
124
+ return this.use(async (ctx, next) => {
125
+ if (!ctx.chat || !ctx.from)
126
+ return;
127
+ const member = await ctx.api.getChatMember({ chat_id: ctx.chat.id, user_id: ctx.from.id }).catch(() => null);
128
+ if (member?.status === "administrator" || member?.status === "creator")
129
+ return inner(ctx, next);
130
+ });
131
+ }
132
+ /** Only run fns if the sender is the creator (owner) of the chat. */
133
+ creator(...fns) {
134
+ const inner = Composer.compose(fns);
135
+ return this.use(async (ctx, next) => {
136
+ if (!ctx.chat || !ctx.from)
137
+ return;
138
+ const member = await ctx.api.getChatMember({ chat_id: ctx.chat.id, user_id: ctx.from.id }).catch(() => null);
139
+ if (member?.status === "creator")
140
+ return inner(ctx, next);
141
+ });
142
+ }
143
+ /** Alias for filter() matching telegraf's naming — run fns only when predicate(ctx) is true. */
144
+ optional(predicate, ...fns) {
145
+ return this.filter(predicate, ...fns);
146
+ }
147
+ /** Route to one of two middleware branches depending on predicate(ctx), telegraf-style. */
148
+ branch(predicate, whenTrue, whenFalse) {
149
+ const trueMw = Composer.compose(Array.isArray(whenTrue) ? whenTrue : [whenTrue]);
150
+ const falseMw = Composer.compose(Array.isArray(whenFalse) ? whenFalse : [whenFalse]);
151
+ return this.use(async (ctx, next) => {
152
+ const result = await predicate(ctx);
153
+ return result ? trueMw(ctx, next) : falseMw(ctx, next);
154
+ });
155
+ }
156
+ /** Resolve which middleware to run at dispatch time — e.g. per-tenant config, feature flags, A/B tests. */
157
+ lazy(factory) {
158
+ return this.use(async (ctx, next) => {
159
+ const mw = await factory(ctx);
160
+ return mw(ctx, next);
161
+ });
162
+ }
163
+ /** Combine several middlewares into one, without needing a Composer instance — matches telegraf's static Composer.compose. */
164
+ static compose(fns) {
165
+ return (ctx, next) => runChain(fns, ctx, next);
166
+ }
167
+ /** Run the whole stack for a context. Used internally by Bot; exposed for testing. */
168
+ async handle(ctx, done = async () => { }) {
169
+ await runChain(this.stack, ctx, done);
170
+ }
171
+ }
172
+ exports.Composer = Composer;
173
+ /**
174
+ * Routes to a different sub-middleware based on a key derived from ctx —
175
+ * e.g. multi-tenant bots, or dispatching by a stored "current step" value
176
+ * that doesn't fit the Scenes model. Falls through via otherwise() (or the
177
+ * outer chain) if no route matches.
178
+ *
179
+ * const router = new Router<Context>((ctx) => ctx.session?.lang);
180
+ * router.on('en', (ctx) => ctx.reply('Hi!'));
181
+ * router.on('fr', (ctx) => ctx.reply('Salut!'));
182
+ * router.otherwise((ctx) => ctx.reply('Hi!'));
183
+ * bot.use(router.middleware());
184
+ */
185
+ class Router {
186
+ keyFn;
187
+ routes = new Map();
188
+ fallback;
189
+ constructor(keyFn) {
190
+ this.keyFn = keyFn;
191
+ }
192
+ on(key, ...fns) {
193
+ this.routes.set(key, Composer.compose(fns));
194
+ return this;
195
+ }
196
+ otherwise(...fns) {
197
+ this.fallback = Composer.compose(fns);
198
+ return this;
199
+ }
200
+ middleware() {
201
+ return async (ctx, next) => {
202
+ const key = await this.keyFn(ctx);
203
+ const route = key !== undefined ? this.routes.get(key) : undefined;
204
+ if (route)
205
+ return route(ctx, next);
206
+ if (this.fallback)
207
+ return this.fallback(ctx, next);
208
+ return next();
209
+ };
210
+ }
211
+ }
212
+ exports.Router = Router;
213
+ async function runChain(fns, ctx, done) {
214
+ let i = -1;
215
+ async function dispatch(idx) {
216
+ if (idx <= i)
217
+ throw new Error("next() called multiple times");
218
+ i = idx;
219
+ const fn = fns[idx];
220
+ if (!fn)
221
+ return done();
222
+ await fn(ctx, () => dispatch(idx + 1));
223
+ }
224
+ await dispatch(0);
225
+ }
226
+ function matchesUpdateType(ctx, type) {
227
+ if (ctx.updateType === type)
228
+ return true;
229
+ if (ctx.message && type in ctx.message)
230
+ return true;
231
+ return false;
232
+ }
233
+ //# sourceMappingURL=composer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composer.js","sourceRoot":"","sources":["../src/composer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import { Api } from "./client";
2
+ import * as T from "./types";
3
+ /**
4
+ * Context wraps one incoming Update with the bits people reach for constantly:
5
+ * ctx.message, ctx.chat, ctx.from, ctx.reply(...), ctx.match (from hears/command regex).
6
+ *
7
+ * Every reply/send helper is chainable-friendly (returns the sent Message),
8
+ * and all take the same options object shape as the underlying Api method —
9
+ * no new vocabulary to learn on top of the Bot API docs.
10
+ */
11
+ export declare class Context {
12
+ readonly update: T.Update;
13
+ readonly api: Api;
14
+ readonly botInfo: T.User;
15
+ /** Populated by Composer when a command/hears/action pattern captured groups. */
16
+ match: RegExpMatchArray | null;
17
+ /** Free-for-all bag for your own middleware to stash things (auth info, etc). */
18
+ state: Record<string, unknown>;
19
+ /** Populated by the `session()` middleware — persisted across updates for the same chat (or user, if configured). */
20
+ session?: Record<string, unknown>;
21
+ /** Populated by `Stage.middleware()` (see scenes.ts) once mounted — lets any handler call ctx.scene.enter(...)/leave(). */
22
+ scene?: {
23
+ enter: (sceneId: string, initialState?: Record<string, unknown>) => Promise<void>;
24
+ leave: () => Promise<void>;
25
+ current: string | undefined;
26
+ };
27
+ /** Populated by `Stage.middleware()` only while a scene is active for this update. */
28
+ wizard?: {
29
+ cursor: number;
30
+ state: Record<string, unknown>;
31
+ next: () => Promise<void>;
32
+ selectStep: (index: number) => Promise<void>;
33
+ };
34
+ constructor(update: T.Update, api: Api, botInfo: T.User);
35
+ get updateType(): T.UpdateType;
36
+ get message(): T.Message | undefined;
37
+ get callbackQuery(): T.CallbackQuery | undefined;
38
+ get inlineQuery(): T.InlineQuery | undefined;
39
+ get chat(): T.Chat | undefined;
40
+ get from(): T.User | undefined;
41
+ get text(): string | undefined;
42
+ get chatId(): T.ChatId | undefined;
43
+ private baseSend;
44
+ reply(text: string, extra?: Omit<Parameters<Api["sendMessage"]>[0], "chat_id" | "text">): Promise<T.Message>;
45
+ /** Send a Rich Message (Bot API 10.1) — tables, checklists, blockquotes, inline media. */
46
+ replyRich(rich: T.InputRichMessage, extra?: Omit<Parameters<Api["sendRichMessage"]>[0], "chat_id" | "rich_message">): Promise<T.Message>;
47
+ replyWithPhoto(photo: T.InputFile, extra?: Omit<Parameters<Api["sendPhoto"]>[0], "chat_id" | "photo">): Promise<T.Message>;
48
+ replyWithVideo(video: T.InputFile, extra?: Omit<Parameters<Api["sendVideo"]>[0], "chat_id" | "video">): Promise<T.Message>;
49
+ replyWithDocument(document: T.InputFile, extra?: Omit<Parameters<Api["sendDocument"]>[0], "chat_id" | "document">): Promise<T.Message>;
50
+ replyWithAudio(audio: T.InputFile, extra?: Omit<Parameters<Api["sendAudio"]>[0], "chat_id" | "audio">): Promise<T.Message>;
51
+ replyWithSticker(sticker: T.InputFile, extra?: Omit<Parameters<Api["sendSticker"]>[0], "chat_id" | "sticker">): Promise<T.Message>;
52
+ replyWithPoll(question: string, options: string[], extra?: Omit<Parameters<Api["sendPoll"]>[0], "chat_id" | "question" | "options">): Promise<T.Message>;
53
+ replyWithChatAction(action: Parameters<Api["sendChatAction"]>[0]["action"]): Promise<true>;
54
+ /**
55
+ * The message_id to edit/delete when no explicit one is given — resolves
56
+ * from the current message OR, critically, from callback_query.message
57
+ * (the "user taps a button, bot edits that message" pattern). Previously
58
+ * editText()/deleteMessage() only checked ctx.message, which is always
59
+ * undefined for callback_query updates, silently breaking the single most
60
+ * common edit-on-button-tap pattern.
61
+ */
62
+ private get editableMessageId();
63
+ /** Edit the message this context came from — works from a plain message context and from inside action()/callback_query handlers. */
64
+ editText(text: string, extra?: Record<string, unknown>): Promise<true | T.Message>;
65
+ editCaption(caption: string, extra?: Record<string, unknown>): Promise<true | T.Message>;
66
+ editMedia(media: T.InputMedia, extra?: Record<string, unknown>): Promise<true | T.Message>;
67
+ /** Update just the inline keyboard — e.g. toggling a selection without resending the message. */
68
+ editReplyMarkup(reply_markup?: T.InlineKeyboardMarkup): Promise<true | T.Message>;
69
+ deleteMessage(messageId?: T.Integer): Promise<true>;
70
+ answerCbQuery(text?: string, extra?: Omit<Parameters<Api["answerCallbackQuery"]>[0], "callback_query_id" | "text">): Promise<true>;
71
+ answerInlineQuery(results: T.InlineQueryResult[], extra?: Omit<Parameters<Api["answerInlineQuery"]>[0], "inline_query_id" | "results">): Promise<true>;
72
+ }
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Context = void 0;
4
+ /**
5
+ * Context wraps one incoming Update with the bits people reach for constantly:
6
+ * ctx.message, ctx.chat, ctx.from, ctx.reply(...), ctx.match (from hears/command regex).
7
+ *
8
+ * Every reply/send helper is chainable-friendly (returns the sent Message),
9
+ * and all take the same options object shape as the underlying Api method —
10
+ * no new vocabulary to learn on top of the Bot API docs.
11
+ */
12
+ class Context {
13
+ update;
14
+ api;
15
+ botInfo;
16
+ /** Populated by Composer when a command/hears/action pattern captured groups. */
17
+ match = null;
18
+ /** Free-for-all bag for your own middleware to stash things (auth info, etc). */
19
+ state = {};
20
+ /** Populated by the `session()` middleware — persisted across updates for the same chat (or user, if configured). */
21
+ session;
22
+ /** Populated by `Stage.middleware()` (see scenes.ts) once mounted — lets any handler call ctx.scene.enter(...)/leave(). */
23
+ scene;
24
+ /** Populated by `Stage.middleware()` only while a scene is active for this update. */
25
+ wizard;
26
+ constructor(update, api, botInfo) {
27
+ this.update = update;
28
+ this.api = api;
29
+ this.botInfo = botInfo;
30
+ }
31
+ get updateType() {
32
+ const known = [
33
+ "message", "edited_message", "channel_post", "edited_channel_post",
34
+ "business_message", "inline_query", "chosen_inline_result", "callback_query",
35
+ "shipping_query", "pre_checkout_query", "poll", "poll_answer",
36
+ "my_chat_member", "chat_member", "chat_join_request", "subscription",
37
+ "managed_bot_created", "managed_bot",
38
+ ];
39
+ return known.find((k) => this.update[k] !== undefined);
40
+ }
41
+ get message() {
42
+ return this.update.message ?? this.update.channel_post ?? this.update.business_message ?? this.update.edited_message;
43
+ }
44
+ get callbackQuery() { return this.update.callback_query; }
45
+ get inlineQuery() { return this.update.inline_query; }
46
+ get chat() {
47
+ return (this.message?.chat ??
48
+ this.callbackQuery?.message?.chat ??
49
+ this.update.my_chat_member?.chat ??
50
+ this.update.chat_member?.chat ??
51
+ this.update.chat_join_request?.chat ??
52
+ this.update.chat_boost?.chat ??
53
+ this.update.removed_chat_boost?.chat ??
54
+ this.update.poll_answer?.voter_chat);
55
+ }
56
+ get from() {
57
+ return (this.message?.from ??
58
+ this.callbackQuery?.from ??
59
+ this.inlineQuery?.from ??
60
+ this.update.chat_join_request?.from ??
61
+ this.update.my_chat_member?.from ??
62
+ this.update.chat_member?.from ??
63
+ this.update.shipping_query?.from ??
64
+ this.update.pre_checkout_query?.from ??
65
+ this.update.poll_answer?.user);
66
+ }
67
+ get text() { return this.message?.text; }
68
+ get chatId() { return this.chat?.id; }
69
+ // ----- Reply helpers (auto-fill chat_id + reply_to_message_id) -----
70
+ baseSend() {
71
+ if (this.chatId === undefined)
72
+ throw new Error("ctx has no chat to reply to for this update type");
73
+ return { chat_id: this.chatId };
74
+ }
75
+ reply(text, extra = {}) {
76
+ return this.api.sendMessage({ ...this.baseSend(), text, ...extra });
77
+ }
78
+ /** Send a Rich Message (Bot API 10.1) — tables, checklists, blockquotes, inline media. */
79
+ replyRich(rich, extra = {}) {
80
+ return this.api.sendRichMessage({ ...this.baseSend(), rich_message: rich, ...extra });
81
+ }
82
+ replyWithPhoto(photo, extra = {}) {
83
+ return this.api.sendPhoto({ ...this.baseSend(), photo, ...extra });
84
+ }
85
+ replyWithVideo(video, extra = {}) {
86
+ return this.api.sendVideo({ ...this.baseSend(), video, ...extra });
87
+ }
88
+ replyWithDocument(document, extra = {}) {
89
+ return this.api.sendDocument({ ...this.baseSend(), document, ...extra });
90
+ }
91
+ replyWithAudio(audio, extra = {}) {
92
+ return this.api.sendAudio({ ...this.baseSend(), audio, ...extra });
93
+ }
94
+ replyWithSticker(sticker, extra = {}) {
95
+ return this.api.sendSticker({ ...this.baseSend(), sticker, ...extra });
96
+ }
97
+ replyWithPoll(question, options, extra = {}) {
98
+ return this.api.sendPoll({ ...this.baseSend(), question, options, ...extra });
99
+ }
100
+ replyWithChatAction(action) {
101
+ return this.api.sendChatAction({ ...this.baseSend(), action });
102
+ }
103
+ /**
104
+ * The message_id to edit/delete when no explicit one is given — resolves
105
+ * from the current message OR, critically, from callback_query.message
106
+ * (the "user taps a button, bot edits that message" pattern). Previously
107
+ * editText()/deleteMessage() only checked ctx.message, which is always
108
+ * undefined for callback_query updates, silently breaking the single most
109
+ * common edit-on-button-tap pattern.
110
+ */
111
+ get editableMessageId() {
112
+ return this.message?.message_id ?? this.callbackQuery?.message?.message_id;
113
+ }
114
+ /** Edit the message this context came from — works from a plain message context and from inside action()/callback_query handlers. */
115
+ editText(text, extra = {}) {
116
+ return this.api.editMessageText({ chat_id: this.chatId, message_id: this.editableMessageId, text, ...extra });
117
+ }
118
+ editCaption(caption, extra = {}) {
119
+ return this.api.editMessageCaption({ chat_id: this.chatId, message_id: this.editableMessageId, caption, ...extra });
120
+ }
121
+ editMedia(media, extra = {}) {
122
+ return this.api.editMessageMedia({ chat_id: this.chatId, message_id: this.editableMessageId, media, ...extra });
123
+ }
124
+ /** Update just the inline keyboard — e.g. toggling a selection without resending the message. */
125
+ editReplyMarkup(reply_markup) {
126
+ return this.api.editMessageReplyMarkup({ chat_id: this.chatId, message_id: this.editableMessageId, reply_markup });
127
+ }
128
+ deleteMessage(messageId) {
129
+ const id = messageId ?? this.editableMessageId;
130
+ if (this.chatId === undefined || id === undefined) {
131
+ throw new Error("ctx has no message to delete for this update type — pass a message_id explicitly");
132
+ }
133
+ return this.api.deleteMessage({ chat_id: this.chatId, message_id: id });
134
+ }
135
+ // ----- Callback query -----
136
+ answerCbQuery(text, extra = {}) {
137
+ if (!this.callbackQuery)
138
+ throw new Error("No callback_query on this update");
139
+ return this.api.answerCallbackQuery({ callback_query_id: this.callbackQuery.id, text, ...extra });
140
+ }
141
+ // ----- Inline query -----
142
+ answerInlineQuery(results, extra = {}) {
143
+ if (!this.inlineQuery)
144
+ throw new Error("No inline_query on this update");
145
+ return this.api.answerInlineQuery({ inline_query_id: this.inlineQuery.id, results, ...extra });
146
+ }
147
+ }
148
+ exports.Context = Context;
149
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { Context } from "./context";
2
+ /**
3
+ * Decorator-based controllers, for people coming from aiogram's class-based
4
+ * routers or from Nest/Spring-style frameworks. These are pure sugar: every
5
+ * decorator just records "when X matches, call this method" into a registry,
6
+ * and `bot.useController(instance)` replays that registry onto the same
7
+ * Composer used by the functional and fluent styles — so all three styles
8
+ * can be mixed in one bot.
9
+ */
10
+ type HandlerKind = {
11
+ kind: "command";
12
+ value: string | string[];
13
+ } | {
14
+ kind: "hears";
15
+ value: string | RegExp;
16
+ } | {
17
+ kind: "action";
18
+ value: string | RegExp;
19
+ } | {
20
+ kind: "on";
21
+ value: string;
22
+ };
23
+ export declare function getControllerHandlers(instance: object): {
24
+ meta: HandlerKind;
25
+ fn: (ctx: Context, next: () => Promise<void>) => unknown;
26
+ }[];
27
+ /** Class decorator — purely documentational/marker, so tgplus controllers are easy to spot. Optional. */
28
+ export declare function BotController(): ClassDecorator;
29
+ export declare function Command(name: string | string[]): MethodDecorator;
30
+ export declare function Hears(trigger: string | RegExp): MethodDecorator;
31
+ export declare function Action(trigger: string | RegExp): MethodDecorator;
32
+ export declare function On(updateType: string): MethodDecorator;
33
+ export {};
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getControllerHandlers = getControllerHandlers;
4
+ exports.BotController = BotController;
5
+ exports.Command = Command;
6
+ exports.Hears = Hears;
7
+ exports.Action = Action;
8
+ exports.On = On;
9
+ const registry = new WeakMap();
10
+ function register(target, methodName, meta) {
11
+ const ctor = target.constructor;
12
+ const list = registry.get(ctor) ?? [];
13
+ list.push({ ...meta, methodName });
14
+ registry.set(ctor, list);
15
+ }
16
+ function getControllerHandlers(instance) {
17
+ const list = registry.get(instance.constructor) ?? [];
18
+ return list.map((h) => ({
19
+ meta: h,
20
+ fn: (ctx, next) => instance[h.methodName](ctx, next),
21
+ }));
22
+ }
23
+ /** Class decorator — purely documentational/marker, so tgplus controllers are easy to spot. Optional. */
24
+ function BotController() {
25
+ return (target) => target;
26
+ }
27
+ function Command(name) {
28
+ return (target, propertyKey) => register(target, propertyKey, { kind: "command", value: name });
29
+ }
30
+ function Hears(trigger) {
31
+ return (target, propertyKey) => register(target, propertyKey, { kind: "hears", value: trigger });
32
+ }
33
+ function Action(trigger) {
34
+ return (target, propertyKey) => register(target, propertyKey, { kind: "action", value: trigger });
35
+ }
36
+ function On(updateType) {
37
+ return (target, propertyKey) => register(target, propertyKey, { kind: "on", value: updateType });
38
+ }
39
+ //# sourceMappingURL=decorators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":";;AA4BA,sDAMC;AAGD,sCAEC;AAED,0BAEC;AAED,sBAEC;AAED,wBAEC;AAED,gBAEC;AApCD,MAAM,QAAQ,GAAG,IAAI,OAAO,EAA2B,CAAC;AAExD,SAAS,QAAQ,CAAC,MAAc,EAAE,UAA2B,EAAE,IAAiB;IAC9E,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,CAAC;IAChC,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACtC,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IACnC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED,SAAgB,qBAAqB,CAAC,QAAgB;IACpD,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;IACtD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtB,IAAI,EAAE,CAAC;QACP,EAAE,EAAE,CAAC,GAAY,EAAE,IAAyB,EAAE,EAAE,CAAE,QAAgB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC;KAC5F,CAAC,CAAC,CAAC;AACN,CAAC;AAED,yGAAyG;AACzG,SAAgB,aAAa;IAC3B,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC;AAC5B,CAAC;AAED,SAAgB,OAAO,CAAC,IAAuB;IAC7C,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAgB,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5G,CAAC;AAED,SAAgB,KAAK,CAAC,OAAwB;IAC5C,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAgB,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;AAC7G,CAAC;AAED,SAAgB,MAAM,CAAC,OAAwB;IAC7C,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAgB,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;AAC9G,CAAC;AAED,SAAgB,EAAE,CAAC,UAAkB;IACnC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAgB,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;AAC7G,CAAC"}