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 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** **2018-09-01**
*Released 11.1.0* *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.jobqueue
telegram.ext.messagequeue telegram.ext.messagequeue
telegram.ext.delayqueue telegram.ext.delayqueue
telegram.ext.callbackcontext
Handlers 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. bot.
""" """
import logging
from telegram import (ReplyKeyboardMarkup, ReplyKeyboardRemove) from telegram import (ReplyKeyboardMarkup, ReplyKeyboardRemove)
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, RegexHandler, from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, RegexHandler,
ConversationHandler) ConversationHandler)
import logging
# Enable logging # Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO) level=logging.INFO)
@ -32,7 +32,7 @@ logger = logging.getLogger(__name__)
GENDER, PHOTO, LOCATION, BIO = range(4) GENDER, PHOTO, LOCATION, BIO = range(4)
def start(bot, update): def start(update, context):
reply_keyboard = [['Boy', 'Girl', 'Other']] reply_keyboard = [['Boy', 'Girl', 'Other']]
update.message.reply_text( update.message.reply_text(
@ -44,7 +44,7 @@ def start(bot, update):
return GENDER return GENDER
def gender(bot, update): def gender(update, context):
user = update.message.from_user user = update.message.from_user
logger.info("Gender of %s: %s", user.first_name, update.message.text) 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, ' update.message.reply_text('I see! Please send me a photo of yourself, '
@ -54,9 +54,9 @@ def gender(bot, update):
return PHOTO return PHOTO
def photo(bot, update): def photo(update, context):
user = update.message.from_user 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') photo_file.download('user_photo.jpg')
logger.info("Photo of %s: %s", user.first_name, '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, ' update.message.reply_text('Gorgeous! Now, send me your location please, '
@ -65,7 +65,7 @@ def photo(bot, update):
return LOCATION return LOCATION
def skip_photo(bot, update): def skip_photo(update, context):
user = update.message.from_user user = update.message.from_user
logger.info("User %s did not send a photo.", user.first_name) 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, ' 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 return LOCATION
def location(bot, update): def location(update, context):
user = update.message.from_user user = update.message.from_user
user_location = update.message.location user_location = update.message.location
logger.info("Location of %s: %f / %f", user.first_name, user_location.latitude, logger.info("Location of %s: %f / %f", user.first_name, user_location.latitude,
@ -85,7 +85,7 @@ def location(bot, update):
return BIO return BIO
def skip_location(bot, update): def skip_location(update, context):
user = update.message.from_user user = update.message.from_user
logger.info("User %s did not send a location.", user.first_name) logger.info("User %s did not send a location.", user.first_name)
update.message.reply_text('You seem a bit paranoid! ' update.message.reply_text('You seem a bit paranoid! '
@ -94,7 +94,7 @@ def skip_location(bot, update):
return BIO return BIO
def bio(bot, update): def bio(update, context):
user = update.message.from_user user = update.message.from_user
logger.info("Bio of %s: %s", user.first_name, update.message.text) 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.') 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 return ConversationHandler.END
def cancel(bot, update): def cancel(update, context):
user = update.message.from_user user = update.message.from_user
logger.info("User %s canceled the conversation.", user.first_name) logger.info("User %s canceled the conversation.", user.first_name)
update.message.reply_text('Bye! I hope we can talk again some day.', update.message.reply_text('Bye! I hope we can talk again some day.',
@ -111,14 +111,16 @@ def cancel(bot, update):
return ConversationHandler.END return ConversationHandler.END
def error(bot, update, error): def error(update, context):
"""Log Errors caused by Updates.""" """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(): def main():
# Create the EventHandler and pass it your bot's token. # 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 # Get the dispatcher to register handlers
dp = updater.dispatcher 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. bot.
""" """
import logging
from telegram import ReplyKeyboardMarkup from telegram import ReplyKeyboardMarkup
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, RegexHandler, from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, RegexHandler,
ConversationHandler) ConversationHandler)
import logging
# Enable logging # Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO) level=logging.INFO)
@ -46,7 +46,7 @@ def facts_to_str(user_data):
return "\n".join(facts).join(['\n', '\n']) return "\n".join(facts).join(['\n', '\n'])
def start(bot, update): def start(update, context):
update.message.reply_text( update.message.reply_text(
"Hi! My name is Doctor Botter. I will hold a more complex conversation with you. " "Hi! My name is Doctor Botter. I will hold a more complex conversation with you. "
"Why don't you tell me something about yourself?", "Why don't you tell me something about yourself?",
@ -55,23 +55,24 @@ def start(bot, update):
return CHOOSING return CHOOSING
def regular_choice(bot, update, user_data): def regular_choice(update, context):
text = update.message.text text = update.message.text
user_data['choice'] = text context.user_data['choice'] = text
update.message.reply_text( update.message.reply_text(
'Your {}? Yes, I would love to hear about that!'.format(text.lower())) 'Your {}? Yes, I would love to hear about that!'.format(text.lower()))
return TYPING_REPLY return TYPING_REPLY
def custom_choice(bot, update): def custom_choice(update, context):
update.message.reply_text('Alright, please send me the category first, ' update.message.reply_text('Alright, please send me the category first, '
'for example "Most impressive skill"') 'for example "Most impressive skill"')
return TYPING_CHOICE return TYPING_CHOICE
def received_information(bot, update, user_data): def received_information(update, context):
user_data = context.user_data
text = update.message.text text = update.message.text
category = user_data['choice'] category = user_data['choice']
user_data[category] = text user_data[category] = text
@ -85,7 +86,8 @@ def received_information(bot, update, user_data):
return CHOOSING return CHOOSING
def done(bot, update, user_data): def done(update, context):
user_data = context.user_data
if 'choice' in user_data: if 'choice' in user_data:
del user_data['choice'] del user_data['choice']
@ -97,14 +99,16 @@ def done(bot, update, user_data):
return ConversationHandler.END return ConversationHandler.END
def error(bot, update, error): def error(update, context):
"""Log Errors caused by Updates.""" """Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error) logger.warning('Update "%s" caused error "%s"', update, error)
def main(): def main():
# Create the Updater and pass it your bot's token. # 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 # Get the dispatcher to register handlers
dp = updater.dispatcher 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. bot.
""" """
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
import logging import logging
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
# Enable logging # Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO) level=logging.INFO)
@ -29,30 +30,32 @@ logger = logging.getLogger(__name__)
# Define a few command handlers. These usually take the two arguments bot and # Define a few command handlers. These usually take the two arguments bot and
# update. Error handlers also receive the raised TelegramError object in error. # 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.""" """Send a message when the command /start is issued."""
update.message.reply_text('Hi!') update.message.reply_text('Hi!')
def help(bot, update): def help(update, context):
"""Send a message when the command /help is issued.""" """Send a message when the command /help is issued."""
update.message.reply_text('Help!') update.message.reply_text('Help!')
def echo(bot, update): def echo(update, context):
"""Echo the user message.""" """Echo the user message."""
update.message.reply_text(update.message.text) update.message.reply_text(update.message.text)
def error(bot, update, error): def error(update, context):
"""Log Errors caused by Updates.""" """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(): def main():
"""Start the bot.""" """Start the bot."""
# Create the EventHandler and pass it your bot's token. # 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 # Get the dispatcher to register handlers
dp = updater.dispatcher 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 Press Ctrl-C on the command line or send a signal to the process to stop the
bot. bot.
""" """
import logging
from uuid import uuid4 from uuid import uuid4
from telegram.utils.helpers import escape_markdown
from telegram import InlineQueryResultArticle, ParseMode, \ from telegram import InlineQueryResultArticle, ParseMode, \
InputTextMessageContent InputTextMessageContent
from telegram.ext import Updater, InlineQueryHandler, CommandHandler from telegram.ext import Updater, InlineQueryHandler, CommandHandler
import logging from telegram.utils.helpers import escape_markdown
# Enable logging # Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', 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 # Define a few command handlers. These usually take the two arguments bot and
# update. Error handlers also receive the raised TelegramError object in error. # 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.""" """Send a message when the command /start is issued."""
update.message.reply_text('Hi!') update.message.reply_text('Hi!')
def help(bot, update): def help(update, context):
"""Send a message when the command /help is issued.""" """Send a message when the command /help is issued."""
update.message.reply_text('Help!') update.message.reply_text('Help!')
def inlinequery(bot, update): def inlinequery(update, context):
"""Handle the inline query.""" """Handle the inline query."""
query = update.inline_query.query query = update.inline_query.query
results = [ results = [
@ -69,14 +68,16 @@ def inlinequery(bot, update):
update.inline_query.answer(results) update.inline_query.answer(results)
def error(bot, update, error): def error(update, context):
"""Log Errors caused by Updates.""" """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(): def main():
# Create the Updater and pass it your bot's token. # 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 # Get the dispatcher to register handlers
dp = updater.dispatcher dp = updater.dispatcher

View file

@ -5,6 +5,7 @@
# This program is dedicated to the public domain under the CC0 license. # This program is dedicated to the public domain under the CC0 license.
""" """
import logging import logging
from telegram import InlineKeyboardButton, InlineKeyboardMarkup from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler 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__) logger = logging.getLogger(__name__)
def start(bot, update): def start(update, context):
keyboard = [[InlineKeyboardButton("Option 1", callback_data='1'), keyboard = [[InlineKeyboardButton("Option 1", callback_data='1'),
InlineKeyboardButton("Option 2", callback_data='2')], InlineKeyboardButton("Option 2", callback_data='2')],
@ -24,26 +25,26 @@ def start(bot, update):
update.message.reply_text('Please choose:', reply_markup=reply_markup) update.message.reply_text('Please choose:', reply_markup=reply_markup)
def button(bot, update): def button(update, context):
query = update.callback_query query = update.callback_query
bot.edit_message_text(text="Selected option: {}".format(query.data), query.edit_message_text(text="Selected option: {}".format(query.data))
chat_id=query.message.chat_id,
message_id=query.message.message_id)
def help(bot, update): def help(update, context):
update.message.reply_text("Use /start to test this bot.") update.message.reply_text("Use /start to test this bot.")
def error(bot, update, error): def error(update, context):
"""Log Errors caused by Updates.""" """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(): def main():
# Create the Updater and pass it your bot's token. # 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(CommandHandler('start', start))
updater.dispatcher.add_handler(CallbackQueryHandler(button)) updater.dispatcher.add_handler(CallbackQueryHandler(button))

View file

@ -6,10 +6,11 @@
This program is dedicated to the public domain under the CC0 license. This program is dedicated to the public domain under the CC0 license.
""" """
import logging
from telegram import (LabeledPrice, ShippingOption) from telegram import (LabeledPrice, ShippingOption)
from telegram.ext import (Updater, CommandHandler, MessageHandler, from telegram.ext import (Updater, CommandHandler, MessageHandler,
Filters, PreCheckoutQueryHandler, ShippingQueryHandler) Filters, PreCheckoutQueryHandler, ShippingQueryHandler)
import logging
# Enable logging # Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', 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__) logger = logging.getLogger(__name__)
def error(bot, update, error): def error(update, context):
"""Log Errors caused by Updates.""" """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 = "Use /shipping to get an invoice for shipping-payment, "
msg += "or /noshipping for an invoice without shipping." msg += "or /noshipping for an invoice without shipping."
update.message.reply_text(msg) 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 chat_id = update.message.chat_id
title = "Payment Example" title = "Payment Example"
description = "Payment Example using python-telegram-bot" 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, # optionally pass need_name=True, need_phone_number=True,
# need_email=True, need_shipping_address=True, is_flexible=True # need_email=True, need_shipping_address=True, is_flexible=True
bot.sendInvoice(chat_id, title, description, payload, context.bot.send_invoice(chat_id, title, description, payload,
provider_token, start_parameter, currency, prices, provider_token, start_parameter, currency, prices,
need_name=True, need_phone_number=True, need_name=True, need_phone_number=True,
need_email=True, need_shipping_address=True, is_flexible=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 chat_id = update.message.chat_id
title = "Payment Example" title = "Payment Example"
description = "Payment Example using python-telegram-bot" 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, # optionally pass need_name=True, need_phone_number=True,
# need_email=True, need_shipping_address=True, is_flexible=True # need_email=True, need_shipping_address=True, is_flexible=True
bot.sendInvoice(chat_id, title, description, payload, context.bot.send_invoice(chat_id, title, description, payload,
provider_token, start_parameter, currency, prices) provider_token, start_parameter, currency, prices)
def shipping_callback(bot, update): def shipping_callback(update, context):
query = update.shipping_query query = update.shipping_query
# check the payload, is this from your bot? # check the payload, is this from your bot?
if query.invoice_payload != 'Custom-Payload': if query.invoice_payload != 'Custom-Payload':
# answer False pre_checkout_query # answer False pre_checkout_query
bot.answer_shipping_query(shipping_query_id=query.id, ok=False, query.answer(ok=False, error_message="Something went wrong...")
error_message="Something went wrong...")
return return
else: else:
options = list() options = list()
@ -89,31 +89,31 @@ def shipping_callback(bot, update):
# an array of LabeledPrice objects # an array of LabeledPrice objects
price_list = [LabeledPrice('B1', 150), LabeledPrice('B2', 200)] price_list = [LabeledPrice('B1', 150), LabeledPrice('B2', 200)]
options.append(ShippingOption('2', 'Shipping Option B', price_list)) options.append(ShippingOption('2', 'Shipping Option B', price_list))
bot.answer_shipping_query(shipping_query_id=query.id, ok=True, query.answer(ok=True, shipping_options=options)
shipping_options=options)
# after (optional) shipping, it's the pre-checkout # after (optional) shipping, it's the pre-checkout
def precheckout_callback(bot, update): def precheckout_callback(update, context):
query = update.pre_checkout_query query = update.pre_checkout_query
# check the payload, is this from your bot? # check the payload, is this from your bot?
if query.invoice_payload != 'Custom-Payload': if query.invoice_payload != 'Custom-Payload':
# answer False pre_checkout_query # answer False pre_checkout_query
bot.answer_pre_checkout_query(pre_checkout_query_id=query.id, ok=False, query.answer(ok=False, error_message="Something went wrong...")
error_message="Something went wrong...")
else: 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... # 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? # do something after successful receive of payment?
update.message.reply_text("Thank you for your payment!") update.message.reply_text("Thank you for your payment!")
def main(): def main():
# Create the EventHandler and pass it your bot's token. # Create the Updater and pass it your bot's token.
updater = Updater(token="BOT_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 # Get the dispatcher to register handlers
dp = updater.dispatcher 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. bot.
""" """
from telegram.ext import Updater, CommandHandler
import logging import logging
from telegram.ext import Updater, CommandHandler
# Enable logging # Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO) level=logging.INFO)
@ -31,28 +32,29 @@ logger = logging.getLogger(__name__)
# Define a few command handlers. These usually take the two arguments bot and # Define a few command handlers. These usually take the two arguments bot and
# update. Error handlers also receive the raised TelegramError object in error. # 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') update.message.reply_text('Hi! Use /set <seconds> to set a timer')
def alarm(bot, job): def alarm(context):
"""Send the alarm message.""" """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.""" """Add a job to the queue."""
chat_id = update.message.chat_id chat_id = update.message.chat_id
try: try:
# args[0] should contain the time for the timer in seconds # args[0] should contain the time for the timer in seconds
due = int(args[0]) due = int(context.args[0])
if due < 0: if due < 0:
update.message.reply_text('Sorry we can not go back to future!') update.message.reply_text('Sorry we can not go back to future!')
return return
# Add job to queue # Add job to queue
job = job_queue.run_once(alarm, due, context=chat_id) job = context.job_queue.run_once(alarm, due, context=chat_id)
chat_data['job'] = job context.chat_data['job'] = job
update.message.reply_text('Timer successfully set!') 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>') 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.""" """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') update.message.reply_text('You have no active timer')
return return
job = chat_data['job'] job = context.chat_data['job']
job.schedule_removal() job.schedule_removal()
del chat_data['job'] del context.chat_data['job']
update.message.reply_text('Timer successfully unset!') update.message.reply_text('Timer successfully unset!')
def error(bot, update, error): def error(update, context):
"""Log Errors caused by Updates.""" """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(): def main():
"""Run bot.""" """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 # Get the dispatcher to register handlers
dp = updater.dispatcher dp = updater.dispatcher

View file

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

View file

@ -21,13 +21,14 @@
from .basepersistence import BasePersistence from .basepersistence import BasePersistence
from .picklepersistence import PicklePersistence from .picklepersistence import PicklePersistence
from .dictpersistence import DictPersistence from .dictpersistence import DictPersistence
from .handler import Handler
from .callbackcontext import CallbackContext
from .dispatcher import Dispatcher, DispatcherHandlerStop, run_async from .dispatcher import Dispatcher, DispatcherHandlerStop, run_async
from .jobqueue import JobQueue, Job from .jobqueue import JobQueue, Job
from .updater import Updater from .updater import Updater
from .callbackqueryhandler import CallbackQueryHandler from .callbackqueryhandler import CallbackQueryHandler
from .choseninlineresulthandler import ChosenInlineResultHandler from .choseninlineresulthandler import ChosenInlineResultHandler
from .commandhandler import CommandHandler from .commandhandler import CommandHandler
from .handler import Handler
from .inlinequeryhandler import InlineQueryHandler from .inlinequeryhandler import InlineQueryHandler
from .messagehandler import MessageHandler from .messagehandler import MessageHandler
from .filters import BaseFilter, Filters from .filters import BaseFilter, Filters
@ -46,5 +47,5 @@ __all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler',
'MessageHandler', 'BaseFilter', 'Filters', 'RegexHandler', 'StringCommandHandler', 'MessageHandler', 'BaseFilter', 'Filters', 'RegexHandler', 'StringCommandHandler',
'StringRegexHandler', 'TypeHandler', 'ConversationHandler', 'StringRegexHandler', 'TypeHandler', 'ConversationHandler',
'PreCheckoutQueryHandler', 'ShippingQueryHandler', 'MessageQueue', 'DelayQueue', 'PreCheckoutQueryHandler', 'ShippingQueryHandler', 'MessageQueue', 'DelayQueue',
'DispatcherHandlerStop', 'run_async', 'BasePersistence', 'PicklePersistence', 'DispatcherHandlerStop', 'run_async', 'CallbackContext', 'BasePersistence',
'DictPersistence') '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: Attributes:
callback (:obj:`callable`): The callback function for this handler. 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. 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. the callback function.
pattern (:obj:`str` | `Pattern`): Optional. Regex pattern to test pattern (:obj:`str` | `Pattern`): Optional. Regex pattern to test
:attr:`telegram.CallbackQuery.data` against. :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. 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. 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. 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. the callback function.
Note: 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 either the user or the chat that the update was sent in. For each update from the same user
or in the same chat, it will be the same ``dict``. or in the same chat, it will be the same ``dict``.
Note that this is DEPRECATED, and you should use context based callbacks. See
https://git.io/vp113 for more info.
Args: Args:
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments. callback (:obj:`callable`): The callback function for this handler. Will be called when
It will be called when the :attr:`check_update` has determined that an update should be :attr:`check_update` has determined that an update should be processed by this handler.
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 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`` ``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` 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``. 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 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 ``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` :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``. 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`` 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 is used on :attr:`telegram.CallbackQuery.data` to determine if an update should be
handled by this handler. handled by this handler.
pass_groups (:obj:`bool`, optional): If the callback should be passed the result of 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``. ``re.match(pattern, data).groups()`` as a keyword argument called ``groups``.
Default is ``False`` Default is ``False``
DEPRECATED: Please switch to context based callbacks.
pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of 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``. ``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``.
Default is ``False`` Default is ``False``
DEPRECATED: Please switch to context based callbacks.
pass_user_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called 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``. ``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 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``. ``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 self.pattern:
if update.callback_query.data: if update.callback_query.data:
match = re.match(self.pattern, update.callback_query.data) match = re.match(self.pattern, update.callback_query.data)
return bool(match) if match:
return match
else: else:
return True return True
def handle_update(self, update, dispatcher): def collect_optional_args(self, dispatcher, update=None, check_result=None):
"""Send the update to the :attr:`callback`. optional_args = super(CallbackQueryHandler, self).collect_optional_args(dispatcher,
update,
Args: check_result)
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)
if self.pattern: if self.pattern:
match = re.match(self.pattern, update.callback_query.data)
if self.pass_groups: if self.pass_groups:
optional_args['groups'] = match.groups() optional_args['groups'] = check_result.groups()
if self.pass_groupdict: 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/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the ChosenInlineResultHandler class.""" """This module contains the ChosenInlineResultHandler class."""
from .handler import Handler
from telegram import Update from telegram import Update
from telegram.utils.deprecate import deprecate from .handler import Handler
class ChosenInlineResultHandler(Handler): class ChosenInlineResultHandler(Handler):
@ -28,13 +27,13 @@ class ChosenInlineResultHandler(Handler):
Attributes: Attributes:
callback (:obj:`callable`): The callback function for this handler. 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. 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. 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. 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. the callback function.
Note: 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 either the user or the chat that the update was sent in. For each update from the same user
or in the same chat, it will be the same ``dict``. or in the same chat, it will be the same ``dict``.
Note that this is DEPRECATED, and you should use context based callbacks. See
https://git.io/vp113 for more info.
Args: Args:
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments. callback (:obj:`callable`): The callback function for this handler. Will be called when
It will be called when the :attr:`check_update` has determined that an update should be :attr:`check_update` has determined that an update should be processed by this handler.
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 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`` ``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` 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``. 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 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 ``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` :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``. 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 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``. ``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 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``. ``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): def check_update(self, update):
"""Determines whether an update should be passed to this handlers :attr:`callback`. """Determines whether an update should be passed to this handlers :attr:`callback`.
@ -86,20 +84,3 @@ class ChosenInlineResultHandler(Handler):
""" """
return isinstance(update, Update) and update.chosen_inline_result 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 # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the CommandHandler class.""" """This module contains the CommandHandler class."""
import warnings
from future.utils import string_types from future.utils import string_types
from .handler import Handler
from telegram import Update from telegram import Update
from .handler import Handler
class CommandHandler(Handler): class CommandHandler(Handler):
@ -37,17 +35,17 @@ class CommandHandler(Handler):
callback (:obj:`callable`): The callback function for this handler. callback (:obj:`callable`): The callback function for this handler.
filters (:class:`telegram.ext.BaseFilter`): Optional. Only allow updates with these filters (:class:`telegram.ext.BaseFilter`): Optional. Only allow updates with these
Filters. 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. 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``. ``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. 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. 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. 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. the callback function.
Note: 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 either the user or the chat that the update was sent in. For each update from the same user
or in the same chat, it will be the same ``dict``. or in the same chat, it will be the same ``dict``.
Note that this is DEPRECATED, and you should use context based callbacks. See
https://git.io/vp113 for more info.
Args: Args:
command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
should listen for. should listen for.
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments. callback (:obj:`callable`): The callback function for this handler. Will be called when
It will be called when the :attr:`check_update` has determined that an update should be :attr:`check_update` has determined that an update should be processed by this handler.
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 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.BaseFilter`. Standard filters can be found in
:class:`telegram.ext.filters.Filters`. Filters can be combined using bitwise :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 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 a list of strings, which is the text following the command split on single or
consecutive whitespace characters. Default is ``False`` 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 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`` ``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` 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``. 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 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 ``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` :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``. 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 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``. ``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 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``. ``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.allow_edited = allow_edited
self.pass_args = pass_args 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): def check_update(self, update):
"""Determines whether an update should be passed to this handlers :attr:`callback`. """Determines whether an update should be passed to this handlers :attr:`callback`.
@ -129,8 +133,8 @@ class CommandHandler(Handler):
:obj:`bool` :obj:`bool`
""" """
if (isinstance(update, Update) if (isinstance(update, Update) and
and (update.message or update.edited_message and self.allow_edited)): (update.message or update.edited_message and self.allow_edited)):
message = update.message or update.edited_message message = update.message or update.edited_message
if message.text and message.text.startswith('/') and len(message.text) > 1: 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 if not (command[0].lower() in self.command
and command[1].lower() == message.bot.username.lower()): and command[1].lower() == message.bot.username.lower()):
return False return None
if self.filters is None: if self.filters is None or self.filters(message):
res = True return message.text.split()[1:]
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
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: 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 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 the user is in a conversation, but every handler for their current state returned
``False`` on :attr:`check_update`. ``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. an entry point.
run_async_timeout (:obj:`float`): Optional. The time-out for ``run_async`` decorated run_async_timeout (:obj:`float`): Optional. The time-out for ``run_async`` decorated
Handlers. Handlers.
timed_out_behavior (List[:class:`telegram.ext.Handler`]): Optional. A list of handlers that 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. 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_chat (:obj:`bool`): If the conversationkey should contain the Chat's ID.
per_user (:obj:`bool`): Optional. If the conversationkey should contain the User's ID. per_user (:obj:`bool`): If the conversationkey should contain the User's ID.
per_message (:obj:`bool`): Optional. If the conversationkey should contain the Message's per_message (:obj:`bool`): If the conversationkey should contain the Message's
ID. ID.
conversation_timeout (:obj:`float`|:obj:`datetime.timedelta`): Optional. When this handler 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 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.timeout_jobs = dict()
self.conversations = dict() self.conversations = dict()
self.current_conversation = None
self.current_handler = None
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
@ -237,7 +235,7 @@ class ConversationHandler(Handler):
self.per_chat and not update.effective_chat or self.per_chat and not update.effective_chat or
self.per_message and not update.callback_query or self.per_message and not update.callback_query or
update.callback_query and self.per_chat and not update.callback_query.message): update.callback_query and self.per_chat and not update.callback_query.message):
return False return None
key = self._get_key(update) key = self._get_key(update)
state = self.conversations.get(key) state = self.conversations.get(key)
@ -260,15 +258,13 @@ class ConversationHandler(Handler):
state = self.conversations.get(key) state = self.conversations.get(key)
else: else:
for candidate in (self.timed_out_behavior or []): 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 # Save the current user and the selected handler for handle_update
self.current_conversation = key return key, candidate, check
self.current_handler = candidate
return True
else: else:
return False return None
self.logger.debug('selecting conversation %s with state %s' % (str(key), str(state))) 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 # Search entry points for a match
if state is None or self.allow_reentry: if state is None or self.allow_reentry:
for entry_point in self.entry_points: 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 handler = entry_point
break break
else: else:
if state is None: 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 # 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: if state is not None and not handler:
handlers = self.states.get(state) handlers = self.states.get(state)
for candidate in (handlers or []): 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 handler = candidate
break break
# Find a fallback handler if all other handlers fail # Find a fallback handler if all other handlers fail
else: else:
for fallback in self.fallbacks: 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 handler = fallback
break break
else: else:
return False return None
# Save the current user and the selected handler for handle_update return key, handler, check
self.current_conversation = key
self.current_handler = handler
return True def handle_update(self, update, dispatcher, check_result):
def handle_update(self, update, dispatcher):
"""Send the update to the callback for the current state and Handler """Send the update to the callback for the current state and Handler
Args: 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. update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update. dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
""" """
new_state = self.current_handler.handle_update(update, dispatcher) conversation_key, handler, check_result = check_result
timeout_job = self.timeout_jobs.pop(self.current_conversation, None) new_state = handler.handle_update(update, dispatcher, check_result)
timeout_job = self.timeout_jobs.pop(conversation_key, None)
if timeout_job is not None: if timeout_job is not None:
timeout_job.schedule_removal() timeout_job.schedule_removal()
if self.conversation_timeout and new_state != self.END: 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, 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): def update_state(self, new_state, key):
if new_state == self.END: if new_state == self.END:

View file

@ -19,6 +19,7 @@
"""This module contains the Dispatcher class.""" """This module contains the Dispatcher class."""
import logging import logging
import warnings
import weakref import weakref
from functools import wraps from functools import wraps
from threading import Thread, Lock, Event, current_thread, BoundedSemaphore from threading import Thread, Lock, Event, current_thread, BoundedSemaphore
@ -32,6 +33,8 @@ from future.builtins import range
from telegram import TelegramError from telegram import TelegramError
from telegram.ext.handler import Handler 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.utils.promise import Promise
from telegram.ext import BasePersistence from telegram.ext import BasePersistence
@ -86,6 +89,9 @@ class Dispatcher(object):
``@run_async`` decorator. defaults to 4. ``@run_async`` decorator. defaults to 4.
persistence (:class:`telegram.ext.BasePersistence`, optional): The persistence class to persistence (:class:`telegram.ext.BasePersistence`, optional): The persistence class to
store data that should be persistent over restarts 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 __singleton = None
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def __init__(self, bot, update_queue, workers=4, exception_event=None, job_queue=None, def __init__(self,
persistence=None): bot,
update_queue,
workers=4,
exception_event=None,
job_queue=None,
persistence=None,
use_context=False):
self.bot = bot self.bot = bot
self.update_queue = update_queue self.update_queue = update_queue
self.job_queue = job_queue
self.workers = workers 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) self.user_data = defaultdict(dict)
""":obj:`dict`: A dictionary handlers can use to store data for the user."""
self.chat_data = defaultdict(dict) self.chat_data = defaultdict(dict)
if persistence: if persistence:
if not isinstance(persistence, BasePersistence): if not isinstance(persistence, BasePersistence):
@ -297,22 +317,24 @@ class Dispatcher(object):
for group in self.groups: for group in self.groups:
try: try:
for handler in (x for x in self.handlers[group] if x.check_update(update)): for handler in self.handlers[group]:
handler.handle_update(update, self) check = handler.check_update(update)
if self.persistence: if check is not None and check is not False:
if self.persistence.store_chat_data and update.effective_chat.id: handler.handle_update(update, self)
chat_id = update.effective_chat.id if self.persistence:
try: if self.persistence.store_chat_data and update.effective_chat.id:
self.persistence.update_chat_data(chat_id, self.chat_data[chat_id]) chat_id = update.effective_chat.id
except Exception: try:
self.logger.exception('Saving chat data raised an error') self.persistence.update_chat_data(chat_id, self.chat_data[chat_id])
if self.persistence.store_user_data and update.effective_user.id: except Exception:
user_id = update.effective_user.id self.logger.exception('Saving chat data raised an error')
try: if self.persistence.store_user_data and update.effective_user.id:
self.persistence.update_user_data(user_id, self.user_data[user_id]) user_id = update.effective_user.id
except Exception: try:
self.logger.exception('Saving user data raised an error') self.persistence.update_user_data(user_id, self.user_data[user_id])
break except Exception:
self.logger.exception('Saving user data raised an error')
break
# Stop processing with any other handler. # Stop processing with any other handler.
except DispatcherHandlerStop: except DispatcherHandlerStop:
@ -399,9 +421,15 @@ class Dispatcher(object):
"""Registers an error handler in the Dispatcher. """Registers an error handler in the Dispatcher.
Args: Args:
callback (:obj:`callable`): A function that takes ``Bot, Update, TelegramError`` as callback (:obj:`callable`): The callback function for this error handler. Will be
arguments. 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) self.error_handlers.append(callback)
@ -425,7 +453,10 @@ class Dispatcher(object):
""" """
if self.error_handlers: if self.error_handlers:
for callback in 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: else:
self.logger.exception( self.logger.exception(

View file

@ -17,6 +17,7 @@
# You should have received a copy of the GNU Lesser Public License # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the base class for handlers as used by the Dispatcher.""" """This module contains the base class for handlers as used by the Dispatcher."""
from telegram.ext.callbackcontext import CallbackContext
class Handler(object): class Handler(object):
@ -24,13 +25,13 @@ class Handler(object):
Attributes: Attributes:
callback (:obj:`callable`): The callback function for this handler. 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. 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. 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. 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. the callback function.
Note: 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 either the user or the chat that the update was sent in. For each update from the same user
or in the same chat, it will be the same ``dict``. or in the same chat, it will be the same ``dict``.
Note that this is DEPRECATED, and you should use context based callbacks. See
https://git.io/vp113 for more info.
Args: Args:
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments. callback (:obj:`callable`): The callback function for this handler. Will be called when
It will be called when the :attr:`check_update` has determined that an update should be :attr:`check_update` has determined that an update should be processed by this handler.
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 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`` ``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` 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``. 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 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 ``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` :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``. 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 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``. ``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 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``. ``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. update (:obj:`str` | :class:`telegram.Update`): The update to be tested.
Returns: 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 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 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 be handled by this instance. Calls :attr:`self.callback` along with its respectful
cases call ``self.callback(dispatcher.bot, update)``, possibly along with arguments. To work with the :class:`telegram.ext.ConversationHandler`, this method
optional arguments. To work with the ``ConversationHandler``, this method should return the returns the value returned from ``self.callback``.
value returned from ``self.callback`` Note that it can be overridden if needed by the subclassing handler.
Args: Args:
update (:obj:`str` | :class:`telegram.Update`): The update to be handled. 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): def collect_additional_context(self, context, update, dispatcher, check_result):
"""Prepares the optional arguments that are the same for all types of handlers. """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: Args:
dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher. 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() optional_args = dict()

View file

@ -22,7 +22,6 @@ import re
from future.utils import string_types from future.utils import string_types
from telegram import Update from telegram import Update
from telegram.utils.deprecate import deprecate
from .handler import Handler from .handler import Handler
@ -33,19 +32,19 @@ class InlineQueryHandler(Handler):
Attributes: Attributes:
callback (:obj:`callable`): The callback function for this handler. 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. 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. the callback function.
pattern (:obj:`str` | :obj:`Pattern`): Optional. Regex pattern to test pattern (:obj:`str` | :obj:`Pattern`): Optional. Regex pattern to test
:attr:`telegram.InlineQuery.query` against. :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. 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. 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. 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. the callback function.
Note: 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 either the user or the chat that the update was sent in. For each update from the same user
or in the same chat, it will be the same ``dict``. or in the same chat, it will be the same ``dict``.
Note that this is DEPRECATED, and you should use context based callbacks. See
https://git.io/vp113 for more info.
Args: Args:
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments. callback (:obj:`callable`): The callback function for this handler. Will be called when
It will be called when the :attr:`check_update` has determined that an update should be :attr:`check_update` has determined that an update should be processed by this handler.
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 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`` ``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` 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``. 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 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 ``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` :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``. 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``, 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 ``re.match`` is used on :attr:`telegram.InlineQuery.query` to determine if an update
should be handled by this handler. should be handled by this handler.
pass_groups (:obj:`bool`, optional): If the callback should be passed the result of 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``. ``re.match(pattern, data).groups()`` as a keyword argument called ``groups``.
Default is ``False`` Default is ``False``
DEPRECATED: Please switch to context based callbacks.
pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of 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``. ``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``.
Default is ``False`` Default is ``False``
DEPRECATED: Please switch to context based callbacks.
pass_user_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called 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``. ``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 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``. ``chat_data`` will be passed to the callback function. Default is ``False``.
DEPRECATED: Please switch to context based callbacks.
""" """
def __init__(self, def __init__(self,
@ -113,37 +127,28 @@ class InlineQueryHandler(Handler):
Returns: Returns:
:obj:`bool` :obj:`bool`
""" """
if isinstance(update, Update) and update.inline_query: if isinstance(update, Update) and update.inline_query:
if self.pattern: if self.pattern:
if update.inline_query.query: if update.inline_query.query:
match = re.match(self.pattern, update.inline_query.query) match = re.match(self.pattern, update.inline_query.query)
return bool(match) if match:
return match
else: else:
return True return True
def handle_update(self, update, dispatcher): def collect_optional_args(self, dispatcher, update=None, check_result=None):
""" optional_args = super(InlineQueryHandler, self).collect_optional_args(dispatcher,
Send the update to the :attr:`callback`. update, check_result)
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)
if self.pattern: if self.pattern:
match = re.match(self.pattern, update.inline_query.query)
if self.pass_groups: if self.pass_groups:
optional_args['groups'] = match.groups() optional_args['groups'] = check_result.groups()
if self.pass_groupdict: 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:
# old non-PEP8 Handler methods context.match = check_result
m = "telegram.InlineQueryHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")

View file

@ -18,13 +18,17 @@
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes JobQueue and Job.""" """This module contains the classes JobQueue and Job."""
import datetime
import logging import logging
import time import time
import datetime import warnings
import weakref import weakref
from numbers import Number from numbers import Number
from threading import Thread, Lock, Event
from queue import PriorityQueue, Empty 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): class Days(object):
@ -37,16 +41,24 @@ class JobQueue(object):
Attributes: Attributes:
_queue (:obj:`PriorityQueue`): The queue that holds the Jobs. _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. 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._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.logger = logging.getLogger(self.__class__.__name__)
self.__start_lock = Lock() self.__start_lock = Lock()
self.__next_peek_lock = Lock() # to protect self._next_peek & self.__tick self.__next_peek_lock = Lock() # to protect self._next_peek & self.__tick
@ -55,6 +67,9 @@ class JobQueue(object):
self._next_peek = None self._next_peek = None
self._running = False self._running = False
def set_dispatcher(self, dispatcher):
self._dispatcher = dispatcher
def _put(self, job, next_t=None, last_t=None): def _put(self, job, next_t=None, last_t=None):
if next_t is None: if next_t is None:
next_t = job.interval next_t = job.interval
@ -242,7 +257,7 @@ class JobQueue(object):
current_week_day = datetime.datetime.now().weekday() current_week_day = datetime.datetime.now().weekday()
if any(day == current_week_day for day in job.days): if any(day == current_week_day for day in job.days):
self.logger.debug('Running job %s', job.name) self.logger.debug('Running job %s', job.name)
job.run(self.bot) job.run(self._dispatcher)
except Exception: except Exception:
self.logger.exception('An uncaught error was raised while executing job %s', self.logger.exception('An uncaught error was raised while executing job %s',
@ -367,9 +382,12 @@ class Job(object):
self._enabled = Event() self._enabled = Event()
self._enabled.set() self._enabled.set()
def run(self, bot): def run(self, dispatcher):
"""Executes the callback function.""" """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): def schedule_removal(self):
""" """

View file

@ -31,21 +31,21 @@ class MessageHandler(Handler):
filters (:obj:`Filter`): Only allow updates with these Filters. See filters (:obj:`Filter`): Only allow updates with these Filters. See
:mod:`telegram.ext.filters` for a full list of all available filters. :mod:`telegram.ext.filters` for a full list of all available filters.
callback (:obj:`callable`): The callback function for this handler. 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. 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. 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. 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. 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``. 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``. 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``. 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. Default is ``False`` - Deprecated. use edited_updates instead.
Note: 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 either the user or the chat that the update was sent in. For each update from the same user
or in the same chat, it will be the same ``dict``. or in the same chat, it will be the same ``dict``.
Note that this is DEPRECATED, and you should use context based callbacks. See
https://git.io/vp113 for more info.
Args: Args:
filters (:class:`telegram.ext.BaseFilter`, optional): A filter inheriting from 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.BaseFilter`. Standard filters can be found in
:class:`telegram.ext.filters.Filters`. Filters can be combined using bitwise :class:`telegram.ext.filters.Filters`. Filters can be combined using bitwise
operators (& for and, | for or, ~ for not). operators (& for and, | for or, ~ for not).
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments. callback (:obj:`callable`): The callback function for this handler. Will be called when
It will be called when the :attr:`check_update` has determined that an update should be :attr:`check_update` has determined that an update should be processed by this handler.
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 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`` ``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` 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``. 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 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 ``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` :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``. 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 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``. ``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 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``. ``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? message_updates (:obj:`bool`, optional): Should "normal" message updates be handled?
Default is ``True``. Default is ``True``.
channel_post_updates (:obj:`bool`, optional): Should channel posts updates be handled? 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.channel_post_updates = channel_post_updates
self.edited_updates = edited_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): def _is_allowed_update(self, update):
return any([self.message_updates and update.message, return any([self.message_updates and update.message,
self.edited_updates and (update.edited_message or update.edited_channel_post), 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 isinstance(update, Update) and self._is_allowed_update(update):
if not self.filters: if not self.filters:
res = True return True
else: else:
message = update.effective_message return self.filters(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)

View file

@ -27,13 +27,13 @@ class PreCheckoutQueryHandler(Handler):
Attributes: Attributes:
callback (:obj:`callable`): The callback function for this handler. 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. 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. 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. 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. the callback function.
Note: 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 either the user or the chat that the update was sent in. For each update from the same user
or in the same chat, it will be the same ``dict``. or in the same chat, it will be the same ``dict``.
Note that this is DEPRECATED, and you should use context based callbacks. See
https://git.io/vp113 for more info.
Args: Args:
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments. callback (:obj:`callable`): The callback function for this handler. Will be called when
It will be called when the :attr:`check_update` has determined that an update should be :attr:`check_update` has determined that an update should be processed by this handler.
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 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`` ``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` 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``. 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 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 ``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` :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``. 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 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``. ``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 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``. ``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): def check_update(self, update):
"""Determines whether an update should be passed to this handlers :attr:`callback`. """Determines whether an update should be passed to this handlers :attr:`callback`.
@ -85,14 +84,3 @@ class PreCheckoutQueryHandler(Handler):
""" """
return isinstance(update, Update) and update.pre_checkout_query 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: Attributes:
pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. pattern (:obj:`str` | :obj:`Pattern`): The regex pattern.
callback (:obj:`callable`): The callback function for this handler. 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. 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. 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. 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. 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. 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. the callback function.
Note: 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 either the user or the chat that the update was sent in. For each update from the same user
or in the same chat, it will be the same ``dict``. or in the same chat, it will be the same ``dict``.
Note that this is DEPRECATED, and you should use context based callbacks. See
https://git.io/vp113 for more info.
Args: Args:
pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. pattern (:obj:`str` | :obj:`Pattern`): The regex pattern.
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments. callback (:obj:`callable`): The callback function for this handler. Will be called when
It will be called when the :attr:`check_update` has determined that an update should be :attr:`check_update` has determined that an update should be processed by this handler.
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 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``. ``re.match(pattern, data).groups()`` as a keyword argument called ``groups``.
Default is ``False`` Default is ``False``
DEPRECATED: Please switch to context based callbacks.
pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of 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``. ``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``.
Default is ``False`` Default is ``False``
DEPRECATED: Please switch to context based callbacks.
pass_update_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called 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`` ``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` 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``. 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 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 ``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` :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``. 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 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``. ``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 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``. ``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? message_updates (:obj:`bool`, optional): Should "normal" message updates be handled?
Default is ``True``. Default is ``True``.
channel_post_updates (:obj:`bool`, optional): Should channel posts updates be handled? channel_post_updates (:obj:`bool`, optional): Should channel posts updates be handled?
@ -106,8 +120,7 @@ class RegexHandler(Handler):
allow_edited=False, allow_edited=False,
message_updates=True, message_updates=True,
channel_post_updates=False, channel_post_updates=False,
edited_updates=False edited_updates=False):
):
if not message_updates and not channel_post_updates and not edited_updates: if not message_updates and not channel_post_updates and not edited_updates:
raise ValueError( raise ValueError(
'message_updates, channel_post_updates and edited_updates are all False') '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: if not isinstance(update, Update) and not update.effective_message:
return False return None
if any([self.message_updates and update.message, if any([self.message_updates and update.message,
self.edited_updates and (update.edited_message or update.edited_channel_post), self.edited_updates and (update.edited_message or update.edited_channel_post),
self.channel_post_updates and update.channel_post]) and \ self.channel_post_updates and update.channel_post]) and \
update.effective_message.text: update.effective_message.text:
match = re.match(self.pattern, update.effective_message.text) match = re.match(self.pattern, update.effective_message.text)
return bool(match) if match:
return False return match
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)
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: if self.pass_groups:
optional_args['groups'] = match.groups() optional_args['groups'] = check_result.groups()
if self.pass_groupdict: 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: Attributes:
callback (:obj:`callable`): The callback function for this handler. 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. 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. 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. 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. the callback function.
Note: 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 either the user or the chat that the update was sent in. For each update from the same user
or in the same chat, it will be the same ``dict``. or in the same chat, it will be the same ``dict``.
Note that this is DEPRECATED, and you should use context based callbacks. See
https://git.io/vp113 for more info.
Args: Args:
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments. callback (:obj:`callable`): The callback function for this handler. Will be called when
It will be called when the :attr:`check_update` has determined that an update should be :attr:`check_update` has determined that an update should be processed by this handler.
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 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`` ``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` 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``. 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 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 ``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` :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``. 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 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``. ``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 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``. ``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): def check_update(self, update):
"""Determines whether an update should be passed to this handlers :attr:`callback`. """Determines whether an update should be passed to this handlers :attr:`callback`.
@ -85,14 +84,3 @@ class ShippingQueryHandler(Handler):
""" """
return isinstance(update, Update) and update.shipping_query 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: Attributes:
command (:obj:`str`): The command this handler should listen for. command (:obj:`str`): The command this handler should listen for.
callback (:obj:`callable`): The callback function for this handler. 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``. ``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. 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. the callback function.
Args: Args:
command (:obj:`str`): The command this handler should listen for. callback (:obj:`callable`): The callback function for this handler. Will be called when
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments. :attr:`check_update` has determined that an update should be processed by this handler.
It will be called when the :attr:`check_update` has determined that a command should be Callback signature for context based API:
processed by this handler.
``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 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 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 a list of strings, which is the text following the command split on single or
consecutive whitespace characters. Default is ``False`` 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 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`` ``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` 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``. 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 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 ``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` class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``. 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_update_queue=False,
pass_job_queue=False): pass_job_queue=False):
super(StringCommandHandler, self).__init__( 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.command = command
self.pass_args = pass_args self.pass_args = pass_args
@ -76,28 +84,24 @@ class StringCommandHandler(Handler):
"""Determines whether an update should be passed to this handlers :attr:`callback`. """Determines whether an update should be passed to this handlers :attr:`callback`.
Args: Args:
update (:obj:`str`): An incomming command. update (:obj:`str`): An incoming command.
Returns: Returns:
:obj:`bool` :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('/') def collect_optional_args(self, dispatcher, update=None, check_result=None):
and update[1:].split(' ')[0] == self.command) optional_args = super(StringCommandHandler, self).collect_optional_args(dispatcher,
update,
def handle_update(self, update, dispatcher): check_result)
"""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)
if self.pass_args: 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: Attributes:
pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. pattern (:obj:`str` | :obj:`Pattern`): The regex pattern.
callback (:obj:`callable`): The callback function for this handler. 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. 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. 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. 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. the callback function.
Args: Args:
pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. pattern (:obj:`str` | :obj:`Pattern`): The regex pattern.
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments. callback (:obj:`callable`): The callback function for this handler. Will be called when
It will be called when the :attr:`check_update` has determined that an update should be :attr:`check_update` has determined that an update should be processed by this handler.
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 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``. ``re.match(pattern, data).groups()`` as a keyword argument called ``groups``.
Default is ``False`` Default is ``False``
DEPRECATED: Please switch to context based callbacks.
pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of 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``. ``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``.
Default is ``False`` Default is ``False``
DEPRECATED: Please switch to context based callbacks.
pass_update_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called 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`` ``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` 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``. 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 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 ``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` :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``. 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_update_queue=False,
pass_job_queue=False): pass_job_queue=False):
super(StringRegexHandler, self).__init__( 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): if isinstance(pattern, string_types):
pattern = re.compile(pattern) pattern = re.compile(pattern)
@ -90,28 +101,27 @@ class StringRegexHandler(Handler):
"""Determines whether an update should be passed to this handlers :attr:`callback`. """Determines whether an update should be passed to this handlers :attr:`callback`.
Args: Args:
update (:obj:`str`): An incomming command. update (:obj:`str`): An incoming command.
Returns: Returns:
:obj:`bool` :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): def collect_optional_args(self, dispatcher, update=None, check_result=None):
"""Send the update to the :attr:`callback`. 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: def collect_additional_context(self, context, update, dispatcher, check_result):
update (:obj:`str`): An incomming command. if self.pattern:
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the command. context.match = check_result
"""
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)

View file

@ -27,36 +27,48 @@ class TypeHandler(Handler):
Attributes: Attributes:
type (:obj:`type`): The ``type`` of updates this handler should process. type (:obj:`type`): The ``type`` of updates this handler should process.
callback (:obj:`callable`): The callback function for this handler. callback (:obj:`callable`): The callback function for this handler.
strict (:obj:`bool`): Optional. Use ``type`` instead of ``isinstance``. strict (:obj:`bool`): Use ``type`` instead of ``isinstance``. Default is ``False``.
Default is ``False`` pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
pass_update_queue (:obj:`bool`): Optional. Determines whether ``update_queue`` will be
passed to the callback function. 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. the callback function.
Args: Args:
type (:obj:`type`): The ``type`` of updates this handler should process, as type (:obj:`type`): The ``type`` of updates this handler should process, as
determined by ``isinstance`` determined by ``isinstance``
callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments. callback (:obj:`callable`): The callback function for this handler. Will be called when
It will be called when the :attr:`check_update` has determined that an update should be :attr:`check_update` has determined that an update should be processed by this handler.
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``. strict (:obj:`bool`, optional): Use ``type`` instead of ``isinstance``.
Default is ``False`` Default is ``False``
pass_update_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called 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`` ``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` 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``. 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 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 ``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` :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``. 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): pass_job_queue=False):
super(TypeHandler, self).__init__( 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.type = type
self.strict = strict self.strict = strict
@ -70,20 +82,7 @@ class TypeHandler(Handler):
:obj:`bool` :obj:`bool`
""" """
if not self.strict: if not self.strict:
return isinstance(update, self.type) return isinstance(update, self.type)
else: else:
return type(update) is self.type 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. running (:obj:`bool`): Indicates if the updater is running.
persistence (:class:`telegram.ext.BasePersistence`): Optional. The persistence class to persistence (:class:`telegram.ext.BasePersistence`): Optional. The persistence class to
store data that should be persistent over restarts. store data that should be persistent over restarts.
use_context (:obj:`bool`, optional): ``True`` if using context based callbacks.
Args: Args:
token (:obj:`str`, optional): The bot's token given by the @BotFather. 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 `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 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. 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 persistence (:class:`telegram.ext.BasePersistence`, optional): The persistence class to
store data that should be persistent over restarts. store data that should be persistent over restarts.
@ -97,7 +101,8 @@ class Updater(object):
private_key_password=None, private_key_password=None,
user_sig_handler=None, user_sig_handler=None,
request_kwargs=None, request_kwargs=None,
persistence=None): persistence=None,
use_context=False):
if (token is None) and (bot is None): if (token is None) and (bot is None):
raise ValueError('`token` or `bot` must be passed') raise ValueError('`token` or `bot` must be passed')
@ -132,7 +137,7 @@ class Updater(object):
private_key_password=private_key_password) private_key_password=private_key_password)
self.user_sig_handler = user_sig_handler self.user_sig_handler = user_sig_handler
self.update_queue = Queue() self.update_queue = Queue()
self.job_queue = JobQueue(self.bot) self.job_queue = JobQueue()
self.__exception_event = Event() self.__exception_event = Event()
self.persistence = persistence self.persistence = persistence
self.dispatcher = Dispatcher( self.dispatcher = Dispatcher(
@ -141,7 +146,9 @@ class Updater(object):
job_queue=self.job_queue, job_queue=self.job_queue,
workers=workers, workers=workers,
exception_event=self.__exception_event, 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.last_update_id = 0
self.running = False self.running = False
self.is_idle = False self.is_idle = False

View file

@ -72,7 +72,8 @@ def provider_token(bot_info):
def create_dp(bot): def create_dp(bot):
# Dispatcher is heavy to init (due to many threads and such) so we have a single session # 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) # 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 = Thread(target=dispatcher.start)
thr.start() thr.start()
sleep(2) sleep(2)
@ -109,6 +110,13 @@ def dp(_dp):
Dispatcher._Dispatcher__singleton_semaphore.release() 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') @pytest.fixture(scope='function')
def updater(bot): def updater(bot):
up = Updater(bot=bot, workers=2) 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 # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest import pytest
from telegram import (Update, CallbackQuery, Bot, Message, User, Chat, InlineQuery, from telegram import (Update, CallbackQuery, Bot, Message, User, Chat, InlineQuery,
ChosenInlineResult, ShippingQuery, PreCheckoutQuery) 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') message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
@ -47,7 +49,7 @@ def false_update(request):
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def callback_query(bot): 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): class TestCallbackQueryHandler(object):
@ -80,6 +82,22 @@ class TestCallbackQueryHandler(object):
if groupdict is not None: if groupdict is not None:
self.test_flag = groupdict == {'begin': 't', 'end': ' data'} 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): def test_basic(self, dp, callback_query):
handler = CallbackQueryHandler(self.callback_basic) handler = CallbackQueryHandler(self.callback_basic)
dp.add_handler(handler) dp.add_handler(handler)
@ -117,14 +135,16 @@ class TestCallbackQueryHandler(object):
assert self.test_flag assert self.test_flag
def test_pass_user_or_chat_data(self, dp, callback_query): 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.add_handler(handler)
dp.process_update(callback_query) dp.process_update(callback_query)
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -132,7 +152,8 @@ class TestCallbackQueryHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) pass_user_data=True)
dp.add_handler(handler) dp.add_handler(handler)
@ -141,14 +162,16 @@ class TestCallbackQueryHandler(object):
assert self.test_flag assert self.test_flag
def test_pass_job_or_update_queue(self, dp, callback_query): 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.add_handler(handler)
dp.process_update(callback_query) dp.process_update(callback_query)
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -156,7 +179,8 @@ class TestCallbackQueryHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) pass_update_queue=True)
dp.add_handler(handler) dp.add_handler(handler)
@ -167,3 +191,26 @@ class TestCallbackQueryHandler(object):
def test_other_update_types(self, false_update): def test_other_update_types(self, false_update):
handler = CallbackQueryHandler(self.callback_basic) handler = CallbackQueryHandler(self.callback_basic)
assert not handler.check_update(false_update) 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 # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest import pytest
from telegram import (Update, Chat, Bot, ChosenInlineResult, User, Message, CallbackQuery, from telegram import (Update, Chat, Bot, ChosenInlineResult, User, Message, CallbackQuery,
InlineQuery, ShippingQuery, PreCheckoutQuery) 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') 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): 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) 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): def test_basic(self, dp, chosen_inline_result):
handler = ChosenInlineResultHandler(self.callback_basic) handler = ChosenInlineResultHandler(self.callback_basic)
dp.add_handler(handler) dp.add_handler(handler)
@ -87,14 +98,16 @@ class TestChosenInlineResultHandler(object):
assert self.test_flag assert self.test_flag
def test_pass_user_or_chat_data(self, dp, chosen_inline_result): 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.add_handler(handler)
dp.process_update(chosen_inline_result) dp.process_update(chosen_inline_result)
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -102,8 +115,8 @@ class TestChosenInlineResultHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) dp.remove_handler(handler)
handler = ChosenInlineResultHandler(self.callback_data_2, pass_chat_data=True, handler = ChosenInlineResultHandler(self.callback_data_2,
pass_user_data=True) pass_chat_data=True, pass_user_data=True)
dp.add_handler(handler) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -111,14 +124,16 @@ class TestChosenInlineResultHandler(object):
assert self.test_flag assert self.test_flag
def test_pass_job_or_update_queue(self, dp, chosen_inline_result): 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.add_handler(handler)
dp.process_update(chosen_inline_result) dp.process_update(chosen_inline_result)
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -126,8 +141,8 @@ class TestChosenInlineResultHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) dp.remove_handler(handler)
handler = ChosenInlineResultHandler(self.callback_queue_2, pass_job_queue=True, handler = ChosenInlineResultHandler(self.callback_queue_2,
pass_update_queue=True) pass_job_queue=True, pass_update_queue=True)
dp.add_handler(handler) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -137,3 +152,10 @@ class TestChosenInlineResultHandler(object):
def test_other_update_types(self, false_update): def test_other_update_types(self, false_update):
handler = ChosenInlineResultHandler(self.callback_basic) handler = ChosenInlineResultHandler(self.callback_basic)
assert not handler.check_update(false_update) 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 # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest import pytest
from telegram import (Message, Update, Chat, Bot, User, CallbackQuery, InlineQuery, from telegram import (Message, Update, Chat, Bot, User, CallbackQuery, InlineQuery,
ChosenInlineResult, ShippingQuery, PreCheckoutQuery) ChosenInlineResult, ShippingQuery, PreCheckoutQuery)
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') message = Message(1, User(1, '', False), None, Chat(1, ''), text='test')
@ -48,7 +49,7 @@ def false_update(request):
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def message(bot): 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): class TestCommandHandler(object):
@ -83,51 +84,73 @@ class TestCommandHandler(object):
else: else:
self.test_flag = args == ['one', 'two'] 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): def test_basic(self, dp, message):
handler = CommandHandler('test', self.callback_basic) handler = CommandHandler('test', self.callback_basic)
dp.add_handler(handler) dp.add_handler(handler)
message.text = '/test' message.text = '/test'
assert handler.check_update(Update(0, message))
dp.process_update(Update(0, message)) dp.process_update(Update(0, message))
assert self.test_flag assert self.test_flag
message.text = '/nottest' 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' 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' 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): def test_command_list(self, message):
handler = CommandHandler(['test', 'start'], self.callback_basic) handler = CommandHandler(['test', 'start'], self.callback_basic)
message.text = '/test' message.text = '/test'
assert handler.check_update(Update(0, message)) check = handler.check_update(Update(0, message))
message.text = '/start' message.text = '/start'
assert handler.check_update(Update(0, message)) check = handler.check_update(Update(0, message))
message.text = '/stop' 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): 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' message.text = '/test'
assert handler.check_update(Update(0, message)) check = handler.check_update(Update(0, message))
assert not handler.check_update(Update(0, edited_message=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 handler.allow_edited = True
assert handler.check_update(Update(0, message)) check = handler.check_update(Update(0, message))
assert handler.check_update(Update(0, edited_message=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): def test_directed_commands(self, message):
handler = CommandHandler('test', self.callback_basic) handler = CommandHandler('test', self.callback_basic)
message.text = '/test@{}'.format(message.bot.username) message.text = '/test@{}'.format(message.bot.username)
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' message.text = '/test@otherbot'
assert not handler.check_update(Update(0, message)) assert not handler.check_update(Update(0, message))
@ -137,10 +160,12 @@ class TestCommandHandler(object):
message.chat = Chat(-23, 'group') message.chat = Chat(-23, 'group')
message.text = '/test' 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') 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): def test_pass_args(self, dp, message):
handler = CommandHandler('test', self.ch_callback_args, pass_args=True) handler = CommandHandler('test', self.ch_callback_args, pass_args=True)
@ -170,7 +195,8 @@ class TestCommandHandler(object):
dp.add_handler(handler) dp.add_handler(handler)
message.text = '/test\nfoobar' 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)) dp.process_update(Update(0, message))
assert self.test_flag assert self.test_flag
@ -180,7 +206,8 @@ class TestCommandHandler(object):
dp.add_handler(handler) dp.add_handler(handler)
message.text = 'a' 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): def test_single_slash(self, dp, message):
# Regression test for https://github.com/python-telegram-bot/python-telegram-bot/issues/871 # 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) dp.add_handler(handler)
message.text = '/' 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' 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): 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) dp.add_handler(handler)
message.text = '/test' message.text = '/test'
@ -202,7 +232,8 @@ class TestCommandHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -210,7 +241,8 @@ class TestCommandHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) pass_user_data=True)
dp.add_handler(handler) dp.add_handler(handler)
@ -219,7 +251,8 @@ class TestCommandHandler(object):
assert self.test_flag assert self.test_flag
def test_pass_job_or_update_queue(self, dp, message): 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) dp.add_handler(handler)
message.text = '/test' message.text = '/test'
@ -227,7 +260,8 @@ class TestCommandHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -235,7 +269,8 @@ class TestCommandHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) pass_update_queue=True)
dp.add_handler(handler) dp.add_handler(handler)
@ -245,7 +280,8 @@ class TestCommandHandler(object):
def test_other_update_types(self, false_update): def test_other_update_types(self, false_update):
handler = CommandHandler('test', self.callback_basic) 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): def test_filters_for_wrong_command(self, message):
"""Filters should not be executed if the command does not match the handler""" """Filters should not be executed if the command does not match the handler"""
@ -259,9 +295,31 @@ class TestCommandHandler(object):
test_filter = TestFilter() test_filter = TestFilter()
handler = CommandHandler('foo', self.callback_basic, filters=test_filter) handler = CommandHandler('foo', self.callback_basic,
filters=test_filter)
message.text = '/bar' 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 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.current_state = dict()
self.entry_points = [CommandHandler('start', self.start)] self.entry_points = [CommandHandler('start', self.start)]
self.states = { 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.BREWING: [CommandHandler('pourCoffee', self.drink)],
self.DRINKING: self.DRINKING:
[CommandHandler('startCoding', self.code), [CommandHandler('startCoding', self.code),
@ -266,7 +267,8 @@ class TestConversationHandler(object):
def test_end_on_first_message(self, dp, bot, user1): def test_end_on_first_message(self, dp, bot, user1):
handler = ConversationHandler( handler = ConversationHandler(
entry_points=[CommandHandler('start', self.start_end)], states={}, fallbacks=[]) entry_points=[CommandHandler('start', self.start_end)], states={},
fallbacks=[])
dp.add_handler(handler) dp.add_handler(handler)
# User starts the state machine and immediately ends it. # User starts the state machine and immediately ends it.
@ -278,7 +280,8 @@ class TestConversationHandler(object):
start_end_async = (lambda bot, update: dp.run_async(self.start_end, bot, update)) start_end_async = (lambda bot, update: dp.run_async(self.start_end, bot, update))
handler = ConversationHandler( handler = ConversationHandler(
entry_points=[CommandHandler('start', start_end_async)], states={}, fallbacks=[]) entry_points=[CommandHandler('start', start_end_async)], states={},
fallbacks=[])
dp.add_handler(handler) dp.add_handler(handler)
# User starts the state machine with an async function that immediately ends the # User starts the state machine with an async function that immediately ends the
@ -297,7 +300,8 @@ class TestConversationHandler(object):
def test_per_chat_message_without_chat(self, bot, user1): def test_per_chat_message_without_chat(self, bot, user1):
handler = ConversationHandler( 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) cbq = CallbackQuery(0, user1, None, None, bot=bot)
update = Update(0, callback_query=cbq) update = Update(0, callback_query=cbq)
assert not handler.check_update(update) assert not handler.check_update(update)

View file

@ -16,15 +16,17 @@
# #
# You should have received a copy of the GNU Lesser Public License # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
import sys
from queue import Queue from queue import Queue
from threading import current_thread from threading import current_thread
from time import sleep from time import sleep
import pytest import pytest
from telegram import TelegramError, Message, User, Chat, Update from telegram import TelegramError, Message, User, Chat, Update, Bot
from telegram.ext import MessageHandler, Filters, CommandHandler from telegram.ext import MessageHandler, Filters, CommandHandler, CallbackContext, JobQueue
from telegram.ext.dispatcher import run_async, Dispatcher, DispatcherHandlerStop from telegram.ext.dispatcher import run_async, Dispatcher, DispatcherHandlerStop
from telegram.utils.deprecate import TelegramDeprecationWarning
from tests.conftest import create_dp from tests.conftest import create_dp
@ -67,6 +69,14 @@ class TestDispatcher(object):
if update_queue is not None: if update_queue is not None:
self.received = update.message 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): def test_error_handler(self, dp):
dp.add_error_handler(self.error_handler) dp.add_error_handler(self.error_handler)
error = TelegramError('Unauthorized.') error = TelegramError('Unauthorized.')
@ -326,3 +336,17 @@ class TestDispatcher(object):
dp.process_update(update) dp.process_update(update)
assert passed == ['start1', 'error', err] assert passed == ['start1', 'error', err]
assert passed[2] is 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 # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest import pytest
from telegram import (Update, CallbackQuery, Bot, Message, User, Chat, InlineQuery, from telegram import (Update, CallbackQuery, Bot, Message, User, Chat, InlineQuery,
ChosenInlineResult, ShippingQuery, PreCheckoutQuery, Location) 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') message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
@ -84,6 +86,22 @@ class TestCallbackQueryHandler(object):
if groupdict is not None: if groupdict is not None:
self.test_flag = groupdict == {'begin': 't', 'end': ' query'} 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): def test_basic(self, dp, inline_query):
handler = InlineQueryHandler(self.callback_basic) handler = InlineQueryHandler(self.callback_basic)
dp.add_handler(handler) dp.add_handler(handler)
@ -94,7 +112,8 @@ class TestCallbackQueryHandler(object):
assert self.test_flag assert self.test_flag
def test_with_pattern(self, inline_query): 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) assert handler.check_update(inline_query)
@ -152,7 +171,8 @@ class TestCallbackQueryHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -171,3 +191,26 @@ class TestCallbackQueryHandler(object):
def test_other_update_types(self, false_update): def test_other_update_types(self, false_update):
handler = InlineQueryHandler(self.callback_basic) handler = InlineQueryHandler(self.callback_basic)
assert not handler.check_update(false_update) 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/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
import datetime import datetime
import os import os
import sys
import time import time
from queue import Queue
from time import sleep from time import sleep
import pytest import pytest
from flaky import flaky 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') @pytest.fixture(scope='function')
def job_queue(bot): def job_queue(bot, _dp):
jq = JobQueue(bot) jq = JobQueue()
jq.set_dispatcher(_dp)
jq.start() jq.start()
yield jq yield jq
jq.stop() jq.stop()
@ -62,6 +66,16 @@ class TestJobQueue(object):
def job_datetime_tests(self, bot, job): def job_datetime_tests(self, bot, job):
self.job_time = time.time() 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): def test_run_once(self, job_queue):
job_queue.run_once(self.job_run_once, 0.01) job_queue.run_once(self.job_run_once, 0.01)
sleep(0.02) sleep(0.02)
@ -244,3 +258,15 @@ class TestJobQueue(object):
assert job_queue.jobs() == (job1, job2, job3) assert job_queue.jobs() == (job1, job2, job3)
assert job_queue.get_jobs_by_name('name1') == (job1, job2) assert job_queue.get_jobs_by_name('name1') == (job1, job2)
assert job_queue.get_jobs_by_name('name2') == (job3,) 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 # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest import pytest
from telegram import (Message, Update, Chat, Bot, User, CallbackQuery, InlineQuery, from telegram import (Message, Update, Chat, Bot, User, CallbackQuery, InlineQuery,
ChosenInlineResult, ShippingQuery, PreCheckoutQuery) ChosenInlineResult, ShippingQuery, PreCheckoutQuery)
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') message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
@ -45,7 +46,7 @@ def false_update(request):
@pytest.fixture(scope='class') @pytest.fixture(scope='class')
def message(bot): 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): class TestMessageHandler(object):
@ -72,6 +73,25 @@ class TestMessageHandler(object):
def callback_queue_2(self, bot, update, job_queue=None, update_queue=None): def callback_queue_2(self, bot, update, job_queue=None, update_queue=None):
self.test_flag = (job_queue is not None) and (update_queue is not None) 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): def test_basic(self, dp, message):
handler = MessageHandler(None, self.callback_basic) handler = MessageHandler(None, self.callback_basic)
dp.add_handler(handler) dp.add_handler(handler)
@ -90,8 +110,9 @@ class TestMessageHandler(object):
assert handler.check_update(Update(0, edited_channel_post=message)) assert handler.check_update(Update(0, edited_channel_post=message))
def test_channel_post(self, message): def test_channel_post(self, message):
handler = MessageHandler(None, self.callback_basic, edited_updates=False, handler = MessageHandler(None, self.callback_basic,
message_updates=False, channel_post_updates=True) 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, edited_message=message))
assert not handler.check_update(Update(0, message=message)) assert not handler.check_update(Update(0, message=message))
@ -109,8 +130,9 @@ class TestMessageHandler(object):
def test_allow_edited(self, message): def test_allow_edited(self, message):
with pytest.warns(UserWarning): with pytest.warns(UserWarning):
handler = MessageHandler(None, self.callback_basic, message_updates=True, handler = MessageHandler(None, self.callback_basic,
allow_edited=True, channel_post_updates=False) 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, edited_message=message))
assert handler.check_update(Update(0, 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)) assert not handler.check_update(Update(0, message))
def test_pass_user_or_chat_data(self, dp, 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.add_handler(handler)
dp.process_update(Update(0, message=message)) dp.process_update(Update(0, message=message))
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -147,8 +171,8 @@ class TestMessageHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) dp.remove_handler(handler)
handler = MessageHandler(None, self.callback_data_2, pass_chat_data=True, handler = MessageHandler(None, self.callback_data_2,
pass_user_data=True) pass_chat_data=True, pass_user_data=True)
dp.add_handler(handler) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -156,14 +180,16 @@ class TestMessageHandler(object):
assert self.test_flag assert self.test_flag
def test_pass_job_or_update_queue(self, dp, message): 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.add_handler(handler)
dp.process_update(Update(0, message=message)) dp.process_update(Update(0, message=message))
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -171,8 +197,8 @@ class TestMessageHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) dp.remove_handler(handler)
handler = MessageHandler(None, self.callback_queue_2, pass_job_queue=True, handler = MessageHandler(None, self.callback_queue_2,
pass_update_queue=True) pass_job_queue=True, pass_update_queue=True)
dp.add_handler(handler) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -182,3 +208,23 @@ class TestMessageHandler(object):
def test_other_update_types(self, false_update): def test_other_update_types(self, false_update):
handler = MessageHandler(None, self.callback_basic, edited_updates=True) handler = MessageHandler(None, self.callback_basic, edited_updates=True)
assert not handler.check_update(false_update) 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 # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest import pytest
from telegram import (Update, Chat, Bot, ChosenInlineResult, User, Message, CallbackQuery, from telegram import (Update, Chat, Bot, ChosenInlineResult, User, Message, CallbackQuery,
InlineQuery, ShippingQuery, PreCheckoutQuery) 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') 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): 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) 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): def test_basic(self, dp, pre_checkout_query):
handler = PreCheckoutQueryHandler(self.callback_basic) handler = PreCheckoutQueryHandler(self.callback_basic)
dp.add_handler(handler) dp.add_handler(handler)
@ -87,14 +98,16 @@ class TestPreCheckoutQueryHandler(object):
assert self.test_flag assert self.test_flag
def test_pass_user_or_chat_data(self, dp, pre_checkout_query): 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.add_handler(handler)
dp.process_update(pre_checkout_query) dp.process_update(pre_checkout_query)
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -102,7 +115,8 @@ class TestPreCheckoutQueryHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) pass_user_data=True)
dp.add_handler(handler) dp.add_handler(handler)
@ -111,14 +125,16 @@ class TestPreCheckoutQueryHandler(object):
assert self.test_flag assert self.test_flag
def test_pass_job_or_update_queue(self, dp, pre_checkout_query): 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.add_handler(handler)
dp.process_update(pre_checkout_query) dp.process_update(pre_checkout_query)
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -126,7 +142,8 @@ class TestPreCheckoutQueryHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) pass_update_queue=True)
dp.add_handler(handler) dp.add_handler(handler)
@ -137,3 +154,10 @@ class TestPreCheckoutQueryHandler(object):
def test_other_update_types(self, false_update): def test_other_update_types(self, false_update):
handler = PreCheckoutQueryHandler(self.callback_basic) handler = PreCheckoutQueryHandler(self.callback_basic)
assert not handler.check_update(false_update) 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 # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest import pytest
from telegram import (Message, Update, Chat, Bot, User, CallbackQuery, InlineQuery, from telegram import (Message, Update, Chat, Bot, User, CallbackQuery, InlineQuery,
ChosenInlineResult, ShippingQuery, PreCheckoutQuery) ChosenInlineResult, ShippingQuery, PreCheckoutQuery)
from telegram.ext import RegexHandler from telegram.ext import RegexHandler, CallbackContext, JobQueue
message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text') message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
@ -45,7 +46,7 @@ def false_update(request):
@pytest.fixture(scope='class') @pytest.fixture(scope='class')
def message(bot): 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): class TestRegexHandler(object):
@ -78,6 +79,22 @@ class TestRegexHandler(object):
if groupdict is not None: if groupdict is not None:
self.test_flag = groupdict == {'begin': 't', 'end': ' message'} 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): def test_basic(self, dp, message):
handler = RegexHandler('.*', self.callback_basic) handler = RegexHandler('.*', self.callback_basic)
dp.add_handler(handler) dp.add_handler(handler)
@ -140,7 +157,8 @@ class TestRegexHandler(object):
def test_allow_edited(self, message): def test_allow_edited(self, message):
with pytest.warns(UserWarning): with pytest.warns(UserWarning):
handler = RegexHandler('.*', self.callback_basic, message_updates=True, handler = RegexHandler('.*', self.callback_basic,
message_updates=True,
allow_edited=True) allow_edited=True)
assert handler.check_update(Update(0, edited_message=message)) assert handler.check_update(Update(0, edited_message=message))
@ -185,7 +203,8 @@ class TestRegexHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -204,3 +223,24 @@ class TestRegexHandler(object):
def test_other_update_types(self, false_update): def test_other_update_types(self, false_update):
handler = RegexHandler('.*', self.callback_basic, edited_updates=True) handler = RegexHandler('.*', self.callback_basic, edited_updates=True)
assert not handler.check_update(false_update) 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 # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest import pytest
from telegram import (Update, Chat, Bot, ChosenInlineResult, User, Message, CallbackQuery, from telegram import (Update, Chat, Bot, ChosenInlineResult, User, Message, CallbackQuery,
InlineQuery, ShippingQuery, PreCheckoutQuery, ShippingAddress) 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') 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): 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) 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): def test_basic(self, dp, shiping_query):
handler = ShippingQueryHandler(self.callback_basic) handler = ShippingQueryHandler(self.callback_basic)
dp.add_handler(handler) dp.add_handler(handler)
@ -88,14 +99,16 @@ class TestShippingQueryHandler(object):
assert self.test_flag assert self.test_flag
def test_pass_user_or_chat_data(self, dp, shiping_query): 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.add_handler(handler)
dp.process_update(shiping_query) dp.process_update(shiping_query)
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -103,7 +116,8 @@ class TestShippingQueryHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) pass_user_data=True)
dp.add_handler(handler) dp.add_handler(handler)
@ -112,14 +126,16 @@ class TestShippingQueryHandler(object):
assert self.test_flag assert self.test_flag
def test_pass_job_or_update_queue(self, dp, shiping_query): 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.add_handler(handler)
dp.process_update(shiping_query) dp.process_update(shiping_query)
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -127,7 +143,8 @@ class TestShippingQueryHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) pass_update_queue=True)
dp.add_handler(handler) dp.add_handler(handler)
@ -138,3 +155,10 @@ class TestShippingQueryHandler(object):
def test_other_update_types(self, false_update): def test_other_update_types(self, false_update):
handler = ShippingQueryHandler(self.callback_basic) handler = ShippingQueryHandler(self.callback_basic)
assert not handler.check_update(false_update) 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 # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest import pytest
from telegram import (Bot, Update, Message, User, Chat, CallbackQuery, InlineQuery, from telegram import (Bot, Update, Message, User, Chat, CallbackQuery, InlineQuery,
ChosenInlineResult, ShippingQuery, PreCheckoutQuery) 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') message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
@ -71,20 +73,37 @@ class TestStringCommandHandler(object):
else: else:
self.test_flag = args == ['one', 'two'] 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): def test_basic(self, dp):
handler = StringCommandHandler('test', self.callback_basic) handler = StringCommandHandler('test', self.callback_basic)
dp.add_handler(handler) 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') dp.process_update('/test')
assert self.test_flag assert self.test_flag
assert not handler.check_update('/nottest') check = handler.check_update('/nottest')
assert not handler.check_update('not /test in front') assert check is None or check is False
assert handler.check_update('/test followed by text') 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): 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.add_handler(handler)
dp.process_update('/test') dp.process_update('/test')
@ -95,14 +114,16 @@ class TestStringCommandHandler(object):
assert self.test_flag assert self.test_flag
def test_pass_job_or_update_queue(self, dp): 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.add_handler(handler)
dp.process_update('/test') dp.process_update('/test')
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -110,7 +131,8 @@ class TestStringCommandHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) pass_update_queue=True)
dp.add_handler(handler) dp.add_handler(handler)
@ -121,3 +143,20 @@ class TestStringCommandHandler(object):
def test_other_update_types(self, false_update): def test_other_update_types(self, false_update):
handler = StringCommandHandler('test', self.callback_basic) handler = StringCommandHandler('test', self.callback_basic)
assert not handler.check_update(false_update) 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 # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
from queue import Queue
import pytest import pytest
from telegram import (Bot, Update, Message, User, Chat, CallbackQuery, InlineQuery, from telegram import (Bot, Update, Message, User, Chat, CallbackQuery, InlineQuery,
ChosenInlineResult, ShippingQuery, PreCheckoutQuery) 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') message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
@ -71,6 +73,19 @@ class TestStringRegexHandler(object):
if groupdict is not None: if groupdict is not None:
self.test_flag = groupdict == {'begin': 't', 'end': ' message'} 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): def test_basic(self, dp):
handler = StringRegexHandler('(?P<begin>.*)est(?P<end>.*)', self.callback_basic) handler = StringRegexHandler('(?P<begin>.*)est(?P<end>.*)', self.callback_basic)
dp.add_handler(handler) dp.add_handler(handler)
@ -99,14 +114,16 @@ class TestStringRegexHandler(object):
assert self.test_flag assert self.test_flag
def test_pass_job_or_update_queue(self, dp): 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.add_handler(handler)
dp.process_update('test') dp.process_update('test')
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -114,8 +131,8 @@ class TestStringRegexHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) dp.remove_handler(handler)
handler = StringRegexHandler('test', self.callback_queue_2, pass_job_queue=True, handler = StringRegexHandler('test', self.callback_queue_2,
pass_update_queue=True) pass_job_queue=True, pass_update_queue=True)
dp.add_handler(handler) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -125,3 +142,24 @@ class TestStringRegexHandler(object):
def test_other_update_types(self, false_update): def test_other_update_types(self, false_update):
handler = StringRegexHandler('test', self.callback_basic) handler = StringRegexHandler('test', self.callback_basic)
assert not handler.check_update(false_update) 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 # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
from collections import OrderedDict from collections import OrderedDict
from queue import Queue
import pytest import pytest
from telegram import Bot from telegram import Bot
from telegram.ext import TypeHandler from telegram.ext import TypeHandler, CallbackContext, JobQueue
class TestTypeHandler(object): class TestTypeHandler(object):
@ -42,6 +43,15 @@ class TestTypeHandler(object):
def callback_queue_2(self, bot, update, job_queue=None, update_queue=None): def callback_queue_2(self, bot, update, job_queue=None, update_queue=None):
self.test_flag = (job_queue is not None) and (update_queue is not None) 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): def test_basic(self, dp):
handler = TypeHandler(dict, self.callback_basic) handler = TypeHandler(dict, self.callback_basic)
dp.add_handler(handler) dp.add_handler(handler)
@ -65,7 +75,8 @@ class TestTypeHandler(object):
assert self.test_flag assert self.test_flag
dp.remove_handler(handler) 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) dp.add_handler(handler)
self.test_flag = False self.test_flag = False
@ -80,3 +91,10 @@ class TestTypeHandler(object):
self.test_flag = False self.test_flag = False
dp.process_update({'a': 1, 'b': 2}) dp.process_update({'a': 1, 'b': 2})
assert self.test_flag 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