use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
#[cfg(not(target_arch = "wasm32"))]
use async_io::Timer;
#[cfg(target_arch = "wasm32")]
use gloo_timers::future::sleep;
use crate::BotError;
#[cfg(not(target_arch = "wasm32"))]
pub type ChatActionFuture<'a> = Pin<Box<dyn Future<Output = Result<(), BotError>> + Send + 'a>>;
#[cfg(target_arch = "wasm32")]
pub type ChatActionFuture<'a> = Pin<Box<dyn Future<Output = Result<(), BotError>> + 'a>>;
#[cfg(not(target_arch = "wasm32"))]
pub trait ChatActionSenderBounds: Send + Sync {}
#[cfg(not(target_arch = "wasm32"))]
impl<T: Send + Sync + ?Sized> ChatActionSenderBounds for T {}
#[cfg(target_arch = "wasm32")]
pub trait ChatActionSenderBounds {}
#[cfg(target_arch = "wasm32")]
impl<T: ?Sized> ChatActionSenderBounds for T {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ChatAction {
Typing,
UploadPhoto,
RecordVideo,
UploadVideo,
RecordVoice,
UploadVoice,
UploadDocument,
ChooseSticker,
FindLocation,
RecordVideoNote,
UploadVideoNote,
}
pub trait ChatActionSender: ChatActionSenderBounds + 'static {
fn send_action(&self, action: ChatAction) -> ChatActionFuture<'_>;
fn action_expiry(&self) -> Duration;
fn clone_boxed(&self) -> Box<dyn ChatActionSender>;
}
pub struct ChatActionGuard {
stop_flag: Arc<AtomicBool>,
}
impl ChatActionGuard {
pub fn start(sender: Box<dyn ChatActionSender>, action: ChatAction) -> Self {
let stop_flag = Arc::new(AtomicBool::new(false));
let flag_clone = Arc::clone(&stop_flag);
let expiry = sender.action_expiry();
let renewal_interval = Duration::from_millis((expiry.as_millis() as u64 * 80) / 100);
spawn_renewal(async move {
let _ = sender.send_action(action).await;
loop {
sleep_for(renewal_interval).await;
if flag_clone.load(Ordering::Acquire) {
break;
}
if sender.send_action(action).await.is_err() {
break;
}
}
});
Self { stop_flag }
}
}
#[cfg(not(target_arch = "wasm32"))]
async fn sleep_for(duration: Duration) {
Timer::after(duration).await;
}
#[cfg(not(target_arch = "wasm32"))]
fn spawn_renewal(task: impl Future<Output = ()> + Send + 'static) {
executor_core::spawn(task).detach();
}
#[cfg(target_arch = "wasm32")]
async fn sleep_for(duration: Duration) {
sleep(duration).await;
}
#[cfg(target_arch = "wasm32")]
fn spawn_renewal(task: impl Future<Output = ()> + 'static) {
executor_core::spawn_local(task).detach();
}
impl Drop for ChatActionGuard {
fn drop(&mut self) {
self.stop_flag.store(true, Ordering::Release);
}
}