Provide a webhook example instead of a library function

This commit is contained in:
Temirkhan Myrzamadi 2020-03-31 00:57:19 +06:00
parent 8ea460e04f
commit 51129f5d0f
4 changed files with 103 additions and 49 deletions

View file

@ -25,9 +25,6 @@ maintenance = { status = "actively-developed" }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
webhooks = ["warp"]
[dependencies]
serde_json = "1.0.44"
serde = { version = "1.0.101", features = ["derive"] }
@ -47,12 +44,9 @@ async-trait = "0.1.22"
futures = "0.3.1"
pin-project = "0.4.6"
serde_with_macros = "1.0.1"
either = "1.5.3"
teloxide-macros = "0.2.1"
warp = { version = "0.2.2", features = ["tls"], optional = true }
[dev-dependencies]
smart-default = "0.6.0"
rand = "0.7.3"

View file

@ -0,0 +1,18 @@
[package]
name = "webhook_ping_pong_bot"
version = "0.1.0"
authors = ["Temirkhan Myrzamadi <hirrolot@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4.8"
futures = "0.3.4"
tokio = "0.2.9"
pretty_env_logger = "0.4.0"
teloxide = { path = "../../" }
# Used to setup a webhook
warp = "0.2.2"

View file

@ -0,0 +1,85 @@
// The version of 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, 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<impl warp::Reply, Infallible> {
log::error!("Cannot process the request due to: {:?}", error);
Ok(StatusCode::INTERNAL_SERVER_ERROR)
}
pub async fn webhook<'a>(
bot: Arc<Bot>,
) -> impl update_listeners::UpdateListener<Infallible> {
// You might want to specify a self-signed certificate via .certificate
// method on SetWebhook.
bot.set_webhook("Your HTTPS ngrok URL here. Get it by 'ngrok http 80'")
.send()
.await
.expect("Cannot setup a webhook");
let (tx, rx) = mpsc::unbounded_channel();
let server = warp::post()
.and(warp::body::json())
.map(move |json: serde_json::Value| {
match serde_json::from_str::<Update>(&json.to_string()) {
Ok(update) => tx
.send(Ok(update))
.expect("Cannot send an incoming update from the webhook"),
Err(error) => {
// In this case, please report a bug at https://github.com/teloxide/teloxide/issues !!!
log::error!(
"Cannot parse Update: {}\nError: {}",
json,
error
);
}
}
StatusCode::OK
})
.recover(handle_rejection);
let serve = warp::serve(server);
// You might want to use serve.key_path/serve.cert_path methods here to
// setup a self-signed TLS certificate.
tokio::spawn(serve.run("127.0.0.1:80".parse::<SocketAddr>().unwrap()));
rx
}
async fn run() {
teloxide::enable_logging!();
log::info!("Starting ping_pong_bot!");
let bot = Bot::from_env();
Dispatcher::new(Arc::clone(&bot))
.messages_handler(|rx: DispatcherHandlerRx<Message>| {
rx.for_each(|message| async move {
message.answer("pong").send().await.log_on_error().await;
})
})
.dispatch_with_listener(
webhook(bot).await,
LoggingErrorHandler::with_custom_text(
"An error from the update listener",
),
)
.await;
}

View file

@ -198,46 +198,3 @@ pub fn polling(
)
.flatten()
}
#[cfg(feature = "webhooks")]
pub async fn webhook<KP, KB, CP, CB>(
url: reqwest::Url,
addr: std::net::SocketAddr,
key: either::Either<KP, KB>,
cert: either::Either<CP, CB>,
) -> crate::prelude::ResponseResult<impl UpdateListener<std::convert::Infallible>>
where
KP: AsRef<std::path::Path>,
KB: AsRef<[u8]>,
CP: AsRef<std::path::Path>,
CB: AsRef<[u8]>,
{
use either::Either;
use tokio::sync::mpsc;
use warp::Filter;
let (tx, rx) = mpsc::unbounded_channel();
let server = warp::post().and(warp::path(url)).and(warp::body::json()).map(
move |update: Update| {
tx.send(Ok(update)).expect("Cannot send an update from webhook");
""
},
);
let serve = warp::serve(server).tls();
let serve = match key {
Either::Left(path) => serve.key_path(path),
Either::Right(bytes) => serve.key(bytes),
};
let serve = match cert {
Either::Left(path) => serve.cert_path(path),
Either::Right(bytes) => serve.cert(bytes),
};
tokio::spawn(serve.run(addr));
Ok(rx)
}