From 733bb2ebbb3ae14f219edc5def2b6dcc9de21ef2 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 13:35:52 +0500 Subject: [PATCH 01/14] 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. From 2e5590b566503a05b7fb014ef154c26b7efffb7f Mon Sep 17 00:00:00 2001 From: abdullaev388 <78722918+abdullaev388@users.noreply.github.com> Date: Sat, 12 Feb 2022 14:11:16 +0500 Subject: [PATCH 02/14] token removed :) --- examples/custom_filters/advanced_text_filter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/custom_filters/advanced_text_filter.py b/examples/custom_filters/advanced_text_filter.py index 5f926b0..01387cc 100644 --- a/examples/custom_filters/advanced_text_filter.py +++ b/examples/custom_filters/advanced_text_filter.py @@ -8,7 +8,7 @@ with (message_handler, callback_query_handler, poll_handler) from telebot import TeleBot, types from telebot.custom_filters import TextFilter, AdvancedCustomFilter -bot = TeleBot("1254795383:AAE7gbj1aas4lEDHB1eVuZZhSGPWcH1B5ds") +bot = TeleBot("") class TextFilterKey(AdvancedCustomFilter): From b89ecb3e5af1d4bc339f3c6a950cf96270c43143 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 14:32:59 +0500 Subject: [PATCH 03/14] modified code --- telebot/custom_filters.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index efbd218..467f7dc 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -89,24 +89,28 @@ class TextFilter: else: return False + if self.ignore_case: + text = text.lower() + + if self.equals: + self.equals = self.equals.lower() + elif self.contains: + self.contains = tuple(i.lower() for i in self.contains) + elif self.starts_with: + self.starts_with = self.starts_with.lower() + elif self.ends_with: + self.ends_with = self.ends_with.lower() + 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 From 6fd2a38fe952285abf2a184f0bb4886e2a6cb2c0 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 15:12:30 +0500 Subject: [PATCH 04/14] An asyncio example demostrating TextFilter usage --- .../custom_filters/advanced_text_filter.py | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 examples/asynchronous_telebot/custom_filters/advanced_text_filter.py diff --git a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py new file mode 100644 index 0000000..eac295e --- /dev/null +++ b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py @@ -0,0 +1,103 @@ +# -*- 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) +""" +import asyncio + +from telebot.async_telebot import AsyncTeleBot +from telebot import types +from telebot.custom_filters import TextFilter +from telebot.asyncio_filters import AdvancedCustomFilter + +bot = AsyncTeleBot("1254795383:AAE7gbj1aas4lEDHB1eVuZZhSGPWcH1B5ds") + + +class TextFilterKey(AdvancedCustomFilter): + key = 'text' + + async def check(self, message, config: TextFilter): + return config.check(message) + + +@bot.message_handler(text=TextFilter(equals='hello')) +async def hello_handler(message: types.Message): + await bot.send_message(message.chat.id, message.text) + + +@bot.message_handler(text=TextFilter(equals='hello', ignore_case=True)) +async def hello_handler_ignore_case(message: types.Message): + await bot.send_message(message.chat.id, message.text + ' ignore case') + + +@bot.message_handler(text=TextFilter(contains=['good', 'bad'])) +async def contains_handler(message: types.Message): + await bot.send_message(message.chat.id, message.text) + + +@bot.message_handler(text=TextFilter(contains=['good', 'bad'], ignore_case=True)) +async def contains_handler_ignore_case(message: types.Message): + await bot.send_message(message.chat.id, message.text + ' ignore case') + + +@bot.message_handler(text=TextFilter(starts_with='st')) # stArk, steve, stONE +async def starts_with_handler(message: types.Message): + await bot.send_message(message.chat.id, message.text) + + +@bot.message_handler(text=TextFilter(starts_with='st', ignore_case=True)) # STark, sTeve, stONE +async def starts_with_handler_ignore_case(message: types.Message): + await bot.send_message(message.chat.id, message.text + ' ignore case') + + +@bot.message_handler(text=TextFilter(ends_with='ay')) # wednesday, SUNday, WeekDay +async def ends_with_handler(message: types.Message): + await bot.send_message(message.chat.id, message.text) + + +@bot.message_handler(text=TextFilter(ends_with='ay', ignore_case=True)) # wednesdAY, sundAy, WeekdaY +async def ends_with_handler_ignore_case(message: types.Message): + await bot.send_message(message.chat.id, message.text + ' ignore case') + + +@bot.message_handler(text=TextFilter(equals='/callback')) +async 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')] + ] + ) + await bot.send_message(message.chat.id, message.text, reply_markup=keyboard) + + +@bot.callback_query_handler(func=None, text=TextFilter(equals='example')) +async def callback_query_handler(call: types.CallbackQuery): + await bot.answer_callback_query(call.id, call.data, show_alert=True) + + +@bot.callback_query_handler(func=None, text=TextFilter(equals='example', ignore_case=True)) +async def callback_query_handler_ignore_case(call: types.CallbackQuery): + await bot.answer_callback_query(call.id, call.data + " ignore case", show_alert=True) + + +@bot.message_handler(text=TextFilter(equals='/poll')) +async def send_poll(message: types.Message): + await bot.send_poll(message.chat.id, question='When do you prefer to work?', options=['Morning', 'Night']) + await 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?')) +async 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)) +async def poll_question_handler_ignore_case(poll: types.Poll): + print(poll.question + ' ignore case') + + +if __name__ == '__main__': + bot.add_custom_filter(TextFilterKey()) + asyncio.run(bot.polling()) From a893fbc35895f8470fe992a1e791aeccfa623559 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 15:30:04 +0500 Subject: [PATCH 05/14] async advanced callback_data example was added --- .../advanced_calendar_example/filters.py | 26 ++++++ .../advanced_calendar_example/keyboards.py | 92 +++++++++++++++++++ .../advanced_calendar_example/main.py | 57 ++++++++++++ .../simple_products_example.py} | 0 4 files changed, 175 insertions(+) create mode 100644 examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/filters.py create mode 100644 examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/keyboards.py create mode 100644 examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/main.py rename examples/asynchronous_telebot/{CallbackData_example.py => callback_data_examples/simple_products_example.py} (100%) diff --git a/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/filters.py b/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/filters.py new file mode 100644 index 0000000..7c5c304 --- /dev/null +++ b/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/filters.py @@ -0,0 +1,26 @@ +from telebot import types +from telebot.async_telebot import AsyncTeleBot +from telebot.asyncio_filters import AdvancedCustomFilter +from telebot.callback_data import CallbackData, CallbackDataFilter + +calendar_factory = CallbackData("year", "month", prefix="calendar") +calendar_zoom = CallbackData("year", prefix="calendar_zoom") + + +class CalendarCallbackFilter(AdvancedCustomFilter): + key = 'calendar_config' + + async def check(self, call: types.CallbackQuery, config: CallbackDataFilter): + return config.check(query=call) + + +class CalendarZoomCallbackFilter(AdvancedCustomFilter): + key = 'calendar_zoom_config' + + async def check(self, call: types.CallbackQuery, config: CallbackDataFilter): + return config.check(query=call) + + +def bind_filters(bot: AsyncTeleBot): + bot.add_custom_filter(CalendarCallbackFilter()) + bot.add_custom_filter(CalendarZoomCallbackFilter()) diff --git a/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/keyboards.py b/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/keyboards.py new file mode 100644 index 0000000..1aee88c --- /dev/null +++ b/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/keyboards.py @@ -0,0 +1,92 @@ +import calendar +from datetime import date, timedelta + +from filters import calendar_factory, calendar_zoom +from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton + +EMTPY_FIELD = '1' +WEEK_DAYS = [calendar.day_abbr[i] for i in range(7)] +MONTHS = [(i, calendar.month_name[i]) for i in range(1, 13)] + + +def generate_calendar_days(year: int, month: int): + keyboard = InlineKeyboardMarkup(row_width=7) + today = date.today() + + keyboard.add( + InlineKeyboardButton( + text=date(year=year, month=month, day=1).strftime('%b %Y'), + callback_data=EMTPY_FIELD + ) + ) + keyboard.add(*[ + InlineKeyboardButton( + text=day, + callback_data=EMTPY_FIELD + ) + for day in WEEK_DAYS + ]) + + for week in calendar.Calendar().monthdayscalendar(year=year, month=month): + week_buttons = [] + for day in week: + day_name = ' ' + if day == today.day and today.year == year and today.month == month: + day_name = '🔘' + elif day != 0: + day_name = str(day) + week_buttons.append( + InlineKeyboardButton( + text=day_name, + callback_data=EMTPY_FIELD + ) + ) + keyboard.add(*week_buttons) + + previous_date = date(year=year, month=month, day=1) - timedelta(days=1) + next_date = date(year=year, month=month, day=1) + timedelta(days=31) + + keyboard.add( + InlineKeyboardButton( + text='Previous month', + callback_data=calendar_factory.new(year=previous_date.year, month=previous_date.month) + ), + InlineKeyboardButton( + text='Zoom out', + callback_data=calendar_zoom.new(year=year) + ), + InlineKeyboardButton( + text='Next month', + callback_data=calendar_factory.new(year=next_date.year, month=next_date.month) + ), + ) + + return keyboard + + +def generate_calendar_months(year: int): + keyboard = InlineKeyboardMarkup(row_width=3) + keyboard.add( + InlineKeyboardButton( + text=date(year=year, month=1, day=1).strftime('Year %Y'), + callback_data=EMTPY_FIELD + ) + ) + keyboard.add(*[ + InlineKeyboardButton( + text=month, + callback_data=calendar_factory.new(year=year, month=month_number) + ) + for month_number, month in MONTHS + ]) + keyboard.add( + InlineKeyboardButton( + text='Previous year', + callback_data=calendar_zoom.new(year=year - 1) + ), + InlineKeyboardButton( + text='Next year', + callback_data=calendar_zoom.new(year=year + 1) + ) + ) + return keyboard diff --git a/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/main.py b/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/main.py new file mode 100644 index 0000000..392f9ac --- /dev/null +++ b/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/main.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +""" +This Example will show you an advanced usage of CallbackData. +In this example calendar was implemented +""" +import asyncio +from datetime import date + +from filters import calendar_factory, calendar_zoom, bind_filters +from keyboards import generate_calendar_days, generate_calendar_months, EMTPY_FIELD +from telebot import types +from telebot.async_telebot import AsyncTeleBot + +API_TOKEN = '1254795383:AAE7gbj1aas4lEDHB1eVuZZhSGPWcH1B5ds' +bot = AsyncTeleBot(API_TOKEN) + + +@bot.message_handler(commands='start') +async def start_command_handler(message: types.Message): + await bot.send_message(message.chat.id, + f"Hello {message.from_user.first_name}. This bot is an example of calendar keyboard." + "\nPress /calendar to see it.") + + +@bot.message_handler(commands='calendar') +async def calendar_command_handler(message: types.Message): + now = date.today() + await bot.send_message(message.chat.id, 'Calendar', + reply_markup=generate_calendar_days(year=now.year, month=now.month)) + + +@bot.callback_query_handler(func=None, calendar_config=calendar_factory.filter()) +async def calendar_action_handler(call: types.CallbackQuery): + callback_data: dict = calendar_factory.parse(callback_data=call.data) + year, month = int(callback_data['year']), int(callback_data['month']) + + await bot.edit_message_reply_markup(call.message.chat.id, call.message.id, + reply_markup=generate_calendar_days(year=year, month=month)) + + +@bot.callback_query_handler(func=None, calendar_zoom_config=calendar_zoom.filter()) +async def calendar_zoom_out_handler(call: types.CallbackQuery): + callback_data: dict = calendar_zoom.parse(callback_data=call.data) + year = int(callback_data.get('year')) + + await bot.edit_message_reply_markup(call.message.chat.id, call.message.id, + reply_markup=generate_calendar_months(year=year)) + + +@bot.callback_query_handler(func=lambda call: call.data == EMTPY_FIELD) +async def callback_empty_field_handler(call: types.CallbackQuery): + await bot.answer_callback_query(call.id) + + +if __name__ == '__main__': + bind_filters(bot) + asyncio.run(bot.infinity_polling()) diff --git a/examples/asynchronous_telebot/CallbackData_example.py b/examples/asynchronous_telebot/callback_data_examples/simple_products_example.py similarity index 100% rename from examples/asynchronous_telebot/CallbackData_example.py rename to examples/asynchronous_telebot/callback_data_examples/simple_products_example.py From 3cd86d0e9390480816c00c544b2053a1c2ed8b3a Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 15:31:54 +0500 Subject: [PATCH 06/14] token. again. --- .../callback_data_examples/advanced_calendar_example/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/main.py b/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/main.py index 392f9ac..0041474 100644 --- a/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/main.py +++ b/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/main.py @@ -11,7 +11,7 @@ from keyboards import generate_calendar_days, generate_calendar_months, EMTPY_FI from telebot import types from telebot.async_telebot import AsyncTeleBot -API_TOKEN = '1254795383:AAE7gbj1aas4lEDHB1eVuZZhSGPWcH1B5ds' +API_TOKEN = '' bot = AsyncTeleBot(API_TOKEN) From 5b1483f646b25a40d050ec508c0468686e99ef06 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 17:07:59 +0500 Subject: [PATCH 07/14] removed TextFilterKey in example, instead TextMatchFilter was modified --- .../custom_filters/advanced_text_filter.py | 13 ++---- .../custom_filters/advanced_text_filter.py | 11 +---- telebot/asyncio_filters.py | 40 ++++++++++++++----- telebot/custom_filters.py | 4 +- 4 files changed, 38 insertions(+), 30 deletions(-) diff --git a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py index eac295e..184da31 100644 --- a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py +++ b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py @@ -9,16 +9,9 @@ import asyncio from telebot.async_telebot import AsyncTeleBot from telebot import types from telebot.custom_filters import TextFilter -from telebot.asyncio_filters import AdvancedCustomFilter +from telebot.asyncio_filters import TextMatchFilter -bot = AsyncTeleBot("1254795383:AAE7gbj1aas4lEDHB1eVuZZhSGPWcH1B5ds") - - -class TextFilterKey(AdvancedCustomFilter): - key = 'text' - - async def check(self, message, config: TextFilter): - return config.check(message) +bot = AsyncTeleBot("") @bot.message_handler(text=TextFilter(equals='hello')) @@ -99,5 +92,5 @@ async def poll_question_handler_ignore_case(poll: types.Poll): if __name__ == '__main__': - bot.add_custom_filter(TextFilterKey()) + bot.add_custom_filter(TextMatchFilter()) asyncio.run(bot.polling()) diff --git a/examples/custom_filters/advanced_text_filter.py b/examples/custom_filters/advanced_text_filter.py index 01387cc..9f2eb68 100644 --- a/examples/custom_filters/advanced_text_filter.py +++ b/examples/custom_filters/advanced_text_filter.py @@ -6,18 +6,11 @@ with (message_handler, callback_query_handler, poll_handler) """ from telebot import TeleBot, types -from telebot.custom_filters import TextFilter, AdvancedCustomFilter +from telebot.custom_filters import TextFilter, TextMatchFilter bot = TeleBot("") -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) @@ -96,5 +89,5 @@ def poll_question_handler_ignore_case(poll: types.Poll): if __name__ == '__main__': - bot.add_custom_filter(TextFilterKey()) + bot.add_custom_filter(TextMatchFilter()) bot.infinity_polling() diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 417b110..bacc26f 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -1,5 +1,8 @@ from abc import ABC +from telebot.custom_filters import TextFilter + + class SimpleCustomFilter(ABC): """ Simple Custom Filter base class. @@ -42,8 +45,13 @@ class TextMatchFilter(AdvancedCustomFilter): key = 'text' async def check(self, message, text): - if type(text) is list:return message.text in text - else: return text == message.text + if isinstance(text, TextFilter): + return text.check(message) + elif type(text) is list: + return message.text in text + else: + return text == message.text + class TextContainsFilter(AdvancedCustomFilter): """ @@ -60,6 +68,7 @@ class TextContainsFilter(AdvancedCustomFilter): async def check(self, message, text): return text in message.text + class TextStartsFilter(AdvancedCustomFilter): """ Filter to check whether message starts with some text. @@ -70,8 +79,10 @@ class TextStartsFilter(AdvancedCustomFilter): """ key = 'text_startswith' + async def check(self, message, text): - return message.text.startswith(text) + return message.text.startswith(text) + class ChatFilter(AdvancedCustomFilter): """ @@ -82,9 +93,11 @@ class ChatFilter(AdvancedCustomFilter): """ key = 'chat_id' + async def check(self, message, text): return message.chat.id in text + class ForwardFilter(SimpleCustomFilter): """ Check whether message was forwarded from channel or group. @@ -99,6 +112,7 @@ class ForwardFilter(SimpleCustomFilter): async def check(self, message): return message.forward_from_chat is not None + class IsReplyFilter(SimpleCustomFilter): """ Check whether message is a reply. @@ -114,7 +128,6 @@ class IsReplyFilter(SimpleCustomFilter): return message.reply_to_message is not None - class LanguageFilter(AdvancedCustomFilter): """ Check users language_code. @@ -127,8 +140,11 @@ class LanguageFilter(AdvancedCustomFilter): key = 'language_code' async 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): """ @@ -147,6 +163,7 @@ class IsAdminFilter(SimpleCustomFilter): result = await self._bot.get_chat_member(message.chat.id, message.from_user.id) return result.status in ['creator', 'administrator'] + class StateFilter(AdvancedCustomFilter): """ Filter to check state. @@ -154,8 +171,10 @@ class StateFilter(AdvancedCustomFilter): Example: @bot.message_handler(state=1) """ + def __init__(self, bot): self.bot = bot + key = 'state' async def check(self, message, text): @@ -166,22 +185,23 @@ class StateFilter(AdvancedCustomFilter): text = new_text elif isinstance(text, object): text = text.name - + if message.chat.type == 'group': group_state = await self.bot.current_states.get_state(message.chat.id, message.from_user.id) if group_state == text: return True elif group_state in text and type(text) is list: return True - - + + else: - user_state = await self.bot.current_states.get_state(message.chat.id,message.from_user.id) + user_state = await 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. diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 467f7dc..d7fe3e0 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -128,7 +128,9 @@ class TextMatchFilter(AdvancedCustomFilter): key = 'text' def check(self, message, text): - if type(text) is list: + if isinstance(text, TextFilter): + return text.check(message) + elif type(text) is list: return message.text in text else: return text == message.text From 5f7ccc8c9b5d14b99a7e2669830c3bf242380ae3 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 17:33:29 +0500 Subject: [PATCH 08/14] created async TextFilter --- .../custom_filters/advanced_text_filter.py | 5 +- telebot/asyncio_filters.py | 87 ++++++++++++++++++- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py index 184da31..4c363b5 100644 --- a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py +++ b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py @@ -6,10 +6,9 @@ with (message_handler, callback_query_handler, poll_handler) """ import asyncio -from telebot.async_telebot import AsyncTeleBot from telebot import types -from telebot.custom_filters import TextFilter -from telebot.asyncio_filters import TextMatchFilter +from telebot.async_telebot import AsyncTeleBot +from telebot.asyncio_filters import TextMatchFilter, TextFilter bot = AsyncTeleBot("") diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index bacc26f..9944ff4 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -1,6 +1,7 @@ from abc import ABC +from typing import Optional, Union -from telebot.custom_filters import TextFilter +from telebot import types class SimpleCustomFilter(ABC): @@ -33,6 +34,88 @@ class AdvancedCustomFilter(ABC): pass +class TextFilter: + """ + Advanced async 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 + + async 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.ignore_case: + text = text.lower() + + if self.equals: + self.equals = self.equals.lower() + elif self.contains: + self.contains = tuple(i.lower() for i in self.contains) + elif self.starts_with: + self.starts_with = self.starts_with.lower() + elif self.ends_with: + self.ends_with = self.ends_with.lower() + + if self.equals: + return self.equals == text + + if self.contains: + return text in self.contains + + if self.starts_with: + return text.startswith(self.starts_with) + + if self.ends_with: + return text.endswith(self.ends_with) + + return False + + class TextMatchFilter(AdvancedCustomFilter): """ Filter to check Text message. @@ -46,7 +129,7 @@ class TextMatchFilter(AdvancedCustomFilter): async def check(self, message, text): if isinstance(text, TextFilter): - return text.check(message) + return await text.check(message) elif type(text) is list: return message.text in text else: From 8bbd062d1345dcd83ed09ec1f173bb505813df84 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 20:31:02 +0500 Subject: [PATCH 09/14] text contains filter was fixed --- telebot/custom_filters.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index d7fe3e0..6eea0f0 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -50,7 +50,7 @@ class TextFilter: """ :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 contains: list[str] or tuple[str], True if any string element of iterable is in text :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 @@ -62,9 +62,12 @@ class TextFilter: 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") + if not isinstance(contains, str) and not isinstance(contains, list) and not isinstance(contains, tuple): + raise ValueError("Incorrect contains value") + elif isinstance(contains, str): + contains = [contains] + elif isinstance(contains, list) or isinstance(contains, tuple): + contains = [i for i in contains if isinstance(i, str)] 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): @@ -105,7 +108,7 @@ class TextFilter: return self.equals == text if self.contains: - return text in self.contains + return any([i in text for i in self.contains]) if self.starts_with: return text.startswith(self.starts_with) @@ -149,7 +152,14 @@ class TextContainsFilter(AdvancedCustomFilter): key = 'text_contains' def check(self, message, text): - return text in message.text + if not isinstance(text, str) and not isinstance(text, list) and not isinstance(text, tuple): + raise ValueError("Incorrect text_contains value") + elif isinstance(text, str): + text = [text] + elif isinstance(text, list) or isinstance(text, tuple): + text = [i for i in text if isinstance(i, str)] + + return any([i in message.text for i in text]) class TextStartsFilter(AdvancedCustomFilter): From 6e4f2e19d63fe763985c4183e368b5f9e92f654e Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 20:36:10 +0500 Subject: [PATCH 10/14] async text contains filter was fixed --- telebot/asyncio_filters.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 9944ff4..5fa003d 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -38,7 +38,7 @@ class TextFilter: """ Advanced async text filter to check (types.Message, types.CallbackQuery, types.InlineQuery, types.Poll) - example of usage is in examples/custom_filters/advanced_text_filter.py + example of usage is in examples/asynchronous_telebot/custom_filters/advanced_text_filter.py """ def __init__(self, @@ -50,7 +50,7 @@ class TextFilter: """ :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 contains: list[str] or tuple[str], True if any string element of iterable is in text :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 @@ -62,9 +62,12 @@ class TextFilter: 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") + if not isinstance(contains, str) and not isinstance(contains, list) and not isinstance(contains, tuple): + raise ValueError("Incorrect contains value") + elif isinstance(contains, str): + contains = [contains] + elif isinstance(contains, list) or isinstance(contains, tuple): + contains = [i for i in contains if isinstance(i, str)] 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): @@ -105,7 +108,7 @@ class TextFilter: return self.equals == text if self.contains: - return text in self.contains + return any([i in text for i in self.contains]) if self.starts_with: return text.startswith(self.starts_with) @@ -149,7 +152,14 @@ class TextContainsFilter(AdvancedCustomFilter): key = 'text_contains' async def check(self, message, text): - return text in message.text + if not isinstance(text, str) and not isinstance(text, list) and not isinstance(text, tuple): + raise ValueError("Incorrect text_contains value") + elif isinstance(text, str): + text = [text] + elif isinstance(text, list) or isinstance(text, tuple): + text = [i for i in text if isinstance(i, str)] + + return any([i in message.text for i in text]) class TextStartsFilter(AdvancedCustomFilter): From 6822f18cbbc9d6ac54049cf78828e769cc8a6507 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 21:41:10 +0500 Subject: [PATCH 11/14] multiple check patterns && multiple startwith, endswith fields --- telebot/asyncio_filters.py | 64 ++++++++++++++++++++++---------------- telebot/custom_filters.py | 61 ++++++++++++++++++++---------------- 2 files changed, 72 insertions(+), 53 deletions(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 5fa003d..a8b7180 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -36,16 +36,16 @@ class AdvancedCustomFilter(ABC): class TextFilter: """ - Advanced async text filter to check (types.Message, types.CallbackQuery, types.InlineQuery, types.Poll) + Advanced text filter to check (types.Message, types.CallbackQuery, types.InlineQuery, types.Poll) - example of usage is in examples/asynchronous_telebot/custom_filters/advanced_text_filter.py + 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, + starts_with: Optional[Union[str, list, tuple]] = None, + ends_with: Optional[Union[str, list, tuple]] = None, ignore_case: bool = False): """ @@ -59,26 +59,24 @@ class TextFilter: 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: - if not isinstance(contains, str) and not isinstance(contains, list) and not isinstance(contains, tuple): - raise ValueError("Incorrect contains value") - elif isinstance(contains, str): - contains = [contains] - elif isinstance(contains, list) or isinstance(contains, tuple): - contains = [i for i in contains if isinstance(i, str)] - 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.contains = self._check_iterable(contains) + self.starts_with = self._check_iterable(starts_with) + self.ends_with = self._check_iterable(ends_with) self.ignore_case = ignore_case + def _check_iterable(self, iterable): + if not iterable: + pass + elif not isinstance(iterable, str) and not isinstance(iterable, list) and not isinstance(iterable, tuple): + raise ValueError + elif isinstance(iterable, str): + iterable = [iterable] + elif isinstance(iterable, list) or isinstance(iterable, tuple): + iterable = [i for i in iterable if isinstance(i, str)] + return iterable + async def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery, types.Poll]): if isinstance(obj, types.Poll): @@ -98,23 +96,35 @@ class TextFilter: if self.equals: self.equals = self.equals.lower() elif self.contains: - self.contains = tuple(i.lower() for i in self.contains) + self.contains = tuple(map(str.lower, self.contains)) elif self.starts_with: - self.starts_with = self.starts_with.lower() + self.starts_with = tuple(map(str.lower, self.starts_with)) elif self.ends_with: - self.ends_with = self.ends_with.lower() + self.ends_with = tuple(map(str.lower, self.ends_with)) if self.equals: - return self.equals == text + result = self.equals == text + if result: + return True + elif not result and not any((self.contains, self.starts_with, self.ends_with)): + return False if self.contains: - return any([i in text for i in self.contains]) + result = any([i in text for i in self.contains]) + if result: + return True + elif not result and not any((self.starts_with, self.ends_with)): + return False if self.starts_with: - return text.startswith(self.starts_with) + result = any([text.startswith(i) for i in self.starts_with]) + if result: + return True + elif not result and not self.ends_with: + return False if self.ends_with: - return text.endswith(self.ends_with) + return any([text.endswith(i) for i in self.ends_with]) return False diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 6eea0f0..145cc74 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -44,8 +44,8 @@ class TextFilter: def __init__(self, equals: Optional[str] = None, contains: Optional[Union[list, tuple]] = None, - starts_with: Optional[str] = None, - ends_with: Optional[str] = None, + starts_with: Optional[Union[str, list, tuple]] = None, + ends_with: Optional[Union[str, list, tuple]] = None, ignore_case: bool = False): """ @@ -59,26 +59,24 @@ class TextFilter: 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: - if not isinstance(contains, str) and not isinstance(contains, list) and not isinstance(contains, tuple): - raise ValueError("Incorrect contains value") - elif isinstance(contains, str): - contains = [contains] - elif isinstance(contains, list) or isinstance(contains, tuple): - contains = [i for i in contains if isinstance(i, str)] - 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.contains = self._check_iterable(contains) + self.starts_with = self._check_iterable(starts_with) + self.ends_with = self._check_iterable(ends_with) self.ignore_case = ignore_case + def _check_iterable(self, iterable): + if not iterable: + pass + elif not isinstance(iterable, str) and not isinstance(iterable, list) and not isinstance(iterable, tuple): + raise ValueError + elif isinstance(iterable, str): + iterable = [iterable] + elif isinstance(iterable, list) or isinstance(iterable, tuple): + iterable = [i for i in iterable if isinstance(i, str)] + return iterable + def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery, types.Poll]): if isinstance(obj, types.Poll): @@ -98,27 +96,38 @@ class TextFilter: if self.equals: self.equals = self.equals.lower() elif self.contains: - self.contains = tuple(i.lower() for i in self.contains) + self.contains = tuple(map(str.lower, self.contains)) elif self.starts_with: - self.starts_with = self.starts_with.lower() + self.starts_with = tuple(map(str.lower, self.starts_with)) elif self.ends_with: - self.ends_with = self.ends_with.lower() + self.ends_with = tuple(map(str.lower, self.ends_with)) if self.equals: - return self.equals == text + result = self.equals == text + if result: + return True + elif not result and not any((self.contains, self.starts_with, self.ends_with)): + return False if self.contains: - return any([i in text for i in self.contains]) + result = any([i in text for i in self.contains]) + if result: + return True + elif not result and not any((self.starts_with, self.ends_with)): + return False if self.starts_with: - return text.startswith(self.starts_with) + result = any([text.startswith(i) for i in self.starts_with]) + if result: + return True + elif not result and not self.ends_with: + return False if self.ends_with: - return text.endswith(self.ends_with) + return any([text.endswith(i) for i in self.ends_with]) return False - class TextMatchFilter(AdvancedCustomFilter): """ Filter to check Text message. From 8c3d1e608ccf41bda7bf1f12501061d3b66f8510 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 21:53:40 +0500 Subject: [PATCH 12/14] new TextFilter examples were added --- .../custom_filters/advanced_text_filter.py | 18 ++++++++++++++++++ .../custom_filters/advanced_text_filter.py | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py index 4c363b5..df8b2dd 100644 --- a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py +++ b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py @@ -90,6 +90,24 @@ async def poll_question_handler_ignore_case(poll: types.Poll): print(poll.question + ' ignore case') +# either hi or contains one of (привет, salom) +@bot.message_handler(text=TextFilter(equals="hi", contains=('привет', 'salom'), ignore_case=True)) +async def multiple_patterns_handler(message: types.Message): + await bot.send_message(message.chat.id, message.text) + + +# starts with one of (mi, mea) for ex. minor, milk, meal, meat +@bot.message_handler(text=TextFilter(starts_with=['mi', 'mea'], ignore_case=True)) +async def multiple_starts_with_handler(message: types.Message): + await bot.send_message(message.chat.id, message.text) + + +# ends with one of (es, on) for ex. Jones, Davies, Johnson, Wilson +@bot.message_handler(text=TextFilter(ends_with=['es', 'on'], ignore_case=True)) +async def multiple_ends_with_handler(message: types.Message): + await bot.send_message(message.chat.id, message.text) + + if __name__ == '__main__': bot.add_custom_filter(TextMatchFilter()) asyncio.run(bot.polling()) diff --git a/examples/custom_filters/advanced_text_filter.py b/examples/custom_filters/advanced_text_filter.py index 9f2eb68..41ce3d5 100644 --- a/examples/custom_filters/advanced_text_filter.py +++ b/examples/custom_filters/advanced_text_filter.py @@ -88,6 +88,24 @@ def poll_question_handler_ignore_case(poll: types.Poll): print(poll.question + ' ignore case') +# either hi or contains one of (привет, salom) +@bot.message_handler(text=TextFilter(equals="hi", contains=('привет', 'salom'), ignore_case=True)) +def multiple_patterns_handler(message: types.Message): + bot.send_message(message.chat.id, message.text) + + +# starts with one of (mi, mea) for ex. minor, milk, meal, meat +@bot.message_handler(text=TextFilter(starts_with=['mi', 'mea'], ignore_case=True)) +def multiple_starts_with_handler(message: types.Message): + bot.send_message(message.chat.id, message.text) + + +# ends with one of (es, on) for ex. Jones, Davies, Johnson, Wilson +@bot.message_handler(text=TextFilter(ends_with=['es', 'on'], ignore_case=True)) +def multiple_ends_with_handler(message: types.Message): + bot.send_message(message.chat.id, message.text) + + if __name__ == '__main__': bot.add_custom_filter(TextMatchFilter()) bot.infinity_polling() From b41435f4079026afd546b36ae3bdbf8d5b144982 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Wed, 16 Feb 2022 12:29:27 +0500 Subject: [PATCH 13/14] more descriptive exceptions --- telebot/asyncio_filters.py | 10 +++++----- telebot/custom_filters.py | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index a8b7180..cb0120b 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -61,16 +61,16 @@ class TextFilter: raise ValueError('None of the check modes was specified') self.equals = equals - self.contains = self._check_iterable(contains) - self.starts_with = self._check_iterable(starts_with) - self.ends_with = self._check_iterable(ends_with) + self.contains = self._check_iterable(contains, filter_name='contains') + self.starts_with = self._check_iterable(starts_with, filter_name='starts_with') + self.ends_with = self._check_iterable(ends_with, filter_name='ends_with') self.ignore_case = ignore_case - def _check_iterable(self, iterable): + def _check_iterable(self, iterable, filter_name): if not iterable: pass elif not isinstance(iterable, str) and not isinstance(iterable, list) and not isinstance(iterable, tuple): - raise ValueError + raise ValueError(f"Incorrect value of {filter_name!r}") elif isinstance(iterable, str): iterable = [iterable] elif isinstance(iterable, list) or isinstance(iterable, tuple): diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 145cc74..e6a1531 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -61,16 +61,16 @@ class TextFilter: raise ValueError('None of the check modes was specified') self.equals = equals - self.contains = self._check_iterable(contains) - self.starts_with = self._check_iterable(starts_with) - self.ends_with = self._check_iterable(ends_with) + self.contains = self._check_iterable(contains, filter_name='contains') + self.starts_with = self._check_iterable(starts_with, filter_name='starts_with') + self.ends_with = self._check_iterable(ends_with, filter_name='ends_with') self.ignore_case = ignore_case - def _check_iterable(self, iterable): + def _check_iterable(self, iterable, filter_name: str): if not iterable: pass elif not isinstance(iterable, str) and not isinstance(iterable, list) and not isinstance(iterable, tuple): - raise ValueError + raise ValueError(f"Incorrect value of {filter_name!r}") elif isinstance(iterable, str): iterable = [iterable] elif isinstance(iterable, list) or isinstance(iterable, tuple): From 3a86916e7251bb69b3572adc47348f6cc5520731 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Wed, 16 Feb 2022 12:43:23 +0500 Subject: [PATCH 14/14] example of TextFilter starts_with and ends_with usage simultaneously --- .../custom_filters/advanced_text_filter.py | 16 +++++++++++++++- examples/custom_filters/advanced_text_filter.py | 16 +++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py index df8b2dd..1200a6c 100644 --- a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py +++ b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py @@ -8,7 +8,7 @@ import asyncio from telebot import types from telebot.async_telebot import AsyncTeleBot -from telebot.asyncio_filters import TextMatchFilter, TextFilter +from telebot.asyncio_filters import TextMatchFilter, TextFilter, IsReplyFilter bot = AsyncTeleBot("") @@ -108,6 +108,20 @@ async def multiple_ends_with_handler(message: types.Message): await bot.send_message(message.chat.id, message.text) +# !ban /ban .ban !бан /бан .бан +@bot.message_handler(is_reply=True, + text=TextFilter(starts_with=('!', '/', '.'), ends_with=['ban', 'бан'], ignore_case=True)) +async def ban_command_handler(message: types.Message): + if len(message.text) == 4 and message.chat.type != 'private': + try: + await bot.ban_chat_member(message.chat.id, message.reply_to_message.from_user.id) + await bot.reply_to(message.reply_to_message, 'Banned.') + except Exception as err: + print(err.args) + return + + if __name__ == '__main__': bot.add_custom_filter(TextMatchFilter()) + bot.add_custom_filter(IsReplyFilter()) asyncio.run(bot.polling()) diff --git a/examples/custom_filters/advanced_text_filter.py b/examples/custom_filters/advanced_text_filter.py index 41ce3d5..2b01685 100644 --- a/examples/custom_filters/advanced_text_filter.py +++ b/examples/custom_filters/advanced_text_filter.py @@ -6,7 +6,7 @@ with (message_handler, callback_query_handler, poll_handler) """ from telebot import TeleBot, types -from telebot.custom_filters import TextFilter, TextMatchFilter +from telebot.custom_filters import TextFilter, TextMatchFilter, IsReplyFilter bot = TeleBot("") @@ -106,6 +106,20 @@ def multiple_ends_with_handler(message: types.Message): bot.send_message(message.chat.id, message.text) +# !ban /ban .ban !бан /бан .бан +@bot.message_handler(is_reply=True, + text=TextFilter(starts_with=('!', '/', '.'), ends_with=['ban', 'бан'], ignore_case=True)) +def ban_command_handler(message: types.Message): + if len(message.text) == 4 and message.chat.type != 'private': + try: + bot.ban_chat_member(message.chat.id, message.reply_to_message.from_user.id) + bot.reply_to(message.reply_to_message, 'Banned.') + except Exception as err: + print(err.args) + return + + if __name__ == '__main__': bot.add_custom_filter(TextMatchFilter()) + bot.add_custom_filter(IsReplyFilter()) bot.infinity_polling()