use crate::{keyboard::Reply, platform::PlatformKind, Result};
use std::sync::Arc;
#[derive(Clone)]
pub struct Ctx {
pub(crate) inner: Arc<CtxInner>,
}
pub(crate) struct CtxInner {
pub(crate) platform: PlatformKind,
pub(crate) chat_id: String,
pub(crate) user_id: String,
pub(crate) text: String,
pub(crate) reply_fn: ReplyFn,
pub(crate) is_dm: Option<bool>,
pub(crate) callback_data: Option<String>,
pub(crate) edit_fn: Option<EditFn>,
}
pub type ReplyFn =
Box<dyn Fn(Reply) -> futures::future::BoxFuture<'static, Result<()>> + Send + Sync + 'static>;
pub type EditFn =
Arc<dyn Fn(Reply) -> futures::future::BoxFuture<'static, Result<()>> + Send + Sync + 'static>;
impl Ctx {
pub fn new(
platform: PlatformKind,
chat_id: impl Into<String>,
user_id: impl Into<String>,
text: impl Into<String>,
reply_fn: ReplyFn,
) -> Self {
Self {
inner: Arc::new(CtxInner {
platform,
chat_id: chat_id.into(),
user_id: user_id.into(),
text: text.into(),
reply_fn,
is_dm: None,
callback_data: None,
edit_fn: None,
}),
}
}
pub fn new_full(
platform: PlatformKind,
chat_id: impl Into<String>,
user_id: impl Into<String>,
text: impl Into<String>,
reply_fn: ReplyFn,
is_dm: Option<bool>,
callback_data: Option<String>,
) -> Self {
Self {
inner: Arc::new(CtxInner {
platform,
chat_id: chat_id.into(),
user_id: user_id.into(),
text: text.into(),
reply_fn,
is_dm,
callback_data,
edit_fn: None,
}),
}
}
#[allow(clippy::too_many_arguments)]
pub fn new_with_edit(
platform: PlatformKind,
chat_id: impl Into<String>,
user_id: impl Into<String>,
text: impl Into<String>,
reply_fn: ReplyFn,
is_dm: Option<bool>,
callback_data: Option<String>,
edit_fn: Option<EditFn>,
) -> Self {
Self {
inner: Arc::new(CtxInner {
platform,
chat_id: chat_id.into(),
user_id: user_id.into(),
text: text.into(),
reply_fn,
is_dm,
callback_data,
edit_fn,
}),
}
}
pub fn platform(&self) -> PlatformKind {
self.inner.platform
}
pub fn chat_id(&self) -> &str {
&self.inner.chat_id
}
pub fn user_id(&self) -> &str {
&self.inner.user_id
}
pub fn text(&self) -> &str {
&self.inner.text
}
pub fn args(&self) -> &str {
self.inner
.text
.split_once(char::is_whitespace)
.map(|(_, rest)| rest.trim())
.unwrap_or("")
}
pub fn is_dm(&self) -> bool {
match self.inner.is_dm {
Some(v) => v,
None => {
matches!(self.inner.platform, PlatformKind::Telegram)
&& self.inner.chat_id == self.inner.user_id
}
}
}
pub fn callback_data(&self) -> Option<&str> {
self.inner.callback_data.as_deref()
}
pub fn is_callback(&self) -> bool {
self.inner.callback_data.is_some()
}
pub async fn reply(&self, text: impl Into<String>) -> Result<()> {
(self.inner.reply_fn)(Reply::text(text)).await
}
pub async fn reply_with(&self, reply: impl Into<Reply>) -> Result<()> {
(self.inner.reply_fn)(reply.into()).await
}
pub async fn edit_reply(&self, reply: impl Into<Reply>) -> Result<()> {
let r = reply.into();
if let Some(edit) = &self.inner.edit_fn {
return (edit)(r).await;
}
(self.inner.reply_fn)(r).await
}
}