Skip to main content

Filter Queries

Filters are needed in order to divide the logic into different types of updates, as well as to understand that you are guaranteed to work with the object you expected.

Of course, you can try to use if-else or switch, but they are usually not so convenient and create a lot of boilerplate code. The built-in filtering methods in Opengram allow you to filter updates easily and flexibly.

Filter types

Filters are present in the Composer class, its instances and all classes that inherit from it (Wizard / BaseScenes, Opengram) and are divided into several types:

  • Static methods - Usually often used where it is not required to create a chain of handlers and can be used to move the handlers in place with their filtering logic to a separate file.
  • Instance methods - Methods that are used to create filters and immediately register them on the current instance. They are the most commonly used. As an example, this is registering handlers directly to a bot instance (like bot.on or bot.hears)

Typically, filters exist as static methods and class members.

How filters works

caution

It may seem that the on method is an EventEmitter method, but it is not, it returns middleware like other methods, and is not an EventEmitter

Typically, you can filter by update type using .on method. If you look Update in Bots API reference, you can see update types like inline_query, message, callback_query and other, all of the then you can filter using .on method and some sugar methods like hears, inlineQuery, action (callback_query) and etc .

Also, some of the updates may have subtypes - message, edited_message, channel_post, edited_channel_post, every of them can contain subtypes like video, audio, text, photo, dice, poll and many others.

All filters can accept predicate function, regular expression or string or array of them.

Something like:

filter(['string', 'string2', (...) => ..., /test ([0-9]+)/], ...)
filter('sometext', ...)

Examples

note

All methods except .on(...) call next automatically if the filter does not fit the condition, this means that if you use this method (especially several), you should call next yourself in order to not stop the execution chain.

For example:

bot.on('message', (ctx) => {
// executed
})

bot.on('message', (ctx) => {
// never executed
})

bot.hears('hi', ctx => {
// never executed
})
bot.on('message', async (ctx, next) => {
// executed
await next()
})

bot.on('message', async (ctx, next) => {
// executed
await next()
})

bot.hears('hi', ctx => {
// executed
})

Regular Queries

bot.on('callback_query', ...) // any callback query
bot.on('edited_message', ...); // any edited message
bot.on('message', ...); // any message
bot.on('text', ...); // only messages with text
bot.on('photo', ...); // only messages with photo
bot.on('sticker', ...); // only messages with sticker

With sugar and advanced filtering

Text messages

// you can access match result from ctx.match. For this example it contains something like ['test 2', '2']
bot.hears(/^test ([0-9])/, ...) // messages which starts with "test" and contains 0-9 number after that by using regex
bot.hears('test', ...) // only messages where text = "test"
// custom filtering function
// if text contains "test" or "test2", middlewares will be executed and ctx.match will be "hello"
bot.hears((value, ctx) => {
// value - text of message, ctx - context
if (['test', 'test2'].includes(value) {
return 'hello'
}

return false
}, ...)

// text is "opengram" or "bot"
bot.hears(['opengram', 'bot'], ...)

// All above can be used with static methods, for example
bot.use(
Composer.hears(/^test ([0-9])/, ...)
)

Callback query

// you can access match result from ctx.match. For this example it contains something like ['test 2', '2']
bot.action(/^test ([0-9])/, ...) // messages which starts with "test" and contains 0-9 number after that by using regex
bot.action('test', ...) // only messages where text = "test"
// custom filtering function
// if callback_query data contains "test" or "test2", middlewares will be executed and ctx.match will be "hello"
bot.action((value, ctx) => {
// value - text of message, ctx - context
if (['test', 'test2'].includes(value) {
return 'hello'
}

return false
}, ...)

// callback query data is "opengram" or "bot"
bot.action(['opengram', 'bot'], ...)

// All above can be used with static methods, for example
bot.use(
Composer.action(/^test ([0-9])/, ...)
)

Entities

// By entity name
bot.entity('url', ...) // any contains url entity
bot.entity('code', ...) // any contains code entity

// custom match function
bot.entity((type, value, ctx) => {
return type === 'code'
}, ...)

// or

bot.email(...) // any contains email entity
bot.cashtag(...) // any contains cashtag entity
bot.textMention(...) // any contains text mention entity

// All above can be used with static methods, for example
bot.use(
Composer.entity('code', ...),
Composer.email(...),
Composer.cashtag(...),
Composer.textMention(...)
)

Combining queries

bot.on(['document', 'video'], ...) // Video or photo
bot.on(['message', 'edited_message'], ...) // Video or photo
bot.on(['message', 'edited_message'], ...) // Video or photo
bot.hears([(value, ctx) => ..., (value, ctx) => ..., /^test/], ...) // 1st or 2nd predicate return truthy or regex match
bot.action([(value, ctx) => ..., (value, ctx) => ..., /^test/], ...) // 1st or 2nd predicate return truthy or regex match
bot.inlineQuery([(value, ctx) => ..., (value, ctx) => ..., /^test/], ...) // 1st or 2nd predicate return truthy or regex match
bot.entity([
(type, value, ctx) => {
return type === 'code'
},
(type, value, ctx) => {
return type === 'email'
}
], ...) // 1st or 2nd predicate return truthy