use super::EventLoop;
use crate::{errors, types::parameters::UpdateKind};
use std::{
convert::{Infallible, TryInto},
num::NonZeroUsize,
sync::Arc,
time::Duration,
};
use tokio::time::{delay_for, timeout as timeout_future};
use tracing::instrument;
type ErrorHandler = dyn Fn(errors::Polling) + Send + Sync;
#[must_use = "polling does nothing unless `start` is called"]
pub struct Polling {
event_loop: EventLoop,
limit: Option<u8>,
timeout: Option<u64>,
allowed_updates: Option<&'static [UpdateKind]>,
poll_interval: Duration,
error_handler: Box<ErrorHandler>,
request_timeout: Option<Duration>,
offset: Option<isize>,
}
impl Polling {
pub(crate) fn new(event_loop: EventLoop) -> Self {
Self {
event_loop,
limit: None,
timeout: None,
allowed_updates: None,
poll_interval: Duration::from_millis(25),
error_handler: Box::new(|err| {
eprintln!("[tbot] Polling error: {:#?}", err);
}),
request_timeout: None,
offset: None,
}
}
pub fn limit(mut self, limit: u8) -> Self {
self.limit = Some(limit);
self
}
pub fn timeout(mut self, timeout: u64) -> Self {
self.timeout = Some(timeout);
self
}
pub fn allowed_updates(
mut self,
allowed_updates: &'static [UpdateKind],
) -> Self {
self.allowed_updates = Some(allowed_updates);
self
}
pub fn error_handler<H, F>(mut self, handler: H) -> Self
where
H: (Fn(errors::Polling) -> F) + Send + Sync + 'static,
F: std::future::Future<Output = ()> + Send + 'static,
{
self.error_handler = Box::new(move |error| {
tokio::spawn(handler(error));
});
self
}
pub const fn poll_interval(mut self, poll_interval: Duration) -> Self {
self.poll_interval = poll_interval;
self
}
pub const fn request_timeout(mut self, timeout: Duration) -> Self {
self.request_timeout = Some(timeout);
self
}
pub fn last_n_updates(mut self, n: NonZeroUsize) -> Self {
let n: isize = n.get().try_into().unwrap_or_else(|_| {
panic!("\n[tbot] Cannot convert {} to isize safely\n", n);
});
self.offset = Some(-n);
self
}
}
impl Polling {
#[instrument(name = "polling", skip(self))]
pub async fn start(self) -> Result<Infallible, errors::PollingSetup> {
let Self {
event_loop,
poll_interval,
limit,
timeout,
allowed_updates,
error_handler,
request_timeout,
mut offset,
} = self;
let request_timeout = request_timeout
.unwrap_or_else(|| Duration::from_secs(timeout.unwrap_or(0) + 60));
let delete_webhook = event_loop.bot.delete_webhook().call();
timeout_future(request_timeout, delete_webhook).await??;
let bot = Arc::new(event_loop.bot.clone());
loop {
let mut next_tick = delay_for(poll_interval);
let get_updates = bot
.get_updates(offset, limit, timeout, allowed_updates)
.call();
match timeout_future(request_timeout, get_updates).await {
Ok(Ok(raw_updates)) => {
if let Some(update) = raw_updates.last() {
offset = Some(update.id.0 + 1);
}
for raw_update in raw_updates {
match raw_update.try_into() {
Ok(update) => event_loop
.handle_update(Arc::clone(&bot), update),
Err(error) => eprintln!(
"[tbot] Failed to parse an update: {:?}. `tbot` \
will skip it, but this error means that \
`tbot`'s type deserialization doesn't match \
the Bot API. You should file an issue at \
https://gitlab.com/SnejUgal/tbot.",
error
),
}
}
}
Ok(Err(error)) => {
if let errors::MethodCall::RequestError {
retry_after: Some(retry_after),
..
} = error
{
next_tick = delay_for(Duration::from_secs(retry_after));
}
error_handler(error.into());
}
Err(error) => error_handler(error.into()),
}
next_tick.await
}
}
}