1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
use std::sync::Arc;
#[cfg(all(feature = "tokio_compat", not(feature = "tokio")))]
use tokio::time::delay_for as sleep;
#[cfg(feature = "tokio")]
use tokio::time::sleep;
use tokio::{
sync::oneshot::{self, error::TryRecvError, Sender},
time::Duration,
};
use crate::internal::tokio::spawn_named;
use crate::{error::Result, http::Http};
/// A struct to start typing in a [`Channel`] for an indefinite period of time.
///
/// It indicates that the current user is currently typing in the channel.
///
/// Typing is started by using the [`Typing::start`] method
/// and stopped by using the [`Typing::stop`] method.
/// Note that on some clients, typing may persist for a few seconds after [`Typing::stop`] is called.
/// Typing is also stopped when the struct is dropped.
///
/// If a message is sent while typing is triggered, the user will stop typing for a brief period
/// of time and then resume again until either [`Typing::stop`] is called or the struct is dropped.
///
/// This should rarely be used for bots, although it is a good indicator that a
/// long-running command is still being processed.
///
/// ## Examples
///
/// ```rust,no_run
/// # use serenity::{http::{Http, Typing}, Result};
/// # use std::sync::Arc;
/// #
/// # fn long_process() {}
/// # fn main() -> Result<()> {
/// # let http = Http::default();
/// // Initiate typing (assuming `http` is bound)
/// let typing = Typing::start(Arc::new(http), 7)?;
///
/// // Run some long-running process
/// long_process();
///
/// // Stop typing
/// typing.stop();
/// #
/// # Ok(())
/// # }
/// ```
///
/// [`Channel`]: crate::model::channel::Channel
#[derive(Debug)]
pub struct Typing(Sender<()>);
impl Typing {
/// Starts typing in the specified [`Channel`] for an indefinite period of time.
///
/// Returns [`Typing`]. To stop typing, you must call the [`Typing::stop`] method on
/// the returned [`Typing`] object or wait for it to be dropped. Note that on some
/// clients, typing may persist for a few seconds after stopped.
///
/// # Errors
///
/// Returns an [`Error::Http`] if there is an error.
///
/// [`Channel`]: crate::model::channel::Channel
/// [`Error::Http`]: crate::error::Error::Http
pub fn start(http: Arc<Http>, channel_id: u64) -> Result<Self> {
let (sx, mut rx) = oneshot::channel();
spawn_named("typing::start", async move {
loop {
match rx.try_recv() {
Ok(_) | Err(TryRecvError::Closed) => break,
_ => (),
}
http.broadcast_typing(channel_id).await?;
// It is unclear for how long typing persists after this method is called.
// It is generally assumed to be 7 or 10 seconds, so we use 7 to be safe.
sleep(Duration::from_secs(7)).await;
}
Result::Ok(())
});
Ok(Self(sx))
}
/// Stops typing in [`Channel`].
///
/// This should be used to stop typing after it is started using [`Typing::start`].
/// Typing may persist for a few seconds on some clients after this is called.
///
/// [`Channel`]: crate::model::channel::Channel
pub fn stop(self) -> Option<()> {
self.0.send(()).ok()
}
}