Module tbot::compositors

source ·
Expand description

Useful compositors for update handlers.

Let’s start with a simple example. You’re writing a bot, and its main functionality is searching through something. In private chats, it’s enough to send a text message to start searching; but in groups, it’s better to search only when someone sent the /search command. Since you already expected to have the same logic in different handlers, you wrote a generic handler, search, and then registered it like this:

use std::sync::Arc;
use tbot::{Bot, contexts::fields::Text, prelude::*};
async fn search(context: Arc<impl Text>) {
    // ..

let mut bot = Bot::from_env("BOT_TOKEN").event_loop();

bot.text(|context| async move {
    if {

bot.command("search", search);

The text handler seems a bit too long just to filter out messages from channels and groups. That’s where we can make use of our first compositor, filter! It takes a predicate and a handler and returns its own handler. If the predicate returns true, filter’s handler executes yours. Here’s an example:

use tbot::{contexts::Text, compositors::filter};

    |context: Arc<Text>| async move { },

That looks better! But tbot already provides the predicate we just wrote ourselves, so let’s use that:

use tbot::{compositors::filter, predicates::chat::is_private};

bot.text(filter(is_private, search));

Great! Now you see how compositors make registering handlers easier. filter isn’t the only compositor, you’ll find all of them in this module.

Remember one thing though. Compositors add layers between the handler and handler registers. If you’re going to use a closure for your handler, type inference will fail and you’ll have to explicitly define context’s type, as seen in the example with an inlined predicate above. That only affects closures: using plain functions is fine even with generics, as seen in the last example.


All the compositors, but adapted for StatefulEventLoop.


Filters updates: executes handler only if predicate returns true.
Filters and maps updates: calls predicate, and if it returned Some, calls handler with that value.
Maps updates: calls mapper, and then passes its return value to handler.