mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-12-22 14:35:00 +01:00
CommandHandler overhaul and PrefixHandler added (#1114)
* Commandhandler reworked * Make CommandHandler strict Only register valid botcommands, else raise ValueError * Add PrefixHandler * declare encoding on test_commandhandler * Fix some tests dependend on CommandHandler * CR changes * small docfix. * Test all possibilities for PrefixHandler
This commit is contained in:
parent
247577b2e2
commit
87afd98e02
16 changed files with 575 additions and 97 deletions
6
docs/source/telegram.ext.prefixhandler.rst
Normal file
6
docs/source/telegram.ext.prefixhandler.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
telegram.ext.PrefixHandler
|
||||||
|
===========================
|
||||||
|
|
||||||
|
.. autoclass:: telegram.ext.PrefixHandler
|
||||||
|
:members:
|
||||||
|
:show-inheritance:
|
|
@ -25,6 +25,7 @@ Handlers
|
||||||
telegram.ext.inlinequeryhandler
|
telegram.ext.inlinequeryhandler
|
||||||
telegram.ext.messagehandler
|
telegram.ext.messagehandler
|
||||||
telegram.ext.precheckoutqueryhandler
|
telegram.ext.precheckoutqueryhandler
|
||||||
|
telegram.ext.prefixhandler
|
||||||
telegram.ext.regexhandler
|
telegram.ext.regexhandler
|
||||||
telegram.ext.shippingqueryhandler
|
telegram.ext.shippingqueryhandler
|
||||||
telegram.ext.stringcommandhandler
|
telegram.ext.stringcommandhandler
|
||||||
|
|
|
@ -25,7 +25,7 @@ from .jobqueue import JobQueue, Job
|
||||||
from .updater import Updater
|
from .updater import Updater
|
||||||
from .callbackqueryhandler import CallbackQueryHandler
|
from .callbackqueryhandler import CallbackQueryHandler
|
||||||
from .choseninlineresulthandler import ChosenInlineResultHandler
|
from .choseninlineresulthandler import ChosenInlineResultHandler
|
||||||
from .commandhandler import CommandHandler
|
from .commandhandler import CommandHandler, PrefixHandler
|
||||||
from .inlinequeryhandler import InlineQueryHandler
|
from .inlinequeryhandler import InlineQueryHandler
|
||||||
from .messagehandler import MessageHandler
|
from .messagehandler import MessageHandler
|
||||||
from .filters import BaseFilter, Filters
|
from .filters import BaseFilter, Filters
|
||||||
|
@ -44,4 +44,4 @@ __all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler',
|
||||||
'MessageHandler', 'BaseFilter', 'Filters', 'RegexHandler', 'StringCommandHandler',
|
'MessageHandler', 'BaseFilter', 'Filters', 'RegexHandler', 'StringCommandHandler',
|
||||||
'StringRegexHandler', 'TypeHandler', 'ConversationHandler',
|
'StringRegexHandler', 'TypeHandler', 'ConversationHandler',
|
||||||
'PreCheckoutQueryHandler', 'ShippingQueryHandler', 'MessageQueue', 'DelayQueue',
|
'PreCheckoutQueryHandler', 'ShippingQueryHandler', 'MessageQueue', 'DelayQueue',
|
||||||
'DispatcherHandlerStop', 'run_async', 'CallbackContext')
|
'DispatcherHandlerStop', 'run_async', 'CallbackContext', 'PrefixHandler')
|
||||||
|
|
|
@ -37,9 +37,9 @@ class CallbackContext(object):
|
||||||
regex-supported handler, this will contain the object returned from
|
regex-supported handler, this will contain the object returned from
|
||||||
``re.match(pattern, string)``.
|
``re.match(pattern, string)``.
|
||||||
args (List[:obj:`str`], optional): Arguments passed to a command if the associated update
|
args (List[:obj:`str`], optional): Arguments passed to a command if the associated update
|
||||||
is handled by :class:`telegram.ext.CommandHandler` or
|
is handled by :class:`telegram.ext.CommandHandler`, :class:`telegram.ext.PrefixHandler`
|
||||||
:class:`telegram.ext.StringCommandHandler`. It contains a list of the words in the text
|
or :class:`telegram.ext.StringCommandHandler`. It contains a list of the words in the
|
||||||
after the command, using any whitespace string as a delimiter.
|
text after the command, using any whitespace string as a delimiter.
|
||||||
error (:class:`telegram.TelegramError`, optional): The Telegram error that was raised.
|
error (:class:`telegram.TelegramError`, optional): The Telegram error that was raised.
|
||||||
Only present when passed to a error handler registered with
|
Only present when passed to a error handler registered with
|
||||||
:attr:`telegram.ext.Dispatcher.add_error_handler`.
|
:attr:`telegram.ext.Dispatcher.add_error_handler`.
|
||||||
|
|
|
@ -50,7 +50,7 @@ class CallbackQueryHandler(Handler):
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
||||||
can use to keep any data in will be sent to the :attr:`callback` function.. Related to
|
can use to keep any data in will be sent to the :attr:`callback` function. Related to
|
||||||
either the user or the chat that the update was sent in. For each update from the same user
|
either the user or the chat that the update was sent in. For each update from the same user
|
||||||
or in the same chat, it will be the same ``dict``.
|
or in the same chat, it will be the same ``dict``.
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ class ChosenInlineResultHandler(Handler):
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
||||||
can use to keep any data in will be sent to the :attr:`callback` function.. Related to
|
can use to keep any data in will be sent to the :attr:`callback` function. Related to
|
||||||
either the user or the chat that the update was sent in. For each update from the same user
|
either the user or the chat that the update was sent in. For each update from the same user
|
||||||
or in the same chat, it will be the same ``dict``.
|
or in the same chat, it will be the same ``dict``.
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,12 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser Public License
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
"""This module contains the CommandHandler class."""
|
"""This module contains the CommandHandler and PrefixHandler classes."""
|
||||||
|
import re
|
||||||
|
|
||||||
from future.utils import string_types
|
from future.utils import string_types
|
||||||
|
|
||||||
from telegram import Update
|
from telegram import Update, MessageEntity
|
||||||
from .handler import Handler
|
from .handler import Handler
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,9 +29,177 @@ class CommandHandler(Handler):
|
||||||
"""Handler class to handle Telegram commands.
|
"""Handler class to handle Telegram commands.
|
||||||
|
|
||||||
Commands are Telegram messages that start with ``/``, optionally followed by an ``@`` and the
|
Commands are Telegram messages that start with ``/``, optionally followed by an ``@`` and the
|
||||||
bot's name and/or some additional text.
|
bot's name and/or some additional text. The handler will add a ``list`` to the
|
||||||
|
:class:`CallbackContext` named :attr:`CallbackContext.args`. It will contain a list of strings,
|
||||||
|
which is the text following the command split on single or consecutive whitespace characters.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
|
command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
|
||||||
|
should listen for. Limitations are the same as described here
|
||||||
|
https://core.telegram.org/bots#commands
|
||||||
|
callback (:obj:`callable`): The callback function for this handler.
|
||||||
|
filters (:class:`telegram.ext.BaseFilter`): Optional. Only allow updates with these
|
||||||
|
Filters.
|
||||||
|
allow_edited (:obj:`bool`): Determines Whether the handler should also accept
|
||||||
|
edited messages.
|
||||||
|
pass_args (:obj:`bool`): Determines whether the handler should be passed
|
||||||
|
``args``.
|
||||||
|
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
|
||||||
|
passed to the callback function.
|
||||||
|
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
|
||||||
|
the callback function.
|
||||||
|
pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to
|
||||||
|
the callback function.
|
||||||
|
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
|
||||||
|
the callback function.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
||||||
|
can use to keep any data in will be sent to the :attr:`callback` function. Related to
|
||||||
|
either the user or the chat that the update was sent in. For each update from the same user
|
||||||
|
or in the same chat, it will be the same ``dict``.
|
||||||
|
|
||||||
|
Note that this is DEPRECATED, and you should use context based callbacks. See
|
||||||
|
https://git.io/vp113 for more info.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
|
||||||
|
should listen for. Limitations are the same as described here
|
||||||
|
https://core.telegram.org/bots#commands
|
||||||
|
callback (:obj:`callable`): The callback function for this handler. Will be called when
|
||||||
|
:attr:`check_update` has determined that an update should be processed by this handler.
|
||||||
|
Callback signature for context based API:
|
||||||
|
|
||||||
|
``def callback(update: Update, context: CallbackContext)``
|
||||||
|
|
||||||
|
The return value of the callback is usually ignored except for the special case of
|
||||||
|
:class:`telegram.ext.ConversationHandler`.
|
||||||
|
filters (:class:`telegram.ext.BaseFilter`, optional): A filter inheriting from
|
||||||
|
:class:`telegram.ext.filters.BaseFilter`. Standard filters can be found in
|
||||||
|
:class:`telegram.ext.filters.Filters`. Filters can be combined using bitwise
|
||||||
|
operators (& for and, | for or, ~ for not).
|
||||||
|
allow_edited (:obj:`bool`, optional): Determines whether the handler should also accept
|
||||||
|
edited messages. Default is ``False``.
|
||||||
|
pass_args (:obj:`bool`, optional): Determines whether the handler should be passed the
|
||||||
|
arguments passed to the command as a keyword argument called ``args``. It will contain
|
||||||
|
a list of strings, which is the text following the command split on single or
|
||||||
|
consecutive whitespace characters. Default is ``False``
|
||||||
|
DEPRECATED: Please switch to context based callbacks.
|
||||||
|
pass_update_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called
|
||||||
|
``update_queue`` will be passed to the callback function. It will be the ``Queue``
|
||||||
|
instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher`
|
||||||
|
that contains new updates which can be used to insert updates. Default is ``False``.
|
||||||
|
DEPRECATED: Please switch to context based callbacks.
|
||||||
|
pass_job_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called
|
||||||
|
``job_queue`` will be passed to the callback function. It will be a
|
||||||
|
:class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
|
||||||
|
which can be used to schedule new jobs. Default is ``False``.
|
||||||
|
DEPRECATED: Please switch to context based callbacks.
|
||||||
|
pass_user_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
|
||||||
|
``user_data`` will be passed to the callback function. Default is ``False``.
|
||||||
|
DEPRECATED: Please switch to context based callbacks.
|
||||||
|
pass_chat_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
|
||||||
|
``chat_data`` will be passed to the callback function. Default is ``False``.
|
||||||
|
DEPRECATED: Please switch to context based callbacks.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError - when command is too long or has illegal chars.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
command,
|
||||||
|
callback,
|
||||||
|
filters=None,
|
||||||
|
allow_edited=False,
|
||||||
|
pass_args=False,
|
||||||
|
pass_update_queue=False,
|
||||||
|
pass_job_queue=False,
|
||||||
|
pass_user_data=False,
|
||||||
|
pass_chat_data=False):
|
||||||
|
super(CommandHandler, self).__init__(
|
||||||
|
callback,
|
||||||
|
pass_update_queue=pass_update_queue,
|
||||||
|
pass_job_queue=pass_job_queue,
|
||||||
|
pass_user_data=pass_user_data,
|
||||||
|
pass_chat_data=pass_chat_data)
|
||||||
|
|
||||||
|
if isinstance(command, string_types):
|
||||||
|
self.command = [command.lower()]
|
||||||
|
else:
|
||||||
|
self.command = [x.lower() for x in command]
|
||||||
|
for comm in self.command:
|
||||||
|
if not re.match(r'^[\da-z_]{1,32}$', comm):
|
||||||
|
raise ValueError('Command is not a valid bot command')
|
||||||
|
|
||||||
|
self.filters = filters
|
||||||
|
self.allow_edited = allow_edited
|
||||||
|
self.pass_args = pass_args
|
||||||
|
|
||||||
|
def check_update(self, update):
|
||||||
|
"""Determines whether an update should be passed to this handlers :attr:`callback`.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
update (:class:`telegram.Update`): Incoming telegram update.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
:obj:`bool`
|
||||||
|
|
||||||
|
"""
|
||||||
|
if (isinstance(update, Update) and
|
||||||
|
(update.message or update.edited_message and self.allow_edited)):
|
||||||
|
message = update.effective_message
|
||||||
|
|
||||||
|
if (message.entities and message.entities[0].type == MessageEntity.BOT_COMMAND and
|
||||||
|
message.entities[0].offset == 0):
|
||||||
|
command = message.text[1:message.entities[0].length]
|
||||||
|
args = message.text.split()[1:]
|
||||||
|
command = command.split('@')
|
||||||
|
command.append(message.bot.username)
|
||||||
|
|
||||||
|
if not (command[0].lower() in self.command and
|
||||||
|
command[1].lower() == message.bot.username.lower()):
|
||||||
|
return None
|
||||||
|
|
||||||
|
if self.filters is None or self.filters(message):
|
||||||
|
return args
|
||||||
|
|
||||||
|
def collect_optional_args(self, dispatcher, update=None, check_result=None):
|
||||||
|
optional_args = super(CommandHandler, self).collect_optional_args(dispatcher, update)
|
||||||
|
if self.pass_args:
|
||||||
|
optional_args['args'] = check_result
|
||||||
|
return optional_args
|
||||||
|
|
||||||
|
def collect_additional_context(self, context, update, dispatcher, check_result):
|
||||||
|
context.args = check_result
|
||||||
|
|
||||||
|
|
||||||
|
class PrefixHandler(CommandHandler):
|
||||||
|
"""Handler class to handle custom prefix commands
|
||||||
|
|
||||||
|
This is a intermediate handler between :class:`MessageHandler` and :class:`CommandHandler`.
|
||||||
|
It supports configurable commands with the same options as CommandHandler. It will respond to
|
||||||
|
every combination of :attr:`prefix` and :attr:`command`. It will add a ``list`` to the
|
||||||
|
:class:`CallbackContext` named :attr:`CallbackContext.args`. It will contain a list of strings,
|
||||||
|
which is the text following the command split on single or consecutive whitespace characters.
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
Single prefix and command:
|
||||||
|
|
||||||
|
PrefixHandler('!', 'test', callback) will respond to '!test'.
|
||||||
|
|
||||||
|
Multiple prefixes, single command:
|
||||||
|
|
||||||
|
PrefixHandler(['!', '#'], 'test', callback) will respond to '!test' and
|
||||||
|
'#test'.
|
||||||
|
|
||||||
|
Miltiple prefixes and commands:
|
||||||
|
|
||||||
|
PrefixHandler(['!', '#'], ['test', 'help`], callback) will respond to '!test',
|
||||||
|
'#test', '!help' and '#help'.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
prefix (:obj:`str` | List[:obj:`str`]): The prefix(es) that will precede :attr:`command`.
|
||||||
command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
|
command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
|
||||||
should listen for.
|
should listen for.
|
||||||
callback (:obj:`callable`): The callback function for this handler.
|
callback (:obj:`callable`): The callback function for this handler.
|
||||||
|
@ -50,7 +220,7 @@ class CommandHandler(Handler):
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
||||||
can use to keep any data in will be sent to the :attr:`callback` function.. Related to
|
can use to keep any data in will be sent to the :attr:`callback` function. Related to
|
||||||
either the user or the chat that the update was sent in. For each update from the same user
|
either the user or the chat that the update was sent in. For each update from the same user
|
||||||
or in the same chat, it will be the same ``dict``.
|
or in the same chat, it will be the same ``dict``.
|
||||||
|
|
||||||
|
@ -58,6 +228,7 @@ class CommandHandler(Handler):
|
||||||
https://git.io/vp113 for more info.
|
https://git.io/vp113 for more info.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
prefix (:obj:`str` | List[:obj:`str`]): The prefix(es) that will precede :attr:`command`.
|
||||||
command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
|
command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
|
||||||
should listen for.
|
should listen for.
|
||||||
callback (:obj:`callable`): The callback function for this handler. Will be called when
|
callback (:obj:`callable`): The callback function for this handler. Will be called when
|
||||||
|
@ -99,6 +270,7 @@ class CommandHandler(Handler):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
|
prefix,
|
||||||
command,
|
command,
|
||||||
callback,
|
callback,
|
||||||
filters=None,
|
filters=None,
|
||||||
|
@ -108,20 +280,23 @@ class CommandHandler(Handler):
|
||||||
pass_job_queue=False,
|
pass_job_queue=False,
|
||||||
pass_user_data=False,
|
pass_user_data=False,
|
||||||
pass_chat_data=False):
|
pass_chat_data=False):
|
||||||
super(CommandHandler, self).__init__(
|
|
||||||
callback,
|
super(PrefixHandler, self).__init__(
|
||||||
|
'nocommand', callback, filters=filters, allow_edited=allow_edited, pass_args=pass_args,
|
||||||
pass_update_queue=pass_update_queue,
|
pass_update_queue=pass_update_queue,
|
||||||
pass_job_queue=pass_job_queue,
|
pass_job_queue=pass_job_queue,
|
||||||
pass_user_data=pass_user_data,
|
pass_user_data=pass_user_data,
|
||||||
pass_chat_data=pass_chat_data)
|
pass_chat_data=pass_chat_data)
|
||||||
|
|
||||||
|
if isinstance(prefix, string_types):
|
||||||
|
self.prefix = [prefix.lower()]
|
||||||
|
else:
|
||||||
|
self.prefix = prefix
|
||||||
if isinstance(command, string_types):
|
if isinstance(command, string_types):
|
||||||
self.command = [command.lower()]
|
self.command = [command.lower()]
|
||||||
else:
|
else:
|
||||||
self.command = [x.lower() for x in command]
|
self.command = command
|
||||||
self.filters = filters
|
self.command = [x.lower() + y.lower() for x in self.prefix for y in self.command]
|
||||||
self.allow_edited = allow_edited
|
|
||||||
self.pass_args = pass_args
|
|
||||||
|
|
||||||
def check_update(self, update):
|
def check_update(self, update):
|
||||||
"""Determines whether an update should be passed to this handlers :attr:`callback`.
|
"""Determines whether an update should be passed to this handlers :attr:`callback`.
|
||||||
|
@ -135,27 +310,10 @@ class CommandHandler(Handler):
|
||||||
"""
|
"""
|
||||||
if (isinstance(update, Update) and
|
if (isinstance(update, Update) and
|
||||||
(update.message or update.edited_message and self.allow_edited)):
|
(update.message or update.edited_message and self.allow_edited)):
|
||||||
message = update.message or update.edited_message
|
message = update.effective_message
|
||||||
|
|
||||||
if message.text and message.text.startswith('/') and len(message.text) > 1:
|
text_list = message.text.split()
|
||||||
first_word = message.text_html.split(None, 1)[0]
|
if text_list[0].lower() not in self.command:
|
||||||
if len(first_word) > 1 and first_word.startswith('/'):
|
return None
|
||||||
command = first_word[1:].split('@')
|
if self.filters is None or self.filters(message):
|
||||||
command.append(
|
return text_list[1:]
|
||||||
message.bot.username) # in case the command was sent without a username
|
|
||||||
|
|
||||||
if not (command[0].lower() in self.command
|
|
||||||
and command[1].lower() == message.bot.username.lower()):
|
|
||||||
return None
|
|
||||||
|
|
||||||
if self.filters is None or self.filters(message):
|
|
||||||
return message.text.split()[1:]
|
|
||||||
|
|
||||||
def collect_optional_args(self, dispatcher, update=None, check_result=None):
|
|
||||||
optional_args = super(CommandHandler, self).collect_optional_args(dispatcher, update)
|
|
||||||
if self.pass_args:
|
|
||||||
optional_args['args'] = check_result
|
|
||||||
return optional_args
|
|
||||||
|
|
||||||
def collect_additional_context(self, context, update, dispatcher, check_result):
|
|
||||||
context.args = check_result
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ class Handler(object):
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
||||||
can use to keep any data in will be sent to the :attr:`callback` function.. Related to
|
can use to keep any data in will be sent to the :attr:`callback` function. Related to
|
||||||
either the user or the chat that the update was sent in. For each update from the same user
|
either the user or the chat that the update was sent in. For each update from the same user
|
||||||
or in the same chat, it will be the same ``dict``.
|
or in the same chat, it will be the same ``dict``.
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ class InlineQueryHandler(Handler):
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
||||||
can use to keep any data in will be sent to the :attr:`callback` function.. Related to
|
can use to keep any data in will be sent to the :attr:`callback` function. Related to
|
||||||
either the user or the chat that the update was sent in. For each update from the same user
|
either the user or the chat that the update was sent in. For each update from the same user
|
||||||
or in the same chat, it will be the same ``dict``.
|
or in the same chat, it will be the same ``dict``.
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ class MessageHandler(Handler):
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
||||||
can use to keep any data in will be sent to the :attr:`callback` function.. Related to
|
can use to keep any data in will be sent to the :attr:`callback` function. Related to
|
||||||
either the user or the chat that the update was sent in. For each update from the same user
|
either the user or the chat that the update was sent in. For each update from the same user
|
||||||
or in the same chat, it will be the same ``dict``.
|
or in the same chat, it will be the same ``dict``.
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ class PreCheckoutQueryHandler(Handler):
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
||||||
can use to keep any data in will be sent to the :attr:`callback` function.. Related to
|
can use to keep any data in will be sent to the :attr:`callback` function. Related to
|
||||||
either the user or the chat that the update was sent in. For each update from the same user
|
either the user or the chat that the update was sent in. For each update from the same user
|
||||||
or in the same chat, it will be the same ``dict``.
|
or in the same chat, it will be the same ``dict``.
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ class RegexHandler(Handler):
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
||||||
can use to keep any data in will be sent to the :attr:`callback` function.. Related to
|
can use to keep any data in will be sent to the :attr:`callback` function. Related to
|
||||||
either the user or the chat that the update was sent in. For each update from the same user
|
either the user or the chat that the update was sent in. For each update from the same user
|
||||||
or in the same chat, it will be the same ``dict``.
|
or in the same chat, it will be the same ``dict``.
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ class ShippingQueryHandler(Handler):
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
||||||
can use to keep any data in will be sent to the :attr:`callback` function.. Related to
|
can use to keep any data in will be sent to the :attr:`callback` function. Related to
|
||||||
either the user or the chat that the update was sent in. For each update from the same user
|
either the user or the chat that the update was sent in. For each update from the same user
|
||||||
or in the same chat, it will be the same ``dict``.
|
or in the same chat, it will be the same ``dict``.
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# A library that provides a Python interface to the Telegram Bot API
|
# A library that provides a Python interface to the Telegram Bot API
|
||||||
# Copyright (C) 2015-2018
|
# Copyright (C) 2015-2018
|
||||||
|
@ -21,8 +22,9 @@ from queue import Queue
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from telegram import (Message, Update, Chat, Bot, User, CallbackQuery, InlineQuery,
|
from telegram import (Message, Update, Chat, Bot, User, CallbackQuery, InlineQuery,
|
||||||
ChosenInlineResult, ShippingQuery, PreCheckoutQuery)
|
ChosenInlineResult, ShippingQuery, PreCheckoutQuery, MessageEntity)
|
||||||
from telegram.ext import CommandHandler, Filters, BaseFilter, CallbackContext, JobQueue
|
from telegram.ext import CommandHandler, Filters, BaseFilter, CallbackContext, JobQueue, \
|
||||||
|
PrefixHandler
|
||||||
|
|
||||||
message = Message(1, User(1, '', False), None, Chat(1, ''), text='test')
|
message = Message(1, User(1, '', False), None, Chat(1, ''), text='test')
|
||||||
|
|
||||||
|
@ -49,7 +51,15 @@ def false_update(request):
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
@pytest.fixture(scope='function')
|
||||||
def message(bot):
|
def message(bot):
|
||||||
return Message(1, User(1, '', False), None, Chat(1, ''), bot=bot)
|
return Message(message_id=1,
|
||||||
|
from_user=User(id=1, first_name='', is_bot=False),
|
||||||
|
date=None,
|
||||||
|
chat=Chat(id=1, type=''),
|
||||||
|
message='/test',
|
||||||
|
bot=bot,
|
||||||
|
entities=[MessageEntity(type=MessageEntity.BOT_COMMAND,
|
||||||
|
offset=0,
|
||||||
|
length=len('/test'))])
|
||||||
|
|
||||||
|
|
||||||
class TestCommandHandler(object):
|
class TestCommandHandler(object):
|
||||||
|
@ -117,13 +127,26 @@ class TestCommandHandler(object):
|
||||||
check = handler.check_update(Update(0, message))
|
check = handler.check_update(Update(0, message))
|
||||||
assert check is None or check is False
|
assert check is None or check is False
|
||||||
|
|
||||||
|
message.entities = []
|
||||||
|
message.text = '/test'
|
||||||
|
check = handler.check_update(Update(0, message))
|
||||||
|
assert check is None or check is False
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('command',
|
||||||
|
['way_too_longcommand1234567yes_way_toooooooLong', 'ïñválídletters',
|
||||||
|
'invalid #&* chars'],
|
||||||
|
ids=['too long', 'invalid letter', 'invalid characters'])
|
||||||
|
def test_invalid_commands(self, command):
|
||||||
|
with pytest.raises(ValueError, match='not a valid bot command'):
|
||||||
|
CommandHandler(command, self.callback_basic)
|
||||||
|
|
||||||
def test_command_list(self, message):
|
def test_command_list(self, message):
|
||||||
handler = CommandHandler(['test', 'start'], self.callback_basic)
|
handler = CommandHandler(['test', 'star'], self.callback_basic)
|
||||||
|
|
||||||
message.text = '/test'
|
message.text = '/test'
|
||||||
check = handler.check_update(Update(0, message))
|
check = handler.check_update(Update(0, message))
|
||||||
|
|
||||||
message.text = '/start'
|
message.text = '/star'
|
||||||
check = handler.check_update(Update(0, message))
|
check = handler.check_update(Update(0, message))
|
||||||
|
|
||||||
message.text = '/stop'
|
message.text = '/stop'
|
||||||
|
@ -137,11 +160,14 @@ class TestCommandHandler(object):
|
||||||
message.text = '/test'
|
message.text = '/test'
|
||||||
check = handler.check_update(Update(0, message))
|
check = handler.check_update(Update(0, message))
|
||||||
assert check is not None and check is not False
|
assert check is not None and check is not False
|
||||||
|
|
||||||
check = handler.check_update(Update(0, edited_message=message))
|
check = handler.check_update(Update(0, edited_message=message))
|
||||||
assert check is None or check is False
|
assert check is None or check is False
|
||||||
|
|
||||||
handler.allow_edited = True
|
handler.allow_edited = True
|
||||||
check = handler.check_update(Update(0, message))
|
check = handler.check_update(Update(0, message))
|
||||||
assert check is not None and check is not False
|
assert check is not None and check is not False
|
||||||
|
|
||||||
check = handler.check_update(Update(0, edited_message=message))
|
check = handler.check_update(Update(0, edited_message=message))
|
||||||
assert check is not None and check is not False
|
assert check is not None and check is not False
|
||||||
|
|
||||||
|
@ -149,11 +175,13 @@ class TestCommandHandler(object):
|
||||||
handler = CommandHandler('test', self.callback_basic)
|
handler = CommandHandler('test', self.callback_basic)
|
||||||
|
|
||||||
message.text = '/test@{}'.format(message.bot.username)
|
message.text = '/test@{}'.format(message.bot.username)
|
||||||
|
message.entities[0].length = len(message.text)
|
||||||
check = handler.check_update(Update(0, message))
|
check = handler.check_update(Update(0, message))
|
||||||
assert check is not None and check is not False
|
assert check is not None and check is not False
|
||||||
|
|
||||||
message.text = '/test@otherbot'
|
message.text = '/test@otherbot'
|
||||||
assert not handler.check_update(Update(0, message))
|
check = handler.check_update(Update(0, message))
|
||||||
|
assert check is None or check is False
|
||||||
|
|
||||||
def test_with_filter(self, message):
|
def test_with_filter(self, message):
|
||||||
handler = CommandHandler('test', self.callback_basic, Filters.group)
|
handler = CommandHandler('test', self.callback_basic, Filters.group)
|
||||||
|
@ -177,11 +205,7 @@ class TestCommandHandler(object):
|
||||||
|
|
||||||
self.test_flag = False
|
self.test_flag = False
|
||||||
message.text = '/test@{}'.format(message.bot.username)
|
message.text = '/test@{}'.format(message.bot.username)
|
||||||
dp.process_update(Update(0, message=message))
|
message.entities[0].length = len(message.text)
|
||||||
assert self.test_flag
|
|
||||||
|
|
||||||
self.test_flag = False
|
|
||||||
message.text = '/test one two'
|
|
||||||
dp.process_update(Update(0, message=message))
|
dp.process_update(Update(0, message=message))
|
||||||
assert self.test_flag
|
assert self.test_flag
|
||||||
|
|
||||||
|
@ -190,6 +214,12 @@ class TestCommandHandler(object):
|
||||||
dp.process_update(Update(0, message=message))
|
dp.process_update(Update(0, message=message))
|
||||||
assert self.test_flag
|
assert self.test_flag
|
||||||
|
|
||||||
|
self.test_flag = False
|
||||||
|
message.text = '/test one two'
|
||||||
|
message.entities[0].length = len('/test')
|
||||||
|
dp.process_update(Update(0, message=message))
|
||||||
|
assert self.test_flag
|
||||||
|
|
||||||
def test_newline(self, dp, message):
|
def test_newline(self, dp, message):
|
||||||
handler = CommandHandler('test', self.callback_basic)
|
handler = CommandHandler('test', self.callback_basic)
|
||||||
dp.add_handler(handler)
|
dp.add_handler(handler)
|
||||||
|
@ -197,31 +227,10 @@ class TestCommandHandler(object):
|
||||||
message.text = '/test\nfoobar'
|
message.text = '/test\nfoobar'
|
||||||
check = handler.check_update(Update(0, message))
|
check = handler.check_update(Update(0, message))
|
||||||
assert check is not None and check is not False
|
assert check is not None and check is not False
|
||||||
|
|
||||||
dp.process_update(Update(0, message))
|
dp.process_update(Update(0, message))
|
||||||
assert self.test_flag
|
assert self.test_flag
|
||||||
|
|
||||||
def test_single_char(self, dp, message):
|
|
||||||
# Regression test for https://github.com/python-telegram-bot/python-telegram-bot/issues/871
|
|
||||||
handler = CommandHandler('test', self.callback_basic)
|
|
||||||
dp.add_handler(handler)
|
|
||||||
|
|
||||||
message.text = 'a'
|
|
||||||
check = handler.check_update(Update(0, message))
|
|
||||||
assert check is None or check is False
|
|
||||||
|
|
||||||
def test_single_slash(self, dp, message):
|
|
||||||
# Regression test for https://github.com/python-telegram-bot/python-telegram-bot/issues/871
|
|
||||||
handler = CommandHandler('test', self.callback_basic)
|
|
||||||
dp.add_handler(handler)
|
|
||||||
|
|
||||||
message.text = '/'
|
|
||||||
check = handler.check_update(Update(0, message))
|
|
||||||
assert check is None or check is False
|
|
||||||
|
|
||||||
message.text = '/ test'
|
|
||||||
check = handler.check_update(Update(0, message))
|
|
||||||
assert check is None or check is False
|
|
||||||
|
|
||||||
def test_pass_user_or_chat_data(self, dp, message):
|
def test_pass_user_or_chat_data(self, dp, message):
|
||||||
handler = CommandHandler('test', self.callback_data_1,
|
handler = CommandHandler('test', self.callback_data_1,
|
||||||
pass_user_data=True)
|
pass_user_data=True)
|
||||||
|
@ -295,9 +304,9 @@ class TestCommandHandler(object):
|
||||||
|
|
||||||
test_filter = TestFilter()
|
test_filter = TestFilter()
|
||||||
|
|
||||||
handler = CommandHandler('foo', self.callback_basic,
|
handler = CommandHandler('test', self.callback_basic,
|
||||||
filters=test_filter)
|
filters=test_filter)
|
||||||
message.text = '/bar'
|
message.text = '/star'
|
||||||
|
|
||||||
check = handler.check_update(Update(0, message=message))
|
check = handler.check_update(Update(0, message=message))
|
||||||
assert check is None or check is False
|
assert check is None or check is False
|
||||||
|
@ -323,3 +332,241 @@ class TestCommandHandler(object):
|
||||||
message.text = '/test one two'
|
message.text = '/test one two'
|
||||||
cdp.process_update(Update(0, message))
|
cdp.process_update(Update(0, message))
|
||||||
assert self.test_flag
|
assert self.test_flag
|
||||||
|
|
||||||
|
|
||||||
|
par = ['!help', '!test', '#help', '#test', 'mytrig-help', 'mytrig-test']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function', params=par)
|
||||||
|
def prefixmessage(bot, request):
|
||||||
|
return Message(message_id=1,
|
||||||
|
from_user=User(id=1, first_name='', is_bot=False),
|
||||||
|
date=None,
|
||||||
|
chat=Chat(id=1, type=''),
|
||||||
|
text=request.param,
|
||||||
|
bot=bot)
|
||||||
|
|
||||||
|
|
||||||
|
class TestPrefixHandler(object):
|
||||||
|
test_flag = False
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def reset(self):
|
||||||
|
self.test_flag = False
|
||||||
|
|
||||||
|
def callback_basic(self, bot, update):
|
||||||
|
test_bot = isinstance(bot, Bot)
|
||||||
|
test_update = isinstance(update, Update)
|
||||||
|
self.test_flag = test_bot and test_update
|
||||||
|
|
||||||
|
def callback_data_1(self, bot, update, user_data=None, chat_data=None):
|
||||||
|
self.test_flag = (user_data is not None) or (chat_data is not None)
|
||||||
|
|
||||||
|
def callback_data_2(self, bot, update, user_data=None, chat_data=None):
|
||||||
|
self.test_flag = (user_data is not None) and (chat_data is not None)
|
||||||
|
|
||||||
|
def callback_queue_1(self, bot, update, job_queue=None, update_queue=None):
|
||||||
|
self.test_flag = (job_queue is not None) or (update_queue is not None)
|
||||||
|
|
||||||
|
def callback_queue_2(self, bot, update, job_queue=None, update_queue=None):
|
||||||
|
self.test_flag = (job_queue is not None) and (update_queue is not None)
|
||||||
|
|
||||||
|
def ch_callback_args(self, bot, update, args):
|
||||||
|
if update.message.text in par:
|
||||||
|
self.test_flag = len(args) == 0
|
||||||
|
else:
|
||||||
|
self.test_flag = args == ['one', 'two']
|
||||||
|
|
||||||
|
def callback_context(self, update, context):
|
||||||
|
self.test_flag = (isinstance(context, CallbackContext) and
|
||||||
|
isinstance(context.bot, Bot) and
|
||||||
|
isinstance(update, Update) and
|
||||||
|
isinstance(context.update_queue, Queue) and
|
||||||
|
isinstance(context.job_queue, JobQueue) and
|
||||||
|
isinstance(context.user_data, dict) and
|
||||||
|
isinstance(context.chat_data, dict) and
|
||||||
|
isinstance(update.message, Message))
|
||||||
|
|
||||||
|
def callback_context_args(self, update, context):
|
||||||
|
self.test_flag = context.args == ['one', 'two']
|
||||||
|
|
||||||
|
def test_basic(self, dp, prefixmessage):
|
||||||
|
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'], self.callback_basic)
|
||||||
|
dp.add_handler(handler)
|
||||||
|
|
||||||
|
dp.process_update(Update(0, prefixmessage))
|
||||||
|
assert self.test_flag
|
||||||
|
|
||||||
|
prefixmessage.text = 'test'
|
||||||
|
check = handler.check_update(Update(0, prefixmessage))
|
||||||
|
assert check is None or check is False
|
||||||
|
|
||||||
|
prefixmessage.text = '#nocom'
|
||||||
|
check = handler.check_update(Update(0, prefixmessage))
|
||||||
|
assert check is None or check is False
|
||||||
|
|
||||||
|
message.text = 'not !test at start'
|
||||||
|
check = handler.check_update(Update(0, message))
|
||||||
|
assert check is None or check is False
|
||||||
|
|
||||||
|
def test_single_prefix_single_command(self, prefixmessage):
|
||||||
|
handler = PrefixHandler('!', 'test', self.callback_basic)
|
||||||
|
|
||||||
|
check = handler.check_update(Update(0, prefixmessage))
|
||||||
|
if prefixmessage.text in ['!test']:
|
||||||
|
assert check is not None and check is not False
|
||||||
|
else:
|
||||||
|
assert check is None or check is False
|
||||||
|
|
||||||
|
def test_single_prefix_multi_command(self, prefixmessage):
|
||||||
|
handler = PrefixHandler('!', ['test', 'help'], self.callback_basic)
|
||||||
|
|
||||||
|
check = handler.check_update(Update(0, prefixmessage))
|
||||||
|
if prefixmessage.text in ['!test', '!help']:
|
||||||
|
assert check is not None and check is not False
|
||||||
|
else:
|
||||||
|
assert check is None or check is False
|
||||||
|
|
||||||
|
def test_multi_prefix_single_command(self, prefixmessage):
|
||||||
|
handler = PrefixHandler(['!', '#'], 'test', self.callback_basic)
|
||||||
|
|
||||||
|
check = handler.check_update(Update(0, prefixmessage))
|
||||||
|
if prefixmessage.text in ['!test', '#test']:
|
||||||
|
assert check is not None and check is not False
|
||||||
|
else:
|
||||||
|
assert check is None or check is False
|
||||||
|
|
||||||
|
def test_edited(self, prefixmessage):
|
||||||
|
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'], self.callback_basic)
|
||||||
|
|
||||||
|
check = handler.check_update(Update(0, prefixmessage))
|
||||||
|
assert check is not None and check is not False
|
||||||
|
|
||||||
|
check = handler.check_update(Update(0, edited_message=prefixmessage))
|
||||||
|
assert check is None or check is False
|
||||||
|
|
||||||
|
handler.allow_edited = True
|
||||||
|
check = handler.check_update(Update(0, prefixmessage))
|
||||||
|
assert check is not None and check is not False
|
||||||
|
|
||||||
|
check = handler.check_update(Update(0, edited_message=prefixmessage))
|
||||||
|
assert check is not None and check is not False
|
||||||
|
|
||||||
|
def test_with_filter(self, prefixmessage):
|
||||||
|
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'], self.callback_basic,
|
||||||
|
filters=Filters.group)
|
||||||
|
|
||||||
|
prefixmessage.chat = Chat(-23, 'group')
|
||||||
|
check = handler.check_update(Update(0, prefixmessage))
|
||||||
|
assert check is not None and check is not False
|
||||||
|
|
||||||
|
prefixmessage.chat = Chat(23, 'private')
|
||||||
|
check = handler.check_update(Update(0, prefixmessage))
|
||||||
|
assert check is None or check is False
|
||||||
|
|
||||||
|
def test_pass_args(self, dp, prefixmessage):
|
||||||
|
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'], self.ch_callback_args,
|
||||||
|
pass_args=True)
|
||||||
|
dp.add_handler(handler)
|
||||||
|
|
||||||
|
dp.process_update(Update(0, message=prefixmessage))
|
||||||
|
assert self.test_flag
|
||||||
|
|
||||||
|
self.test_flag = False
|
||||||
|
prefixmessage.text += ' one two'
|
||||||
|
dp.process_update(Update(0, message=prefixmessage))
|
||||||
|
assert self.test_flag
|
||||||
|
|
||||||
|
def test_pass_user_or_chat_data(self, dp, prefixmessage):
|
||||||
|
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'], self.callback_data_1,
|
||||||
|
pass_user_data=True)
|
||||||
|
dp.add_handler(handler)
|
||||||
|
|
||||||
|
dp.process_update(Update(0, message=prefixmessage))
|
||||||
|
assert self.test_flag
|
||||||
|
|
||||||
|
dp.remove_handler(handler)
|
||||||
|
self.test_flag = False
|
||||||
|
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'], self.callback_data_1,
|
||||||
|
pass_chat_data=True)
|
||||||
|
dp.add_handler(handler)
|
||||||
|
dp.process_update(Update(0, message=prefixmessage))
|
||||||
|
assert self.test_flag
|
||||||
|
|
||||||
|
dp.remove_handler(handler)
|
||||||
|
self.test_flag = False
|
||||||
|
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'], self.callback_data_2,
|
||||||
|
pass_chat_data=True, pass_user_data=True)
|
||||||
|
dp.add_handler(handler)
|
||||||
|
dp.process_update(Update(0, message=prefixmessage))
|
||||||
|
assert self.test_flag
|
||||||
|
|
||||||
|
def test_pass_job_or_update_queue(self, dp, prefixmessage):
|
||||||
|
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'], self.callback_queue_1,
|
||||||
|
pass_job_queue=True)
|
||||||
|
dp.add_handler(handler)
|
||||||
|
|
||||||
|
dp.process_update(Update(0, message=prefixmessage))
|
||||||
|
assert self.test_flag
|
||||||
|
|
||||||
|
dp.remove_handler(handler)
|
||||||
|
self.test_flag = False
|
||||||
|
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'], self.callback_queue_1,
|
||||||
|
pass_update_queue=True)
|
||||||
|
dp.add_handler(handler)
|
||||||
|
dp.process_update(Update(0, message=prefixmessage))
|
||||||
|
assert self.test_flag
|
||||||
|
|
||||||
|
dp.remove_handler(handler)
|
||||||
|
self.test_flag = False
|
||||||
|
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'], self.callback_queue_2,
|
||||||
|
pass_job_queue=True, pass_update_queue=True)
|
||||||
|
dp.add_handler(handler)
|
||||||
|
dp.process_update(Update(0, message=prefixmessage))
|
||||||
|
assert self.test_flag
|
||||||
|
|
||||||
|
def test_other_update_types(self, false_update):
|
||||||
|
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'], self.callback_basic)
|
||||||
|
check = handler.check_update(false_update)
|
||||||
|
assert check is None or check is False
|
||||||
|
|
||||||
|
def test_filters_for_wrong_command(self, prefixmessage):
|
||||||
|
"""Filters should not be executed if the command does not match the handler"""
|
||||||
|
|
||||||
|
class TestFilter(BaseFilter):
|
||||||
|
def __init__(self):
|
||||||
|
self.tested = False
|
||||||
|
|
||||||
|
def filter(self, message):
|
||||||
|
self.tested = True
|
||||||
|
|
||||||
|
test_filter = TestFilter()
|
||||||
|
|
||||||
|
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'], self.callback_basic,
|
||||||
|
filters=test_filter)
|
||||||
|
|
||||||
|
prefixmessage.text = '/star'
|
||||||
|
|
||||||
|
check = handler.check_update(Update(0, message=prefixmessage))
|
||||||
|
assert check is None or check is False
|
||||||
|
|
||||||
|
assert not test_filter.tested
|
||||||
|
|
||||||
|
def test_context(self, cdp, prefixmessage):
|
||||||
|
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'], self.callback_context)
|
||||||
|
cdp.add_handler(handler)
|
||||||
|
|
||||||
|
cdp.process_update(Update(0, prefixmessage))
|
||||||
|
assert self.test_flag
|
||||||
|
|
||||||
|
def test_context_args(self, cdp, prefixmessage):
|
||||||
|
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'],
|
||||||
|
self.callback_context_args)
|
||||||
|
cdp.add_handler(handler)
|
||||||
|
|
||||||
|
cdp.process_update(Update(0, prefixmessage))
|
||||||
|
assert not self.test_flag
|
||||||
|
|
||||||
|
prefixmessage.text += ' one two'
|
||||||
|
cdp.process_update(Update(0, prefixmessage))
|
||||||
|
assert self.test_flag
|
||||||
|
|
|
@ -22,7 +22,7 @@ from time import sleep
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from telegram import (CallbackQuery, Chat, ChosenInlineResult, InlineQuery, Message,
|
from telegram import (CallbackQuery, Chat, ChosenInlineResult, InlineQuery, Message,
|
||||||
PreCheckoutQuery, ShippingQuery, Update, User)
|
PreCheckoutQuery, ShippingQuery, Update, User, MessageEntity)
|
||||||
from telegram.ext import (ConversationHandler, CommandHandler, CallbackQueryHandler)
|
from telegram.ext import (ConversationHandler, CommandHandler, CallbackQueryHandler)
|
||||||
|
|
||||||
|
|
||||||
|
@ -103,22 +103,28 @@ class TestConversationHandler(object):
|
||||||
dp.add_handler(handler)
|
dp.add_handler(handler)
|
||||||
|
|
||||||
# User one, starts the state machine.
|
# User one, starts the state machine.
|
||||||
message = Message(0, user1, None, self.group, text='/start', bot=bot)
|
message = Message(0, user1, None, self.group, text='/start',
|
||||||
|
entities=[MessageEntity(type=MessageEntity.BOT_COMMAND,
|
||||||
|
offset=0, length=len('/start'))],
|
||||||
|
bot=bot)
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
assert self.current_state[user1.id] == self.THIRSTY
|
assert self.current_state[user1.id] == self.THIRSTY
|
||||||
|
|
||||||
# The user is thirsty and wants to brew coffee.
|
# The user is thirsty and wants to brew coffee.
|
||||||
message.text = '/brew'
|
message.text = '/brew'
|
||||||
|
message.entities[0].length = len('/brew')
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
assert self.current_state[user1.id] == self.BREWING
|
assert self.current_state[user1.id] == self.BREWING
|
||||||
|
|
||||||
# Lets see if an invalid command makes sure, no state is changed.
|
# Lets see if an invalid command makes sure, no state is changed.
|
||||||
message.text = '/nothing'
|
message.text = '/nothing'
|
||||||
|
message.entities[0].length = len('/nothing')
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
assert self.current_state[user1.id] == self.BREWING
|
assert self.current_state[user1.id] == self.BREWING
|
||||||
|
|
||||||
# Lets see if the state machine still works by pouring coffee.
|
# Lets see if the state machine still works by pouring coffee.
|
||||||
message.text = '/pourCoffee'
|
message.text = '/pourCoffee'
|
||||||
|
message.entities[0].length = len('/pourCoffee')
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
assert self.current_state[user1.id] == self.DRINKING
|
assert self.current_state[user1.id] == self.DRINKING
|
||||||
|
|
||||||
|
@ -134,13 +140,19 @@ class TestConversationHandler(object):
|
||||||
fallbacks=self.fallbacks)
|
fallbacks=self.fallbacks)
|
||||||
dp.add_handler(handler)
|
dp.add_handler(handler)
|
||||||
|
|
||||||
message = Message(0, user1, None, self.group, text='/start', bot=bot)
|
message = Message(0, user1, None, self.group, text='/start',
|
||||||
|
entities=[MessageEntity(type=MessageEntity.BOT_COMMAND,
|
||||||
|
offset=0, length=len('/start'))],
|
||||||
|
bot=bot)
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
message.text = '/brew'
|
message.text = '/brew'
|
||||||
|
message.entities[0].length = len('/brew')
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
message.text = '/pourCoffee'
|
message.text = '/pourCoffee'
|
||||||
|
message.entities[0].length = len('/pourCoffee')
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
message.text = '/end'
|
message.text = '/end'
|
||||||
|
message.entities[0].length = len('/end')
|
||||||
with caplog.at_level(logging.ERROR):
|
with caplog.at_level(logging.ERROR):
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
assert len(caplog.records) == 0
|
assert len(caplog.records) == 0
|
||||||
|
@ -154,23 +166,29 @@ class TestConversationHandler(object):
|
||||||
dp.add_handler(handler)
|
dp.add_handler(handler)
|
||||||
|
|
||||||
# first check if fallback will not trigger start when not started
|
# first check if fallback will not trigger start when not started
|
||||||
message = Message(0, user1, None, self.group, text='/eat', bot=bot)
|
message = Message(0, user1, None, self.group, text='/eat',
|
||||||
|
entities=[MessageEntity(type=MessageEntity.BOT_COMMAND,
|
||||||
|
offset=0, length=len('/eat'))],
|
||||||
|
bot=bot)
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
with pytest.raises(KeyError):
|
with pytest.raises(KeyError):
|
||||||
self.current_state[user1.id]
|
self.current_state[user1.id]
|
||||||
|
|
||||||
# User starts the state machine.
|
# User starts the state machine.
|
||||||
message.text = '/start'
|
message.text = '/start'
|
||||||
|
message.entities[0].length = len('/start')
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
assert self.current_state[user1.id] == self.THIRSTY
|
assert self.current_state[user1.id] == self.THIRSTY
|
||||||
|
|
||||||
# The user is thirsty and wants to brew coffee.
|
# The user is thirsty and wants to brew coffee.
|
||||||
message.text = '/brew'
|
message.text = '/brew'
|
||||||
|
message.entities[0].length = len('/brew')
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
assert self.current_state[user1.id] == self.BREWING
|
assert self.current_state[user1.id] == self.BREWING
|
||||||
|
|
||||||
# Now a fallback command is issued
|
# Now a fallback command is issued
|
||||||
message.text = '/eat'
|
message.text = '/eat'
|
||||||
|
message.entities[0].length = len('/eat')
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
assert self.current_state[user1.id] == self.THIRSTY
|
assert self.current_state[user1.id] == self.THIRSTY
|
||||||
|
|
||||||
|
@ -183,17 +201,22 @@ class TestConversationHandler(object):
|
||||||
dp.add_handler(handler)
|
dp.add_handler(handler)
|
||||||
|
|
||||||
# User one, starts the state machine.
|
# User one, starts the state machine.
|
||||||
message = Message(0, user1, None, self.group, text='/start', bot=bot)
|
message = Message(0, user1, None, self.group, text='/start',
|
||||||
|
entities=[MessageEntity(type=MessageEntity.BOT_COMMAND,
|
||||||
|
offset=0, length=len('/start'))],
|
||||||
|
bot=bot)
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
|
|
||||||
# The user is thirsty and wants to brew coffee.
|
# The user is thirsty and wants to brew coffee.
|
||||||
message.text = '/brew'
|
message.text = '/brew'
|
||||||
|
message.entities[0].length = len('/brew')
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
|
|
||||||
# Let's now verify that for another user, who did not start yet,
|
# Let's now verify that for another user, who did not start yet,
|
||||||
# the state will be changed because they are in the same group.
|
# the state will be changed because they are in the same group.
|
||||||
message.from_user = user2
|
message.from_user = user2
|
||||||
message.text = '/pourCoffee'
|
message.text = '/pourCoffee'
|
||||||
|
message.entities[0].length = len('/pourCoffee')
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
|
|
||||||
assert handler.conversations[(self.group.id,)] == self.DRINKING
|
assert handler.conversations[(self.group.id,)] == self.DRINKING
|
||||||
|
@ -207,17 +230,22 @@ class TestConversationHandler(object):
|
||||||
dp.add_handler(handler)
|
dp.add_handler(handler)
|
||||||
|
|
||||||
# User one, starts the state machine.
|
# User one, starts the state machine.
|
||||||
message = Message(0, user1, None, self.group, text='/start', bot=bot)
|
message = Message(0, user1, None, self.group, text='/start',
|
||||||
|
entities=[MessageEntity(type=MessageEntity.BOT_COMMAND,
|
||||||
|
offset=0, length=len('/start'))],
|
||||||
|
bot=bot)
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
|
|
||||||
# The user is thirsty and wants to brew coffee.
|
# The user is thirsty and wants to brew coffee.
|
||||||
message.text = '/brew'
|
message.text = '/brew'
|
||||||
|
message.entities[0].length = len('/brew')
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
|
|
||||||
# Let's now verify that for the same user in a different group, the state will still be
|
# Let's now verify that for the same user in a different group, the state will still be
|
||||||
# updated
|
# updated
|
||||||
message.chat = self.second_group
|
message.chat = self.second_group
|
||||||
message.text = '/pourCoffee'
|
message.text = '/pourCoffee'
|
||||||
|
message.entities[0].length = len('/pourCoffee')
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
|
|
||||||
assert handler.conversations[(user1.id,)] == self.DRINKING
|
assert handler.conversations[(user1.id,)] == self.DRINKING
|
||||||
|
@ -266,7 +294,10 @@ class TestConversationHandler(object):
|
||||||
dp.add_handler(handler)
|
dp.add_handler(handler)
|
||||||
|
|
||||||
# User starts the state machine and immediately ends it.
|
# User starts the state machine and immediately ends it.
|
||||||
message = Message(0, user1, None, self.group, text='/start', bot=bot)
|
message = Message(0, user1, None, self.group, text='/start',
|
||||||
|
entities=[MessageEntity(type=MessageEntity.BOT_COMMAND,
|
||||||
|
offset=0, length=len('/start'))],
|
||||||
|
bot=bot)
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
assert len(handler.conversations) == 0
|
assert len(handler.conversations) == 0
|
||||||
|
|
||||||
|
@ -280,13 +311,17 @@ class TestConversationHandler(object):
|
||||||
|
|
||||||
# User starts the state machine with an async function that immediately ends the
|
# User starts the state machine with an async function that immediately ends the
|
||||||
# conversation. Async results are resolved when the users state is queried next time.
|
# conversation. Async results are resolved when the users state is queried next time.
|
||||||
message = Message(0, user1, None, self.group, text='/start', bot=bot)
|
message = Message(0, user1, None, self.group, text='/start',
|
||||||
|
entities=[MessageEntity(type=MessageEntity.BOT_COMMAND,
|
||||||
|
offset=0, length=len('/start'))],
|
||||||
|
bot=bot)
|
||||||
dp.update_queue.put(Update(update_id=0, message=message))
|
dp.update_queue.put(Update(update_id=0, message=message))
|
||||||
sleep(.1)
|
sleep(.1)
|
||||||
# Assert that the Promise has been accepted as the new state
|
# Assert that the Promise has been accepted as the new state
|
||||||
assert len(handler.conversations) == 1
|
assert len(handler.conversations) == 1
|
||||||
|
|
||||||
message.text = 'resolve promise pls'
|
message.text = 'resolve promise pls'
|
||||||
|
message.entities[0].length = len('resolve promise pls')
|
||||||
dp.update_queue.put(Update(update_id=0, message=message))
|
dp.update_queue.put(Update(update_id=0, message=message))
|
||||||
sleep(.1)
|
sleep(.1)
|
||||||
# Assert that the Promise has been resolved and the conversation ended.
|
# Assert that the Promise has been resolved and the conversation ended.
|
||||||
|
@ -329,7 +364,10 @@ class TestConversationHandler(object):
|
||||||
dp.add_handler(handler)
|
dp.add_handler(handler)
|
||||||
|
|
||||||
# Start state machine, then reach timeout
|
# Start state machine, then reach timeout
|
||||||
message = Message(0, user1, None, self.group, text='/start', bot=bot)
|
message = Message(0, user1, None, self.group, text='/start',
|
||||||
|
entities=[MessageEntity(type=MessageEntity.BOT_COMMAND,
|
||||||
|
offset=0, length=len('/start'))],
|
||||||
|
bot=bot)
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY
|
assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY
|
||||||
sleep(0.5)
|
sleep(0.5)
|
||||||
|
@ -340,6 +378,7 @@ class TestConversationHandler(object):
|
||||||
dp.process_update(Update(update_id=1, message=message))
|
dp.process_update(Update(update_id=1, message=message))
|
||||||
assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY
|
assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY
|
||||||
message.text = '/brew'
|
message.text = '/brew'
|
||||||
|
message.entities[0].length = len('/brew')
|
||||||
dp.job_queue.tick()
|
dp.job_queue.tick()
|
||||||
dp.process_update(Update(update_id=2, message=message))
|
dp.process_update(Update(update_id=2, message=message))
|
||||||
assert handler.conversations.get((self.group.id, user1.id)) == self.BREWING
|
assert handler.conversations.get((self.group.id, user1.id)) == self.BREWING
|
||||||
|
@ -359,19 +398,24 @@ class TestConversationHandler(object):
|
||||||
# t=.6 /pourCoffee (timeout=1.1)
|
# t=.6 /pourCoffee (timeout=1.1)
|
||||||
# t=.75 second timeout
|
# t=.75 second timeout
|
||||||
# t=1.1 actual timeout
|
# t=1.1 actual timeout
|
||||||
message = Message(0, user1, None, self.group, text='/start', bot=bot)
|
message = Message(0, user1, None, self.group, text='/start',
|
||||||
|
entities=[MessageEntity(type=MessageEntity.BOT_COMMAND,
|
||||||
|
offset=0, length=len('/start'))],
|
||||||
|
bot=bot)
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY
|
assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY
|
||||||
sleep(0.25) # t=.25
|
sleep(0.25) # t=.25
|
||||||
dp.job_queue.tick()
|
dp.job_queue.tick()
|
||||||
assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY
|
assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY
|
||||||
message.text = '/brew'
|
message.text = '/brew'
|
||||||
|
message.entities[0].length = len('/brew')
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
assert handler.conversations.get((self.group.id, user1.id)) == self.BREWING
|
assert handler.conversations.get((self.group.id, user1.id)) == self.BREWING
|
||||||
sleep(0.35) # t=.6
|
sleep(0.35) # t=.6
|
||||||
dp.job_queue.tick()
|
dp.job_queue.tick()
|
||||||
assert handler.conversations.get((self.group.id, user1.id)) == self.BREWING
|
assert handler.conversations.get((self.group.id, user1.id)) == self.BREWING
|
||||||
message.text = '/pourCoffee'
|
message.text = '/pourCoffee'
|
||||||
|
message.entities[0].length = len('/pourCoffee')
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
assert handler.conversations.get((self.group.id, user1.id)) == self.DRINKING
|
assert handler.conversations.get((self.group.id, user1.id)) == self.DRINKING
|
||||||
sleep(.4) # t=1
|
sleep(.4) # t=1
|
||||||
|
@ -387,15 +431,21 @@ class TestConversationHandler(object):
|
||||||
dp.add_handler(handler)
|
dp.add_handler(handler)
|
||||||
|
|
||||||
# Start state machine, do something as second user, then reach timeout
|
# Start state machine, do something as second user, then reach timeout
|
||||||
message = Message(0, user1, None, self.group, text='/start', bot=bot)
|
message = Message(0, user1, None, self.group, text='/start',
|
||||||
|
entities=[MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0,
|
||||||
|
length=len('/start'))],
|
||||||
|
bot=bot)
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY
|
assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY
|
||||||
message.text = '/brew'
|
message.text = '/brew'
|
||||||
|
message.entities[0].length = len('/brew')
|
||||||
|
message.entities[0].length = len('/brew')
|
||||||
message.from_user = user2
|
message.from_user = user2
|
||||||
dp.job_queue.tick()
|
dp.job_queue.tick()
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
assert handler.conversations.get((self.group.id, user2.id)) is None
|
assert handler.conversations.get((self.group.id, user2.id)) is None
|
||||||
message.text = '/start'
|
message.text = '/start'
|
||||||
|
message.entities[0].length = len('/start')
|
||||||
dp.job_queue.tick()
|
dp.job_queue.tick()
|
||||||
dp.process_update(Update(update_id=0, message=message))
|
dp.process_update(Update(update_id=0, message=message))
|
||||||
assert handler.conversations.get((self.group.id, user2.id)) == self.THIRSTY
|
assert handler.conversations.get((self.group.id, user2.id)) == self.THIRSTY
|
||||||
|
|
|
@ -23,7 +23,7 @@ from time import sleep
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from telegram import TelegramError, Message, User, Chat, Update, Bot
|
from telegram import TelegramError, Message, User, Chat, Update, Bot, MessageEntity
|
||||||
from telegram.ext import MessageHandler, Filters, CommandHandler, CallbackContext, JobQueue
|
from telegram.ext import MessageHandler, Filters, CommandHandler, CallbackContext, JobQueue
|
||||||
from telegram.ext.dispatcher import run_async, Dispatcher, DispatcherHandlerStop
|
from telegram.ext.dispatcher import run_async, Dispatcher, DispatcherHandlerStop
|
||||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||||
|
@ -227,7 +227,11 @@ class TestDispatcher(object):
|
||||||
passed.append('error')
|
passed.append('error')
|
||||||
passed.append(e)
|
passed.append(e)
|
||||||
|
|
||||||
update = Update(1, message=Message(1, None, None, None, text='/start', bot=bot))
|
update = Update(1, message=Message(1, None, None, None, text='/start',
|
||||||
|
entities=[MessageEntity(type=MessageEntity.BOT_COMMAND,
|
||||||
|
offset=0,
|
||||||
|
length=len('/start'))],
|
||||||
|
bot=bot))
|
||||||
|
|
||||||
# If Stop raised handlers in other groups should not be called.
|
# If Stop raised handlers in other groups should not be called.
|
||||||
passed = []
|
passed = []
|
||||||
|
@ -254,7 +258,11 @@ class TestDispatcher(object):
|
||||||
passed.append('error')
|
passed.append('error')
|
||||||
passed.append(e)
|
passed.append(e)
|
||||||
|
|
||||||
update = Update(1, message=Message(1, None, None, None, text='/start', bot=bot))
|
update = Update(1, message=Message(1, None, None, None, text='/start',
|
||||||
|
entities=[MessageEntity(type=MessageEntity.BOT_COMMAND,
|
||||||
|
offset=0,
|
||||||
|
length=len('/start'))],
|
||||||
|
bot=bot))
|
||||||
|
|
||||||
# If an unhandled exception was caught, no further handlers from the same group should be
|
# If an unhandled exception was caught, no further handlers from the same group should be
|
||||||
# called.
|
# called.
|
||||||
|
@ -284,7 +292,11 @@ class TestDispatcher(object):
|
||||||
passed.append('error')
|
passed.append('error')
|
||||||
passed.append(e)
|
passed.append(e)
|
||||||
|
|
||||||
update = Update(1, message=Message(1, None, None, None, text='/start', bot=bot))
|
update = Update(1, message=Message(1, None, None, None, text='/start',
|
||||||
|
entities=[MessageEntity(type=MessageEntity.BOT_COMMAND,
|
||||||
|
offset=0,
|
||||||
|
length=len('/start'))],
|
||||||
|
bot=bot))
|
||||||
|
|
||||||
# If a TelegramException was caught, an error handler should be called and no further
|
# If a TelegramException was caught, an error handler should be called and no further
|
||||||
# handlers from the same group should be called.
|
# handlers from the same group should be called.
|
||||||
|
@ -315,7 +327,11 @@ class TestDispatcher(object):
|
||||||
passed.append(e)
|
passed.append(e)
|
||||||
raise DispatcherHandlerStop
|
raise DispatcherHandlerStop
|
||||||
|
|
||||||
update = Update(1, message=Message(1, None, None, None, text='/start', bot=bot))
|
update = Update(1, message=Message(1, None, None, None, text='/start',
|
||||||
|
entities=[MessageEntity(type=MessageEntity.BOT_COMMAND,
|
||||||
|
offset=0,
|
||||||
|
length=len('/start'))],
|
||||||
|
bot=bot))
|
||||||
|
|
||||||
# If a TelegramException was caught, an error handler should be called and no further
|
# If a TelegramException was caught, an error handler should be called and no further
|
||||||
# handlers from the same group should be called.
|
# handlers from the same group should be called.
|
||||||
|
|
Loading…
Reference in a new issue