Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Filters

Filters determine which updates reach a handler. They are composable, type-safe predicates that inspect an Update and return whether it matches.

Built-in Filters

TEXT

Matches any message that contains text:

#![allow(unused)]
fn main() {
use rust_tg_bot::ext::prelude::TEXT;

MessageHandler::new(TEXT(), my_handler)
}

COMMAND

Matches messages that start with a bot command (/something):

#![allow(unused)]
fn main() {
use rust_tg_bot::ext::prelude::COMMAND;

MessageHandler::new(COMMAND(), my_handler)
}

Combining Filters

Filters support bitwise operators for composition:

#![allow(unused)]
fn main() {
// Text messages that are NOT commands
TEXT() & !COMMAND()

// Text OR photo messages
// (when photo filter is available)
}

The operators:

  • & – AND: both filters must match.
  • | – OR: at least one filter must match.
  • ^ – XOR: exactly one filter must match.
  • ! – NOT: inverts the filter.

The F Wrapper

All filters are wrapped in the F type, which provides the operator overloads:

#![allow(unused)]
fn main() {
use rust_tg_bot::ext::prelude::F;
}

The TEXT() and COMMAND() functions return F values directly, so you can combine them immediately.

Filter Results

Filters return a FilterResult enum:

#![allow(unused)]
fn main() {
pub enum FilterResult {
    NoMatch,
    Match,
    MatchWithData(HashMap<String, Vec<String>>),
}
}
  • NoMatch – the update does not match.
  • Match – the update matches (no additional data).
  • MatchWithData – the update matches and carries extracted data (e.g., regex capture groups).

Available Filter Modules

The framework includes filters for many update properties:

ModuleWhat It Matches
textText content presence
commandBot commands (/start, etc.)
chatChat type (private, group, supergroup, channel)
userUser properties
documentDocument/file messages
photoPhoto messages
entityMessage entity types (mentions, URLs, etc.)
forwardedForwarded messages
regexText matching a regular expression
status_updateChat status changes (member joined, etc.)
via_botMessages sent via inline bots

Using Filters with MessageHandler

#![allow(unused)]
fn main() {
use rust_tg_bot::ext::prelude::{MessageHandler, TEXT, COMMAND};

// Echo non-command text
app.add_handler(
    MessageHandler::new(TEXT() & !COMMAND(), echo), 0,
).await;
}

Using Predicates with FnHandler

When built-in filters are not enough, FnHandler lets you write arbitrary predicates:

#![allow(unused)]
fn main() {
use rust_tg_bot::ext::prelude::FnHandler;

// Match updates that have a callback query with specific data
app.add_handler(
    FnHandler::new(
        |u| {
            u.callback_query()
                .and_then(|cq| cq.data.as_deref())
                == Some("my_button")
        },
        my_callback_handler,
    ),
    0,
).await;
}

Filter Composition in Practice

Here is a real-world example that matches text messages in a specific conversation state:

#![allow(unused)]
fn main() {
fn is_text_in_state(update: &Update, conv_store: &ConvStore, state: ConvState) -> bool {
    let msg = match update.effective_message() {
        Some(m) => m,
        None => return false,
    };
    // Must have text
    if msg.text.is_none() {
        return false;
    }
    // Must NOT be a command
    let is_cmd = msg.entities.as_ref()
        .and_then(|ents| ents.first())
        .map(|e| e.entity_type == MessageEntityType::BotCommand && e.offset == 0)
        .unwrap_or(false);
    if is_cmd {
        return false;
    }
    // Must be in the expected state
    let chat_id = msg.chat.id;
    conv_store.try_read()
        .map(|guard| guard.get(&chat_id) == Some(&state))
        .unwrap_or(false)
}
}

Then use it with FnHandler:

#![allow(unused)]
fn main() {
let cs_check = Arc::clone(&conv_store);
app.add_handler(
    FnHandler::new(
        move |u| is_text_in_state(u, &cs_check, ConvState::AskName),
        move |update, ctx| {
            let cs = Arc::clone(&cs);
            async move { receive_name(update, ctx, cs).await }
        },
    ),
    1,
).await;
}

Next Steps

Learn about the Context object that handlers receive in Context.