Composer

The composer is the heart of the middleware system in Opengram. It is also the super class of Opengram.

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.

Constructor

new Composer(…fns)

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.

Parameters:
NameTypeAttributesDescription
fnsMiddleware<repeatable>

The middlewares to compose as arguments

Methods

action(triggers, …fns) → {Composer}

Registers some middleware(s) 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

bot.on('callback_query', ctx => { ... })

but it also allows you to match the query data against a given text or regular expression.

// Create an inline keyboard
const keyboard = Markup.inlineKeyboard([
  Markup.callbackButton('Go!', 'button-payload')
])
// Send a message with the keyboard
await bot.telegram.sendMessage(chat_id, 'Press a button!', keyboard.extra())
// Listen to users pressing buttons with that specific payload
bot.action('button-payload', ctx => { ... })

// Listen to users pressing any button your bot ever sent
bot.on('callback_query', ctx => { ... })

Always remember to call Telegram#answerCbQuery or OpengramContext#answerCbQuery — even if you don't perform any action: https://core.telegram.org/bots/api#answercallbackquery

bot.on('callback_query', async ctx => {
  await ctx.answerCbQuery()
})

You can pass one or an array of triggers (Regexp / strings). Your middleware(s) will be executed if at least one of them matches.

Note how ctx.match will contain the result of the regular expression. So ctx.match[1] refers to the part of the regexp that was matched by ([0-9]+), i.e. the text that comes after "button:".

bot.action(/button:([0-9]+)/, ctx => ctx.reply(`You choose button with number ${ctx.match[1]} in payload`))
const keyboard = Markup.inlineKeyboard([
 Markup.callbackButton('Button 1', 'button:1'),
 Markup.callbackButton('Button 2', 'button:2'),
 Markup.callbackButton('Button 3', 'button:3')
])
await bot.telegram.sendMessage(chat_id, 'Press a button!', keyboard.extra())

You can also paste function (or array of functions) that takes the value and context as arguments and returns true or false (or some Truthy result) based on them. This can be used, for example, for dynamic text matching at i18n. The result returned by the function will be available from ctx.match

bot.action(
  (value, ctx) => {
    //... some checks ...
    return ['some', 'data']
  },
  // Show cb query answer for all queries with "I love some data"
  ctx => ctx.answerCbQuery(`I love ${ctx.match[0]} ${ctx.match[1]}`)
)
Parameters:
NameTypeAttributesDescription
triggersTrigger | Array.<Trigger>

One or an array of regular expressions / strings to search in the payload

fnsMiddleware<repeatable>

The middleware(s) to register as arguments

Returns:
Type: 
Composer

cashtag(…args) → {Composer}

Registers some middleware(s) that will only be executed if hashtag entity is present in the update

Shortcut to Composer.entity('cashtag', ...)

This method matches entity in channel post, message and media caption

Parameters:
NameTypeAttributesDescription
argsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Composer

command(commands, …fns)

Registers some middleware(s) that will only be executed when a certain command is found.

// Reacts to /start commands
bot.command('start', ctx => { ... })
// Reacts to /help commands
bot.command('help', ctx => { ... })

Note: Commands are not matched in the middle of the text.

bot.command('start', ctx => { ... })
// ... does not match:
// A message saying: “some text /start some more text”
// A photo message with the caption “some text /start some more text”

By default, commands are detected in channel posts and media captions, too. This means that ctx.message for channel post or ctx.message.text for media is potentially undefined, so you should use ctx.channelPost and ctx.message.caption accordingly for channel posts. Alternatively, if you want to limit your bot to finding commands only in private and group chats, you can use

const { Opengram, Composer: { command } } = require('opengram')
// ...
bot.on('message', command('start', ctx => ctx.reply('Only private / group messages or media with caption')))`

or using Composer.chatType:

const { Opengram, Composer, Composer: { command } } = require('opengram')
// ...
bot.use(
  Composer.chatType(
    ["private", "group", "supergroup"],
    command('start', ctx => ctx.reply('Only private / group messages or media with caption'))
  )
)

for match all message exclude channel posts, or

const { Opengram, Composer: { command } } = require('opengram')
// ...
bot.on('text', command('start', ctx => ctx.reply('Math commands only text, not media captions')))

for match only text message, not media caption or even store a message-only version of your bot in a variable like so:

Be careful, the example above may not work as expected if channelMode is enabled.

By default text type not match channel posts, but channel_post matched as text type and ctx.message potentially undefined when channelMode enabled. You can add additional chat type check for this case

Parameters:
NameTypeAttributesDescription
commandsstring | Array.<string> | 'start' | 'settings' | 'help'

The command or array of commands to look for

fnsMiddleware<repeatable>

The middleware(s) to register as arguments

customEmoji(…args) → {Composer}

Registers some middleware(s) that will only be executed if custom_emoji entity is present in the update

Shortcut to Composer.entity('custom_emoji', ...)

This method matches entity in channel post, message and media caption

Parameters:
NameTypeAttributesDescription
argsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Composer

drop(predicate) → {Composer}

Registers middleware behind a custom filter function that operates on the context object and decides whether to execute the middleware.

In other words, the middleware(s) after that middleware will only be executed if the given predicate returns false for the given context object. 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.

// Drop all message updates sent more than 6 hr in all middlewares / handlers registered after bot.drop(...)
bot.drop(ctx => {
  if(!ctx.message) return false // Drop only messages
  return (Date.now() / 1000) - ctx.message.date < 60 * 60 * 6
})
// Called only for messages with date < 6 hr after send
bot.on('message', () => ctx.reply('Good, update date less then 6 hours!'))
Parameters:
NameTypeDescription
predicatePredicateFn | boolean

The predicate to check. Can be async, returns boolean or Promise with boolean

Returns:
Type: 
Composer

email(…args) → {Composer}

Registers some middleware(s) that will only be executed if email entity is present in the update

Shortcut to Composer.entity('email', ...)

This method matches entity in channel post, message and media caption

Parameters:
NameTypeAttributesDescription
argsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Composer

entity(predicate, …fns) → {Composer}

Registers some middleware(s) that will only be executed if a certain entity is present in the update

This method matches entity in channel post, message and media caption

Parameters:
NameTypeAttributesDescription
predicateEntityPredicate

The predicate to check. Can be async, returns boolean or Promise with boolean

fnsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Composer

filter(predicate) → {Composer}

Registers middleware(s) behind a custom filter function that operates on the context object and decides whether to execute the middleware. In other words, the middleware will only be executed if the given predicate returns true for the given context object. Otherwise, it will be skipped and the next middleware will be executed.

In other words, the middleware after that middleware will only be executed if the given predicate returns true for the given context object. Note that the predicate may be asynchronous, i.e. it can return a Promise of a boolean.

// Only process every second update
bot.filter(ctx => ctx.update.update_id % 2 === 0)
bot.on('message', ctx => ctx.reply('Update id of this message is divided by two without a remainder'))
Parameters:
NameTypeDescription
predicatePredicateFn | boolean

The predicate to check. Can be async, returns boolean or Promise with boolean

Returns:
Type: 
Composer

gameQuery(…fns) → {Composer}

Registers some middleware(s) 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

bot.on('callback_query', ctx => {
 if (ctx.callbackQuery.game_short_name) {
   ...
 }
})
Parameters:
NameTypeAttributesDescription
fnsMiddleware<repeatable>

The middleware to register as arguments

Returns:
Type: 
Composer

hashtag(…args) → {Composer}

Registers some middleware(s) that will only be executed if hashtag entity is present in the update

Shortcut to Composer.entity('hashtag', ...)

This method matches entity in channel post, message and media caption

Parameters:
NameTypeAttributesDescription
argsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Composer

hears(triggers, …fns)

Registers some middleware(s) that will only be executed when the message / channel post contains some text (in media caption too). Is it possible to pass a regular expression to match:

// Match some text (exact match)
bot.hears('I love anime', ctx => ctx.reply('I love too'))

// 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. 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 also paste function (or array of functions) that takes the value and context as arguments and returns true or false (or some Truthy result) based on them. This can be used, for example, for dynamic text matching at i18n. The result returned by the function will be available from ctx.match

bot.hears(
  (value, ctx) => {
    //... some checks ...
    return ['some', 'data']
  },
  ctx => ctx.reply(`I love ${ctx.match[0]} ${ctx.match[1]}`) // Replies at all with "I love some data"
)

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:

const { Composer: { hears } } = require('opengram')
// Only matches text messages for the regex
bot.on('text', hears(/\/echo (.+)/, ctx => { ... }))

Be careful, the example above may not work as expected if channelMode is enabled.

By default text type not match channel posts, but channel_post matched as text type and ctx.message potentially undefined when channelMode enabled. You can add additional chat type check for this case

Parameters:
NameTypeAttributesDescription
triggersTrigger | Array.<Trigger>

The text / array of texts / regex / function to look for

fnsMiddleware<repeatable>

The middleware(s) to register as argument(s)

help(…fns) → {Middleware}

Registers some middleware that will only be executed when /help command is found.

Shortcut to Composer.command('help', ...)

Parameters:
NameTypeAttributesDescription
fnsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Middleware

inlineQuery(triggers, …fns) → {Composer}

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.

Your 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 here to read more about inline bots.

Note that you have to enable inline mode for you bot by contacting @BotFather first.

// 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( ... )
})

You can pass one or an array of triggers (Regexp / strings). Your middleware(s) will be executed if at least one of them matches.

Note how ctx.match will contain the result of the regular expression. So ctx.match[1] refers to the part of the regexp that was matched by ([0-9]+), i.e. the text that comes after "query:".

// Listen for users typing `@your_bot_name query`
bot.inlineQuery(/query:([0-9]+)/, async ctx => {
  // Answer the inline query, confer https://core.telegram.org/bots/api#answerinlinequery
  await ctx.answerInlineQuery([{
    type: 'article',
    id: Math.random(),
    title: 'Regex test',
    cache_time: 1,
    description: `Query Regex result: ${ctx.match[1]}`,
    input_message_content: {
      message_text: `Query Regex result: ${ctx.match[1]}`,
    }
  }])
})

You can also paste function (or array of functions) that takes the value and context as arguments and returns true or false (or some Truthy result) based on them. This can be used, for example, for dynamic text matching at i18n. The result returned by the function will be available from ctx.match

bot.inlineQuery(
  (value, ctx) => {
    //... some checks ...
    return ['some', 'data']
  },
  // Show cb query answer for all queries with "I love some data"
  ctx => ctx.answerInlineQuery([{
    type: 'article',
    id: Math.random(),
    title: 'Regex test',
    cache_time: 1,
    description: `I love ${ctx.match[0]} ${ctx.match[1]}`,
    input_message_content: {
      message_text: `I love ${ctx.match[0]} ${ctx.match[1]}`,
    }
  }])
})
Parameters:
NameTypeAttributesDescription
triggersTrigger | Array.<Trigger>

The inline query text or array of text to match

fnsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Composer

mention(…args) → {Composer}

Registers some middleware(s) that will only be executed if mention entity is present in the update

Shortcut to Composer.entity('mention', ...)

This method matches entity in channel post, message and media caption

Parameters:
NameTypeAttributesDescription
argsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Composer

middleware() → {Middleware}

Returns the middleware to embed

Returns:
Type: 
Middleware

on(updateTypes, …fns) → {Composer}

Registers some middleware(s) 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:

// All kinds of message updates
bot.on('message', ctx => { ... })

// Text messages
bot.on('text', ctx => { ... })

// Messages with document
bot.on('document', ctx => { ... })

It is possible to pass multiple filter queries in an array, i.e.

// Matches all messages that contain a video or audio
bot.on(['audio', 'video'], ctx => { ... })

Your middleware will be executed if any of the provided filter queries matches (logical OR).

This method returns same as Composer#use.

Parameters:
NameTypeAttributesDescription
updateTypesUpdateType | UpdateSubtype | Array.<(UpdateType|UpdateSubtype)>

The update type or array of update types to use, may also be an array or string

fnsMiddleware<repeatable>

The middleware(s) to register with the given types as argument(s)

Returns:
Type: 
Composer

phone(…args) → {Composer}

Registers some middleware(s) that will only be executed if phone entity is present in the update

Shortcut to Composer.entity('phone', ...)

This method matches entity in channel post, message and media caption

Parameters:
NameTypeAttributesDescription
argsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Composer

settings(…fns) → {Middleware}

Registers some middleware that will only be executed when /settings command is found.

Shortcut to Composer.command('settings', ...)

Parameters:
NameTypeAttributesDescription
fnsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Middleware

spoiler(…args) → {Composer}

Registers some middleware(s) that will only be executed if spoiler entity is present in the update

Shortcut to Composer.entity('spoiler', ...)

This method matches entity in channel post, message and media caption

Parameters:
NameTypeAttributesDescription
argsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Composer

start(…fns) → {Middleware}

Registers some middleware that will only be executed when /start command is found.

Shortcut to Composer.command('start', ...), but with additional functionally, when you use this and deep linking, you can get start payload from ctx.startPayload

For example if user start the bot from link like this: http://t.me/examplebot?start=1234

With this code, bot reply user with text of start payload:

bot.start(ctx => ctx.reply(`Start payload: ${ctx.startPayload}`)) // Reply with "Start payload: 1234"
Parameters:
NameTypeAttributesDescription
fnsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Middleware

Registers some middleware(s) that will only be executed if text_link entity is present in the update

Shortcut to Composer.entity('text_link', ...)

This method matches entity in channel post, message and media caption

Parameters:
NameTypeAttributesDescription
argsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Composer

textMention(…args) → {Composer}

Registers some middleware(s) that will only be executed if text_mention entity is present in the update

Shortcut to Composer.entity('text_mention', ...)

This method matches entity in channel post, message and media caption

Parameters:
NameTypeAttributesDescription
argsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Composer

url(…args) → {Composer}

Registers some middleware(s) that will only be executed if url entity is present in the update

Shortcut to Composer.entity('url', ...)

This method matches entity in channel post, message and media caption

Parameters:
NameTypeAttributesDescription
argsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Composer

use(…fns) → {Composer}

Registers some middleware(s) 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(s) that behaves like a plugin, for example session middleware.

bot.use(session())

You can pass middleware separated by commas as arguments or as a chain of calls:

const { Opengram, Stage, session } = require('opengram')
const bot = require('opengram')
const stage = new Stage([...])
bot.use(session(), stage) // As arguments

or

const { Opengram, Stage, session } = require('opengram')
const bot = require('opengram')
const stage = new Stage([...])
bot // As chain of calls
  .use(session())
  .use(stage)

This method returns a new instance of Composer.

Parameters:
NameTypeAttributesDescription
fnsMiddleware<repeatable>

The middleware(s) to register as arguments

Returns:
Type: 
Composer

(static) acl(userId, …fns) → {Middleware}

Generates and returns a middleware that runs the given middleware(s) only for when array of given id's contains user id or predicate function returns true for given context

Access-control list - allows you to create guards for middlewares

Usage example:

bot.use(
  Composer.admin(
    1234567890,
    Composer.reply('Some middleware for admin of bot - 1234567890')
  )
)

bot.use(
  Composer.admin(
    [1234567890, 09876543],
    Composer.reply('Some middleware for admins of bot - 1234567890 and 09876543')
  )
)


function checkIsAdmin (ctx) {
  // ...
  return true
}

bot.use(
  Composer.admin(
    ctx => checkIsAdmin(ctx),
    Composer.reply('Some middleware for admins of bot')
  )
)
Parameters:
NameTypeAttributesDescription
userIdPredicateFn | number | Array.<number>

The predicate to check or user id / array of user id's

fnsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Middleware

(static) action(triggers, …fns) → {Middleware}

Generates middleware that execute given middlewares will only be executed for certain 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

bot.on('callback_query', ctx => { ... })

but it also allows you to match the query data against a given text or regular expression.

// Create an inline keyboard
const keyboard = Markup.inlineKeyboard([
  Markup.callbackButton('Go!', 'button-payload')
])
// Send a message with the keyboard
await bot.telegram.sendMessage(chat_id, 'Press a button!', keyboard.extra())
// Listen to users pressing buttons with that specific payload
bot.use(
  Composer.action('button-payload', ctx => { ... })
)

// Listen to users pressing any button your bot ever sent
bot.on('callback_query', ctx => { ... })

Always remember to call Telegram#answerCbQuery or OpengramContext#answerCbQuery — even if you don't perform any action: https://core.telegram.org/bots/api#answercallbackquery

bot.on('callback_query', async ctx => {
  await ctx.answerCbQuery()
})

You can pass one or an array of triggers (Regexp / strings). Your middleware(s) will be executed if at least one of them matches.

Note how ctx.match will contain the result of the regular expression. So ctx.match[1] refers to the part of the regexp that was matched by ([0-9]+), i.e. the text that comes after "button:".

const mw = Composer.action(/button:([0-9]+)/, ctx => ctx.reply(`You choose button with number ${ctx.match[1]} in payload`))
const keyboard = Markup.inlineKeyboard([
 Markup.callbackButton('Button 1', 'button:1'),
 Markup.callbackButton('Button 2', 'button:2'),
 Markup.callbackButton('Button 3', 'button:3')
])

bot.use(mw)
await bot.telegram.sendMessage(chat_id, 'Press a button!', keyboard.extra())

You can also paste function (or array of functions) that takes the value and context as arguments and returns true or false (or some Truthy result) based on them. This can be used, for example, for dynamic text matching at i18n. The result returned by the function will be available from ctx.match

bot.use(
  Composer.action(
    (value, ctx) => {
      //... some checks ...
      return ['some', 'data']
    },
    // Show cb query answer for all queries with "I love some data"
    ctx => ctx.answerCbQuery(`I love ${ctx.match[0]} ${ctx.match[1]}`)
  )
)
Parameters:
NameTypeAttributesDescription
triggersTrigger | Array.<Trigger>

One or an array of regular expressions / strings to search in the payload

fnsMiddleware<repeatable>

The middleware(s) to register as arguments

Returns:
Type: 
Middleware

(static) admin(…fns) → {Middleware}

Generates and returns a middleware that runs the given middleware(s) only for updates if member status = creator or 'administrator'

Usage example:

bot.use(
  Composer.admin(
    Composer.reply('I work only when called by chat creator and administrator ')
  )
)
Parameters:
NameTypeAttributesDescription
fnsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Middleware

(static) branch(predicate, trueMiddleware, falseMiddlewareopt) → {Middleware}

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.

bot.use(
  Composer.branch(
    (ctx) => ctx.from.is_premium,
    (ctx) => ctx.reply('This mw executed only for premium users'),
    (ctx) => ctx.reply('Buy premium :(')
  )
)
Parameters:
NameTypeAttributesDescription
predicatePredicateFn | boolean

The predicate to check. Can be async, returns boolean or Promise with boolean

trueMiddlewareMiddleware

The middleware for the true case

falseMiddlewareMiddleware<optional>

The middleware for the false case

Returns:
Type: 
Middleware

(static) catch(errorHandler, …fns) → {Middleware}

Generates middleware that catches all errors in the middleware(s) given to it and calls given error handler

Parameters:
NameTypeAttributesDescription
errorHandlerfunction

Error handler which takes error and context object as arguments

fnsMiddleware<repeatable>

Middleware(s)

Returns:
Type: 
Middleware

(static) catchAll(…fns) → {Middleware}

Generates middleware that catches all errors in the middleware(s) given to it and outputs them to the console

Parameters:
NameTypeAttributesDescription
fnsMiddleware<repeatable>

Middlewares

Returns:
Type: 
Middleware

(static) chatType(type, …fns) → {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:

Usage example:

const privateZone = new Composer()
privateZone.command("start", ctx => { ... })

bot.use(
  Composer.chatType('private', privateZone)
)

bot.use(
  Composer.chatType('supergroup', Composer.reply('I work only in supergroups chats'))
)

bot.use(
  Composer.chatType(['supergroup', 'group'], Composer.reply('I work only in supergroup + group chats'))
)
const onlyGroup = new Composer()

onlyGroup.hears(...)
onlyGroup.command(...)
// ...
bot.use(
  Composer.chatType('group', onlyGroup)
)
Parameters:
NameTypeAttributesDescription
typeArray.<ChatType> | ChatType

Chat type or array of shat types

fnsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Middleware

(static) command(command, …fns)

Generates middleware that execute given middlewares will only be executed if a certain command is found.

// Reacts to /start commands
bot.use(
  Composer.command('start', ctx => { ... })
)
// Reacts to /help commands
bot.use(
  Composer.command('help', ctx => { ... })
)

Note: Commands are not matched in the middle of the text.

bot.use(
  Composer.command('start', ctx => { ... })
)
// ... does not match:
// A message saying: “some text /start some more text”
// A photo message with the caption “some text /start some more text”

By default, commands are detected in channel posts and media captions, too. This means that ctx.message for channel post or ctx.message.text for media is potentially undefined, so you should use ctx.channelPost and ctx.message.caption accordingly for channel posts. Alternatively, if you want to limit your bot to finding commands only in private and group chats, you can use

const { Opengram, Composer: { command } } = require('opengram')
// ...
bot.on('message', command('start', ctx => ctx.reply('Only private / group messages or media with caption')))`

or using Composer.chatType:

const { Opengram, Composer, Composer: { command } } = require('opengram')
// ...
bot.use(
  Composer.chatType(
    ["private", "group", "supergroup"],
    command('start', ctx => ctx.reply('Only private / group messages or media with caption'))
  )
)

for match all message exclude channel posts, or

const { Opengram, Composer: { command } } = require('opengram')
// ...
bot.on('text', command('start', ctx => ctx.reply('Math commands only text, not media captions')))

for match only text message, not media caption or even store a message-only version of your bot in a variable like so:

Be careful, the example above may not work as expected if channelMode is enabled.

By default text type not match channel posts, but channel_post matched as text type and ctx.message potentially undefined when channelMode enabled. You can add additional chat type check for this case

Parameters:
NameTypeAttributesDescription
commandstring | Array.<string> | 'start' | 'settings' | 'help'

The command or array of commands to look for

fnsMiddleware<repeatable>

The middleware(s) to register as arguments

(static) compose(middlewares) → {Middleware}

Used for compose array of middlewares

Parameters:
NameTypeDescription
middlewaresArray.<Middleware>

The middlewares for compose

Throws:
Error | TypeError
Returns:
Type: 
Middleware

(static) creator(…fns) → {Middleware}

Generates and returns a middleware that runs the given middleware(s) only for updates if member status = creator

Usage example:

bot.use(
  Composer.creator(
    Composer.reply('I work only when called by chat creator')
  )
)
Parameters:
NameTypeAttributesDescription
fnsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Middleware

(static) drop(predicate) → {Middleware}

Generates middleware behind a custom filter function that operates on the context object and decides whether to execute the middleware.

In other words, the middleware(s) after that middleware will only be executed if the given predicate returns false for the given context object. 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.

// Drop all message updates sent more than 6 hr in all middlewares / handlers registered after bot.drop(...)
const mw = Composer.drop(ctx => {
  if(!ctx.message) return false // Drop only messages
  return (Date.now() / 1000) - ctx.message.date < 60 * 60 * 6
})
// Called only for messages with date < 6 hr after send
bot.on('message', mw, () => ctx.reply('Good, update date less then 6 hours!'))
Parameters:
NameTypeDescription
predicatePredicateFn | boolean

The predicate to check. Can be async, returns boolean or Promise with boolean

Returns:
Type: 
Middleware

(static) entity(predicate, …fns) → {Middleware}

Generates middleware that execute given middlewares if a certain entity is present in the update

This method matches entity in channel post, message and media caption

Parameters:
NameTypeAttributesDescription
predicateEntityPredicate

The predicate to check. Entity name or predicate function. If function provided, it can be sync only and returns boolean

fnsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Middleware

(static) filter(predicate) → {Middleware}

Generates middleware behind a custom filter function that operates on the context object and decides whether to execute the middleware. In other words, the middleware will only be executed if the given predicate returns true for the given context object. Otherwise, it will be skipped and the next middleware will be executed.

In other words, the middleware after that middleware will only be executed if the given predicate returns true for the given context object. Note that the predicate may be asynchronous, i.e. it can return a Promise of a boolean.

// Only process every second update
bot.on(
 'message',
 Composer.filter(ctx => ctx.update.update_id % 2 === 0)
 ctx => ctx.reply('Update id of this message is divided by two without a remainder')
)
Parameters:
NameTypeDescription
predicatePredicateFn | boolean

The predicate to check. Can be async, returns boolean or Promise with boolean

Returns:
Type: 
Middleware

(static) fork(middleware) → {Middleware}

Registers some middleware that runs concurrently to the executing middleware stack. Runs the middleware at the next event loop using setImmediate and force call next()

For example, you can use that method for saving metrics and other non-priority or optional features in background

❗️ If you call next in this middleware, then nothing will happen, it will be ignored

Parameters:
NameTypeDescription
middlewareMiddleware

The middleware to run concurrently

Returns:
Type: 
Middleware

(static) groupChat(…fns) → {Middleware}

Creates and returns a middleware that runs the given middleware only for updates from "group" and "supergroup".

Usage example:

// Send message with text "I do not support group chats" when receive update from group chat
bot.use(
  Composer.groupChat(Composer.reply('I do not support group chats'))
)

Isolate group commands:

const group = new Composer()

group.hears(...)
group.command(...)

bot.use(
  Composer.groupChat(group)
)
Parameters:
NameTypeAttributesDescription
fnsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Middleware

(static) hears(triggers, …fns)

Generates middleware that execute given middlewares when the message / channel post contains some text (in media caption too). Is it possible to pass a regular expression to match:

// Match some text (exact match)
bot.use(
  Composer.hears('I love anime', ctx => ctx.reply('I love too'))
)

// Match a regular expression
bot.use(
  Composer.hears(/\/echo (.+)/, ctx => ctx.reply(ctx.match[1]))
)

Note how ctx.match will contain the result of the regular expression. 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 also paste function (or array of functions) that takes the value and context as arguments and returns true or false (or some Truthy result) based on them. This can be used, for example, for dynamic text matching at i18n. The result returned by the function will be available from ctx.match

bot.use(
  Composer.hears(
    (value, ctx) => {
      //... some checks ...
      return ['some', 'data']
    },
    ctx => ctx.reply(`I love ${ctx.match[0]} ${ctx.match[1]}`) // Replies at all with "I love some data"
  )
)

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:

// Only matches text messages for the regex
bot.on('text', Composer.hears(/\/echo (.+)/, ctx => { ... }))

Be careful, the example above may not work as expected if channelMode is enabled.

By default text type not match channel posts, but channel_post matched as text type and ctx.message potentially undefined when channelMode enabled. You can add additional chat type check for this case

Parameters:
NameTypeAttributesDescription
triggersTrigger | Array.<Trigger>

The text / array of texts / regex / function to look for

fnsMiddleware<repeatable>

The middleware(s) to register as argument(s)

(static) inlineQuery(triggers, …fns) → {Middleware}

Generates middleware that execute given middleware(s) will only be executed for certain inline queries. Telegram sends an inline query to your bot whenever a user types @your_bot_name ... into a text field in Telegram.

Your 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 here to read more about inline bots.

Note that you have to enable inline mode for you bot by contacting @BotFather first.

// Listen for users typing `@your_bot_name query`
bot.use(
  Composer.inlineQuery('query', async ctx => {
    // Answer the inline query, confer https://core.telegram.org/bots/api#answerinlinequery
    await ctx.answerInlineQuery( ... )
  })
)

You can pass one or an array of triggers (Regexp / strings). Your middleware(s) will be executed if at least one of them matches.

Note how ctx.match will contain the result of the regular expression. So ctx.match[1] refers to the part of the regexp that was matched by ([0-9]+), i.e. the text that comes after "query:".

// Listen for users typing `@your_bot_name query`
bot.use(
  Composer.inlineQuery(/query:([0-9]+)/, async ctx => {
    // Answer the inline query, confer https://core.telegram.org/bots/api#answerinlinequery
    await ctx.answerInlineQuery([{
      type: 'article',
      id: Math.random(),
      title: 'Regex test',
      cache_time: 1,
      description: `Query Regex result: ${ctx.match[1]}`,
      input_message_content: {
        message_text: `Query Regex result: ${ctx.match[1]}`,
      }
    }])
  })
)

You can also paste function (or array of functions) that takes the value and context as arguments and returns true or false (or some Truthy result) based on them. This can be used, for example, for dynamic text matching at i18n. The result returned by the function will be available from ctx.match

bot.inlineQuery(
  (value, ctx) => {
    //... some checks ...
    return ['some', 'data']
  },
  // Show cb query answer for all queries with "I love some data"
  ctx => ctx.answerInlineQuery([{
    type: 'article',
    id: Math.random(),
    title: 'Regex test',
    cache_time: 1,
    description: `I love ${ctx.match[0]} ${ctx.match[1]}`,
    input_message_content: {
      message_text: `I love ${ctx.match[0]} ${ctx.match[1]}`,
    }
  }])
})
Parameters:
NameTypeAttributesDescription
triggersTrigger | Array.<Trigger>

The inline query text or array of text to match

fnsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Middleware

(static) lazy(factoryFn) → {Middleware.<Promise>}

Lazily asynchronously returns some middleware that can be generated on the fly for each context. Pass a factory function that creates some middleware

The factory function will be called once per context, and its result will be executed with the context object.

// The middleware returned by `createMyMiddleware` will be used only once
bot.use(
  Composer.lazy(ctx => createMyMiddleware(ctx))
)

You may generate this middleware in an async fashion.

Parameters:
NameTypeDescription
factoryFnfunction

The factory function creating the middleware

Throws:
TypeError
Returns:
Type: 
Middleware.<Promise>

(static) log(logFn) → {Middleware}

Method that generates a middleware to output the content of the context with indented beautiful in console using serialization

The default for logs is console.log, you can pass your own log function in argument:

const myOwnLogFn = (data) => console.log('[Logs]', data)
bot.use(Composer.log(myOwnLogFn))
Parameters:
NameTypeDescription
logFnfunction

Custom log function

Returns:
Type: 
Middleware

(static) match(triggers, …fns)

Generates middleware that execute given middlewares when some given trigger(s) returns true

Triggers are executed for channel post / message text / callback query / inline query

Example:

Composer.match (/[a-z]/, ...fns)

Composer.match ([/[a-z]/, /[0-9]/], ...fns)

Composer.match ((value, context) => {
  // ...checks...
} , ...fns)

Composer.match (
  (value, context) => {
    // ...checks...
  },
  (value, context) => {
    // ...checks...
  },
  ...fns
)
Parameters:
NameTypeAttributesDescription
triggersTrigger | Array.<Trigger>

The text / array of texts / regex / function to look for

fnsMiddleware<repeatable>

The middleware(s) to register as argument(s)

(static) memberStatus(status, …fns) → {Middleware}

Generates and returns a middleware that runs the given middleware(s) only for updates user has one of given member statuses of chat

Usage example:

bot.use(
  Composer.memberStatus(
    ["creator", "administrator"],
    Composer.reply('I work only for chat creator and administrator ')
  )
)
Parameters:
NameTypeAttributesDescription
statusArray.<ChatMemberStatus> | ChatMemberStatus

Member status of array of statuses

fnsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Middleware

(static) mount(updateType, …fns) → {Middleware}

Generates middleware that execute given middleware(s) only 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:

// All kinds of message updates
bot.use(
  Composer.mount('message', ctx => { ... })
)

// Text messages
bot.use(
  Composer.mount('text', ctx => { ... })
)

// Messages with document
bot.use(
  Composer.mount('document', ctx => { ... })
)

It is possible to pass multiple filter queries in an array, i.e.

// Matches all messages that contain a video or audio
bot.use(
  Composer.mount(['audio', 'video'], ctx => { ... })
)

Your middleware will be executed if any of the provided filter queries matches (logical OR).

Parameters:
NameTypeAttributesDescription
updateTypeUpdateType | UpdateSubtype | Array.<(UpdateType|UpdateSubtype)>

The update type or array of update types to use, may also be an array or string

fnsMiddleware<repeatable>

The middleware(s) to register with the given types as argument(s)

Returns:
Type: 
Middleware

(static) optional(predicate, …fns) → {Middleware}

Generates middleware that makes given middleware(s) optional

Example

bot.use(
  Composer.optional(
    (ctx) => ctx.from.is_premium, // Check premium
    // The handlers from below will be executed only if predict returns true
    async (ctx, next) => {
      await ctx.reply('This mw and below will be executed only for premium users')
      return next()
    },
    (ctx) => {
      // ...other middleware.. ...code...,
    },
    (ctx) => {
      // ...other middleware.. ...code...,
    },
  )
)
Parameters:
NameTypeAttributesDescription
predicatePredicateFn | boolean

The predicate to check. Can be async, returns boolean or Promise with boolean

fnsMiddleware<repeatable>

Middleware(s)

Returns:
Type: 
Middleware

(static) passThru() → {Middleware}

Generates middleware which call next middleware

For example, you can use it with Composer.branch or other to skip middleware (make middleware optional)

Returns:
Type: 
Middleware

(static) privateChat(…fns) → {Middleware}

Generates and returns a middleware that runs the given middleware only for updates from "private" (DM)

Usage example:

// Send message with text "I do not support group chats" when receive update from group chat
bot.use(
  Composer.privateChat(Composer.reply('I work only in group chats'))
)

Isolate private commands:

const private = new Composer()

private.hears(...)
private.command(...)

bot.use(
  Composer.privateChat(private)
)
Parameters:
NameTypeAttributesDescription
fnsMiddleware<repeatable>

The middleware(s) to register

Returns:
Type: 
Middleware

(static) reply(text, extraopt) → {Middleware.<Promise.<Message>>}

Generates and return middleware for reply with given arguments, has same arguments like in Opengram, context method reply

Usage example:

// Send message with text "I do not support group chats" when receive update from group chat
bot.use(
  Composer.groupChat(Composer.reply('I do not support group chats'))
)
Parameters:
NameTypeAttributesDescription
textstring

Text of the message to be sent, 1-4096 characters after entities parsing

extraExtraSendMessage | Extra<optional>

Other parameters

Returns:
Type: 
Middleware.<Promise.<Message>>

(static) safePassThru() → {Middleware}

Generates middleware which call next middleware if next function exists or returns empty resolved promise

This method is similar to Composer.passThru(), but calls next if exists, otherwise returns resolved promise

Returns:
Type: 
Middleware

(static) tap(middleware) → {Middleware}

Middleware that calls a middleware or chain of middleware and calls the next, whether it called next or not. Allows you to execute some code and continue execution regardless of its result.

Parameters:
NameTypeDescription
middlewareMiddleware

The middleware to run without access to next

Returns:
Type: 
Middleware

(static) unwrap(handler) → {Middleware}

Method used for unwrapping middleware, when middleware has method with name middleware (middleware factory) Composer.unwrap calls him and return result

This method used in some other Composer methods, like Composer.compose, Composer.lazy and other

Parameters:
NameTypeDescription
handlerMiddleware

The middleware for unwrap

Throws:
Error
Returns:
Type: 
Middleware