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.messagehandler
|
||||
telegram.ext.precheckoutqueryhandler
|
||||
telegram.ext.prefixhandler
|
||||
telegram.ext.regexhandler
|
||||
telegram.ext.shippingqueryhandler
|
||||
telegram.ext.stringcommandhandler
|
||||
|
|
|
@ -25,7 +25,7 @@ from .jobqueue import JobQueue, Job
|
|||
from .updater import Updater
|
||||
from .callbackqueryhandler import CallbackQueryHandler
|
||||
from .choseninlineresulthandler import ChosenInlineResultHandler
|
||||
from .commandhandler import CommandHandler
|
||||
from .commandhandler import CommandHandler, PrefixHandler
|
||||
from .inlinequeryhandler import InlineQueryHandler
|
||||
from .messagehandler import MessageHandler
|
||||
from .filters import BaseFilter, Filters
|
||||
|
@ -44,4 +44,4 @@ __all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler',
|
|||
'MessageHandler', 'BaseFilter', 'Filters', 'RegexHandler', 'StringCommandHandler',
|
||||
'StringRegexHandler', 'TypeHandler', 'ConversationHandler',
|
||||
'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
|
||||
``re.match(pattern, string)``.
|
||||
args (List[:obj:`str`], optional): Arguments passed to a command if the associated update
|
||||
is handled by :class:`telegram.ext.CommandHandler` or
|
||||
:class:`telegram.ext.StringCommandHandler`. It contains a list of the words in the text
|
||||
after the command, using any whitespace string as a delimiter.
|
||||
is handled by :class:`telegram.ext.CommandHandler`, :class:`telegram.ext.PrefixHandler`
|
||||
or :class:`telegram.ext.StringCommandHandler`. It contains a list of the words in the
|
||||
text after the command, using any whitespace string as a delimiter.
|
||||
error (:class:`telegram.TelegramError`, optional): The Telegram error that was raised.
|
||||
Only present when passed to a error handler registered with
|
||||
:attr:`telegram.ext.Dispatcher.add_error_handler`.
|
||||
|
|
|
@ -50,7 +50,7 @@ class CallbackQueryHandler(Handler):
|
|||
|
||||
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
|
||||
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``.
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ class ChosenInlineResultHandler(Handler):
|
|||
|
||||
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
|
||||
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``.
|
||||
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# 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 telegram import Update
|
||||
from telegram import Update, MessageEntity
|
||||
from .handler import Handler
|
||||
|
||||
|
||||
|
@ -27,9 +29,177 @@ class CommandHandler(Handler):
|
|||
"""Handler class to handle Telegram commands.
|
||||
|
||||
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:
|
||||
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
|
||||
should listen for.
|
||||
callback (:obj:`callable`): The callback function for this handler.
|
||||
|
@ -50,7 +220,7 @@ class CommandHandler(Handler):
|
|||
|
||||
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
|
||||
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``.
|
||||
|
||||
|
@ -58,6 +228,7 @@ class CommandHandler(Handler):
|
|||
https://git.io/vp113 for more info.
|
||||
|
||||
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
|
||||
should listen for.
|
||||
callback (:obj:`callable`): The callback function for this handler. Will be called when
|
||||
|
@ -99,6 +270,7 @@ class CommandHandler(Handler):
|
|||
"""
|
||||
|
||||
def __init__(self,
|
||||
prefix,
|
||||
command,
|
||||
callback,
|
||||
filters=None,
|
||||
|
@ -108,20 +280,23 @@ class CommandHandler(Handler):
|
|||
pass_job_queue=False,
|
||||
pass_user_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_job_queue=pass_job_queue,
|
||||
pass_user_data=pass_user_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):
|
||||
self.command = [command.lower()]
|
||||
else:
|
||||
self.command = [x.lower() for x in command]
|
||||
self.filters = filters
|
||||
self.allow_edited = allow_edited
|
||||
self.pass_args = pass_args
|
||||
self.command = command
|
||||
self.command = [x.lower() + y.lower() for x in self.prefix for y in self.command]
|
||||
|
||||
def check_update(self, update):
|
||||
"""Determines whether an update should be passed to this handlers :attr:`callback`.
|
||||
|
@ -135,27 +310,10 @@ class CommandHandler(Handler):
|
|||
"""
|
||||
if (isinstance(update, Update) and
|
||||
(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:
|
||||
first_word = message.text_html.split(None, 1)[0]
|
||||
if len(first_word) > 1 and first_word.startswith('/'):
|
||||
command = first_word[1:].split('@')
|
||||
command.append(
|
||||
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
|
||||
text_list = message.text.split()
|
||||
if text_list[0].lower() not in self.command:
|
||||
return None
|
||||
if self.filters is None or self.filters(message):
|
||||
return text_list[1:]
|
||||
|
|
|
@ -36,7 +36,7 @@ class Handler(object):
|
|||
|
||||
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
|
||||
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``.
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ class InlineQueryHandler(Handler):
|
|||
|
||||
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
|
||||
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``.
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ class MessageHandler(Handler):
|
|||
|
||||
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
|
||||
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``.
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ class PreCheckoutQueryHandler(Handler):
|
|||
|
||||
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
|
||||
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``.
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ class RegexHandler(Handler):
|
|||
|
||||
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
|
||||
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``.
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ class ShippingQueryHandler(Handler):
|
|||
|
||||
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
|
||||
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``.
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2018
|
||||
|
@ -21,8 +22,9 @@ from queue import Queue
|
|||
import pytest
|
||||
|
||||
from telegram import (Message, Update, Chat, Bot, User, CallbackQuery, InlineQuery,
|
||||
ChosenInlineResult, ShippingQuery, PreCheckoutQuery)
|
||||
from telegram.ext import CommandHandler, Filters, BaseFilter, CallbackContext, JobQueue
|
||||
ChosenInlineResult, ShippingQuery, PreCheckoutQuery, MessageEntity)
|
||||
from telegram.ext import CommandHandler, Filters, BaseFilter, CallbackContext, JobQueue, \
|
||||
PrefixHandler
|
||||
|
||||
message = Message(1, User(1, '', False), None, Chat(1, ''), text='test')
|
||||
|
||||
|
@ -49,7 +51,15 @@ def false_update(request):
|
|||
|
||||
@pytest.fixture(scope='function')
|
||||
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):
|
||||
|
@ -117,13 +127,26 @@ class TestCommandHandler(object):
|
|||
check = handler.check_update(Update(0, message))
|
||||
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):
|
||||
handler = CommandHandler(['test', 'start'], self.callback_basic)
|
||||
handler = CommandHandler(['test', 'star'], self.callback_basic)
|
||||
|
||||
message.text = '/test'
|
||||
check = handler.check_update(Update(0, message))
|
||||
|
||||
message.text = '/start'
|
||||
message.text = '/star'
|
||||
check = handler.check_update(Update(0, message))
|
||||
|
||||
message.text = '/stop'
|
||||
|
@ -137,11 +160,14 @@ class TestCommandHandler(object):
|
|||
message.text = '/test'
|
||||
check = handler.check_update(Update(0, message))
|
||||
assert check is not None and check is not False
|
||||
|
||||
check = handler.check_update(Update(0, edited_message=message))
|
||||
assert check is None or check is False
|
||||
|
||||
handler.allow_edited = True
|
||||
check = handler.check_update(Update(0, message))
|
||||
assert check is not None and check is not False
|
||||
|
||||
check = handler.check_update(Update(0, edited_message=message))
|
||||
assert check is not None and check is not False
|
||||
|
||||
|
@ -149,11 +175,13 @@ class TestCommandHandler(object):
|
|||
handler = CommandHandler('test', self.callback_basic)
|
||||
|
||||
message.text = '/test@{}'.format(message.bot.username)
|
||||
message.entities[0].length = len(message.text)
|
||||
check = handler.check_update(Update(0, message))
|
||||
assert check is not None and check is not False
|
||||
|
||||
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):
|
||||
handler = CommandHandler('test', self.callback_basic, Filters.group)
|
||||
|
@ -177,11 +205,7 @@ class TestCommandHandler(object):
|
|||
|
||||
self.test_flag = False
|
||||
message.text = '/test@{}'.format(message.bot.username)
|
||||
dp.process_update(Update(0, message=message))
|
||||
assert self.test_flag
|
||||
|
||||
self.test_flag = False
|
||||
message.text = '/test one two'
|
||||
message.entities[0].length = len(message.text)
|
||||
dp.process_update(Update(0, message=message))
|
||||
assert self.test_flag
|
||||
|
||||
|
@ -190,6 +214,12 @@ class TestCommandHandler(object):
|
|||
dp.process_update(Update(0, message=message))
|
||||
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):
|
||||
handler = CommandHandler('test', self.callback_basic)
|
||||
dp.add_handler(handler)
|
||||
|
@ -197,31 +227,10 @@ class TestCommandHandler(object):
|
|||
message.text = '/test\nfoobar'
|
||||
check = handler.check_update(Update(0, message))
|
||||
assert check is not None and check is not False
|
||||
|
||||
dp.process_update(Update(0, message))
|
||||
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):
|
||||
handler = CommandHandler('test', self.callback_data_1,
|
||||
pass_user_data=True)
|
||||
|
@ -295,9 +304,9 @@ class TestCommandHandler(object):
|
|||
|
||||
test_filter = TestFilter()
|
||||
|
||||
handler = CommandHandler('foo', self.callback_basic,
|
||||
handler = CommandHandler('test', self.callback_basic,
|
||||
filters=test_filter)
|
||||
message.text = '/bar'
|
||||
message.text = '/star'
|
||||
|
||||
check = handler.check_update(Update(0, message=message))
|
||||
assert check is None or check is False
|
||||
|
@ -323,3 +332,241 @@ class TestCommandHandler(object):
|
|||
message.text = '/test one two'
|
||||
cdp.process_update(Update(0, message))
|
||||
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
|
||||
|
||||
from telegram import (CallbackQuery, Chat, ChosenInlineResult, InlineQuery, Message,
|
||||
PreCheckoutQuery, ShippingQuery, Update, User)
|
||||
PreCheckoutQuery, ShippingQuery, Update, User, MessageEntity)
|
||||
from telegram.ext import (ConversationHandler, CommandHandler, CallbackQueryHandler)
|
||||
|
||||
|
||||
|
@ -103,22 +103,28 @@ class TestConversationHandler(object):
|
|||
dp.add_handler(handler)
|
||||
|
||||
# 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))
|
||||
assert self.current_state[user1.id] == self.THIRSTY
|
||||
|
||||
# The user is thirsty and wants to brew coffee.
|
||||
message.text = '/brew'
|
||||
message.entities[0].length = len('/brew')
|
||||
dp.process_update(Update(update_id=0, message=message))
|
||||
assert self.current_state[user1.id] == self.BREWING
|
||||
|
||||
# Lets see if an invalid command makes sure, no state is changed.
|
||||
message.text = '/nothing'
|
||||
message.entities[0].length = len('/nothing')
|
||||
dp.process_update(Update(update_id=0, message=message))
|
||||
assert self.current_state[user1.id] == self.BREWING
|
||||
|
||||
# Lets see if the state machine still works by pouring coffee.
|
||||
message.text = '/pourCoffee'
|
||||
message.entities[0].length = len('/pourCoffee')
|
||||
dp.process_update(Update(update_id=0, message=message))
|
||||
assert self.current_state[user1.id] == self.DRINKING
|
||||
|
||||
|
@ -134,13 +140,19 @@ class TestConversationHandler(object):
|
|||
fallbacks=self.fallbacks)
|
||||
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))
|
||||
message.text = '/brew'
|
||||
message.entities[0].length = len('/brew')
|
||||
dp.process_update(Update(update_id=0, message=message))
|
||||
message.text = '/pourCoffee'
|
||||
message.entities[0].length = len('/pourCoffee')
|
||||
dp.process_update(Update(update_id=0, message=message))
|
||||
message.text = '/end'
|
||||
message.entities[0].length = len('/end')
|
||||
with caplog.at_level(logging.ERROR):
|
||||
dp.process_update(Update(update_id=0, message=message))
|
||||
assert len(caplog.records) == 0
|
||||
|
@ -154,23 +166,29 @@ class TestConversationHandler(object):
|
|||
dp.add_handler(handler)
|
||||
|
||||
# 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))
|
||||
with pytest.raises(KeyError):
|
||||
self.current_state[user1.id]
|
||||
|
||||
# User starts the state machine.
|
||||
message.text = '/start'
|
||||
message.entities[0].length = len('/start')
|
||||
dp.process_update(Update(update_id=0, message=message))
|
||||
assert self.current_state[user1.id] == self.THIRSTY
|
||||
|
||||
# The user is thirsty and wants to brew coffee.
|
||||
message.text = '/brew'
|
||||
message.entities[0].length = len('/brew')
|
||||
dp.process_update(Update(update_id=0, message=message))
|
||||
assert self.current_state[user1.id] == self.BREWING
|
||||
|
||||
# Now a fallback command is issued
|
||||
message.text = '/eat'
|
||||
message.entities[0].length = len('/eat')
|
||||
dp.process_update(Update(update_id=0, message=message))
|
||||
assert self.current_state[user1.id] == self.THIRSTY
|
||||
|
||||
|
@ -183,17 +201,22 @@ class TestConversationHandler(object):
|
|||
dp.add_handler(handler)
|
||||
|
||||
# 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))
|
||||
|
||||
# The user is thirsty and wants to brew coffee.
|
||||
message.text = '/brew'
|
||||
message.entities[0].length = len('/brew')
|
||||
dp.process_update(Update(update_id=0, message=message))
|
||||
|
||||
# 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.
|
||||
message.from_user = user2
|
||||
message.text = '/pourCoffee'
|
||||
message.entities[0].length = len('/pourCoffee')
|
||||
dp.process_update(Update(update_id=0, message=message))
|
||||
|
||||
assert handler.conversations[(self.group.id,)] == self.DRINKING
|
||||
|
@ -207,17 +230,22 @@ class TestConversationHandler(object):
|
|||
dp.add_handler(handler)
|
||||
|
||||
# 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))
|
||||
|
||||
# The user is thirsty and wants to brew coffee.
|
||||
message.text = '/brew'
|
||||
message.entities[0].length = len('/brew')
|
||||
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
|
||||
# updated
|
||||
message.chat = self.second_group
|
||||
message.text = '/pourCoffee'
|
||||
message.entities[0].length = len('/pourCoffee')
|
||||
dp.process_update(Update(update_id=0, message=message))
|
||||
|
||||
assert handler.conversations[(user1.id,)] == self.DRINKING
|
||||
|
@ -266,7 +294,10 @@ class TestConversationHandler(object):
|
|||
dp.add_handler(handler)
|
||||
|
||||
# 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))
|
||||
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
|
||||
# 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))
|
||||
sleep(.1)
|
||||
# Assert that the Promise has been accepted as the new state
|
||||
assert len(handler.conversations) == 1
|
||||
|
||||
message.text = 'resolve promise pls'
|
||||
message.entities[0].length = len('resolve promise pls')
|
||||
dp.update_queue.put(Update(update_id=0, message=message))
|
||||
sleep(.1)
|
||||
# Assert that the Promise has been resolved and the conversation ended.
|
||||
|
@ -329,7 +364,10 @@ class TestConversationHandler(object):
|
|||
dp.add_handler(handler)
|
||||
|
||||
# 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))
|
||||
assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY
|
||||
sleep(0.5)
|
||||
|
@ -340,6 +378,7 @@ class TestConversationHandler(object):
|
|||
dp.process_update(Update(update_id=1, message=message))
|
||||
assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY
|
||||
message.text = '/brew'
|
||||
message.entities[0].length = len('/brew')
|
||||
dp.job_queue.tick()
|
||||
dp.process_update(Update(update_id=2, message=message))
|
||||
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=.75 second 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))
|
||||
assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY
|
||||
sleep(0.25) # t=.25
|
||||
dp.job_queue.tick()
|
||||
assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY
|
||||
message.text = '/brew'
|
||||
message.entities[0].length = len('/brew')
|
||||
dp.process_update(Update(update_id=0, message=message))
|
||||
assert handler.conversations.get((self.group.id, user1.id)) == self.BREWING
|
||||
sleep(0.35) # t=.6
|
||||
dp.job_queue.tick()
|
||||
assert handler.conversations.get((self.group.id, user1.id)) == self.BREWING
|
||||
message.text = '/pourCoffee'
|
||||
message.entities[0].length = len('/pourCoffee')
|
||||
dp.process_update(Update(update_id=0, message=message))
|
||||
assert handler.conversations.get((self.group.id, user1.id)) == self.DRINKING
|
||||
sleep(.4) # t=1
|
||||
|
@ -387,15 +431,21 @@ class TestConversationHandler(object):
|
|||
dp.add_handler(handler)
|
||||
|
||||
# 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))
|
||||
assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY
|
||||
message.text = '/brew'
|
||||
message.entities[0].length = len('/brew')
|
||||
message.entities[0].length = len('/brew')
|
||||
message.from_user = user2
|
||||
dp.job_queue.tick()
|
||||
dp.process_update(Update(update_id=0, message=message))
|
||||
assert handler.conversations.get((self.group.id, user2.id)) is None
|
||||
message.text = '/start'
|
||||
message.entities[0].length = len('/start')
|
||||
dp.job_queue.tick()
|
||||
dp.process_update(Update(update_id=0, message=message))
|
||||
assert handler.conversations.get((self.group.id, user2.id)) == self.THIRSTY
|
||||
|
|
|
@ -23,7 +23,7 @@ from time import sleep
|
|||
|
||||
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.dispatcher import run_async, Dispatcher, DispatcherHandlerStop
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
|
@ -227,7 +227,11 @@ class TestDispatcher(object):
|
|||
passed.append('error')
|
||||
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.
|
||||
passed = []
|
||||
|
@ -254,7 +258,11 @@ class TestDispatcher(object):
|
|||
passed.append('error')
|
||||
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
|
||||
# called.
|
||||
|
@ -284,7 +292,11 @@ class TestDispatcher(object):
|
|||
passed.append('error')
|
||||
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
|
||||
# handlers from the same group should be called.
|
||||
|
@ -315,7 +327,11 @@ class TestDispatcher(object):
|
|||
passed.append(e)
|
||||
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
|
||||
# handlers from the same group should be called.
|
||||
|
|
Loading…
Reference in a new issue