Completed docstrings for all files except types.py

This commit is contained in:
_run 2022-07-24 23:14:09 +05:00
parent 7c12162576
commit b0e06253ff
12 changed files with 3286 additions and 866 deletions

View File

@ -84,10 +84,13 @@ class TeleBot:
Usage:
.. code-block:: python
.. code-block:: python3
:caption: Creating instance of TeleBot
from telebot import TeleBot
bot = TeleBot('token') # get token from @BotFather
# now you can register other handlers/update listeners,
# and use bot methods.
See more examples in examples/ directory:
https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples
@ -243,7 +246,7 @@ class TeleBot:
Enable saving states (by default saving disabled)
.. note::
It is recommended to pass a :class:`~telebot.storage.StateMemoryStorage` instance as state_storage
It is recommended to pass a :class:`~telebot.storage.StatePickleStorage` instance as state_storage
to TeleBot class.
:param filename: Filename of saving file, defaults to "./.state-save/states.pkl"
@ -547,8 +550,6 @@ class TeleBot:
Telegram documentation: https://core.telegram.org/bots/api#getupdates
:param allowed_updates: Array of string. List the types of updates you want your bot to receive.
:type allowed_updates: :obj:`list`, optional
:param offset: Identifier of the first update to be returned. Must be greater by one than the highest among the identifiers of previously received updates.
By default, updates starting with the earliest unconfirmed update are returned. An update is considered confirmed as soon as getUpdates is called with an offset
@ -562,6 +563,9 @@ class TeleBot:
:param timeout: Request connection timeout
:type timeout: :obj:`int`, optional
:param allowed_updates: Array of string. List the types of updates you want your bot to receive.
:type allowed_updates: :obj:`list`, optional
:param long_polling_timeout: Timeout in seconds for long polling.
:type long_polling_timeout: :obj:`int`, optional
@ -604,6 +608,9 @@ class TeleBot:
Processes new updates. Just pass list of subclasses of Update to this method.
:param updates: List of :class:`telebot.types.Update` objects.
:type updates: :obj:`list` of :class:`telebot.types.Update`
:return None:
"""
upd_count = len(updates)
logger.debug('Received {0} new updates'.format(upd_count))
@ -885,11 +892,11 @@ class TeleBot:
none_stop: Optional[bool]=None):
"""
This function creates a new Thread that calls an internal __retrieve_updates function.
This allows the bot to retrieve Updates automagically and notify listeners and message handlers accordingly.
This allows the bot to retrieve Updates automatically and notify listeners and message handlers accordingly.
Warning: Do not call this function more than once!
Always get updates.
Always gets updates.
.. deprecated:: 4.1.1
Use :meth:`infinity_polling` instead.
@ -921,7 +928,7 @@ class TeleBot:
Please note that this parameter doesn't affect updates created before the call to the get_updates,
so unwanted updates may be received for a short period of time.
:type allowed_updates: :obj:`list`] of :obj:`str`
:type allowed_updates: :obj:`list` of :obj:`str`
:param none_stop: Deprecated, use non_stop. Old typo, kept for backward compatibility.
:type none_stop: :obj:`bool`
@ -1104,6 +1111,8 @@ class TeleBot:
def stop_polling(self):
"""
Stops polling.
Does not accept any arguments.
"""
self.__stop_polling.set()
@ -1170,6 +1179,15 @@ class TeleBot:
def download_file(self, file_path: str) -> bytes:
"""
Downloads file.
:param file_path: Path where the file should be downloaded.
:type file_path: str
:return: bytes
:rtype: :obj:`bytes`
"""
return apihelper.download_file(self.token, file_path)
@ -1209,7 +1227,7 @@ class TeleBot:
limit: Optional[int]=None) -> types.UserProfilePhotos:
"""
Use this method to get a list of profile pictures for a user.
Returns a UserProfilePhotos object.
Returns a :class:`telebot.types.UserProfilePhotos` object.
Telegram documentation: https://core.telegram.org/bots/api#getuserprofilephotos
@ -1240,7 +1258,7 @@ class TeleBot:
:param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername)
:type chat_id: :obj:`int` or :obj:`str`
:return: :class:`telebot.types.Chat`
:return: Chat information
:rtype: :class:`telebot.types.Chat`
"""
result = apihelper.get_chat(self.token, chat_id)
@ -1384,8 +1402,8 @@ class TeleBot:
"""
Use this method to send text messages.
Warning: Do not send more than about 4000 characters each message, otherwise you'll risk an HTTP 414 error.
If you must send more than 4000 characters,
Warning: Do not send more than about 4096 characters each message, otherwise you'll risk an HTTP 414 error.
If you must send more than 4096 characters,
use the `split_string` or `smart_split` function in util.py.
Telegram documentation: https://core.telegram.org/bots/api#sendmessage
@ -3552,7 +3570,12 @@ class TeleBot:
message_id: Optional[int]=None,
inline_message_id: Optional[str]=None) -> List[types.GameHighScore]:
"""
Gets top points and game play.
Use this method to get data for high score tables. Will return the score of the specified user and several of
their neighbors in a game. On success, returns an Array of GameHighScore objects.
This method will currently return scores for the target user, plus two of their closest neighbors on each side.
Will also return the top three users if the user and their neighbors are not among them.
Please note that this behavior is subject to change.
Telegram documentation: https://core.telegram.org/bots/api#getgamehighscores
@ -4430,10 +4453,16 @@ class TeleBot:
self.middlewares.append(middleware)
def set_state(self, user_id: int, state: Union[int, str, State], chat_id: int=None) -> None:
def set_state(self, user_id: int, state: Union[int, str, State], chat_id: Optional[int]=None) -> None:
"""
Sets a new state of a user.
.. note::
You should set both user id and chat id in order to set state for a user in a chat.
Otherwise, if you only set user_id, chat_id will equal to user_id, this means that
state will be set for the user in his private chat with a bot.
:param user_id: User's identifier
:type user_id: :obj:`int`
@ -4449,7 +4478,7 @@ class TeleBot:
chat_id = user_id
self.current_states.set_state(chat_id, user_id, state)
def reset_data(self, user_id: int, chat_id: int=None):
def reset_data(self, user_id: int, chat_id: Optional[int]=None):
"""
Reset data for a user in chat.
@ -4465,7 +4494,7 @@ class TeleBot:
chat_id = user_id
self.current_states.reset_data(chat_id, user_id)
def delete_state(self, user_id: int, chat_id: int=None) -> None:
def delete_state(self, user_id: int, chat_id: Optional[int]=None) -> None:
"""
Delete the current state of a user.
@ -4481,12 +4510,24 @@ class TeleBot:
chat_id = user_id
self.current_states.delete_state(chat_id, user_id)
def retrieve_data(self, user_id: int, chat_id: int=None) -> Optional[Any]:
def retrieve_data(self, user_id: int, chat_id: Optional[int]=None) -> Optional[Any]:
"""
Returns context manager with data for a user in chat.
:param user_id: User identifier
:type user_id: int
:param chat_id: Chat's unique identifier, defaults to user_id
:type chat_id: int, optional
:return: Context manager with data for a user in chat
:rtype: Optional[Any]
"""
if chat_id is None:
chat_id = user_id
return self.current_states.get_interactive_data(chat_id, user_id)
def get_state(self, user_id: int, chat_id: int=None) -> Optional[Union[int, str, State]]:
def get_state(self, user_id: int, chat_id: Optional[int]=None) -> Optional[Union[int, str, State]]:
"""
Gets current state of a user.
Not recommended to use this method. But it is ok for debugging.
@ -4504,7 +4545,7 @@ class TeleBot:
chat_id = user_id
return self.current_states.get_state(chat_id, user_id)
def add_data(self, user_id: int, chat_id:int=None, **kwargs):
def add_data(self, user_id: int, chat_id: Optional[int]=None, **kwargs):
"""
Add data to states.
@ -4635,6 +4676,7 @@ class TeleBot:
Example:
.. code-block:: python3
:caption: Usage of middleware_handler
bot = TeleBot('TOKEN')
@ -4728,13 +4770,14 @@ class TeleBot:
def message_handler(self, commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None,
content_types: Optional[List[str]]=None, chat_types: Optional[List[str]]=None, **kwargs):
"""
Message handler decorator.
This decorator can be used to decorate functions that must handle certain types of messages.
Handles New incoming message of any kind - text, photo, sticker, etc.
As a parameter to the decorator function, it passes :class:`telebot.types.Message` object.
All message handlers are tested in the order they were added.
Example:
.. code-block:: python
.. code-block:: python3
:caption: Usage of message_handler
bot = TeleBot('TOKEN')
@ -4768,8 +4811,17 @@ class TeleBot:
:param func: Optional lambda function. The lambda receives the message to test as the first parameter.
It must return True if the command should handle the message.
:type func: :obj:`lambda`
:param content_types: Supported message content types. Must be a list. Defaults to ['text'].
:type content_types: :obj:`list` of :obj:`str`
:param chat_types: list of chat types
:type chat_types: :obj:`list` of :obj:`str`
:param kwargs: Optional keyword arguments(custom filters)
:return: decorated function
"""
if content_types is None:
content_types = ["text"]
@ -4871,7 +4923,8 @@ class TeleBot:
def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs):
"""
Edit message handler decorator
Handles new version of a message that is known to the bot and was edited.
As a parameter to the decorator function, it passes :class:`telebot.types.Message` object.
:param commands: Optional list of strings (commands to handle).
:type commands: :obj:`list` of :obj:`str`
@ -4889,6 +4942,7 @@ class TeleBot:
:type chat_types: :obj:`list` of :obj:`str`
:param kwargs: Optional keyword arguments(custom filters)
:return: None
"""
if content_types is None:
@ -4960,6 +5014,8 @@ class TeleBot:
:param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files)
:type pass_bot: :obj:`bool`
:param kwargs: Optional keyword arguments(custom filters)
:return: None
"""
method_name = "register_edited_message_handler"
@ -4988,7 +5044,8 @@ class TeleBot:
def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs):
"""
Channel post handler decorator.
Handles new incoming channel post of any kind - text, photo, sticker, etc.
As a parameter to the decorator function, it passes :class:`telebot.types.Message` object.
:param commands: Optional list of strings (commands to handle).
:type commands: :obj:`list` of :obj:`str`
@ -5003,6 +5060,7 @@ class TeleBot:
:type content_types: :obj:`list` of :obj:`str`
:param kwargs: Optional keyword arguments(custom filters)
:return: None
"""
if content_types is None:
@ -5069,6 +5127,8 @@ class TeleBot:
:param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files)
:type pass_bot: :obj:`bool`
:param kwargs: Optional keyword arguments(custom filters)
:return: None
"""
method_name = "register_channel_post_handler"
@ -5096,7 +5156,8 @@ class TeleBot:
def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs):
"""
Edit channel post handler decorator
Handles new version of a channel post that is known to the bot and was edited.
As a parameter to the decorator function, it passes :class:`telebot.types.Message` object.
:param commands: Optional list of strings (commands to handle).
:type commands: :obj:`list` of :obj:`str`
@ -5178,6 +5239,8 @@ class TeleBot:
:param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files)
:type pass_bot: :obj:`bool`
:param kwargs: Optional keyword arguments(custom filters)
:return: decorated function
"""
method_name = "register_edited_channel_post_handler"
@ -5205,7 +5268,8 @@ class TeleBot:
def inline_handler(self, func, **kwargs):
"""
Inline call handler decorator
Handles new incoming inline query.
As a parameter to the decorator function, it passes :class:`telebot.types.InlineQuery` object.
:param func: Function executed as a filter
:type func: :obj:`function`
@ -5246,6 +5310,8 @@ class TeleBot:
:param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files)
:type pass_bot: :obj:`bool`
:param kwargs: Optional keyword arguments(custom filters)
:return: decorated function
"""
handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs)
@ -5253,7 +5319,9 @@ class TeleBot:
def chosen_inline_handler(self, func, **kwargs):
"""
Description: The result of an inline query that was chosen by a user and sent to their chat partner.
Handles the result of an inline query that was chosen by a user and sent to their chat partner.
Please see our documentation on the feedback collecting for details on how to enable these updates for your bot.
As a parameter to the decorator function, it passes :class:`telebot.types.ChosenInlineResult` object.
:param func: Function executed as a filter
:type func: :obj:`function`
@ -5294,6 +5362,8 @@ class TeleBot:
:param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files)
:type pass_bot: :obj:`bool`
:param kwargs: Optional keyword arguments(custom filters)
:return: None
"""
handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs)
@ -5301,7 +5371,8 @@ class TeleBot:
def callback_query_handler(self, func, **kwargs):
"""
Callback request handler decorator
Handles new incoming callback query.
As a parameter to the decorator function, it passes :class:`telebot.types.CallbackQuery` object.
:param func: Function executed as a filter
:type func: :obj:`function`
@ -5342,6 +5413,8 @@ class TeleBot:
:param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files)
:type pass_bot: :obj:`bool`
:param kwargs: Optional keyword arguments(custom filters)
:return: None
"""
handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs)
@ -5349,7 +5422,8 @@ class TeleBot:
def shipping_query_handler(self, func, **kwargs):
"""
Shipping request handler
Handles new incoming shipping query. Only for invoices with flexible price.
As a parameter to the decorator function, it passes :class:`telebot.types.ShippingQuery` object.
:param func: Function executed as a filter
:type func: :obj:`function`
@ -5390,6 +5464,8 @@ class TeleBot:
:param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files)
:type pass_bot: :obj:`bool`
:param kwargs: Optional keyword arguments(custom filters)
:return: None
"""
handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs)
@ -5397,7 +5473,8 @@ class TeleBot:
def pre_checkout_query_handler(self, func, **kwargs):
"""
Pre-checkout request handler
New incoming pre-checkout query. Contains full information about checkout.
As a parameter to the decorator function, it passes :class:`telebot.types.PreCheckoutQuery` object.
:param func: Function executed as a filter
:type func: :obj:`function`
@ -5437,6 +5514,8 @@ class TeleBot:
:param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files)
:type pass_bot: :obj:`bool`
:param kwargs: Optional keyword arguments(custom filters)
:return: decorated function
"""
handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs)
@ -5444,7 +5523,8 @@ class TeleBot:
def poll_handler(self, func, **kwargs):
"""
Poll request handler
Handles new state of a poll. Bots receive only updates about stopped polls and polls, which are sent by the bot
As a parameter to the decorator function, it passes :class:`telebot.types.Poll` object.
:param func: Function executed as a filter
:type func: :obj:`function`
@ -5484,6 +5564,8 @@ class TeleBot:
:param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files)
:type pass_bot: :obj:`bool`
:param kwargs: Optional keyword arguments(custom filters)
:return: None
"""
handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs)
@ -5491,7 +5573,9 @@ class TeleBot:
def poll_answer_handler(self, func=None, **kwargs):
"""
Poll_answer request handler
Handles change of user's answer in a non-anonymous poll(when user changes the vote).
Bots receive new votes only in polls that were sent by the bot itself.
As a parameter to the decorator function, it passes :class:`telebot.types.PollAnswer` object.
:param func: Function executed as a filter
:type func: :obj:`function`
@ -5532,6 +5616,8 @@ class TeleBot:
:param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files)
:type pass_bot: :obj:`bool`
:param kwargs: Optional keyword arguments(custom filters)
:return: None
"""
handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs)
@ -5539,8 +5625,9 @@ class TeleBot:
def my_chat_member_handler(self, func=None, **kwargs):
"""
The bot's chat member status was updated in a chat. For private chats,
Handles update in a status of a bot. For private chats,
this update is received only when the bot is blocked or unblocked by the user.
As a parameter to the decorator function, it passes :class:`telebot.types.ChatMemberUpdated` object.
:param func: Function executed as a filter
:type func: :obj:`function`
@ -5581,6 +5668,8 @@ class TeleBot:
:param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files)
:type pass_bot: :obj:`bool`
:param kwargs: Optional keyword arguments(custom filters)
:return: None
"""
handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs)
@ -5588,8 +5677,10 @@ class TeleBot:
def chat_member_handler(self, func=None, **kwargs):
"""
A chat member's status was updated in a chat. The bot must be an administrator
in the chat and must explicitly specify chat_member in the list of allowed_updates to receive these updates.
Handles update in a status of a user in a chat.
The bot must be an administrator in the chat and must explicitly specify chat_member
in the list of allowed_updates to receive these updates.
As a parameter to the decorator function, it passes :class:`telebot.types.ChatMemberUpdated` object.
:param func: Function executed as a filter
:type func: :obj:`function`
@ -5639,8 +5730,9 @@ class TeleBot:
def chat_join_request_handler(self, func=None, **kwargs):
"""
A request to join the chat has been sent. The bot must have the can_invite_users
Handles a request to join the chat has been sent. The bot must have the can_invite_users
administrator right in the chat to receive these updates.
As a parameter to the decorator function, it passes :class:`telebot.types.ChatJoinRequest` object.
:param func: Function executed as a filter
:type func: :obj:`function`
@ -5681,6 +5773,8 @@ class TeleBot:
:param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files)
:type pass_bot: :obj:`bool`
:param kwargs: Optional keyword arguments(custom filters)
:return: None
"""
handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs)
@ -5707,6 +5801,15 @@ class TeleBot:
"""
Create custom filter.
.. code-block:: python3
:caption: Example on checking the text of a message
class TextMatchFilter(AdvancedCustomFilter):
key = 'text'
async def check(self, message, text):
return text == message.text
:param custom_filter: Class with check(message) method.
:param custom_filter: Custom filter class with key.
"""

File diff suppressed because it is too large Load Diff

View File

@ -10,8 +10,19 @@ class SimpleCustomFilter(ABC):
Simple Custom Filter base class.
Create child class with check() method.
Accepts only message, returns bool value, that is compared with given in handler.
Child classes should have .key property.
.. code-block:: python3
:caption: Example on creating a simple custom filter.
class ForwardFilter(SimpleCustomFilter):
# Check whether message was forwarded from channel or group.
key = 'is_forwarded'
def check(self, message):
return message.forward_date is not None
"""
key: str = None
@ -25,13 +36,23 @@ class SimpleCustomFilter(ABC):
class AdvancedCustomFilter(ABC):
"""
Simple Custom Filter base class.
Advanced Custom Filter base class.
Create child class with check() method.
Accepts two parameters, returns bool: True - filter passed, False - filter failed.
message: Message class
text: Filter value given in handler
Child classes should have .key property.
.. code-block:: python3
:caption: Example on creating an advanced custom filter.
class TextStartsFilter(AdvancedCustomFilter):
# Filter to check whether message starts with some text.
key = 'text_startswith'
def check(self, message, text):
return message.text.startswith(text)
"""
key: str = None
@ -48,6 +69,25 @@ class TextFilter:
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
:param equals: string, True if object's text is equal to passed string
:type equals: :obj:`str`
:param contains: list[str] or tuple[str], True if any string element of iterable is in text
:type contains: list[str] or tuple[str]
:param starts_with: string, True if object's text starts with passed string
:type starts_with: :obj:`str`
:param ends_with: string, True if object's text starts with passed string
:type ends_with: :obj:`str`
:param ignore_case: bool (default False), case insensitive
:type ignore_case: :obj:`bool`
:raises ValueError: if incorrect value for a parameter was supplied
:return: None
"""
def __init__(self,
@ -56,13 +96,25 @@ class TextFilter:
starts_with: Optional[Union[str, list, tuple]] = None,
ends_with: Optional[Union[str, list, tuple]] = None,
ignore_case: bool = False):
"""
:param equals: string, True if object's text is equal to passed string
:type equals: :obj:`str`
:param contains: list[str] or tuple[str], True if any string element of iterable is in text
:type contains: list[str] or tuple[str]
:param starts_with: string, True if object's text starts with passed string
:type starts_with: :obj:`str`
:param ends_with: string, True if object's text starts with passed string
:type ends_with: :obj:`str`
:param ignore_case: bool (default False), case insensitive
:type ignore_case: :obj:`bool`
:raises ValueError: if incorrect value for a parameter was supplied
:return: None
"""
to_check = sum((pattern is not None for pattern in (equals, contains, starts_with, ends_with)))
@ -87,7 +139,9 @@ class TextFilter:
return iterable
async def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery, types.Poll]):
"""
:meta private:
"""
if isinstance(obj, types.Poll):
text = obj.question
elif isinstance(obj, types.Message):
@ -135,15 +189,20 @@ class TextFilter:
class TextMatchFilter(AdvancedCustomFilter):
"""
Filter to check Text message.
key: text
Example:
@bot.message_handler(text=['account'])
.. code-block:: python3
:caption: Example on using this filter:
@bot.message_handler(text=['account'])
# your function
"""
key = 'text'
async def check(self, message, text):
"""
:meta private:
"""
if isinstance(text, TextFilter):
return await text.check(message)
elif type(text) is list:
@ -157,14 +216,21 @@ class TextContainsFilter(AdvancedCustomFilter):
Filter to check Text message.
key: text
Example:
# Will respond if any message.text contains word 'account'
@bot.message_handler(text_contains=['account'])
.. code-block:: python3
:caption: Example on using this filter:
# Will respond if any message.text contains word 'account'
@bot.message_handler(text_contains=['account'])
# your function
"""
key = 'text_contains'
async def check(self, message, text):
"""
:meta private:
"""
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):
@ -179,14 +245,20 @@ class TextStartsFilter(AdvancedCustomFilter):
"""
Filter to check whether message starts with some text.
Example:
# Will work if message.text starts with 'Sir'.
@bot.message_handler(text_startswith='Sir')
.. code-block:: python3
:caption: Example on using this filter:
# Will work if message.text starts with 'sir'.
@bot.message_handler(text_startswith='sir')
# your function
"""
key = 'text_startswith'
async def check(self, message, text):
"""
:meta private:
"""
return message.text.startswith(text)
@ -194,13 +266,19 @@ class ChatFilter(AdvancedCustomFilter):
"""
Check whether chat_id corresponds to given chat_id.
Example:
@bot.message_handler(chat_id=[99999])
.. code-block:: python3
:caption: Example on using this filter:
@bot.message_handler(chat_id=[99999])
# your function
"""
key = 'chat_id'
async def check(self, message, text):
"""
:meta private:
"""
if isinstance(message, types.CallbackQuery):
return message.message.chat.id in text
return message.chat.id in text
@ -210,14 +288,19 @@ class ForwardFilter(SimpleCustomFilter):
"""
Check whether message was forwarded from channel or group.
Example:
.. code-block:: python3
:caption: Example on using this filter:
@bot.message_handler(is_forwarded=True)
@bot.message_handler(is_forwarded=True)
# your function
"""
key = 'is_forwarded'
async def check(self, message):
"""
:meta private:
"""
return message.forward_date is not None
@ -225,14 +308,19 @@ class IsReplyFilter(SimpleCustomFilter):
"""
Check whether message is a reply.
Example:
.. code-block:: python3
:caption: Example on using this filter:
@bot.message_handler(is_reply=True)
@bot.message_handler(is_reply=True)
# your function
"""
key = 'is_reply'
async def check(self, message):
"""
:meta private:
"""
if isinstance(message, types.CallbackQuery):
return message.message.reply_to_message is not None
return message.reply_to_message is not None
@ -242,14 +330,19 @@ class LanguageFilter(AdvancedCustomFilter):
"""
Check users language_code.
Example:
.. code-block:: python3
:caption: Example on using this filter:
@bot.message_handler(language_code=['ru'])
@bot.message_handler(language_code=['ru'])
# your function
"""
key = 'language_code'
async def check(self, message, text):
"""
:meta private:
"""
if type(text) is list:
return message.from_user.language_code in text
else:
@ -260,8 +353,11 @@ class IsAdminFilter(SimpleCustomFilter):
"""
Check whether the user is administrator / owner of the chat.
Example:
@bot.message_handler(chat_types=['supergroup'], is_chat_admin=True)
.. code-block:: python3
:caption: Example on using this filter:
@bot.message_handler(chat_types=['supergroup'], is_chat_admin=True)
# your function
"""
key = 'is_chat_admin'
@ -270,6 +366,9 @@ class IsAdminFilter(SimpleCustomFilter):
self._bot = bot
async def check(self, message):
"""
:meta private:
"""
if isinstance(message, types.CallbackQuery):
result = await self._bot.get_chat_member(message.message.chat.id, message.from_user.id)
return result.status ('creator', 'administrator')
@ -281,8 +380,11 @@ class StateFilter(AdvancedCustomFilter):
"""
Filter to check state.
Example:
@bot.message_handler(state=1)
.. code-block:: python3
:caption: Example on using this filter:
@bot.message_handler(state=1)
# your function
"""
def __init__(self, bot):
@ -291,6 +393,9 @@ class StateFilter(AdvancedCustomFilter):
key = 'state'
async def check(self, message, text):
"""
:meta private:
"""
if text == '*': return True
# needs to work with callbackquery
@ -334,10 +439,16 @@ class IsDigitFilter(SimpleCustomFilter):
"""
Filter to check whether the string is made up of only digits.
Example:
@bot.message_handler(is_digit=True)
.. code-block:: python3
:caption: Example on using this filter:
@bot.message_handler(is_digit=True)
# your function
"""
key = 'is_digit'
async def check(self, message):
"""
:meta private:
"""
return message.text.isdigit()

View File

@ -1,3 +1,10 @@
"""
File with all middleware classes, states.
"""
class BaseMiddleware:
"""
Base class for middleware.
@ -9,23 +16,25 @@ class BaseMiddleware:
so on. Same applies to post_process.
.. code-block:: python
:caption: Example of class-based middlewares
class MyMiddleware(BaseMiddleware):
def __init__(self):
self.update_sensitive = True
self.update_types = ['message', 'edited_message']
def pre_process_message(self, message, data):
async def pre_process_message(self, message, data):
# only message update here
pass
def post_process_message(self, message, data, exception):
async def post_process_message(self, message, data, exception):
pass # only message update here for post_process
def pre_process_edited_message(self, message, data):
async def pre_process_edited_message(self, message, data):
# only edited_message update here
pass
def post_process_edited_message(self, message, data, exception):
async def post_process_edited_message(self, message, data, exception):
pass # only edited_message update here for post_process
"""
@ -42,6 +51,14 @@ class BaseMiddleware:
class State:
"""
Class representing a state.
.. code-block:: python3
class MyStates(StatesGroup):
my_state = State() # returns my_state:State string.
"""
def __init__(self) -> None:
self.name = None
@ -50,6 +67,14 @@ class State:
class StatesGroup:
"""
Class representing common states.
.. code-block:: python3
class MyStates(StatesGroup):
my_state = State() # returns my_state:State string.
"""
def __init_subclass__(cls) -> None:
for name, value in cls.__dict__.items():

View File

@ -1,3 +1,7 @@
"""
Callback data factory's file.
"""
"""
Copyright (c) 2017-2018 Alex Root Junior
@ -29,17 +33,23 @@ import typing
class CallbackDataFilter:
"""
Filter for CallbackData.
"""
def __init__(self, factory, config: typing.Dict[str, str]):
self.config = config
self.factory = factory
def check(self, query):
def check(self, query) -> bool:
"""
Checks if query.data appropriates to specified config
:param query: telebot.types.CallbackQuery
:return: bool
:type query: telebot.types.CallbackQuery
:return: True if query.data appropriates to specified config
:rtype: bool
"""
try:
@ -135,7 +145,7 @@ class CallbackData:
"""
Generate filter
:param config: specified named parameters will be checked with CallbackQury.data
:param config: specified named parameters will be checked with CallbackQuery.data
:return: CallbackDataFilter class
"""

View File

@ -14,6 +14,17 @@ class SimpleCustomFilter(ABC):
Accepts only message, returns bool value, that is compared with given in handler.
Child classes should have .key property.
.. code-block:: python3
:caption: Example on creating a simple custom filter.
class ForwardFilter(SimpleCustomFilter):
# Check whether message was forwarded from channel or group.
key = 'is_forwarded'
def check(self, message):
return message.forward_date is not None
"""
key: str = None
@ -27,13 +38,23 @@ class SimpleCustomFilter(ABC):
class AdvancedCustomFilter(ABC):
"""
Simple Custom Filter base class.
Advanced Custom Filter base class.
Create child class with check() method.
Accepts two parameters, returns bool: True - filter passed, False - filter failed.
message: Message class
text: Filter value given in handler
Child classes should have .key property.
.. code-block:: python3
:caption: Example on creating an advanced custom filter.
class TextStartsFilter(AdvancedCustomFilter):
# Filter to check whether message starts with some text.
key = 'text_startswith'
def check(self, message, text):
return message.text.startswith(text)
"""
key: str = None
@ -50,6 +71,25 @@ 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
:param equals: string, True if object's text is equal to passed string
:type equals: :obj:`str`
:param contains: list[str] or tuple[str], True if any string element of iterable is in text
:type contains: list[str] or tuple[str]
:param starts_with: string, True if object's text starts with passed string
:type starts_with: :obj:`str`
:param ends_with: string, True if object's text starts with passed string
:type ends_with: :obj:`str`
:param ignore_case: bool (default False), case insensitive
:type ignore_case: :obj:`bool`
:raises ValueError: if incorrect value for a parameter was supplied
:return: None
"""
def __init__(self,
@ -58,15 +98,27 @@ class TextFilter:
starts_with: Optional[Union[str, list, tuple]] = None,
ends_with: Optional[Union[str, list, tuple]] = 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 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
"""
:type equals: :obj:`str`
:param contains: list[str] or tuple[str], True if any string element of iterable is in text
:type contains: list[str] or tuple[str]
:param starts_with: string, True if object's text starts with passed string
:type starts_with: :obj:`str`
:param ends_with: string, True if object's text starts with passed string
:type ends_with: :obj:`str`
:param ignore_case: bool (default False), case insensitive
:type ignore_case: :obj:`bool`
:raises ValueError: if incorrect value for a parameter was supplied
:return: None
"""
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')
@ -89,6 +141,9 @@ class TextFilter:
return iterable
def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery, types.Poll]):
"""
:meta private:
"""
if isinstance(obj, types.Poll):
text = obj.question
@ -142,15 +197,20 @@ class TextFilter:
class TextMatchFilter(AdvancedCustomFilter):
"""
Filter to check Text message.
key: text
Example:
@bot.message_handler(text=['account'])
.. code-block:: python3
:caption: Example on using this filter:
@bot.message_handler(text=['account'])
# your function
"""
key = 'text'
def check(self, message, text):
"""
:meta private:
"""
if isinstance(text, TextFilter):
return text.check(message)
elif type(text) is list:
@ -164,14 +224,21 @@ class TextContainsFilter(AdvancedCustomFilter):
Filter to check Text message.
key: text
Example:
# Will respond if any message.text contains word 'account'
@bot.message_handler(text_contains=['account'])
.. code-block:: python3
:caption: Example on using this filter:
# Will respond if any message.text contains word 'account'
@bot.message_handler(text_contains=['account'])
# your function
"""
key = 'text_contains'
def check(self, message, text):
"""
:meta private:
"""
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):
@ -186,14 +253,20 @@ class TextStartsFilter(AdvancedCustomFilter):
"""
Filter to check whether message starts with some text.
Example:
# Will work if message.text starts with 'Sir'.
@bot.message_handler(text_startswith='Sir')
.. code-block:: python3
:caption: Example on using this filter:
# Will work if message.text starts with 'sir'.
@bot.message_handler(text_startswith='sir')
# your function
"""
key = 'text_startswith'
def check(self, message, text):
"""
:meta private:
"""
return message.text.startswith(text)
@ -201,13 +274,19 @@ class ChatFilter(AdvancedCustomFilter):
"""
Check whether chat_id corresponds to given chat_id.
Example:
@bot.message_handler(chat_id=[99999])
.. code-block:: python3
:caption: Example on using this filter:
@bot.message_handler(chat_id=[99999])
# your function
"""
key = 'chat_id'
def check(self, message, text):
"""
:meta private:
"""
if isinstance(message, types.CallbackQuery):
return message.message.chat.id in text
return message.chat.id in text
@ -217,14 +296,19 @@ class ForwardFilter(SimpleCustomFilter):
"""
Check whether message was forwarded from channel or group.
Example:
.. code-block:: python3
:caption: Example on using this filter:
@bot.message_handler(is_forwarded=True)
@bot.message_handler(is_forwarded=True)
# your function
"""
key = 'is_forwarded'
def check(self, message):
"""
:meta private:
"""
return message.forward_date is not None
@ -232,14 +316,19 @@ class IsReplyFilter(SimpleCustomFilter):
"""
Check whether message is a reply.
Example:
.. code-block:: python3
:caption: Example on using this filter:
@bot.message_handler(is_reply=True)
@bot.message_handler(is_reply=True)
# your function
"""
key = 'is_reply'
def check(self, message):
"""
:meta private:
"""
if isinstance(message, types.CallbackQuery):
return message.message.reply_to_message is not None
return message.reply_to_message is not None
@ -249,14 +338,19 @@ class LanguageFilter(AdvancedCustomFilter):
"""
Check users language_code.
Example:
.. code-block:: python3
:caption: Example on using this filter:
@bot.message_handler(language_code=['ru'])
@bot.message_handler(language_code=['ru'])
# your function
"""
key = 'language_code'
def check(self, message, text):
"""
:meta private:
"""
if type(text) is list:
return message.from_user.language_code in text
else:
@ -267,8 +361,11 @@ class IsAdminFilter(SimpleCustomFilter):
"""
Check whether the user is administrator / owner of the chat.
Example:
@bot.message_handler(chat_types=['supergroup'], is_chat_admin=True)
.. code-block:: python3
:caption: Example on using this filter:
@bot.message_handler(chat_types=['supergroup'], is_chat_admin=True)
# your function
"""
key = 'is_chat_admin'
@ -277,6 +374,9 @@ class IsAdminFilter(SimpleCustomFilter):
self._bot = bot
def check(self, message):
"""
:meta private:
"""
if isinstance(message, types.CallbackQuery):
return self._bot.get_chat_member(message.message.chat.id, message.from_user.id).status in ['creator', 'administrator']
return self._bot.get_chat_member(message.chat.id, message.from_user.id).status in ['creator', 'administrator']
@ -286,8 +386,11 @@ class StateFilter(AdvancedCustomFilter):
"""
Filter to check state.
Example:
@bot.message_handler(state=1)
.. code-block:: python3
:caption: Example on using this filter:
@bot.message_handler(state=1)
# your function
"""
def __init__(self, bot):
@ -296,6 +399,9 @@ class StateFilter(AdvancedCustomFilter):
key = 'state'
def check(self, message, text):
"""
:meta private:
"""
if text == '*': return True
# needs to work with callbackquery
@ -341,10 +447,16 @@ class IsDigitFilter(SimpleCustomFilter):
"""
Filter to check whether the string is made up of only digits.
Example:
@bot.message_handler(is_digit=True)
.. code-block:: python3
:caption: Example on using this filter:
@bot.message_handler(is_digit=True)
# your function
"""
key = 'is_digit'
def check(self, message):
"""
:meta private:
"""
return message.text.isdigit()

View File

@ -34,14 +34,34 @@ class AsyncWebhookListener:
"""
Aynchronous implementation of webhook listener
for asynchronous version of telebot.
Not supposed to be used manually by user.
Use AsyncTeleBot.run_webhooks() instead.
:param bot: AsyncTeleBot instance.
:type bot: telebot.async_telebot.AsyncTeleBot
:param bot: TeleBot instance
:param secret_token: Telegram secret token
:type secret_token: str
:param host: Webhook host
:type host: str
:param port: Webhook port
:type port: int
:param ssl_context: SSL context
:type ssl_context: tuple
:param url_path: Webhook url path
:type url_path: str
:param debug: Debug mode
:type debug: bool
:raises ImportError: If FastAPI or uvicorn is not installed.
:raises ImportError: If Starlette version is too old.
:return: None
"""
self._check_dependencies()
@ -73,6 +93,8 @@ class AsyncWebhookListener:
async def process_update(self, request: Request, update: dict):
"""
Processes updates.
:meta private:
"""
# header containsX-Telegram-Bot-Api-Secret-Token
if request.headers.get('X-Telegram-Bot-Api-Secret-Token') != self._secret_token:
@ -88,7 +110,10 @@ class AsyncWebhookListener:
async def run_app(self):
"""
Run app with the given parameters.
Run app with the given parameters to init.
Not supposed to be used manually by user.
:return: None
"""
config = Config(app=self.app,

View File

@ -1,8 +1,7 @@
"""
This file is used by TeleBot.run_webhooks() &
AsyncTeleBot.run_webhooks() functions.
This file is used by TeleBot.run_webhooks() function.
Flask/fastapi is required to run this script.
Fastapi is required to run this script.
"""
# modules required for running this script
@ -34,16 +33,36 @@ class SyncWebhookListener:
debug: Optional[bool]=False
) -> None:
"""
Synchronous implementation of webhook listener
for synchronous version of telebot.
Aynchronous implementation of webhook listener
for asynchronous version of telebot.
Not supposed to be used manually by user.
Use AsyncTeleBot.run_webhooks() instead.
:param bot: AsyncTeleBot instance.
:type bot: telebot.async_telebot.AsyncTeleBot
:param bot: TeleBot instance
:param secret_token: Telegram secret token
:type secret_token: str
:param host: Webhook host
:type host: str
:param port: Webhook port
:type port: int
:param ssl_context: SSL context
:type ssl_context: tuple
:param url_path: Webhook url path
:type url_path: str
:param debug: Debug mode
:type debug: bool
:raises ImportError: If FastAPI or uvicorn is not installed.
:raises ImportError: If Starlette version is too old.
:return: None
"""
self._check_dependencies()
@ -75,6 +94,8 @@ class SyncWebhookListener:
def process_update(self, request: Request, update: dict):
"""
Processes updates.
:meta private:
"""
# header containsX-Telegram-Bot-Api-Secret-Token
if request.headers.get('X-Telegram-Bot-Api-Secret-Token') != self._secret_token:
@ -89,7 +110,10 @@ class SyncWebhookListener:
def run_app(self):
"""
Run app with the given parameters.
Run app with the given parameters to init.
Not supposed to be used manually by user.
:return: None
"""
uvicorn.run(app=self.app,

View File

@ -5,14 +5,17 @@ Markdown & HTML formatting functions.
"""
import html
import re
from typing import Optional
def format_text(*args, separator="\n"):
"""
Formats a list of strings into a single string.
.. code:: python
.. code:: python3
format_text( # just an example
mbold('Hello'),
@ -20,7 +23,13 @@ def format_text(*args, separator="\n"):
)
:param args: Strings to format.
:type args: :obj:`str`
:param separator: The separator to use between each string.
:type separator: :obj:`str`
:return: The formatted string.
:rtype: :obj:`str`
"""
return separator.join(args)
@ -31,6 +40,10 @@ def escape_html(content: str) -> str:
Escapes HTML characters in a string of HTML.
:param content: The string of HTML to escape.
:type content: :obj:`str`
:return: The escaped string.
:rtype: :obj:`str`
"""
return html.escape(content)
@ -39,9 +52,13 @@ def escape_markdown(content: str) -> str:
"""
Escapes Markdown characters in a string of Markdown.
Credits: simonsmh
Credits to: simonsmh
:param content: The string of Markdown to escape.
:type content: :obj:`str`
:return: The escaped string.
:rtype: :obj:`str`
"""
parse = re.sub(r"([_*\[\]()~`>\#\+\-=|\.!])", r"\\\1", content)
@ -49,154 +66,249 @@ def escape_markdown(content: str) -> str:
return reparse
def mbold(content: str, escape: bool=True) -> str:
def mbold(content: str, escape: Optional[bool]=True) -> str:
"""
Returns a Markdown-formatted bold string.
:param content: The string to bold.
:param escape: True if you need to escape special characters.
:type content: :obj:`str`
:param escape: True if you need to escape special characters. Defaults to True.
:type escape: :obj:`bool`
:return: The formatted string.
:rtype: :obj:`str`
"""
return '*{}*'.format(escape_markdown(content) if escape else content)
def hbold(content: str, escape: bool=True) -> str:
def hbold(content: str, escape: Optional[bool]=True) -> str:
"""
Returns an HTML-formatted bold string.
:param content: The string to bold.
:param escape: True if you need to escape special characters.
:type content: :obj:`str`
:param escape: True if you need to escape special characters. Defaults to True.
:type escape: :obj:`bool`
:return: The formatted string.
:rtype: :obj:`str`
"""
return '<b>{}</b>'.format(escape_html(content) if escape else content)
def mitalic(content: str, escape: bool=True) -> str:
def mitalic(content: str, escape: Optional[bool]=True) -> str:
"""
Returns a Markdown-formatted italic string.
:param content: The string to italicize.
:param escape: True if you need to escape special characters.
:type content: :obj:`str`
:param escape: True if you need to escape special characters. Defaults to True.
:type escape: :obj:`bool`
:return: The formatted string.
:rtype: :obj:`str`
"""
return '_{}_\r'.format(escape_markdown(content) if escape else content)
def hitalic(content: str, escape: bool=True) -> str:
def hitalic(content: str, escape: Optional[bool]=True) -> str:
"""
Returns an HTML-formatted italic string.
:param content: The string to italicize.
:param escape: True if you need to escape special characters.
:type content: :obj:`str`
:param escape: True if you need to escape special characters. Defaults to True.
:type escape: :obj:`bool`
:return: The formatted string.
:rtype: :obj:`str`
"""
return '<i>{}</i>'.format(escape_html(content) if escape else content)
def munderline(content: str, escape: bool=True) -> str:
def munderline(content: str, escape: Optional[bool]=True) -> str:
"""
Returns a Markdown-formatted underline string.
:param content: The string to underline.
:param escape: True if you need to escape special characters.
:type content: :obj:`str`
:param escape: True if you need to escape special characters. Defaults to True.
:type escape: :obj:`bool`
:return: The formatted string.
:rtype: :obj:`str`
"""
return '__{}__'.format(escape_markdown(content) if escape else content)
def hunderline(content: str, escape: bool=True) -> str:
def hunderline(content: str, escape: Optional[bool]=True) -> str:
"""
Returns an HTML-formatted underline string.
:param content: The string to underline.
:param escape: True if you need to escape special characters.
:type content: :obj:`str`
:param escape: True if you need to escape special characters. Defaults to True.
:type escape: :obj:`bool`
:return: The formatted string.
:rtype: :obj:`str`
"""
return '<u>{}</u>'.format(escape_html(content) if escape else content)
def mstrikethrough(content: str, escape: bool=True) -> str:
def mstrikethrough(content: str, escape: Optional[bool]=True) -> str:
"""
Returns a Markdown-formatted strikethrough string.
:param content: The string to strikethrough.
:param escape: True if you need to escape special characters.
:type content: :obj:`str`
:param escape: True if you need to escape special characters. Defaults to True.
:type escape: :obj:`bool`
:return: The formatted string.
:rtype: :obj:`str`
"""
return '~{}~'.format(escape_markdown(content) if escape else content)
def hstrikethrough(content: str, escape: bool=True) -> str:
def hstrikethrough(content: str, escape: Optional[bool]=True) -> str:
"""
Returns an HTML-formatted strikethrough string.
:param content: The string to strikethrough.
:param escape: True if you need to escape special characters.
:type content: :obj:`str`
:param escape: True if you need to escape special characters. Defaults to True.
:type escape: :obj:`bool`
:return: The formatted string.
:rtype: :obj:`str`
"""
return '<s>{}</s>'.format(escape_html(content) if escape else content)
def mspoiler(content: str, escape: bool=True) -> str:
def mspoiler(content: str, escape: Optional[bool]=True) -> str:
"""
Returns a Markdown-formatted spoiler string.
:param content: The string to spoiler.
:param escape: True if you need to escape special characters.
:type content: :obj:`str`
:param escape: True if you need to escape special characters. Defaults to True.
:type escape: :obj:`bool`
:return: The formatted string.
:rtype: :obj:`str`
"""
return '||{}||'.format(escape_markdown(content) if escape else content)
def hspoiler(content: str, escape: bool=True) -> str:
def hspoiler(content: str, escape: Optional[bool]=True) -> str:
"""
Returns an HTML-formatted spoiler string.
:param content: The string to spoiler.
:param escape: True if you need to escape special characters.
:type content: :obj:`str`
:param escape: True if you need to escape special characters. Defaults to True.
:type escape: :obj:`bool`
:return: The formatted string.
:rtype: :obj:`str`
"""
return '<tg-spoiler>{}</tg-spoiler>'.format(escape_html(content) if escape else content)
def mlink(content: str, url: str, escape: bool=True) -> str:
def mlink(content: str, url: str, escape: Optional[bool]=True) -> str:
"""
Returns a Markdown-formatted link string.
:param content: The string to link.
:type content: :obj:`str`
:param url: The URL to link to.
:param escape: True if you need to escape special characters.
:type url: str
:param escape: True if you need to escape special characters. Defaults to True.
:type escape: :obj:`bool`
:return: The formatted string.
:rtype: :obj:`str`
"""
return '[{}]({})'.format(escape_markdown(content), escape_markdown(url) if escape else content)
def hlink(content: str, url: str, escape: bool=True) -> str:
def hlink(content: str, url: str, escape: Optional[bool]=True) -> str:
"""
Returns an HTML-formatted link string.
:param content: The string to link.
:type content: :obj:`str`
:param url: The URL to link to.
:param escape: True if you need to escape special characters.
:type url: :obj:`str`
:param escape: True if you need to escape special characters. Defaults to True.
:type escape: :obj:`bool`
:return: The formatted string.
:rtype: :obj:`str`
"""
return '<a href="{}">{}</a>'.format(escape_html(url), escape_html(content) if escape else content)
def mcode(content: str, language: str="", escape: bool=True) -> str:
def mcode(content: str, language: str="", escape: Optional[bool]=True) -> str:
"""
Returns a Markdown-formatted code string.
:param content: The string to code.
:param escape: True if you need to escape special characters.
:type content: :obj:`str`
:param escape: True if you need to escape special characters. Defaults to True.
:type escape: :obj:`bool`
:return: The formatted string.
:rtype: :obj:`str`
"""
return '```{}\n{}```'.format(language, escape_markdown(content) if escape else content)
def hcode(content: str, escape: bool=True) -> str:
def hcode(content: str, escape: Optional[bool]=True) -> str:
"""
Returns an HTML-formatted code string.
:param content: The string to code.
:param escape: True if you need to escape special characters.
:type content: :obj:`str`
:param escape: True if you need to escape special characters. Defaults to True.
:type escape: :obj:`bool`
:return: The formatted string.
:rtype: :obj:`str`
"""
return '<code>{}</code>'.format(escape_html(content) if escape else content)
def hpre(content: str, escape: bool=True, language: str="") -> str:
def hpre(content: str, escape: Optional[bool]=True, language: str="") -> str:
"""
Returns an HTML-formatted preformatted string.
:param content: The string to preformatted.
:param escape: True if you need to escape special characters.
:type content: :obj:`str`
:param escape: True if you need to escape special characters. Defaults to True.
:type escape: :obj:`bool`
:return: The formatted string.
:rtype: :obj:`str`
"""
return '<pre><code class="{}">{}</code></pre>'.format(language, escape_html(content) if escape else content)
@ -205,7 +317,10 @@ def hide_link(url: str) -> str:
"""
Hide url of an image.
:param url:
:return: str
:param url: The url of the image.
:type url: :obj:`str`
:return: The hidden url.
:rtype: :obj:`str`
"""
return f'<a href="{url}">&#8288;</a>'

View File

@ -12,7 +12,9 @@ except:
class HandlerBackend(object):
"""
Class for saving (next step|reply) handlers
Class for saving (next step|reply) handlers.
:meta private:
"""
def __init__(self, handlers=None):
if handlers is None:
@ -30,6 +32,9 @@ class HandlerBackend(object):
class MemoryHandlerBackend(HandlerBackend):
"""
:meta private:
"""
def register_handler(self, handler_group_id, handler):
if handler_group_id in self.handlers:
self.handlers[handler_group_id].append(handler)
@ -47,6 +52,9 @@ class MemoryHandlerBackend(HandlerBackend):
class FileHandlerBackend(HandlerBackend):
"""
:meta private:
"""
def __init__(self, handlers=None, filename='./.handler-saves/handlers.save', delay=120):
super(FileHandlerBackend, self).__init__(handlers)
self.filename = filename
@ -119,6 +127,9 @@ class FileHandlerBackend(HandlerBackend):
class RedisHandlerBackend(HandlerBackend):
"""
:meta private:
"""
def __init__(self, handlers=None, host='localhost', port=6379, db=0, prefix='telebot', password=None):
super(RedisHandlerBackend, self).__init__(handlers)
if not redis_installed:
@ -150,6 +161,14 @@ class RedisHandlerBackend(HandlerBackend):
class State:
"""
Class representing a state.
.. code-block:: python3
class MyStates(StatesGroup):
my_state = State() # returns my_state:State string.
"""
def __init__(self) -> None:
self.name = None
def __str__(self) -> str:
@ -158,6 +177,14 @@ class State:
class StatesGroup:
"""
Class representing common states.
.. code-block:: python3
class MyStates(StatesGroup):
my_state = State() # returns my_state:State string.
"""
def __init_subclass__(cls) -> None:
for name, value in cls.__dict__.items():
if not name.startswith('__') and not callable(value) and isinstance(value, State):
@ -179,8 +206,13 @@ class BaseMiddleware:
message update, then you will have to create pre_process_message function, and
so on. Same applies to post_process.
.. code-block:: python
.. note::
If you want to use middleware, you have to set use_class_middlewares=True in your
TeleBot instance.
.. code-block:: python3
:caption: Example of class-based middlewares.
class MyMiddleware(BaseMiddleware):
def __init__(self):
self.update_sensitive = True

View File

@ -20,6 +20,7 @@ class JsonSerializable(object):
"""
Subclasses of this class are guaranteed to be able to be converted to JSON format.
All subclasses of this class must override to_json.
"""
def to_json(self):
@ -36,6 +37,7 @@ class Dictionaryable(object):
"""
Subclasses of this class are guaranteed to be able to be converted to dictionary.
All subclasses of this class must override to_dict.
"""
def to_dict(self):
@ -90,6 +92,65 @@ class JsonDeserializable(object):
class Update(JsonDeserializable):
"""
This object represents an incoming update.
https://core.telegram.org/bots/api#update
:attribute update_id: The update's unique identifier. Update identifiers start from a certain positive number and increase sequentially.
This ID becomes especially handy if you're using webhooks, since it allows you to ignore repeated updates or to restore the correct
update sequence, should they get out of order. If there are no new updates for at least a week, then identifier of the next update will
be chosen randomly instead of sequentially.
:type update_id: int
:attribute message: New incoming message of any kind text, photo, sticker, etc.
:type message: :class:`telebot.types.Message`
:attribute edited_message: New version of a message that is known to the bot and was edited.
:type edited_message: :class:`telebot.types.Message`
:attribute channel_post: New incoming channel post of any kind text, photo, sticker, etc.
:type channel_post: :class:`telebot.types.Message`
:attribute edited_channel_post: New version of a channel post that is known to the bot and was edited.
:type edited_channel_post: :class:`telebot.types.Message`
:attribute inline_query: New incoming query from a user, which is answered by a bot and can be further processed.
:type inline_query: :class:`telebot.types.InlineQuery`
:attribute chosen_inline_result: New incoming result of an inline query that was chosen by a user and sent to their chat partner.
:type chosen_inline_result: :class:`telebot.types.ChosenInlineResult`
:attribute callback_query: New incoming callback query from a user.
:type callback_query: :class:`telebot.types.CallbackQuery`
:attribute shipping_query: New incoming shipping query. Only for invoices with flexible price
:type shipping_query: :class:`telebot.types.ShippingQuery`
:attribute pre_checkout_query: New incoming pre-checkout query. Contains full information about checkout
:type pre_checkout_query: :class:`telebot.types.PreCheckoutQuery`
:attribute poll: New poll state. Bots receive only updates about stopped polls and polls, which are sent by the bot
:type poll: :class:`telebot.types.Poll`
:attribute poll_answer: A user changed their answer in a non-anonymous poll. Bots receive new votes only in polls that were sent by the bot itself.
:type poll_answer: :class:`telebot.types.PollAnswer`
:attribute my_chat_member: The bot's chat member status was updated in a chat. For private chats,
this update is received only when the bot is blocked or unblocked by the user.
:type my_chat_member: :class:`telebot.types.ChatMember`
:attribute chat_member: A chat member's status was updated in a chat. The bot must be an administrator in the chat and must
explicitly specify chat_member in the list of allowed_updates to receive these updates.
:type chat_member: :class:`telebot.types.ChatMember`
:attribute chat_join_request: A request to join the chat has been sent. The bot must have the can_invite_users
administrator right in the chat to receive these updates.
:type chat_join_request: :class:`telebot.types.ChatJoinRequest`
:return: An Update object.
:rtype: :class:`telebot.types.Update`
"""
@classmethod
def de_json(cls, json_string):
if json_string is None: return None

View File

@ -35,11 +35,13 @@ logger = logging.getLogger('TeleBot')
thread_local = threading.local()
#: Contains all media content types.
content_type_media = [
'text', 'audio', 'animation', 'document', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', 'dice', 'poll',
'venue', 'location'
]
#: Contains all service content types such as `User joined the group`.
content_type_service = [
'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created',
'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message',
@ -47,6 +49,7 @@ content_type_service = [
'video_chat_participants_invited', 'message_auto_delete_timer_changed'
]
#: All update types, should be used for allowed_updates parameter in polling.
update_types = [
"message", "edited_message", "channel_post", "edited_channel_post", "inline_query",
"chosen_inline_result", "callback_query", "shipping_query", "pre_checkout_query", "poll", "poll_answer",
@ -55,6 +58,9 @@ update_types = [
class WorkerThread(threading.Thread):
"""
:meta private:
"""
count = 0
def __init__(self, exception_callback=None, queue=None, name=None):
@ -118,7 +124,9 @@ class WorkerThread(threading.Thread):
class ThreadPool:
"""
:meta private:
"""
def __init__(self, telebot, num_threads=2):
self.telebot = telebot
self.tasks = Queue.Queue()
@ -156,6 +164,9 @@ class ThreadPool:
class AsyncTask:
"""
:meta private:
"""
def __init__(self, target, *args, **kwargs):
self.target = target
self.args = args
@ -182,6 +193,9 @@ class AsyncTask:
class CustomRequestResponse():
"""
:meta private:
"""
def __init__(self, json_text, status_code = 200, reason = ""):
self.status_code = status_code
self.text = json_text
@ -192,6 +206,9 @@ class CustomRequestResponse():
def async_dec():
"""
:meta private:
"""
def decorator(fn):
def wrapper(*args, **kwargs):
return AsyncTask(fn, *args, **kwargs)
@ -201,19 +218,49 @@ def async_dec():
return decorator
def is_string(var):
def is_string(var) -> bool:
"""
Returns True if the given object is a string.
"""
return isinstance(var, str)
def is_dict(var):
def is_dict(var) -> bool:
"""
Returns True if the given object is a dictionary.
:param var: object to be checked
:type var: :obj:`object`
:return: True if the given object is a dictionary.
:rtype: :obj:`bool`
"""
return isinstance(var, dict)
def is_bytes(var):
def is_bytes(var) -> bool:
"""
Returns True if the given object is a bytes object.
:param var: object to be checked
:type var: :obj:`object`
:return: True if the given object is a bytes object.
:rtype: :obj:`bool`
"""
return isinstance(var, bytes)
def is_pil_image(var):
def is_pil_image(var) -> bool:
"""
Returns True if the given object is a PIL.Image.Image object.
:param var: object to be checked
:type var: :obj:`object`
:return: True if the given object is a PIL.Image.Image object.
:rtype: :obj:`bool`
"""
return pil_imported and isinstance(var, Image.Image)
@ -233,7 +280,10 @@ def is_command(text: str) -> bool:
Checks if `text` is a command. Telegram chat commands start with the '/' character.
:param text: Text to check.
:type text: :obj:`str`
:return: True if `text` is a command, else False.
:rtype: :obj:`bool`
"""
if text is None: return False
return text.startswith('/')
@ -244,30 +294,40 @@ def extract_command(text: str) -> Union[str, None]:
Extracts the command from `text` (minus the '/') if `text` is a command (see is_command).
If `text` is not a command, this function returns None.
Examples:
extract_command('/help'): 'help'
extract_command('/help@BotName'): 'help'
extract_command('/search black eyed peas'): 'search'
extract_command('Good day to you'): None
.. code-block:: python3
:caption: Examples:
extract_command('/help'): 'help'
extract_command('/help@BotName'): 'help'
extract_command('/search black eyed peas'): 'search'
extract_command('Good day to you'): None
:param text: String to extract the command from
:type text: :obj:`str`
:return: the command if `text` is a command (according to is_command), else None.
:rtype: :obj:`str` or :obj:`None`
"""
if text is None: return None
return text.split()[0].split('@')[0][1:] if is_command(text) else None
def extract_arguments(text: str) -> str:
def extract_arguments(text: str) -> str or None:
"""
Returns the argument after the command.
Examples:
extract_arguments("/get name"): 'name'
extract_arguments("/get"): ''
extract_arguments("/get@botName name"): 'name'
.. code-block:: python3
:caption: Examples:
extract_arguments("/get name"): 'name'
extract_arguments("/get"): ''
extract_arguments("/get@botName name"): 'name'
:param text: String to extract the arguments from a command
:type text: :obj:`str`
:return: the arguments if `text` is a command (according to is_command), else None.
:rtype: :obj:`str` or :obj:`None`
"""
regexp = re.compile(r"/\w*(@\w*)*\s*([\s\S]*)", re.IGNORECASE)
result = regexp.match(text)
@ -280,8 +340,13 @@ def split_string(text: str, chars_per_string: int) -> List[str]:
This is very useful for splitting one giant message into multiples.
:param text: The text to split
:type text: :obj:`str`
:param chars_per_string: The number of characters per line the text is split into.
:type chars_per_string: :obj:`int`
:return: The splitted text as a list of strings.
:rtype: :obj:`list` of :obj:`str`
"""
return [text[i:i + chars_per_string] for i in range(0, len(text), chars_per_string)]
@ -294,8 +359,13 @@ def smart_split(text: str, chars_per_string: int=MAX_MESSAGE_LENGTH) -> List[str
Splits by '\n', '. ' or ' ' in exactly this priority.
:param text: The text to split
:type text: :obj:`str`
:param chars_per_string: The number of maximum characters per part the text is split to.
:type chars_per_string: :obj:`int`
:return: The splitted text as a list of strings.
:rtype: :obj:`list` of :obj:`str`
"""
def _text_before_last(substr: str) -> str:
@ -336,12 +406,25 @@ def user_link(user: types.User, include_id: bool=False) -> str:
Returns an HTML user link. This is useful for reports.
Attention: Don't forget to set parse_mode to 'HTML'!
Example:
bot.send_message(your_user_id, user_link(message.from_user) + ' started the bot!', parse_mode='HTML')
.. code-block:: python3
:caption: Example:
bot.send_message(your_user_id, user_link(message.from_user) + ' started the bot!', parse_mode='HTML')
.. note::
You can use formatting.* for all other formatting options(bold, italic, links, and etc.)
This method is kept for backward compatibility, and it is recommended to use formatting.* for
more options.
:param user: the user (not the user_id)
:type user: :obj:`telebot.types.User`
:param include_id: include the user_id
:type include_id: :obj:`bool`
:return: HTML user link
:rtype: :obj:`str`
"""
name = escape(user.first_name)
return (f"<a href='tg://user?id={user.id}'>{name}</a>"
@ -355,15 +438,16 @@ def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.I
Example:
.. code-block:: python
.. code-block:: python3
:caption: Using quick_markup:
quick_markup({
'Twitter': {'url': 'https://twitter.com'},
'Facebook': {'url': 'https://facebook.com'},
'Back': {'callback_data': 'whatever'}
}, row_width=2):
# returns an InlineKeyboardMarkup with two buttons in a row, one leading to Twitter, the other to facebook
# and a back button below
# returns an InlineKeyboardMarkup with two buttons in a row, one leading to Twitter, the other to facebook
# and a back button below
# kwargs can be:
{
@ -378,8 +462,13 @@ def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.I
}
:param values: a dict containing all buttons to create in this format: {text: kwargs} {str:}
:type values: :obj:`dict`
:param row_width: int row width
:type row_width: :obj:`int`
:return: InlineKeyboardMarkup
:rtype: :obj:`types.InlineKeyboardMarkup`
"""
markup = types.InlineKeyboardMarkup(row_width=row_width)
buttons = [
@ -392,16 +481,25 @@ def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.I
# CREDITS TO http://stackoverflow.com/questions/12317940#answer-12320352
def or_set(self):
"""
:meta private:
"""
self._set()
self.changed()
def or_clear(self):
"""
:meta private:
"""
self._clear()
self.changed()
def orify(e, changed_callback):
"""
:meta private:
"""
if not hasattr(e, "_set"):
e._set = e.set
if not hasattr(e, "_clear"):
@ -412,6 +510,9 @@ def orify(e, changed_callback):
def OrEvent(*events):
"""
:meta private:
"""
or_event = threading.Event()
def changed():
@ -435,6 +536,9 @@ def OrEvent(*events):
def per_thread(key, construct_value, reset=False):
"""
:meta private:
"""
if reset or not hasattr(thread_local, key):
value = construct_value()
setattr(thread_local, key, value)
@ -449,7 +553,13 @@ def chunks(lst, n):
yield lst[i:i + n]
def generate_random_token():
def generate_random_token() -> str:
"""
Generates a random token consisting of letters and digits, 16 characters long.
:return: a random token
:rtype: :obj:`str`
"""
return ''.join(random.sample(string.ascii_letters, 16))
@ -457,10 +567,19 @@ def deprecated(warn: bool=True, alternative: Optional[Callable]=None, deprecatio
"""
Use this decorator to mark functions as deprecated.
When the function is used, an info (or warning if `warn` is True) is logged.
:meta private:
:param warn: If True a warning is logged else an info
:type warn: :obj:`bool`
:param alternative: The new function to use instead
:type alternative: :obj:`Callable`
:param deprecation_text: Custom deprecation text
:type deprecation_text: :obj:`str`
:return: The decorated function
"""
def decorator(function):
def wrapper(*args, **kwargs):
@ -480,7 +599,17 @@ def deprecated(warn: bool=True, alternative: Optional[Callable]=None, deprecatio
# Cloud helpers
def webhook_google_functions(bot, request):
"""A webhook endpoint for Google Cloud Functions FaaS."""
"""
A webhook endpoint for Google Cloud Functions FaaS.
:param bot: The bot instance
:type bot: :obj:`telebot.TeleBot` or :obj:`telebot.async_telebot.AsyncTeleBot`
:param request: The request object
:type request: :obj:`flask.Request`
:return: The response object
"""
if request.is_json:
try:
request_json = request.get_json()
@ -494,7 +623,7 @@ def webhook_google_functions(bot, request):
return 'Bot ON'
def antiflood(function, *args, **kwargs):
def antiflood(function: Callable, *args, **kwargs):
"""
Use this function inside loops in order to avoid getting TooManyRequests error.
Example:
@ -505,9 +634,15 @@ def antiflood(function, *args, **kwargs):
for chat_id in chat_id_list:
msg = antiflood(bot.send_message, chat_id, text)
:param function:
:param args:
:param kwargs:
:param function: The function to call
:type function: :obj:`Callable`
:param args: The arguments to pass to the function
:type args: :obj:`tuple`
:param kwargs: The keyword arguments to pass to the function
:type kwargs: :obj:`dict`
:return: None
"""
from telebot.apihelper import ApiTelegramException
@ -524,6 +659,17 @@ def antiflood(function, *args, **kwargs):
def parse_web_app_data(token: str, raw_init_data: str):
"""
Parses web app data.
:param token: The bot token
:type token: :obj:`str`
:param raw_init_data: The raw init data
:type raw_init_data: :obj:`str`
:return: The parsed init data
"""
is_valid = validate_web_app_data(token, raw_init_data)
if not is_valid:
return False
@ -539,7 +685,18 @@ def parse_web_app_data(token: str, raw_init_data: str):
return result
def validate_web_app_data(token, raw_init_data):
def validate_web_app_data(token: str, raw_init_data: str):
"""
Validates web app data.
:param token: The bot token
:type token: :obj:`str`
:param raw_init_data: The raw init data
:type raw_init_data: :obj:`str`
:return: The parsed init data
"""
try:
parsed_data = dict(parse_qsl(raw_init_data))
except ValueError: