From 733bb2ebbb3ae14f219edc5def2b6dcc9de21ef2 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 13:35:52 +0500 Subject: [PATCH] new advanced TextFilter was added && An example demostrating TextFilter usage --- .../custom_filters/advanced_text_filter.py | 100 +++++++++++++++ telebot/custom_filters.py | 117 ++++++++++++++++-- 2 files changed, 208 insertions(+), 9 deletions(-) create mode 100644 examples/custom_filters/advanced_text_filter.py diff --git a/examples/custom_filters/advanced_text_filter.py b/examples/custom_filters/advanced_text_filter.py new file mode 100644 index 0000000..5f926b0 --- /dev/null +++ b/examples/custom_filters/advanced_text_filter.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +""" +This Example will show you usage of TextFilter +In this example you will see how to use TextFilter +with (message_handler, callback_query_handler, poll_handler) +""" + +from telebot import TeleBot, types +from telebot.custom_filters import TextFilter, AdvancedCustomFilter + +bot = TeleBot("1254795383:AAE7gbj1aas4lEDHB1eVuZZhSGPWcH1B5ds") + + +class TextFilterKey(AdvancedCustomFilter): + key = 'text' + + def check(self, message, config: TextFilter): + return config.check(message) + + +@bot.message_handler(text=TextFilter(equals='hello')) +def hello_handler(message: types.Message): + bot.send_message(message.chat.id, message.text) + + +@bot.message_handler(text=TextFilter(equals='hello', ignore_case=True)) +def hello_handler_ignore_case(message: types.Message): + bot.send_message(message.chat.id, message.text + ' ignore case') + + +@bot.message_handler(text=TextFilter(contains=['good', 'bad'])) +def contains_handler(message: types.Message): + bot.send_message(message.chat.id, message.text) + + +@bot.message_handler(text=TextFilter(contains=['good', 'bad'], ignore_case=True)) +def contains_handler_ignore_case(message: types.Message): + bot.send_message(message.chat.id, message.text + ' ignore case') + + +@bot.message_handler(text=TextFilter(starts_with='st')) # stArk, steve, stONE +def starts_with_handler(message: types.Message): + bot.send_message(message.chat.id, message.text) + + +@bot.message_handler(text=TextFilter(starts_with='st', ignore_case=True)) # STark, sTeve, stONE +def starts_with_handler_ignore_case(message: types.Message): + bot.send_message(message.chat.id, message.text + ' ignore case') + + +@bot.message_handler(text=TextFilter(ends_with='ay')) # wednesday, SUNday, WeekDay +def ends_with_handler(message: types.Message): + bot.send_message(message.chat.id, message.text) + + +@bot.message_handler(text=TextFilter(ends_with='ay', ignore_case=True)) # wednesdAY, sundAy, WeekdaY +def ends_with_handler_ignore_case(message: types.Message): + bot.send_message(message.chat.id, message.text + ' ignore case') + + +@bot.message_handler(text=TextFilter(equals='/callback')) +def send_callback(message: types.Message): + keyboard = types.InlineKeyboardMarkup( + keyboard=[ + [types.InlineKeyboardButton(text='callback data', callback_data='example')], + [types.InlineKeyboardButton(text='ignore case callback data', callback_data='ExAmPLe')] + ] + ) + bot.send_message(message.chat.id, message.text, reply_markup=keyboard) + + +@bot.callback_query_handler(func=None, text=TextFilter(equals='example')) +def callback_query_handler(call: types.CallbackQuery): + bot.answer_callback_query(call.id, call.data, show_alert=True) + + +@bot.callback_query_handler(func=None, text=TextFilter(equals='example', ignore_case=True)) +def callback_query_handler_ignore_case(call: types.CallbackQuery): + bot.answer_callback_query(call.id, call.data + " ignore case", show_alert=True) + + +@bot.message_handler(text=TextFilter(equals='/poll')) +def send_poll(message: types.Message): + bot.send_poll(message.chat.id, question='When do you prefer to work?', options=['Morning', 'Night']) + bot.send_poll(message.chat.id, question='WHEN DO you pRefeR to worK?', options=['Morning', 'Night']) + + +@bot.poll_handler(func=None, text=TextFilter(equals='When do you prefer to work?')) +def poll_question_handler(poll: types.Poll): + print(poll.question) + + +@bot.poll_handler(func=None, text=TextFilter(equals='When do you prefer to work?', ignore_case=True)) +def poll_question_handler_ignore_case(poll: types.Poll): + print(poll.question + ' ignore case') + + +if __name__ == '__main__': + bot.add_custom_filter(TextFilterKey()) + bot.infinity_polling() diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index d95ecd3..efbd218 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -1,4 +1,9 @@ from abc import ABC +from typing import Optional, Union + +from telebot import types + + class SimpleCustomFilter(ABC): """ Simple Custom Filter base class. @@ -29,6 +34,84 @@ class AdvancedCustomFilter(ABC): pass +class TextFilter: + """ + Advanced text filter to check (types.Message, types.CallbackQuery, types.InlineQuery, types.Poll) + + example of usage is in examples/custom_filters/advanced_text_filter.py + """ + + def __init__(self, + equals: Optional[str] = None, + contains: Optional[Union[list, tuple]] = None, + starts_with: Optional[str] = None, + ends_with: Optional[str] = None, + ignore_case: bool = False): + + """ + :param equals: string, True if object's text is equal to passed string + :param contains: list[str] or tuple[str], True if object's text is in list or tuple + :param starts_with: string, True if object's text starts with passed string + :param ends_with: string, True if object's text starts with passed string + :param ignore_case: bool (default False), case insensitive + """ + + to_check = sum((pattern is not None for pattern in (equals, contains, starts_with, ends_with))) + if to_check == 0: + raise ValueError('None of the check modes was specified') + elif to_check > 1: + raise ValueError('Only one check mode can be specified') + elif contains: + for i in contains: + if not isinstance(i, str): + raise ValueError(f"Invalid value '{i}' is in contains") + elif starts_with and not isinstance(starts_with, str): + raise ValueError("starts_with has to be a string") + elif ends_with and not isinstance(ends_with, str): + raise ValueError("ends_with has to be a string") + + self.equals = equals + self.contains = contains + self.starts_with = starts_with + self.ends_with = ends_with + self.ignore_case = ignore_case + + def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery, types.Poll]): + + if isinstance(obj, types.Poll): + text = obj.question + elif isinstance(obj, types.Message): + text = obj.text or obj.caption + elif isinstance(obj, types.CallbackQuery): + text = obj.data + elif isinstance(obj, types.InlineQuery): + text = obj.query + else: + return False + + if self.equals: + if self.ignore_case: + return self.equals.lower() == text.lower() + return self.equals == text + + if self.contains: + if self.ignore_case: + return text.lower() in (i.lower() for i in self.contains) + return text in self.contains + + if self.starts_with: + if self.ignore_case: + return text.lower().startswith(self.starts_with) + return text.startswith(self.starts_with) + + if self.ends_with: + if self.ignore_case: + return text.lower().endswith(self.ends_with) + return text.endswith(self.ends_with) + + return False + + class TextMatchFilter(AdvancedCustomFilter): """ Filter to check Text message. @@ -41,8 +124,11 @@ class TextMatchFilter(AdvancedCustomFilter): key = 'text' def check(self, message, text): - if type(text) is list:return message.text in text - else: return text == message.text + if type(text) is list: + return message.text in text + else: + return text == message.text + class TextContainsFilter(AdvancedCustomFilter): """ @@ -59,6 +145,7 @@ class TextContainsFilter(AdvancedCustomFilter): def check(self, message, text): return text in message.text + class TextStartsFilter(AdvancedCustomFilter): """ Filter to check whether message starts with some text. @@ -69,8 +156,10 @@ class TextStartsFilter(AdvancedCustomFilter): """ key = 'text_startswith' + def check(self, message, text): - return message.text.startswith(text) + return message.text.startswith(text) + class ChatFilter(AdvancedCustomFilter): """ @@ -81,9 +170,11 @@ class ChatFilter(AdvancedCustomFilter): """ key = 'chat_id' + def check(self, message, text): return message.chat.id in text + class ForwardFilter(SimpleCustomFilter): """ Check whether message was forwarded from channel or group. @@ -98,6 +189,7 @@ class ForwardFilter(SimpleCustomFilter): def check(self, message): return message.forward_from_chat is not None + class IsReplyFilter(SimpleCustomFilter): """ Check whether message is a reply. @@ -113,7 +205,6 @@ class IsReplyFilter(SimpleCustomFilter): return message.reply_to_message is not None - class LanguageFilter(AdvancedCustomFilter): """ Check users language_code. @@ -126,8 +217,11 @@ class LanguageFilter(AdvancedCustomFilter): key = 'language_code' def check(self, message, text): - if type(text) is list:return message.from_user.language_code in text - else: return message.from_user.language_code == text + if type(text) is list: + return message.from_user.language_code in text + else: + return message.from_user.language_code == text + class IsAdminFilter(SimpleCustomFilter): """ @@ -145,6 +239,7 @@ class IsAdminFilter(SimpleCustomFilter): def check(self, message): return self._bot.get_chat_member(message.chat.id, message.from_user.id).status in ['creator', 'administrator'] + class StateFilter(AdvancedCustomFilter): """ Filter to check state. @@ -152,8 +247,10 @@ class StateFilter(AdvancedCustomFilter): Example: @bot.message_handler(state=1) """ + def __init__(self, bot): self.bot = bot + key = 'state' def check(self, message, text): @@ -170,14 +267,16 @@ class StateFilter(AdvancedCustomFilter): return True elif group_state in text and type(text) is list: return True - - + + else: - user_state = self.bot.current_states.get_state(message.chat.id,message.from_user.id) + user_state = self.bot.current_states.get_state(message.chat.id, message.from_user.id) if user_state == text: return True elif type(text) is list and user_state in text: return True + + class IsDigitFilter(SimpleCustomFilter): """ Filter to check whether the string is made up of only digits.