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