1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", { value: true });
|
4 | exports.Composer = void 0;
|
5 | const context_1 = require("./context");
|
6 | function always(x) {
|
7 | return () => x;
|
8 | }
|
9 | const anoop = always(Promise.resolve());
|
10 | class Composer {
|
11 | constructor(...fns) {
|
12 | this.handler = Composer.compose(fns);
|
13 | }
|
14 | |
15 |
|
16 |
|
17 | use(...fns) {
|
18 | this.handler = Composer.compose([this.handler, ...fns]);
|
19 | return this;
|
20 | }
|
21 | |
22 |
|
23 |
|
24 |
|
25 | guard(guardFn, ...fns) {
|
26 | return this.use(Composer.guard(guardFn, ...fns));
|
27 | }
|
28 | |
29 |
|
30 |
|
31 | on(updateType, ...fns) {
|
32 | return this.use(Composer.mount(updateType, ...fns));
|
33 | }
|
34 | |
35 |
|
36 |
|
37 | hears(triggers, ...fns) {
|
38 | return this.use(Composer.hears(triggers, ...fns));
|
39 | }
|
40 | |
41 |
|
42 |
|
43 | command(command, ...fns) {
|
44 | return this.use(Composer.command(command, ...fns));
|
45 | }
|
46 | |
47 |
|
48 |
|
49 | action(triggers, ...fns) {
|
50 | return this.use(Composer.action(triggers, ...fns));
|
51 | }
|
52 | |
53 |
|
54 |
|
55 | inlineQuery(triggers, ...fns) {
|
56 | return this.use(Composer.inlineQuery(triggers, ...fns));
|
57 | }
|
58 | |
59 |
|
60 |
|
61 | gameQuery(...fns) {
|
62 | return this.use(Composer.gameQuery(...fns));
|
63 | }
|
64 | |
65 |
|
66 |
|
67 | drop(predicate) {
|
68 | return this.use(Composer.drop(predicate));
|
69 | }
|
70 | filter(predicate) {
|
71 | return this.use(Composer.filter(predicate));
|
72 | }
|
73 | entity(predicate, ...fns) {
|
74 | return this.use(Composer.entity(predicate, ...fns));
|
75 | }
|
76 | email(email, ...fns) {
|
77 | return this.use(Composer.email(email, ...fns));
|
78 | }
|
79 | url(url, ...fns) {
|
80 | return this.use(Composer.url(url, ...fns));
|
81 | }
|
82 | textLink(link, ...fns) {
|
83 | return this.use(Composer.textLink(link, ...fns));
|
84 | }
|
85 | textMention(mention, ...fns) {
|
86 | return this.use(Composer.textMention(mention, ...fns));
|
87 | }
|
88 | mention(mention, ...fns) {
|
89 | return this.use(Composer.mention(mention, ...fns));
|
90 | }
|
91 | phone(number, ...fns) {
|
92 | return this.use(Composer.phone(number, ...fns));
|
93 | }
|
94 | hashtag(hashtag, ...fns) {
|
95 | return this.use(Composer.hashtag(hashtag, ...fns));
|
96 | }
|
97 | cashtag(cashtag, ...fns) {
|
98 | return this.use(Composer.cashtag(cashtag, ...fns));
|
99 | }
|
100 | |
101 |
|
102 |
|
103 | start(...fns) {
|
104 | const handler = Composer.compose(fns);
|
105 | return this.command('start', (ctx, next) => {
|
106 | const entity = ctx.message.entities[0];
|
107 | const startPayload = ctx.message.text.slice(entity.length + 1);
|
108 | return handler(Object.assign(ctx, { startPayload }), next);
|
109 | });
|
110 | }
|
111 | |
112 |
|
113 |
|
114 | help(...fns) {
|
115 | return this.command('help', ...fns);
|
116 | }
|
117 | |
118 |
|
119 |
|
120 | settings(...fns) {
|
121 | return this.command('settings', ...fns);
|
122 | }
|
123 | middleware() {
|
124 | return this.handler;
|
125 | }
|
126 | static reply(...args) {
|
127 | return (ctx) => ctx.reply(...args);
|
128 | }
|
129 | static catch(errorHandler, ...fns) {
|
130 | const handler = Composer.compose(fns);
|
131 |
|
132 | return (ctx, next) => Promise.resolve(handler(ctx, next))
|
133 | .catch((err) => errorHandler(err, ctx));
|
134 | }
|
135 | |
136 |
|
137 |
|
138 | static fork(middleware) {
|
139 | const handler = Composer.unwrap(middleware);
|
140 | return async (ctx, next) => {
|
141 | await Promise.all([handler(ctx, anoop), next()]);
|
142 | };
|
143 | }
|
144 | static tap(middleware) {
|
145 | const handler = Composer.unwrap(middleware);
|
146 | return (ctx, next) => Promise.resolve(handler(ctx, anoop)).then(() => next());
|
147 | }
|
148 | |
149 |
|
150 |
|
151 | static passThru() {
|
152 | return (ctx, next) => next();
|
153 | }
|
154 | static lazy(factoryFn) {
|
155 | if (typeof factoryFn !== 'function') {
|
156 | throw new Error('Argument must be a function');
|
157 | }
|
158 | return (ctx, next) => Promise.resolve(factoryFn(ctx)).then((middleware) => Composer.unwrap(middleware)(ctx, next));
|
159 | }
|
160 | static log(logFn = console.log) {
|
161 | return (ctx, next) => {
|
162 | logFn(JSON.stringify(ctx.update, null, 2));
|
163 | return next();
|
164 | };
|
165 | }
|
166 | |
167 |
|
168 |
|
169 |
|
170 | static branch(predicate, trueMiddleware, falseMiddleware) {
|
171 | if (typeof predicate !== 'function') {
|
172 | return Composer.unwrap(predicate ? trueMiddleware : falseMiddleware);
|
173 | }
|
174 | return Composer.lazy((ctx) => Promise.resolve(predicate(ctx)).then((value) => value ? trueMiddleware : falseMiddleware));
|
175 | }
|
176 | |
177 |
|
178 |
|
179 |
|
180 |
|
181 | static optional(predicate, ...fns) {
|
182 | return Composer.branch(predicate, Composer.compose(fns), Composer.passThru());
|
183 | }
|
184 | static filter(predicate) {
|
185 | return Composer.branch(predicate, Composer.passThru(), anoop);
|
186 | }
|
187 | |
188 |
|
189 |
|
190 | static drop(predicate) {
|
191 | return Composer.branch(predicate, anoop, Composer.passThru());
|
192 | }
|
193 | static dispatch(routeFn, handlers) {
|
194 | return Composer.lazy((ctx) => Promise.resolve(routeFn(ctx)).then((value) => handlers[value]));
|
195 | }
|
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 |
|
202 | |
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 |
|
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 |
|
220 |
|
221 | static guard(guardFn, ...fns) {
|
222 | return Composer.optional((ctx) => guardFn(ctx.update),
|
223 |
|
224 | ...fns);
|
225 | }
|
226 | |
227 |
|
228 |
|
229 |
|
230 | static mount(updateType, ...fns) {
|
231 | return Composer.on(updateType, ...fns);
|
232 | }
|
233 | |
234 |
|
235 |
|
236 | static on(updateType, ...fns) {
|
237 | const updateTypes = normalizeTextArguments(updateType);
|
238 | const predicate = (update) => updateTypes.some((type) =>
|
239 |
|
240 | type in update ||
|
241 |
|
242 | ('message' in update && type in update.message));
|
243 | return Composer.guard(predicate, ...fns);
|
244 | }
|
245 | static entity(predicate, ...fns) {
|
246 | if (typeof predicate !== 'function') {
|
247 | const entityTypes = normalizeTextArguments(predicate);
|
248 | return Composer.entity(({ type }) => entityTypes.includes(type), ...fns);
|
249 | }
|
250 | return Composer.optional((ctx) => {
|
251 | var _a;
|
252 | const msg = (_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost;
|
253 | if (msg === undefined) {
|
254 | return false;
|
255 | }
|
256 | const text = getText(msg);
|
257 | const entities = getEntities(msg);
|
258 | if (text === undefined)
|
259 | return false;
|
260 | return entities.some((entity) => predicate(entity, text.substring(entity.offset, entity.offset + entity.length), ctx));
|
261 |
|
262 | }, ...fns);
|
263 | }
|
264 | static entityText(entityType, predicate, ...fns) {
|
265 | if (fns.length === 0) {
|
266 |
|
267 | return Array.isArray(predicate)
|
268 |
|
269 | ? Composer.entity(entityType, ...predicate)
|
270 |
|
271 | : Composer.entity(entityType, predicate);
|
272 | }
|
273 | const triggers = normalizeTriggers(predicate);
|
274 | return Composer.entity(({ type }, value, ctx) => {
|
275 | if (type !== entityType) {
|
276 | return false;
|
277 | }
|
278 | for (const trigger of triggers) {
|
279 |
|
280 | if ((ctx.match = trigger(value, ctx))) {
|
281 | return true;
|
282 | }
|
283 | }
|
284 | return false;
|
285 |
|
286 | }, ...fns);
|
287 | }
|
288 | static email(email, ...fns) {
|
289 | return Composer.entityText('email', email, ...fns);
|
290 | }
|
291 | static phone(number, ...fns) {
|
292 | return Composer.entityText('phone_number', number, ...fns);
|
293 | }
|
294 | static url(url, ...fns) {
|
295 | return Composer.entityText('url', url, ...fns);
|
296 | }
|
297 | static textLink(link, ...fns) {
|
298 | return Composer.entityText('text_link', link, ...fns);
|
299 | }
|
300 | static textMention(mention, ...fns) {
|
301 | return Composer.entityText('text_mention', mention, ...fns);
|
302 | }
|
303 | static mention(mention, ...fns) {
|
304 | return Composer.entityText('mention', normalizeTextArguments(mention, '@'), ...fns);
|
305 | }
|
306 | static hashtag(hashtag, ...fns) {
|
307 | return Composer.entityText('hashtag', normalizeTextArguments(hashtag, '#'), ...fns);
|
308 | }
|
309 | static cashtag(cashtag, ...fns) {
|
310 | return Composer.entityText('cashtag', normalizeTextArguments(cashtag, '$'), ...fns);
|
311 | }
|
312 | static match(triggers, ...fns) {
|
313 | const handler = Composer.compose(fns);
|
314 | return (ctx, next) => {
|
315 | var _a, _b, _c, _d;
|
316 | const text = (_c = (_b = (_a = getText(ctx.message)) !== null && _a !== void 0 ? _a : getText(ctx.channelPost)) !== null && _b !== void 0 ? _b : getText(ctx.callbackQuery)) !== null && _c !== void 0 ? _c : (_d = ctx.inlineQuery) === null || _d === void 0 ? void 0 : _d.query;
|
317 | if (text === undefined)
|
318 | return next();
|
319 | for (const trigger of triggers) {
|
320 |
|
321 | const match = trigger(text, ctx);
|
322 | if (match) {
|
323 |
|
324 | return handler(Object.assign(ctx, { match }), next);
|
325 | }
|
326 | }
|
327 | return next();
|
328 | };
|
329 | }
|
330 | |
331 |
|
332 |
|
333 | static hears(triggers, ...fns) {
|
334 | return Composer.mount('text', Composer.match(normalizeTriggers(triggers), ...fns));
|
335 | }
|
336 | |
337 |
|
338 |
|
339 | static command(command, ...fns) {
|
340 | if (fns.length === 0) {
|
341 |
|
342 | return Composer.entity('bot_command', command);
|
343 | }
|
344 | const commands = normalizeTextArguments(command, '/');
|
345 | return Composer.mount('text', Composer.lazy((ctx) => {
|
346 | var _a;
|
347 | const groupCommands = ctx.me && ((_a = ctx.chat) === null || _a === void 0 ? void 0 : _a.type.endsWith('group'))
|
348 | ? commands.map((command) => `${command}@${ctx.me}`)
|
349 | : [];
|
350 | return Composer.entity(({ offset, type }, value) => offset === 0 &&
|
351 | type === 'bot_command' &&
|
352 | (commands.includes(value) || groupCommands.includes(value)),
|
353 |
|
354 | ...fns);
|
355 | }));
|
356 | }
|
357 | |
358 |
|
359 |
|
360 | static action(triggers, ...fns) {
|
361 | return Composer.mount('callback_query', Composer.match(normalizeTriggers(triggers), ...fns));
|
362 | }
|
363 | |
364 |
|
365 |
|
366 | static inlineQuery(triggers, ...fns) {
|
367 | return Composer.mount('inline_query', Composer.match(normalizeTriggers(triggers), ...fns));
|
368 | }
|
369 | |
370 |
|
371 |
|
372 | static acl(userId, ...fns) {
|
373 | if (typeof userId === 'function') {
|
374 | return Composer.optional(userId, ...fns);
|
375 | }
|
376 | const allowed = Array.isArray(userId) ? userId : [userId];
|
377 |
|
378 | return Composer.optional((ctx) => !ctx.from || allowed.includes(ctx.from.id), ...fns);
|
379 | }
|
380 | static memberStatus(status, ...fns) {
|
381 | const statuses = Array.isArray(status) ? status : [status];
|
382 | return Composer.optional(async (ctx) => {
|
383 | if (ctx.message === undefined)
|
384 | return false;
|
385 | const member = await ctx.getChatMember(ctx.message.from.id);
|
386 | return statuses.includes(member.status);
|
387 | }, ...fns);
|
388 | }
|
389 | |
390 |
|
391 |
|
392 | static admin(...fns) {
|
393 | return Composer.memberStatus(['administrator', 'creator'], ...fns);
|
394 | }
|
395 | |
396 |
|
397 |
|
398 | static creator(...fns) {
|
399 | return Composer.memberStatus('creator', ...fns);
|
400 | }
|
401 | |
402 |
|
403 |
|
404 | static chatType(type, ...fns) {
|
405 | const types = Array.isArray(type) ? type : [type];
|
406 | return Composer.optional((ctx) => {
|
407 | const chat = ctx.chat;
|
408 | return chat !== undefined && types.includes(chat.type);
|
409 | }, ...fns);
|
410 | }
|
411 | |
412 |
|
413 |
|
414 | static privateChat(...fns) {
|
415 | return Composer.chatType('private', ...fns);
|
416 | }
|
417 | |
418 |
|
419 |
|
420 | static groupChat(...fns) {
|
421 | return Composer.chatType(['group', 'supergroup'], ...fns);
|
422 | }
|
423 | |
424 |
|
425 |
|
426 | static gameQuery(...fns) {
|
427 | return Composer.guard((u) => 'callback_query' in u && 'game_short_name' in u.callback_query, ...fns);
|
428 | }
|
429 | static unwrap(handler) {
|
430 | if (!handler) {
|
431 | throw new Error('Handler is undefined');
|
432 | }
|
433 | return 'middleware' in handler ? handler.middleware() : handler;
|
434 | }
|
435 | static compose(middlewares) {
|
436 | if (!Array.isArray(middlewares)) {
|
437 | throw new Error('Middlewares must be an array');
|
438 | }
|
439 | if (middlewares.length === 0) {
|
440 | return Composer.passThru();
|
441 | }
|
442 | if (middlewares.length === 1) {
|
443 | return Composer.unwrap(middlewares[0]);
|
444 | }
|
445 | return (ctx, next) => {
|
446 | let index = -1;
|
447 | return execute(0, ctx);
|
448 | async function execute(i, context) {
|
449 | var _a;
|
450 | if (!(context instanceof context_1.default)) {
|
451 | throw new Error('next(ctx) called with invalid context');
|
452 | }
|
453 | if (i <= index) {
|
454 | throw new Error('next() called multiple times');
|
455 | }
|
456 | index = i;
|
457 | const handler = Composer.unwrap((_a = middlewares[i]) !== null && _a !== void 0 ? _a : next);
|
458 | await handler(context, async (ctx = context) => {
|
459 | await execute(i + 1, ctx);
|
460 | });
|
461 | }
|
462 | };
|
463 | }
|
464 | }
|
465 | exports.Composer = Composer;
|
466 | function escapeRegExp(s) {
|
467 |
|
468 | return s.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&');
|
469 | }
|
470 | function normalizeTriggers(triggers) {
|
471 | if (!Array.isArray(triggers)) {
|
472 | triggers = [triggers];
|
473 | }
|
474 | return triggers.map((trigger) => {
|
475 | if (!trigger) {
|
476 | throw new Error('Invalid trigger');
|
477 | }
|
478 | if (typeof trigger === 'function') {
|
479 | return trigger;
|
480 | }
|
481 | if (trigger instanceof RegExp) {
|
482 | return (value = '') => {
|
483 | trigger.lastIndex = 0;
|
484 | return trigger.exec(value);
|
485 | };
|
486 | }
|
487 | const regex = new RegExp(`^${escapeRegExp(trigger)}$`);
|
488 | return (value) => regex.exec(value);
|
489 | });
|
490 | }
|
491 | function getEntities(msg) {
|
492 | var _a, _b;
|
493 | if (msg == null)
|
494 | return [];
|
495 | if ('caption_entities' in msg)
|
496 | return (_a = msg.caption_entities) !== null && _a !== void 0 ? _a : [];
|
497 | if ('entities' in msg)
|
498 | return (_b = msg.entities) !== null && _b !== void 0 ? _b : [];
|
499 | return [];
|
500 | }
|
501 | function getText(msg) {
|
502 | if (msg == null)
|
503 | return undefined;
|
504 | if ('caption' in msg)
|
505 | return msg.caption;
|
506 | if ('text' in msg)
|
507 | return msg.text;
|
508 | if ('data' in msg)
|
509 | return msg.data;
|
510 | if ('game_short_name' in msg)
|
511 | return msg.game_short_name;
|
512 | return undefined;
|
513 | }
|
514 | function normalizeTextArguments(argument, prefix = '') {
|
515 | const args = Array.isArray(argument) ? argument : [argument];
|
516 |
|
517 | return args
|
518 | .filter(Boolean)
|
519 | .map((arg) => prefix && typeof arg === 'string' && !arg.startsWith(prefix) ? `${prefix}${arg}` : arg);
|
520 | }
|
521 | exports.default = Composer;
|