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