Revert "Revert "Context based callbacks (#1100)""

This reverts commit f8a17cd
This commit is contained in:
Jasmin Bom 2018-09-21 08:57:01 +02:00
parent 439790375e
commit cf95027308
45 changed files with 1404 additions and 630 deletions

View file

@ -2,6 +2,25 @@
Changes
=======
**2018-??-??**
*Released 11.0.0*
Context based callbacks:
See https://git.io/vp113 for help.
- Use of `pass_` in handlers is deprecated.
- Instead use `use_context=True` on `Updater` or `Dispatcher` and change callback from (bot, update, others...) to (update, context).
- This also applies to error handlers `Dispatcher.add_error_handler` and JobQueue jobs (change (bot, job) to (context) here).
- For users with custom handlers subclassing Handler, this is mostly backwards compatible, but to use the new context based callbacks you need to implement the new collect_additional_context method.
- Passing bot to JobQueue.__init__ is deprecated. Use JobQueue.set_dispatcher with a dispatcher instead.
Other:
- Handlers should be faster due to deduped logic.
Other removals:
- Remove the ability to use filter lists in handlers.
- Remove the last CamelCase CheckUpdate methods from the handlers we missed earlier.
**2018-09-01**
*Released 11.1.0*

View file

@ -0,0 +1,5 @@
telegram.ext.CallbackContext
============================
.. autoclass:: telegram.ext.CallbackContext
:members:

View file

@ -10,6 +10,7 @@ telegram.ext package
telegram.ext.jobqueue
telegram.ext.messagequeue
telegram.ext.delayqueue
telegram.ext.callbackcontext
Handlers
--------

View file

@ -17,12 +17,12 @@ Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
import logging
from telegram import (ReplyKeyboardMarkup, ReplyKeyboardRemove)
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, RegexHandler,
ConversationHandler)
import logging
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
@ -32,7 +32,7 @@ logger = logging.getLogger(__name__)
GENDER, PHOTO, LOCATION, BIO = range(4)
def start(bot, update):
def start(update, context):
reply_keyboard = [['Boy', 'Girl', 'Other']]
update.message.reply_text(
@ -44,7 +44,7 @@ def start(bot, update):
return GENDER
def gender(bot, update):
def gender(update, context):
user = update.message.from_user
logger.info("Gender of %s: %s", user.first_name, update.message.text)
update.message.reply_text('I see! Please send me a photo of yourself, '
@ -54,9 +54,9 @@ def gender(bot, update):
return PHOTO
def photo(bot, update):
def photo(update, context):
user = update.message.from_user
photo_file = bot.get_file(update.message.photo[-1].file_id)
photo_file = update.message.photo[-1].get_file()
photo_file.download('user_photo.jpg')
logger.info("Photo of %s: %s", user.first_name, 'user_photo.jpg')
update.message.reply_text('Gorgeous! Now, send me your location please, '
@ -65,7 +65,7 @@ def photo(bot, update):
return LOCATION
def skip_photo(bot, update):
def skip_photo(update, context):
user = update.message.from_user
logger.info("User %s did not send a photo.", user.first_name)
update.message.reply_text('I bet you look great! Now, send me your location please, '
@ -74,7 +74,7 @@ def skip_photo(bot, update):
return LOCATION
def location(bot, update):
def location(update, context):
user = update.message.from_user
user_location = update.message.location
logger.info("Location of %s: %f / %f", user.first_name, user_location.latitude,
@ -85,7 +85,7 @@ def location(bot, update):
return BIO
def skip_location(bot, update):
def skip_location(update, context):
user = update.message.from_user
logger.info("User %s did not send a location.", user.first_name)
update.message.reply_text('You seem a bit paranoid! '
@ -94,7 +94,7 @@ def skip_location(bot, update):
return BIO
def bio(bot, update):
def bio(update, context):
user = update.message.from_user
logger.info("Bio of %s: %s", user.first_name, update.message.text)
update.message.reply_text('Thank you! I hope we can talk again some day.')
@ -102,7 +102,7 @@ def bio(bot, update):
return ConversationHandler.END
def cancel(bot, update):
def cancel(update, context):
user = update.message.from_user
logger.info("User %s canceled the conversation.", user.first_name)
update.message.reply_text('Bye! I hope we can talk again some day.',
@ -111,14 +111,16 @@ def cancel(bot, update):
return ConversationHandler.END
def error(bot, update, error):
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error)
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
# Create the EventHandler and pass it your bot's token.
updater = Updater("TOKEN")
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater("TOKEN", use_context=True)
# Get the dispatcher to register handlers
dp = updater.dispatcher

View file

@ -17,12 +17,12 @@ Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
import logging
from telegram import ReplyKeyboardMarkup
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, RegexHandler,
ConversationHandler)
import logging
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
@ -46,7 +46,7 @@ def facts_to_str(user_data):
return "\n".join(facts).join(['\n', '\n'])
def start(bot, update):
def start(update, context):
update.message.reply_text(
"Hi! My name is Doctor Botter. I will hold a more complex conversation with you. "
"Why don't you tell me something about yourself?",
@ -55,23 +55,24 @@ def start(bot, update):
return CHOOSING
def regular_choice(bot, update, user_data):
def regular_choice(update, context):
text = update.message.text
user_data['choice'] = text
context.user_data['choice'] = text
update.message.reply_text(
'Your {}? Yes, I would love to hear about that!'.format(text.lower()))
return TYPING_REPLY
def custom_choice(bot, update):
def custom_choice(update, context):
update.message.reply_text('Alright, please send me the category first, '
'for example "Most impressive skill"')
return TYPING_CHOICE
def received_information(bot, update, user_data):
def received_information(update, context):
user_data = context.user_data
text = update.message.text
category = user_data['choice']
user_data[category] = text
@ -85,7 +86,8 @@ def received_information(bot, update, user_data):
return CHOOSING
def done(bot, update, user_data):
def done(update, context):
user_data = context.user_data
if 'choice' in user_data:
del user_data['choice']
@ -97,14 +99,16 @@ def done(bot, update, user_data):
return ConversationHandler.END
def error(bot, update, error):
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error)
def main():
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN")
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater("TOKEN", use_context=True)
# Get the dispatcher to register handlers
dp = updater.dispatcher

View file

@ -17,9 +17,10 @@ Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
import logging
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
@ -29,30 +30,32 @@ logger = logging.getLogger(__name__)
# Define a few command handlers. These usually take the two arguments bot and
# update. Error handlers also receive the raised TelegramError object in error.
def start(bot, update):
def start(update, context):
"""Send a message when the command /start is issued."""
update.message.reply_text('Hi!')
def help(bot, update):
def help(update, context):
"""Send a message when the command /help is issued."""
update.message.reply_text('Help!')
def echo(bot, update):
def echo(update, context):
"""Echo the user message."""
update.message.reply_text(update.message.text)
def error(bot, update, error):
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error)
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
"""Start the bot."""
# Create the EventHandler and pass it your bot's token.
updater = Updater("TOKEN")
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater("TOKEN", use_context=True)
# Get the dispatcher to register handlers
dp = updater.dispatcher

View file

@ -16,14 +16,13 @@ Basic inline bot example. Applies different text transformations.
Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
import logging
from uuid import uuid4
from telegram.utils.helpers import escape_markdown
from telegram import InlineQueryResultArticle, ParseMode, \
InputTextMessageContent
from telegram.ext import Updater, InlineQueryHandler, CommandHandler
import logging
from telegram.utils.helpers import escape_markdown
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
@ -34,17 +33,17 @@ logger = logging.getLogger(__name__)
# Define a few command handlers. These usually take the two arguments bot and
# update. Error handlers also receive the raised TelegramError object in error.
def start(bot, update):
def start(update, context):
"""Send a message when the command /start is issued."""
update.message.reply_text('Hi!')
def help(bot, update):
def help(update, context):
"""Send a message when the command /help is issued."""
update.message.reply_text('Help!')
def inlinequery(bot, update):
def inlinequery(update, context):
"""Handle the inline query."""
query = update.inline_query.query
results = [
@ -69,14 +68,16 @@ def inlinequery(bot, update):
update.inline_query.answer(results)
def error(bot, update, error):
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error)
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN")
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater("TOKEN", use_context=True)
# Get the dispatcher to register handlers
dp = updater.dispatcher

View file

@ -5,6 +5,7 @@
# This program is dedicated to the public domain under the CC0 license.
"""
import logging
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler
@ -13,7 +14,7 @@ logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s
logger = logging.getLogger(__name__)
def start(bot, update):
def start(update, context):
keyboard = [[InlineKeyboardButton("Option 1", callback_data='1'),
InlineKeyboardButton("Option 2", callback_data='2')],
@ -24,26 +25,26 @@ def start(bot, update):
update.message.reply_text('Please choose:', reply_markup=reply_markup)
def button(bot, update):
def button(update, context):
query = update.callback_query
bot.edit_message_text(text="Selected option: {}".format(query.data),
chat_id=query.message.chat_id,
message_id=query.message.message_id)
query.edit_message_text(text="Selected option: {}".format(query.data))
def help(bot, update):
def help(update, context):
update.message.reply_text("Use /start to test this bot.")
def error(bot, update, error):
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error)
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN")
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater("TOKEN", use_context=True)
updater.dispatcher.add_handler(CommandHandler('start', start))
updater.dispatcher.add_handler(CallbackQueryHandler(button))

View file

@ -6,10 +6,11 @@
This program is dedicated to the public domain under the CC0 license.
"""
import logging
from telegram import (LabeledPrice, ShippingOption)
from telegram.ext import (Updater, CommandHandler, MessageHandler,
Filters, PreCheckoutQueryHandler, ShippingQueryHandler)
import logging
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
@ -18,18 +19,18 @@ logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s
logger = logging.getLogger(__name__)
def error(bot, update, error):
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error)
logger.warning('Update "%s" caused error "%s"', update, context.error)
def start_callback(bot, update):
def start_callback(update, context):
msg = "Use /shipping to get an invoice for shipping-payment, "
msg += "or /noshipping for an invoice without shipping."
update.message.reply_text(msg)
def start_with_shipping_callback(bot, update):
def start_with_shipping_callback(update, context):
chat_id = update.message.chat_id
title = "Payment Example"
description = "Payment Example using python-telegram-bot"
@ -47,13 +48,13 @@ def start_with_shipping_callback(bot, update):
# optionally pass need_name=True, need_phone_number=True,
# need_email=True, need_shipping_address=True, is_flexible=True
bot.sendInvoice(chat_id, title, description, payload,
provider_token, start_parameter, currency, prices,
need_name=True, need_phone_number=True,
need_email=True, need_shipping_address=True, is_flexible=True)
context.bot.send_invoice(chat_id, title, description, payload,
provider_token, start_parameter, currency, prices,
need_name=True, need_phone_number=True,
need_email=True, need_shipping_address=True, is_flexible=True)
def start_without_shipping_callback(bot, update):
def start_without_shipping_callback(update, context):
chat_id = update.message.chat_id
title = "Payment Example"
description = "Payment Example using python-telegram-bot"
@ -70,17 +71,16 @@ def start_without_shipping_callback(bot, update):
# optionally pass need_name=True, need_phone_number=True,
# need_email=True, need_shipping_address=True, is_flexible=True
bot.sendInvoice(chat_id, title, description, payload,
provider_token, start_parameter, currency, prices)
context.bot.send_invoice(chat_id, title, description, payload,
provider_token, start_parameter, currency, prices)
def shipping_callback(bot, update):
def shipping_callback(update, context):
query = update.shipping_query
# check the payload, is this from your bot?
if query.invoice_payload != 'Custom-Payload':
# answer False pre_checkout_query
bot.answer_shipping_query(shipping_query_id=query.id, ok=False,
error_message="Something went wrong...")
query.answer(ok=False, error_message="Something went wrong...")
return
else:
options = list()
@ -89,31 +89,31 @@ def shipping_callback(bot, update):
# an array of LabeledPrice objects
price_list = [LabeledPrice('B1', 150), LabeledPrice('B2', 200)]
options.append(ShippingOption('2', 'Shipping Option B', price_list))
bot.answer_shipping_query(shipping_query_id=query.id, ok=True,
shipping_options=options)
query.answer(ok=True, shipping_options=options)
# after (optional) shipping, it's the pre-checkout
def precheckout_callback(bot, update):
def precheckout_callback(update, context):
query = update.pre_checkout_query
# check the payload, is this from your bot?
if query.invoice_payload != 'Custom-Payload':
# answer False pre_checkout_query
bot.answer_pre_checkout_query(pre_checkout_query_id=query.id, ok=False,
error_message="Something went wrong...")
query.answer(ok=False, error_message="Something went wrong...")
else:
bot.answer_pre_checkout_query(pre_checkout_query_id=query.id, ok=True)
query.answer(ok=True)
# finally, after contacting to the payment provider...
def successful_payment_callback(bot, update):
def successful_payment_callback(update, context):
# do something after successful receive of payment?
update.message.reply_text("Thank you for your payment!")
def main():
# Create the EventHandler and pass it your bot's token.
updater = Updater(token="BOT_TOKEN")
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater("TOKEN", use_context=True)
# Get the dispatcher to register handlers
dp = updater.dispatcher

View file

@ -19,9 +19,10 @@ Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
from telegram.ext import Updater, CommandHandler
import logging
from telegram.ext import Updater, CommandHandler
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
@ -31,28 +32,29 @@ logger = logging.getLogger(__name__)
# Define a few command handlers. These usually take the two arguments bot and
# update. Error handlers also receive the raised TelegramError object in error.
def start(bot, update):
def start(update, context):
update.message.reply_text('Hi! Use /set <seconds> to set a timer')
def alarm(bot, job):
def alarm(context):
"""Send the alarm message."""
bot.send_message(job.context, text='Beep!')
job = context.job
context.bot.send_message(job.context, text='Beep!')
def set_timer(bot, update, args, job_queue, chat_data):
def set_timer(update, context):
"""Add a job to the queue."""
chat_id = update.message.chat_id
try:
# args[0] should contain the time for the timer in seconds
due = int(args[0])
due = int(context.args[0])
if due < 0:
update.message.reply_text('Sorry we can not go back to future!')
return
# Add job to queue
job = job_queue.run_once(alarm, due, context=chat_id)
chat_data['job'] = job
job = context.job_queue.run_once(alarm, due, context=chat_id)
context.chat_data['job'] = job
update.message.reply_text('Timer successfully set!')
@ -60,27 +62,30 @@ def set_timer(bot, update, args, job_queue, chat_data):
update.message.reply_text('Usage: /set <seconds>')
def unset(bot, update, chat_data):
def unset(update, context):
"""Remove the job if the user changed their mind."""
if 'job' not in chat_data:
if 'job' not in context.chat_data:
update.message.reply_text('You have no active timer')
return
job = chat_data['job']
job = context.chat_data['job']
job.schedule_removal()
del chat_data['job']
del context.chat_data['job']
update.message.reply_text('Timer successfully unset!')
def error(bot, update, error):
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error)
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
"""Run bot."""
updater = Updater("TOKEN")
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater("TOKEN", use_context=True)
# Get the dispatcher to register handlers
dp = updater.dispatcher

View file

@ -27,6 +27,7 @@ addopts = --no-success-flaky-report -rsxX
filterwarnings =
error
ignore::DeprecationWarning
ignore::telegram.utils.deprecate.TelegramDeprecationWarning
[coverage:run]
branch = True

View file

@ -21,13 +21,14 @@
from .basepersistence import BasePersistence
from .picklepersistence import PicklePersistence
from .dictpersistence import DictPersistence
from .handler import Handler
from .callbackcontext import CallbackContext
from .dispatcher import Dispatcher, DispatcherHandlerStop, run_async
from .jobqueue import JobQueue, Job
from .updater import Updater
from .callbackqueryhandler import CallbackQueryHandler
from .choseninlineresulthandler import ChosenInlineResultHandler
from .commandhandler import CommandHandler
from .handler import Handler
from .inlinequeryhandler import InlineQueryHandler
from .messagehandler import MessageHandler
from .filters import BaseFilter, Filters
@ -46,5 +47,5 @@ __all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler',
'MessageHandler', 'BaseFilter', 'Filters', 'RegexHandler', 'StringCommandHandler',
'StringRegexHandler', 'TypeHandler', 'ConversationHandler',
'PreCheckoutQueryHandler', 'ShippingQueryHandler', 'MessageQueue', 'DelayQueue',
'DispatcherHandlerStop', 'run_async', 'BasePersistence', 'PicklePersistence',
'DictPersistence')
'DispatcherHandlerStop', 'run_async', 'CallbackContext', 'BasePersistence',
'PicklePersistence', 'DictPersistence')

View file

@ -0,0 +1,115 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# 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 CallbackContext class."""
from telegram import Update
class CallbackContext(object):
"""
This is a context object passed to the callback called by :class:`telegram.ext.Handler`
or by the :class:`telegram.ext.Dispatcher` in an error handler added by
:attr:`telegram.ext.Dispatcher.add_error_handler` or to the callback of a
:class:`telegram.ext.Job`.
Attributes:
chat_data (:obj:`dict`, optional): A dict that can be used to keep any data in. For each
update from the same chat it will be the same ``dict``.
user_data (:obj:`dict`, optional): A dict that can be used to keep any data in. For each
update from the same user it will be the same ``dict``.
match (:obj:`re match object`, optional): If the associated update originated from a
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.
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`.
job (:class:`telegram.ext.Job`): The job that that originated this callback.
Only present when passed to the callback of :class:`telegram.ext.Job`.
"""
def __init__(self, dispatcher):
"""
Args:
dispatcher (:class:`telegram.ext.Dispatcher`):
"""
if not dispatcher.use_context:
raise ValueError('CallbackContext should not be used with a non context aware '
'dispatcher!')
self._dispatcher = dispatcher
self.chat_data = None
self.user_data = None
self.args = None
self.match = None
self.error = None
self.job = None
@classmethod
def from_error(cls, update, error, dispatcher):
self = cls.from_update(update, dispatcher)
self.error = error
return self
@classmethod
def from_update(cls, update, dispatcher):
self = cls(dispatcher)
if update is not None and isinstance(update, Update):
chat = update.effective_chat
user = update.effective_user
if chat:
self.chat_data = dispatcher.chat_data[chat.id]
if user:
self.user_data = dispatcher.user_data[user.id]
return self
@classmethod
def from_job(cls, job, dispatcher):
self = cls(dispatcher)
self.job = job
return self
@property
def bot(self):
""":class:`telegram.Bot`: The bot associated with this context."""
return self._dispatcher.bot
@property
def job_queue(self):
"""
:class:`telegram.ext.JobQueue`: The ``JobQueue`` used by the
:class:`telegram.ext.Dispatcher` and (usually) the :class:`telegram.ext.Updater`
associated with this context.
"""
return self._dispatcher.job_queue
@property
def update_queue(self):
"""
:class:`queue.Queue`: The ``Queue`` instance used by the
:class:`telegram.ext.Dispatcher` and (usually) the :class:`telegram.ext.Updater`
associated with this context.
"""
return self._dispatcher.update_queue

View file

@ -33,19 +33,19 @@ class CallbackQueryHandler(Handler):
Attributes:
callback (:obj:`callable`): The callback function for this handler.
pass_update_queue (:obj:`bool`): Optional. Determines whether ``update_queue`` will be
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
passed to the callback function.
pass_job_queue (:obj:`bool`): Optional. Determines whether ``job_queue`` will be passed to
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
the callback function.
pattern (:obj:`str` | `Pattern`): Optional. Regex pattern to test
:attr:`telegram.CallbackQuery.data` against.
pass_groups (:obj:`bool`): Optional. Determines whether ``groups`` will be passed to the
pass_groups (:obj:`bool`): Determines whether ``groups`` will be passed to the
callback function.
pass_groupdict (:obj:`bool`): Optional. Determines whether ``groupdict``. will be passed to
pass_groupdict (:obj:`bool`): Determines whether ``groupdict``. will be passed to
the callback function.
pass_user_data (:obj:`bool`): Optional. Determines whether ``user_data`` will be passed to
pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to
the callback function.
pass_chat_data (:obj:`bool`): Optional. Determines whether ``chat_data`` will be passed to
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
the callback function.
Note:
@ -54,31 +54,45 @@ class CallbackQueryHandler(Handler):
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:
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments.
It will be called when the :attr:`check_update` has determined that an update should be
processed by this handler.
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`.
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.
pattern (:obj:`str` | `Pattern`, optional): Regex pattern. If not ``None``, ``re.match``
is used on :attr:`telegram.CallbackQuery.data` to determine if an update should be
handled by this handler.
pass_groups (:obj:`bool`, optional): If the callback should be passed the result of
``re.match(pattern, data).groups()`` as a keyword argument called ``groups``.
Default is ``False``
DEPRECATED: Please switch to context based callbacks.
pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of
``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``.
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.
"""
@ -119,25 +133,22 @@ class CallbackQueryHandler(Handler):
if self.pattern:
if update.callback_query.data:
match = re.match(self.pattern, update.callback_query.data)
return bool(match)
if match:
return match
else:
return True
def handle_update(self, update, dispatcher):
"""Send the update to the :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
optional_args = self.collect_optional_args(dispatcher, update)
def collect_optional_args(self, dispatcher, update=None, check_result=None):
optional_args = super(CallbackQueryHandler, self).collect_optional_args(dispatcher,
update,
check_result)
if self.pattern:
match = re.match(self.pattern, update.callback_query.data)
if self.pass_groups:
optional_args['groups'] = match.groups()
optional_args['groups'] = check_result.groups()
if self.pass_groupdict:
optional_args['groupdict'] = match.groupdict()
optional_args['groupdict'] = check_result.groupdict()
return optional_args
return self.callback(dispatcher.bot, update, **optional_args)
def collect_additional_context(self, context, update, dispatcher, check_result):
if self.pattern:
context.match = check_result

View file

@ -18,9 +18,8 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the ChosenInlineResultHandler class."""
from .handler import Handler
from telegram import Update
from telegram.utils.deprecate import deprecate
from .handler import Handler
class ChosenInlineResultHandler(Handler):
@ -28,13 +27,13 @@ class ChosenInlineResultHandler(Handler):
Attributes:
callback (:obj:`callable`): The callback function for this handler.
pass_update_queue (:obj:`bool`): Optional. Determines whether ``update_queue`` will be
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
passed to the callback function.
pass_job_queue (:obj:`bool`): Optional. Determines whether ``job_queue`` will be passed to
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
the callback function.
pass_user_data (:obj:`bool`): Optional. Determines whether ``user_data`` will be passed to
pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to
the callback function.
pass_chat_data (:obj:`bool`): Optional. Determines whether ``chat_data`` will be passed to
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
the callback function.
Note:
@ -43,38 +42,37 @@ class ChosenInlineResultHandler(Handler):
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:
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments.
It will be called when the :attr:`check_update` has determined that an update should be
processed by this handler.
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`.
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.
"""
def __init__(self,
callback,
pass_update_queue=False,
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
super(ChosenInlineResultHandler, 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)
def check_update(self, update):
"""Determines whether an update should be passed to this handlers :attr:`callback`.
@ -86,20 +84,3 @@ class ChosenInlineResultHandler(Handler):
"""
return isinstance(update, Update) and update.chosen_inline_result
def handle_update(self, update, dispatcher):
"""Send the update to the :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
optional_args = self.collect_optional_args(dispatcher, update)
return self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.ChosenInlineResultHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")

View file

@ -17,12 +17,10 @@
# 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."""
import warnings
from future.utils import string_types
from .handler import Handler
from telegram import Update
from .handler import Handler
class CommandHandler(Handler):
@ -37,17 +35,17 @@ class CommandHandler(Handler):
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`): Optional. Determines Whether the handler should also accept
allow_edited (:obj:`bool`): Determines Whether the handler should also accept
edited messages.
pass_args (:obj:`bool`): Optional. Determines whether the handler should be passed
pass_args (:obj:`bool`): Determines whether the handler should be passed
``args``.
pass_update_queue (:obj:`bool`): Optional. Determines whether ``update_queue`` will be
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
passed to the callback function.
pass_job_queue (:obj:`bool`): Optional. Determines whether ``job_queue`` will be passed to
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
the callback function.
pass_user_data (:obj:`bool`): Optional. Determines whether ``user_data`` will be passed to
pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to
the callback function.
pass_chat_data (:obj:`bool`): Optional. Determines whether ``chat_data`` will be passed to
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
the callback function.
Note:
@ -56,12 +54,20 @@ class CommandHandler(Handler):
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.
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments.
It will be called when the :attr:`check_update` has determined that an update should be
processed by this handler.
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
@ -72,18 +78,23 @@ class CommandHandler(Handler):
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.
"""
@ -112,13 +123,6 @@ class CommandHandler(Handler):
self.allow_edited = allow_edited
self.pass_args = pass_args
# We put this up here instead of with the rest of checking code
# in check_update since we don't wanna spam a ton
if isinstance(self.filters, list):
warnings.warn('Using a list of filters in MessageHandler is getting '
'deprecated, please use bitwise operators (& and |) '
'instead. More info: https://git.io/vPTbc.')
def check_update(self, update):
"""Determines whether an update should be passed to this handlers :attr:`callback`.
@ -129,8 +133,8 @@ class CommandHandler(Handler):
:obj:`bool`
"""
if (isinstance(update, Update)
and (update.message or update.edited_message and self.allow_edited)):
if (isinstance(update, Update) and
(update.message or update.edited_message and self.allow_edited)):
message = update.message or update.edited_message
if message.text and message.text.startswith('/') and len(message.text) > 1:
@ -142,32 +146,16 @@ class CommandHandler(Handler):
if not (command[0].lower() in self.command
and command[1].lower() == message.bot.username.lower()):
return False
return None
if self.filters is None:
res = True
elif isinstance(self.filters, list):
res = any(func(message) for func in self.filters)
else:
res = self.filters(message)
return res
return False
def handle_update(self, update, dispatcher):
"""Send the update to the :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
optional_args = self.collect_optional_args(dispatcher, update)
message = update.message or update.edited_message
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'] = message.text.split()[1:]
optional_args['args'] = check_result
return optional_args
return self.callback(dispatcher.bot, update, **optional_args)
def collect_additional_context(self, context, update, dispatcher, check_result):
context.args = check_result

View file

@ -66,15 +66,15 @@ class ConversationHandler(Handler):
fallbacks (List[:class:`telegram.ext.Handler`]): A list of handlers that might be used if
the user is in a conversation, but every handler for their current state returned
``False`` on :attr:`check_update`.
allow_reentry (:obj:`bool`): Optional. Determines if a user can restart a conversation with
allow_reentry (:obj:`bool`): Determines if a user can restart a conversation with
an entry point.
run_async_timeout (:obj:`float`): Optional. The time-out for ``run_async`` decorated
Handlers.
timed_out_behavior (List[:class:`telegram.ext.Handler`]): Optional. A list of handlers that
might be used if the wait for ``run_async`` timed out.
per_chat (:obj:`bool`): Optional. If the conversationkey should contain the Chat's ID.
per_user (:obj:`bool`): Optional. If the conversationkey should contain the User's ID.
per_message (:obj:`bool`): Optional. If the conversationkey should contain the Message's
per_chat (:obj:`bool`): If the conversationkey should contain the Chat's ID.
per_user (:obj:`bool`): If the conversationkey should contain the User's ID.
per_message (:obj:`bool`): If the conversationkey should contain the Message's
ID.
conversation_timeout (:obj:`float`|:obj:`datetime.timedelta`): Optional. When this handler
is inactive more than this timeout (in seconds), it will be automatically ended. If
@ -164,8 +164,6 @@ class ConversationHandler(Handler):
self.timeout_jobs = dict()
self.conversations = dict()
self.current_conversation = None
self.current_handler = None
self.logger = logging.getLogger(__name__)
@ -237,7 +235,7 @@ class ConversationHandler(Handler):
self.per_chat and not update.effective_chat or
self.per_message and not update.callback_query or
update.callback_query and self.per_chat and not update.callback_query.message):
return False
return None
key = self._get_key(update)
state = self.conversations.get(key)
@ -260,15 +258,13 @@ class ConversationHandler(Handler):
state = self.conversations.get(key)
else:
for candidate in (self.timed_out_behavior or []):
if candidate.check_update(update):
check = candidate.check_update(update)
if check is not None and check is not False:
# Save the current user and the selected handler for handle_update
self.current_conversation = key
self.current_handler = candidate
return True
return key, candidate, check
else:
return False
return None
self.logger.debug('selecting conversation %s with state %s' % (str(key), str(state)))
@ -277,59 +273,61 @@ class ConversationHandler(Handler):
# Search entry points for a match
if state is None or self.allow_reentry:
for entry_point in self.entry_points:
if entry_point.check_update(update):
check = entry_point.check_update(update)
if check is not None and check is not False:
handler = entry_point
break
else:
if state is None:
return False
return None
# Get the handler list for current state, if we didn't find one yet and we're still here
if state is not None and not handler:
handlers = self.states.get(state)
for candidate in (handlers or []):
if candidate.check_update(update):
check = candidate.check_update(update)
if check is not None and check is not False:
handler = candidate
break
# Find a fallback handler if all other handlers fail
else:
for fallback in self.fallbacks:
if fallback.check_update(update):
check = fallback.check_update(update)
if check is not None and check is not False:
handler = fallback
break
else:
return False
return None
# Save the current user and the selected handler for handle_update
self.current_conversation = key
self.current_handler = handler
return key, handler, check
return True
def handle_update(self, update, dispatcher):
def handle_update(self, update, dispatcher, check_result):
"""Send the update to the callback for the current state and Handler
Args:
check_result: The result from check_update. For this handler it's a tuple of key,
handler, and the handler's check result.
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
new_state = self.current_handler.handle_update(update, dispatcher)
timeout_job = self.timeout_jobs.pop(self.current_conversation, None)
conversation_key, handler, check_result = check_result
new_state = handler.handle_update(update, dispatcher, check_result)
timeout_job = self.timeout_jobs.pop(conversation_key, None)
if timeout_job is not None:
timeout_job.schedule_removal()
if self.conversation_timeout and new_state != self.END:
self.timeout_jobs[self.current_conversation] = dispatcher.job_queue.run_once(
self.timeout_jobs[conversation_key] = dispatcher.job_queue.run_once(
self._trigger_timeout, self.conversation_timeout,
context=self.current_conversation
context=conversation_key
)
self.update_state(new_state, self.current_conversation)
self.update_state(new_state, conversation_key)
def update_state(self, new_state, key):
if new_state == self.END:

View file

@ -19,6 +19,7 @@
"""This module contains the Dispatcher class."""
import logging
import warnings
import weakref
from functools import wraps
from threading import Thread, Lock, Event, current_thread, BoundedSemaphore
@ -32,6 +33,8 @@ from future.builtins import range
from telegram import TelegramError
from telegram.ext.handler import Handler
from telegram.ext.callbackcontext import CallbackContext
from telegram.utils.deprecate import TelegramDeprecationWarning
from telegram.utils.promise import Promise
from telegram.ext import BasePersistence
@ -86,6 +89,9 @@ class Dispatcher(object):
``@run_async`` decorator. defaults to 4.
persistence (:class:`telegram.ext.BasePersistence`, optional): The persistence class to
store data that should be persistent over restarts
use_context (:obj:`bool`, optional): If set to ``True`` Use the context based callback API.
During the deprecation period of the old API the default is ``False``. **New users**:
set this to ``True``.
"""
@ -94,12 +100,26 @@ class Dispatcher(object):
__singleton = None
logger = logging.getLogger(__name__)
def __init__(self, bot, update_queue, workers=4, exception_event=None, job_queue=None,
persistence=None):
def __init__(self,
bot,
update_queue,
workers=4,
exception_event=None,
job_queue=None,
persistence=None,
use_context=False):
self.bot = bot
self.update_queue = update_queue
self.job_queue = job_queue
self.workers = workers
self.use_context = use_context
if not use_context:
warnings.warn('Old Handler API is deprecated - see https://git.io/vp113 for details',
TelegramDeprecationWarning, stacklevel=3)
self.user_data = defaultdict(dict)
""":obj:`dict`: A dictionary handlers can use to store data for the user."""
self.chat_data = defaultdict(dict)
if persistence:
if not isinstance(persistence, BasePersistence):
@ -297,22 +317,24 @@ class Dispatcher(object):
for group in self.groups:
try:
for handler in (x for x in self.handlers[group] if x.check_update(update)):
handler.handle_update(update, self)
if self.persistence:
if self.persistence.store_chat_data and update.effective_chat.id:
chat_id = update.effective_chat.id
try:
self.persistence.update_chat_data(chat_id, self.chat_data[chat_id])
except Exception:
self.logger.exception('Saving chat data raised an error')
if self.persistence.store_user_data and update.effective_user.id:
user_id = update.effective_user.id
try:
self.persistence.update_user_data(user_id, self.user_data[user_id])
except Exception:
self.logger.exception('Saving user data raised an error')
break
for handler in self.handlers[group]:
check = handler.check_update(update)
if check is not None and check is not False:
handler.handle_update(update, self)
if self.persistence:
if self.persistence.store_chat_data and update.effective_chat.id:
chat_id = update.effective_chat.id
try:
self.persistence.update_chat_data(chat_id, self.chat_data[chat_id])
except Exception:
self.logger.exception('Saving chat data raised an error')
if self.persistence.store_user_data and update.effective_user.id:
user_id = update.effective_user.id
try:
self.persistence.update_user_data(user_id, self.user_data[user_id])
except Exception:
self.logger.exception('Saving user data raised an error')
break
# Stop processing with any other handler.
except DispatcherHandlerStop:
@ -399,9 +421,15 @@ class Dispatcher(object):
"""Registers an error handler in the Dispatcher.
Args:
callback (:obj:`callable`): A function that takes ``Bot, Update, TelegramError`` as
arguments.
callback (:obj:`callable`): The callback function for this error handler. Will be
called when an error is raised Callback signature for context based API:
``def callback(update: Update, context: CallbackContext)``
The error that happened will be present in context.error.
Note:
See https://git.io/vp113 for more info about switching to context based API.
"""
self.error_handlers.append(callback)
@ -425,7 +453,10 @@ class Dispatcher(object):
"""
if self.error_handlers:
for callback in self.error_handlers:
callback(self.bot, update, error)
if self.use_context:
callback(update, CallbackContext.from_error(update, error, self))
else:
callback(self.bot, update, error)
else:
self.logger.exception(

View file

@ -17,6 +17,7 @@
# 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 base class for handlers as used by the Dispatcher."""
from telegram.ext.callbackcontext import CallbackContext
class Handler(object):
@ -24,13 +25,13 @@ class Handler(object):
Attributes:
callback (:obj:`callable`): The callback function for this handler.
pass_update_queue (:obj:`bool`): Optional. Determines whether ``update_queue`` will be
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
passed to the callback function.
pass_job_queue (:obj:`bool`): Optional. Determines whether ``job_queue`` will be passed to
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
the callback function.
pass_user_data (:obj:`bool`): Optional. Determines whether ``user_data`` will be passed to
pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to
the callback function.
pass_chat_data (:obj:`bool`): Optional. Determines whether ``chat_data`` will be passed to
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
the callback function.
Note:
@ -39,22 +40,34 @@ class Handler(object):
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:
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments.
It will be called when the :attr:`check_update` has determined that an update should be
processed by this handler.
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`.
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.
"""
@ -79,31 +92,59 @@ class Handler(object):
update (:obj:`str` | :class:`telegram.Update`): The update to be tested.
Returns:
:obj:`bool`
Either ``None`` or ``False`` if the update should not be handled. Otherwise an object
that will be passed to :attr:`handle_update` and :attr:`collect_additional_context`
when the update gets handled.
"""
raise NotImplementedError
def handle_update(self, update, dispatcher):
def handle_update(self, update, dispatcher, check_result):
"""
This method is called if it was determined that an update should indeed
be handled by this instance. It should also be overridden, but in most
cases call ``self.callback(dispatcher.bot, update)``, possibly along with
optional arguments. To work with the ``ConversationHandler``, this method should return the
value returned from ``self.callback``
be handled by this instance. Calls :attr:`self.callback` along with its respectful
arguments. To work with the :class:`telegram.ext.ConversationHandler`, this method
returns the value returned from ``self.callback``.
Note that it can be overridden if needed by the subclassing handler.
Args:
update (:obj:`str` | :class:`telegram.Update`): The update to be handled.
dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher to collect optional args.
dispatcher (:class:`telegram.ext.Dispatcher`): The calling dispatcher.
check_result: The result from :attr:`check_update`.
"""
raise NotImplementedError
if dispatcher.use_context:
context = CallbackContext.from_update(update, dispatcher)
self.collect_additional_context(context, update, dispatcher, check_result)
return self.callback(update, context)
else:
optional_args = self.collect_optional_args(dispatcher, update, check_result)
return self.callback(dispatcher.bot, update, **optional_args)
def collect_optional_args(self, dispatcher, update=None):
"""Prepares the optional arguments that are the same for all types of handlers.
def collect_additional_context(self, context, update, dispatcher, check_result):
"""Prepares additional arguments for the context. Override if needed.
Args:
context (:class:`telegram.ext.CallbackContext`): The context object.
update (:class:`telegram.Update`): The update to gather chat/user id from.
dispatcher (:class:`telegram.ext.Dispatcher`): The calling dispatcher.
check_result: The result (return value) from :attr:`check_update`.
"""
pass
def collect_optional_args(self, dispatcher, update=None, check_result=None):
"""
Prepares the optional arguments. If the handler has additional optional args,
it should subclass this method, but remember to call this super method.
DEPRECATED: This method is being replaced by new context based callbacks. Please see
https://git.io/vp113 for more info.
Args:
dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher.
update (:class:`telegram.Update`): The update to gather chat/user id from.
check_result: The result from check_update
"""
optional_args = dict()

View file

@ -22,7 +22,6 @@ import re
from future.utils import string_types
from telegram import Update
from telegram.utils.deprecate import deprecate
from .handler import Handler
@ -33,19 +32,19 @@ class InlineQueryHandler(Handler):
Attributes:
callback (:obj:`callable`): The callback function for this handler.
pass_update_queue (:obj:`bool`): Optional. Determines whether ``update_queue`` will be
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
passed to the callback function.
pass_job_queue (:obj:`bool`): Optional. Determines whether ``job_queue`` will be passed to
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
the callback function.
pattern (:obj:`str` | :obj:`Pattern`): Optional. Regex pattern to test
:attr:`telegram.InlineQuery.query` against.
pass_groups (:obj:`bool`): Optional. Determines whether ``groups`` will be passed to the
pass_groups (:obj:`bool`): Determines whether ``groups`` will be passed to the
callback function.
pass_groupdict (:obj:`bool`): Optional. Determines whether ``groupdict``. will be passed to
pass_groupdict (:obj:`bool`): Determines whether ``groupdict``. will be passed to
the callback function.
pass_user_data (:obj:`bool`): Optional. Determines whether ``user_data`` will be passed to
pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to
the callback function.
pass_chat_data (:obj:`bool`): Optional. Determines whether ``chat_data`` will be passed to
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
the callback function.
Note:
@ -54,31 +53,46 @@ class InlineQueryHandler(Handler):
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:
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments.
It will be called when the :attr:`check_update` has determined that an update should be
processed by this handler.
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`.
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.
pattern (:obj:`str` | :obj:`Pattern`, optional): Regex pattern. If not ``None``,
``re.match`` is used on :attr:`telegram.InlineQuery.query` to determine if an update
should be handled by this handler.
pass_groups (:obj:`bool`, optional): If the callback should be passed the result of
``re.match(pattern, data).groups()`` as a keyword argument called ``groups``.
Default is ``False``
DEPRECATED: Please switch to context based callbacks.
pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of
``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``.
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.
"""
def __init__(self,
@ -113,37 +127,28 @@ class InlineQueryHandler(Handler):
Returns:
:obj:`bool`
"""
if isinstance(update, Update) and update.inline_query:
if self.pattern:
if update.inline_query.query:
match = re.match(self.pattern, update.inline_query.query)
return bool(match)
if match:
return match
else:
return True
def handle_update(self, update, dispatcher):
"""
Send the update to the :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
optional_args = self.collect_optional_args(dispatcher, update)
def collect_optional_args(self, dispatcher, update=None, check_result=None):
optional_args = super(InlineQueryHandler, self).collect_optional_args(dispatcher,
update, check_result)
if self.pattern:
match = re.match(self.pattern, update.inline_query.query)
if self.pass_groups:
optional_args['groups'] = match.groups()
optional_args['groups'] = check_result.groups()
if self.pass_groupdict:
optional_args['groupdict'] = match.groupdict()
optional_args['groupdict'] = check_result.groupdict()
return optional_args
return self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.InlineQueryHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
def collect_additional_context(self, context, update, dispatcher, check_result):
if self.pattern:
context.match = check_result

View file

@ -18,13 +18,17 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes JobQueue and Job."""
import datetime
import logging
import time
import datetime
import warnings
import weakref
from numbers import Number
from threading import Thread, Lock, Event
from queue import PriorityQueue, Empty
from threading import Thread, Lock, Event
from telegram.ext.callbackcontext import CallbackContext
from telegram.utils.deprecate import TelegramDeprecationWarning
class Days(object):
@ -37,16 +41,24 @@ class JobQueue(object):
Attributes:
_queue (:obj:`PriorityQueue`): The queue that holds the Jobs.
bot (:class:`telegram.Bot`): Bot that's send to the handlers.
Args:
bot (:class:`telegram.Bot`): The bot instance that should be passed to the jobs.
DEPRECATED: Use set_dispatcher instead.
"""
def __init__(self, bot):
def __init__(self, bot=None):
self._queue = PriorityQueue()
self.bot = bot
if bot:
warnings.warn("Passing bot to jobqueue is deprecated. Please use set_dispatcher "
"instead!", TelegramDeprecationWarning, stacklevel=2)
class MockDispatcher(object):
def __init__(self):
self.bot = bot
self.use_context = False
self._dispatcher = MockDispatcher()
else:
self._dispatcher = None
self.logger = logging.getLogger(self.__class__.__name__)
self.__start_lock = Lock()
self.__next_peek_lock = Lock() # to protect self._next_peek & self.__tick
@ -55,6 +67,9 @@ class JobQueue(object):
self._next_peek = None
self._running = False
def set_dispatcher(self, dispatcher):
self._dispatcher = dispatcher
def _put(self, job, next_t=None, last_t=None):
if next_t is None:
next_t = job.interval
@ -242,7 +257,7 @@ class JobQueue(object):
current_week_day = datetime.datetime.now().weekday()
if any(day == current_week_day for day in job.days):
self.logger.debug('Running job %s', job.name)
job.run(self.bot)
job.run(self._dispatcher)
except Exception:
self.logger.exception('An uncaught error was raised while executing job %s',
@ -367,9 +382,12 @@ class Job(object):
self._enabled = Event()
self._enabled.set()
def run(self, bot):
def run(self, dispatcher):
"""Executes the callback function."""
self.callback(bot, self)
if dispatcher.use_context:
self.callback(CallbackContext.from_job(self, dispatcher))
else:
self.callback(dispatcher.bot, self)
def schedule_removal(self):
"""

View file

@ -31,21 +31,21 @@ class MessageHandler(Handler):
filters (:obj:`Filter`): Only allow updates with these Filters. See
:mod:`telegram.ext.filters` for a full list of all available filters.
callback (:obj:`callable`): The callback function for this handler.
pass_update_queue (:obj:`bool`): Optional. Determines whether ``update_queue`` will be
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
passed to the callback function.
pass_job_queue (:obj:`bool`): Optional. Determines whether ``job_queue`` will be passed to
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
the callback function.
pass_user_data (:obj:`bool`): Optional. Determines whether ``user_data`` will be passed to
pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to
the callback function.
pass_chat_data (:obj:`bool`): Optional. Determines whether ``chat_data`` will be passed to
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
the callback function.
message_updates (:obj:`bool`): Optional. Should "normal" message updates be handled?
message_updates (:obj:`bool`): Should "normal" message updates be handled?
Default is ``True``.
channel_post_updates (:obj:`bool`): Optional. Should channel posts updates be handled?
channel_post_updates (:obj:`bool`): Should channel posts updates be handled?
Default is ``True``.
edited_updates (:obj:`bool`): Optional. Should "edited" message updates be handled?
edited_updates (:obj:`bool`): Should "edited" message updates be handled?
Default is ``False``.
allow_edited (:obj:`bool`): Optional. If the handler should also accept edited messages.
allow_edited (:obj:`bool`): If the handler should also accept edited messages.
Default is ``False`` - Deprecated. use edited_updates instead.
Note:
@ -54,26 +54,38 @@ class MessageHandler(Handler):
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:
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).
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments.
It will be called when the :attr:`check_update` has determined that an update should be
processed by this handler.
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`.
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.
message_updates (:obj:`bool`, optional): Should "normal" message updates be handled?
Default is ``True``.
channel_post_updates (:obj:`bool`, optional): Should channel posts updates be handled?
@ -117,13 +129,6 @@ class MessageHandler(Handler):
self.channel_post_updates = channel_post_updates
self.edited_updates = edited_updates
# We put this up here instead of with the rest of checking code
# in check_update since we don't wanna spam a ton
if isinstance(self.filters, list):
warnings.warn('Using a list of filters in MessageHandler is getting '
'deprecated, please use bitwise operators (& and |) '
'instead. More info: https://git.io/vPTbc.')
def _is_allowed_update(self, update):
return any([self.message_updates and update.message,
self.edited_updates and (update.edited_message or update.edited_channel_post),
@ -140,30 +145,7 @@ class MessageHandler(Handler):
"""
if isinstance(update, Update) and self._is_allowed_update(update):
if not self.filters:
res = True
return True
else:
message = update.effective_message
if isinstance(self.filters, list):
res = any(func(message) for func in self.filters)
else:
res = self.filters(message)
else:
res = False
return res
def handle_update(self, update, dispatcher):
"""Send the update to the :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
optional_args = self.collect_optional_args(dispatcher, update)
return self.callback(dispatcher.bot, update, **optional_args)
return self.filters(update.effective_message)

View file

@ -27,13 +27,13 @@ class PreCheckoutQueryHandler(Handler):
Attributes:
callback (:obj:`callable`): The callback function for this handler.
pass_update_queue (:obj:`bool`): Optional. Determines whether ``update_queue`` will be
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
passed to the callback function.
pass_job_queue (:obj:`bool`): Optional. Determines whether ``job_queue`` will be passed to
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
the callback function.
pass_user_data (:obj:`bool`): Optional. Determines whether ``user_data`` will be passed to
pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to
the callback function.
pass_chat_data (:obj:`bool`): Optional. Determines whether ``chat_data`` will be passed to
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
the callback function.
Note:
@ -42,38 +42,37 @@ class PreCheckoutQueryHandler(Handler):
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:
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments.
It will be called when the :attr:`check_update` has determined that an update should be
processed by this handler.
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`.
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``
DEPRECATED: Please switch to context based callbacks.
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``.
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.
"""
def __init__(self,
callback,
pass_update_queue=False,
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
super(PreCheckoutQueryHandler, 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)
def check_update(self, update):
"""Determines whether an update should be passed to this handlers :attr:`callback`.
@ -85,14 +84,3 @@ class PreCheckoutQueryHandler(Handler):
"""
return isinstance(update, Update) and update.pre_checkout_query
def handle_update(self, update, dispatcher):
"""Send the update to the :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
optional_args = self.collect_optional_args(dispatcher, update)
return self.callback(dispatcher.bot, update, **optional_args)

View file

@ -38,17 +38,17 @@ class RegexHandler(Handler):
Attributes:
pattern (:obj:`str` | :obj:`Pattern`): The regex pattern.
callback (:obj:`callable`): The callback function for this handler.
pass_groups (:obj:`bool`): Optional. Determines whether ``groups`` will be passed to the
pass_groups (:obj:`bool`): Determines whether ``groups`` will be passed to the
callback function.
pass_groupdict (:obj:`bool`): Optional. Determines whether ``groupdict``. will be passed to
pass_groupdict (:obj:`bool`): Determines whether ``groupdict``. will be passed to
the callback function.
pass_update_queue (:obj:`bool`): Optional. Determines whether ``update_queue`` will be
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
passed to the callback function.
pass_job_queue (:obj:`bool`): Optional. Determines whether ``job_queue`` will be passed to
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
the callback function.
pass_user_data (:obj:`bool`): Optional. Determines whether ``user_data`` will be passed to
pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to
the callback function.
pass_chat_data (:obj:`bool`): Optional. Determines whether ``chat_data`` will be passed to
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
the callback function.
Note:
@ -57,29 +57,43 @@ class RegexHandler(Handler):
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:
pattern (:obj:`str` | :obj:`Pattern`): The regex pattern.
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments.
It will be called when the :attr:`check_update` has determined that an update should be
processed by this handler.
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`.
pass_groups (:obj:`bool`, optional): If the callback should be passed the result of
``re.match(pattern, data).groups()`` as a keyword argument called ``groups``.
Default is ``False``
DEPRECATED: Please switch to context based callbacks.
pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of
``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``.
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.
message_updates (:obj:`bool`, optional): Should "normal" message updates be handled?
Default is ``True``.
channel_post_updates (:obj:`bool`, optional): Should channel posts updates be handled?
@ -106,8 +120,7 @@ class RegexHandler(Handler):
allow_edited=False,
message_updates=True,
channel_post_updates=False,
edited_updates=False
):
edited_updates=False):
if not message_updates and not channel_post_updates and not edited_updates:
raise ValueError(
'message_updates, channel_post_updates and edited_updates are all False')
@ -144,30 +157,23 @@ class RegexHandler(Handler):
"""
if not isinstance(update, Update) and not update.effective_message:
return False
return None
if any([self.message_updates and update.message,
self.edited_updates and (update.edited_message or update.edited_channel_post),
self.channel_post_updates and update.channel_post]) and \
update.effective_message.text:
match = re.match(self.pattern, update.effective_message.text)
return bool(match)
return False
def handle_update(self, update, dispatcher):
"""Send the update to the :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
optional_args = self.collect_optional_args(dispatcher, update)
match = re.match(self.pattern, update.effective_message.text)
if match:
return match
def collect_optional_args(self, dispatcher, update=None, check_result=None):
optional_args = super(RegexHandler, self).collect_optional_args(dispatcher, update,
check_result)
if self.pass_groups:
optional_args['groups'] = match.groups()
optional_args['groups'] = check_result.groups()
if self.pass_groupdict:
optional_args['groupdict'] = match.groupdict()
optional_args['groupdict'] = check_result.groupdict()
return optional_args
return self.callback(dispatcher.bot, update, **optional_args)
def collect_additional_context(self, context, update, dispatcher, check_result):
context.match = check_result

View file

@ -27,13 +27,13 @@ class ShippingQueryHandler(Handler):
Attributes:
callback (:obj:`callable`): The callback function for this handler.
pass_update_queue (:obj:`bool`): Optional. Determines whether ``update_queue`` will be
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
passed to the callback function.
pass_job_queue (:obj:`bool`): Optional. Determines whether ``job_queue`` will be passed to
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
the callback function.
pass_user_data (:obj:`bool`): Optional. Determines whether ``user_data`` will be passed to
pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to
the callback function.
pass_chat_data (:obj:`bool`): Optional. Determines whether ``chat_data`` will be passed to
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
the callback function.
Note:
@ -42,38 +42,37 @@ class ShippingQueryHandler(Handler):
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:
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments.
It will be called when the :attr:`check_update` has determined that an update should be
processed by this handler.
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`.
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.
"""
def __init__(self,
callback,
pass_update_queue=False,
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
super(ShippingQueryHandler, 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)
def check_update(self, update):
"""Determines whether an update should be passed to this handlers :attr:`callback`.
@ -85,14 +84,3 @@ class ShippingQueryHandler(Handler):
"""
return isinstance(update, Update) and update.shipping_query
def handle_update(self, update, dispatcher):
"""Send the update to the :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
optional_args = self.collect_optional_args(dispatcher, update)
return self.callback(dispatcher.bot, update, **optional_args)

View file

@ -33,31 +33,37 @@ class StringCommandHandler(Handler):
Attributes:
command (:obj:`str`): The command this handler should listen for.
callback (:obj:`callable`): The callback function for this handler.
pass_args (:obj:`bool`): Optional. Determines whether the handler should be passed
pass_args (:obj:`bool`): Determines whether the handler should be passed
``args``.
pass_update_queue (:obj:`bool`): Optional. Determines whether ``update_queue`` will be
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
passed to the callback function.
pass_job_queue (:obj:`bool`): Optional. Determines whether ``job_queue`` will be passed to
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
the callback function.
Args:
command (:obj:`str`): The command this handler should listen for.
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments.
It will be called when the :attr:`check_update` has determined that a command should be
processed by this handler.
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`.
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.
"""
@ -68,7 +74,9 @@ class StringCommandHandler(Handler):
pass_update_queue=False,
pass_job_queue=False):
super(StringCommandHandler, self).__init__(
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue)
self.command = command
self.pass_args = pass_args
@ -76,28 +84,24 @@ class StringCommandHandler(Handler):
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:obj:`str`): An incomming command.
update (:obj:`str`): An incoming command.
Returns:
:obj:`bool`
"""
if isinstance(update, string_types) and update.startswith('/'):
args = update[1:].split(' ')
if args[0] == self.command:
return args[1:]
return (isinstance(update, string_types) and update.startswith('/')
and update[1:].split(' ')[0] == self.command)
def handle_update(self, update, dispatcher):
"""Send the update to the :attr:`callback`.
Args:
update (:obj:`str`): An incomming command.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the command.
"""
optional_args = self.collect_optional_args(dispatcher)
def collect_optional_args(self, dispatcher, update=None, check_result=None):
optional_args = super(StringCommandHandler, self).collect_optional_args(dispatcher,
update,
check_result)
if self.pass_args:
optional_args['args'] = update.split()[1:]
optional_args['args'] = check_result
return optional_args
return self.callback(dispatcher.bot, update, **optional_args)
def collect_additional_context(self, context, update, dispatcher, check_result):
context.args = check_result

View file

@ -38,34 +38,43 @@ class StringRegexHandler(Handler):
Attributes:
pattern (:obj:`str` | :obj:`Pattern`): The regex pattern.
callback (:obj:`callable`): The callback function for this handler.
pass_groups (:obj:`bool`): Optional. Determines whether ``groups`` will be passed to the
pass_groups (:obj:`bool`): Determines whether ``groups`` will be passed to the
callback function.
pass_groupdict (:obj:`bool`): Optional. Determines whether ``groupdict``. will be passed to
pass_groupdict (:obj:`bool`): Determines whether ``groupdict``. will be passed to
the callback function.
pass_update_queue (:obj:`bool`): Optional. Determines whether ``update_queue`` will be
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
passed to the callback function.
pass_job_queue (:obj:`bool`): Optional. Determines whether ``job_queue`` will be passed to
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
the callback function.
Args:
pattern (:obj:`str` | :obj:`Pattern`): The regex pattern.
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments.
It will be called when the :attr:`check_update` has determined that an update should be
processed by this handler.
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`.
pass_groups (:obj:`bool`, optional): If the callback should be passed the result of
``re.match(pattern, data).groups()`` as a keyword argument called ``groups``.
Default is ``False``
DEPRECATED: Please switch to context based callbacks.
pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of
``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``.
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.
"""
@ -77,7 +86,9 @@ class StringRegexHandler(Handler):
pass_update_queue=False,
pass_job_queue=False):
super(StringRegexHandler, self).__init__(
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue)
if isinstance(pattern, string_types):
pattern = re.compile(pattern)
@ -90,28 +101,27 @@ class StringRegexHandler(Handler):
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:obj:`str`): An incomming command.
update (:obj:`str`): An incoming command.
Returns:
:obj:`bool`
"""
return isinstance(update, string_types) and bool(re.match(self.pattern, update))
if isinstance(update, string_types):
match = re.match(self.pattern, update)
if match:
return match
def handle_update(self, update, dispatcher):
"""Send the update to the :attr:`callback`.
def collect_optional_args(self, dispatcher, update=None, check_result=None):
optional_args = super(StringRegexHandler, self).collect_optional_args(dispatcher,
update, check_result)
if self.pattern:
if self.pass_groups:
optional_args['groups'] = check_result.groups()
if self.pass_groupdict:
optional_args['groupdict'] = check_result.groupdict()
return optional_args
Args:
update (:obj:`str`): An incomming command.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the command.
"""
optional_args = self.collect_optional_args(dispatcher)
match = re.match(self.pattern, update)
if self.pass_groups:
optional_args['groups'] = match.groups()
if self.pass_groupdict:
optional_args['groupdict'] = match.groupdict()
return self.callback(dispatcher.bot, update, **optional_args)
def collect_additional_context(self, context, update, dispatcher, check_result):
if self.pattern:
context.match = check_result

View file

@ -27,36 +27,48 @@ class TypeHandler(Handler):
Attributes:
type (:obj:`type`): The ``type`` of updates this handler should process.
callback (:obj:`callable`): The callback function for this handler.
strict (:obj:`bool`): Optional. Use ``type`` instead of ``isinstance``.
Default is ``False``
pass_update_queue (:obj:`bool`): Optional. Determines whether ``update_queue`` will be
strict (:obj:`bool`): Use ``type`` instead of ``isinstance``. Default is ``False``.
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
passed to the callback function.
pass_job_queue (:obj:`bool`): Optional. Determines whether ``job_queue`` will be passed to
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
the callback function.
Args:
type (:obj:`type`): The ``type`` of updates this handler should process, as
determined by ``isinstance``
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments.
It will be called when the :attr:`check_update` has determined that an update should be
processed by this handler.
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`.
strict (:obj:`bool`, optional): Use ``type`` instead of ``isinstance``.
Default is ``False``
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.
"""
def __init__(self, type, callback, strict=False, pass_update_queue=False,
def __init__(self,
type,
callback,
strict=False,
pass_update_queue=False,
pass_job_queue=False):
super(TypeHandler, self).__init__(
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue)
self.type = type
self.strict = strict
@ -70,20 +82,7 @@ class TypeHandler(Handler):
:obj:`bool`
"""
if not self.strict:
return isinstance(update, self.type)
else:
return type(update) is self.type
def handle_update(self, update, dispatcher):
"""Send the update to the :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
optional_args = self.collect_optional_args(dispatcher)
return self.callback(dispatcher.bot, update, **optional_args)

View file

@ -57,6 +57,7 @@ class Updater(object):
running (:obj:`bool`): Indicates if the updater is running.
persistence (:class:`telegram.ext.BasePersistence`): Optional. The persistence class to
store data that should be persistent over restarts.
use_context (:obj:`bool`, optional): ``True`` if using context based callbacks.
Args:
token (:obj:`str`, optional): The bot's token given by the @BotFather.
@ -75,6 +76,9 @@ class Updater(object):
`telegram.utils.request.Request` object (ignored if `bot` argument is used). The
request_kwargs are very useful for the advanced users who would like to control the
default timeouts and/or control the proxy used for http communication.
use_context (:obj:`bool`, optional): If set to ``True`` Use the context based callback API.
During the deprecation period of the old API the default is ``False``. **New users**:
set this to ``True``.
persistence (:class:`telegram.ext.BasePersistence`, optional): The persistence class to
store data that should be persistent over restarts.
@ -97,7 +101,8 @@ class Updater(object):
private_key_password=None,
user_sig_handler=None,
request_kwargs=None,
persistence=None):
persistence=None,
use_context=False):
if (token is None) and (bot is None):
raise ValueError('`token` or `bot` must be passed')
@ -132,7 +137,7 @@ class Updater(object):
private_key_password=private_key_password)
self.user_sig_handler = user_sig_handler
self.update_queue = Queue()
self.job_queue = JobQueue(self.bot)
self.job_queue = JobQueue()
self.__exception_event = Event()
self.persistence = persistence
self.dispatcher = Dispatcher(
@ -141,7 +146,9 @@ class Updater(object):
job_queue=self.job_queue,
workers=workers,
exception_event=self.__exception_event,
persistence=self.persistence)
persistence=persistence,
use_context=use_context)
self.job_queue.set_dispatcher(self.dispatcher)
self.last_update_id = 0
self.running = False
self.is_idle = False

View file

@ -72,7 +72,8 @@ def provider_token(bot_info):
def create_dp(bot):
# Dispatcher is heavy to init (due to many threads and such) so we have a single session
# scoped one here, but before each test, reset it (dp fixture below)
dispatcher = Dispatcher(bot, Queue(), job_queue=JobQueue(bot), workers=2)
dispatcher = Dispatcher(bot, Queue(), job_queue=JobQueue(), workers=2, use_context=False)
dispatcher.job_queue.set_dispatcher(dispatcher)
thr = Thread(target=dispatcher.start)
thr.start()
sleep(2)
@ -109,6 +110,13 @@ def dp(_dp):
Dispatcher._Dispatcher__singleton_semaphore.release()
@pytest.fixture(scope='function')
def cdp(dp):
dp.use_context = True
yield dp
dp.use_context = False
@pytest.fixture(scope='function')
def updater(bot):
up = Updater(bot=bot, workers=2)

View file

@ -0,0 +1,98 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import pytest
from telegram import Update, Message, Chat, User, TelegramError
from telegram.ext import CallbackContext
class TestCallbackContext(object):
def test_non_context_dp(self, dp):
with pytest.raises(ValueError):
CallbackContext(dp)
def test_from_job(self, cdp):
job = cdp.job_queue.run_once(lambda x: x, 10)
callback_context = CallbackContext.from_job(job, cdp)
assert callback_context.job is job
assert callback_context.chat_data is None
assert callback_context.user_data is None
assert callback_context.bot is cdp.bot
assert callback_context.job_queue is cdp.job_queue
assert callback_context.update_queue is cdp.update_queue
def test_from_update(self, cdp):
update = Update(0, message=Message(0, User(1, 'user', False), None, Chat(1, 'chat')))
callback_context = CallbackContext.from_update(update, cdp)
assert callback_context.chat_data == {}
assert callback_context.user_data == {}
assert callback_context.bot is cdp.bot
assert callback_context.job_queue is cdp.job_queue
assert callback_context.update_queue is cdp.update_queue
callback_context_same_user_chat = CallbackContext.from_update(update, cdp)
callback_context.chat_data['test'] = 'chat'
callback_context.user_data['test'] = 'user'
assert callback_context_same_user_chat.chat_data is callback_context.chat_data
assert callback_context_same_user_chat.user_data is callback_context.user_data
update_other_user_chat = Update(0, message=Message(0, User(2, 'user', False),
None, Chat(2, 'chat')))
callback_context_other_user_chat = CallbackContext.from_update(update_other_user_chat, cdp)
assert callback_context_other_user_chat.chat_data is not callback_context.chat_data
assert callback_context_other_user_chat.user_data is not callback_context.user_data
def test_from_update_not_update(self, cdp):
callback_context = CallbackContext.from_update(None, cdp)
assert callback_context.chat_data is None
assert callback_context.user_data is None
assert callback_context.bot is cdp.bot
assert callback_context.job_queue is cdp.job_queue
assert callback_context.update_queue is cdp.update_queue
callback_context = CallbackContext.from_update('', cdp)
assert callback_context.chat_data is None
assert callback_context.user_data is None
assert callback_context.bot is cdp.bot
assert callback_context.job_queue is cdp.job_queue
assert callback_context.update_queue is cdp.update_queue
def test_from_error(self, cdp):
error = TelegramError('test')
update = Update(0, message=Message(0, User(1, 'user', False), None, Chat(1, 'chat')))
callback_context = CallbackContext.from_error(update, error, cdp)
assert callback_context.error is error
assert callback_context.chat_data == {}
assert callback_context.user_data == {}
assert callback_context.bot is cdp.bot
assert callback_context.job_queue is cdp.job_queue
assert callback_context.update_queue is cdp.update_queue

View file

@ -16,11 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest
from telegram import (Update, CallbackQuery, Bot, Message, User, Chat, InlineQuery,
ChosenInlineResult, ShippingQuery, PreCheckoutQuery)
from telegram.ext import CallbackQueryHandler
from telegram.ext import CallbackQueryHandler, CallbackContext, JobQueue
message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
@ -47,7 +49,7 @@ def false_update(request):
@pytest.fixture(scope='function')
def callback_query(bot):
return Update(0, callback_query=CallbackQuery(2, None, None, data='test data'))
return Update(0, callback_query=CallbackQuery(2, User(1, '', False), None, data='test data'))
class TestCallbackQueryHandler(object):
@ -80,6 +82,22 @@ class TestCallbackQueryHandler(object):
if groupdict is not None:
self.test_flag = groupdict == {'begin': 't', 'end': ' data'}
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
context.chat_data is None and
isinstance(update.callback_query, CallbackQuery))
def callback_context_pattern(self, update, context):
if context.match.groups():
self.test_flag = context.match.groups() == ('t', ' data')
if context.match.groupdict():
self.test_flag = context.match.groupdict() == {'begin': 't', 'end': ' data'}
def test_basic(self, dp, callback_query):
handler = CallbackQueryHandler(self.callback_basic)
dp.add_handler(handler)
@ -117,14 +135,16 @@ class TestCallbackQueryHandler(object):
assert self.test_flag
def test_pass_user_or_chat_data(self, dp, callback_query):
handler = CallbackQueryHandler(self.callback_data_1, pass_user_data=True)
handler = CallbackQueryHandler(self.callback_data_1,
pass_user_data=True)
dp.add_handler(handler)
dp.process_update(callback_query)
assert self.test_flag
dp.remove_handler(handler)
handler = CallbackQueryHandler(self.callback_data_1, pass_chat_data=True)
handler = CallbackQueryHandler(self.callback_data_1,
pass_chat_data=True)
dp.add_handler(handler)
self.test_flag = False
@ -132,7 +152,8 @@ class TestCallbackQueryHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = CallbackQueryHandler(self.callback_data_2, pass_chat_data=True,
handler = CallbackQueryHandler(self.callback_data_2,
pass_chat_data=True,
pass_user_data=True)
dp.add_handler(handler)
@ -141,14 +162,16 @@ class TestCallbackQueryHandler(object):
assert self.test_flag
def test_pass_job_or_update_queue(self, dp, callback_query):
handler = CallbackQueryHandler(self.callback_queue_1, pass_job_queue=True)
handler = CallbackQueryHandler(self.callback_queue_1,
pass_job_queue=True)
dp.add_handler(handler)
dp.process_update(callback_query)
assert self.test_flag
dp.remove_handler(handler)
handler = CallbackQueryHandler(self.callback_queue_1, pass_update_queue=True)
handler = CallbackQueryHandler(self.callback_queue_1,
pass_update_queue=True)
dp.add_handler(handler)
self.test_flag = False
@ -156,7 +179,8 @@ class TestCallbackQueryHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = CallbackQueryHandler(self.callback_queue_2, pass_job_queue=True,
handler = CallbackQueryHandler(self.callback_queue_2,
pass_job_queue=True,
pass_update_queue=True)
dp.add_handler(handler)
@ -167,3 +191,26 @@ class TestCallbackQueryHandler(object):
def test_other_update_types(self, false_update):
handler = CallbackQueryHandler(self.callback_basic)
assert not handler.check_update(false_update)
def test_context(self, cdp, callback_query):
handler = CallbackQueryHandler(self.callback_context)
cdp.add_handler(handler)
cdp.process_update(callback_query)
assert self.test_flag
def test_context_pattern(self, cdp, callback_query):
handler = CallbackQueryHandler(self.callback_context_pattern,
pattern=r'(?P<begin>.*)est(?P<end>.*)')
cdp.add_handler(handler)
cdp.process_update(callback_query)
assert self.test_flag
cdp.remove_handler(handler)
handler = CallbackQueryHandler(self.callback_context_pattern,
pattern=r'(t)est(.*)')
cdp.add_handler(handler)
cdp.process_update(callback_query)
assert self.test_flag

View file

@ -16,12 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest
from telegram import (Update, Chat, Bot, ChosenInlineResult, User, Message, CallbackQuery,
InlineQuery, ShippingQuery, PreCheckoutQuery)
from telegram.ext import ChosenInlineResultHandler
from telegram.ext import ChosenInlineResultHandler, CallbackContext, JobQueue
message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
@ -78,6 +79,16 @@ class TestChosenInlineResultHandler(object):
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 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
context.chat_data is None and
isinstance(update.chosen_inline_result, ChosenInlineResult))
def test_basic(self, dp, chosen_inline_result):
handler = ChosenInlineResultHandler(self.callback_basic)
dp.add_handler(handler)
@ -87,14 +98,16 @@ class TestChosenInlineResultHandler(object):
assert self.test_flag
def test_pass_user_or_chat_data(self, dp, chosen_inline_result):
handler = ChosenInlineResultHandler(self.callback_data_1, pass_user_data=True)
handler = ChosenInlineResultHandler(self.callback_data_1,
pass_user_data=True)
dp.add_handler(handler)
dp.process_update(chosen_inline_result)
assert self.test_flag
dp.remove_handler(handler)
handler = ChosenInlineResultHandler(self.callback_data_1, pass_chat_data=True)
handler = ChosenInlineResultHandler(self.callback_data_1,
pass_chat_data=True)
dp.add_handler(handler)
self.test_flag = False
@ -102,8 +115,8 @@ class TestChosenInlineResultHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = ChosenInlineResultHandler(self.callback_data_2, pass_chat_data=True,
pass_user_data=True)
handler = ChosenInlineResultHandler(self.callback_data_2,
pass_chat_data=True, pass_user_data=True)
dp.add_handler(handler)
self.test_flag = False
@ -111,14 +124,16 @@ class TestChosenInlineResultHandler(object):
assert self.test_flag
def test_pass_job_or_update_queue(self, dp, chosen_inline_result):
handler = ChosenInlineResultHandler(self.callback_queue_1, pass_job_queue=True)
handler = ChosenInlineResultHandler(self.callback_queue_1,
pass_job_queue=True)
dp.add_handler(handler)
dp.process_update(chosen_inline_result)
assert self.test_flag
dp.remove_handler(handler)
handler = ChosenInlineResultHandler(self.callback_queue_1, pass_update_queue=True)
handler = ChosenInlineResultHandler(self.callback_queue_1,
pass_update_queue=True)
dp.add_handler(handler)
self.test_flag = False
@ -126,8 +141,8 @@ class TestChosenInlineResultHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = ChosenInlineResultHandler(self.callback_queue_2, pass_job_queue=True,
pass_update_queue=True)
handler = ChosenInlineResultHandler(self.callback_queue_2,
pass_job_queue=True, pass_update_queue=True)
dp.add_handler(handler)
self.test_flag = False
@ -137,3 +152,10 @@ class TestChosenInlineResultHandler(object):
def test_other_update_types(self, false_update):
handler = ChosenInlineResultHandler(self.callback_basic)
assert not handler.check_update(false_update)
def test_context(self, cdp, chosen_inline_result):
handler = ChosenInlineResultHandler(self.callback_context)
cdp.add_handler(handler)
cdp.process_update(chosen_inline_result)
assert self.test_flag

View file

@ -16,12 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
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
from telegram.ext import CommandHandler, Filters, BaseFilter, CallbackContext, JobQueue
message = Message(1, User(1, '', False), None, Chat(1, ''), text='test')
@ -48,7 +49,7 @@ def false_update(request):
@pytest.fixture(scope='function')
def message(bot):
return Message(1, None, None, None, bot=bot)
return Message(1, User(1, '', False), None, Chat(1, ''), bot=bot)
class TestCommandHandler(object):
@ -83,51 +84,73 @@ class TestCommandHandler(object):
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, message):
handler = CommandHandler('test', self.callback_basic)
dp.add_handler(handler)
message.text = '/test'
assert handler.check_update(Update(0, message))
dp.process_update(Update(0, message))
assert self.test_flag
message.text = '/nottest'
assert not handler.check_update(Update(0, message))
check = handler.check_update(Update(0, message))
assert check is None or check is False
message.text = 'test'
assert not handler.check_update(Update(0, message))
check = handler.check_update(Update(0, message))
assert check is None or check is False
message.text = 'not /test at start'
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_command_list(self, message):
handler = CommandHandler(['test', 'start'], self.callback_basic)
message.text = '/test'
assert handler.check_update(Update(0, message))
check = handler.check_update(Update(0, message))
message.text = '/start'
assert handler.check_update(Update(0, message))
check = handler.check_update(Update(0, message))
message.text = '/stop'
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_edited(self, message):
handler = CommandHandler('test', self.callback_basic, allow_edited=False)
handler = CommandHandler('test', self.callback_basic,
allow_edited=False)
message.text = '/test'
assert handler.check_update(Update(0, message))
assert not handler.check_update(Update(0, edited_message=message))
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
assert handler.check_update(Update(0, message))
assert handler.check_update(Update(0, edited_message=message))
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
def test_directed_commands(self, message):
handler = CommandHandler('test', self.callback_basic)
message.text = '/test@{}'.format(message.bot.username)
assert handler.check_update(Update(0, message))
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))
@ -137,10 +160,12 @@ class TestCommandHandler(object):
message.chat = Chat(-23, 'group')
message.text = '/test'
assert handler.check_update(Update(0, message))
check = handler.check_update(Update(0, message))
assert check is not None and check is not False
message.chat = Chat(23, 'private')
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_pass_args(self, dp, message):
handler = CommandHandler('test', self.ch_callback_args, pass_args=True)
@ -170,7 +195,8 @@ class TestCommandHandler(object):
dp.add_handler(handler)
message.text = '/test\nfoobar'
assert handler.check_update(Update(0, message))
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
@ -180,7 +206,8 @@ class TestCommandHandler(object):
dp.add_handler(handler)
message.text = 'a'
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_single_slash(self, dp, message):
# Regression test for https://github.com/python-telegram-bot/python-telegram-bot/issues/871
@ -188,13 +215,16 @@ class TestCommandHandler(object):
dp.add_handler(handler)
message.text = '/'
assert not handler.check_update(Update(0, message))
check = handler.check_update(Update(0, message))
assert check is None or check is False
message.text = '/ test'
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_pass_user_or_chat_data(self, dp, message):
handler = CommandHandler('test', self.callback_data_1, pass_user_data=True)
handler = CommandHandler('test', self.callback_data_1,
pass_user_data=True)
dp.add_handler(handler)
message.text = '/test'
@ -202,7 +232,8 @@ class TestCommandHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = CommandHandler('test', self.callback_data_1, pass_chat_data=True)
handler = CommandHandler('test', self.callback_data_1,
pass_chat_data=True)
dp.add_handler(handler)
self.test_flag = False
@ -210,7 +241,8 @@ class TestCommandHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = CommandHandler('test', self.callback_data_2, pass_chat_data=True,
handler = CommandHandler('test', self.callback_data_2,
pass_chat_data=True,
pass_user_data=True)
dp.add_handler(handler)
@ -219,7 +251,8 @@ class TestCommandHandler(object):
assert self.test_flag
def test_pass_job_or_update_queue(self, dp, message):
handler = CommandHandler('test', self.callback_queue_1, pass_job_queue=True)
handler = CommandHandler('test', self.callback_queue_1,
pass_job_queue=True)
dp.add_handler(handler)
message.text = '/test'
@ -227,7 +260,8 @@ class TestCommandHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = CommandHandler('test', self.callback_queue_1, pass_update_queue=True)
handler = CommandHandler('test', self.callback_queue_1,
pass_update_queue=True)
dp.add_handler(handler)
self.test_flag = False
@ -235,7 +269,8 @@ class TestCommandHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = CommandHandler('test', self.callback_queue_2, pass_job_queue=True,
handler = CommandHandler('test', self.callback_queue_2,
pass_job_queue=True,
pass_update_queue=True)
dp.add_handler(handler)
@ -245,7 +280,8 @@ class TestCommandHandler(object):
def test_other_update_types(self, false_update):
handler = CommandHandler('test', self.callback_basic)
assert not handler.check_update(false_update)
check = handler.check_update(false_update)
assert check is None or check is False
def test_filters_for_wrong_command(self, message):
"""Filters should not be executed if the command does not match the handler"""
@ -259,9 +295,31 @@ class TestCommandHandler(object):
test_filter = TestFilter()
handler = CommandHandler('foo', self.callback_basic, filters=test_filter)
handler = CommandHandler('foo', self.callback_basic,
filters=test_filter)
message.text = '/bar'
handler.check_update(Update(0, message=message))
check = handler.check_update(Update(0, message=message))
assert check is None or check is False
assert not test_filter.tested
def test_context(self, cdp, message):
handler = CommandHandler('test', self.callback_context)
cdp.add_handler(handler)
message.text = '/test'
cdp.process_update(Update(0, message))
assert self.test_flag
def test_context_args(self, cdp, message):
handler = CommandHandler('test', self.callback_context_args)
cdp.add_handler(handler)
message.text = '/test'
cdp.process_update(Update(0, message))
assert not self.test_flag
message.text = '/test one two'
cdp.process_update(Update(0, message))
assert self.test_flag

View file

@ -52,7 +52,8 @@ class TestConversationHandler(object):
self.current_state = dict()
self.entry_points = [CommandHandler('start', self.start)]
self.states = {
self.THIRSTY: [CommandHandler('brew', self.brew), CommandHandler('wait', self.start)],
self.THIRSTY: [CommandHandler('brew', self.brew),
CommandHandler('wait', self.start)],
self.BREWING: [CommandHandler('pourCoffee', self.drink)],
self.DRINKING:
[CommandHandler('startCoding', self.code),
@ -266,7 +267,8 @@ class TestConversationHandler(object):
def test_end_on_first_message(self, dp, bot, user1):
handler = ConversationHandler(
entry_points=[CommandHandler('start', self.start_end)], states={}, fallbacks=[])
entry_points=[CommandHandler('start', self.start_end)], states={},
fallbacks=[])
dp.add_handler(handler)
# User starts the state machine and immediately ends it.
@ -278,7 +280,8 @@ class TestConversationHandler(object):
start_end_async = (lambda bot, update: dp.run_async(self.start_end, bot, update))
handler = ConversationHandler(
entry_points=[CommandHandler('start', start_end_async)], states={}, fallbacks=[])
entry_points=[CommandHandler('start', start_end_async)], states={},
fallbacks=[])
dp.add_handler(handler)
# User starts the state machine with an async function that immediately ends the
@ -297,7 +300,8 @@ class TestConversationHandler(object):
def test_per_chat_message_without_chat(self, bot, user1):
handler = ConversationHandler(
entry_points=[CommandHandler('start', self.start_end)], states={}, fallbacks=[])
entry_points=[CommandHandler('start', self.start_end)], states={},
fallbacks=[])
cbq = CallbackQuery(0, user1, None, None, bot=bot)
update = Update(0, callback_query=cbq)
assert not handler.check_update(update)

View file

@ -16,15 +16,17 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import sys
from queue import Queue
from threading import current_thread
from time import sleep
import pytest
from telegram import TelegramError, Message, User, Chat, Update
from telegram.ext import MessageHandler, Filters, CommandHandler
from telegram import TelegramError, Message, User, Chat, Update, Bot
from telegram.ext import MessageHandler, Filters, CommandHandler, CallbackContext, JobQueue
from telegram.ext.dispatcher import run_async, Dispatcher, DispatcherHandlerStop
from telegram.utils.deprecate import TelegramDeprecationWarning
from tests.conftest import create_dp
@ -67,6 +69,14 @@ class TestDispatcher(object):
if update_queue is not None:
self.received = update.message
def callback_context(self, update, context):
if (isinstance(context, CallbackContext) and
isinstance(context.bot, Bot) and
isinstance(context.update_queue, Queue) and
isinstance(context.job_queue, JobQueue) and
isinstance(context.error, TelegramError)):
self.received = context.error.message
def test_error_handler(self, dp):
dp.add_error_handler(self.error_handler)
error = TelegramError('Unauthorized.')
@ -326,3 +336,17 @@ class TestDispatcher(object):
dp.process_update(update)
assert passed == ['start1', 'error', err]
assert passed[2] is err
def test_error_handler_context(self, cdp):
cdp.add_error_handler(self.callback_context)
error = TelegramError('Unauthorized.')
cdp.update_queue.put(error)
sleep(.1)
assert self.received == 'Unauthorized.'
@pytest.mark.skipif(sys.version_info < (3, 0), reason='pytest fails this for no reason')
def test_non_context_deprecation(self, dp):
with pytest.warns(TelegramDeprecationWarning):
Dispatcher(dp.bot, dp.update_queue, job_queue=dp.job_queue, workers=0,
use_context=False)

View file

@ -16,11 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest
from telegram import (Update, CallbackQuery, Bot, Message, User, Chat, InlineQuery,
ChosenInlineResult, ShippingQuery, PreCheckoutQuery, Location)
from telegram.ext import InlineQueryHandler
from telegram.ext import InlineQueryHandler, CallbackContext, JobQueue
message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
@ -84,6 +86,22 @@ class TestCallbackQueryHandler(object):
if groupdict is not None:
self.test_flag = groupdict == {'begin': 't', 'end': ' query'}
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
context.chat_data is None and
isinstance(update.inline_query, InlineQuery))
def callback_context_pattern(self, update, context):
if context.match.groups():
self.test_flag = context.match.groups() == ('t', ' query')
if context.match.groupdict():
self.test_flag = context.match.groupdict() == {'begin': 't', 'end': ' query'}
def test_basic(self, dp, inline_query):
handler = InlineQueryHandler(self.callback_basic)
dp.add_handler(handler)
@ -94,7 +112,8 @@ class TestCallbackQueryHandler(object):
assert self.test_flag
def test_with_pattern(self, inline_query):
handler = InlineQueryHandler(self.callback_basic, pattern='(?P<begin>.*)est(?P<end>.*)')
handler = InlineQueryHandler(self.callback_basic,
pattern='(?P<begin>.*)est(?P<end>.*)')
assert handler.check_update(inline_query)
@ -152,7 +171,8 @@ class TestCallbackQueryHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = InlineQueryHandler(self.callback_queue_1, pass_update_queue=True)
handler = InlineQueryHandler(self.callback_queue_1,
pass_update_queue=True)
dp.add_handler(handler)
self.test_flag = False
@ -171,3 +191,26 @@ class TestCallbackQueryHandler(object):
def test_other_update_types(self, false_update):
handler = InlineQueryHandler(self.callback_basic)
assert not handler.check_update(false_update)
def test_context(self, cdp, inline_query):
handler = InlineQueryHandler(self.callback_context)
cdp.add_handler(handler)
cdp.process_update(inline_query)
assert self.test_flag
def test_context_pattern(self, cdp, inline_query):
handler = InlineQueryHandler(self.callback_context_pattern,
pattern=r'(?P<begin>.*)est(?P<end>.*)')
cdp.add_handler(handler)
cdp.process_update(inline_query)
assert self.test_flag
cdp.remove_handler(handler)
handler = InlineQueryHandler(self.callback_context_pattern,
pattern=r'(t)est(.*)')
cdp.add_handler(handler)
cdp.process_update(inline_query)
assert self.test_flag

View file

@ -18,18 +18,22 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
import datetime
import os
import sys
import time
from queue import Queue
from time import sleep
import pytest
from flaky import flaky
from telegram.ext import JobQueue, Updater, Job
from telegram.ext import JobQueue, Updater, Job, CallbackContext
from telegram.utils.deprecate import TelegramDeprecationWarning
@pytest.fixture(scope='function')
def job_queue(bot):
jq = JobQueue(bot)
def job_queue(bot, _dp):
jq = JobQueue()
jq.set_dispatcher(_dp)
jq.start()
yield jq
jq.stop()
@ -62,6 +66,16 @@ class TestJobQueue(object):
def job_datetime_tests(self, bot, job):
self.job_time = time.time()
def job_context_based_callback(self, context):
if (isinstance(context, CallbackContext) and
isinstance(context.job, Job) and
isinstance(context.update_queue, Queue) and
context.job.context == 2 and
context.chat_data is None and
context.user_data is None and
context.job_queue is context.job.job_queue):
self.result += 1
def test_run_once(self, job_queue):
job_queue.run_once(self.job_run_once, 0.01)
sleep(0.02)
@ -244,3 +258,15 @@ class TestJobQueue(object):
assert job_queue.jobs() == (job1, job2, job3)
assert job_queue.get_jobs_by_name('name1') == (job1, job2)
assert job_queue.get_jobs_by_name('name2') == (job3,)
@pytest.mark.skipif(sys.version_info < (3, 0), reason='pytest fails this for no reason')
def test_bot_in_init_deprecation(self, bot):
with pytest.warns(TelegramDeprecationWarning):
JobQueue(bot)
def test_context_based_callback(self, job_queue):
job_queue.run_once(self.job_context_based_callback, 0.01, context=2)
sleep(0.03)
assert self.result == 0

View file

@ -16,12 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest
from telegram import (Message, Update, Chat, Bot, User, CallbackQuery, InlineQuery,
ChosenInlineResult, ShippingQuery, PreCheckoutQuery)
from telegram.ext import Filters, MessageHandler
from telegram.ext import Filters, MessageHandler, CallbackContext, JobQueue
message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
@ -45,7 +46,7 @@ def false_update(request):
@pytest.fixture(scope='class')
def message(bot):
return Message(1, None, None, None, bot=bot)
return Message(1, User(1, '', False), None, Chat(1, ''), bot=bot)
class TestMessageHandler(object):
@ -72,6 +73,25 @@ class TestMessageHandler(object):
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 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.chat_data, dict) and
(
(isinstance(context.user_data, dict) and
(isinstance(update.message, Message) or
isinstance(update.edited_message, Message)))
or
(context.user_data is None and
(isinstance(update.channel_post, Message) or
isinstance(update.edited_channel_post, Message)))
)
)
def test_basic(self, dp, message):
handler = MessageHandler(None, self.callback_basic)
dp.add_handler(handler)
@ -90,8 +110,9 @@ class TestMessageHandler(object):
assert handler.check_update(Update(0, edited_channel_post=message))
def test_channel_post(self, message):
handler = MessageHandler(None, self.callback_basic, edited_updates=False,
message_updates=False, channel_post_updates=True)
handler = MessageHandler(None, self.callback_basic,
edited_updates=False, message_updates=False,
channel_post_updates=True)
assert not handler.check_update(Update(0, edited_message=message))
assert not handler.check_update(Update(0, message=message))
@ -109,8 +130,9 @@ class TestMessageHandler(object):
def test_allow_edited(self, message):
with pytest.warns(UserWarning):
handler = MessageHandler(None, self.callback_basic, message_updates=True,
allow_edited=True, channel_post_updates=False)
handler = MessageHandler(None, self.callback_basic,
message_updates=True, allow_edited=True,
channel_post_updates=False)
assert handler.check_update(Update(0, edited_message=message))
assert handler.check_update(Update(0, message=message))
@ -132,14 +154,16 @@ class TestMessageHandler(object):
assert not handler.check_update(Update(0, message))
def test_pass_user_or_chat_data(self, dp, message):
handler = MessageHandler(None, self.callback_data_1, pass_user_data=True)
handler = MessageHandler(None, self.callback_data_1,
pass_user_data=True)
dp.add_handler(handler)
dp.process_update(Update(0, message=message))
assert self.test_flag
dp.remove_handler(handler)
handler = MessageHandler(None, self.callback_data_1, pass_chat_data=True)
handler = MessageHandler(None, self.callback_data_1,
pass_chat_data=True)
dp.add_handler(handler)
self.test_flag = False
@ -147,8 +171,8 @@ class TestMessageHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = MessageHandler(None, self.callback_data_2, pass_chat_data=True,
pass_user_data=True)
handler = MessageHandler(None, self.callback_data_2,
pass_chat_data=True, pass_user_data=True)
dp.add_handler(handler)
self.test_flag = False
@ -156,14 +180,16 @@ class TestMessageHandler(object):
assert self.test_flag
def test_pass_job_or_update_queue(self, dp, message):
handler = MessageHandler(None, self.callback_queue_1, pass_job_queue=True)
handler = MessageHandler(None, self.callback_queue_1,
pass_job_queue=True)
dp.add_handler(handler)
dp.process_update(Update(0, message=message))
assert self.test_flag
dp.remove_handler(handler)
handler = MessageHandler(None, self.callback_queue_1, pass_update_queue=True)
handler = MessageHandler(None, self.callback_queue_1,
pass_update_queue=True)
dp.add_handler(handler)
self.test_flag = False
@ -171,8 +197,8 @@ class TestMessageHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = MessageHandler(None, self.callback_queue_2, pass_job_queue=True,
pass_update_queue=True)
handler = MessageHandler(None, self.callback_queue_2,
pass_job_queue=True, pass_update_queue=True)
dp.add_handler(handler)
self.test_flag = False
@ -182,3 +208,23 @@ class TestMessageHandler(object):
def test_other_update_types(self, false_update):
handler = MessageHandler(None, self.callback_basic, edited_updates=True)
assert not handler.check_update(false_update)
def test_context(self, cdp, message):
handler = MessageHandler(None, self.callback_context,
edited_updates=True, channel_post_updates=True)
cdp.add_handler(handler)
cdp.process_update(Update(0, message=message))
assert self.test_flag
self.test_flag = False
cdp.process_update(Update(0, edited_message=message))
assert self.test_flag
self.test_flag = False
cdp.process_update(Update(0, channel_post=message))
assert self.test_flag
self.test_flag = False
cdp.process_update(Update(0, edited_channel_post=message))
assert self.test_flag

View file

@ -16,12 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest
from telegram import (Update, Chat, Bot, ChosenInlineResult, User, Message, CallbackQuery,
InlineQuery, ShippingQuery, PreCheckoutQuery)
from telegram.ext import PreCheckoutQueryHandler
from telegram.ext import PreCheckoutQueryHandler, CallbackContext, JobQueue
message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
@ -78,6 +79,16 @@ class TestPreCheckoutQueryHandler(object):
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 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
context.chat_data is None and
isinstance(update.pre_checkout_query, PreCheckoutQuery))
def test_basic(self, dp, pre_checkout_query):
handler = PreCheckoutQueryHandler(self.callback_basic)
dp.add_handler(handler)
@ -87,14 +98,16 @@ class TestPreCheckoutQueryHandler(object):
assert self.test_flag
def test_pass_user_or_chat_data(self, dp, pre_checkout_query):
handler = PreCheckoutQueryHandler(self.callback_data_1, pass_user_data=True)
handler = PreCheckoutQueryHandler(self.callback_data_1,
pass_user_data=True)
dp.add_handler(handler)
dp.process_update(pre_checkout_query)
assert self.test_flag
dp.remove_handler(handler)
handler = PreCheckoutQueryHandler(self.callback_data_1, pass_chat_data=True)
handler = PreCheckoutQueryHandler(self.callback_data_1,
pass_chat_data=True)
dp.add_handler(handler)
self.test_flag = False
@ -102,7 +115,8 @@ class TestPreCheckoutQueryHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = PreCheckoutQueryHandler(self.callback_data_2, pass_chat_data=True,
handler = PreCheckoutQueryHandler(self.callback_data_2,
pass_chat_data=True,
pass_user_data=True)
dp.add_handler(handler)
@ -111,14 +125,16 @@ class TestPreCheckoutQueryHandler(object):
assert self.test_flag
def test_pass_job_or_update_queue(self, dp, pre_checkout_query):
handler = PreCheckoutQueryHandler(self.callback_queue_1, pass_job_queue=True)
handler = PreCheckoutQueryHandler(self.callback_queue_1,
pass_job_queue=True)
dp.add_handler(handler)
dp.process_update(pre_checkout_query)
assert self.test_flag
dp.remove_handler(handler)
handler = PreCheckoutQueryHandler(self.callback_queue_1, pass_update_queue=True)
handler = PreCheckoutQueryHandler(self.callback_queue_1,
pass_update_queue=True)
dp.add_handler(handler)
self.test_flag = False
@ -126,7 +142,8 @@ class TestPreCheckoutQueryHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = PreCheckoutQueryHandler(self.callback_queue_2, pass_job_queue=True,
handler = PreCheckoutQueryHandler(self.callback_queue_2,
pass_job_queue=True,
pass_update_queue=True)
dp.add_handler(handler)
@ -137,3 +154,10 @@ class TestPreCheckoutQueryHandler(object):
def test_other_update_types(self, false_update):
handler = PreCheckoutQueryHandler(self.callback_basic)
assert not handler.check_update(false_update)
def test_context(self, cdp, pre_checkout_query):
handler = PreCheckoutQueryHandler(self.callback_context)
cdp.add_handler(handler)
cdp.process_update(pre_checkout_query)
assert self.test_flag

View file

@ -16,12 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest
from telegram import (Message, Update, Chat, Bot, User, CallbackQuery, InlineQuery,
ChosenInlineResult, ShippingQuery, PreCheckoutQuery)
from telegram.ext import RegexHandler
from telegram.ext import RegexHandler, CallbackContext, JobQueue
message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
@ -45,7 +46,7 @@ def false_update(request):
@pytest.fixture(scope='class')
def message(bot):
return Message(1, None, None, None, text='test message', bot=bot)
return Message(1, User(1, '', False), None, Chat(1, ''), text='test message', bot=bot)
class TestRegexHandler(object):
@ -78,6 +79,22 @@ class TestRegexHandler(object):
if groupdict is not None:
self.test_flag = groupdict == {'begin': 't', 'end': ' message'}
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_pattern(self, update, context):
if context.match.groups():
self.test_flag = context.match.groups() == ('t', ' message')
if context.match.groupdict():
self.test_flag = context.match.groupdict() == {'begin': 't', 'end': ' message'}
def test_basic(self, dp, message):
handler = RegexHandler('.*', self.callback_basic)
dp.add_handler(handler)
@ -140,7 +157,8 @@ class TestRegexHandler(object):
def test_allow_edited(self, message):
with pytest.warns(UserWarning):
handler = RegexHandler('.*', self.callback_basic, message_updates=True,
handler = RegexHandler('.*', self.callback_basic,
message_updates=True,
allow_edited=True)
assert handler.check_update(Update(0, edited_message=message))
@ -185,7 +203,8 @@ class TestRegexHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = RegexHandler('.*', self.callback_queue_1, pass_update_queue=True)
handler = RegexHandler('.*', self.callback_queue_1,
pass_update_queue=True)
dp.add_handler(handler)
self.test_flag = False
@ -204,3 +223,24 @@ class TestRegexHandler(object):
def test_other_update_types(self, false_update):
handler = RegexHandler('.*', self.callback_basic, edited_updates=True)
assert not handler.check_update(false_update)
def test_context(self, cdp, message):
handler = RegexHandler(r'(t)est(.*)', self.callback_context)
cdp.add_handler(handler)
cdp.process_update(Update(0, message=message))
assert self.test_flag
def test_context_pattern(self, cdp, message):
handler = RegexHandler(r'(t)est(.*)', self.callback_context_pattern)
cdp.add_handler(handler)
cdp.process_update(Update(0, message=message))
assert self.test_flag
cdp.remove_handler(handler)
handler = RegexHandler(r'(t)est(.*)', self.callback_context_pattern)
cdp.add_handler(handler)
cdp.process_update(Update(0, message=message))
assert self.test_flag

View file

@ -16,12 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest
from telegram import (Update, Chat, Bot, ChosenInlineResult, User, Message, CallbackQuery,
InlineQuery, ShippingQuery, PreCheckoutQuery, ShippingAddress)
from telegram.ext import ShippingQueryHandler
from telegram.ext import ShippingQueryHandler, CallbackContext, JobQueue
message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
@ -79,6 +80,16 @@ class TestShippingQueryHandler(object):
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 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
context.chat_data is None and
isinstance(update.shipping_query, ShippingQuery))
def test_basic(self, dp, shiping_query):
handler = ShippingQueryHandler(self.callback_basic)
dp.add_handler(handler)
@ -88,14 +99,16 @@ class TestShippingQueryHandler(object):
assert self.test_flag
def test_pass_user_or_chat_data(self, dp, shiping_query):
handler = ShippingQueryHandler(self.callback_data_1, pass_user_data=True)
handler = ShippingQueryHandler(self.callback_data_1,
pass_user_data=True)
dp.add_handler(handler)
dp.process_update(shiping_query)
assert self.test_flag
dp.remove_handler(handler)
handler = ShippingQueryHandler(self.callback_data_1, pass_chat_data=True)
handler = ShippingQueryHandler(self.callback_data_1,
pass_chat_data=True)
dp.add_handler(handler)
self.test_flag = False
@ -103,7 +116,8 @@ class TestShippingQueryHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = ShippingQueryHandler(self.callback_data_2, pass_chat_data=True,
handler = ShippingQueryHandler(self.callback_data_2,
pass_chat_data=True,
pass_user_data=True)
dp.add_handler(handler)
@ -112,14 +126,16 @@ class TestShippingQueryHandler(object):
assert self.test_flag
def test_pass_job_or_update_queue(self, dp, shiping_query):
handler = ShippingQueryHandler(self.callback_queue_1, pass_job_queue=True)
handler = ShippingQueryHandler(self.callback_queue_1,
pass_job_queue=True)
dp.add_handler(handler)
dp.process_update(shiping_query)
assert self.test_flag
dp.remove_handler(handler)
handler = ShippingQueryHandler(self.callback_queue_1, pass_update_queue=True)
handler = ShippingQueryHandler(self.callback_queue_1,
pass_update_queue=True)
dp.add_handler(handler)
self.test_flag = False
@ -127,7 +143,8 @@ class TestShippingQueryHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = ShippingQueryHandler(self.callback_queue_2, pass_job_queue=True,
handler = ShippingQueryHandler(self.callback_queue_2,
pass_job_queue=True,
pass_update_queue=True)
dp.add_handler(handler)
@ -138,3 +155,10 @@ class TestShippingQueryHandler(object):
def test_other_update_types(self, false_update):
handler = ShippingQueryHandler(self.callback_basic)
assert not handler.check_update(false_update)
def test_context(self, cdp, shiping_query):
handler = ShippingQueryHandler(self.callback_context)
cdp.add_handler(handler)
cdp.process_update(shiping_query)
assert self.test_flag

View file

@ -16,11 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest
from telegram import (Bot, Update, Message, User, Chat, CallbackQuery, InlineQuery,
ChosenInlineResult, ShippingQuery, PreCheckoutQuery)
from telegram.ext import StringCommandHandler
from telegram.ext import StringCommandHandler, CallbackContext, JobQueue
message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
@ -71,20 +73,37 @@ class TestStringCommandHandler(object):
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, str) and
isinstance(context.update_queue, Queue) and
isinstance(context.job_queue, JobQueue) and
context.user_data is None and
context.chat_data is None)
def callback_context_args(self, update, context):
self.test_flag = context.args == ['one', 'two']
def test_basic(self, dp):
handler = StringCommandHandler('test', self.callback_basic)
dp.add_handler(handler)
assert handler.check_update('/test')
check = handler.check_update('/test')
assert check is not None and check is not False
dp.process_update('/test')
assert self.test_flag
assert not handler.check_update('/nottest')
assert not handler.check_update('not /test in front')
assert handler.check_update('/test followed by text')
check = handler.check_update('/nottest')
assert check is None or check is False
check = handler.check_update('not /test in front')
assert check is None or check is False
check = handler.check_update('/test followed by text')
assert check is not None and check is not False
def test_pass_args(self, dp):
handler = StringCommandHandler('test', self.sch_callback_args, pass_args=True)
handler = StringCommandHandler('test', self.sch_callback_args,
pass_args=True)
dp.add_handler(handler)
dp.process_update('/test')
@ -95,14 +114,16 @@ class TestStringCommandHandler(object):
assert self.test_flag
def test_pass_job_or_update_queue(self, dp):
handler = StringCommandHandler('test', self.callback_queue_1, pass_job_queue=True)
handler = StringCommandHandler('test', self.callback_queue_1,
pass_job_queue=True)
dp.add_handler(handler)
dp.process_update('/test')
assert self.test_flag
dp.remove_handler(handler)
handler = StringCommandHandler('test', self.callback_queue_1, pass_update_queue=True)
handler = StringCommandHandler('test', self.callback_queue_1,
pass_update_queue=True)
dp.add_handler(handler)
self.test_flag = False
@ -110,7 +131,8 @@ class TestStringCommandHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = StringCommandHandler('test', self.callback_queue_2, pass_job_queue=True,
handler = StringCommandHandler('test', self.callback_queue_2,
pass_job_queue=True,
pass_update_queue=True)
dp.add_handler(handler)
@ -121,3 +143,20 @@ class TestStringCommandHandler(object):
def test_other_update_types(self, false_update):
handler = StringCommandHandler('test', self.callback_basic)
assert not handler.check_update(false_update)
def test_context(self, cdp):
handler = StringCommandHandler('test', self.callback_context)
cdp.add_handler(handler)
cdp.process_update('/test')
assert self.test_flag
def test_context_args(self, cdp):
handler = StringCommandHandler('test', self.callback_context_args)
cdp.add_handler(handler)
cdp.process_update('/test')
assert not self.test_flag
cdp.process_update('/test one two')
assert self.test_flag

View file

@ -16,11 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest
from telegram import (Bot, Update, Message, User, Chat, CallbackQuery, InlineQuery,
ChosenInlineResult, ShippingQuery, PreCheckoutQuery)
from telegram.ext import StringRegexHandler
from telegram.ext import StringRegexHandler, CallbackContext, JobQueue
message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
@ -71,6 +73,19 @@ class TestStringRegexHandler(object):
if groupdict is not None:
self.test_flag = groupdict == {'begin': 't', 'end': ' message'}
def callback_context(self, update, context):
self.test_flag = (isinstance(context, CallbackContext) and
isinstance(context.bot, Bot) and
isinstance(update, str) and
isinstance(context.update_queue, Queue) and
isinstance(context.job_queue, JobQueue))
def callback_context_pattern(self, update, context):
if context.match.groups():
self.test_flag = context.match.groups() == ('t', ' message')
if context.match.groupdict():
self.test_flag = context.match.groupdict() == {'begin': 't', 'end': ' message'}
def test_basic(self, dp):
handler = StringRegexHandler('(?P<begin>.*)est(?P<end>.*)', self.callback_basic)
dp.add_handler(handler)
@ -99,14 +114,16 @@ class TestStringRegexHandler(object):
assert self.test_flag
def test_pass_job_or_update_queue(self, dp):
handler = StringRegexHandler('test', self.callback_queue_1, pass_job_queue=True)
handler = StringRegexHandler('test', self.callback_queue_1,
pass_job_queue=True)
dp.add_handler(handler)
dp.process_update('test')
assert self.test_flag
dp.remove_handler(handler)
handler = StringRegexHandler('test', self.callback_queue_1, pass_update_queue=True)
handler = StringRegexHandler('test', self.callback_queue_1,
pass_update_queue=True)
dp.add_handler(handler)
self.test_flag = False
@ -114,8 +131,8 @@ class TestStringRegexHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = StringRegexHandler('test', self.callback_queue_2, pass_job_queue=True,
pass_update_queue=True)
handler = StringRegexHandler('test', self.callback_queue_2,
pass_job_queue=True, pass_update_queue=True)
dp.add_handler(handler)
self.test_flag = False
@ -125,3 +142,24 @@ class TestStringRegexHandler(object):
def test_other_update_types(self, false_update):
handler = StringRegexHandler('test', self.callback_basic)
assert not handler.check_update(false_update)
def test_context(self, cdp):
handler = StringRegexHandler(r'(t)est(.*)', self.callback_context)
cdp.add_handler(handler)
cdp.process_update('test message')
assert self.test_flag
def test_context_pattern(self, cdp):
handler = StringRegexHandler(r'(t)est(.*)', self.callback_context_pattern)
cdp.add_handler(handler)
cdp.process_update('test message')
assert self.test_flag
cdp.remove_handler(handler)
handler = StringRegexHandler(r'(t)est(.*)', self.callback_context_pattern)
cdp.add_handler(handler)
cdp.process_update('test message')
assert self.test_flag

View file

@ -17,11 +17,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/].
from collections import OrderedDict
from queue import Queue
import pytest
from telegram import Bot
from telegram.ext import TypeHandler
from telegram.ext import TypeHandler, CallbackContext, JobQueue
class TestTypeHandler(object):
@ -42,6 +43,15 @@ class TestTypeHandler(object):
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 callback_context(self, update, context):
self.test_flag = (isinstance(context, CallbackContext) and
isinstance(context.bot, Bot) and
isinstance(update, dict) and
isinstance(context.update_queue, Queue) and
isinstance(context.job_queue, JobQueue) and
context.user_data is None and
context.chat_data is None)
def test_basic(self, dp):
handler = TypeHandler(dict, self.callback_basic)
dp.add_handler(handler)
@ -65,7 +75,8 @@ class TestTypeHandler(object):
assert self.test_flag
dp.remove_handler(handler)
handler = TypeHandler(dict, self.callback_queue_1, pass_update_queue=True)
handler = TypeHandler(dict, self.callback_queue_1,
pass_update_queue=True)
dp.add_handler(handler)
self.test_flag = False
@ -80,3 +91,10 @@ class TestTypeHandler(object):
self.test_flag = False
dp.process_update({'a': 1, 'b': 2})
assert self.test_flag
def test_context(self, cdp):
handler = TypeHandler(dict, self.callback_context)
cdp.add_handler(handler)
cdp.process_update({'a': 1, 'b': 2})
assert self.test_flag