mobot 0.2.12

A system health bot
Documentation

mobot

mobot is a telegram chat bot written in Rust. Uses a native implementation of the Telegram Bot API. Docs at https://docs.rs/mobot.

MIT Licensed. Copyright 2023 Mohit Muthanna Cheppudira.

Features implemented so far

  • Messages, stickers, and inline queries.
  • Routing API for chat and inline query handlers.
  • Override POST handlers for testability.

Examples

Hello World

Bot that replies with "Hello world!" to every message. Working example in src/bin/hello.rs.

use mobot::*;

#[tokio::main]
async fn main() {
    let client = Client::new(std::env::var("TELEGRAM_TOKEN").unwrap().into());
    let mut router = Router::new(client);

    router.add_chat_handler(|_, _: chat::State<()>| async move {
        Ok(chat::Action::ReplyText("Hello world!".into()))
    }).await;
    router.start().await;
}

Counter Bot - Managing state

This bot demonstrates state managenent in your bots. Working example in src/bin/ping.rs.

/// Every Telegram chat session has a unique ID. This is used to identify the
/// chat that the bot is currently in.
///
/// The `ChatState` is a simple counter that is incremented every time a message
/// is received. Every chat session has its own `ChatState`. The `Router` keeps
/// track of the `ChatState` for each chat session.
#[derive(Debug, Clone, Default)]
struct ChatState {
    counter: usize,
}

/// This is our chat handler. We simply increment the counter and reply with a
/// message containing the counter.
async fn handle_chat_event(
    e: chat::Event,
    state: chat::State<ChatState>,
) -> Result<chat::Action, anyhow::Error> {
    let mut state = state.get().write().await;

    match e.message {
        chat::MessageEvent::New(message) => {
            state.counter += 1;

            Ok(chat::Action::ReplyText(format!(
                "pong({}): {}",
                state.counter,
                message.text.unwrap_or_default()
            )))
        }
        _ => anyhow::bail!("Unhandled update"),
    }
}

#[tokio::main]
async fn main() {
    mobot::init_logger();
    info!("Starting pingbot...");

    // The `Client` is the main entry point to the Telegram API. It is used to
    // send requests to the Telegram API.
    let client = Client::new(env::var("TELEGRAM_TOKEN").unwrap().into());

    // The `Router` is the main entry point to the bot. It is used to register
    // handlers for different types of events, and keeps track of the state of
    // the bot, passing it to the right handler.
    let mut router = Router::new(client);

    // We add a helper handler that logs all incoming messages.
    router.add_chat_handler(chat::log_handler).await;

    // We add our own handler that responds to messages.
    router.add_chat_handler(handle_chat_event).await;

    // Start the chat router -- this blocks forever.
    router.start().await;
}

Uptime Bot

Bot that returns server uptime. Working example in src/bin/uptime.rs.

/// The state of the chat. This is a simple counter that is incremented every
/// time a message is received.
#[derive(Debug, Clone, Default)]
struct ChatState {
    counter: usize,
}

/// Get the uptime of the system.
async fn get_uptime() -> anyhow::Result<String> {
    let child = Command::new("uptime").arg("-p").output();
    let output = child.await?;
    Ok(String::from_utf8(output.stdout)?)
}

/// The handler for the chat. This is a simple function that takes a `chat::Event`
/// and returns a `chat::Action`. It also receives the current `ChatState` for the
/// chat ID.
async fn handle_chat_event(e: chat::Event, state: chat::State<ChatState>)-> Result<chat::Action, anyhow::Error> {
    let mut state = state.get().write().await;

    match e.message {
        // Ignore the chat message, just return the uptime.
        chat::MessageEvent::New(_) => {
            state.counter += 1;

            Ok(chat::Action::ReplyText(format!(
                "uptime({}): {}",
                state.counter,
                get_uptime()
                    .await
                    .or(Err(anyhow!("Failed to get uptime")))?
            )))
        }
        _ => anyhow::bail!("Unhandled update"),
    }
}

#[tokio::main]
async fn main() {
    mobot::init_logger();
    info!("Starting uptimebot...");

    let client = Client::new(env::var("TELEGRAM_TOKEN").unwrap().into());
    let mut router = Router::new(client);

    router.add_chat_handler(chat::log_handler).await;
    router.add_chat_handler(handle_chat_event).await;
    router.start().await;
}

Testing

Set your telegram API token and then run bin/hello/rs.

export TELEGRAM_TOKEN=...

RUST_LOG=debug cargo run hello

Dependencies

Need OpenSSL and pkg-config.

sudo apt-get install pkg-config libssl-dev

TODO

  • Process handler return actions
  • Handler stack
  • OpenAI integration