gramio 0.5.0 → 0.6.1

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/dist/index.js CHANGED
@@ -9,177 +9,181 @@ export * from '@gramio/keyboards';
9
9
  import fs from 'node:fs/promises';
10
10
  import { Readable } from 'node:stream';
11
11
  import debug from 'debug';
12
- import { E as ErrorKind, w as withRetries, T as TelegramError, s as sleep, d as debug$updates, I as IS_BUN, a as simplifyObject, t as timeoutWebhook } from './utils-En0iVLWH.js';
13
- import { createComposer, noopNext, eventTypes, EventQueue } from '@gramio/composer';
14
- export { EventQueue, compose, noopNext, skip, stop } from '@gramio/composer';
12
+ import { E as ErrorKind, w as withRetries, T as TelegramError, s as sleep, d as debug$updates, I as IS_BUN, a as simplifyObject, t as timeoutWebhook } from './utils-DqK73ALI.js';
13
+ import { defineComposerMethods, buildFromOptions, noopNext, createComposer, eventTypes, EventQueue } from '@gramio/composer';
14
+ export { EventQueue, buildFromOptions, compose, noopNext, skip, stop } from '@gramio/composer';
15
15
 
16
- const { Composer } = createComposer({
17
- discriminator: (ctx) => ctx.updateType,
18
- types: eventTypes(),
19
- methods: {
20
- reaction(trigger, handler) {
21
- const reactions = Array.isArray(trigger) ? trigger : [trigger];
22
- return this.on("message_reaction", (context, next) => {
23
- const newReactions = [];
24
- for (const reaction of context.newReactions) {
25
- if (reaction.type !== "emoji") continue;
26
- const foundIndex = context.oldReactions.findIndex(
27
- (oldReaction) => oldReaction.type === "emoji" && oldReaction.emoji === reaction.emoji
28
- );
29
- if (foundIndex === -1) {
30
- newReactions.push(reaction);
31
- } else {
32
- context.oldReactions.splice(foundIndex, 1);
33
- }
16
+ const methods = defineComposerMethods({
17
+ reaction(trigger, handler, macroOptions) {
18
+ const reactions = Array.isArray(trigger) ? trigger : [trigger];
19
+ const macroHandler = macroOptions ? buildFromOptions(this["~"].macros, macroOptions, handler) : null;
20
+ return this.on(
21
+ "message_reaction",
22
+ (context, next) => {
23
+ const oldEmojis = /* @__PURE__ */ new Set();
24
+ for (const r of context.oldReactions) {
25
+ if (r.type === "emoji") oldEmojis.add(r.emoji);
34
26
  }
35
- if (!newReactions.some(
36
- (x) => x.type === "emoji" && reactions.includes(x.emoji)
37
- ))
38
- return next();
39
- return handler(context);
40
- });
41
- },
42
- callbackQuery(trigger, handler) {
43
- if (typeof trigger === "string") {
44
- return this.on("callback_query", (context, next) => {
45
- if (context.data !== trigger) return next();
46
- return handler(context);
47
- });
48
- }
49
- if (trigger instanceof RegExp) {
50
- return this.on("callback_query", (context, next) => {
51
- if (!context.data || !trigger.test(context.data)) return next();
52
- context.queryData = context.data.match(trigger);
53
- return handler(context);
54
- });
27
+ const hasNewMatchingReaction = context.newReactions.some(
28
+ (reaction) => reaction.type === "emoji" && !oldEmojis.has(reaction.emoji) && reactions.includes(reaction.emoji)
29
+ );
30
+ if (!hasNewMatchingReaction) return next();
31
+ return macroHandler ? macroHandler(context, noopNext) : handler(context);
55
32
  }
56
- const regexp = trigger.regexp();
33
+ );
34
+ },
35
+ callbackQuery(trigger, handler, macroOptions) {
36
+ const macroHandler = macroOptions ? buildFromOptions(this["~"].macros, macroOptions, handler) : null;
37
+ if (typeof trigger === "string") {
57
38
  return this.on("callback_query", (context, next) => {
58
- if (!context.data || !regexp.test(context.data)) return next();
59
- context.queryData = trigger.unpack(context.data);
60
- return handler(context);
39
+ if (context.data !== trigger) return next();
40
+ return macroHandler ? macroHandler(context, noopNext) : handler(context);
61
41
  });
62
- },
63
- chosenInlineResult(trigger, handler) {
64
- if (typeof trigger === "string") {
65
- return this.on("chosen_inline_result", (context, next) => {
66
- if (context.query !== trigger) return next();
67
- context.args = null;
68
- return handler(context);
69
- });
70
- }
71
- if (trigger instanceof RegExp) {
72
- return this.on("chosen_inline_result", (context, next) => {
73
- if (!trigger.test(context.query)) return next();
74
- context.args = context.query?.match(trigger);
75
- return handler(context);
76
- });
77
- }
42
+ }
43
+ if (trigger instanceof RegExp) {
44
+ return this.on("callback_query", (context, next) => {
45
+ if (!context.data || !trigger.test(context.data)) return next();
46
+ context.queryData = context.data.match(trigger);
47
+ return macroHandler ? macroHandler(context, noopNext) : handler(context);
48
+ });
49
+ }
50
+ return this.on("callback_query", (context, next) => {
51
+ if (!context.data || !trigger.filter(context.data)) return next();
52
+ context.queryData = trigger.unpack(context.data);
53
+ return macroHandler ? macroHandler(context, noopNext) : handler(context);
54
+ });
55
+ },
56
+ chosenInlineResult(trigger, handler, macroOptions) {
57
+ const macroHandler = macroOptions ? buildFromOptions(this["~"].macros, macroOptions, handler) : null;
58
+ if (typeof trigger === "string") {
78
59
  return this.on("chosen_inline_result", (context, next) => {
79
- if (!trigger(context)) return next();
60
+ if (context.query !== trigger) return next();
80
61
  context.args = null;
81
- return handler(context);
62
+ return macroHandler ? macroHandler(context, noopNext) : handler(context);
82
63
  });
83
- },
84
- inlineQuery(trigger, handler, options = {}) {
85
- if (options.onResult)
86
- this.chosenInlineResult(trigger, options.onResult);
87
- if (typeof trigger === "string") {
88
- return this.on("inline_query", (context, next) => {
89
- if (context.query !== trigger) return next();
90
- context.args = null;
91
- return handler(context);
92
- });
93
- }
94
- if (trigger instanceof RegExp) {
95
- return this.on("inline_query", (context, next) => {
96
- if (!trigger.test(context.query)) return next();
97
- context.args = context.query?.match(trigger);
98
- return handler(context);
99
- });
100
- }
64
+ }
65
+ if (trigger instanceof RegExp) {
66
+ return this.on("chosen_inline_result", (context, next) => {
67
+ if (!trigger.test(context.query)) return next();
68
+ context.args = context.query?.match(trigger);
69
+ return macroHandler ? macroHandler(context, noopNext) : handler(context);
70
+ });
71
+ }
72
+ return this.on("chosen_inline_result", (context, next) => {
73
+ if (!trigger(context)) return next();
74
+ context.args = null;
75
+ return macroHandler ? macroHandler(context, noopNext) : handler(context);
76
+ });
77
+ },
78
+ inlineQuery(trigger, handler, options = {}) {
79
+ if (options.onResult) this.chosenInlineResult(trigger, options.onResult);
80
+ const { onResult: _, ...macroOptions } = options;
81
+ const hasMacros = Object.keys(macroOptions).length > 0;
82
+ const macroHandler = hasMacros ? buildFromOptions(this["~"].macros, macroOptions, handler) : null;
83
+ if (typeof trigger === "string") {
101
84
  return this.on("inline_query", (context, next) => {
102
- if (!trigger(context)) return next();
85
+ if (context.query !== trigger) return next();
103
86
  context.args = null;
104
- return handler(context);
87
+ return macroHandler ? macroHandler(context, noopNext) : handler(context);
105
88
  });
106
- },
107
- hears(trigger, handler) {
108
- if (typeof trigger === "string") {
109
- return this.on("message", (context, next) => {
110
- if ((context.text ?? context.caption) !== trigger) return next();
111
- context.args = null;
112
- return handler(context);
113
- });
114
- }
115
- if (Array.isArray(trigger)) {
116
- return this.on("message", (context, next) => {
117
- const text = context.text ?? context.caption;
118
- if (!text || !trigger.includes(text)) return next();
119
- context.args = null;
120
- return handler(context);
121
- });
122
- }
123
- if (trigger instanceof RegExp) {
124
- return this.on("message", (context, next) => {
125
- const text = context.text ?? context.caption;
126
- if (!text || !trigger.test(text)) return next();
127
- context.args = text.match(trigger);
128
- return handler(context);
129
- });
130
- }
89
+ }
90
+ if (trigger instanceof RegExp) {
91
+ return this.on("inline_query", (context, next) => {
92
+ if (!trigger.test(context.query)) return next();
93
+ context.args = context.query?.match(trigger);
94
+ return macroHandler ? macroHandler(context, noopNext) : handler(context);
95
+ });
96
+ }
97
+ return this.on("inline_query", (context, next) => {
98
+ if (!trigger(context)) return next();
99
+ context.args = null;
100
+ return macroHandler ? macroHandler(context, noopNext) : handler(context);
101
+ });
102
+ },
103
+ hears(trigger, handler, macroOptions) {
104
+ const macroHandler = macroOptions ? buildFromOptions(this["~"].macros, macroOptions, handler) : null;
105
+ if (typeof trigger === "string") {
131
106
  return this.on("message", (context, next) => {
132
- if (typeof trigger !== "function" || !trigger(context)) return next();
107
+ if ((context.text ?? context.caption) !== trigger) return next();
133
108
  context.args = null;
134
- return handler(context);
109
+ return macroHandler ? macroHandler(context, noopNext) : handler(context);
135
110
  });
136
- },
137
- command(command, handler) {
138
- const normalizedCommands = typeof command === "string" ? [command] : Array.from(command);
139
- for (const cmd of normalizedCommands) {
140
- if (cmd.startsWith("/"))
141
- throw new Error(`Do not use / in command name (${cmd})`);
142
- }
143
- return this.on(["message", "business_message"], (context, next) => {
144
- if (context.entities?.some((entity) => {
145
- if (entity.type !== "bot_command" || entity.offset > 0)
146
- return false;
147
- const botInfo = context.bot.info;
148
- const cmd = context.text?.slice(1, entity.length)?.replace(
149
- botInfo?.username ? `@${botInfo.username}` : /@[a-zA-Z0-9_]+/,
150
- ""
151
- );
152
- context.args = context.text?.slice(entity.length).trim() || null;
153
- return normalizedCommands.some(
154
- (normalizedCommand) => cmd === normalizedCommand
155
- );
156
- }))
157
- return handler(context);
158
- return next();
111
+ }
112
+ if (Array.isArray(trigger)) {
113
+ return this.on("message", (context, next) => {
114
+ const text = context.text ?? context.caption;
115
+ if (!text || !trigger.includes(text)) return next();
116
+ context.args = null;
117
+ return macroHandler ? macroHandler(context, noopNext) : handler(context);
159
118
  });
160
- },
161
- startParameter(parameter, handler) {
162
- if (parameter instanceof RegExp) {
163
- return this.on("message", (context, next) => {
164
- if (!context.rawStartPayload || !parameter.test(context.rawStartPayload))
165
- return next();
166
- return handler(context, noopNext);
167
- });
168
- }
169
- if (Array.isArray(parameter)) {
170
- return this.on("message", (context, next) => {
171
- if (!context.rawStartPayload || !parameter.includes(context.rawStartPayload))
172
- return next();
173
- return handler(context, noopNext);
174
- });
119
+ }
120
+ if (trigger instanceof RegExp) {
121
+ return this.on("message", (context, next) => {
122
+ const text = context.text ?? context.caption;
123
+ if (!text || !trigger.test(text)) return next();
124
+ context.args = text.match(trigger);
125
+ return macroHandler ? macroHandler(context, noopNext) : handler(context);
126
+ });
127
+ }
128
+ return this.on("message", (context, next) => {
129
+ if (typeof trigger !== "function" || !trigger(context)) return next();
130
+ context.args = null;
131
+ return macroHandler ? macroHandler(context, noopNext) : handler(context);
132
+ });
133
+ },
134
+ command(command, handler, macroOptions) {
135
+ const normalizedCommands = typeof command === "string" ? [command] : Array.from(command);
136
+ for (const cmd of normalizedCommands) {
137
+ if (cmd.startsWith("/"))
138
+ throw new Error(`Do not use / in command name (${cmd})`);
139
+ }
140
+ const macroHandler = macroOptions ? buildFromOptions(this["~"].macros, macroOptions, handler) : null;
141
+ return this.on(["message", "business_message"], (context, next) => {
142
+ const entity = context.entities?.find((entity2) => {
143
+ if (entity2.type !== "bot_command" || entity2.offset > 0) return false;
144
+ const botInfo = context.bot.info;
145
+ const cmd = context.text?.slice(1, entity2.length)?.replace(
146
+ botInfo?.username ? `@${botInfo.username}` : /@[a-zA-Z0-9_]+/,
147
+ ""
148
+ );
149
+ return normalizedCommands.some(
150
+ (normalizedCommand) => cmd === normalizedCommand
151
+ );
152
+ });
153
+ if (entity) {
154
+ context.args = context.text?.slice(entity.length).trim() || null;
155
+ return macroHandler ? macroHandler(context, noopNext) : handler(context);
175
156
  }
157
+ return next();
158
+ });
159
+ },
160
+ startParameter(parameter, handler, macroOptions) {
161
+ const macroHandler = macroOptions ? buildFromOptions(this["~"].macros, macroOptions, handler) : null;
162
+ if (parameter instanceof RegExp) {
163
+ return this.on("message", (context, next) => {
164
+ if (!context.rawStartPayload || !parameter.test(context.rawStartPayload))
165
+ return next();
166
+ return macroHandler ? macroHandler(context, noopNext) : handler(context, noopNext);
167
+ });
168
+ }
169
+ if (Array.isArray(parameter)) {
176
170
  return this.on("message", (context, next) => {
177
- if (context.rawStartPayload !== parameter) return next();
178
- return handler(context, noopNext);
171
+ if (!context.rawStartPayload || !parameter.includes(context.rawStartPayload))
172
+ return next();
173
+ return macroHandler ? macroHandler(context, noopNext) : handler(context, noopNext);
179
174
  });
180
175
  }
176
+ return this.on("message", (context, next) => {
177
+ if (!context.rawStartPayload || context.rawStartPayload !== parameter) return next();
178
+ return macroHandler ? macroHandler(context, noopNext) : handler(context, noopNext);
179
+ });
181
180
  }
182
181
  });
182
+ const { Composer } = createComposer({
183
+ discriminator: (ctx) => ctx.updateType,
184
+ types: eventTypes(),
185
+ methods
186
+ });
183
187
 
184
188
  class Plugin {
185
189
  /**
@@ -197,6 +201,8 @@ class Plugin {
197
201
  Errors: {},
198
202
  /** remap generic type. {} in runtime */
199
203
  Derives: {},
204
+ /** remap generic type. {} in runtime */
205
+ Macros: {},
200
206
  /** Composer */
201
207
  composer: new Composer(),
202
208
  /** Store plugin preRequests hooks */
@@ -273,9 +279,27 @@ class Plugin {
273
279
  }
274
280
  return this;
275
281
  }
276
- /** Register handler to one or many Updates */
277
- on(updateName, handler) {
278
- this._.composer.on(updateName, handler);
282
+ macro(nameOrDefs, definition) {
283
+ this._.composer.macro(nameOrDefs, definition);
284
+ return this;
285
+ }
286
+ on(updateNameOrFilter, filterOrHandler, handler) {
287
+ if (typeof updateNameOrFilter === "function") {
288
+ this._.composer.on(updateNameOrFilter, filterOrHandler);
289
+ return this;
290
+ }
291
+ if (handler) {
292
+ this._.composer.on(
293
+ updateNameOrFilter,
294
+ filterOrHandler,
295
+ handler
296
+ );
297
+ } else {
298
+ this._.composer.on(
299
+ updateNameOrFilter,
300
+ filterOrHandler
301
+ );
302
+ }
279
303
  return this;
280
304
  }
281
305
  /** Register handler to any Updates */
@@ -765,6 +789,10 @@ class Bot {
765
789
  );
766
790
  return this;
767
791
  }
792
+ macro(nameOrDefs, definition) {
793
+ this.updates.composer.macro(nameOrDefs, definition);
794
+ return this;
795
+ }
768
796
  /**
769
797
  * This hook called when the bot is `started`.
770
798
  *
@@ -859,22 +887,26 @@ class Bot {
859
887
  }
860
888
  return this;
861
889
  }
862
- // onExperimental(
863
- // // filter: Filters,
864
- // filter: (
865
- // f: Filters<
866
- // Context<typeof this> & Derives["global"],
867
- // [{ equal: { prop: number }; addition: { some: () => 2 } }]
868
- // >,
869
- // ) => Filters,
870
- // handler: Handler<Context<typeof this> & Derives["global"]>,
871
- // ) {}
872
- /** Register handler to one or many Updates */
873
- on(updateName, handler) {
874
- this.updates.composer.on(
875
- updateName,
876
- handler
877
- );
890
+ on(updateNameOrFilter, filterOrHandler, handler) {
891
+ if (typeof updateNameOrFilter === "function") {
892
+ this.updates.composer.on(
893
+ updateNameOrFilter,
894
+ filterOrHandler
895
+ );
896
+ return this;
897
+ }
898
+ if (handler) {
899
+ this.updates.composer.on(
900
+ updateNameOrFilter,
901
+ filterOrHandler,
902
+ handler
903
+ );
904
+ } else {
905
+ this.updates.composer.on(
906
+ updateNameOrFilter,
907
+ filterOrHandler
908
+ );
909
+ }
878
910
  return this;
879
911
  }
880
912
  /** Register handler to any Updates */
@@ -899,6 +931,11 @@ class Bot {
899
931
  if (plugin._.composer["~"].middlewares.length) {
900
932
  plugin._.composer.as("scoped");
901
933
  this.updates.composer.extend(plugin._.composer);
934
+ } else if (Object.keys(plugin._.composer["~"].macros).length) {
935
+ Object.assign(
936
+ this.updates.composer["~"].macros,
937
+ plugin._.composer["~"].macros
938
+ );
902
939
  }
903
940
  this.decorate(plugin._.decorators);
904
941
  for (const [key, value] of Object.entries(plugin._.errorsDefinitions)) {
@@ -949,8 +986,8 @@ class Bot {
949
986
  * });
950
987
  * ```
951
988
  * */
952
- reaction(trigger, handler) {
953
- this.updates.composer.reaction(trigger, handler);
989
+ reaction(trigger, handler, options) {
990
+ this.updates.composer.reaction(trigger, handler, options);
954
991
  return this;
955
992
  }
956
993
  /**
@@ -976,13 +1013,21 @@ class Bot {
976
1013
  * });
977
1014
  * ```
978
1015
  */
979
- callbackQuery(trigger, handler) {
980
- this.updates.composer.callbackQuery(trigger, handler);
1016
+ callbackQuery(trigger, handler, options) {
1017
+ this.updates.composer.callbackQuery(
1018
+ trigger,
1019
+ handler,
1020
+ options
1021
+ );
981
1022
  return this;
982
1023
  }
983
1024
  /** Register handler to `chosen_inline_result` update */
984
- chosenInlineResult(trigger, handler) {
985
- this.updates.composer.chosenInlineResult(trigger, handler);
1025
+ chosenInlineResult(trigger, handler, options) {
1026
+ this.updates.composer.chosenInlineResult(
1027
+ trigger,
1028
+ handler,
1029
+ options
1030
+ );
986
1031
  return this;
987
1032
  }
988
1033
  /**
@@ -1020,7 +1065,7 @@ class Bot {
1020
1065
  * );
1021
1066
  * ```
1022
1067
  * */
1023
- inlineQuery(trigger, handler, options = {}) {
1068
+ inlineQuery(trigger, handler, options) {
1024
1069
  this.updates.composer.inlineQuery(
1025
1070
  trigger,
1026
1071
  handler,
@@ -1038,8 +1083,8 @@ class Bot {
1038
1083
  * });
1039
1084
  * ```
1040
1085
  */
1041
- hears(trigger, handler) {
1042
- this.updates.composer.hears(trigger, handler);
1086
+ hears(trigger, handler, options) {
1087
+ this.updates.composer.hears(trigger, handler, options);
1043
1088
  return this;
1044
1089
  }
1045
1090
  /**
@@ -1052,8 +1097,8 @@ class Bot {
1052
1097
  * });
1053
1098
  * ```
1054
1099
  */
1055
- command(command, handler) {
1056
- this.updates.composer.command(command, handler);
1100
+ command(command, handler, options) {
1101
+ this.updates.composer.command(command, handler, options);
1057
1102
  return this;
1058
1103
  }
1059
1104
  /**
@@ -1066,8 +1111,12 @@ class Bot {
1066
1111
  * });
1067
1112
  * ```
1068
1113
  */
1069
- startParameter(parameter, handler) {
1070
- this.updates.composer.startParameter(parameter, handler);
1114
+ startParameter(parameter, handler, options) {
1115
+ this.updates.composer.startParameter(
1116
+ parameter,
1117
+ handler,
1118
+ options
1119
+ );
1071
1120
  return this;
1072
1121
  }
1073
1122
  /** Currently not isolated!!! */
@@ -1178,13 +1227,185 @@ class Bot {
1178
1227
  }
1179
1228
  }
1180
1229
 
1230
+ const forwardOriginFilter = ((type) => {
1231
+ if (type === void 0) {
1232
+ return ((ctx) => ctx.hasForwardOrigin());
1233
+ }
1234
+ return ((ctx) => ctx.hasForwardOrigin() && ctx.forwardOrigin?.type === type);
1235
+ });
1236
+ const senderChatFilter = ((type) => {
1237
+ if (type === void 0) {
1238
+ return ((ctx) => ctx.hasSenderChat());
1239
+ }
1240
+ return ((ctx) => ctx.hasSenderChat() && ctx.senderChat?.type === type);
1241
+ });
1242
+ function createAttachmentFilter(type) {
1243
+ return ((ctx) => ctx.hasAttachmentType(type));
1244
+ }
1245
+ const filters = {
1246
+ // ── Attachment filters ──────────────────────────────────────────────
1247
+ photo: createAttachmentFilter("photo"),
1248
+ video: createAttachmentFilter("video"),
1249
+ document: createAttachmentFilter("document"),
1250
+ audio: createAttachmentFilter("audio"),
1251
+ sticker: createAttachmentFilter("sticker"),
1252
+ voice: createAttachmentFilter("voice"),
1253
+ videoNote: createAttachmentFilter("video_note"),
1254
+ animation: createAttachmentFilter("animation"),
1255
+ contact: createAttachmentFilter("contact"),
1256
+ location: createAttachmentFilter("location"),
1257
+ poll: createAttachmentFilter("poll"),
1258
+ /** Matches any message that has an attachment */
1259
+ media: ((ctx) => ctx.hasAttachment()),
1260
+ // ── Text & caption ──────────────────────────────────────────────────
1261
+ /** Matches messages that have text */
1262
+ text: ((ctx) => ctx.hasText()),
1263
+ /** Matches messages that have a caption */
1264
+ caption: ((ctx) => ctx.hasCaption()),
1265
+ // ── Message content & structure ─────────────────────────────────────
1266
+ /** Matches messages that contain a dice */
1267
+ dice: ((ctx) => ctx.hasDice()),
1268
+ /** Matches forwarded messages. Call without args for any origin, or pass a type to narrow precisely. */
1269
+ forwardOrigin: forwardOriginFilter,
1270
+ /** Matches messages that are replies */
1271
+ reply: ((ctx) => ctx.hasReplyMessage()),
1272
+ /** Matches messages that have text entities */
1273
+ entities: ((ctx) => ctx.hasEntities()),
1274
+ /** Matches messages that have caption entities */
1275
+ captionEntities: ((ctx) => ctx.hasCaptionEntities()),
1276
+ /** Matches messages that have a quote */
1277
+ quote: ((ctx) => ctx.hasQuote()),
1278
+ /** Matches messages sent via a bot */
1279
+ viaBot: ((ctx) => ctx.hasViaBot()),
1280
+ /** Matches messages that have link preview options */
1281
+ linkPreview: ((ctx) => ctx.hasLinkPreviewOptions()),
1282
+ /** Matches messages with a /start payload */
1283
+ startPayload: ((ctx) => ctx.hasStartPayload()),
1284
+ /** Matches messages with a raw /start payload string */
1285
+ rawStartPayload: ((ctx) => ctx.rawStartPayload !== void 0),
1286
+ /** Matches messages with an author signature */
1287
+ authorSignature: ((ctx) => ctx.hasAuthorSignature()),
1288
+ /** Matches messages with external reply info */
1289
+ replyInfo: ((ctx) => ctx.hasReplyInfo()),
1290
+ /** Matches contexts that have a sender (from user) */
1291
+ hasFrom: ((ctx) => ctx.hasFrom()),
1292
+ /** Matches messages sent on behalf of a chat. Pass a type to also narrow `senderChat.type`. */
1293
+ senderChat: senderChatFilter,
1294
+ /** Matches giveaway messages */
1295
+ giveaway: ((ctx) => ctx.isGiveaway()),
1296
+ /** Matches messages with paid media */
1297
+ paidMedia: ((ctx) => ctx.paidMedia !== void 0),
1298
+ // ── Raw property narrowing ──────────────────────────────────────────
1299
+ /** Matches messages with a game */
1300
+ game: ((ctx) => ctx.game !== void 0),
1301
+ /** Matches messages with a story */
1302
+ story: ((ctx) => ctx.story !== void 0),
1303
+ /** Matches messages with an effect ID */
1304
+ effectId: ((ctx) => ctx.effectId !== void 0),
1305
+ /** Matches messages that belong to a media group */
1306
+ mediaGroup: ((ctx) => ctx.mediaGroupId !== void 0),
1307
+ /** Matches messages with a venue */
1308
+ venue: ((ctx) => ctx.venue !== void 0),
1309
+ // ── Sender/chat property filters (boolean, no narrowing) ────────────
1310
+ /** Matches messages from bot accounts */
1311
+ isBot: (ctx) => ctx.from?.isBot() === true,
1312
+ /** Matches messages from premium users */
1313
+ isPremium: (ctx) => ctx.from?.isPremium() === true,
1314
+ /** Matches messages in forum (topic) chats */
1315
+ isForum: (ctx) => ctx.isForum === true,
1316
+ // ── Message state filters ───────────────────────────────────────────
1317
+ /** Matches service messages */
1318
+ service: (ctx) => ctx.isServiceMessage?.() ?? false,
1319
+ /** Matches messages in topics */
1320
+ topicMessage: (ctx) => ctx.isTopicMessage?.() ?? false,
1321
+ /** Matches media with spoiler. Narrows `attachment` to confirm media is present. */
1322
+ mediaSpoiler: ((ctx) => ctx.hasAttachment() && ctx.hasMediaSpoiler?.() === true),
1323
+ /** Matches messages with protected content. No type narrowing. */
1324
+ protectedContent: (ctx) => ctx.hasProtectedContent?.() === true,
1325
+ /** Matches messages sent while user was offline. No type narrowing. */
1326
+ fromOffline: (ctx) => ctx.isFromOffline?.() === true,
1327
+ // ── Callback query specific filters ─────────────────────────────────
1328
+ /** Matches callback queries that have an associated message */
1329
+ hasMessage: ((ctx) => ctx.hasMessage?.()),
1330
+ /** Matches callback queries that have data */
1331
+ hasData: ((ctx) => ctx.hasData?.()),
1332
+ /** Matches callback queries with an inline message ID */
1333
+ hasInlineMessageId: ((ctx) => ctx.hasInlineMessageId?.()),
1334
+ /** Matches callback queries with a game short name */
1335
+ hasGameShortName: ((ctx) => ctx.hasGameShortName?.()),
1336
+ // ── Parameterized entity filters ────────────────────────────────────
1337
+ /** Matches messages with a specific text entity type */
1338
+ entity(type) {
1339
+ return ((ctx) => ctx.hasEntities(type));
1340
+ },
1341
+ /** Matches messages with a specific caption entity type */
1342
+ captionEntity(type) {
1343
+ return ((ctx) => ctx.hasCaptionEntities(type));
1344
+ },
1345
+ // ── Chat type shortcuts ─────────────────────────────────────────────
1346
+ /** Matches messages from a specific chat type. Narrows both `chatType` and `chat.type`. */
1347
+ chat(type) {
1348
+ return ((ctx) => ctx.chatType === type);
1349
+ },
1350
+ /** Matches private (DM) chats. Narrows both `chatType` and `chat.type`. */
1351
+ pm: ((ctx) => ctx.chatType === "private"),
1352
+ /** Matches group chats. Narrows both `chatType` and `chat.type`. */
1353
+ group: ((ctx) => ctx.chatType === "group"),
1354
+ /** Matches supergroup chats. Narrows both `chatType` and `chat.type`. */
1355
+ supergroup: ((ctx) => ctx.chatType === "supergroup"),
1356
+ /** Matches channel chats. Narrows both `chatType` and `chat.type`. */
1357
+ channel: ((ctx) => ctx.chatType === "channel"),
1358
+ // ── Parameterized filters ───────────────────────────────────────────
1359
+ /** Matches messages from specific user(s). No type narrowing. */
1360
+ from(userId) {
1361
+ const ids = Array.isArray(userId) ? userId : [userId];
1362
+ return (ctx) => ctx.senderId !== void 0 && ids.includes(ctx.senderId);
1363
+ },
1364
+ /** Matches messages in specific chat(s). No type narrowing. */
1365
+ chatId(chatId) {
1366
+ const ids = Array.isArray(chatId) ? chatId : [chatId];
1367
+ return (ctx) => ids.includes(ctx.chatId);
1368
+ },
1369
+ /** Matches messages whose text/caption matches the regex, sets `ctx.match` */
1370
+ regex(pattern) {
1371
+ return ((ctx) => {
1372
+ const value = ctx.text ?? ctx.caption;
1373
+ if (!value) return false;
1374
+ const result = value.match(pattern);
1375
+ if (!result) return false;
1376
+ ctx.match = result;
1377
+ return true;
1378
+ });
1379
+ },
1380
+ // ── Composition ─────────────────────────────────────────────────────
1381
+ /** Intersection: both filters must match */
1382
+ and(f1, f2) {
1383
+ return ((ctx) => f1(ctx) && f2(ctx));
1384
+ },
1385
+ /** Union: either filter must match */
1386
+ or(f1, f2) {
1387
+ return ((ctx) => f1(ctx) || f2(ctx));
1388
+ },
1389
+ /** Negation: inverts the filter (no type narrowing) */
1390
+ not(f) {
1391
+ return (ctx) => !f(ctx);
1392
+ },
1393
+ /** Variadic intersection: all filters must match */
1394
+ every(...filters2) {
1395
+ return ((ctx) => filters2.every((f) => f(ctx)));
1396
+ },
1397
+ /** Variadic union: any filter must match */
1398
+ some(...filters2) {
1399
+ return ((ctx) => filters2.some((f) => f(ctx)));
1400
+ }
1401
+ };
1402
+
1181
1403
  const SECRET_TOKEN_HEADER = "X-Telegram-Bot-Api-Secret-Token";
1182
1404
  const WRONG_TOKEN_ERROR = "secret token is invalid";
1183
1405
  const RESPONSE_OK = "ok!";
1184
1406
  const responseOK = () => new Response(RESPONSE_OK);
1185
1407
  const responseUnauthorized = () => new Response(WRONG_TOKEN_ERROR, {
1186
1408
  status: 401
1187
- // @ts-ignore
1188
1409
  });
1189
1410
  const frameworks = {
1190
1411
  elysia: ({ body, headers }) => ({
@@ -1278,7 +1499,11 @@ function webhookHandler(bot, framework, secretTokenOrOptions) {
1278
1499
  const timeoutOptions = typeof shouldWaitOptions === "object" ? shouldWaitOptions : void 0;
1279
1500
  const timeout = timeoutOptions?.timeout ?? 3e4;
1280
1501
  const onTimeout = timeoutOptions?.onTimeout ?? "throw";
1281
- await timeoutWebhook(bot.updates.handleUpdate(await update), timeout, onTimeout);
1502
+ await timeoutWebhook(
1503
+ bot.updates.handleUpdate(await update),
1504
+ timeout,
1505
+ onTimeout
1506
+ );
1282
1507
  if (response) return response();
1283
1508
  }
1284
1509
  });
@@ -1286,4 +1511,4 @@ function webhookHandler(bot, framework, secretTokenOrOptions) {
1286
1511
 
1287
1512
  Symbol.metadata ??= Symbol("Symbol.metadata");
1288
1513
 
1289
- export { Bot, Composer, ErrorKind, Plugin, TelegramError, Updates, webhookHandler };
1514
+ export { Bot, Composer, ErrorKind, Plugin, TelegramError, Updates, filters, webhookHandler };