// The version of Heroku ping-pong-bot, which uses a webhook to receive updates // from Telegram, instead of long polling. use teloxide::{dispatching::update_listeners, prelude::*}; use std::{convert::Infallible, env, net::SocketAddr, sync::Arc}; use tokio::sync::mpsc; use warp::Filter; use reqwest::StatusCode; #[tokio::main] async fn main() { run().await; } async fn handle_rejection( error: warp::Rejection, ) -> Result { log::error!("Cannot process the request due to: {:?}", error); Ok(StatusCode::INTERNAL_SERVER_ERROR) } pub async fn webhook<'a>( bot: Arc, ) -> impl update_listeners::UpdateListener { // Heroku defines auto defines a port value let teloxide_token = env::var("TELOXIDE_TOKEN") .expect("TELOXIDE_TOKEN env variable missing"); let port: u16 = env::var("PORT") .expect("PORT env variable missing") .parse() .expect("PORT value to be integer"); // Heroku host example .: "heroku-ping-pong-bot.herokuapp.com" let host = env::var("HOST").expect("have HOST env variable"); let path = format!("bot{}", teloxide_token); let url = format!("https://{}/{}", host, path); bot.set_webhook(url).send().await.expect("Cannot setup a webhook"); let (tx, rx) = mpsc::unbounded_channel(); let server = warp::post() .and(warp::path(path)) .and(warp::body::json()) .map(move |json: serde_json::Value| { let try_parse = match serde_json::from_str(&json.to_string()) { Ok(update) => Ok(update), Err(error) => { log::error!( "Cannot parse an update.\nError: {:?}\nValue: {}\n\ This is a bug in teloxide, please open an issue here: \ https://github.com/teloxide/teloxide/issues.", error, json ); Err(error) } }; if let Ok(update) = try_parse { tx.send(Ok(update)) .expect("Cannot send an incoming update from the webhook") } StatusCode::OK }) .recover(handle_rejection); let serve = warp::serve(server); let address = format!("0.0.0.0:{}", port); tokio::spawn(serve.run(address.parse::().unwrap())); rx } async fn run() { teloxide::enable_logging!(); log::info!("Starting heroku_ping_pong_bot!"); let bot = Bot::from_env(); Dispatcher::new(Arc::clone(&bot)) .messages_handler(|rx: DispatcherHandlerRx| { rx.for_each(|message| async move { message.answer_str("pong").await.log_on_error().await; }) }) .dispatch_with_listener( webhook(bot).await, LoggingErrorHandler::with_custom_text( "An error from the update listener", ), ) .await; }