From 4abafbcacce955a1cbd6e0caa8f1d076b9d7378e Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 25 Mar 2022 18:29:19 +0400 Subject: [PATCH] Write documentation for webhooks --- src/dispatching/update_listeners.rs | 14 ++-- src/dispatching/update_listeners/webhooks.rs | 14 ++++ .../update_listeners/webhooks/axum.rs | 71 ++++++++++++++++++- 3 files changed, 92 insertions(+), 7 deletions(-) diff --git a/src/dispatching/update_listeners.rs b/src/dispatching/update_listeners.rs index 4f3c01b5..4c01d174 100644 --- a/src/dispatching/update_listeners.rs +++ b/src/dispatching/update_listeners.rs @@ -1,18 +1,20 @@ //! Receiving updates from Telegram. //! //! The key trait here is [`UpdateListener`]. You can get its implementation -//! using one these functions: +//! from: //! -//! - [`polling_default`], which returns a default long polling listener. -//! - [`polling`], which returns a long polling listener with your +//! - [`polling_default`] function, which returns a default long polling +//! listener. +//! - [`polling`] function, which returns a long polling listener with your //! configuration. +//! - Various functions in the [`webhooks`] module that return webhook listeners //! //! And then you can extract updates from it or pass them directly to a //! [`Dispatcher`]. //! //! Telegram supports two ways of [getting updates]: [long polling] and -//! [webhooks]. Currently, only the former one is implemented (see [`polling()`] -//! and [`polling_default`]). See also [README FAQ about webhooks](https://github.com/teloxide/teloxide/blob/master/README.md#faq). +//! [webhooks]. For the former see [`polling`] and [`polling_default`], for the +//! latter see the [`webhooks`] module. //! //! [`UpdateListener`]: UpdateListener //! [`polling_default`]: polling_default @@ -23,6 +25,8 @@ //! [long polling]: https://en.wikipedia.org/wiki/Push_technology#Long_polling //! [webhooks]: https://en.wikipedia.org/wiki/Webhook +/// Implementations of webhook update listeners - an alternative (to +/// [`fn@polling`]) way of receiving updates from telegram. #[cfg(any(feature = "webhooks-axum"))] pub mod webhooks; diff --git a/src/dispatching/update_listeners/webhooks.rs b/src/dispatching/update_listeners/webhooks.rs index 41df9085..4a6f8546 100644 --- a/src/dispatching/update_listeners/webhooks.rs +++ b/src/dispatching/update_listeners/webhooks.rs @@ -1,3 +1,4 @@ +//! use std::net::SocketAddr; use crate::{requests::Requester, types::InputFile}; @@ -62,6 +63,9 @@ pub use self::axum::{axum, axum_no_setup, axum_to_router}; #[cfg(feature = "webhooks-axum")] mod axum; +/// Calls `set_webhook` with arguments from `options`. +/// +/// Note: this takes out `certificate`. async fn setup_webhook(bot: R, options: &mut Options) -> Result<(), R::Err> where R: Requester, @@ -80,6 +84,16 @@ where Ok(()) } +/// Returns first (`.0`) field from a tuple as a `&mut` reference. +/// +/// This hack is needed because there isn't currently a way to easily force a +/// closure to be higher-ranked (`for<'a> &'a mut _ -> &'a mut _`) which causes +/// problems when using [`StatefulListener`] to implement update listener. +/// +/// This could be probably removed once [rfc#3216] is implemented. +/// +/// [`StatefulListener`]: +/// [rfc#3216]: https://github.com/rust-lang/rfcs/pull/3216 fn tuple_first_mut(tuple: &mut (A, B)) -> &mut A { &mut tuple.0 } diff --git a/src/dispatching/update_listeners/webhooks/axum.rs b/src/dispatching/update_listeners/webhooks/axum.rs index 24ef5f11..1d90b3ff 100644 --- a/src/dispatching/update_listeners/webhooks/axum.rs +++ b/src/dispatching/update_listeners/webhooks/axum.rs @@ -11,7 +11,16 @@ use crate::{ requests::Requester, }; -/// Webhook implementation based on the [axum] framework. +/// Webhook implementation based on the [mod@axum] framework. +/// +/// This function does all the work necessary for webhook to work, it: +/// - Calls [`set_webhook`], so telegram starts sending updates our way +/// - Spawns [mod@axum] server listening for updates +/// - When the update listener is [`stop`]ped, calls [`delete_webhook`] +/// +/// [`set_webhook`]: crate::payloads::SetWebhook +/// [`delete_webhook`]: crate::payloads::DeleteWebhook +/// [`stop`]: StopToken::stop /// /// ## Panics /// @@ -19,9 +28,14 @@ use crate::{ /// /// [address]: Options.address /// -/// ## Errors +/// ## Fails /// /// If `set_webhook()` fails. +/// +/// ## See also +/// +/// [`axum_to_router`] and [`axum_no_setup`] for lower-level versions of this +/// function. pub async fn axum( bot: R, options: Options, @@ -50,6 +64,47 @@ where Ok(update_listener) } +/// Webhook implementation based on the [mod@axum] framework that can reuse +/// existing [mod@axum] server. +/// +/// This function does most of the work necessary for webhook to work, it: +/// - Calls [`set_webhook`], so telegram starts sending updates our way +/// - When the update listener is [`stop`]ped, calls [`delete_webhook`] +/// +/// The only missing part is running [mod@axum] server with a returned +/// [`axum::Router`]. +/// +/// This function is intended to be used in cases when you already have an +/// [mod@axum] server running and can reuse it for webhooks. +/// +/// **Note**: in order for webhooks to work, you need to use returned +/// [`axum::Router`] in an [mod@axum] server that is bound to +/// [`options.address`]. +/// +/// It may also be desired to use [`with_graceful_shutdown`] with the returned +/// future in order to shutdown the server with the [`stop`] of the listener. +/// +/// [`set_webhook`]: crate::payloads::SetWebhook +/// [`delete_webhook`]: crate::payloads::DeleteWebhook +/// [`stop`]: StopToken::stop +/// [`options.address`]: Options.address +/// [`with_graceful_shutdown`]: axum::Server::with_graceful_shutdown +/// +/// ## Returns +/// +/// A update listener, stop-future, axum router triplet on success. +/// +/// The "stop-future" is resolved after [`stop`] is called on the stop token of +/// the returned update listener. +/// +/// ## Fails +/// +/// If `set_webhook()` fails. +/// +/// ## See also +/// +/// [`fn@axum`] for higher-level and [`axum_no_setup`] for lower-level +/// versions of this function. pub async fn axum_to_router( bot: R, mut options: Options, @@ -85,6 +140,13 @@ where Ok((listener, stop_flag, router)) } +/// Webhook implementation based on the [mod@axum] framework that doesn't +/// perform any setup work. +/// +/// ## See also +/// +/// [`fn@axum`] and [`axum_to_router`] for higher-level versions of this +/// function. pub fn axum_no_setup( options: Options, ) -> ( @@ -110,6 +172,10 @@ pub fn axum_no_setup( let (tx, rx): (Sender, _) = mpsc::unbounded_channel(); async fn telegram_request(input: String, tx: Extension) -> impl IntoResponse { + // FIXME: this should probably start returning an error response after `.stop()` + // is called to account for cases when update listener is stopped without + // stopping the server + match serde_json::from_str(&input) { Ok(update) => { tx.send(Ok(update)).expect("Cannot send an incoming update from the webhook") @@ -139,6 +205,7 @@ pub fn axum_no_setup( let stream = UnboundedReceiverStream::new(rx); + // FIXME: this should support `hint_allowed_updates()` let listener = update_listeners::StatefulListener::new( (stream, stop_token), tuple_first_mut,