Skip to main content

stoat/
utils.rs

1use std::time::{Duration, SystemTime};
2
3use stoat_database::events::server::ClientMessage;
4use tokio::{select, time::sleep};
5use ulid::Ulid;
6
7use crate::context::Events;
8
9pub fn created_at(id: &str) -> SystemTime {
10    Ulid::from_string(id).expect("Malformed ID").datetime()
11}
12
13/// Automatically sends typing events while the inner future is running, this is useful for displaying that the bot is still processing the users input.
14///
15/// This will automatically end typing when the inner future is finished.
16///
17/// Also accessable via [`crate::ChannelExt::with_typing`].
18///
19/// ## Example:
20/// ```rust
21/// let output = with_typing(&ctx, channel_id, async move {
22///     // Some long calculation
23/// }).await;
24///
25/// ctx.reply()
26///     .content(output)
27///     .build()
28///     .await?
29/// ```
30pub async fn with_typing<Fut: Future<Output = R>, R>(
31    events: impl AsRef<Events>,
32    channel_id: String,
33    fut: Fut,
34) -> R {
35    let events = events.as_ref();
36
37    let bg = {
38        let events = events.clone();
39        let channel_id = channel_id.clone();
40
41        async move {
42            loop {
43                if let Err(e) = events.send_event(ClientMessage::BeginTyping {
44                    channel: channel_id.clone(),
45                }) {
46                    log::error!("Error occurred in with_typing: {e:?}");
47                };
48
49                sleep(Duration::from_secs(10)).await;
50            }
51        }
52    };
53
54    select! {
55        _ = bg => {
56            unreachable!()
57        },
58        r = fut => {
59            if let Err(e) = events.send_event(ClientMessage::EndTyping { channel: channel_id }) {
60                log::error!("Error occurred in with_typing: {e:?}");
61            };
62
63            r
64        }
65    }
66}