Dependency Injection Callback Telegram Bot

I am new to Rust programming language and I am trying to create a Telegram bot using the Teloxide library. I am having an issue with passing an instance of the "Eliza" struct to the "answer" function, which is used as a callback by the "repl" function. I have tried different methods such as creating a struct with "Eliza" inside and making "answer" a method of the struct to use "Eliza" implicitly in it, and also tried using "Lazy_static", but none of them seem to work. The only thing that works is instantiating "eliza" inside the "answer" function but it is not an efficient method as it takes up a lot of resources and does not remember the conversation. I have come across a trait called "Injectable" but I am unable to understand how to use it. As I am a beginner in rust, many basic things are not clear to me. Can anyone please help me out with this?

use teloxide::dptree::di::{Injectable, Asyncify, CompiledFn};
use teloxide::{prelude::*, utils::command::BotCommands};
use teloxide::repl;
use tokio;
use eliza::Eliza;

#[tokio::main]
async fn main() {
    pretty_env_logger::init();
    log::info!("Starting start bot...");

    let bot = Bot::new("Token");
    let ela = Eliza::from_file("src/ai-helper/doctor.json").unwrap();
    repl(bot, answer).await;
} 
fn answer(bot: Bot, msg: Message) -> ResponseResult<()> {
//i want eliza there
    let cmd = Command::parse(msg.text().unwrap(), "");
    match cmd {
        Ok(Command::Start) => {
            bot.send_message(msg.chat.id, /*eliza.greet()*/);
        },
        Ok(Command::Diagnosis) => {
            bot.send_message(msg.chat.id, /*eliza.farewell()*/);
        },
        _ => {
            bot.send_message(msg.chat.id, /*eliza.respond(msg.text().unwrap()*/));
        }
    }
    Ok(())
}

The docs have a section that points you in the right direction called "Dispatching or REPLs?"

The difference between dispatching and the REPLs (crate::repl & co) is that dispatching gives you a greater degree of flexibility at the cost of a bit more complicated setup.

Here are things that dispatching can do, but REPLs can’t:

Thus, REPLs are good for simple bots and rapid prototyping, but for more involved scenarios, we recommend using dispatching over REPLs.

Passing dependencies is what you're trying to do, so the repl function probably isn't what you want.

If you peek at the implementation of the repl function, and combine that with the shared state example on github, you might end up with something like this

use std::sync::Arc;
use teloxide::prelude::*;

struct State;

#[tokio::main]
async fn main() {
    let bot = Bot::new("Token");
    let state = Arc::new(State);

    Dispatcher::builder(bot, Update::filter_message().endpoint(answer))
        .dependencies(dptree::deps![state])
        .build()
        .dispatch()
        .await;
}

async fn answer(bot: Bot, state: Arc<State>, msg: Message) -> ResponseResult<()> {
    todo!();
}

State is just a placeholder for your actual data you want to share.

I don't have a telegram dev environment so I haven't tested it, but it builds.


Technically you could also use a static Mutex or something to share data between main and your answer function but there's a perfectly good dependency injection system in the library so you might as well use it!

2 Likes

you saved me, thank you very much

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.