use rustigram_types::update::UpdateKind;
use crate::context::Context;
pub trait Filter: Send + Sync + 'static {
fn check(&self, ctx: &Context) -> bool;
}
pub trait FilterExt: Filter + Sized + Clone {
fn and<F: Filter + Clone>(self, other: F) -> And<Self, F> {
And {
left: self,
right: other,
}
}
fn or<F: Filter + Clone>(self, other: F) -> Or<Self, F> {
Or {
left: self,
right: other,
}
}
fn not(self) -> Not<Self> {
Not { inner: self }
}
}
impl<F: Filter + Clone> FilterExt for F {}
#[derive(Clone)]
pub struct And<L, R> {
left: L,
right: R,
}
impl<L: Filter, R: Filter> Filter for And<L, R> {
fn check(&self, ctx: &Context) -> bool {
self.left.check(ctx) && self.right.check(ctx)
}
}
#[derive(Clone)]
pub struct Or<L, R> {
left: L,
right: R,
}
impl<L: Filter, R: Filter> Filter for Or<L, R> {
fn check(&self, ctx: &Context) -> bool {
self.left.check(ctx) || self.right.check(ctx)
}
}
#[derive(Clone)]
pub struct Not<F> {
inner: F,
}
impl<F: Filter> Filter for Not<F> {
fn check(&self, ctx: &Context) -> bool {
!self.inner.check(ctx)
}
}
#[derive(Clone)]
pub struct FnFilter<F>(pub F);
impl<F: Fn(&Context) -> bool + Send + Sync + Clone + 'static> Filter for FnFilter<F> {
fn check(&self, ctx: &Context) -> bool {
(self.0)(ctx)
}
}
pub fn filter_fn<F>(f: F) -> FnFilter<F>
where
F: Fn(&Context) -> bool + Send + Sync + Clone + 'static,
{
FnFilter(f)
}
#[derive(Clone, Copy)]
pub struct MessageFilter;
impl Filter for MessageFilter {
fn check(&self, ctx: &Context) -> bool {
matches!(ctx.update.kind, UpdateKind::Message(_))
}
}
#[derive(Clone, Copy)]
pub struct EditedMessageFilter;
impl Filter for EditedMessageFilter {
fn check(&self, ctx: &Context) -> bool {
matches!(ctx.update.kind, UpdateKind::EditedMessage(_))
}
}
#[derive(Clone, Copy)]
pub struct CallbackQueryFilter;
impl Filter for CallbackQueryFilter {
fn check(&self, ctx: &Context) -> bool {
matches!(ctx.update.kind, UpdateKind::CallbackQuery(_))
}
}
#[derive(Clone, Copy)]
pub struct InlineQueryFilter;
impl Filter for InlineQueryFilter {
fn check(&self, ctx: &Context) -> bool {
matches!(ctx.update.kind, UpdateKind::InlineQuery(_))
}
}
#[derive(Clone)]
pub struct CommandFilter {
command: String,
}
impl CommandFilter {
pub fn new(command: impl Into<String>) -> Self {
Self {
command: command.into(),
}
}
}
impl Filter for CommandFilter {
fn check(&self, ctx: &Context) -> bool {
ctx.command()
.is_some_and(|cmd| cmd.eq_ignore_ascii_case(&self.command))
}
}
#[derive(Clone)]
pub struct TextFilter {
text: String,
}
impl TextFilter {
pub fn new(text: impl Into<String>) -> Self {
Self { text: text.into() }
}
}
impl Filter for TextFilter {
fn check(&self, ctx: &Context) -> bool {
ctx.text().is_some_and(|t| t == self.text)
}
}
#[derive(Clone)]
pub struct TextContainsFilter {
needle: String,
}
impl TextContainsFilter {
pub fn new(needle: impl Into<String>) -> Self {
Self {
needle: needle.into(),
}
}
}
impl Filter for TextContainsFilter {
fn check(&self, ctx: &Context) -> bool {
ctx.text().is_some_and(|t| t.contains(self.needle.as_str()))
}
}
#[derive(Clone)]
pub struct CallbackDataFilter {
data: String,
}
impl CallbackDataFilter {
pub fn new(data: impl Into<String>) -> Self {
Self { data: data.into() }
}
}
impl Filter for CallbackDataFilter {
fn check(&self, ctx: &Context) -> bool {
ctx.callback_query()
.and_then(|q| q.data.as_deref())
.is_some_and(|d| d == self.data)
}
}
#[derive(Clone)]
pub struct CallbackDataPrefixFilter {
prefix: String,
}
impl CallbackDataPrefixFilter {
pub fn new(prefix: impl Into<String>) -> Self {
Self {
prefix: prefix.into(),
}
}
}
impl Filter for CallbackDataPrefixFilter {
fn check(&self, ctx: &Context) -> bool {
ctx.callback_query()
.and_then(|q| q.data.as_deref())
.is_some_and(|d| d.starts_with(self.prefix.as_str()))
}
}
#[derive(Clone, Copy)]
pub struct PrivateChatFilter;
impl Filter for PrivateChatFilter {
fn check(&self, ctx: &Context) -> bool {
ctx.message()
.is_some_and(|m| matches!(m.chat.kind, rustigram_types::chat::ChatType::Private))
}
}
#[derive(Clone, Copy)]
pub struct GroupFilter;
impl Filter for GroupFilter {
fn check(&self, ctx: &Context) -> bool {
ctx.message().is_some_and(|m| {
matches!(
m.chat.kind,
rustigram_types::chat::ChatType::Group
| rustigram_types::chat::ChatType::Supergroup
)
})
}
}
#[cfg(feature = "tma")]
#[derive(Clone, Copy)]
pub struct WebAppDataFilter;
#[cfg(feature = "tma")]
impl Filter for WebAppDataFilter {
fn check(&self, ctx: &Context) -> bool {
ctx.message()
.and_then(|m| m.web_app_data.as_ref())
.is_some()
}
}
#[cfg(feature = "tma")]
#[derive(Clone)]
pub struct WebAppDataMatchingFilter<F> {
predicate: F,
}
#[cfg(feature = "tma")]
impl<F> Filter for WebAppDataMatchingFilter<F>
where
F: Fn(&str) -> bool + Send + Sync + Clone + 'static,
{
fn check(&self, ctx: &Context) -> bool {
ctx.message()
.and_then(|m| m.web_app_data.as_ref())
.is_some_and(|d| (self.predicate)(d.button_text.as_str()))
}
}
pub mod filters {
use super::*;
pub fn message() -> MessageFilter {
MessageFilter
}
pub fn edited_message() -> EditedMessageFilter {
EditedMessageFilter
}
pub fn callback_query() -> CallbackQueryFilter {
CallbackQueryFilter
}
pub fn inline_query() -> InlineQueryFilter {
InlineQueryFilter
}
pub fn command(cmd: impl Into<String>) -> CommandFilter {
CommandFilter::new(cmd)
}
pub fn text(t: impl Into<String>) -> TextFilter {
TextFilter::new(t)
}
pub fn text_contains(needle: impl Into<String>) -> TextContainsFilter {
TextContainsFilter::new(needle)
}
pub fn callback_data(data: impl Into<String>) -> CallbackDataFilter {
CallbackDataFilter::new(data)
}
pub fn callback_data_prefix(prefix: impl Into<String>) -> CallbackDataPrefixFilter {
CallbackDataPrefixFilter::new(prefix)
}
pub fn private() -> PrivateChatFilter {
PrivateChatFilter
}
pub fn group() -> GroupFilter {
GroupFilter
}
pub fn any() -> FnFilter<fn(&Context) -> bool> {
FnFilter(|_| true)
}
#[cfg(feature = "tma")]
pub fn web_app_data() -> WebAppDataFilter {
WebAppDataFilter
}
#[cfg(feature = "tma")]
pub fn web_app_data_matching<F>(predicate: F) -> WebAppDataMatchingFilter<F>
where
F: Fn(&str) -> bool + Send + Sync + Clone + 'static,
{
WebAppDataMatchingFilter { predicate }
}
}