630 lines
24 KiB
JavaScript
630 lines
24 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.Composer = exports.run = exports.BotError = void 0;
|
|
const context_js_1 = require("./context.js");
|
|
// === Middleware errors
|
|
/**
|
|
* This error is thrown when middleware throws. It simply wraps the original
|
|
* error (accessible via the `error` property), but also provides access to the
|
|
* respective context object that was processed while the error occurred.
|
|
*/
|
|
class BotError extends Error {
|
|
constructor(error, ctx) {
|
|
super(generateBotErrorMessage(error));
|
|
this.error = error;
|
|
this.ctx = ctx;
|
|
this.name = "BotError";
|
|
if (error instanceof Error)
|
|
this.stack = error.stack;
|
|
}
|
|
}
|
|
exports.BotError = BotError;
|
|
function generateBotErrorMessage(error) {
|
|
let msg;
|
|
if (error instanceof Error) {
|
|
msg = `${error.name} in middleware: ${error.message}`;
|
|
}
|
|
else {
|
|
const type = typeof error;
|
|
msg = `Non-error value of type ${type} thrown in middleware`;
|
|
switch (type) {
|
|
case "bigint":
|
|
case "boolean":
|
|
case "number":
|
|
case "symbol":
|
|
msg += `: ${error}`;
|
|
break;
|
|
case "string":
|
|
msg += `: ${String(error).substring(0, 50)}`;
|
|
break;
|
|
default:
|
|
msg += "!";
|
|
break;
|
|
}
|
|
}
|
|
return msg;
|
|
}
|
|
// === Middleware base functions
|
|
function flatten(mw) {
|
|
return typeof mw === "function"
|
|
? mw
|
|
: (ctx, next) => mw.middleware()(ctx, next);
|
|
}
|
|
function concat(first, andThen) {
|
|
return async (ctx, next) => {
|
|
let nextCalled = false;
|
|
await first(ctx, async () => {
|
|
if (nextCalled)
|
|
throw new Error("`next` already called before!");
|
|
else
|
|
nextCalled = true;
|
|
await andThen(ctx, next);
|
|
});
|
|
};
|
|
}
|
|
function pass(_ctx, next) {
|
|
return next();
|
|
}
|
|
const leaf = () => Promise.resolve();
|
|
/**
|
|
* Runs some given middleware function with a given context object.
|
|
*
|
|
* @param middleware The middleware to run
|
|
* @param ctx The context to use
|
|
*/
|
|
async function run(middleware, ctx) {
|
|
await middleware(ctx, leaf);
|
|
}
|
|
exports.run = run;
|
|
// === Composer
|
|
/**
|
|
* The composer is the heart of the middleware system in grammY. It is also the
|
|
* superclass of `Bot`. Whenever you call `use` or `on` or some of the other
|
|
* methods on your bot, you are in fact using the underlying composer instance
|
|
* to register your middleware.
|
|
*
|
|
* If you're just getting started, you do not need to worry about what
|
|
* middleware is, or about how to use a composer.
|
|
*
|
|
* On the other hand, if you want to dig deeper into how grammY implements
|
|
* middleware, check out the
|
|
* [documentation](https://grammy.dev/advanced/middleware.html) on the website.
|
|
*/
|
|
class Composer {
|
|
/**
|
|
* Constructs a new composer based on the provided middleware. If no
|
|
* middleware is given, the composer instance will simply make all context
|
|
* objects pass through without touching them.
|
|
*
|
|
* @param middleware The middleware to compose
|
|
*/
|
|
constructor(...middleware) {
|
|
this.handler = middleware.length === 0
|
|
? pass
|
|
: middleware.map(flatten).reduce(concat);
|
|
}
|
|
middleware() {
|
|
return this.handler;
|
|
}
|
|
/**
|
|
* Registers some middleware that receives all updates. It is installed by
|
|
* concatenating it to the end of all previously installed middleware.
|
|
*
|
|
* Often, this method is used to install middleware that behaves like a
|
|
* plugin, for example session middleware.
|
|
* ```ts
|
|
* bot.use(session())
|
|
* ```
|
|
*
|
|
* This method returns a new instance of composer. The returned instance can
|
|
* be further extended, and all changes will be regarded here. Confer the
|
|
* [documentation](https://grammy.dev/advanced/middleware.html) on the
|
|
* website if you want to know more about how the middleware system in
|
|
* grammY works, especially when it comes to chaining the method calls
|
|
* (`use( ... ).use( ... ).use( ... )`).
|
|
*
|
|
* @param middleware The middleware to register
|
|
*/
|
|
use(...middleware) {
|
|
const composer = new Composer(...middleware);
|
|
this.handler = concat(this.handler, flatten(composer));
|
|
return composer;
|
|
}
|
|
/**
|
|
* Registers some middleware that will only be executed for some specific
|
|
* updates, namely those matching the provided filter query. Filter queries
|
|
* are a concise way to specify which updates you are interested in.
|
|
*
|
|
* Here are some examples of valid filter queries:
|
|
* ```ts
|
|
* // All kinds of message updates
|
|
* bot.on('message', ctx => { ... })
|
|
*
|
|
* // Only text messages
|
|
* bot.on('message:text', ctx => { ... })
|
|
*
|
|
* // Only text messages with URL
|
|
* bot.on('message:entities:url', ctx => { ... })
|
|
*
|
|
* // Text messages and text channel posts
|
|
* bot.on(':text', ctx => { ... })
|
|
*
|
|
* // Messages with URL in text or caption (i.e. entities or caption entities)
|
|
* bot.on('message::url', ctx => { ... })
|
|
*
|
|
* // Messages or channel posts with URL in text or caption
|
|
* bot.on('::url', ctx => { ... })
|
|
* ```
|
|
*
|
|
* You can use autocomplete in VS Code to see all available filter queries.
|
|
* Check out the
|
|
* [documentation](https://grammy.dev/guide/filter-queries.html) on the
|
|
* website to learn more about filter queries in grammY.
|
|
*
|
|
* It is possible to pass multiple filter queries in an array, i.e.
|
|
* ```ts
|
|
* // Matches all text messages and edited text messages that contain a URL
|
|
* bot.on(['message:entities:url', 'edited_message:entities:url'], ctx => { ... })
|
|
* ```
|
|
*
|
|
* Your middleware will be executed if _any of the provided filter queries_
|
|
* matches (logical OR).
|
|
*
|
|
* If you instead want to match _all of the provided filter queries_
|
|
* (logical AND), you can chain the `.on` calls:
|
|
* ```ts
|
|
* // Matches all messages and channel posts that both a) contain a URL and b) are forwards
|
|
* bot.on('::url').on(':forward_date', ctx => { ... })
|
|
* ```
|
|
*
|
|
* @param filter The filter query to use, may also be an array of queries
|
|
* @param middleware The middleware to register behind the given filter
|
|
*/
|
|
on(filter, ...middleware) {
|
|
return this.filter(context_js_1.Context.has.filterQuery(filter), ...middleware);
|
|
}
|
|
/**
|
|
* Registers some middleware that will only be executed when the message
|
|
* contains some text. Is it possible to pass a regular expression to match:
|
|
* ```ts
|
|
* // Match some text (exact match)
|
|
* bot.hears('I love grammY', ctx => ctx.reply('And grammY loves you! <3'))
|
|
* // Match a regular expression
|
|
* bot.hears(/\/echo (.+)/, ctx => ctx.reply(ctx.match[1]))
|
|
* ```
|
|
* Note how `ctx.match` will contain the result of the regular expression.
|
|
* Here it is a `RegExpMatchArray` object, so `ctx.match[1]` refers to the
|
|
* part of the regex that was matched by `(.+)`, i.e. the text that comes
|
|
* after “/echo”.
|
|
*
|
|
* You can pass an array of triggers. Your middleware will be executed if at
|
|
* least one of them matches.
|
|
*
|
|
* Both text and captions of the received messages will be scanned. For
|
|
* example, when a photo is sent to the chat and its caption matches the
|
|
* trigger, your middleware will be executed.
|
|
*
|
|
* If you only want to match text messages and not captions, you can do
|
|
* this:
|
|
* ```ts
|
|
* // Only matches text messages (and channel posts) for the regex
|
|
* bot.on(':text').hears(/\/echo (.+)/, ctx => { ... })
|
|
* ```
|
|
*
|
|
* @param trigger The text to look for
|
|
* @param middleware The middleware to register
|
|
*/
|
|
hears(trigger, ...middleware) {
|
|
return this.filter(context_js_1.Context.has.text(trigger), ...middleware);
|
|
}
|
|
/**
|
|
* Registers some middleware that will only be executed when a certain
|
|
* command is found.
|
|
* ```ts
|
|
* // Reacts to /start commands
|
|
* bot.command('start', ctx => { ... })
|
|
* // Reacts to /help commands
|
|
* bot.command('help', ctx => { ... })
|
|
* ```
|
|
*
|
|
* The rest of the message (excluding the command, and trimmed) is provided
|
|
* via `ctx.match`.
|
|
*
|
|
* > **Did you know?** You can use deep linking
|
|
* > (https://core.telegram.org/bots/features#deep-linking) to let users
|
|
* > start your bot with a custom payload. As an example, send someone the
|
|
* > link https://t.me/name-of-your-bot?start=custom-payload and register a
|
|
* > start command handler on your bot with grammY. As soon as the user
|
|
* > starts your bot, you will receive `custom-payload` in the `ctx.match`
|
|
* > property!
|
|
* > ```ts
|
|
* > bot.command('start', ctx => {
|
|
* > const payload = ctx.match // will be 'custom-payload'
|
|
* > })
|
|
* > ```
|
|
*
|
|
* Note that commands are not matched in captions or in the middle of the
|
|
* text.
|
|
* ```ts
|
|
* bot.command('start', ctx => { ... })
|
|
* // ... does not match:
|
|
* // A message saying: “some text /start some more text”
|
|
* // A photo message with the caption “/start”
|
|
* ```
|
|
*
|
|
* By default, commands are detected in channel posts, too. This means that
|
|
* `ctx.message` is potentially `undefined`, so you should use `ctx.msg`
|
|
* instead to grab both messages and channel posts. Alternatively, if you
|
|
* want to limit your bot to finding commands only in private and group
|
|
* chats, you can use `bot.on('message').command('start', ctx => { ... })`,
|
|
* or even store a message-only version of your bot in a variable like so:
|
|
* ```ts
|
|
* const m = bot.on('message')
|
|
*
|
|
* m.command('start', ctx => { ... })
|
|
* m.command('help', ctx => { ... })
|
|
* // etc
|
|
* ```
|
|
*
|
|
* If you need more freedom matching your commands, check out the `commands`
|
|
* plugin.
|
|
*
|
|
* @param command The command to look for
|
|
* @param middleware The middleware to register
|
|
*/
|
|
command(command, ...middleware) {
|
|
return this.filter(context_js_1.Context.has.command(command), ...middleware);
|
|
}
|
|
/**
|
|
* Registers some middleware for certain chat types only. For example, you
|
|
* can use this method to only receive updates from private chats. The four
|
|
* chat types are `"channel"`, `"supergroup"`, `"group"`, and `"private"`.
|
|
* This is especially useful when combined with other filtering logic. For
|
|
* example, this is how can you respond to `/start` commands only from
|
|
* private chats:
|
|
* ```ts
|
|
* bot.chatType("private").command("start", ctx => { ... })
|
|
* ```
|
|
*
|
|
* Naturally, you can also use this method on its own.
|
|
* ```ts
|
|
* // Private chats only
|
|
* bot.chatType("private", ctx => { ... });
|
|
* // Channels only
|
|
* bot.chatType("channel", ctx => { ... });
|
|
* ```
|
|
*
|
|
* You can pass an array of chat types if you want your middleware to run
|
|
* for any of several provided chat types.
|
|
* ```ts
|
|
* // Groups and supergroups only
|
|
* bot.chatType(["group", "supergroup"], ctx => { ... });
|
|
* ```
|
|
* [Remember](https://grammy.dev/guide/context.html#shortcuts) also that you
|
|
* can access the chat type via `ctx.chat.type`.
|
|
*
|
|
* @param chatType The chat type
|
|
* @param middleware The middleware to register
|
|
*/
|
|
chatType(chatType, ...middleware) {
|
|
return this.filter(context_js_1.Context.has.chatType(chatType), ...middleware);
|
|
}
|
|
/**
|
|
* Registers some middleware for callback queries, i.e. the updates that
|
|
* Telegram delivers to your bot when a user clicks an inline button (that
|
|
* is a button under a message).
|
|
*
|
|
* This method is essentially the same as calling
|
|
* ```ts
|
|
* bot.on('callback_query:data', ctx => { ... })
|
|
* ```
|
|
* but it also allows you to match the query data against a given text or
|
|
* regular expression.
|
|
*
|
|
* ```ts
|
|
* // Create an inline keyboard
|
|
* const keyboard = new InlineKeyboard().text('Go!', 'button-payload')
|
|
* // Send a message with the keyboard
|
|
* await bot.api.sendMessage(chat_id, 'Press a button!', {
|
|
* reply_markup: keyboard
|
|
* })
|
|
* // Listen to users pressing buttons with that specific payload
|
|
* bot.callbackQuery('button-payload', ctx => { ... })
|
|
*
|
|
* // Listen to users pressing any button your bot ever sent
|
|
* bot.on('callback_query:data', ctx => { ... })
|
|
* ```
|
|
*
|
|
* Always remember to call `answerCallbackQuery`—even if you don't perform
|
|
* any action: https://core.telegram.org/bots/api#answercallbackquery
|
|
* ```ts
|
|
* bot.on('callback_query:data', async ctx => {
|
|
* await ctx.answerCallbackQuery()
|
|
* })
|
|
* ```
|
|
*
|
|
* You can pass an array of triggers. Your middleware will be executed if at
|
|
* least one of them matches.
|
|
*
|
|
* @param trigger The string to look for in the payload
|
|
* @param middleware The middleware to register
|
|
*/
|
|
callbackQuery(trigger, ...middleware) {
|
|
return this.filter(context_js_1.Context.has.callbackQuery(trigger), ...middleware);
|
|
}
|
|
/**
|
|
* Registers some middleware for game queries, i.e. the updates that
|
|
* Telegram delivers to your bot when a user clicks an inline button for the
|
|
* HTML5 games platform on Telegram.
|
|
*
|
|
* This method is essentially the same as calling
|
|
* ```ts
|
|
* bot.on('callback_query:game_short_name', ctx => { ... })
|
|
* ```
|
|
* but it also allows you to match the query data against a given text or
|
|
* regular expression.
|
|
*
|
|
* You can pass an array of triggers. Your middleware will be executed if at
|
|
* least one of them matches.
|
|
*
|
|
* @param trigger The string to look for in the payload
|
|
* @param middleware The middleware to register
|
|
*/
|
|
gameQuery(trigger, ...middleware) {
|
|
return this.filter(context_js_1.Context.has.gameQuery(trigger), ...middleware);
|
|
}
|
|
/**
|
|
* Registers middleware for inline queries. Telegram sends an inline query
|
|
* to your bot whenever a user types “@your_bot_name ...” into a text field
|
|
* in Telegram. You bot will then receive the entered search query and can
|
|
* respond with a number of results (text, images, etc) that the user can
|
|
* pick from to send a message _via_ your bot to the respective chat. Check
|
|
* out https://core.telegram.org/bots/inline to read more about inline bots.
|
|
*
|
|
* > Note that you have to enable inline mode for you bot by contacting
|
|
* > @BotFather first.
|
|
*
|
|
* ```ts
|
|
* // Listen for users typing “@your_bot_name query”
|
|
* bot.inlineQuery('query', async ctx => {
|
|
* // Answer the inline query, confer https://core.telegram.org/bots/api#answerinlinequery
|
|
* await ctx.answerInlineQuery( ... )
|
|
* })
|
|
* ```
|
|
*
|
|
* @param trigger The inline query text to match
|
|
* @param middleware The middleware to register
|
|
*/
|
|
inlineQuery(trigger, ...middleware) {
|
|
return this.filter(context_js_1.Context.has.inlineQuery(trigger), ...middleware);
|
|
}
|
|
/**
|
|
* Registers middleware for the ChosenInlineResult by the given id or ids.
|
|
* ChosenInlineResult represents a result of an inline query that was
|
|
* chosen by the user and sent to their chat partner. Check out
|
|
* https://core.telegram.org/bots/api#choseninlineresult to read more
|
|
* about chosen inline results.
|
|
*
|
|
* ```ts
|
|
* bot.chosenInlineResult('id', async ctx => {
|
|
* const id = ctx.result_id;
|
|
* // Your code
|
|
* })
|
|
* ```
|
|
*
|
|
* @param resultId An id or array of ids
|
|
* @param middleware The middleware to register
|
|
*/
|
|
chosenInlineResult(resultId, ...middleware) {
|
|
return this.filter(context_js_1.Context.has.chosenInlineResult(resultId), ...middleware);
|
|
}
|
|
filter(predicate, ...middleware) {
|
|
const composer = new Composer(...middleware);
|
|
this.branch(predicate, composer, pass);
|
|
return composer;
|
|
}
|
|
/**
|
|
* > This is an advanced method of grammY.
|
|
*
|
|
* Registers middleware behind a custom filter function that operates on the
|
|
* context object and decides whether or not to execute the middleware. In
|
|
* other words, the middleware will only be executed if the given predicate
|
|
* returns `false` for the given context object. Otherwise, it will be
|
|
* skipped and the next middleware will be executed. Note that the predicate
|
|
* may be asynchronous, i.e. it can return a Promise of a boolean.
|
|
*
|
|
* This method is the same using `filter` (normal usage) with a negated
|
|
* predicate.
|
|
*
|
|
* @param predicate The predicate to check
|
|
* @param middleware The middleware to register
|
|
*/
|
|
drop(predicate, ...middleware) {
|
|
return this.filter(async (ctx) => !(await predicate(ctx)), ...middleware);
|
|
}
|
|
/**
|
|
* > This is an advanced method of grammY.
|
|
*
|
|
* Registers some middleware that runs concurrently to the executing
|
|
* middleware stack.
|
|
* ```ts
|
|
* bot.use( ... ) // will run first
|
|
* bot.fork( ... ) // will be started second, but run concurrently
|
|
* bot.use( ... ) // will also be run second
|
|
* ```
|
|
* In the first middleware, as soon as `next`'s Promise resolves, both forks
|
|
* have completed.
|
|
*
|
|
* Both the fork and the downstream middleware are awaited with
|
|
* `Promise.all`, so you will only be to catch up to one error (the one that
|
|
* is thrown first).
|
|
*
|
|
* In opposite to the other middleware methods on composer, `fork` does not
|
|
* return simply return the composer connected to the main middleware stack.
|
|
* Instead, it returns the created composer _of the fork_ connected to the
|
|
* middleware stack. This allows for the following pattern.
|
|
* ```ts
|
|
* // Middleware will be run concurrently!
|
|
* bot.fork().on('message', ctx => { ... })
|
|
* ```
|
|
*
|
|
* @param middleware The middleware to run concurrently
|
|
*/
|
|
fork(...middleware) {
|
|
const composer = new Composer(...middleware);
|
|
const fork = flatten(composer);
|
|
this.use((ctx, next) => Promise.all([next(), run(fork, ctx)]));
|
|
return composer;
|
|
}
|
|
/**
|
|
* > This is an advanced method of grammY.
|
|
*
|
|
* Executes some middleware that can be generated on the fly for each
|
|
* context. Pass a factory function that creates some middleware (or a
|
|
* middleware array even). The factory function will be called once per
|
|
* context, and its result will be executed with the context object.
|
|
* ```ts
|
|
* // The middleware returned by `createMyMiddleware` will be used only once
|
|
* bot.lazy(ctx => createMyMiddleware(ctx))
|
|
* ```
|
|
*
|
|
* You may generate this middleware in an `async` fashion.
|
|
*
|
|
* You can decide to return an empty array (`[]`) if you don't want to run
|
|
* any middleware for a given context object. This is equivalent to
|
|
* returning an empty instance of `Composer`.
|
|
*
|
|
* @param middlewareFactory The factory function creating the middleware
|
|
*/
|
|
lazy(middlewareFactory) {
|
|
return this.use(async (ctx, next) => {
|
|
const middleware = await middlewareFactory(ctx);
|
|
const arr = Array.isArray(middleware) ? middleware : [middleware];
|
|
await flatten(new Composer(...arr))(ctx, next);
|
|
});
|
|
}
|
|
/**
|
|
* > This is an advanced method of grammY.
|
|
*
|
|
* _Not to be confused with the `router` plugin._
|
|
*
|
|
* This method is an alternative to the `router` plugin. It allows you to
|
|
* branch between different middleware per context object. You can pass two
|
|
* things to it:
|
|
* 1. A routing function
|
|
* 2. Different middleware identified by key
|
|
*
|
|
* The routing function decides based on the context object which middleware
|
|
* to run. Each middleware is identified by a key, so the routing function
|
|
* simply returns the key of that middleware.
|
|
* ```ts
|
|
* // Define different route handlers
|
|
* const routeHandlers = {
|
|
* evenUpdates: (ctx: Context) => { ... }
|
|
* oddUpdates: (ctx: Context) => { ... }
|
|
* }
|
|
* // Decide for a context object which one to pick
|
|
* const router = (ctx: Context) => ctx.update.update_id % 2 === 0
|
|
* ? 'evenUpdates'
|
|
* : 'oddUpdates'
|
|
* // Route it!
|
|
* bot.route(router, routeHandlers)
|
|
* ```
|
|
*
|
|
* Optionally, you can pass a third option that is used as fallback
|
|
* middleware if your route function returns `undefined`, or if the key
|
|
* returned by your router has no middleware associated with it.
|
|
*
|
|
* This method may need less setup than first instantiating a `Router`, but
|
|
* for more complex setups, having a `Router` may be more readable.
|
|
*
|
|
* @param router The routing function to use
|
|
* @param routeHandlers Handlers for every route
|
|
* @param fallback Optional fallback middleware if no route matches
|
|
*/
|
|
route(router, routeHandlers, fallback = pass) {
|
|
return this.lazy(async (ctx) => {
|
|
var _a;
|
|
const route = await router(ctx);
|
|
return (_a = (route === undefined || !routeHandlers[route]
|
|
? fallback
|
|
: routeHandlers[route])) !== null && _a !== void 0 ? _a : [];
|
|
});
|
|
}
|
|
/**
|
|
* > This is an advanced method of grammY.
|
|
*
|
|
* Allows you to branch between two cases for a given context object.
|
|
*
|
|
* This method takes a predicate function that is tested once per context
|
|
* object. If it returns `true`, the first supplied middleware is executed.
|
|
* If it returns `false`, the second supplied middleware is executed. Note
|
|
* that the predicate may be asynchronous, i.e. it can return a Promise of a
|
|
* boolean.
|
|
*
|
|
* @param predicate The predicate to check
|
|
* @param trueMiddleware The middleware for the `true` case
|
|
* @param falseMiddleware The middleware for the `false` case
|
|
*/
|
|
branch(predicate, trueMiddleware, falseMiddleware) {
|
|
return this.lazy(async (ctx) => (await predicate(ctx)) ? trueMiddleware : falseMiddleware);
|
|
}
|
|
/**
|
|
* > This is an advanced function of grammY.
|
|
*
|
|
* Installs an error boundary that catches errors that happen only inside
|
|
* the given middleware. This allows you to install custom error handlers
|
|
* that protect some parts of your bot. Errors will not be able to bubble
|
|
* out of this part of your middleware system, unless the supplied error
|
|
* handler rethrows them, in which case the next surrounding error boundary
|
|
* will catch the error.
|
|
*
|
|
* Example usage:
|
|
* ```ts
|
|
* function errHandler(err: BotError) {
|
|
* console.error('Error boundary caught error!', err)
|
|
* }
|
|
*
|
|
* const safe =
|
|
* // All passed middleware will be protected by the error boundary.
|
|
* bot.errorBoundary(errHandler, middleware0, middleware1, middleware2)
|
|
*
|
|
* // Those will also be protected!
|
|
* safe.on('message', middleware3)
|
|
*
|
|
* // No error from `middleware4` will reach the `errHandler` from above,
|
|
* // as errors are suppressed.
|
|
*
|
|
* // do nothing on error (suppress error), and run outside middleware
|
|
* const suppress = (_err: BotError, next: NextFunction) => { return next() }
|
|
* safe.errorBoundary(suppress).on('edited_message', middleware4)
|
|
* ```
|
|
*
|
|
* Check out the
|
|
* [documentation](https://grammy.dev/guide/errors.html#error-boundaries) on
|
|
* the website to learn more about error boundaries.
|
|
*
|
|
* @param errorHandler The error handler to use
|
|
* @param middleware The middleware to protect
|
|
*/
|
|
errorBoundary(errorHandler, ...middleware) {
|
|
const composer = new Composer(...middleware);
|
|
const bound = flatten(composer);
|
|
this.use(async (ctx, next) => {
|
|
let nextCalled = false;
|
|
const cont = () => ((nextCalled = true), Promise.resolve());
|
|
try {
|
|
await bound(ctx, cont);
|
|
}
|
|
catch (err) {
|
|
nextCalled = false;
|
|
await errorHandler(new BotError(err, ctx), cont);
|
|
}
|
|
if (nextCalled)
|
|
await next();
|
|
});
|
|
return composer;
|
|
}
|
|
}
|
|
exports.Composer = Composer;
|