diff --git a/telegram/ext/conversationhandler.py b/telegram/ext/conversationhandler.py index a31809a70..13775f928 100644 --- a/telegram/ext/conversationhandler.py +++ b/telegram/ext/conversationhandler.py @@ -393,8 +393,10 @@ class ConversationHandler(Handler): self.logger.debug('conversation timeout was triggered!') # Backward compatibility with bots that do not use CallbackContext + callback_context = None if isinstance(context, CallbackContext): job = context.job + callback_context = context context = job.context @@ -409,5 +411,5 @@ class ConversationHandler(Handler): for handler in handlers: check = handler.check_update(context.update) if check is not None and check is not False: - handler.handle_update(context.update, context.dispatcher, check) + handler.handle_update(context.update, context.dispatcher, check, callback_context) self.update_state(self.END, context.conversation_key) diff --git a/tests/test_conversationhandler.py b/tests/test_conversationhandler.py index d82d10cbe..5480ea369 100644 --- a/tests/test_conversationhandler.py +++ b/tests/test_conversationhandler.py @@ -24,7 +24,7 @@ import pytest from telegram import (CallbackQuery, Chat, ChosenInlineResult, InlineQuery, Message, PreCheckoutQuery, ShippingQuery, Update, User, MessageEntity) from telegram.ext import (ConversationHandler, CommandHandler, CallbackQueryHandler, - MessageHandler, Filters, InlineQueryHandler) + MessageHandler, Filters, InlineQueryHandler, CallbackContext) @pytest.fixture(scope='class') @@ -117,7 +117,10 @@ class TestConversationHandler(object): # Actions def start(self, bot, update): - return self._set_state(update, self.THIRSTY) + if isinstance(update, Update): + return self._set_state(update, self.THIRSTY) + else: + return self._set_state(bot, self.THIRSTY) def end(self, bot, update): return self._set_state(update, self.END) @@ -129,7 +132,10 @@ class TestConversationHandler(object): return self._set_state(update, None) def brew(self, bot, update): - return self._set_state(update, self.BREWING) + if isinstance(update, Update): + return self._set_state(update, self.BREWING) + else: + return self._set_state(bot, self.BREWING) def drink(self, bot, update): return self._set_state(update, self.DRINKING) @@ -139,9 +145,20 @@ class TestConversationHandler(object): def passout(self, bot, update): assert update.message.text == '/brew' + assert isinstance(update, Update) self.is_timeout = True def passout2(self, bot, update): + assert isinstance(update, Update) + self.is_timeout = True + + def passout_context(self, update, context): + assert update.message.text == '/brew' + assert isinstance(context, CallbackContext) + self.is_timeout = True + + def passout2_context(self, update, context): + assert isinstance(context, CallbackContext) self.is_timeout = True # Drinking actions (nested) @@ -613,6 +630,54 @@ class TestConversationHandler(object): assert handler.conversations.get((self.group.id, user1.id)) is None assert not self.is_timeout + def test_conversation_handler_timeout_state_context(self, cdp, bot, user1): + states = self.states + states.update({ConversationHandler.TIMEOUT: [ + CommandHandler('brew', self.passout_context), + MessageHandler(~Filters.regex('oding'), self.passout2_context) + ]}) + handler = ConversationHandler(entry_points=self.entry_points, states=states, + fallbacks=self.fallbacks, conversation_timeout=0.5) + cdp.add_handler(handler) + + # CommandHandler timeout + message = Message(0, user1, None, self.group, text='/start', + entities=[MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, + length=len('/start'))], + bot=bot) + cdp.process_update(Update(update_id=0, message=message)) + message.text = '/brew' + message.entities[0].length = len('/brew') + cdp.process_update(Update(update_id=0, message=message)) + sleep(0.5) + cdp.job_queue.tick() + assert handler.conversations.get((self.group.id, user1.id)) is None + assert self.is_timeout + + # MessageHandler timeout + self.is_timeout = False + message.text = '/start' + message.entities[0].length = len('/start') + cdp.process_update(Update(update_id=1, message=message)) + sleep(0.5) + cdp.job_queue.tick() + assert handler.conversations.get((self.group.id, user1.id)) is None + assert self.is_timeout + + # Timeout but no valid handler + self.is_timeout = False + cdp.process_update(Update(update_id=0, message=message)) + message.text = '/brew' + message.entities[0].length = len('/brew') + cdp.process_update(Update(update_id=0, message=message)) + message.text = '/startCoding' + message.entities[0].length = len('/startCoding') + cdp.process_update(Update(update_id=0, message=message)) + sleep(0.5) + cdp.job_queue.tick() + assert handler.conversations.get((self.group.id, user1.id)) is None + assert not self.is_timeout + def test_conversation_timeout_cancel_conflict(self, dp, bot, user1): # Start state machine, wait half the timeout, # then call a callback that takes more than the timeout @@ -717,10 +782,10 @@ class TestConversationHandler(object): def test_nested_conversation_handler(self, dp, bot, user1, user2): self.nested_states[self.DRINKING] = [ConversationHandler( - entry_points=self.drinking_entry_points, - states=self.drinking_states, - fallbacks=self.drinking_fallbacks, - map_to_parent=self.drinking_map_to_parent)] + entry_points=self.drinking_entry_points, + states=self.drinking_states, + fallbacks=self.drinking_fallbacks, + map_to_parent=self.drinking_map_to_parent)] handler = ConversationHandler(entry_points=self.entry_points, states=self.nested_states, fallbacks=self.fallbacks)