use crate::{Client, InvocationError, PeerRef};
use layer_tl_types as tl;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Notify;
use tokio::task::JoinHandle;
pub struct TypingGuard {
stop: Arc<Notify>,
task: Option<JoinHandle<()>>,
}
impl TypingGuard {
pub async fn start(
client: &Client,
peer: impl Into<PeerRef>,
action: tl::enums::SendMessageAction,
) -> Result<Self, InvocationError> {
let peer = peer.into().resolve(client).await?;
Self::start_ex(client, peer, action, None, Duration::from_secs(4)).await
}
pub async fn start_ex(
client: &Client,
peer: tl::enums::Peer,
action: tl::enums::SendMessageAction,
topic_id: Option<i32>,
repeat_delay: Duration,
) -> Result<Self, InvocationError> {
client
.send_chat_action_ex(peer.clone(), action.clone(), topic_id)
.await?;
let stop = Arc::new(Notify::new());
let stop2 = stop.clone();
let client = client.clone();
let task = tokio::spawn(async move {
loop {
tokio::select! {
_ = tokio::time::sleep(repeat_delay) => {
if let Err(e) = client.send_chat_action_ex(peer.clone(), action.clone(), topic_id).await {
tracing::warn!("[typing_guard] Failed to refresh typing action: {e}");
break;
}
}
_ = stop2.notified() => break,
}
}
let cancel = tl::enums::SendMessageAction::SendMessageCancelAction;
let _ = client
.send_chat_action_ex(peer.clone(), cancel, topic_id)
.await;
});
Ok(Self {
stop,
task: Some(task),
})
}
pub fn cancel(&mut self) {
self.stop.notify_one();
}
}
impl Drop for TypingGuard {
fn drop(&mut self) {
self.stop.notify_one();
if let Some(t) = self.task.take() {
t.abort();
}
}
}
impl Client {
pub async fn typing(&self, peer: impl Into<PeerRef>) -> Result<TypingGuard, InvocationError> {
TypingGuard::start(
self,
peer,
tl::enums::SendMessageAction::SendMessageTypingAction,
)
.await
}
pub async fn typing_in_topic(
&self,
peer: impl Into<PeerRef>,
topic_id: i32,
) -> Result<TypingGuard, InvocationError> {
let peer = peer.into().resolve(self).await?;
TypingGuard::start_ex(
self,
peer,
tl::enums::SendMessageAction::SendMessageTypingAction,
Some(topic_id),
std::time::Duration::from_secs(4),
)
.await
}
pub async fn uploading_document(
&self,
peer: impl Into<PeerRef>,
) -> Result<TypingGuard, InvocationError> {
TypingGuard::start(
self,
peer,
tl::enums::SendMessageAction::SendMessageUploadDocumentAction(
tl::types::SendMessageUploadDocumentAction { progress: 0 },
),
)
.await
}
pub async fn recording_video(
&self,
peer: impl Into<PeerRef>,
) -> Result<TypingGuard, InvocationError> {
TypingGuard::start(
self,
peer,
tl::enums::SendMessageAction::SendMessageRecordVideoAction,
)
.await
}
pub(crate) async fn send_chat_action_ex(
&self,
peer: tl::enums::Peer,
action: tl::enums::SendMessageAction,
topic_id: Option<i32>,
) -> Result<(), InvocationError> {
let input_peer = self.inner.peer_cache.read().await.peer_to_input(&peer);
let req = tl::functions::messages::SetTyping {
peer: input_peer,
top_msg_id: topic_id,
action,
};
self.rpc_write(&req).await
}
}