"use strict"; /** @format */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Composer = void 0; const context_1 = __importDefault(require("./context")); const filters_1 = require("./filters"); const args_1 = require("./core/helpers/args"); function always(x) { return () => x; } const anoop = always(Promise.resolve()); class Composer { constructor(...fns) { this.handler = Composer.compose(fns); } /** * Registers a middleware. */ use(...fns) { this.handler = Composer.compose([this.handler, ...fns]); return this; } /** * Registers middleware for handling updates * matching given type guard function. * @deprecated use `Composer::on` */ guard(guardFn, ...fns) { return this.use(Composer.guard(guardFn, ...fns)); } on(filters, ...fns) { // @ts-expect-error This should get resolved when the overloads are removed in v5 return this.use(Composer.on(filters, ...fns)); } /** * Registers middleware for handling matching text messages. */ hears(triggers, ...fns) { return this.use(Composer.hears(triggers, ...fns)); } /** * Registers middleware for handling specified commands. */ command(command, ...fns) { return this.use(Composer.command(command, ...fns)); } /** * Registers middleware for handling matching callback queries. */ action(triggers, ...fns) { return this.use(Composer.action(triggers, ...fns)); } /** * Registers middleware for handling matching inline queries. */ inlineQuery(triggers, ...fns) { return this.use(Composer.inlineQuery(triggers, ...fns)); } /** * Registers middleware for handling game queries */ gameQuery(...fns) { return this.use(Composer.gameQuery(...fns)); } reaction(reaction, ...fns) { return this.use(Composer.reaction(reaction, ...fns)); } /** * Registers middleware for dropping matching updates. */ drop(predicate) { return this.use(Composer.drop(predicate)); } /** @deprecated use `Composer::drop` */ filter(predicate) { return this.use(Composer.filter(predicate)); } entity(predicate, ...fns) { return this.use(Composer.entity(predicate, ...fns)); } email(email, ...fns) { return this.use(Composer.email(email, ...fns)); } url(url, ...fns) { return this.use(Composer.url(url, ...fns)); } textLink(link, ...fns) { return this.use(Composer.textLink(link, ...fns)); } textMention(mention, ...fns) { return this.use(Composer.textMention(mention, ...fns)); } mention(mention, ...fns) { return this.use(Composer.mention(mention, ...fns)); } phone(number, ...fns) { return this.use(Composer.phone(number, ...fns)); } hashtag(hashtag, ...fns) { return this.use(Composer.hashtag(hashtag, ...fns)); } cashtag(cashtag, ...fns) { return this.use(Composer.cashtag(cashtag, ...fns)); } spoiler(text, ...fns) { return this.use(Composer.spoiler(text, ...fns)); } /** * Registers a middleware for handling /start */ start(...fns) { const handler = Composer.compose(fns); return this.command('start', (ctx, next) => handler(Object.assign(ctx, { startPayload: ctx.payload }), next)); } /** * Registers a middleware for handling /help */ help(...fns) { return this.command('help', ...fns); } /** * Registers a middleware for handling /settings */ settings(...fns) { return this.command('settings', ...fns); } middleware() { return this.handler; } static reply(...args) { return (ctx) => ctx.reply(...args); } static catch(errorHandler, ...fns) { const handler = Composer.compose(fns); // prettier-ignore return (ctx, next) => Promise.resolve(handler(ctx, next)) .catch((err) => errorHandler(err, ctx)); } /** * Generates middleware that runs in the background. */ static fork(middleware) { const handler = Composer.unwrap(middleware); return async (ctx, next) => { await Promise.all([handler(ctx, anoop), next()]); }; } static tap(middleware) { const handler = Composer.unwrap(middleware); return (ctx, next) => Promise.resolve(handler(ctx, anoop)).then(() => next()); } /** * Generates middleware that gives up control to the next middleware. */ static passThru() { return (ctx, next) => next(); } static lazy(factoryFn) { if (typeof factoryFn !== 'function') { throw new Error('Argument must be a function'); } return (ctx, next) => Promise.resolve(factoryFn(ctx)).then((middleware) => Composer.unwrap(middleware)(ctx, next)); } static log(logFn = console.log) { return (ctx, next) => { logFn(JSON.stringify(ctx.update, null, 2)); return next(); }; } /** * @param trueMiddleware middleware to run if the predicate returns true * @param falseMiddleware middleware to run if the predicate returns false */ static branch(predicate, trueMiddleware, falseMiddleware) { if (typeof predicate !== 'function') { return Composer.unwrap(predicate ? trueMiddleware : falseMiddleware); } return Composer.lazy((ctx) => Promise.resolve(predicate(ctx)).then((value) => value ? trueMiddleware : falseMiddleware)); } /** * Generates optional middleware. * @param predicate predicate to decide on a context object whether to run the middleware * @param fns middleware to run if the predicate returns true */ static optional(predicate, ...fns) { return Composer.branch(predicate, Composer.compose(fns), Composer.passThru()); } /** @deprecated use `Composer.drop` */ static filter(predicate) { return Composer.branch(predicate, Composer.passThru(), anoop); } /** * Generates middleware for dropping matching updates. */ static drop(predicate) { return Composer.branch(predicate, anoop, Composer.passThru()); } static dispatch(routeFn, handlers) { return Composer.lazy((ctx) => Promise.resolve(routeFn(ctx)).then((value) => handlers[value])); } // EXPLANATION FOR THE ts-expect-error ANNOTATIONS // The annotations around function invocations with `...fns` are there // whenever we perform validation logic that the flow analysis of TypeScript // cannot comprehend. We always make sure that the middleware functions are // only invoked with properly constrained context objects, but this cannot be // determined automatically. /** * Generates optional middleware based on a predicate that only operates on `ctx.update`. * * Example: * ```ts * import { Composer, Update } from 'telegraf' * * const predicate = (u): u is Update.MessageUpdate => 'message' in u * const middleware = Composer.guard(predicate, (ctx) => { * const message = ctx.update.message * }) * ``` * * Note that `Composer.on('message')` is preferred over this. * * @param guardFn predicate to decide whether to run the middleware based on the `ctx.update` object * @param fns middleware to run if the predicate returns true * @see `Composer.optional` for a more generic version of this method that allows the predicate to operate on `ctx` itself * @deprecated use `Composer.on` */ static guard(guardFn, ...fns) { return Composer.optional((ctx) => guardFn(ctx.update), // @ts-expect-error see explanation above ...fns); } static on(updateType, ...fns) { const filters = Array.isArray(updateType) ? updateType : [updateType]; const predicate = (update) => { for (const filter of filters) { if ( // TODO: this should change to === 'function' once TS bug is fixed // https://github.com/microsoft/TypeScript/pull/51502 typeof filter !== 'string' ? // filter is a type guard filter(update) : // check if filter is the update type filter in update || // check if filter is the msg type // TODO: remove in v5! ('message' in update && filter in update.message)) { return true; } } return false; }; return Composer.optional((ctx) => predicate(ctx.update), ...fns); } static entity(predicate, ...fns) { if (typeof predicate !== 'function') { const entityTypes = normaliseTextArguments(predicate); return Composer.entity(({ type }) => entityTypes.includes(type), ...fns); } return Composer.optional((ctx) => { var _a; const msg = (_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost; if (msg === undefined) { return false; } const text = getText(msg); const entities = getEntities(msg); if (text === undefined) return false; return entities.some((entity) => predicate(entity, text.substring(entity.offset, entity.offset + entity.length), ctx)); }, // @ts-expect-error see explanation above ...fns); } static entityText(entityType, predicate, ...fns) { if (fns.length === 0) { // prettier-ignore return Array.isArray(predicate) // @ts-expect-error predicate is really the middleware ? Composer.entity(entityType, ...predicate) // @ts-expect-error predicate is really the middleware : Composer.entity(entityType, predicate); } const triggers = normaliseTriggers(predicate); return Composer.entity(({ type }, value, ctx) => { if (type !== entityType) { return false; } for (const trigger of triggers) { // @ts-expect-error define so far unknown property `match` if ((ctx.match = trigger(value, ctx))) { return true; } } return false; }, // @ts-expect-error see explanation above ...fns); } static email(email, ...fns) { return Composer.entityText('email', email, ...fns); } static phone(number, ...fns) { return Composer.entityText('phone_number', number, ...fns); } static url(url, ...fns) { return Composer.entityText('url', url, ...fns); } static textLink(link, ...fns) { return Composer.entityText('text_link', link, ...fns); } static textMention(mention, ...fns) { return Composer.entityText('text_mention', mention, ...fns); } static mention(mention, ...fns) { return Composer.entityText('mention', normaliseTextArguments(mention, '@'), ...fns); } static hashtag(hashtag, ...fns) { return Composer.entityText('hashtag', normaliseTextArguments(hashtag, '#'), ...fns); } static cashtag(cashtag, ...fns) { return Composer.entityText('cashtag', normaliseTextArguments(cashtag, '$'), ...fns); } static spoiler(text, ...fns) { return Composer.entityText('spoiler', text, ...fns); } static match(triggers, ...fns) { const handler = Composer.compose(fns); return (ctx, next) => { var _a, _b, _c, _d; 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; if (text === undefined) return next(); for (const trigger of triggers) { const match = trigger(text, ctx); if (match) return handler(Object.assign(ctx, { match }), next); } return next(); }; } /** * Generates middleware for handling matching text messages. */ static hears(triggers, ...fns) { return Composer.on('text', Composer.match(normaliseTriggers(triggers), ...fns)); } /** * Generates middleware for handling specified commands. */ static command(command, ...fns) { if (fns.length === 0) // @ts-expect-error command is really the middleware return Composer.entity('bot_command', command); const triggers = normaliseTriggers(command); const filter = (0, filters_1.message)('text'); const handler = Composer.compose(fns); return Composer.on(filter, (ctx, next) => { const { entities } = ctx.message; const cmdEntity = entities === null || entities === void 0 ? void 0 : entities[0]; if ((cmdEntity === null || cmdEntity === void 0 ? void 0 : cmdEntity.type) !== 'bot_command') return next(); if (cmdEntity.offset > 0) return next(); const len = cmdEntity.length; const text = ctx.message.text; const [cmdPart, to] = text.slice(0, len).split('@'); if (!cmdPart) return next(); // always check for bot's own username case-insensitively if (to && to.toLowerCase() !== ctx.me.toLowerCase()) return next(); const command = cmdPart.slice(1); for (const trigger of triggers) { const match = trigger(command, ctx); if (match) { const payloadOffset = len + 1; const payload = text.slice(payloadOffset); const c = Object.assign(ctx, { match, command, payload, args: [] }); let _args = undefined; // using defineProperty only to make parsing lazy on access Object.defineProperty(c, 'args', { enumerable: true, configurable: true, get() { if (_args != null) return _args; // once parsed, cache and don't parse again on every access return (_args = (0, args_1.argsParser)(payload, entities, payloadOffset)); }, set(args) { _args = args; }, }); return handler(c, next); } } return next(); }); } /** * Generates middleware for handling matching callback queries. */ static action(triggers, ...fns) { return Composer.on('callback_query', Composer.match(normaliseTriggers(triggers), ...fns)); } /** * Generates middleware for handling matching inline queries. */ static inlineQuery(triggers, ...fns) { return Composer.on('inline_query', Composer.match(normaliseTriggers(triggers), ...fns)); } static reaction(reaction, ...fns) { const reactions = Array.isArray(reaction) ? reaction : [reaction]; const handler = Composer.compose(fns); return Composer.on('message_reaction', (ctx, next) => { const match = reactions.find((r) => typeof r === 'string' && r.startsWith('-') ? ctx.reactions.removed.has(r.slice(1)) : ctx.reactions.added.has(r)); if (match) return handler(Object.assign(ctx, { match }), next); return next(); }); } /** * Generates middleware responding only to specified users. */ static acl(userId, ...fns) { if (typeof userId === 'function') { return Composer.optional(userId, ...fns); } const allowed = Array.isArray(userId) ? userId : [userId]; // prettier-ignore return Composer.optional((ctx) => !ctx.from || allowed.includes(ctx.from.id), ...fns); } static memberStatus(status, ...fns) { const statuses = Array.isArray(status) ? status : [status]; return Composer.optional(async (ctx) => { if (ctx.message === undefined) return false; const member = await ctx.getChatMember(ctx.message.from.id); return statuses.includes(member.status); }, ...fns); } /** * Generates middleware responding only to chat admins and chat creator. */ static admin(...fns) { return Composer.memberStatus(['administrator', 'creator'], ...fns); } /** * Generates middleware responding only to chat creator. */ static creator(...fns) { return Composer.memberStatus('creator', ...fns); } /** * Generates middleware running only in specified chat types. */ static chatType(type, ...fns) { const types = Array.isArray(type) ? type : [type]; return Composer.optional((ctx) => { const chat = ctx.chat; return chat !== undefined && types.includes(chat.type); }, ...fns); } /** * Generates middleware running only in private chats. */ static privateChat(...fns) { return Composer.chatType('private', ...fns); } /** * Generates middleware running only in groups and supergroups. */ static groupChat(...fns) { return Composer.chatType(['group', 'supergroup'], ...fns); } /** * Generates middleware for handling game queries. */ static gameQuery(...fns) { return Composer.guard((0, filters_1.callbackQuery)('game_short_name'), ...fns); } static unwrap(handler) { if (!handler) { throw new Error('Handler is undefined'); } return 'middleware' in handler ? handler.middleware() : handler; } static compose(middlewares) { if (!Array.isArray(middlewares)) { throw new Error('Middlewares must be an array'); } if (middlewares.length === 0) { return Composer.passThru(); } if (middlewares.length === 1) { // Quite literally asserted in the above condition // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return Composer.unwrap(middlewares[0]); } return (ctx, next) => { let index = -1; return execute(0, ctx); async function execute(i, context) { var _a; if (!(context instanceof context_1.default)) { throw new Error('next(ctx) called with invalid context'); } if (i <= index) { throw new Error('next() called multiple times'); } index = i; const handler = Composer.unwrap((_a = middlewares[i]) !== null && _a !== void 0 ? _a : next); await handler(context, async (ctx = context) => { await execute(i + 1, ctx); }); } }; } } exports.Composer = Composer; /** * Generates middleware for handling provided update types. * @deprecated use `Composer.on` instead */ Composer.mount = Composer.on; function escapeRegExp(s) { // $& means the whole matched string return s.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); } function normaliseTriggers(triggers) { if (!Array.isArray(triggers)) triggers = [triggers]; return triggers.map((trigger) => { if (!trigger) throw new Error('Invalid trigger'); if (typeof trigger === 'function') return trigger; if (trigger instanceof RegExp) return (value = '') => { trigger.lastIndex = 0; return trigger.exec(value); }; const regex = new RegExp(`^${escapeRegExp(trigger)}$`); return (value) => regex.exec(value); }); } function getEntities(msg) { var _a, _b; if (msg == null) return []; if ('caption_entities' in msg) return (_a = msg.caption_entities) !== null && _a !== void 0 ? _a : []; if ('entities' in msg) return (_b = msg.entities) !== null && _b !== void 0 ? _b : []; return []; } function getText(msg) { if (msg == null) return undefined; if ('caption' in msg) return msg.caption; if ('text' in msg) return msg.text; if ('data' in msg) return msg.data; if ('game_short_name' in msg) return msg.game_short_name; return undefined; } function normaliseTextArguments(argument, prefix = '') { const args = Array.isArray(argument) ? argument : [argument]; // prettier-ignore return args .filter(Boolean) .map((arg) => prefix && typeof arg === 'string' && !arg.startsWith(prefix) ? `${prefix}${arg}` : arg); } exports.default = Composer;