Switch Code Formatting to Black (#2122)

* Swtich code formatting to Black

* Update docs

* Fix tests

* TRy fixing pre-commit
This commit is contained in:
Bibo-Joshi 2020-10-09 17:22:07 +02:00 committed by GitHub
parent 8efb05290a
commit 264b2c9c72
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
237 changed files with 9442 additions and 5895 deletions

View file

@ -91,12 +91,14 @@ Here's how to make a one-off code change.
Once the process terminates, you can view the built documentation by opening ``docs/build/html/index.html`` with a browser. Once the process terminates, you can view the built documentation by opening ``docs/build/html/index.html`` with a browser.
- For consistency, please conform to `Google Python Style Guide`_ and `Google Python Style Docstrings`_. In addition, code should be formatted consistently with other code around it. - For consistency, please conform to `Google Python Style Guide`_ and `Google Python Style Docstrings`_.
- The following exceptions to the above (Google's) style guides applies: - The following exceptions to the above (Google's) style guides applies:
- Documenting types of global variables and complex types of class members can be done using the Sphinx docstring convention. - Documenting types of global variables and complex types of class members can be done using the Sphinx docstring convention.
- In addition, PTB uses the `Black`_ coder formatting. Plugins for Black exist for some `popular editors`_. You can use those instead of manually formatting everything.
- Please ensure that the code you write is well-tested. - Please ensure that the code you write is well-tested.
- Dont break backward compatibility. - Dont break backward compatibility.
@ -189,11 +191,6 @@ Here's how to make a one-off code change.
Style commandments Style commandments
------------------ ------------------
Specific commandments
#####################
- Avoid using "double quotes" where you can reasonably use 'single quotes'.
Assert comparison order Assert comparison order
####################### #######################
@ -255,3 +252,5 @@ break the API classes. For example:
.. _AUTHORS.rst: ../AUTHORS.rst .. _AUTHORS.rst: ../AUTHORS.rst
.. _`MyPy`: https://mypy.readthedocs.io/en/stable/index.html .. _`MyPy`: https://mypy.readthedocs.io/en/stable/index.html
.. _`here`: https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html .. _`here`: https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html
.. _`Black`: https://black.readthedocs.io/en/stable/index.html
.. _`popular editors`: https://black.readthedocs.io/en/stable/editor_integration.html

View file

@ -1,9 +1,8 @@
repos: repos:
- repo: git://github.com/python-telegram-bot/mirrors-yapf - repo: https://github.com/psf/black
rev: 5769e088ef6e0a0d1eb63bd6d0c1fe9f3606d6c8 rev: 20.8b1
hooks: hooks:
- id: yapf - id: black
files: ^(telegram|tests)/.*\.py$
args: args:
- --diff - --diff
- repo: https://gitlab.com/pycqa/flake8 - repo: https://gitlab.com/pycqa/flake8

View file

@ -45,6 +45,9 @@ We have a vibrant community of developers helping each other in our `Telegram gr
:target: https://www.codacy.com/app/python-telegram-bot/python-telegram-bot?utm_source=github.com&utm_medium=referral&utm_content=python-telegram-bot/python-telegram-bot&utm_campaign=Badge_Grade :target: https://www.codacy.com/app/python-telegram-bot/python-telegram-bot?utm_source=github.com&utm_medium=referral&utm_content=python-telegram-bot/python-telegram-bot&utm_campaign=Badge_Grade
:alt: Code quality :alt: Code quality
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
.. image:: https://img.shields.io/badge/Telegram-Group-blue.svg .. image:: https://img.shields.io/badge/Telegram-Group-blue.svg
:target: https://telegram.me/pythontelegrambotgroup :target: https://telegram.me/pythontelegrambotgroup
:alt: Telegram Group :alt: Telegram Group

View file

@ -16,13 +16,13 @@ bot.
import logging import logging
from telegram import (ReplyKeyboardMarkup, ReplyKeyboardRemove) from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, ConversationHandler
ConversationHandler)
# Enable logging # Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(
level=logging.INFO) format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -36,7 +36,8 @@ def start(update, context):
'Hi! My name is Professor Bot. I will hold a conversation with you. ' 'Hi! My name is Professor Bot. I will hold a conversation with you. '
'Send /cancel to stop talking to me.\n\n' 'Send /cancel to stop talking to me.\n\n'
'Are you a boy or a girl?', 'Are you a boy or a girl?',
reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)) reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True),
)
return GENDER return GENDER
@ -44,9 +45,11 @@ def start(update, context):
def gender(update, context): 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(
'so I know what you look like, or send /skip if you don\'t want to.', 'I see! Please send me a photo of yourself, '
reply_markup=ReplyKeyboardRemove()) 'so I know what you look like, or send /skip if you don\'t want to.',
reply_markup=ReplyKeyboardRemove(),
)
return PHOTO return PHOTO
@ -56,8 +59,9 @@ def photo(update, context):
photo_file = update.message.photo[-1].get_file() 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(
'or send /skip if you don\'t want to.') 'Gorgeous! Now, send me your location please, ' 'or send /skip if you don\'t want to.'
)
return LOCATION return LOCATION
@ -65,8 +69,9 @@ def photo(update, context):
def skip_photo(update, context): 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(
'or send /skip.') 'I bet you look great! Now, send me your location please, ' 'or send /skip.'
)
return LOCATION return LOCATION
@ -74,10 +79,12 @@ def skip_photo(update, context):
def location(update, context): 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(
user_location.longitude) "Location of %s: %f / %f", user.first_name, user_location.latitude, user_location.longitude
update.message.reply_text('Maybe I can visit you sometime! ' )
'At last, tell me something about yourself.') update.message.reply_text(
'Maybe I can visit you sometime! ' 'At last, tell me something about yourself.'
)
return BIO return BIO
@ -85,8 +92,9 @@ def location(update, context):
def skip_location(update, context): 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(
'At last, tell me something about yourself.') 'You seem a bit paranoid! ' 'At last, tell me something about yourself.'
)
return BIO return BIO
@ -102,8 +110,9 @@ def bio(update, context):
def cancel(update, context): 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(
reply_markup=ReplyKeyboardRemove()) 'Bye! I hope we can talk again some day.', reply_markup=ReplyKeyboardRemove()
)
return ConversationHandler.END return ConversationHandler.END
@ -120,20 +129,16 @@ def main():
# Add conversation handler with the states GENDER, PHOTO, LOCATION and BIO # Add conversation handler with the states GENDER, PHOTO, LOCATION and BIO
conv_handler = ConversationHandler( conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)], entry_points=[CommandHandler('start', start)],
states={ states={
GENDER: [MessageHandler(Filters.regex('^(Boy|Girl|Other)$'), gender)], GENDER: [MessageHandler(Filters.regex('^(Boy|Girl|Other)$'), gender)],
PHOTO: [MessageHandler(Filters.photo, photo), CommandHandler('skip', skip_photo)],
PHOTO: [MessageHandler(Filters.photo, photo), LOCATION: [
CommandHandler('skip', skip_photo)], MessageHandler(Filters.location, location),
CommandHandler('skip', skip_location),
LOCATION: [MessageHandler(Filters.location, location), ],
CommandHandler('skip', skip_location)], BIO: [MessageHandler(Filters.text & ~Filters.command, bio)],
BIO: [MessageHandler(Filters.text & ~Filters.command, bio)]
}, },
fallbacks=[CommandHandler('cancel', cancel)],
fallbacks=[CommandHandler('cancel', cancel)]
) )
dp.add_handler(conv_handler) dp.add_handler(conv_handler)

View file

@ -17,20 +17,22 @@ bot.
import logging import logging
from telegram import ReplyKeyboardMarkup from telegram import ReplyKeyboardMarkup
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, ConversationHandler
ConversationHandler)
# Enable logging # Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(
level=logging.INFO) format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
CHOOSING, TYPING_REPLY, TYPING_CHOICE = range(3) CHOOSING, TYPING_REPLY, TYPING_CHOICE = range(3)
reply_keyboard = [['Age', 'Favourite colour'], reply_keyboard = [
['Number of siblings', 'Something else...'], ['Age', 'Favourite colour'],
['Done']] ['Number of siblings', 'Something else...'],
['Done'],
]
markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True) markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
@ -47,7 +49,8 @@ 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?",
reply_markup=markup) reply_markup=markup,
)
return CHOOSING return CHOOSING
@ -56,14 +59,16 @@ def regular_choice(update, context):
text = update.message.text text = update.message.text
context.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(update, context): def custom_choice(update, context):
update.message.reply_text('Alright, please send me the category first, ' update.message.reply_text(
'for example "Most impressive skill"') 'Alright, please send me the category first, ' 'for example "Most impressive skill"'
)
return TYPING_CHOICE return TYPING_CHOICE
@ -75,10 +80,12 @@ def received_information(update, context):
user_data[category] = text user_data[category] = text
del user_data['choice'] del user_data['choice']
update.message.reply_text("Neat! Just so you know, this is what you already told me:" update.message.reply_text(
"{} You can tell me more, or change your opinion" "Neat! Just so you know, this is what you already told me:"
" on something.".format(facts_to_str(user_data)), "{} You can tell me more, or change your opinion"
reply_markup=markup) " on something.".format(facts_to_str(user_data)),
reply_markup=markup,
)
return CHOOSING return CHOOSING
@ -88,9 +95,9 @@ def done(update, context):
if 'choice' in user_data: if 'choice' in user_data:
del user_data['choice'] del user_data['choice']
update.message.reply_text("I learned these facts about you:" update.message.reply_text(
"{}" "I learned these facts about you:" "{}" "Until next time!".format(facts_to_str(user_data))
"Until next time!".format(facts_to_str(user_data))) )
user_data.clear() user_data.clear()
return ConversationHandler.END return ConversationHandler.END
@ -108,24 +115,26 @@ def main():
# Add conversation handler with the states CHOOSING, TYPING_CHOICE and TYPING_REPLY # Add conversation handler with the states CHOOSING, TYPING_CHOICE and TYPING_REPLY
conv_handler = ConversationHandler( conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)], entry_points=[CommandHandler('start', start)],
states={ states={
CHOOSING: [MessageHandler(Filters.regex('^(Age|Favourite colour|Number of siblings)$'), CHOOSING: [
regular_choice), MessageHandler(
MessageHandler(Filters.regex('^Something else...$'), Filters.regex('^(Age|Favourite colour|Number of siblings)$'), regular_choice
custom_choice) ),
], MessageHandler(Filters.regex('^Something else...$'), custom_choice),
],
TYPING_CHOICE: [ TYPING_CHOICE: [
MessageHandler(Filters.text & ~(Filters.command | Filters.regex('^Done$')), MessageHandler(
regular_choice)], Filters.text & ~(Filters.command | Filters.regex('^Done$')), regular_choice
)
],
TYPING_REPLY: [ TYPING_REPLY: [
MessageHandler(Filters.text & ~(Filters.command | Filters.regex('^Done$')), MessageHandler(
received_information)], Filters.text & ~(Filters.command | Filters.regex('^Done$')),
received_information,
)
],
}, },
fallbacks=[MessageHandler(Filters.regex('^Done$'), done)],
fallbacks=[MessageHandler(Filters.regex('^Done$'), done)]
) )
dp.add_handler(conv_handler) dp.add_handler(conv_handler)

View file

@ -25,8 +25,9 @@ from telegram.ext import Updater, CommandHandler, Filters
# Enable logging # Enable logging
from telegram.utils import helpers from telegram.utils import helpers
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(
level=logging.INFO) format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -48,8 +49,10 @@ def deep_linked_level_1(update, context):
"""Reached through the CHECK_THIS_OUT payload""" """Reached through the CHECK_THIS_OUT payload"""
bot = context.bot bot = context.bot
url = helpers.create_deep_linked_url(bot.get_me().username, SO_COOL) url = helpers.create_deep_linked_url(bot.get_me().username, SO_COOL)
text = "Awesome, you just accessed hidden functionality! " \ text = (
" Now let's get back to the private chat." "Awesome, you just accessed hidden functionality! "
" Now let's get back to the private chat."
)
keyboard = InlineKeyboardMarkup.from_button( keyboard = InlineKeyboardMarkup.from_button(
InlineKeyboardButton(text='Continue here!', url=url) InlineKeyboardButton(text='Continue here!', url=url)
) )
@ -60,16 +63,16 @@ def deep_linked_level_2(update, context):
"""Reached through the SO_COOL payload""" """Reached through the SO_COOL payload"""
bot = context.bot bot = context.bot
url = helpers.create_deep_linked_url(bot.get_me().username, USING_ENTITIES) url = helpers.create_deep_linked_url(bot.get_me().username, USING_ENTITIES)
text = "You can also mask the deep-linked URLs as links: " \ text = "You can also mask the deep-linked URLs as links: " "[▶️ CLICK HERE]({}).".format(url)
"[▶️ CLICK HERE]({}).".format(url)
update.message.reply_text(text, parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True) update.message.reply_text(text, parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True)
def deep_linked_level_3(update, context): def deep_linked_level_3(update, context):
"""Reached through the USING_ENTITIES payload""" """Reached through the USING_ENTITIES payload"""
payload = context.args payload = context.args
update.message.reply_text("Congratulations! This is as deep as it gets 👏🏻\n\n" update.message.reply_text(
"The payload was: {}".format(payload)) "Congratulations! This is as deep as it gets 👏🏻\n\n" "The payload was: {}".format(payload)
)
def main(): def main():
@ -90,10 +93,9 @@ def main():
dp.add_handler(CommandHandler("start", deep_linked_level_2, Filters.regex(SO_COOL))) dp.add_handler(CommandHandler("start", deep_linked_level_2, Filters.regex(SO_COOL)))
# We can also pass on the deep-linking payload # We can also pass on the deep-linking payload
dp.add_handler(CommandHandler("start", dp.add_handler(
deep_linked_level_3, CommandHandler("start", deep_linked_level_3, Filters.regex(USING_ENTITIES), pass_args=True)
Filters.regex(USING_ENTITIES), )
pass_args=True))
# Make sure the deep-linking handlers occur *before* the normal /start handler. # Make sure the deep-linking handlers occur *before* the normal /start handler.
dp.add_handler(CommandHandler("start", start)) dp.add_handler(CommandHandler("start", start))

View file

@ -20,8 +20,9 @@ import logging
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters 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(
level=logging.INFO) format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View file

@ -13,8 +13,9 @@ import traceback
from telegram import Update, ParseMode from telegram import Update, ParseMode
from telegram.ext import Updater, CallbackContext, CommandHandler from telegram.ext import Updater, CallbackContext, CommandHandler
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(
level=logging.INFO) format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -48,7 +49,7 @@ def error_handler(update: Update, context: CallbackContext):
html.escape(json.dumps(update.to_dict(), indent=2, ensure_ascii=False)), html.escape(json.dumps(update.to_dict(), indent=2, ensure_ascii=False)),
html.escape(str(context.chat_data)), html.escape(str(context.chat_data)),
html.escape(str(context.user_data)), html.escape(str(context.user_data)),
html.escape(tb) html.escape(tb),
) )
# Finally, send the message # Finally, send the message
@ -61,9 +62,10 @@ def bad_command(update: Update, context: CallbackContext):
def start(update: Update, context: CallbackContext): def start(update: Update, context: CallbackContext):
update.effective_message.reply_html('Use /bad_command to cause an error.\n' update.effective_message.reply_html(
'Your chat id is <code>{}</code>.' 'Use /bad_command to cause an error.\n'
.format(update.effective_chat.id)) 'Your chat id is <code>{}</code>.'.format(update.effective_chat.id)
)
def main(): def main():

View file

@ -15,14 +15,14 @@ bot.
import logging import logging
from uuid import uuid4 from uuid import uuid4
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
from telegram.utils.helpers import escape_markdown from telegram.utils.helpers import escape_markdown
# Enable logging # Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(
level=logging.INFO) format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -44,22 +44,23 @@ def inlinequery(update, context):
query = update.inline_query.query query = update.inline_query.query
results = [ results = [
InlineQueryResultArticle( InlineQueryResultArticle(
id=uuid4(), id=uuid4(), title="Caps", input_message_content=InputTextMessageContent(query.upper())
title="Caps", ),
input_message_content=InputTextMessageContent(
query.upper())),
InlineQueryResultArticle( InlineQueryResultArticle(
id=uuid4(), id=uuid4(),
title="Bold", title="Bold",
input_message_content=InputTextMessageContent( input_message_content=InputTextMessageContent(
"*{}*".format(escape_markdown(query)), "*{}*".format(escape_markdown(query)), parse_mode=ParseMode.MARKDOWN
parse_mode=ParseMode.MARKDOWN)), ),
),
InlineQueryResultArticle( InlineQueryResultArticle(
id=uuid4(), id=uuid4(),
title="Italic", title="Italic",
input_message_content=InputTextMessageContent( input_message_content=InputTextMessageContent(
"_{}_".format(escape_markdown(query)), "_{}_".format(escape_markdown(query)), parse_mode=ParseMode.MARKDOWN
parse_mode=ParseMode.MARKDOWN))] ),
),
]
update.inline_query.answer(results) update.inline_query.answer(results)

View file

@ -10,16 +10,20 @@ 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
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(
level=logging.INFO) format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def start(update, context): def start(update, context):
keyboard = [[InlineKeyboardButton("Option 1", callback_data='1'), keyboard = [
InlineKeyboardButton("Option 2", callback_data='2')], [
InlineKeyboardButton("Option 1", callback_data='1'),
[InlineKeyboardButton("Option 3", callback_data='3')]] InlineKeyboardButton("Option 2", callback_data='2'),
],
[InlineKeyboardButton("Option 3", callback_data='3')],
]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)

View file

@ -17,8 +17,9 @@ from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, Conversa
import logging import logging
# Enable logging # Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(
level=logging.INFO) format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -38,15 +39,14 @@ def start(update, context):
# The keyboard is a list of button rows, where each row is in turn # The keyboard is a list of button rows, where each row is in turn
# a list (hence `[[...]]`). # a list (hence `[[...]]`).
keyboard = [ keyboard = [
[InlineKeyboardButton("1", callback_data=str(ONE)), [
InlineKeyboardButton("2", callback_data=str(TWO))] InlineKeyboardButton("1", callback_data=str(ONE)),
InlineKeyboardButton("2", callback_data=str(TWO)),
]
] ]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
# Send message with text and appended InlineKeyboard # Send message with text and appended InlineKeyboard
update.message.reply_text( update.message.reply_text("Start handler, Choose a route", reply_markup=reply_markup)
"Start handler, Choose a route",
reply_markup=reply_markup
)
# Tell ConversationHandler that we're in state `FIRST` now # Tell ConversationHandler that we're in state `FIRST` now
return FIRST return FIRST
@ -59,17 +59,16 @@ def start_over(update, context):
# Some clients may have trouble otherwise. See https://core.telegram.org/bots/api#callbackquery # Some clients may have trouble otherwise. See https://core.telegram.org/bots/api#callbackquery
query.answer() query.answer()
keyboard = [ keyboard = [
[InlineKeyboardButton("1", callback_data=str(ONE)), [
InlineKeyboardButton("2", callback_data=str(TWO))] InlineKeyboardButton("1", callback_data=str(ONE)),
InlineKeyboardButton("2", callback_data=str(TWO)),
]
] ]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
# Instead of sending a new message, edit the message that # Instead of sending a new message, edit the message that
# originated the CallbackQuery. This gives the feeling of an # originated the CallbackQuery. This gives the feeling of an
# interactive menu. # interactive menu.
query.edit_message_text( query.edit_message_text(text="Start handler, Choose a route", reply_markup=reply_markup)
text="Start handler, Choose a route",
reply_markup=reply_markup
)
return FIRST return FIRST
@ -78,13 +77,14 @@ def one(update, context):
query = update.callback_query query = update.callback_query
query.answer() query.answer()
keyboard = [ keyboard = [
[InlineKeyboardButton("3", callback_data=str(THREE)), [
InlineKeyboardButton("4", callback_data=str(FOUR))] InlineKeyboardButton("3", callback_data=str(THREE)),
InlineKeyboardButton("4", callback_data=str(FOUR)),
]
] ]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
query.edit_message_text( query.edit_message_text(
text="First CallbackQueryHandler, Choose a route", text="First CallbackQueryHandler, Choose a route", reply_markup=reply_markup
reply_markup=reply_markup
) )
return FIRST return FIRST
@ -94,13 +94,14 @@ def two(update, context):
query = update.callback_query query = update.callback_query
query.answer() query.answer()
keyboard = [ keyboard = [
[InlineKeyboardButton("1", callback_data=str(ONE)), [
InlineKeyboardButton("3", callback_data=str(THREE))] InlineKeyboardButton("1", callback_data=str(ONE)),
InlineKeyboardButton("3", callback_data=str(THREE)),
]
] ]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
query.edit_message_text( query.edit_message_text(
text="Second CallbackQueryHandler, Choose a route", text="Second CallbackQueryHandler, Choose a route", reply_markup=reply_markup
reply_markup=reply_markup
) )
return FIRST return FIRST
@ -110,13 +111,14 @@ def three(update, context):
query = update.callback_query query = update.callback_query
query.answer() query.answer()
keyboard = [ keyboard = [
[InlineKeyboardButton("Yes, let's do it again!", callback_data=str(ONE)), [
InlineKeyboardButton("Nah, I've had enough ...", callback_data=str(TWO))] InlineKeyboardButton("Yes, let's do it again!", callback_data=str(ONE)),
InlineKeyboardButton("Nah, I've had enough ...", callback_data=str(TWO)),
]
] ]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
query.edit_message_text( query.edit_message_text(
text="Third CallbackQueryHandler. Do want to start over?", text="Third CallbackQueryHandler. Do want to start over?", reply_markup=reply_markup
reply_markup=reply_markup
) )
# Transfer to conversation state `SECOND` # Transfer to conversation state `SECOND`
return SECOND return SECOND
@ -127,13 +129,14 @@ def four(update, context):
query = update.callback_query query = update.callback_query
query.answer() query.answer()
keyboard = [ keyboard = [
[InlineKeyboardButton("2", callback_data=str(TWO)), [
InlineKeyboardButton("4", callback_data=str(FOUR))] InlineKeyboardButton("2", callback_data=str(TWO)),
InlineKeyboardButton("4", callback_data=str(FOUR)),
]
] ]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
query.edit_message_text( query.edit_message_text(
text="Fourth CallbackQueryHandler, Choose a route", text="Fourth CallbackQueryHandler, Choose a route", reply_markup=reply_markup
reply_markup=reply_markup
) )
return FIRST return FIRST
@ -143,9 +146,7 @@ def end(update, context):
ConversationHandler that the conversation is over""" ConversationHandler that the conversation is over"""
query = update.callback_query query = update.callback_query
query.answer() query.answer()
query.edit_message_text( query.edit_message_text(text="See you next time!")
text="See you next time!"
)
return ConversationHandler.END return ConversationHandler.END
@ -165,14 +166,18 @@ def main():
conv_handler = ConversationHandler( conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)], entry_points=[CommandHandler('start', start)],
states={ states={
FIRST: [CallbackQueryHandler(one, pattern='^' + str(ONE) + '$'), FIRST: [
CallbackQueryHandler(two, pattern='^' + str(TWO) + '$'), CallbackQueryHandler(one, pattern='^' + str(ONE) + '$'),
CallbackQueryHandler(three, pattern='^' + str(THREE) + '$'), CallbackQueryHandler(two, pattern='^' + str(TWO) + '$'),
CallbackQueryHandler(four, pattern='^' + str(FOUR) + '$')], CallbackQueryHandler(three, pattern='^' + str(THREE) + '$'),
SECOND: [CallbackQueryHandler(start_over, pattern='^' + str(ONE) + '$'), CallbackQueryHandler(four, pattern='^' + str(FOUR) + '$'),
CallbackQueryHandler(end, pattern='^' + str(TWO) + '$')] ],
SECOND: [
CallbackQueryHandler(start_over, pattern='^' + str(ONE) + '$'),
CallbackQueryHandler(end, pattern='^' + str(TWO) + '$'),
],
}, },
fallbacks=[CommandHandler('start', start)] fallbacks=[CommandHandler('start', start)],
) )
# Add ConversationHandler to dispatcher that will be used for handling # Add ConversationHandler to dispatcher that will be used for handling

View file

@ -16,13 +16,20 @@ bot.
import logging import logging
from telegram import (InlineKeyboardMarkup, InlineKeyboardButton) from telegram import InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, from telegram.ext import (
ConversationHandler, CallbackQueryHandler) Updater,
CommandHandler,
MessageHandler,
Filters,
ConversationHandler,
CallbackQueryHandler,
)
# Enable logging # Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(
level=logging.INFO) format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -38,8 +45,20 @@ STOPPING, SHOWING = map(chr, range(8, 10))
END = ConversationHandler.END END = ConversationHandler.END
# Different constants for this example # Different constants for this example
(PARENTS, CHILDREN, SELF, GENDER, MALE, FEMALE, AGE, NAME, START_OVER, FEATURES, (
CURRENT_FEATURE, CURRENT_LEVEL) = map(chr, range(10, 22)) PARENTS,
CHILDREN,
SELF,
GENDER,
MALE,
FEMALE,
AGE,
NAME,
START_OVER,
FEATURES,
CURRENT_FEATURE,
CURRENT_LEVEL,
) = map(chr, range(10, 22))
# Helper # Helper
@ -53,15 +72,20 @@ def _name_switcher(level):
# Top level conversation callbacks # Top level conversation callbacks
def start(update, context): def start(update, context):
"""Select an action: Adding parent/child or show data.""" """Select an action: Adding parent/child or show data."""
text = 'You may add a familiy member, yourself show the gathered data or end the ' \ text = (
'conversation. To abort, simply type /stop.' 'You may add a familiy member, yourself show the gathered data or end the '
buttons = [[ 'conversation. To abort, simply type /stop.'
InlineKeyboardButton(text='Add family member', callback_data=str(ADDING_MEMBER)), )
InlineKeyboardButton(text='Add yourself', callback_data=str(ADDING_SELF)) buttons = [
], [ [
InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)), InlineKeyboardButton(text='Add family member', callback_data=str(ADDING_MEMBER)),
InlineKeyboardButton(text='Done', callback_data=str(END)) InlineKeyboardButton(text='Add yourself', callback_data=str(ADDING_SELF)),
]] ],
[
InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)),
InlineKeyboardButton(text='Done', callback_data=str(END)),
],
]
keyboard = InlineKeyboardMarkup(buttons) keyboard = InlineKeyboardMarkup(buttons)
# If we're starting over we don't need do send a new message # If we're starting over we don't need do send a new message
@ -69,8 +93,9 @@ def start(update, context):
update.callback_query.answer() update.callback_query.answer()
update.callback_query.edit_message_text(text=text, reply_markup=keyboard) update.callback_query.edit_message_text(text=text, reply_markup=keyboard)
else: else:
update.message.reply_text('Hi, I\'m FamiliyBot and here to help you gather information' update.message.reply_text(
'about your family.') 'Hi, I\'m FamiliyBot and here to help you gather information' 'about your family.'
)
update.message.reply_text(text=text, reply_markup=keyboard) update.message.reply_text(text=text, reply_markup=keyboard)
context.user_data[START_OVER] = False context.user_data[START_OVER] = False
@ -92,6 +117,7 @@ def adding_self(update, context):
def show_data(update, context): def show_data(update, context):
"""Pretty print gathered data.""" """Pretty print gathered data."""
def prettyprint(user_data, level): def prettyprint(user_data, level):
people = user_data.get(level) people = user_data.get(level)
if not people: if not people:
@ -106,8 +132,9 @@ def show_data(update, context):
for person in user_data[level]: for person in user_data[level]:
gender = female if person[GENDER] == FEMALE else male gender = female if person[GENDER] == FEMALE else male
text += '\n{}: Name: {}, Age: {}'.format(gender, person.get(NAME, '-'), text += '\n{}: Name: {}, Age: {}'.format(
person.get(AGE, '-')) gender, person.get(NAME, '-'), person.get(AGE, '-')
)
return text return text
ud = context.user_data ud = context.user_data
@ -115,9 +142,7 @@ def show_data(update, context):
text += '\n\nParents:' + prettyprint(ud, PARENTS) text += '\n\nParents:' + prettyprint(ud, PARENTS)
text += '\n\nChildren:' + prettyprint(ud, CHILDREN) text += '\n\nChildren:' + prettyprint(ud, CHILDREN)
buttons = [[ buttons = [[InlineKeyboardButton(text='Back', callback_data=str(END))]]
InlineKeyboardButton(text='Back', callback_data=str(END))
]]
keyboard = InlineKeyboardMarkup(buttons) keyboard = InlineKeyboardMarkup(buttons)
update.callback_query.answer() update.callback_query.answer()
@ -148,13 +173,16 @@ def end(update, context):
def select_level(update, context): def select_level(update, context):
"""Choose to add a parent or a child.""" """Choose to add a parent or a child."""
text = 'You may add a parent or a child. Also you can show the gathered data or go back.' text = 'You may add a parent or a child. Also you can show the gathered data or go back.'
buttons = [[ buttons = [
InlineKeyboardButton(text='Add parent', callback_data=str(PARENTS)), [
InlineKeyboardButton(text='Add child', callback_data=str(CHILDREN)) InlineKeyboardButton(text='Add parent', callback_data=str(PARENTS)),
], [ InlineKeyboardButton(text='Add child', callback_data=str(CHILDREN)),
InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)), ],
InlineKeyboardButton(text='Back', callback_data=str(END)) [
]] InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)),
InlineKeyboardButton(text='Back', callback_data=str(END)),
],
]
keyboard = InlineKeyboardMarkup(buttons) keyboard = InlineKeyboardMarkup(buttons)
update.callback_query.answer() update.callback_query.answer()
@ -172,13 +200,16 @@ def select_gender(update, context):
male, female = _name_switcher(level) male, female = _name_switcher(level)
buttons = [[ buttons = [
InlineKeyboardButton(text='Add ' + male, callback_data=str(MALE)), [
InlineKeyboardButton(text='Add ' + female, callback_data=str(FEMALE)) InlineKeyboardButton(text='Add ' + male, callback_data=str(MALE)),
], [ InlineKeyboardButton(text='Add ' + female, callback_data=str(FEMALE)),
InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)), ],
InlineKeyboardButton(text='Back', callback_data=str(END)) [
]] InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)),
InlineKeyboardButton(text='Back', callback_data=str(END)),
],
]
keyboard = InlineKeyboardMarkup(buttons) keyboard = InlineKeyboardMarkup(buttons)
update.callback_query.answer() update.callback_query.answer()
@ -198,11 +229,13 @@ def end_second_level(update, context):
# Third level callbacks # Third level callbacks
def select_feature(update, context): def select_feature(update, context):
"""Select a feature to update for the person.""" """Select a feature to update for the person."""
buttons = [[ buttons = [
InlineKeyboardButton(text='Name', callback_data=str(NAME)), [
InlineKeyboardButton(text='Age', callback_data=str(AGE)), InlineKeyboardButton(text='Name', callback_data=str(NAME)),
InlineKeyboardButton(text='Done', callback_data=str(END)), InlineKeyboardButton(text='Age', callback_data=str(AGE)),
]] InlineKeyboardButton(text='Done', callback_data=str(END)),
]
]
keyboard = InlineKeyboardMarkup(buttons) keyboard = InlineKeyboardMarkup(buttons)
# If we collect features for a new person, clear the cache and save the gender # If we collect features for a new person, clear the cache and save the gender
@ -278,46 +311,45 @@ def main():
# Set up third level ConversationHandler (collecting features) # Set up third level ConversationHandler (collecting features)
description_conv = ConversationHandler( description_conv = ConversationHandler(
entry_points=[CallbackQueryHandler(select_feature, entry_points=[
pattern='^' + str(MALE) + '$|^' + str(FEMALE) + '$')], CallbackQueryHandler(
select_feature, pattern='^' + str(MALE) + '$|^' + str(FEMALE) + '$'
)
],
states={ states={
SELECTING_FEATURE: [CallbackQueryHandler(ask_for_input, SELECTING_FEATURE: [
pattern='^(?!' + str(END) + ').*$')], CallbackQueryHandler(ask_for_input, pattern='^(?!' + str(END) + ').*$')
],
TYPING: [MessageHandler(Filters.text & ~Filters.command, save_input)], TYPING: [MessageHandler(Filters.text & ~Filters.command, save_input)],
}, },
fallbacks=[ fallbacks=[
CallbackQueryHandler(end_describing, pattern='^' + str(END) + '$'), CallbackQueryHandler(end_describing, pattern='^' + str(END) + '$'),
CommandHandler('stop', stop_nested) CommandHandler('stop', stop_nested),
], ],
map_to_parent={ map_to_parent={
# Return to second level menu # Return to second level menu
END: SELECTING_LEVEL, END: SELECTING_LEVEL,
# End conversation alltogether # End conversation alltogether
STOPPING: STOPPING, STOPPING: STOPPING,
} },
) )
# Set up second level ConversationHandler (adding a person) # Set up second level ConversationHandler (adding a person)
add_member_conv = ConversationHandler( add_member_conv = ConversationHandler(
entry_points=[CallbackQueryHandler(select_level, entry_points=[CallbackQueryHandler(select_level, pattern='^' + str(ADDING_MEMBER) + '$')],
pattern='^' + str(ADDING_MEMBER) + '$')],
states={ states={
SELECTING_LEVEL: [CallbackQueryHandler(select_gender, SELECTING_LEVEL: [
pattern='^{}$|^{}$'.format(str(PARENTS), CallbackQueryHandler(
str(CHILDREN)))], select_gender, pattern='^{}$|^{}$'.format(str(PARENTS), str(CHILDREN))
SELECTING_GENDER: [description_conv] )
],
SELECTING_GENDER: [description_conv],
}, },
fallbacks=[ fallbacks=[
CallbackQueryHandler(show_data, pattern='^' + str(SHOWING) + '$'), CallbackQueryHandler(show_data, pattern='^' + str(SHOWING) + '$'),
CallbackQueryHandler(end_second_level, pattern='^' + str(END) + '$'), CallbackQueryHandler(end_second_level, pattern='^' + str(END) + '$'),
CommandHandler('stop', stop_nested) CommandHandler('stop', stop_nested),
], ],
map_to_parent={ map_to_parent={
# After showing data return to top level menu # After showing data return to top level menu
SHOWING: SHOWING, SHOWING: SHOWING,
@ -325,7 +357,7 @@ def main():
END: SELECTING_ACTION, END: SELECTING_ACTION,
# End conversation alltogether # End conversation alltogether
STOPPING: END, STOPPING: END,
} },
) )
# Set up top level ConversationHandler (selecting action) # Set up top level ConversationHandler (selecting action)
@ -339,7 +371,6 @@ def main():
] ]
conv_handler = ConversationHandler( conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)], entry_points=[CommandHandler('start', start)],
states={ states={
SHOWING: [CallbackQueryHandler(start, pattern='^' + str(END) + '$')], SHOWING: [CallbackQueryHandler(start, pattern='^' + str(END) + '$')],
SELECTING_ACTION: selection_handlers, SELECTING_ACTION: selection_handlers,
@ -347,7 +378,6 @@ def main():
DESCRIBING_SELF: [description_conv], DESCRIBING_SELF: [description_conv],
STOPPING: [CommandHandler('start', start)], STOPPING: [CommandHandler('start', start)],
}, },
fallbacks=[CommandHandler('stop', stop)], fallbacks=[CommandHandler('stop', stop)],
) )

View file

@ -15,8 +15,9 @@ import logging
from telegram.ext import Updater, MessageHandler, Filters from telegram.ext import Updater, MessageHandler, Filters
# Enable logging # Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(
level=logging.DEBUG) format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.DEBUG
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -39,18 +40,28 @@ def msg(update, context):
print('Phone: ', data.phone_number) print('Phone: ', data.phone_number)
elif data.type == 'email': elif data.type == 'email':
print('Email: ', data.email) print('Email: ', data.email)
if data.type in ('personal_details', 'passport', 'driver_license', 'identity_card', if data.type in (
'internal_passport', 'address'): 'personal_details',
'passport',
'driver_license',
'identity_card',
'internal_passport',
'address',
):
print(data.type, data.data) print(data.type, data.data)
if data.type in ('utility_bill', 'bank_statement', 'rental_agreement', if data.type in (
'passport_registration', 'temporary_registration'): 'utility_bill',
'bank_statement',
'rental_agreement',
'passport_registration',
'temporary_registration',
):
print(data.type, len(data.files), 'files') print(data.type, len(data.files), 'files')
for file in data.files: for file in data.files:
actual_file = file.get_file() actual_file = file.get_file()
print(actual_file) print(actual_file)
actual_file.download() actual_file.download()
if data.type in ('passport', 'driver_license', 'identity_card', if data.type in ('passport', 'driver_license', 'identity_card', 'internal_passport'):
'internal_passport'):
if data.front_side: if data.front_side:
file = data.front_side.get_file() file = data.front_side.get_file()
print(data.type, file) print(data.type, file)
@ -60,16 +71,22 @@ def msg(update, context):
file = data.reverse_side.get_file() file = data.reverse_side.get_file()
print(data.type, file) print(data.type, file)
file.download() file.download()
if data.type in ('passport', 'driver_license', 'identity_card', if data.type in ('passport', 'driver_license', 'identity_card', 'internal_passport'):
'internal_passport'):
if data.selfie: if data.selfie:
file = data.selfie.get_file() file = data.selfie.get_file()
print(data.type, file) print(data.type, file)
file.download() file.download()
if data.type in ('passport', 'driver_license', 'identity_card', if data.type in (
'internal_passport', 'utility_bill', 'bank_statement', 'passport',
'rental_agreement', 'passport_registration', 'driver_license',
'temporary_registration'): 'identity_card',
'internal_passport',
'utility_bill',
'bank_statement',
'rental_agreement',
'passport_registration',
'temporary_registration',
):
print(data.type, len(data.translation), 'translation') print(data.type, len(data.translation), 'translation')
for file in data.translation: for file in data.translation:
actual_file = file.get_file() actual_file = file.get_file()

View file

@ -8,13 +8,20 @@ Basic example for a bot that can receive payment from user.
import logging import logging
from telegram import (LabeledPrice, ShippingOption) from telegram import LabeledPrice, ShippingOption
from telegram.ext import (Updater, CommandHandler, MessageHandler, from telegram.ext import (
Filters, PreCheckoutQueryHandler, ShippingQueryHandler) Updater,
CommandHandler,
MessageHandler,
Filters,
PreCheckoutQueryHandler,
ShippingQueryHandler,
)
# Enable logging # Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(
level=logging.INFO) format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -43,10 +50,21 @@ def start_with_shipping_callback(update, context):
# 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
context.bot.send_invoice(chat_id, title, description, payload, context.bot.send_invoice(
provider_token, start_parameter, currency, prices, chat_id,
need_name=True, need_phone_number=True, title,
need_email=True, need_shipping_address=True, is_flexible=True) description,
payload,
provider_token,
start_parameter,
currency,
prices,
need_name=True,
need_phone_number=True,
need_email=True,
need_shipping_address=True,
is_flexible=True,
)
def start_without_shipping_callback(update, context): def start_without_shipping_callback(update, context):
@ -66,8 +84,9 @@ def start_without_shipping_callback(update, context):
# 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
context.bot.send_invoice(chat_id, title, description, payload, context.bot.send_invoice(
provider_token, start_parameter, currency, prices) chat_id, title, description, payload, provider_token, start_parameter, currency, prices
)
def shipping_callback(update, context): def shipping_callback(update, context):

View file

@ -15,22 +15,31 @@ bot.
""" """
from telegram import ReplyKeyboardMarkup from telegram import ReplyKeyboardMarkup
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, from telegram.ext import (
ConversationHandler, PicklePersistence) Updater,
CommandHandler,
MessageHandler,
Filters,
ConversationHandler,
PicklePersistence,
)
import logging import logging
# Enable logging # Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(
level=logging.INFO) format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
CHOOSING, TYPING_REPLY, TYPING_CHOICE = range(3) CHOOSING, TYPING_REPLY, TYPING_CHOICE = range(3)
reply_keyboard = [['Age', 'Favourite colour'], reply_keyboard = [
['Number of siblings', 'Something else...'], ['Age', 'Favourite colour'],
['Done']] ['Number of siblings', 'Something else...'],
['Done'],
]
markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True) markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
@ -46,12 +55,16 @@ def facts_to_str(user_data):
def start(update, context): def start(update, context):
reply_text = "Hi! My name is Doctor Botter." reply_text = "Hi! My name is Doctor Botter."
if context.user_data: if context.user_data:
reply_text += " You already told me your {}. Why don't you tell me something more " \ reply_text += (
"about yourself? Or change anything I " \ " You already told me your {}. Why don't you tell me something more "
"already know.".format(", ".join(context.user_data.keys())) "about yourself? Or change anything I "
"already know.".format(", ".join(context.user_data.keys()))
)
else: else:
reply_text += " I will hold a more complex conversation with you. Why don't you tell me " \ reply_text += (
"something about yourself?" " I will hold a more complex conversation with you. Why don't you tell me "
"something about yourself?"
)
update.message.reply_text(reply_text, reply_markup=markup) update.message.reply_text(reply_text, reply_markup=markup)
return CHOOSING return CHOOSING
@ -61,8 +74,9 @@ def regular_choice(update, context):
text = update.message.text.lower() text = update.message.text.lower()
context.user_data['choice'] = text context.user_data['choice'] = text
if context.user_data.get(text): if context.user_data.get(text):
reply_text = 'Your {}, I already know the following ' \ reply_text = 'Your {}, I already know the following ' 'about that: {}'.format(
'about that: {}'.format(text, context.user_data[text]) text, context.user_data[text]
)
else: else:
reply_text = 'Your {}? Yes, I would love to hear about that!'.format(text) reply_text = 'Your {}? Yes, I would love to hear about that!'.format(text)
update.message.reply_text(reply_text) update.message.reply_text(reply_text)
@ -71,8 +85,9 @@ def regular_choice(update, context):
def custom_choice(update, context): def custom_choice(update, context):
update.message.reply_text('Alright, please send me the category first, ' update.message.reply_text(
'for example "Most impressive skill"') 'Alright, please send me the category first, ' 'for example "Most impressive skill"'
)
return TYPING_CHOICE return TYPING_CHOICE
@ -83,27 +98,32 @@ def received_information(update, context):
context.user_data[category] = text.lower() context.user_data[category] = text.lower()
del context.user_data['choice'] del context.user_data['choice']
update.message.reply_text("Neat! Just so you know, this is what you already told me:" update.message.reply_text(
"{}" "Neat! Just so you know, this is what you already told me:"
"You can tell me more, or change your opinion on " "{}"
"something.".format(facts_to_str(context.user_data)), "You can tell me more, or change your opinion on "
reply_markup=markup) "something.".format(facts_to_str(context.user_data)),
reply_markup=markup,
)
return CHOOSING return CHOOSING
def show_data(update, context): def show_data(update, context):
update.message.reply_text("This is what you already told me:" update.message.reply_text(
"{}".format(facts_to_str(context.user_data))) "This is what you already told me:" "{}".format(facts_to_str(context.user_data))
)
def done(update, context): def done(update, context):
if 'choice' in context.user_data: if 'choice' in context.user_data:
del context.user_data['choice'] del context.user_data['choice']
update.message.reply_text("I learned these facts about you:" update.message.reply_text(
"{}" "I learned these facts about you:"
"Until next time!".format(facts_to_str(context.user_data))) "{}"
"Until next time!".format(facts_to_str(context.user_data))
)
return ConversationHandler.END return ConversationHandler.END
@ -118,26 +138,28 @@ def main():
# Add conversation handler with the states CHOOSING, TYPING_CHOICE and TYPING_REPLY # Add conversation handler with the states CHOOSING, TYPING_CHOICE and TYPING_REPLY
conv_handler = ConversationHandler( conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)], entry_points=[CommandHandler('start', start)],
states={ states={
CHOOSING: [MessageHandler(Filters.regex('^(Age|Favourite colour|Number of siblings)$'), CHOOSING: [
regular_choice), MessageHandler(
MessageHandler(Filters.regex('^Something else...$'), Filters.regex('^(Age|Favourite colour|Number of siblings)$'), regular_choice
custom_choice), ),
], MessageHandler(Filters.regex('^Something else...$'), custom_choice),
],
TYPING_CHOICE: [ TYPING_CHOICE: [
MessageHandler(Filters.text & ~(Filters.command | Filters.regex('^Done$')), MessageHandler(
regular_choice)], Filters.text & ~(Filters.command | Filters.regex('^Done$')), regular_choice
)
],
TYPING_REPLY: [ TYPING_REPLY: [
MessageHandler(Filters.text & ~(Filters.command | Filters.regex('^Done$')), MessageHandler(
received_information)], Filters.text & ~(Filters.command | Filters.regex('^Done$')),
received_information,
)
],
}, },
fallbacks=[MessageHandler(Filters.regex('^Done$'), done)], fallbacks=[MessageHandler(Filters.regex('^Done$'), done)],
name="my_conversation", name="my_conversation",
persistent=True persistent=True,
) )
dp.add_handler(conv_handler) dp.add_handler(conv_handler)

View file

@ -9,30 +9,56 @@ one the user sends the bot
""" """
import logging import logging
from telegram import (Poll, ParseMode, KeyboardButton, KeyboardButtonPollType, from telegram import (
ReplyKeyboardMarkup, ReplyKeyboardRemove) Poll,
from telegram.ext import (Updater, CommandHandler, PollAnswerHandler, PollHandler, MessageHandler, ParseMode,
Filters) KeyboardButton,
KeyboardButtonPollType,
ReplyKeyboardMarkup,
ReplyKeyboardRemove,
)
from telegram.ext import (
Updater,
CommandHandler,
PollAnswerHandler,
PollHandler,
MessageHandler,
Filters,
)
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(
level=logging.INFO) format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def start(update, context): def start(update, context):
"""Inform user about what this bot can do""" """Inform user about what this bot can do"""
update.message.reply_text('Please select /poll to get a Poll, /quiz to get a Quiz or /preview' update.message.reply_text(
' to generate a preview for your poll') 'Please select /poll to get a Poll, /quiz to get a Quiz or /preview'
' to generate a preview for your poll'
)
def poll(update, context): def poll(update, context):
"""Sends a predefined poll""" """Sends a predefined poll"""
questions = ["Good", "Really good", "Fantastic", "Great"] questions = ["Good", "Really good", "Fantastic", "Great"]
message = context.bot.send_poll(update.effective_chat.id, "How are you?", questions, message = context.bot.send_poll(
is_anonymous=False, allows_multiple_answers=True) update.effective_chat.id,
"How are you?",
questions,
is_anonymous=False,
allows_multiple_answers=True,
)
# Save some info about the poll the bot_data for later use in receive_poll_answer # Save some info about the poll the bot_data for later use in receive_poll_answer
payload = {message.poll.id: {"questions": questions, "message_id": message.message_id, payload = {
"chat_id": update.effective_chat.id, "answers": 0}} message.poll.id: {
"questions": questions,
"message_id": message.message_id,
"chat_id": update.effective_chat.id,
"answers": 0,
}
}
context.bot_data.update(payload) context.bot_data.update(payload)
@ -52,25 +78,29 @@ def receive_poll_answer(update, context):
answer_string += questions[question_id] + " and " answer_string += questions[question_id] + " and "
else: else:
answer_string += questions[question_id] answer_string += questions[question_id]
context.bot.send_message(context.bot_data[poll_id]["chat_id"], context.bot.send_message(
"{} feels {}!".format(update.effective_user.mention_html(), context.bot_data[poll_id]["chat_id"],
answer_string), "{} feels {}!".format(update.effective_user.mention_html(), answer_string),
parse_mode=ParseMode.HTML) parse_mode=ParseMode.HTML,
)
context.bot_data[poll_id]["answers"] += 1 context.bot_data[poll_id]["answers"] += 1
# Close poll after three participants voted # Close poll after three participants voted
if context.bot_data[poll_id]["answers"] == 3: if context.bot_data[poll_id]["answers"] == 3:
context.bot.stop_poll(context.bot_data[poll_id]["chat_id"], context.bot.stop_poll(
context.bot_data[poll_id]["message_id"]) context.bot_data[poll_id]["chat_id"], context.bot_data[poll_id]["message_id"]
)
def quiz(update, context): def quiz(update, context):
"""Send a predefined poll""" """Send a predefined poll"""
questions = ["1", "2", "4", "20"] questions = ["1", "2", "4", "20"]
message = update.effective_message.reply_poll("How many eggs do you need for a cake?", message = update.effective_message.reply_poll(
questions, type=Poll.QUIZ, correct_option_id=2) "How many eggs do you need for a cake?", questions, type=Poll.QUIZ, correct_option_id=2
)
# Save some info about the poll the bot_data for later use in receive_quiz_answer # Save some info about the poll the bot_data for later use in receive_quiz_answer
payload = {message.poll.id: {"chat_id": update.effective_chat.id, payload = {
"message_id": message.message_id}} message.poll.id: {"chat_id": update.effective_chat.id, "message_id": message.message_id}
}
context.bot_data.update(payload) context.bot_data.update(payload)
@ -94,9 +124,9 @@ def preview(update, context):
button = [[KeyboardButton("Press me!", request_poll=KeyboardButtonPollType())]] button = [[KeyboardButton("Press me!", request_poll=KeyboardButtonPollType())]]
message = "Press the button to let the bot generate a preview for your poll" message = "Press the button to let the bot generate a preview for your poll"
# using one_time_keyboard to hide the keyboard # using one_time_keyboard to hide the keyboard
update.effective_message.reply_text(message, update.effective_message.reply_text(
reply_markup=ReplyKeyboardMarkup(button, message, reply_markup=ReplyKeyboardMarkup(button, one_time_keyboard=True)
one_time_keyboard=True)) )
def receive_poll(update, context): def receive_poll(update, context):
@ -109,14 +139,13 @@ def receive_poll(update, context):
options=[o.text for o in actual_poll.options], options=[o.text for o in actual_poll.options],
# with is_closed true, the poll/quiz is immediately closed # with is_closed true, the poll/quiz is immediately closed
is_closed=True, is_closed=True,
reply_markup=ReplyKeyboardRemove() reply_markup=ReplyKeyboardRemove(),
) )
def help_handler(update, context): def help_handler(update, context):
"""Display a help message""" """Display a help message"""
update.message.reply_text("Use /quiz, /poll or /preview to test this " update.message.reply_text("Use /quiz, /poll or /preview to test this " "bot.")
"bot.")
def main(): def main():

View file

@ -23,8 +23,9 @@ import logging
from telegram.ext import Updater, CommandHandler from telegram.ext import Updater, CommandHandler
# Enable logging # Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(
level=logging.INFO) format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -90,10 +91,9 @@ def main():
# on different commands - answer in Telegram # on different commands - answer in Telegram
dp.add_handler(CommandHandler("start", start)) dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", start)) dp.add_handler(CommandHandler("help", start))
dp.add_handler(CommandHandler("set", set_timer, dp.add_handler(
pass_args=True, CommandHandler("set", set_timer, pass_args=True, pass_job_queue=True, pass_chat_data=True)
pass_job_queue=True, )
pass_chat_data=True))
dp.add_handler(CommandHandler("unset", unset, pass_chat_data=True)) dp.add_handler(CommandHandler("unset", unset, pass_chat_data=True))
# Start the Bot # Start the Bot

6
pyproject.toml Normal file
View file

@ -0,0 +1,6 @@
[tool.black]
line-length = 99
target-version = ['py36']
include = '(telegram|examples|tests)/.*\.py$'
exclude = 'telegram/vendor'
skip-string-normalization = true

View file

@ -15,13 +15,9 @@ upload-dir = docs/build/html
[flake8] [flake8]
max-line-length = 99 max-line-length = 99
ignore = W503, W605 ignore = W503, W605
extend-ignore = E203
exclude = setup.py, docs/source/conf.py exclude = setup.py, docs/source/conf.py
[yapf]
based_on_style = google
split_before_logical_operator = True
column_limit = 99
[tool:pytest] [tool:pytest]
testpaths = tests testpaths = tests
addopts = --no-success-flaky-report -rsxX addopts = --no-success-flaky-report -rsxX

View file

@ -102,63 +102,170 @@ from .payment.shippingquery import ShippingQuery
from .webhookinfo import WebhookInfo from .webhookinfo import WebhookInfo
from .games.gamehighscore import GameHighScore from .games.gamehighscore import GameHighScore
from .update import Update from .update import Update
from .files.inputmedia import (InputMedia, InputMediaVideo, InputMediaPhoto, InputMediaAnimation, from .files.inputmedia import (
InputMediaAudio, InputMediaDocument) InputMedia,
from .constants import (MAX_MESSAGE_LENGTH, MAX_CAPTION_LENGTH, SUPPORTED_WEBHOOK_PORTS, InputMediaVideo,
MAX_FILESIZE_DOWNLOAD, MAX_FILESIZE_UPLOAD, InputMediaPhoto,
MAX_MESSAGES_PER_SECOND_PER_CHAT, MAX_MESSAGES_PER_SECOND, InputMediaAnimation,
MAX_MESSAGES_PER_MINUTE_PER_GROUP) InputMediaAudio,
from .passport.passportelementerrors import (PassportElementError, InputMediaDocument,
PassportElementErrorDataField, )
PassportElementErrorFile, from .constants import (
PassportElementErrorFiles, MAX_MESSAGE_LENGTH,
PassportElementErrorFrontSide, MAX_CAPTION_LENGTH,
PassportElementErrorReverseSide, SUPPORTED_WEBHOOK_PORTS,
PassportElementErrorSelfie, MAX_FILESIZE_DOWNLOAD,
PassportElementErrorTranslationFile, MAX_FILESIZE_UPLOAD,
PassportElementErrorTranslationFiles, MAX_MESSAGES_PER_SECOND_PER_CHAT,
PassportElementErrorUnspecified) MAX_MESSAGES_PER_SECOND,
from .passport.credentials import (Credentials, MAX_MESSAGES_PER_MINUTE_PER_GROUP,
DataCredentials, )
SecureData, from .passport.passportelementerrors import (
FileCredentials, PassportElementError,
TelegramDecryptionError) PassportElementErrorDataField,
PassportElementErrorFile,
PassportElementErrorFiles,
PassportElementErrorFrontSide,
PassportElementErrorReverseSide,
PassportElementErrorSelfie,
PassportElementErrorTranslationFile,
PassportElementErrorTranslationFiles,
PassportElementErrorUnspecified,
)
from .passport.credentials import (
Credentials,
DataCredentials,
SecureData,
FileCredentials,
TelegramDecryptionError,
)
from .bot import Bot from .bot import Bot
from .version import __version__ # noqa: F401 from .version import __version__ # noqa: F401
__author__ = 'devs@python-telegram-bot.org' __author__ = 'devs@python-telegram-bot.org'
__all__ = [ __all__ = [
'Audio', 'Bot', 'Chat', 'ChatMember', 'ChatPermissions', 'ChatAction', 'ChosenInlineResult', 'Audio',
'CallbackQuery', 'Contact', 'Document', 'File', 'ForceReply', 'InlineKeyboardButton', 'Bot',
'InlineKeyboardMarkup', 'InlineQuery', 'InlineQueryResult', 'InlineQueryResult', 'Chat',
'InlineQueryResultArticle', 'InlineQueryResultAudio', 'InlineQueryResultCachedAudio', 'ChatMember',
'InlineQueryResultCachedDocument', 'InlineQueryResultCachedGif', 'ChatPermissions',
'InlineQueryResultCachedMpeg4Gif', 'InlineQueryResultCachedPhoto', 'ChatAction',
'InlineQueryResultCachedSticker', 'InlineQueryResultCachedVideo', 'ChosenInlineResult',
'InlineQueryResultCachedVoice', 'InlineQueryResultContact', 'InlineQueryResultDocument', 'CallbackQuery',
'InlineQueryResultGif', 'InlineQueryResultLocation', 'InlineQueryResultMpeg4Gif', 'Contact',
'InlineQueryResultPhoto', 'InlineQueryResultVenue', 'InlineQueryResultVideo', 'Document',
'InlineQueryResultVoice', 'InlineQueryResultGame', 'InputContactMessageContent', 'InputFile', 'File',
'InputLocationMessageContent', 'InputMessageContent', 'InputTextMessageContent', 'ForceReply',
'InputVenueMessageContent', 'Location', 'EncryptedCredentials', 'InlineKeyboardButton',
'PassportFile', 'EncryptedPassportElement', 'PassportData', 'Message', 'MessageEntity', 'InlineKeyboardMarkup',
'ParseMode', 'PhotoSize', 'ReplyKeyboardRemove', 'ReplyKeyboardMarkup', 'ReplyMarkup', 'InlineQuery',
'Sticker', 'TelegramError', 'TelegramObject', 'Update', 'User', 'UserProfilePhotos', 'Venue', 'InlineQueryResult',
'Video', 'Voice', 'MAX_MESSAGE_LENGTH', 'MAX_CAPTION_LENGTH', 'SUPPORTED_WEBHOOK_PORTS', 'InlineQueryResult',
'MAX_FILESIZE_DOWNLOAD', 'MAX_FILESIZE_UPLOAD', 'MAX_MESSAGES_PER_SECOND_PER_CHAT', 'InlineQueryResultArticle',
'MAX_MESSAGES_PER_SECOND', 'MAX_MESSAGES_PER_MINUTE_PER_GROUP', 'WebhookInfo', 'Animation', 'InlineQueryResultAudio',
'Game', 'GameHighScore', 'VideoNote', 'LabeledPrice', 'SuccessfulPayment', 'ShippingOption', 'InlineQueryResultCachedAudio',
'ShippingAddress', 'PreCheckoutQuery', 'OrderInfo', 'Invoice', 'ShippingQuery', 'ChatPhoto', 'InlineQueryResultCachedDocument',
'StickerSet', 'MaskPosition', 'CallbackGame', 'InputMedia', 'InputMediaPhoto', 'InlineQueryResultCachedGif',
'InputMediaVideo', 'PassportElementError', 'PassportElementErrorFile', 'InlineQueryResultCachedMpeg4Gif',
'PassportElementErrorReverseSide', 'PassportElementErrorFrontSide', 'InlineQueryResultCachedPhoto',
'PassportElementErrorFiles', 'PassportElementErrorDataField', 'PassportElementErrorFile', 'InlineQueryResultCachedSticker',
'Credentials', 'DataCredentials', 'SecureData', 'FileCredentials', 'IdDocumentData', 'InlineQueryResultCachedVideo',
'PersonalDetails', 'ResidentialAddress', 'InputMediaVideo', 'InputMediaAnimation', 'InlineQueryResultCachedVoice',
'InputMediaAudio', 'InputMediaDocument', 'TelegramDecryptionError', 'InlineQueryResultContact',
'PassportElementErrorSelfie', 'PassportElementErrorTranslationFile', 'InlineQueryResultDocument',
'PassportElementErrorTranslationFiles', 'PassportElementErrorUnspecified', 'Poll', 'InlineQueryResultGif',
'PollOption', 'PollAnswer', 'LoginUrl', 'KeyboardButton', 'KeyboardButtonPollType', 'Dice', 'InlineQueryResultLocation',
'BotCommand' 'InlineQueryResultMpeg4Gif',
'InlineQueryResultPhoto',
'InlineQueryResultVenue',
'InlineQueryResultVideo',
'InlineQueryResultVoice',
'InlineQueryResultGame',
'InputContactMessageContent',
'InputFile',
'InputLocationMessageContent',
'InputMessageContent',
'InputTextMessageContent',
'InputVenueMessageContent',
'Location',
'EncryptedCredentials',
'PassportFile',
'EncryptedPassportElement',
'PassportData',
'Message',
'MessageEntity',
'ParseMode',
'PhotoSize',
'ReplyKeyboardRemove',
'ReplyKeyboardMarkup',
'ReplyMarkup',
'Sticker',
'TelegramError',
'TelegramObject',
'Update',
'User',
'UserProfilePhotos',
'Venue',
'Video',
'Voice',
'MAX_MESSAGE_LENGTH',
'MAX_CAPTION_LENGTH',
'SUPPORTED_WEBHOOK_PORTS',
'MAX_FILESIZE_DOWNLOAD',
'MAX_FILESIZE_UPLOAD',
'MAX_MESSAGES_PER_SECOND_PER_CHAT',
'MAX_MESSAGES_PER_SECOND',
'MAX_MESSAGES_PER_MINUTE_PER_GROUP',
'WebhookInfo',
'Animation',
'Game',
'GameHighScore',
'VideoNote',
'LabeledPrice',
'SuccessfulPayment',
'ShippingOption',
'ShippingAddress',
'PreCheckoutQuery',
'OrderInfo',
'Invoice',
'ShippingQuery',
'ChatPhoto',
'StickerSet',
'MaskPosition',
'CallbackGame',
'InputMedia',
'InputMediaPhoto',
'InputMediaVideo',
'PassportElementError',
'PassportElementErrorFile',
'PassportElementErrorReverseSide',
'PassportElementErrorFrontSide',
'PassportElementErrorFiles',
'PassportElementErrorDataField',
'PassportElementErrorFile',
'Credentials',
'DataCredentials',
'SecureData',
'FileCredentials',
'IdDocumentData',
'PersonalDetails',
'ResidentialAddress',
'InputMediaVideo',
'InputMediaAnimation',
'InputMediaAudio',
'InputMediaDocument',
'TelegramDecryptionError',
'PassportElementErrorSelfie',
'PassportElementErrorTranslationFile',
'PassportElementErrorTranslationFiles',
'PassportElementErrorUnspecified',
'Poll',
'PollOption',
'PollAnswer',
'LoginUrl',
'KeyboardButton',
'KeyboardButtonPollType',
'Dice',
'BotCommand',
] ]

View file

@ -28,8 +28,9 @@ from . import __version__ as telegram_ver
def _git_revision() -> Optional[str]: def _git_revision() -> Optional[str]:
try: try:
output = subprocess.check_output(["git", "describe", "--long", "--tags"], output = subprocess.check_output(
stderr=subprocess.STDOUT) ["git", "describe", "--long", "--tags"], stderr=subprocess.STDOUT
)
except (subprocess.SubprocessError, OSError): except (subprocess.SubprocessError, OSError):
return None return None
return output.decode().strip() return output.decode().strip()
@ -37,8 +38,10 @@ def _git_revision() -> Optional[str]:
def print_ver_info() -> None: def print_ver_info() -> None:
git_revision = _git_revision() git_revision = _git_revision()
print('python-telegram-bot {}'.format(telegram_ver) + (' ({})'.format(git_revision) print(
if git_revision else '')) 'python-telegram-bot {}'.format(telegram_ver)
+ (' ({})'.format(git_revision) if git_revision else '')
)
print('certifi {}'.format(certifi.__version__)) # type: ignore[attr-defined] print('certifi {}'.format(certifi.__version__)) # type: ignore[attr-defined]
print('Python {}'.format(sys.version.replace('\n', ' '))) print('Python {}'.format(sys.version.replace('\n', ' ')))

View file

@ -66,9 +66,7 @@ class TelegramObject:
return cls(bot=bot, **data) # type: ignore[call-arg] return cls(bot=bot, **data) # type: ignore[call-arg]
@classmethod @classmethod
def de_list(cls: Type[TO], def de_list(cls: Type[TO], data: Optional[List[JSONDict]], bot: 'Bot') -> List[Optional[TO]]:
data: Optional[List[JSONDict]],
bot: 'Bot') -> List[Optional[TO]]:
if not data: if not data:
return [] return []
@ -104,11 +102,15 @@ class TelegramObject:
def __eq__(self, other: object) -> bool: def __eq__(self, other: object) -> bool:
if isinstance(other, self.__class__): if isinstance(other, self.__class__):
if self._id_attrs == (): if self._id_attrs == ():
warnings.warn("Objects of type {} can not be meaningfully tested for " warnings.warn(
"equivalence.".format(self.__class__.__name__)) "Objects of type {} can not be meaningfully tested for "
"equivalence.".format(self.__class__.__name__)
)
if other._id_attrs == (): if other._id_attrs == ():
warnings.warn("Objects of type {} can not be meaningfully tested for " warnings.warn(
"equivalence.".format(other.__class__.__name__)) "Objects of type {} can not be meaningfully tested for "
"equivalence.".format(other.__class__.__name__)
)
return self._id_attrs == other._id_attrs return self._id_attrs == other._id_attrs
return super().__eq__(other) # pylint: disable=no-member return super().__eq__(other) # pylint: disable=no-member

File diff suppressed because it is too large Load diff

View file

@ -38,6 +38,7 @@ class BotCommand(TelegramObject):
English letters, digits and underscores. English letters, digits and underscores.
description (:obj:`str`): Description of the command, 3-256 characters. description (:obj:`str`): Description of the command, 3-256 characters.
""" """
def __init__(self, command: str, description: str, **kwargs: Any): def __init__(self, command: str, description: str, **kwargs: Any):
self.command = command self.command = command
self.description = description self.description = description

View file

@ -78,16 +78,18 @@ class CallbackQuery(TelegramObject):
""" """
def __init__(self, def __init__(
id: str, self,
from_user: User, id: str,
chat_instance: str, from_user: User,
message: Message = None, chat_instance: str,
data: str = None, message: Message = None,
inline_message_id: str = None, data: str = None,
game_short_name: str = None, inline_message_id: str = None,
bot: 'Bot' = None, game_short_name: str = None,
**kwargs: Any): bot: 'Bot' = None,
**kwargs: Any,
):
# Required # Required
self.id = id self.id = id
self.from_user = from_user self.from_user = from_user
@ -143,14 +145,21 @@ class CallbackQuery(TelegramObject):
""" """
if self.inline_message_id: if self.inline_message_id:
return self.bot.edit_message_text(text, inline_message_id=self.inline_message_id, return self.bot.edit_message_text(
*args, **kwargs) text, inline_message_id=self.inline_message_id, *args, **kwargs
)
else: else:
return self.bot.edit_message_text(text, chat_id=self.message.chat_id, return self.bot.edit_message_text(
message_id=self.message.message_id, *args, **kwargs) text,
chat_id=self.message.chat_id,
message_id=self.message.message_id,
*args,
**kwargs,
)
def edit_message_caption(self, caption: str, *args: Any, def edit_message_caption(
**kwargs: Any) -> Union[Message, bool]: self, caption: str, *args: Any, **kwargs: Any
) -> Union[Message, bool]:
"""Shortcut for either:: """Shortcut for either::
bot.edit_message_caption(caption=caption, bot.edit_message_caption(caption=caption,
@ -170,16 +179,21 @@ class CallbackQuery(TelegramObject):
""" """
if self.inline_message_id: if self.inline_message_id:
return self.bot.edit_message_caption(caption=caption, return self.bot.edit_message_caption(
inline_message_id=self.inline_message_id, caption=caption, inline_message_id=self.inline_message_id, *args, **kwargs
*args, **kwargs) )
else: else:
return self.bot.edit_message_caption(caption=caption, chat_id=self.message.chat_id, return self.bot.edit_message_caption(
message_id=self.message.message_id, caption=caption,
*args, **kwargs) chat_id=self.message.chat_id,
message_id=self.message.message_id,
*args,
**kwargs,
)
def edit_message_reply_markup(self, reply_markup: 'InlineKeyboardMarkup', *args: Any, def edit_message_reply_markup(
**kwargs: Any) -> Union[Message, bool]: self, reply_markup: 'InlineKeyboardMarkup', *args: Any, **kwargs: Any
) -> Union[Message, bool]:
"""Shortcut for either:: """Shortcut for either::
bot.edit_message_reply_markup(chat_id=update.callback_query.message.chat_id, bot.edit_message_reply_markup(chat_id=update.callback_query.message.chat_id,
@ -199,14 +213,20 @@ class CallbackQuery(TelegramObject):
""" """
if self.inline_message_id: if self.inline_message_id:
return self.bot.edit_message_reply_markup(reply_markup=reply_markup, return self.bot.edit_message_reply_markup(
inline_message_id=self.inline_message_id, reply_markup=reply_markup,
*args, **kwargs) inline_message_id=self.inline_message_id,
*args,
**kwargs,
)
else: else:
return self.bot.edit_message_reply_markup(reply_markup=reply_markup, return self.bot.edit_message_reply_markup(
chat_id=self.message.chat_id, reply_markup=reply_markup,
message_id=self.message.message_id, chat_id=self.message.chat_id,
*args, **kwargs) message_id=self.message.message_id,
*args,
**kwargs,
)
def edit_message_media(self, *args: Any, **kwargs: Any) -> Union[Message, bool]: def edit_message_media(self, *args: Any, **kwargs: Any) -> Union[Message, bool]:
"""Shortcut for either:: """Shortcut for either::
@ -228,12 +248,13 @@ class CallbackQuery(TelegramObject):
""" """
if self.inline_message_id: if self.inline_message_id:
return self.bot.edit_message_media(inline_message_id=self.inline_message_id, return self.bot.edit_message_media(
*args, **kwargs) inline_message_id=self.inline_message_id, *args, **kwargs
)
else: else:
return self.bot.edit_message_media(chat_id=self.message.chat_id, return self.bot.edit_message_media(
message_id=self.message.message_id, chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs
*args, **kwargs) )
def edit_message_live_location(self, *args: Any, **kwargs: Any) -> Union[Message, bool]: def edit_message_live_location(self, *args: Any, **kwargs: Any) -> Union[Message, bool]:
"""Shortcut for either:: """Shortcut for either::
@ -257,12 +278,13 @@ class CallbackQuery(TelegramObject):
""" """
if self.inline_message_id: if self.inline_message_id:
return self.bot.edit_message_live_location(inline_message_id=self.inline_message_id, return self.bot.edit_message_live_location(
*args, **kwargs) inline_message_id=self.inline_message_id, *args, **kwargs
)
else: else:
return self.bot.edit_message_live_location(chat_id=self.message.chat_id, return self.bot.edit_message_live_location(
message_id=self.message.message_id, chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs
*args, **kwargs) )
def stop_message_live_location(self, *args: Any, **kwargs: Any) -> Union[Message, bool]: def stop_message_live_location(self, *args: Any, **kwargs: Any) -> Union[Message, bool]:
"""Shortcut for either:: """Shortcut for either::
@ -286,12 +308,13 @@ class CallbackQuery(TelegramObject):
""" """
if self.inline_message_id: if self.inline_message_id:
return self.bot.stop_message_live_location(inline_message_id=self.inline_message_id, return self.bot.stop_message_live_location(
*args, **kwargs) inline_message_id=self.inline_message_id, *args, **kwargs
)
else: else:
return self.bot.stop_message_live_location(chat_id=self.message.chat_id, return self.bot.stop_message_live_location(
message_id=self.message.message_id, chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs
*args, **kwargs) )
def set_game_score(self, *args: Any, **kwargs: Any) -> Union[Message, bool]: def set_game_score(self, *args: Any, **kwargs: Any) -> Union[Message, bool]:
"""Shortcut for either:: """Shortcut for either::
@ -313,12 +336,13 @@ class CallbackQuery(TelegramObject):
""" """
if self.inline_message_id: if self.inline_message_id:
return self.bot.set_game_score(inline_message_id=self.inline_message_id, return self.bot.set_game_score(
*args, **kwargs) inline_message_id=self.inline_message_id, *args, **kwargs
)
else: else:
return self.bot.set_game_score(chat_id=self.message.chat_id, return self.bot.set_game_score(
message_id=self.message.message_id, chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs
*args, **kwargs) )
def get_game_high_scores(self, *args: Any, **kwargs: Any) -> List['GameHighScore']: def get_game_high_scores(self, *args: Any, **kwargs: Any) -> List['GameHighScore']:
"""Shortcut for either:: """Shortcut for either::
@ -339,9 +363,10 @@ class CallbackQuery(TelegramObject):
""" """
if self.inline_message_id: if self.inline_message_id:
return self.bot.get_game_high_scores(inline_message_id=self.inline_message_id, return self.bot.get_game_high_scores(
*args, **kwargs) inline_message_id=self.inline_message_id, *args, **kwargs
)
else: else:
return self.bot.get_game_high_scores(chat_id=self.message.chat_id, return self.bot.get_game_high_scores(
message_id=self.message.message_id, chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs
*args, **kwargs) )

View file

@ -24,6 +24,7 @@ from .chatpermissions import ChatPermissions
from telegram.utils.types import JSONDict from telegram.utils.types import JSONDict
from typing import Any, Optional, List, TYPE_CHECKING from typing import Any, Optional, List, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot, Message, ChatMember from telegram import Bot, Message, ChatMember
@ -100,23 +101,25 @@ class Chat(TelegramObject):
CHANNEL: str = 'channel' CHANNEL: str = 'channel'
""":obj:`str`: 'channel'""" """:obj:`str`: 'channel'"""
def __init__(self, def __init__(
id: int, self,
type: str, id: int,
title: str = None, type: str,
username: str = None, title: str = None,
first_name: str = None, username: str = None,
last_name: str = None, first_name: str = None,
bot: 'Bot' = None, last_name: str = None,
photo: ChatPhoto = None, bot: 'Bot' = None,
description: str = None, photo: ChatPhoto = None,
invite_link: str = None, description: str = None,
pinned_message: 'Message' = None, invite_link: str = None,
permissions: ChatPermissions = None, pinned_message: 'Message' = None,
sticker_set_name: str = None, permissions: ChatPermissions = None,
can_set_sticker_set: bool = None, sticker_set_name: str = None,
slow_mode_delay: int = None, can_set_sticker_set: bool = None,
**kwargs: Any): slow_mode_delay: int = None,
**kwargs: Any,
):
# Required # Required
self.id = int(id) self.id = int(id)
self.type = type self.type = type
@ -156,6 +159,7 @@ class Chat(TelegramObject):
data['photo'] = ChatPhoto.de_json(data.get('photo'), bot) data['photo'] = ChatPhoto.de_json(data.get('photo'), bot)
from telegram import Message from telegram import Message
data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot) data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot)
data['permissions'] = ChatPermissions.de_json(data.get('permissions'), bot) data['permissions'] = ChatPermissions.de_json(data.get('permissions'), bot)
@ -243,7 +247,7 @@ class Chat(TelegramObject):
Returns: Returns:
:obj:`bool`: If the action was sent successfully. :obj:`bool`: If the action was sent successfully.
""" """
return self.bot.set_chat_permissions(self.id, *args, **kwargs) return self.bot.set_chat_permissions(self.id, *args, **kwargs)
def set_administrator_custom_title(self, *args: Any, **kwargs: Any) -> bool: def set_administrator_custom_title(self, *args: Any, **kwargs: Any) -> bool:
@ -254,7 +258,7 @@ class Chat(TelegramObject):
Returns: Returns:
:obj:`bool`: If the action was sent successfully. :obj:`bool`: If the action was sent successfully.
""" """
return self.bot.set_chat_administrator_custom_title(self.id, *args, **kwargs) return self.bot.set_chat_administrator_custom_title(self.id, *args, **kwargs)
def send_message(self, *args: Any, **kwargs: Any) -> 'Message': def send_message(self, *args: Any, **kwargs: Any) -> 'Message':

View file

@ -24,6 +24,7 @@ from telegram.utils.helpers import to_timestamp, from_timestamp
from telegram.utils.types import JSONDict from telegram.utils.types import JSONDict
from typing import Any, Optional, TYPE_CHECKING from typing import Any, Optional, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot from telegram import Bot
@ -110,6 +111,7 @@ class ChatMember(TelegramObject):
may add web page previews to his messages. may add web page previews to his messages.
""" """
ADMINISTRATOR: str = 'administrator' ADMINISTRATOR: str = 'administrator'
""":obj:`str`: 'administrator'""" """:obj:`str`: 'administrator'"""
CREATOR: str = 'creator' CREATOR: str = 'creator'
@ -123,27 +125,29 @@ class ChatMember(TelegramObject):
RESTRICTED: str = 'restricted' RESTRICTED: str = 'restricted'
""":obj:`str`: 'restricted'""" """:obj:`str`: 'restricted'"""
def __init__(self, def __init__(
user: User, self,
status: str, user: User,
until_date: datetime.datetime = None, status: str,
can_be_edited: bool = None, until_date: datetime.datetime = None,
can_change_info: bool = None, can_be_edited: bool = None,
can_post_messages: bool = None, can_change_info: bool = None,
can_edit_messages: bool = None, can_post_messages: bool = None,
can_delete_messages: bool = None, can_edit_messages: bool = None,
can_invite_users: bool = None, can_delete_messages: bool = None,
can_restrict_members: bool = None, can_invite_users: bool = None,
can_pin_messages: bool = None, can_restrict_members: bool = None,
can_promote_members: bool = None, can_pin_messages: bool = None,
can_send_messages: bool = None, can_promote_members: bool = None,
can_send_media_messages: bool = None, can_send_messages: bool = None,
can_send_polls: bool = None, can_send_media_messages: bool = None,
can_send_other_messages: bool = None, can_send_polls: bool = None,
can_add_web_page_previews: bool = None, can_send_other_messages: bool = None,
is_member: bool = None, can_add_web_page_previews: bool = None,
custom_title: str = None, is_member: bool = None,
**kwargs: Any): custom_title: str = None,
**kwargs: Any,
):
# Required # Required
self.user = user self.user = user
self.status = status self.status = status

View file

@ -77,16 +77,18 @@ class ChatPermissions(TelegramObject):
""" """
def __init__(self, def __init__(
can_send_messages: bool = None, self,
can_send_media_messages: bool = None, can_send_messages: bool = None,
can_send_polls: bool = None, can_send_media_messages: bool = None,
can_send_other_messages: bool = None, can_send_polls: bool = None,
can_add_web_page_previews: bool = None, can_send_other_messages: bool = None,
can_change_info: bool = None, can_add_web_page_previews: bool = None,
can_invite_users: bool = None, can_change_info: bool = None,
can_pin_messages: bool = None, can_invite_users: bool = None,
**kwargs: Any): can_pin_messages: bool = None,
**kwargs: Any,
):
# Required # Required
self.can_send_messages = can_send_messages self.can_send_messages = can_send_messages
self.can_send_media_messages = can_send_media_messages self.can_send_media_messages = can_send_media_messages
@ -105,5 +107,5 @@ class ChatPermissions(TelegramObject):
self.can_add_web_page_previews, self.can_add_web_page_previews,
self.can_change_info, self.can_change_info,
self.can_invite_users, self.can_invite_users,
self.can_pin_messages self.can_pin_messages,
) )

View file

@ -22,6 +22,7 @@
from telegram import TelegramObject, User, Location from telegram import TelegramObject, User, Location
from telegram.utils.types import JSONDict from telegram.utils.types import JSONDict
from typing import Any, Optional, TYPE_CHECKING from typing import Any, Optional, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot from telegram import Bot
@ -61,13 +62,15 @@ class ChosenInlineResult(TelegramObject):
""" """
def __init__(self, def __init__(
result_id: str, self,
from_user: User, result_id: str,
query: str, from_user: User,
location: Location = None, query: str,
inline_message_id: str = None, location: Location = None,
**kwargs: Any): inline_message_id: str = None,
**kwargs: Any,
):
# Required # Required
self.result_id = result_id self.result_id = result_id
self.from_user = from_user self.from_user = from_user

View file

@ -48,9 +48,9 @@ MAX_CAPTION_LENGTH: int = 1024
# constants above this line are tested # constants above this line are tested
SUPPORTED_WEBHOOK_PORTS: List[int] = [443, 80, 88, 8443] SUPPORTED_WEBHOOK_PORTS: List[int] = [443, 80, 88, 8443]
MAX_FILESIZE_DOWNLOAD: int = int(20E6) # (20MB) MAX_FILESIZE_DOWNLOAD: int = int(20e6) # (20MB)
MAX_FILESIZE_UPLOAD: int = int(50E6) # (50MB) MAX_FILESIZE_UPLOAD: int = int(50e6) # (50MB)
MAX_PHOTOSIZE_UPLOAD: int = int(10E6) # (10MB) MAX_PHOTOSIZE_UPLOAD: int = int(10e6) # (10MB)
MAX_MESSAGES_PER_SECOND_PER_CHAT: int = 1 MAX_MESSAGES_PER_SECOND_PER_CHAT: int = 1
MAX_MESSAGES_PER_SECOND: int = 30 MAX_MESSAGES_PER_SECOND: int = 30
MAX_MESSAGES_PER_MINUTE_PER_GROUP: int = 20 MAX_MESSAGES_PER_MINUTE_PER_GROUP: int = 20

View file

@ -48,6 +48,7 @@ class Dice(TelegramObject):
value (:obj:`int`): Value of the dice. 1-6 for dice and darts, 1-5 for basketball. value (:obj:`int`): Value of the dice. 1-6 for dice and darts, 1-5 for basketball.
emoji (:obj:`str`): Emoji on which the dice throw animation is based. emoji (:obj:`str`): Emoji on which the dice throw animation is based.
""" """
def __init__(self, value: int, emoji: str, **kwargs: Any): def __init__(self, value: int, emoji: str, **kwargs: Any):
self.value = value self.value = value
self.emoji = emoji self.emoji = emoji

View file

@ -31,7 +31,7 @@ def _lstrip_str(in_s: str, lstr: str) -> str:
""" """
if in_s.startswith(lstr): if in_s.startswith(lstr):
res = in_s[len(lstr):] res = in_s[len(lstr) :]
else: else:
res = in_s res = in_s
return res return res
@ -116,10 +116,10 @@ class RetryAfter(TelegramError):
class Conflict(TelegramError): class Conflict(TelegramError):
""" """
Raised when a long poll or webhook conflicts with another one. Raised when a long poll or webhook conflicts with another one.
Args: Args:
msg (:obj:`str`): The message from telegrams server. msg (:obj:`str`): The message from telegrams server.
""" """

View file

@ -45,11 +45,38 @@ from .pollanswerhandler import PollAnswerHandler
from .pollhandler import PollHandler from .pollhandler import PollHandler
from .defaults import Defaults from .defaults import Defaults
__all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler', __all__ = (
'ChosenInlineResultHandler', 'CommandHandler', 'Handler', 'InlineQueryHandler', 'Dispatcher',
'MessageHandler', 'BaseFilter', 'MessageFilter', 'UpdateFilter', 'Filters', 'JobQueue',
'RegexHandler', 'StringCommandHandler', 'StringRegexHandler', 'TypeHandler', 'Job',
'ConversationHandler', 'PreCheckoutQueryHandler', 'ShippingQueryHandler', 'Updater',
'MessageQueue', 'DelayQueue', 'DispatcherHandlerStop', 'run_async', 'CallbackContext', 'CallbackQueryHandler',
'BasePersistence', 'PicklePersistence', 'DictPersistence', 'PrefixHandler', 'ChosenInlineResultHandler',
'PollAnswerHandler', 'PollHandler', 'Defaults') 'CommandHandler',
'Handler',
'InlineQueryHandler',
'MessageHandler',
'BaseFilter',
'MessageFilter',
'UpdateFilter',
'Filters',
'RegexHandler',
'StringCommandHandler',
'StringRegexHandler',
'TypeHandler',
'ConversationHandler',
'PreCheckoutQueryHandler',
'ShippingQueryHandler',
'MessageQueue',
'DelayQueue',
'DispatcherHandlerStop',
'run_async',
'CallbackContext',
'BasePersistence',
'PicklePersistence',
'DictPersistence',
'PrefixHandler',
'PollAnswerHandler',
'PollHandler',
'Defaults',
)

View file

@ -108,10 +108,12 @@ class BasePersistence(ABC):
instance.update_bot_data = update_bot_data_replace_bot instance.update_bot_data = update_bot_data_replace_bot
return instance return instance
def __init__(self, def __init__(
store_user_data: bool = True, self,
store_chat_data: bool = True, store_user_data: bool = True,
store_bot_data: bool = True): store_chat_data: bool = True,
store_bot_data: bool = True,
):
self.store_user_data = store_user_data self.store_user_data = store_user_data
self.store_chat_data = store_chat_data self.store_chat_data = store_chat_data
self.store_bot_data = store_bot_data self.store_bot_data = store_bot_data
@ -157,8 +159,11 @@ class BasePersistence(ABC):
return new_obj return new_obj
if hasattr(obj, '__slots__'): if hasattr(obj, '__slots__'):
for attr_name in new_obj.__slots__: for attr_name in new_obj.__slots__:
setattr(new_obj, attr_name, setattr(
cls.replace_bot(cls.replace_bot(getattr(new_obj, attr_name)))) new_obj,
attr_name,
cls.replace_bot(cls.replace_bot(getattr(new_obj, attr_name))),
)
return new_obj return new_obj
return obj return obj
@ -196,14 +201,17 @@ class BasePersistence(ABC):
return new_obj return new_obj
if hasattr(obj, '__slots__'): if hasattr(obj, '__slots__'):
for attr_name in obj.__slots__: for attr_name in obj.__slots__:
setattr(new_obj, attr_name, setattr(
self.insert_bot(self.insert_bot(getattr(new_obj, attr_name)))) new_obj,
attr_name,
self.insert_bot(self.insert_bot(getattr(new_obj, attr_name))),
)
return new_obj return new_obj
return obj return obj
@abstractmethod @abstractmethod
def get_user_data(self) -> DefaultDict[int, Dict[Any, Any]]: def get_user_data(self) -> DefaultDict[int, Dict[Any, Any]]:
""""Will be called by :class:`telegram.ext.Dispatcher` upon creation with a """ "Will be called by :class:`telegram.ext.Dispatcher` upon creation with a
persistence object. It should return the user_data if stored, or an empty persistence object. It should return the user_data if stored, or an empty
``defaultdict(dict)``. ``defaultdict(dict)``.
@ -213,7 +221,7 @@ class BasePersistence(ABC):
@abstractmethod @abstractmethod
def get_chat_data(self) -> DefaultDict[int, Dict[Any, Any]]: def get_chat_data(self) -> DefaultDict[int, Dict[Any, Any]]:
""""Will be called by :class:`telegram.ext.Dispatcher` upon creation with a """ "Will be called by :class:`telegram.ext.Dispatcher` upon creation with a
persistence object. It should return the chat_data if stored, or an empty persistence object. It should return the chat_data if stored, or an empty
``defaultdict(dict)``. ``defaultdict(dict)``.
@ -223,7 +231,7 @@ class BasePersistence(ABC):
@abstractmethod @abstractmethod
def get_bot_data(self) -> Dict[Any, Any]: def get_bot_data(self) -> Dict[Any, Any]:
""""Will be called by :class:`telegram.ext.Dispatcher` upon creation with a """ "Will be called by :class:`telegram.ext.Dispatcher` upon creation with a
persistence object. It should return the bot_data if stored, or an empty persistence object. It should return the bot_data if stored, or an empty
:obj:`dict`. :obj:`dict`.
@ -233,7 +241,7 @@ class BasePersistence(ABC):
@abstractmethod @abstractmethod
def get_conversations(self, name: str) -> ConversationDict: def get_conversations(self, name: str) -> ConversationDict:
""""Will be called by :class:`telegram.ext.Dispatcher` when a """ "Will be called by :class:`telegram.ext.Dispatcher` when a
:class:`telegram.ext.ConversationHandler` is added if :class:`telegram.ext.ConversationHandler` is added if
:attr:`telegram.ext.ConversationHandler.persistent` is :obj:`True`. :attr:`telegram.ext.ConversationHandler.persistent` is :obj:`True`.
It should return the conversations for the handler with `name` or an empty :obj:`dict` It should return the conversations for the handler with `name` or an empty :obj:`dict`
@ -246,9 +254,9 @@ class BasePersistence(ABC):
""" """
@abstractmethod @abstractmethod
def update_conversation(self, def update_conversation(
name: str, key: Tuple[int, ...], self, name: str, key: Tuple[int, ...], new_state: Optional[object]
new_state: Optional[object]) -> None: ) -> None:
"""Will be called when a :attr:`telegram.ext.ConversationHandler.update_state` """Will be called when a :attr:`telegram.ext.ConversationHandler.update_state`
is called. This allows the storage of the new state in the persistence. is called. This allows the storage of the new state in the persistence.

View file

@ -21,6 +21,7 @@ from queue import Queue
from typing import Dict, Any, Tuple, TYPE_CHECKING, Optional, Match, List, NoReturn, Union from typing import Dict, Any, Tuple, TYPE_CHECKING, Optional, Match, List, NoReturn, Union
from telegram import Update from telegram import Update
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot from telegram import Bot
from telegram.ext import Dispatcher, Job, JobQueue from telegram.ext import Dispatcher, Job, JobQueue
@ -91,8 +92,9 @@ class CallbackContext:
dispatcher (:class:`telegram.ext.Dispatcher`): dispatcher (:class:`telegram.ext.Dispatcher`):
""" """
if not dispatcher.use_context: if not dispatcher.use_context:
raise ValueError('CallbackContext should not be used with a non context aware ' raise ValueError(
'dispatcher!') 'CallbackContext should not be used with a non context aware ' 'dispatcher!'
)
self._dispatcher = dispatcher self._dispatcher = dispatcher
self._bot_data = dispatcher.bot_data self._bot_data = dispatcher.bot_data
self._chat_data: Optional[Dict[Any, Any]] = None self._chat_data: Optional[Dict[Any, Any]] = None
@ -115,8 +117,9 @@ class CallbackContext:
@bot_data.setter @bot_data.setter
def bot_data(self, value: Any) -> NoReturn: def bot_data(self, value: Any) -> NoReturn:
raise AttributeError("You can not assign a new value to bot_data, see " raise AttributeError(
"https://git.io/fjxKe") "You can not assign a new value to bot_data, see " "https://git.io/fjxKe"
)
@property @property
def chat_data(self) -> Optional[Dict]: def chat_data(self) -> Optional[Dict]:
@ -124,8 +127,9 @@ class CallbackContext:
@chat_data.setter @chat_data.setter
def chat_data(self, value: Any) -> NoReturn: def chat_data(self, value: Any) -> NoReturn:
raise AttributeError("You can not assign a new value to chat_data, see " raise AttributeError(
"https://git.io/fjxKe") "You can not assign a new value to chat_data, see " "https://git.io/fjxKe"
)
@property @property
def user_data(self) -> Optional[Dict]: def user_data(self) -> Optional[Dict]:
@ -133,16 +137,19 @@ class CallbackContext:
@user_data.setter @user_data.setter
def user_data(self, value: Any) -> NoReturn: def user_data(self, value: Any) -> NoReturn:
raise AttributeError("You can not assign a new value to user_data, see " raise AttributeError(
"https://git.io/fjxKe") "You can not assign a new value to user_data, see " "https://git.io/fjxKe"
)
@classmethod @classmethod
def from_error(cls, def from_error(
update: object, cls,
error: Exception, update: object,
dispatcher: 'Dispatcher', error: Exception,
async_args: Union[List, Tuple] = None, dispatcher: 'Dispatcher',
async_kwargs: Dict[str, Any] = None) -> 'CallbackContext': async_args: Union[List, Tuple] = None,
async_kwargs: Dict[str, Any] = None,
) -> 'CallbackContext':
self = cls.from_update(update, dispatcher) self = cls.from_update(update, dispatcher)
self.error = error self.error = error
self.async_args = async_args self.async_args = async_args

View file

@ -24,8 +24,18 @@ from telegram import Update
from .handler import Handler from .handler import Handler
from telegram.utils.types import HandlerArg from telegram.utils.types import HandlerArg
from typing import Callable, TYPE_CHECKING, Any, Optional, Union, TypeVar, Pattern, Match, Dict, \ from typing import (
cast Callable,
TYPE_CHECKING,
Any,
Optional,
Union,
TypeVar,
Pattern,
Match,
Dict,
cast,
)
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram.ext import CallbackContext, Dispatcher from telegram.ext import CallbackContext, Dispatcher
@ -110,23 +120,26 @@ class CallbackQueryHandler(Handler):
""" """
def __init__(self, def __init__(
callback: Callable[[HandlerArg, 'CallbackContext'], RT], self,
pass_update_queue: bool = False, callback: Callable[[HandlerArg, 'CallbackContext'], RT],
pass_job_queue: bool = False, pass_update_queue: bool = False,
pattern: Union[str, Pattern] = None, pass_job_queue: bool = False,
pass_groups: bool = False, pattern: Union[str, Pattern] = None,
pass_groupdict: bool = False, pass_groups: bool = False,
pass_user_data: bool = False, pass_groupdict: bool = False,
pass_chat_data: bool = False, pass_user_data: bool = False,
run_async: bool = False): pass_chat_data: bool = False,
run_async: bool = False,
):
super().__init__( super().__init__(
callback, callback,
pass_update_queue=pass_update_queue, pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue, pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data, pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data, pass_chat_data=pass_chat_data,
run_async=run_async) run_async=run_async,
)
if isinstance(pattern, str): if isinstance(pattern, str):
pattern = re.compile(pattern) pattern = re.compile(pattern)
@ -155,10 +168,12 @@ class CallbackQueryHandler(Handler):
return True return True
return None return None
def collect_optional_args(self, def collect_optional_args(
dispatcher: 'Dispatcher', self,
update: HandlerArg = None, dispatcher: 'Dispatcher',
check_result: Union[bool, Match] = None) -> Dict[str, Any]: update: HandlerArg = None,
check_result: Union[bool, Match] = None,
) -> Dict[str, Any]:
optional_args = super().collect_optional_args(dispatcher, update, check_result) optional_args = super().collect_optional_args(dispatcher, update, check_result)
if self.pattern: if self.pattern:
check_result = cast(Match, check_result) check_result = cast(Match, check_result)
@ -168,11 +183,13 @@ class CallbackQueryHandler(Handler):
optional_args['groupdict'] = check_result.groupdict() optional_args['groupdict'] = check_result.groupdict()
return optional_args return optional_args
def collect_additional_context(self, def collect_additional_context(
context: 'CallbackContext', self,
update: HandlerArg, context: 'CallbackContext',
dispatcher: 'Dispatcher', update: HandlerArg,
check_result: Union[bool, Match]) -> None: dispatcher: 'Dispatcher',
check_result: Union[bool, Match],
) -> None:
if self.pattern: if self.pattern:
check_result = cast(Match, check_result) check_result = cast(Match, check_result)
context.matches = [check_result] context.matches = [check_result]

View file

@ -23,6 +23,7 @@ from .handler import Handler
from telegram.utils.types import HandlerArg from telegram.utils.types import HandlerArg
from typing import Optional, Union, TypeVar from typing import Optional, Union, TypeVar
RT = TypeVar('RT') RT = TypeVar('RT')

View file

@ -28,6 +28,7 @@ from .handler import Handler
from telegram.utils.types import HandlerArg from telegram.utils.types import HandlerArg
from typing import Callable, TYPE_CHECKING, Any, Optional, Union, TypeVar, Dict, List, Tuple from typing import Callable, TYPE_CHECKING, Any, Optional, Union, TypeVar, Dict, List, Tuple
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram.ext import CallbackContext, Dispatcher from telegram.ext import CallbackContext, Dispatcher
@ -130,24 +131,27 @@ class CommandHandler(Handler):
ValueError - when command is too long or has illegal chars. ValueError - when command is too long or has illegal chars.
""" """
def __init__(self, def __init__(
command: Union[str, List[str]], self,
callback: Callable[[HandlerArg, 'CallbackContext'], RT], command: Union[str, List[str]],
filters: BaseFilter = None, callback: Callable[[HandlerArg, 'CallbackContext'], RT],
allow_edited: bool = None, filters: BaseFilter = None,
pass_args: bool = False, allow_edited: bool = None,
pass_update_queue: bool = False, pass_args: bool = False,
pass_job_queue: bool = False, pass_update_queue: bool = False,
pass_user_data: bool = False, pass_job_queue: bool = False,
pass_chat_data: bool = False, pass_user_data: bool = False,
run_async: bool = False): pass_chat_data: bool = False,
run_async: bool = False,
):
super().__init__( super().__init__(
callback, callback,
pass_update_queue=pass_update_queue, pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue, pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data, pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data, pass_chat_data=pass_chat_data,
run_async=run_async) run_async=run_async,
)
if isinstance(command, str): if isinstance(command, str):
self.command = [command.lower()] self.command = [command.lower()]
@ -163,17 +167,18 @@ class CommandHandler(Handler):
self.filters = Filters.update.messages self.filters = Filters.update.messages
if allow_edited is not None: if allow_edited is not None:
warnings.warn('allow_edited is deprecated. See https://git.io/fxJuV for more info', warnings.warn(
TelegramDeprecationWarning, 'allow_edited is deprecated. See https://git.io/fxJuV for more info',
stacklevel=2) TelegramDeprecationWarning,
stacklevel=2,
)
if not allow_edited: if not allow_edited:
self.filters &= ~Filters.update.edited_message self.filters &= ~Filters.update.edited_message
self.pass_args = pass_args self.pass_args = pass_args
def check_update( def check_update(
self, self, update: HandlerArg
update: HandlerArg) -> Optional[Union[bool, Tuple[List[str], ) -> Optional[Union[bool, Tuple[List[str], Optional[Union[bool, Dict]]]]]:
Optional[Union[bool, Dict]]]]]:
"""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:
@ -186,15 +191,22 @@ class CommandHandler(Handler):
if isinstance(update, Update) and update.effective_message: if isinstance(update, Update) and update.effective_message:
message = update.effective_message message = update.effective_message
if (message.entities and message.entities[0].type == MessageEntity.BOT_COMMAND if (
and message.entities[0].offset == 0 and message.text and message.bot): message.entities
command = message.text[1:message.entities[0].length] and message.entities[0].type == MessageEntity.BOT_COMMAND
and message.entities[0].offset == 0
and message.text
and message.bot
):
command = message.text[1 : message.entities[0].length]
args = message.text.split()[1:] args = message.text.split()[1:]
command_parts = command.split('@') command_parts = command.split('@')
command_parts.append(message.bot.username) command_parts.append(message.bot.username)
if not (command_parts[0].lower() in self.command if not (
and command_parts[1].lower() == message.bot.username.lower()): command_parts[0].lower() in self.command
and command_parts[1].lower() == message.bot.username.lower()
):
return None return None
filter_result = self.filters(update) filter_result = self.filters(update)
@ -205,22 +217,23 @@ class CommandHandler(Handler):
return None return None
def collect_optional_args( def collect_optional_args(
self, self,
dispatcher: 'Dispatcher', dispatcher: 'Dispatcher',
update: HandlerArg = None, update: HandlerArg = None,
check_result: Optional[Union[bool, Tuple[List[str], check_result: Optional[Union[bool, Tuple[List[str], Optional[bool]]]] = None,
Optional[bool]]]] = None) -> Dict[str, Any]: ) -> Dict[str, Any]:
optional_args = super().collect_optional_args(dispatcher, update) optional_args = super().collect_optional_args(dispatcher, update)
if self.pass_args and isinstance(check_result, tuple): if self.pass_args and isinstance(check_result, tuple):
optional_args['args'] = check_result[0] optional_args['args'] = check_result[0]
return optional_args return optional_args
def collect_additional_context( def collect_additional_context(
self, self,
context: 'CallbackContext', context: 'CallbackContext',
update: HandlerArg, update: HandlerArg,
dispatcher: 'Dispatcher', dispatcher: 'Dispatcher',
check_result: Optional[Union[bool, Tuple[List[str], Optional[bool]]]]) -> None: check_result: Optional[Union[bool, Tuple[List[str], Optional[bool]]]],
) -> None:
if isinstance(check_result, tuple): if isinstance(check_result, tuple):
context.args = check_result[0] context.args = check_result[0]
if isinstance(check_result[1], dict): if isinstance(check_result[1], dict):
@ -330,29 +343,36 @@ class PrefixHandler(CommandHandler):
""" """
def __init__(self, def __init__(
prefix: Union[str, List[str]], self,
command: Union[str, List[str]], prefix: Union[str, List[str]],
callback: Callable[[HandlerArg, 'CallbackContext'], RT], command: Union[str, List[str]],
filters: BaseFilter = None, callback: Callable[[HandlerArg, 'CallbackContext'], RT],
pass_args: bool = False, filters: BaseFilter = None,
pass_update_queue: bool = False, pass_args: bool = False,
pass_job_queue: bool = False, pass_update_queue: bool = False,
pass_user_data: bool = False, pass_job_queue: bool = False,
pass_chat_data: bool = False, pass_user_data: bool = False,
run_async: bool = False): pass_chat_data: bool = False,
run_async: bool = False,
):
self._prefix: List[str] = list() self._prefix: List[str] = list()
self._command: List[str] = list() self._command: List[str] = list()
self._commands: List[str] = list() self._commands: List[str] = list()
super().__init__( super().__init__(
'nocommand', callback, filters=filters, allow_edited=None, pass_args=pass_args, 'nocommand',
callback,
filters=filters,
allow_edited=None,
pass_args=pass_args,
pass_update_queue=pass_update_queue, pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue, pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data, pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data, pass_chat_data=pass_chat_data,
run_async=run_async) run_async=run_async,
)
self.prefix = prefix # type: ignore[assignment] self.prefix = prefix # type: ignore[assignment]
self.command = command # type: ignore[assignment] self.command = command # type: ignore[assignment]
@ -385,8 +405,9 @@ class PrefixHandler(CommandHandler):
def _build_commands(self) -> None: def _build_commands(self) -> None:
self._commands = [x.lower() + y.lower() for x in self.prefix for y in self.command] self._commands = [x.lower() + y.lower() for x in self.prefix for y in self.command]
def check_update(self, update: HandlerArg) -> Optional[Union[bool, Tuple[List[str], def check_update(
Optional[Union[bool, Dict]]]]]: self, update: HandlerArg
) -> Optional[Union[bool, Tuple[List[str], Optional[Union[bool, Dict]]]]]:
"""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:
@ -411,11 +432,12 @@ class PrefixHandler(CommandHandler):
return None return None
def collect_additional_context( def collect_additional_context(
self, self,
context: 'CallbackContext', context: 'CallbackContext',
update: HandlerArg, update: HandlerArg,
dispatcher: 'Dispatcher', dispatcher: 'Dispatcher',
check_result: Optional[Union[bool, Tuple[List[str], Optional[bool]]]]) -> None: check_result: Optional[Union[bool, Tuple[List[str], Optional[bool]]]],
) -> None:
if isinstance(check_result, tuple): if isinstance(check_result, tuple):
context.args = check_result[0] context.args = check_result[0]
if isinstance(check_result[1], dict): if isinstance(check_result[1], dict):

View file

@ -23,9 +23,15 @@ import warnings
from threading import Lock from threading import Lock
from telegram import Update from telegram import Update
from telegram.ext import (Handler, CallbackQueryHandler, InlineQueryHandler, from telegram.ext import (
ChosenInlineResultHandler, CallbackContext, BasePersistence, Handler,
DispatcherHandlerStop) CallbackQueryHandler,
InlineQueryHandler,
ChosenInlineResultHandler,
CallbackContext,
BasePersistence,
DispatcherHandlerStop,
)
from telegram.utils.promise import Promise from telegram.utils.promise import Promise
from telegram.utils.types import ConversationDict, HandlerArg from telegram.utils.types import ConversationDict, HandlerArg
@ -37,11 +43,13 @@ CheckUpdateType = Optional[Tuple[Tuple[int, ...], Handler, object]]
class _ConversationTimeoutContext: class _ConversationTimeoutContext:
def __init__(self, def __init__(
conversation_key: Tuple[int, ...], self,
update: Update, conversation_key: Tuple[int, ...],
dispatcher: 'Dispatcher', update: Update,
callback_context: Optional[CallbackContext]): dispatcher: 'Dispatcher',
callback_context: Optional[CallbackContext],
):
self.conversation_key = conversation_key self.conversation_key = conversation_key
self.update = update self.update = update
self.dispatcher = dispatcher self.dispatcher = dispatcher
@ -160,6 +168,7 @@ class ConversationHandler(Handler):
ValueError ValueError
""" """
END = -1 END = -1
""":obj:`int`: Used as a constant to return when a conversation is ended.""" """:obj:`int`: Used as a constant to return when a conversation is ended."""
TIMEOUT = -2 TIMEOUT = -2
@ -168,18 +177,20 @@ class ConversationHandler(Handler):
""":obj:`int`: Used as a constant to handle state when a conversation is still waiting on the """:obj:`int`: Used as a constant to handle state when a conversation is still waiting on the
previous ``@run_sync`` decorated running handler to finish.""" previous ``@run_sync`` decorated running handler to finish."""
def __init__(self, def __init__(
entry_points: List[Handler], self,
states: Dict[object, List[Handler]], entry_points: List[Handler],
fallbacks: List[Handler], states: Dict[object, List[Handler]],
allow_reentry: bool = False, fallbacks: List[Handler],
per_chat: bool = True, allow_reentry: bool = False,
per_user: bool = True, per_chat: bool = True,
per_message: bool = False, per_user: bool = True,
conversation_timeout: int = None, per_message: bool = False,
name: str = None, conversation_timeout: int = None,
persistent: bool = False, name: str = None,
map_to_parent: Dict[object, object] = None): persistent: bool = False,
map_to_parent: Dict[object, object] = None,
):
self.run_async = False self.run_async = False
self._entry_points = entry_points self._entry_points = entry_points
@ -211,8 +222,10 @@ class ConversationHandler(Handler):
raise ValueError("'per_user', 'per_chat' and 'per_message' can't all be 'False'") raise ValueError("'per_user', 'per_chat' and 'per_message' can't all be 'False'")
if self.per_message and not self.per_chat: if self.per_message and not self.per_chat:
warnings.warn("If 'per_message=True' is used, 'per_chat=True' should also be used, " warnings.warn(
"since message IDs are not globally unique.") "If 'per_message=True' is used, 'per_chat=True' should also be used, "
"since message IDs are not globally unique."
)
all_handlers = list() all_handlers = list()
all_handlers.extend(entry_points) all_handlers.extend(entry_points)
@ -224,22 +237,28 @@ class ConversationHandler(Handler):
if self.per_message: if self.per_message:
for handler in all_handlers: for handler in all_handlers:
if not isinstance(handler, CallbackQueryHandler): if not isinstance(handler, CallbackQueryHandler):
warnings.warn("If 'per_message=True', all entry points and state handlers" warnings.warn(
" must be 'CallbackQueryHandler', since no other handlers " "If 'per_message=True', all entry points and state handlers"
"have a message context.") " must be 'CallbackQueryHandler', since no other handlers "
"have a message context."
)
break break
else: else:
for handler in all_handlers: for handler in all_handlers:
if isinstance(handler, CallbackQueryHandler): if isinstance(handler, CallbackQueryHandler):
warnings.warn("If 'per_message=False', 'CallbackQueryHandler' will not be " warnings.warn(
"tracked for every message.") "If 'per_message=False', 'CallbackQueryHandler' will not be "
"tracked for every message."
)
break break
if self.per_chat: if self.per_chat:
for handler in all_handlers: for handler in all_handlers:
if isinstance(handler, (InlineQueryHandler, ChosenInlineResultHandler)): if isinstance(handler, (InlineQueryHandler, ChosenInlineResultHandler)):
warnings.warn("If 'per_chat=True', 'InlineQueryHandler' can not be used, " warnings.warn(
"since inline queries have no chat context.") "If 'per_chat=True', 'InlineQueryHandler' can not be used, "
"since inline queries have no chat context."
)
break break
@property @property
@ -304,8 +323,9 @@ class ConversationHandler(Handler):
@conversation_timeout.setter @conversation_timeout.setter
def conversation_timeout(self, value: Any) -> NoReturn: def conversation_timeout(self, value: Any) -> NoReturn:
raise ValueError('You can not assign a new value to conversation_timeout after ' raise ValueError(
'initialization.') 'You can not assign a new value to conversation_timeout after ' 'initialization.'
)
@property @property
def name(self) -> Optional[str]: def name(self) -> Optional[str]:
@ -362,8 +382,10 @@ class ConversationHandler(Handler):
key.append(user.id) key.append(user.id)
if self.per_message: if self.per_message:
key.append(update.callback_query.inline_message_id # type: ignore[union-attr] key.append(
or update.callback_query.message.message_id) # type: ignore[union-attr] update.callback_query.inline_message_id # type: ignore[union-attr]
or update.callback_query.message.message_id # type: ignore[union-attr]
)
return tuple(key) return tuple(key)
@ -380,11 +402,17 @@ class ConversationHandler(Handler):
""" """
# Ignore messages in channels # Ignore messages in channels
if (not isinstance(update, Update) if (
or update.channel_post not isinstance(update, Update)
or self.per_chat and not update.effective_chat or update.channel_post
or self.per_message and not update.callback_query or self.per_chat
or update.callback_query and self.per_chat and not update.callback_query.message): and not update.effective_chat
or self.per_message
and not update.callback_query
or update.callback_query
and self.per_chat
and not update.callback_query.message
):
return None return None
key = self._get_key(update) key = self._get_key(update)
@ -438,7 +466,7 @@ class ConversationHandler(Handler):
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 []:
check = candidate.check_update(update) check = candidate.check_update(update)
if check is not None and check is not False: if check is not None and check is not False:
handler = candidate handler = candidate
@ -457,11 +485,13 @@ class ConversationHandler(Handler):
return key, handler, check # type: ignore[return-value] return key, handler, check # type: ignore[return-value]
def handle_update(self, # type: ignore[override] def handle_update( # type: ignore[override]
update: HandlerArg, self,
dispatcher: 'Dispatcher', update: HandlerArg,
check_result: CheckUpdateType, dispatcher: 'Dispatcher',
context: CallbackContext = None) -> Optional[object]: check_result: CheckUpdateType,
context: CallbackContext = None,
) -> Optional[object]:
"""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:
@ -492,9 +522,12 @@ class ConversationHandler(Handler):
if self.conversation_timeout and new_state != self.END and dispatcher.job_queue: if self.conversation_timeout and new_state != self.END and dispatcher.job_queue:
# Add the new timeout job # Add the new timeout job
self.timeout_jobs[conversation_key] = dispatcher.job_queue.run_once( self.timeout_jobs[conversation_key] = dispatcher.job_queue.run_once(
self._trigger_timeout, self.conversation_timeout, # type: ignore[arg-type] self._trigger_timeout, # type: ignore[arg-type]
context=_ConversationTimeoutContext(conversation_key, update, self.conversation_timeout,
dispatcher, context)) context=_ConversationTimeoutContext(
conversation_key, update, dispatcher, context
),
)
if isinstance(self.map_to_parent, dict) and new_state in self.map_to_parent: if isinstance(self.map_to_parent, dict) and new_state in self.map_to_parent:
self.update_state(self.END, conversation_key) self.update_state(self.END, conversation_key)
@ -510,9 +543,7 @@ class ConversationHandler(Handler):
raise DispatcherHandlerStop() raise DispatcherHandlerStop()
return None return None
def update_state(self, def update_state(self, new_state: object, key: Tuple[int, ...]) -> None:
new_state: object,
key: Tuple[int, ...]) -> None:
if new_state == self.END: if new_state == self.END:
with self._conversations_lock: with self._conversations_lock:
if key in self.conversations: if key in self.conversations:
@ -525,8 +556,9 @@ class ConversationHandler(Handler):
with self._conversations_lock: with self._conversations_lock:
self.conversations[key] = (self.conversations.get(key), new_state) self.conversations[key] = (self.conversations.get(key), new_state)
if self.persistent and self.persistence and self.name: if self.persistent and self.persistence and self.name:
self.persistence.update_conversation(self.name, key, self.persistence.update_conversation(
(self.conversations.get(key), new_state)) self.name, key, (self.conversations.get(key), new_state)
)
elif new_state is not None: elif new_state is not None:
with self._conversations_lock: with self._conversations_lock:
@ -534,9 +566,7 @@ class ConversationHandler(Handler):
if self.persistent and self.persistence and self.name: if self.persistent and self.persistence and self.name:
self.persistence.update_conversation(self.name, key, new_state) self.persistence.update_conversation(self.name, key, new_state)
def _trigger_timeout(self, def _trigger_timeout(self, context: _ConversationTimeoutContext, job: 'Job' = None) -> None:
context: _ConversationTimeoutContext,
job: 'Job' = None) -> None:
self.logger.debug('conversation timeout was triggered!') self.logger.debug('conversation timeout was triggered!')
# Backward compatibility with bots that do not use CallbackContext # Backward compatibility with bots that do not use CallbackContext
@ -559,9 +589,12 @@ class ConversationHandler(Handler):
check = handler.check_update(context.update) check = handler.check_update(context.update)
if check is not None and check is not False: if check is not None and check is not False:
try: try:
handler.handle_update(context.update, context.dispatcher, check, handler.handle_update(
callback_context) context.update, context.dispatcher, check, callback_context
)
except DispatcherHandlerStop: except DispatcherHandlerStop:
self.logger.warning('DispatcherHandlerStop in TIMEOUT state of ' self.logger.warning(
'ConversationHandler has no effect. Ignoring.') 'DispatcherHandlerStop in TIMEOUT state of '
'ConversationHandler has no effect. Ignoring.'
)
self.update_state(self.END, context.conversation_key) self.update_state(self.END, context.conversation_key)

View file

@ -60,15 +60,18 @@ class Defaults:
somewhere, it will be assumed to be in ``tzinfo``. Must be a timezone provided by the somewhere, it will be assumed to be in ``tzinfo``. Must be a timezone provided by the
``pytz`` module. Defaults to UTC. ``pytz`` module. Defaults to UTC.
""" """
def __init__(self,
parse_mode: str = None, def __init__(
disable_notification: bool = None, self,
disable_web_page_preview: bool = None, parse_mode: str = None,
# Timeout needs special treatment, since the bot methods have two different disable_notification: bool = None,
# default values for timeout (None and 20s) disable_web_page_preview: bool = None,
timeout: Union[float, DefaultValue] = DEFAULT_NONE, # Timeout needs special treatment, since the bot methods have two different
quote: bool = None, # default values for timeout (None and 20s)
tzinfo: pytz.BaseTzInfo = pytz.utc): timeout: Union[float, DefaultValue] = DEFAULT_NONE,
quote: bool = None,
tzinfo: pytz.BaseTzInfo = pytz.utc,
):
self._parse_mode = parse_mode self._parse_mode = parse_mode
self._disable_notification = disable_notification self._disable_notification = disable_notification
self._disable_web_page_preview = disable_web_page_preview self._disable_web_page_preview = disable_web_page_preview
@ -82,8 +85,10 @@ class Defaults:
@parse_mode.setter @parse_mode.setter
def parse_mode(self, value: Any) -> NoReturn: def parse_mode(self, value: Any) -> NoReturn:
raise AttributeError("You can not assign a new value to defaults after because it would " raise AttributeError(
"not have any effect.") "You can not assign a new value to defaults after because it would "
"not have any effect."
)
@property @property
def disable_notification(self) -> Optional[bool]: def disable_notification(self) -> Optional[bool]:
@ -91,8 +96,10 @@ class Defaults:
@disable_notification.setter @disable_notification.setter
def disable_notification(self, value: Any) -> NoReturn: def disable_notification(self, value: Any) -> NoReturn:
raise AttributeError("You can not assign a new value to defaults after because it would " raise AttributeError(
"not have any effect.") "You can not assign a new value to defaults after because it would "
"not have any effect."
)
@property @property
def disable_web_page_preview(self) -> Optional[bool]: def disable_web_page_preview(self) -> Optional[bool]:
@ -100,8 +107,10 @@ class Defaults:
@disable_web_page_preview.setter @disable_web_page_preview.setter
def disable_web_page_preview(self, value: Any) -> NoReturn: def disable_web_page_preview(self, value: Any) -> NoReturn:
raise AttributeError("You can not assign a new value to defaults after because it would " raise AttributeError(
"not have any effect.") "You can not assign a new value to defaults after because it would "
"not have any effect."
)
@property @property
def timeout(self) -> Union[float, DefaultValue]: def timeout(self) -> Union[float, DefaultValue]:
@ -109,8 +118,10 @@ class Defaults:
@timeout.setter @timeout.setter
def timeout(self, value: Any) -> NoReturn: def timeout(self, value: Any) -> NoReturn:
raise AttributeError("You can not assign a new value to defaults after because it would " raise AttributeError(
"not have any effect.") "You can not assign a new value to defaults after because it would "
"not have any effect."
)
@property @property
def quote(self) -> Optional[bool]: def quote(self) -> Optional[bool]:
@ -118,8 +129,10 @@ class Defaults:
@quote.setter @quote.setter
def quote(self, value: Any) -> NoReturn: def quote(self, value: Any) -> NoReturn:
raise AttributeError("You can not assign a new value to defaults after because it would " raise AttributeError(
"not have any effect.") "You can not assign a new value to defaults after because it would "
"not have any effect."
)
@property @property
def tzinfo(self) -> pytz.BaseTzInfo: def tzinfo(self) -> pytz.BaseTzInfo:
@ -127,16 +140,22 @@ class Defaults:
@tzinfo.setter @tzinfo.setter
def tzinfo(self, value: Any) -> NoReturn: def tzinfo(self, value: Any) -> NoReturn:
raise AttributeError("You can not assign a new value to defaults after because it would " raise AttributeError(
"not have any effect.") "You can not assign a new value to defaults after because it would "
"not have any effect."
)
def __hash__(self) -> int: def __hash__(self) -> int:
return hash((self._parse_mode, return hash(
self._disable_notification, (
self._disable_web_page_preview, self._parse_mode,
self._timeout, self._disable_notification,
self._quote, self._disable_web_page_preview,
self._tzinfo)) self._timeout,
self._quote,
self._tzinfo,
)
)
def __eq__(self, other: object) -> bool: def __eq__(self, other: object) -> bool:
if isinstance(other, Defaults): if isinstance(other, Defaults):

View file

@ -19,8 +19,11 @@
"""This module contains the DictPersistence class.""" """This module contains the DictPersistence class."""
from copy import deepcopy from copy import deepcopy
from telegram.utils.helpers import decode_user_chat_data_from_json,\ from telegram.utils.helpers import (
decode_conversations_from_json, encode_conversations_to_json decode_user_chat_data_from_json,
decode_conversations_from_json,
encode_conversations_to_json,
)
try: try:
import ujson as json import ujson as json
@ -76,17 +79,21 @@ class DictPersistence(BasePersistence):
conversation on creating this persistence. Default is ``""``. conversation on creating this persistence. Default is ``""``.
""" """
def __init__(self, def __init__(
store_user_data: bool = True, self,
store_chat_data: bool = True, store_user_data: bool = True,
store_bot_data: bool = True, store_chat_data: bool = True,
user_data_json: str = '', store_bot_data: bool = True,
chat_data_json: str = '', user_data_json: str = '',
bot_data_json: str = '', chat_data_json: str = '',
conversations_json: str = ''): bot_data_json: str = '',
super().__init__(store_user_data=store_user_data, conversations_json: str = '',
store_chat_data=store_chat_data, ):
store_bot_data=store_bot_data) super().__init__(
store_user_data=store_user_data,
store_chat_data=store_chat_data,
store_bot_data=store_bot_data,
)
self._user_data = None self._user_data = None
self._chat_data = None self._chat_data = None
self._bot_data = None self._bot_data = None
@ -226,9 +233,9 @@ class DictPersistence(BasePersistence):
self._conversations = {} self._conversations = {}
return self.conversations.get(name, {}).copy() # type: ignore[union-attr] return self.conversations.get(name, {}).copy() # type: ignore[union-attr]
def update_conversation(self, def update_conversation(
name: str, key: Tuple[int, ...], self, name: str, key: Tuple[int, ...], new_state: Optional[object]
new_state: Optional[object]) -> None: ) -> None:
"""Will update the conversations for the given handler. """Will update the conversations for the given handler.
Args: Args:

View file

@ -47,8 +47,9 @@ if TYPE_CHECKING:
DEFAULT_GROUP = 0 DEFAULT_GROUP = 0
def run_async(func: Callable[[Update, CallbackContext], def run_async(
Any]) -> Callable[[Update, CallbackContext], Any]: func: Callable[[Update, CallbackContext], Any]
) -> Callable[[Update, CallbackContext], Any]:
""" """
Function decorator that will run the function in a new thread. Function decorator that will run the function in a new thread.
@ -67,12 +68,15 @@ def run_async(func: Callable[[Update, CallbackContext],
@wraps(func) @wraps(func)
def async_func(*args: Any, **kwargs: Any) -> Any: def async_func(*args: Any, **kwargs: Any) -> Any:
warnings.warn('The @run_async decorator is deprecated. Use the `run_async` parameter of' warnings.warn(
'`Dispatcher.add_handler` or `Dispatcher.run_async` instead.', 'The @run_async decorator is deprecated. Use the `run_async` parameter of'
TelegramDeprecationWarning, '`Dispatcher.add_handler` or `Dispatcher.run_async` instead.',
stacklevel=2) TelegramDeprecationWarning,
return Dispatcher.get_instance()._run_async(func, *args, update=None, error_handling=False, stacklevel=2,
**kwargs) )
return Dispatcher.get_instance()._run_async(
func, *args, update=None, error_handling=False, **kwargs
)
return async_func return async_func
@ -96,6 +100,7 @@ class DispatcherHandlerStop(Exception):
Args: Args:
state (:obj:`object`, optional): The next state of the conversation. state (:obj:`object`, optional): The next state of the conversation.
""" """
def __init__(self, state: object = None) -> None: def __init__(self, state: object = None) -> None:
super().__init__() super().__init__()
self.state = state self.state = state
@ -137,14 +142,16 @@ class Dispatcher:
__singleton = None __singleton = None
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def __init__(self, def __init__(
bot: 'Bot', self,
update_queue: Queue, bot: 'Bot',
workers: int = 4, update_queue: Queue,
exception_event: Event = None, workers: int = 4,
job_queue: 'JobQueue' = None, exception_event: Event = None,
persistence: BasePersistence = None, job_queue: 'JobQueue' = None,
use_context: bool = True): persistence: BasePersistence = None,
use_context: bool = True,
):
self.bot = bot self.bot = bot
self.update_queue = update_queue self.update_queue = update_queue
self.job_queue = job_queue self.job_queue = job_queue
@ -152,8 +159,11 @@ class Dispatcher:
self.use_context = use_context self.use_context = use_context
if not use_context: if not use_context:
warnings.warn('Old Handler API is deprecated - see https://git.io/fxJuV for details', warnings.warn(
TelegramDeprecationWarning, stacklevel=3) 'Old Handler API is deprecated - see https://git.io/fxJuV for details',
TelegramDeprecationWarning,
stacklevel=3,
)
self.user_data: DefaultDict[int, Dict[Any, Any]] = defaultdict(dict) self.user_data: DefaultDict[int, Dict[Any, Any]] = defaultdict(dict)
self.chat_data: DefaultDict[int, Dict[Any, Any]] = defaultdict(dict) self.chat_data: DefaultDict[int, Dict[Any, Any]] = defaultdict(dict)
@ -211,8 +221,9 @@ class Dispatcher:
base_name = '{}_'.format(base_name) if base_name else '' base_name = '{}_'.format(base_name) if base_name else ''
for i in range(workers): for i in range(workers):
thread = Thread(target=self._pooled, name='Bot:{}:worker:{}{}'.format(self.bot.id, thread = Thread(
base_name, i)) target=self._pooled, name='Bot:{}:worker:{}{}'.format(self.bot.id, base_name, i)
)
self.__async_threads.add(thread) self.__async_threads.add(thread)
thread.start() thread.start()
@ -235,8 +246,9 @@ class Dispatcher:
if cls.__singleton is not None: if cls.__singleton is not None:
return cls.__singleton() # type: ignore[return-value] # pylint: disable=not-callable return cls.__singleton() # type: ignore[return-value] # pylint: disable=not-callable
else: else:
raise RuntimeError('{} not initialized or multiple instances exist'.format( raise RuntimeError(
cls.__name__)) '{} not initialized or multiple instances exist'.format(cls.__name__)
)
def _pooled(self) -> None: def _pooled(self) -> None:
thr_name = current_thread().getName() thr_name = current_thread().getName()
@ -245,8 +257,9 @@ class Dispatcher:
# If unpacking fails, the thread pool is being closed from Updater._join_async_threads # If unpacking fails, the thread pool is being closed from Updater._join_async_threads
if not isinstance(promise, Promise): if not isinstance(promise, Promise):
self.logger.debug("Closing run_async thread %s/%d", thr_name, self.logger.debug(
len(self.__async_threads)) "Closing run_async thread %s/%d", thr_name, len(self.__async_threads)
)
break break
promise.run() promise.run()
@ -258,7 +271,8 @@ class Dispatcher:
if isinstance(promise.exception, DispatcherHandlerStop): if isinstance(promise.exception, DispatcherHandlerStop):
self.logger.warning( self.logger.warning(
'DispatcherHandlerStop is not supported with async functions; func: %s', 'DispatcherHandlerStop is not supported with async functions; func: %s',
promise.pooled_function.__name__) promise.pooled_function.__name__,
)
continue continue
# Avoid infinite recursion of error handlers. # Avoid infinite recursion of error handlers.
@ -280,11 +294,9 @@ class Dispatcher:
except Exception: except Exception:
self.logger.exception('An uncaught error was raised while handling the error.') self.logger.exception('An uncaught error was raised while handling the error.')
def run_async(self, def run_async(
func: Callable[..., Any], self, func: Callable[..., Any], *args: Any, update: HandlerArg = None, **kwargs: Any
*args: Any, ) -> Promise:
update: HandlerArg = None,
**kwargs: Any) -> Promise:
""" """
Queue a function (with given args/kwargs) to be run asynchronously. Exceptions raised Queue a function (with given args/kwargs) to be run asynchronously. Exceptions raised
by the function will be handled by the error handlers registered with by the function will be handled by the error handlers registered with
@ -310,12 +322,14 @@ class Dispatcher:
""" """
return self._run_async(func, *args, update=update, error_handling=True, **kwargs) return self._run_async(func, *args, update=update, error_handling=True, **kwargs)
def _run_async(self, def _run_async(
func: Callable[..., Any], self,
*args: Any, func: Callable[..., Any],
update: HandlerArg = None, *args: Any,
error_handling: bool = True, update: HandlerArg = None,
**kwargs: Any) -> Promise: error_handling: bool = True,
**kwargs: Any,
) -> Promise:
# TODO: Remove error_handling parameter once we drop the @run_async decorator # TODO: Remove error_handling parameter once we drop the @run_async decorator
promise = Promise(func, args, kwargs, update=update, error_handling=error_handling) promise = Promise(func, args, kwargs, update=update, error_handling=error_handling)
self.__async_queue.put(promise) self.__async_queue.put(promise)
@ -482,7 +496,8 @@ class Dispatcher:
if not self.persistence: if not self.persistence:
raise ValueError( raise ValueError(
"ConversationHandler {} can not be persistent if dispatcher has no " "ConversationHandler {} can not be persistent if dispatcher has no "
"persistence".format(handler.name)) "persistence".format(handler.name)
)
handler.persistence = self.persistence handler.persistence = self.persistence
handler.conversations = self.persistence.get_conversations(handler.name) handler.conversations = self.persistence.get_conversations(handler.name)
@ -541,9 +556,11 @@ class Dispatcher:
try: try:
self.dispatch_error(update, e) self.dispatch_error(update, e)
except Exception: except Exception:
message = 'Saving bot data raised an error and an ' \ message = (
'uncaught error was raised while handling ' \ 'Saving bot data raised an error and an '
'the error with an error_handler' 'uncaught error was raised while handling '
'the error with an error_handler'
)
self.logger.exception(message) self.logger.exception(message)
if self.persistence.store_chat_data: if self.persistence.store_chat_data:
for chat_id in chat_ids: for chat_id in chat_ids:
@ -553,9 +570,11 @@ class Dispatcher:
try: try:
self.dispatch_error(update, e) self.dispatch_error(update, e)
except Exception: except Exception:
message = 'Saving chat data raised an error and an ' \ message = (
'uncaught error was raised while handling ' \ 'Saving chat data raised an error and an '
'the error with an error_handler' 'uncaught error was raised while handling '
'the error with an error_handler'
)
self.logger.exception(message) self.logger.exception(message)
if self.persistence.store_user_data: if self.persistence.store_user_data:
for user_id in user_ids: for user_id in user_ids:
@ -565,14 +584,16 @@ class Dispatcher:
try: try:
self.dispatch_error(update, e) self.dispatch_error(update, e)
except Exception: except Exception:
message = 'Saving user data raised an error and an ' \ message = (
'uncaught error was raised while handling ' \ 'Saving user data raised an error and an '
'the error with an error_handler' 'uncaught error was raised while handling '
'the error with an error_handler'
)
self.logger.exception(message) self.logger.exception(message)
def add_error_handler(self, def add_error_handler(
callback: Callable[[Any, CallbackContext], None], self, callback: Callable[[Any, CallbackContext], None], run_async: bool = False
run_async: bool = False) -> None: ) -> None:
"""Registers an error handler in the Dispatcher. This handler will receive every error """Registers an error handler in the Dispatcher. This handler will receive every error
which happens in your bot. which happens in your bot.
@ -610,10 +631,9 @@ class Dispatcher:
""" """
self.error_handlers.pop(callback, None) self.error_handlers.pop(callback, None)
def dispatch_error(self, def dispatch_error(
update: Optional[HandlerArg], self, update: Optional[HandlerArg], error: Exception, promise: Promise = None
error: Exception, ) -> None:
promise: Promise = None) -> None:
"""Dispatches an error. """Dispatches an error.
Args: Args:
@ -629,9 +649,9 @@ class Dispatcher:
if self.error_handlers: if self.error_handlers:
for callback, run_async in self.error_handlers.items(): for callback, run_async in self.error_handlers.items():
if self.use_context: if self.use_context:
context = CallbackContext.from_error(update, error, self, context = CallbackContext.from_error(
async_args=async_args, update, error, self, async_args=async_args, async_kwargs=async_kwargs
async_kwargs=async_kwargs) )
if run_async: if run_async:
self.run_async(callback, update, context, update=update) self.run_async(callback, update, context, update=update)
else: else:
@ -644,4 +664,5 @@ class Dispatcher:
else: else:
self.logger.exception( self.logger.exception(
'No error handlers are registered, logging exception.', exc_info=error) 'No error handlers are registered, logging exception.', exc_info=error
)

View file

@ -27,8 +27,14 @@ from telegram import Chat, Update, MessageEntity, Message
from typing import Optional, Dict, Union, List, Pattern, Match, cast, Set, FrozenSet from typing import Optional, Dict, Union, List, Pattern, Match, cast, Set, FrozenSet
__all__ = ['Filters', 'BaseFilter', 'MessageFilter', 'UpdateFilter', 'InvertedFilter', __all__ = [
'MergedFilter'] 'Filters',
'BaseFilter',
'MessageFilter',
'UpdateFilter',
'InvertedFilter',
'MergedFilter',
]
class BaseFilter(ABC): class BaseFilter(ABC):
@ -120,6 +126,7 @@ class MessageFilter(BaseFilter, ABC):
(depends on the handler). (depends on the handler).
""" """
def __call__(self, update: Update) -> Optional[Union[bool, Dict]]: def __call__(self, update: Update) -> Optional[Union[bool, Dict]]:
return self.filter(update.effective_message) return self.filter(update.effective_message)
@ -151,12 +158,12 @@ class UpdateFilter(BaseFilter, ABC):
(depends on the handler). (depends on the handler).
""" """
def __call__(self, update: Update) -> Optional[Union[bool, Dict]]: def __call__(self, update: Update) -> Optional[Union[bool, Dict]]:
return self.filter(update) return self.filter(update)
@abstractmethod @abstractmethod
def filter(self, def filter(self, update: Update) -> Optional[Union[bool, Dict]]:
update: Update) -> Optional[Union[bool, Dict]]:
"""This method must be overwritten. """This method must be overwritten.
Args: Args:
@ -175,6 +182,7 @@ class InvertedFilter(UpdateFilter):
f: The filter to invert. f: The filter to invert.
""" """
def __init__(self, f: BaseFilter): def __init__(self, f: BaseFilter):
self.f = f self.f = f
@ -194,22 +202,22 @@ class MergedFilter(UpdateFilter):
or_filter: Optional filter to "or" with base_filter. Mutually exclusive with and_filter. or_filter: Optional filter to "or" with base_filter. Mutually exclusive with and_filter.
""" """
def __init__(self,
base_filter: BaseFilter, def __init__(
and_filter: BaseFilter = None, self, base_filter: BaseFilter, and_filter: BaseFilter = None, or_filter: BaseFilter = None
or_filter: BaseFilter = None): ):
self.base_filter = base_filter self.base_filter = base_filter
if self.base_filter.data_filter: if self.base_filter.data_filter:
self.data_filter = True self.data_filter = True
self.and_filter = and_filter self.and_filter = and_filter
if (self.and_filter if (
and not isinstance(self.and_filter, bool) self.and_filter
and self.and_filter.data_filter): and not isinstance(self.and_filter, bool)
and self.and_filter.data_filter
):
self.data_filter = True self.data_filter = True
self.or_filter = or_filter self.or_filter = or_filter
if (self.or_filter if self.or_filter and not isinstance(self.and_filter, bool) and self.or_filter.data_filter:
and not isinstance(self.and_filter, bool)
and self.or_filter.data_filter):
self.data_filter = True self.data_filter = True
def _merge(self, base_output: Union[bool, Dict], comp_output: Union[bool, Dict]) -> Dict: def _merge(self, base_output: Union[bool, Dict], comp_output: Union[bool, Dict]) -> Dict:
@ -257,18 +265,17 @@ class MergedFilter(UpdateFilter):
return False return False
def __repr__(self) -> str: def __repr__(self) -> str:
return "<{} {} {}>".format(self.base_filter, "and" if self.and_filter else "or", return "<{} {} {}>".format(
self.and_filter or self.or_filter) self.base_filter, "and" if self.and_filter else "or", self.and_filter or self.or_filter
)
class _DiceEmoji(MessageFilter): class _DiceEmoji(MessageFilter):
def __init__(self, emoji: str = None, name: str = None): def __init__(self, emoji: str = None, name: str = None):
self.name = 'Filters.dice.{}'.format(name) if name else 'Filters.dice' self.name = 'Filters.dice.{}'.format(name) if name else 'Filters.dice'
self.emoji = emoji self.emoji = emoji
class _DiceValues(MessageFilter): class _DiceValues(MessageFilter):
def __init__(self, values: Union[int, List[int]], name: str, emoji: str = None): def __init__(self, values: Union[int, List[int]], name: str, emoji: str = None):
self.values = [values] if isinstance(values, int) else values self.values = [values] if isinstance(values, int) else values
self.emoji = emoji self.emoji = emoji
@ -281,8 +288,9 @@ class _DiceEmoji(MessageFilter):
return True return True
return False return False
def __call__(self, # type: ignore[override] def __call__( # type: ignore[override]
update: Union[Update, List[int]]) -> Union[bool, '_DiceValues']: self, update: Union[Update, List[int]]
) -> Union[bool, '_DiceValues']:
if isinstance(update, Update): if isinstance(update, Update):
return self.filter(update.effective_message) return self.filter(update.effective_message)
else: else:
@ -319,7 +327,6 @@ class Filters:
name = 'Filters.text' name = 'Filters.text'
class _TextStrings(MessageFilter): class _TextStrings(MessageFilter):
def __init__(self, strings: List[str]): def __init__(self, strings: List[str]):
self.strings = strings self.strings = strings
self.name = 'Filters.text({})'.format(strings) self.name = 'Filters.text({})'.format(strings)
@ -329,8 +336,9 @@ class Filters:
return message.text in self.strings return message.text in self.strings
return False return False
def __call__(self, # type: ignore[override] def __call__( # type: ignore[override]
update: Union[Update, List[str]]) -> Union[bool, '_TextStrings']: self, update: Union[Update, List[str]]
) -> Union[bool, '_TextStrings']:
if isinstance(update, Update): if isinstance(update, Update):
return self.filter(update.effective_message) return self.filter(update.effective_message)
else: else:
@ -371,7 +379,6 @@ class Filters:
name = 'Filters.caption' name = 'Filters.caption'
class _CaptionStrings(MessageFilter): class _CaptionStrings(MessageFilter):
def __init__(self, strings: List[str]): def __init__(self, strings: List[str]):
self.strings = strings self.strings = strings
self.name = 'Filters.caption({})'.format(strings) self.name = 'Filters.caption({})'.format(strings)
@ -381,8 +388,9 @@ class Filters:
return message.caption in self.strings return message.caption in self.strings
return False return False
def __call__(self, # type: ignore[override] def __call__( # type: ignore[override]
update: Union[Update, List[str]]) -> Union[bool, '_CaptionStrings']: self, update: Union[Update, List[str]]
) -> Union[bool, '_CaptionStrings']:
if isinstance(update, Update): if isinstance(update, Update):
return self.filter(update.effective_message) return self.filter(update.effective_message)
else: else:
@ -407,26 +415,30 @@ class Filters:
name = 'Filters.command' name = 'Filters.command'
class _CommandOnlyStart(MessageFilter): class _CommandOnlyStart(MessageFilter):
def __init__(self, only_start: bool): def __init__(self, only_start: bool):
self.only_start = only_start self.only_start = only_start
self.name = 'Filters.command({})'.format(only_start) self.name = 'Filters.command({})'.format(only_start)
def filter(self, message: Message) -> bool: def filter(self, message: Message) -> bool:
return bool(message.entities return bool(
and any([e.type == MessageEntity.BOT_COMMAND message.entities
for e in message.entities])) and any([e.type == MessageEntity.BOT_COMMAND for e in message.entities])
)
def __call__(self, # type: ignore[override] def __call__( # type: ignore[override]
update: Union[bool, Update]) -> Union[bool, '_CommandOnlyStart']: self, update: Union[bool, Update]
) -> Union[bool, '_CommandOnlyStart']:
if isinstance(update, Update): if isinstance(update, Update):
return self.filter(update.effective_message) return self.filter(update.effective_message)
else: else:
return self._CommandOnlyStart(update) return self._CommandOnlyStart(update)
def filter(self, message: Message) -> bool: def filter(self, message: Message) -> bool:
return bool(message.entities and message.entities[0].type == MessageEntity.BOT_COMMAND return bool(
and message.entities[0].offset == 0) message.entities
and message.entities[0].type == MessageEntity.BOT_COMMAND
and message.entities[0].offset == 0
)
command = _Command() command = _Command()
""" """
@ -485,8 +497,7 @@ class Filters:
self.pattern: Pattern = pattern self.pattern: Pattern = pattern
self.name = 'Filters.regex({})'.format(self.pattern) self.name = 'Filters.regex({})'.format(self.pattern)
def filter(self, def filter(self, message: Message) -> Optional[Dict[str, List[Match]]]:
message: Message) -> Optional[Dict[str, List[Match]]]:
"""""" # remove method from docs """""" # remove method from docs
if message.text: if message.text:
match = self.pattern.search(message.text) match = self.pattern.search(message.text)
@ -739,6 +750,7 @@ officedocument.wordprocessingml.document")``-
``Filters.status_update`` for all status update messages. ``Filters.status_update`` for all status update messages.
""" """
class _NewChatMembers(MessageFilter): class _NewChatMembers(MessageFilter):
name = 'Filters.status_update.new_chat_members' name = 'Filters.status_update.new_chat_members'
@ -788,8 +800,11 @@ officedocument.wordprocessingml.document")``-
name = 'Filters.status_update.chat_created' name = 'Filters.status_update.chat_created'
def filter(self, message: Message) -> bool: def filter(self, message: Message) -> bool:
return bool(message.group_chat_created or message.supergroup_chat_created return bool(
or message.channel_chat_created) message.group_chat_created
or message.supergroup_chat_created
or message.channel_chat_created
)
chat_created = _ChatCreated() chat_created = _ChatCreated()
"""Messages that contain :attr:`telegram.Message.group_chat_created`, """Messages that contain :attr:`telegram.Message.group_chat_created`,
@ -827,11 +842,17 @@ officedocument.wordprocessingml.document")``-
name = 'Filters.status_update' name = 'Filters.status_update'
def filter(self, message: Update) -> bool: def filter(self, message: Update) -> bool:
return bool(self.new_chat_members(message) or self.left_chat_member(message) return bool(
or self.new_chat_title(message) or self.new_chat_photo(message) self.new_chat_members(message)
or self.delete_chat_photo(message) or self.chat_created(message) or self.left_chat_member(message)
or self.migrate(message) or self.pinned_message(message) or self.new_chat_title(message)
or self.connected_website(message)) or self.new_chat_photo(message)
or self.delete_chat_photo(message)
or self.chat_created(message)
or self.migrate(message)
or self.pinned_message(message)
or self.connected_website(message)
)
status_update = _StatusUpdate() status_update = _StatusUpdate()
"""Subset for messages containing a status update. """Subset for messages containing a status update.
@ -976,10 +997,13 @@ officedocument.wordprocessingml.document")``-
RuntimeError: If user_id and username are both present. RuntimeError: If user_id and username are both present.
""" """
def __init__(self,
user_id: Union[int, List[int]] = None, def __init__(
username: Union[str, List[str]] = None, self,
allow_empty: bool = False): user_id: Union[int, List[int]] = None,
username: Union[str, List[str]] = None,
allow_empty: bool = False,
):
self.allow_empty = allow_empty self.allow_empty = allow_empty
self.__lock = Lock() self.__lock = Lock()
@ -1008,15 +1032,17 @@ officedocument.wordprocessingml.document")``-
def _set_user_ids(self, user_id: Union[int, List[int]]) -> None: def _set_user_ids(self, user_id: Union[int, List[int]]) -> None:
with self.__lock: with self.__lock:
if user_id and self._usernames: if user_id and self._usernames:
raise RuntimeError("Can't set user_id in conjunction with (already set) " raise RuntimeError(
"usernames.") "Can't set user_id in conjunction with (already set) " "usernames."
)
self._user_ids = self._parse_user_id(user_id) self._user_ids = self._parse_user_id(user_id)
def _set_usernames(self, username: Union[str, List[str]]) -> None: def _set_usernames(self, username: Union[str, List[str]]) -> None:
with self.__lock: with self.__lock:
if username and self._user_ids: if username and self._user_ids:
raise RuntimeError("Can't set username in conjunction with (already set) " raise RuntimeError(
"user_ids.") "Can't set username in conjunction with (already set) " "user_ids."
)
self._usernames = self._parse_username(username) self._usernames = self._parse_username(username)
@property @property
@ -1047,8 +1073,9 @@ officedocument.wordprocessingml.document")``-
""" """
with self.__lock: with self.__lock:
if self._user_ids: if self._user_ids:
raise RuntimeError("Can't set username in conjunction with (already set) " raise RuntimeError(
"user_ids.") "Can't set username in conjunction with (already set) " "user_ids."
)
parsed_username = self._parse_username(username) parsed_username = self._parse_username(username)
self._usernames |= parsed_username self._usernames |= parsed_username
@ -1063,8 +1090,9 @@ officedocument.wordprocessingml.document")``-
""" """
with self.__lock: with self.__lock:
if self._usernames: if self._usernames:
raise RuntimeError("Can't set user_id in conjunction with (already set) " raise RuntimeError(
"usernames.") "Can't set user_id in conjunction with (already set) " "usernames."
)
parsed_user_id = self._parse_user_id(user_id) parsed_user_id = self._parse_user_id(user_id)
@ -1080,8 +1108,9 @@ officedocument.wordprocessingml.document")``-
""" """
with self.__lock: with self.__lock:
if self._user_ids: if self._user_ids:
raise RuntimeError("Can't set username in conjunction with (already set) " raise RuntimeError(
"user_ids.") "Can't set username in conjunction with (already set) " "user_ids."
)
parsed_username = self._parse_username(username) parsed_username = self._parse_username(username)
self._usernames -= parsed_username self._usernames -= parsed_username
@ -1096,8 +1125,9 @@ officedocument.wordprocessingml.document")``-
""" """
with self.__lock: with self.__lock:
if self._usernames: if self._usernames:
raise RuntimeError("Can't set user_id in conjunction with (already set) " raise RuntimeError(
"usernames.") "Can't set user_id in conjunction with (already set) " "usernames."
)
parsed_user_id = self._parse_user_id(user_id) parsed_user_id = self._parse_user_id(user_id)
self._user_ids -= parsed_user_id self._user_ids -= parsed_user_id
@ -1107,8 +1137,9 @@ officedocument.wordprocessingml.document")``-
if self.user_ids: if self.user_ids:
return message.from_user.id in self.user_ids return message.from_user.id in self.user_ids
if self.usernames: if self.usernames:
return bool(message.from_user.username return bool(
and message.from_user.username in self.usernames) message.from_user.username and message.from_user.username in self.usernames
)
return self.allow_empty return self.allow_empty
return False return False
@ -1145,10 +1176,13 @@ officedocument.wordprocessingml.document")``-
Raises: Raises:
RuntimeError: If bot_id and username are both present. RuntimeError: If bot_id and username are both present.
""" """
def __init__(self,
bot_id: Union[int, List[int]] = None, def __init__(
username: Union[str, List[str]] = None, self,
allow_empty: bool = False): bot_id: Union[int, List[int]] = None,
username: Union[str, List[str]] = None,
allow_empty: bool = False,
):
self.allow_empty = allow_empty self.allow_empty = allow_empty
self.__lock = Lock() self.__lock = Lock()
@ -1177,15 +1211,17 @@ officedocument.wordprocessingml.document")``-
def _set_bot_ids(self, bot_id: Union[int, List[int]]) -> None: def _set_bot_ids(self, bot_id: Union[int, List[int]]) -> None:
with self.__lock: with self.__lock:
if bot_id and self._usernames: if bot_id and self._usernames:
raise RuntimeError("Can't set bot_id in conjunction with (already set) " raise RuntimeError(
"usernames.") "Can't set bot_id in conjunction with (already set) " "usernames."
)
self._bot_ids = self._parse_bot_id(bot_id) self._bot_ids = self._parse_bot_id(bot_id)
def _set_usernames(self, username: Union[str, List[str]]) -> None: def _set_usernames(self, username: Union[str, List[str]]) -> None:
with self.__lock: with self.__lock:
if username and self._bot_ids: if username and self._bot_ids:
raise RuntimeError("Can't set username in conjunction with (already set) " raise RuntimeError(
"bot_ids.") "Can't set username in conjunction with (already set) " "bot_ids."
)
self._usernames = self._parse_username(username) self._usernames = self._parse_username(username)
@property @property
@ -1216,8 +1252,9 @@ officedocument.wordprocessingml.document")``-
""" """
with self.__lock: with self.__lock:
if self._bot_ids: if self._bot_ids:
raise RuntimeError("Can't set username in conjunction with (already set) " raise RuntimeError(
"bot_ids.") "Can't set username in conjunction with (already set) " "bot_ids."
)
parsed_username = self._parse_username(username) parsed_username = self._parse_username(username)
self._usernames |= parsed_username self._usernames |= parsed_username
@ -1233,8 +1270,9 @@ officedocument.wordprocessingml.document")``-
""" """
with self.__lock: with self.__lock:
if self._usernames: if self._usernames:
raise RuntimeError("Can't set bot_id in conjunction with (already set) " raise RuntimeError(
"usernames.") "Can't set bot_id in conjunction with (already set) " "usernames."
)
parsed_bot_id = self._parse_bot_id(bot_id) parsed_bot_id = self._parse_bot_id(bot_id)
@ -1250,8 +1288,9 @@ officedocument.wordprocessingml.document")``-
""" """
with self.__lock: with self.__lock:
if self._bot_ids: if self._bot_ids:
raise RuntimeError("Can't set username in conjunction with (already set) " raise RuntimeError(
"bot_ids.") "Can't set username in conjunction with (already set) " "bot_ids."
)
parsed_username = self._parse_username(username) parsed_username = self._parse_username(username)
self._usernames -= parsed_username self._usernames -= parsed_username
@ -1266,8 +1305,9 @@ officedocument.wordprocessingml.document")``-
""" """
with self.__lock: with self.__lock:
if self._usernames: if self._usernames:
raise RuntimeError("Can't set bot_id in conjunction with (already set) " raise RuntimeError(
"usernames.") "Can't set bot_id in conjunction with (already set) " "usernames."
)
parsed_bot_id = self._parse_bot_id(bot_id) parsed_bot_id = self._parse_bot_id(bot_id)
self._bot_ids -= parsed_bot_id self._bot_ids -= parsed_bot_id
@ -1277,8 +1317,9 @@ officedocument.wordprocessingml.document")``-
if self.bot_ids: if self.bot_ids:
return message.via_bot.id in self.bot_ids return message.via_bot.id in self.bot_ids
if self.usernames: if self.usernames:
return bool(message.via_bot.username return bool(
and message.via_bot.username in self.usernames) message.via_bot.username and message.via_bot.username in self.usernames
)
return self.allow_empty return self.allow_empty
return False return False
@ -1316,10 +1357,12 @@ officedocument.wordprocessingml.document")``-
""" """
def __init__(self, def __init__(
chat_id: Union[int, List[int]] = None, self,
username: Union[str, List[str]] = None, chat_id: Union[int, List[int]] = None,
allow_empty: bool = False): username: Union[str, List[str]] = None,
allow_empty: bool = False,
):
self.allow_empty = allow_empty self.allow_empty = allow_empty
self.__lock = Lock() self.__lock = Lock()
@ -1348,15 +1391,17 @@ officedocument.wordprocessingml.document")``-
def _set_chat_ids(self, chat_id: Union[int, List[int]]) -> None: def _set_chat_ids(self, chat_id: Union[int, List[int]]) -> None:
with self.__lock: with self.__lock:
if chat_id and self._usernames: if chat_id and self._usernames:
raise RuntimeError("Can't set chat_id in conjunction with (already set) " raise RuntimeError(
"usernames.") "Can't set chat_id in conjunction with (already set) " "usernames."
)
self._chat_ids = self._parse_chat_id(chat_id) self._chat_ids = self._parse_chat_id(chat_id)
def _set_usernames(self, username: Union[str, List[str]]) -> None: def _set_usernames(self, username: Union[str, List[str]]) -> None:
with self.__lock: with self.__lock:
if username and self._chat_ids: if username and self._chat_ids:
raise RuntimeError("Can't set username in conjunction with (already set) " raise RuntimeError(
"chat_ids.") "Can't set username in conjunction with (already set) " "chat_ids."
)
self._usernames = self._parse_username(username) self._usernames = self._parse_username(username)
@property @property
@ -1387,8 +1432,9 @@ officedocument.wordprocessingml.document")``-
""" """
with self.__lock: with self.__lock:
if self._chat_ids: if self._chat_ids:
raise RuntimeError("Can't set username in conjunction with (already set) " raise RuntimeError(
"chat_ids.") "Can't set username in conjunction with (already set) " "chat_ids."
)
parsed_username = self._parse_username(username) parsed_username = self._parse_username(username)
self._usernames |= parsed_username self._usernames |= parsed_username
@ -1403,8 +1449,9 @@ officedocument.wordprocessingml.document")``-
""" """
with self.__lock: with self.__lock:
if self._usernames: if self._usernames:
raise RuntimeError("Can't set chat_id in conjunction with (already set) " raise RuntimeError(
"usernames.") "Can't set chat_id in conjunction with (already set) " "usernames."
)
parsed_chat_id = self._parse_chat_id(chat_id) parsed_chat_id = self._parse_chat_id(chat_id)
@ -1420,8 +1467,9 @@ officedocument.wordprocessingml.document")``-
""" """
with self.__lock: with self.__lock:
if self._chat_ids: if self._chat_ids:
raise RuntimeError("Can't set username in conjunction with (already set) " raise RuntimeError(
"chat_ids.") "Can't set username in conjunction with (already set) " "chat_ids."
)
parsed_username = self._parse_username(username) parsed_username = self._parse_username(username)
self._usernames -= parsed_username self._usernames -= parsed_username
@ -1436,8 +1484,9 @@ officedocument.wordprocessingml.document")``-
""" """
with self.__lock: with self.__lock:
if self._usernames: if self._usernames:
raise RuntimeError("Can't set chat_id in conjunction with (already set) " raise RuntimeError(
"usernames.") "Can't set chat_id in conjunction with (already set) " "usernames."
)
parsed_chat_id = self._parse_chat_id(chat_id) parsed_chat_id = self._parse_chat_id(chat_id)
self._chat_ids -= parsed_chat_id self._chat_ids -= parsed_chat_id
@ -1447,8 +1496,7 @@ officedocument.wordprocessingml.document")``-
if self.chat_ids: if self.chat_ids:
return message.chat.id in self.chat_ids return message.chat.id in self.chat_ids
if self.usernames: if self.usernames:
return bool(message.chat.username return bool(message.chat.username and message.chat.username in self.usernames)
and message.chat.username in self.usernames)
return self.allow_empty return self.allow_empty
return False return False
@ -1550,8 +1598,10 @@ officedocument.wordprocessingml.document")``-
def filter(self, message: Message) -> bool: def filter(self, message: Message) -> bool:
"""""" # remove method from docs """""" # remove method from docs
return bool(message.from_user.language_code and any( return bool(
[message.from_user.language_code.startswith(x) for x in self.lang])) message.from_user.language_code
and any([message.from_user.language_code.startswith(x) for x in self.lang])
)
class _UpdateType(UpdateFilter): class _UpdateType(UpdateFilter):
name = 'Filters.update' name = 'Filters.update'

View file

@ -24,6 +24,7 @@ from telegram.utils.promise import Promise
from telegram.utils.types import HandlerArg from telegram.utils.types import HandlerArg
from telegram import Update from telegram import Update
from typing import Callable, TYPE_CHECKING, Any, Optional, Union, TypeVar, Dict from typing import Callable, TYPE_CHECKING, Any, Optional, Union, TypeVar, Dict
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram.ext import CallbackContext, Dispatcher from telegram.ext import CallbackContext, Dispatcher
@ -87,13 +88,16 @@ class Handler(ABC):
Defaults to :obj:`False`. Defaults to :obj:`False`.
""" """
def __init__(self,
callback: Callable[[HandlerArg, 'CallbackContext'], RT], def __init__(
pass_update_queue: bool = False, self,
pass_job_queue: bool = False, callback: Callable[[HandlerArg, 'CallbackContext'], RT],
pass_user_data: bool = False, pass_update_queue: bool = False,
pass_chat_data: bool = False, pass_job_queue: bool = False,
run_async: bool = False): pass_user_data: bool = False,
pass_chat_data: bool = False,
run_async: bool = False,
):
self.callback: Callable[[HandlerArg, 'CallbackContext'], RT] = callback self.callback: Callable[[HandlerArg, 'CallbackContext'], RT] = callback
self.pass_update_queue = pass_update_queue self.pass_update_queue = pass_update_queue
self.pass_job_queue = pass_job_queue self.pass_job_queue = pass_job_queue
@ -117,11 +121,13 @@ class Handler(ABC):
""" """
def handle_update(self, def handle_update(
update: HandlerArg, self,
dispatcher: 'Dispatcher', update: HandlerArg,
check_result: object, dispatcher: 'Dispatcher',
context: 'CallbackContext' = None) -> Union[RT, Promise]: check_result: object,
context: 'CallbackContext' = None,
) -> Union[RT, Promise]:
""" """
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. Calls :attr:`callback` along with its respectful be handled by this instance. Calls :attr:`callback` along with its respectful
@ -146,16 +152,19 @@ class Handler(ABC):
else: else:
optional_args = self.collect_optional_args(dispatcher, update, check_result) optional_args = self.collect_optional_args(dispatcher, update, check_result)
if self.run_async: if self.run_async:
return dispatcher.run_async(self.callback, dispatcher.bot, update, update=update, return dispatcher.run_async(
**optional_args) self.callback, dispatcher.bot, update, update=update, **optional_args
)
else: else:
return self.callback(dispatcher.bot, update, **optional_args) # type: ignore return self.callback(dispatcher.bot, update, **optional_args) # type: ignore
def collect_additional_context(self, def collect_additional_context(
context: 'CallbackContext', self,
update: HandlerArg, context: 'CallbackContext',
dispatcher: 'Dispatcher', update: HandlerArg,
check_result: Any) -> None: dispatcher: 'Dispatcher',
check_result: Any,
) -> None:
"""Prepares additional arguments for the context. Override if needed. """Prepares additional arguments for the context. Override if needed.
Args: Args:
@ -167,10 +176,9 @@ class Handler(ABC):
""" """
pass pass
def collect_optional_args(self, def collect_optional_args(
dispatcher: 'Dispatcher', self, dispatcher: 'Dispatcher', update: HandlerArg = None, check_result: Any = None
update: HandlerArg = None, ) -> Dict[str, Any]:
check_result: Any = None) -> Dict[str, Any]:
""" """
Prepares the optional arguments. If the handler has additional optional args, Prepares the optional arguments. If the handler has additional optional args,
it should subclass this method, but remember to call this super method. it should subclass this method, but remember to call this super method.
@ -193,10 +201,12 @@ class Handler(ABC):
if self.pass_user_data and isinstance(update, Update): if self.pass_user_data and isinstance(update, Update):
user = update.effective_user user = update.effective_user
optional_args['user_data'] = dispatcher.user_data[ optional_args['user_data'] = dispatcher.user_data[
user.id if user else None] # type: ignore[index] user.id if user else None # type: ignore[index]
]
if self.pass_chat_data and isinstance(update, Update): if self.pass_chat_data and isinstance(update, Update):
chat = update.effective_chat chat = update.effective_chat
optional_args['chat_data'] = dispatcher.chat_data[ optional_args['chat_data'] = dispatcher.chat_data[
chat.id if chat else None] # type: ignore[index] chat.id if chat else None # type: ignore[index]
]
return optional_args return optional_args

View file

@ -24,8 +24,18 @@ from telegram import Update
from .handler import Handler from .handler import Handler
from telegram.utils.types import HandlerArg from telegram.utils.types import HandlerArg
from typing import Callable, TYPE_CHECKING, Any, Optional, Union, TypeVar, Dict, Pattern, Match, \ from typing import (
cast Callable,
TYPE_CHECKING,
Any,
Optional,
Union,
TypeVar,
Dict,
Pattern,
Match,
cast,
)
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram.ext import CallbackContext, Dispatcher from telegram.ext import CallbackContext, Dispatcher
@ -110,23 +120,26 @@ class InlineQueryHandler(Handler):
""" """
def __init__(self, def __init__(
callback: Callable[[HandlerArg, 'CallbackContext'], RT], self,
pass_update_queue: bool = False, callback: Callable[[HandlerArg, 'CallbackContext'], RT],
pass_job_queue: bool = False, pass_update_queue: bool = False,
pattern: Union[str, Pattern] = None, pass_job_queue: bool = False,
pass_groups: bool = False, pattern: Union[str, Pattern] = None,
pass_groupdict: bool = False, pass_groups: bool = False,
pass_user_data: bool = False, pass_groupdict: bool = False,
pass_chat_data: bool = False, pass_user_data: bool = False,
run_async: bool = False): pass_chat_data: bool = False,
run_async: bool = False,
):
super().__init__( super().__init__(
callback, callback,
pass_update_queue=pass_update_queue, pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue, pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data, pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data, pass_chat_data=pass_chat_data,
run_async=run_async) run_async=run_async,
)
if isinstance(pattern, str): if isinstance(pattern, str):
pattern = re.compile(pattern) pattern = re.compile(pattern)
@ -157,10 +170,12 @@ class InlineQueryHandler(Handler):
return True return True
return None return None
def collect_optional_args(self, def collect_optional_args(
dispatcher: 'Dispatcher', self,
update: HandlerArg = None, dispatcher: 'Dispatcher',
check_result: Optional[Union[bool, Match]] = None) -> Dict[str, Any]: update: HandlerArg = None,
check_result: Optional[Union[bool, Match]] = None,
) -> Dict[str, Any]:
optional_args = super().collect_optional_args(dispatcher, update, check_result) optional_args = super().collect_optional_args(dispatcher, update, check_result)
if self.pattern: if self.pattern:
check_result = cast(Match, check_result) check_result = cast(Match, check_result)
@ -170,11 +185,13 @@ class InlineQueryHandler(Handler):
optional_args['groupdict'] = check_result.groupdict() optional_args['groupdict'] = check_result.groupdict()
return optional_args return optional_args
def collect_additional_context(self, def collect_additional_context(
context: 'CallbackContext', self,
update: HandlerArg, context: 'CallbackContext',
dispatcher: 'Dispatcher', update: HandlerArg,
check_result: Optional[Union[bool, Match]]) -> None: dispatcher: 'Dispatcher',
check_result: Optional[Union[bool, Match]],
) -> None:
if self.pattern: if self.pattern:
check_result = cast(Match, check_result) check_result = cast(Match, check_result)
context.matches = [check_result] context.matches = [check_result]

View file

@ -31,6 +31,7 @@ from telegram.ext.callbackcontext import CallbackContext
from typing import TYPE_CHECKING, Union, Callable, Tuple, Optional, List, Any, cast, overload from typing import TYPE_CHECKING, Union, Callable, Tuple, Optional, List, Any, cast, overload
from telegram.utils.types import JSONDict from telegram.utils.types import JSONDict
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram.ext import Dispatcher from telegram.ext import Dispatcher
from telegram import Bot from telegram import Bot
@ -56,8 +57,9 @@ class JobQueue:
self._dispatcher: 'Dispatcher' = None # type: ignore[assignment] self._dispatcher: 'Dispatcher' = None # type: ignore[assignment]
self.logger = logging.getLogger(self.__class__.__name__) self.logger = logging.getLogger(self.__class__.__name__)
self.scheduler = BackgroundScheduler(timezone=pytz.utc) self.scheduler = BackgroundScheduler(timezone=pytz.utc)
self.scheduler.add_listener(self._update_persistence, self.scheduler.add_listener(
mask=EVENT_JOB_EXECUTED | EVENT_JOB_ERROR) self._update_persistence, mask=EVENT_JOB_EXECUTED | EVENT_JOB_ERROR
)
# Dispatch errors and don't log them in the APS logger # Dispatch errors and don't log them in the APS logger
def aps_log_filter(record): # type: ignore def aps_log_filter(record): # type: ignore
@ -82,25 +84,29 @@ class JobQueue:
self._dispatcher.dispatch_error(None, event.exception) self._dispatcher.dispatch_error(None, event.exception)
# Errors should not stop the thread. # Errors should not stop the thread.
except Exception: except Exception:
self.logger.exception('An error was raised while processing the job and an ' self.logger.exception(
'uncaught error was raised while handling the error ' 'An error was raised while processing the job and an '
'with an error_handler.') 'uncaught error was raised while handling the error '
'with an error_handler.'
)
@overload @overload
def _parse_time_input(self, time: None, shift_day: bool = False) -> None: def _parse_time_input(self, time: None, shift_day: bool = False) -> None:
... ...
@overload @overload
def _parse_time_input(self, def _parse_time_input(
time: Union[float, int, datetime.timedelta, datetime.datetime, self,
datetime.time], time: Union[float, int, datetime.timedelta, datetime.datetime, datetime.time],
shift_day: bool = False) -> datetime.datetime: shift_day: bool = False,
) -> datetime.datetime:
... ...
def _parse_time_input(self, def _parse_time_input(
time: Union[float, int, datetime.timedelta, datetime.datetime, self,
datetime.time, None], time: Union[float, int, datetime.timedelta, datetime.datetime, datetime.time, None],
shift_day: bool = False) -> Optional[datetime.datetime]: shift_day: bool = False,
) -> Optional[datetime.datetime]:
if time is None: if time is None:
return None return None
if isinstance(time, (int, float)): if isinstance(time, (int, float)):
@ -109,7 +115,8 @@ class JobQueue:
return self._tz_now() + time return self._tz_now() + time
if isinstance(time, datetime.time): if isinstance(time, datetime.time):
dt = datetime.datetime.combine( dt = datetime.datetime.combine(
datetime.datetime.now(tz=time.tzinfo or self.scheduler.timezone).date(), time) datetime.datetime.now(tz=time.tzinfo or self.scheduler.timezone).date(), time
)
if dt.tzinfo is None: if dt.tzinfo is None:
dt = self.scheduler.timezone.localize(dt) dt = self.scheduler.timezone.localize(dt)
if shift_day and dt <= datetime.datetime.now(pytz.utc): if shift_day and dt <= datetime.datetime.now(pytz.utc):
@ -131,12 +138,14 @@ class JobQueue:
if dispatcher.bot.defaults: if dispatcher.bot.defaults:
self.scheduler.configure(timezone=dispatcher.bot.defaults.tzinfo or pytz.utc) self.scheduler.configure(timezone=dispatcher.bot.defaults.tzinfo or pytz.utc)
def run_once(self, def run_once(
callback: Callable[['CallbackContext'], None], self,
when: Union[float, datetime.timedelta, datetime.datetime, datetime.time], callback: Callable[['CallbackContext'], None],
context: object = None, when: Union[float, datetime.timedelta, datetime.datetime, datetime.time],
name: str = None, context: object = None,
job_kwargs: JSONDict = None) -> 'Job': name: str = None,
job_kwargs: JSONDict = None,
) -> 'Job':
"""Creates a new ``Job`` that runs once and adds it to the queue. """Creates a new ``Job`` that runs once and adds it to the queue.
Args: Args:
@ -183,27 +192,29 @@ class JobQueue:
job = Job(callback, context, name, self) job = Job(callback, context, name, self)
dt = self._parse_time_input(when, shift_day=True) dt = self._parse_time_input(when, shift_day=True)
j = self.scheduler.add_job(callback, j = self.scheduler.add_job(
name=name, callback,
trigger='date', name=name,
run_date=dt, trigger='date',
args=self._build_args(job), run_date=dt,
timezone=dt.tzinfo or self.scheduler.timezone, args=self._build_args(job),
**job_kwargs) timezone=dt.tzinfo or self.scheduler.timezone,
**job_kwargs,
)
job.job = j job.job = j
return job return job
def run_repeating(self, def run_repeating(
callback: Callable[['CallbackContext'], None], self,
interval: Union[float, datetime.timedelta], callback: Callable[['CallbackContext'], None],
first: Union[float, datetime.timedelta, datetime.datetime, interval: Union[float, datetime.timedelta],
datetime.time] = None, first: Union[float, datetime.timedelta, datetime.datetime, datetime.time] = None,
last: Union[float, datetime.timedelta, datetime.datetime, last: Union[float, datetime.timedelta, datetime.datetime, datetime.time] = None,
datetime.time] = None, context: object = None,
context: object = None, name: str = None,
name: str = None, job_kwargs: JSONDict = None,
job_kwargs: JSONDict = None) -> 'Job': ) -> 'Job':
"""Creates a new ``Job`` that runs at specified intervals and adds it to the queue. """Creates a new ``Job`` that runs at specified intervals and adds it to the queue.
Args: Args:
@ -277,26 +288,30 @@ class JobQueue:
if isinstance(interval, datetime.timedelta): if isinstance(interval, datetime.timedelta):
interval = interval.total_seconds() interval = interval.total_seconds()
j = self.scheduler.add_job(callback, j = self.scheduler.add_job(
trigger='interval', callback,
args=self._build_args(job), trigger='interval',
start_date=dt_first, args=self._build_args(job),
end_date=dt_last, start_date=dt_first,
seconds=interval, end_date=dt_last,
name=name, seconds=interval,
**job_kwargs) name=name,
**job_kwargs,
)
job.job = j job.job = j
return job return job
def run_monthly(self, def run_monthly(
callback: Callable[['CallbackContext'], None], self,
when: datetime.time, callback: Callable[['CallbackContext'], None],
day: int, when: datetime.time,
context: object = None, day: int,
name: str = None, context: object = None,
day_is_strict: bool = True, name: str = None,
job_kwargs: JSONDict = None) -> 'Job': day_is_strict: bool = True,
job_kwargs: JSONDict = None,
) -> 'Job':
"""Creates a new ``Job`` that runs on a monthly basis and adds it to the queue. """Creates a new ``Job`` that runs on a monthly basis and adds it to the queue.
Args: Args:
@ -332,45 +347,55 @@ class JobQueue:
job = Job(callback, context, name, self) job = Job(callback, context, name, self)
if day_is_strict: if day_is_strict:
j = self.scheduler.add_job(callback, j = self.scheduler.add_job(
trigger='cron', callback,
args=self._build_args(job), trigger='cron',
name=name, args=self._build_args(job),
day=day, name=name,
hour=when.hour, day=day,
minute=when.minute, hour=when.hour,
second=when.second, minute=when.minute,
timezone=when.tzinfo or self.scheduler.timezone, second=when.second,
**job_kwargs) timezone=when.tzinfo or self.scheduler.timezone,
**job_kwargs,
)
else: else:
trigger = OrTrigger([CronTrigger(day=day, trigger = OrTrigger(
hour=when.hour, [
minute=when.minute, CronTrigger(
second=when.second, day=day,
timezone=when.tzinfo, hour=when.hour,
**job_kwargs), minute=when.minute,
CronTrigger(day='last', second=when.second,
hour=when.hour, timezone=when.tzinfo,
minute=when.minute, **job_kwargs,
second=when.second, ),
timezone=when.tzinfo or self.scheduler.timezone, CronTrigger(
**job_kwargs)]) day='last',
j = self.scheduler.add_job(callback, hour=when.hour,
trigger=trigger, minute=when.minute,
args=self._build_args(job), second=when.second,
name=name, timezone=when.tzinfo or self.scheduler.timezone,
**job_kwargs) **job_kwargs,
),
]
)
j = self.scheduler.add_job(
callback, trigger=trigger, args=self._build_args(job), name=name, **job_kwargs
)
job.job = j job.job = j
return job return job
def run_daily(self, def run_daily(
callback: Callable[['CallbackContext'], None], self,
time: datetime.time, callback: Callable[['CallbackContext'], None],
days: Tuple[int, ...] = Days.EVERY_DAY, time: datetime.time,
context: object = None, days: Tuple[int, ...] = Days.EVERY_DAY,
name: str = None, context: object = None,
job_kwargs: JSONDict = None) -> 'Job': name: str = None,
job_kwargs: JSONDict = None,
) -> 'Job':
"""Creates a new ``Job`` that runs on a daily basis and adds it to the queue. """Creates a new ``Job`` that runs on a daily basis and adds it to the queue.
Args: Args:
@ -409,25 +434,29 @@ class JobQueue:
name = name or callback.__name__ name = name or callback.__name__
job = Job(callback, context, name, self) job = Job(callback, context, name, self)
j = self.scheduler.add_job(callback, j = self.scheduler.add_job(
name=name, callback,
args=self._build_args(job), name=name,
trigger='cron', args=self._build_args(job),
day_of_week=','.join([str(d) for d in days]), trigger='cron',
hour=time.hour, day_of_week=','.join([str(d) for d in days]),
minute=time.minute, hour=time.hour,
second=time.second, minute=time.minute,
timezone=time.tzinfo or self.scheduler.timezone, second=time.second,
**job_kwargs) timezone=time.tzinfo or self.scheduler.timezone,
**job_kwargs,
)
job.job = j job.job = j
return job return job
def run_custom(self, def run_custom(
callback: Callable[['CallbackContext'], None], self,
job_kwargs: JSONDict, callback: Callable[['CallbackContext'], None],
context: object = None, job_kwargs: JSONDict,
name: str = None) -> 'Job': context: object = None,
name: str = None,
) -> 'Job':
"""Creates a new customly defined ``Job``. """Creates a new customly defined ``Job``.
Args: Args:
@ -453,10 +482,7 @@ class JobQueue:
name = name or callback.__name__ name = name or callback.__name__
job = Job(callback, context, name, self) job = Job(callback, context, name, self)
j = self.scheduler.add_job(callback, j = self.scheduler.add_job(callback, args=self._build_args(job), name=name, **job_kwargs)
args=self._build_args(job),
name=name,
**job_kwargs)
job.job = j job.job = j
return job return job
@ -516,12 +542,14 @@ class Job:
job (:class:`apscheduler.job.Job`, optional): The APS Job this job is a wrapper for. job (:class:`apscheduler.job.Job`, optional): The APS Job this job is a wrapper for.
""" """
def __init__(self, def __init__(
callback: Callable[['CallbackContext'], None], self,
context: object = None, callback: Callable[['CallbackContext'], None],
name: str = None, context: object = None,
job_queue: JobQueue = None, name: str = None,
job: 'Job' = None): job_queue: JobQueue = None,
job: 'Job' = None,
):
self.callback = callback self.callback = callback
self.context = context self.context = context
@ -545,9 +573,11 @@ class Job:
dispatcher.dispatch_error(None, e) dispatcher.dispatch_error(None, e)
# Errors should not stop the thread. # Errors should not stop the thread.
except Exception: except Exception:
dispatcher.logger.exception('An error was raised while processing the job and an ' dispatcher.logger.exception(
'uncaught error was raised while handling the error ' 'An error was raised while processing the job and an '
'with an error_handler.') 'uncaught error was raised while handling the error '
'with an error_handler.'
)
def schedule_removal(self) -> None: def schedule_removal(self) -> None:
""" """

View file

@ -28,6 +28,7 @@ from .handler import Handler
from telegram.utils.types import HandlerArg from telegram.utils.types import HandlerArg
from typing import Callable, TYPE_CHECKING, Any, Optional, Union, TypeVar, Dict from typing import Callable, TYPE_CHECKING, Any, Optional, Union, TypeVar, Dict
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram.ext import CallbackContext, Dispatcher from telegram.ext import CallbackContext, Dispatcher
@ -120,17 +121,19 @@ class MessageHandler(Handler):
""" """
def __init__(self, def __init__(
filters: BaseFilter, self,
callback: Callable[[HandlerArg, 'CallbackContext'], RT], filters: BaseFilter,
pass_update_queue: bool = False, callback: Callable[[HandlerArg, 'CallbackContext'], RT],
pass_job_queue: bool = False, pass_update_queue: bool = False,
pass_user_data: bool = False, pass_job_queue: bool = False,
pass_chat_data: bool = False, pass_user_data: bool = False,
message_updates: bool = None, pass_chat_data: bool = False,
channel_post_updates: bool = None, message_updates: bool = None,
edited_updates: bool = None, channel_post_updates: bool = None,
run_async: bool = False): edited_updates: bool = None,
run_async: bool = False,
):
super().__init__( super().__init__(
callback, callback,
@ -138,36 +141,44 @@ class MessageHandler(Handler):
pass_job_queue=pass_job_queue, pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data, pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data, pass_chat_data=pass_chat_data,
run_async=run_async) run_async=run_async,
)
if message_updates is False and channel_post_updates is False and edited_updates is False: if message_updates is False and channel_post_updates is False and edited_updates is False:
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'
)
if filters is not None: if filters is not None:
self.filters = Filters.update & filters self.filters = Filters.update & filters
else: else:
self.filters = Filters.update self.filters = Filters.update
if message_updates is not None: if message_updates is not None:
warnings.warn('message_updates is deprecated. See https://git.io/fxJuV for more info', warnings.warn(
TelegramDeprecationWarning, 'message_updates is deprecated. See https://git.io/fxJuV for more info',
stacklevel=2) TelegramDeprecationWarning,
stacklevel=2,
)
if message_updates is False: if message_updates is False:
self.filters &= ~Filters.update.message self.filters &= ~Filters.update.message
if channel_post_updates is not None: if channel_post_updates is not None:
warnings.warn('channel_post_updates is deprecated. See https://git.io/fxJuV ' warnings.warn(
'for more info', 'channel_post_updates is deprecated. See https://git.io/fxJuV ' 'for more info',
TelegramDeprecationWarning, TelegramDeprecationWarning,
stacklevel=2) stacklevel=2,
)
if channel_post_updates is False: if channel_post_updates is False:
self.filters &= ~Filters.update.channel_post self.filters &= ~Filters.update.channel_post
if edited_updates is not None: if edited_updates is not None:
warnings.warn('edited_updates is deprecated. See https://git.io/fxJuV for more info', warnings.warn(
TelegramDeprecationWarning, 'edited_updates is deprecated. See https://git.io/fxJuV for more info',
stacklevel=2) TelegramDeprecationWarning,
stacklevel=2,
)
if edited_updates is False: if edited_updates is False:
self.filters &= ~(Filters.update.edited_message self.filters &= ~(
| Filters.update.edited_channel_post) Filters.update.edited_message | Filters.update.edited_channel_post
)
def check_update(self, update: HandlerArg) -> Optional[Union[bool, Dict[str, Any]]]: def check_update(self, update: HandlerArg) -> Optional[Union[bool, Dict[str, Any]]]:
"""Determines whether an update should be passed to this handlers :attr:`callback`. """Determines whether an update should be passed to this handlers :attr:`callback`.
@ -183,10 +194,12 @@ class MessageHandler(Handler):
return self.filters(update) return self.filters(update)
return None return None
def collect_additional_context(self, def collect_additional_context(
context: 'CallbackContext', self,
update: HandlerArg, context: 'CallbackContext',
dispatcher: 'Dispatcher', update: HandlerArg,
check_result: Optional[Union[bool, Dict[str, Any]]]) -> None: dispatcher: 'Dispatcher',
check_result: Optional[Union[bool, Dict[str, Any]]],
) -> None:
if isinstance(check_result, dict): if isinstance(check_result, dict):
context.update(check_result) context.update(check_result)

View file

@ -38,6 +38,7 @@ curtime = time.perf_counter
class DelayQueueError(RuntimeError): class DelayQueueError(RuntimeError):
"""Indicates processing errors.""" """Indicates processing errors."""
pass pass
@ -75,17 +76,19 @@ class DelayQueue(threading.Thread):
_instcnt = 0 # instance counter _instcnt = 0 # instance counter
def __init__(self, def __init__(
queue: q.Queue = None, self,
burst_limit: int = 30, queue: q.Queue = None,
time_limit_ms: int = 1000, burst_limit: int = 30,
exc_route: Callable[[Exception], None] = None, time_limit_ms: int = 1000,
autostart: bool = True, exc_route: Callable[[Exception], None] = None,
name: str = None): autostart: bool = True,
name: str = None,
):
self._queue = queue if queue is not None else q.Queue() self._queue = queue if queue is not None else q.Queue()
self.burst_limit = burst_limit self.burst_limit = burst_limit
self.time_limit = time_limit_ms / 1000 self.time_limit = time_limit_ms / 1000
self.exc_route = (exc_route if exc_route is not None else self._default_exception_handler) self.exc_route = exc_route if exc_route is not None else self._default_exception_handler
self.__exit_req = False # flag to gently exit thread self.__exit_req = False # flag to gently exit thread
self.__class__._instcnt += 1 self.__class__._instcnt += 1
if name is None: if name is None:
@ -201,24 +204,28 @@ class MessageQueue:
""" """
def __init__(self, def __init__(
all_burst_limit: int = 30, self,
all_time_limit_ms: int = 1000, all_burst_limit: int = 30,
group_burst_limit: int = 20, all_time_limit_ms: int = 1000,
group_time_limit_ms: int = 60000, group_burst_limit: int = 20,
exc_route: Callable[[Exception], None] = None, group_time_limit_ms: int = 60000,
autostart: bool = True): exc_route: Callable[[Exception], None] = None,
autostart: bool = True,
):
# create according delay queues, use composition # create according delay queues, use composition
self._all_delayq = DelayQueue( self._all_delayq = DelayQueue(
burst_limit=all_burst_limit, burst_limit=all_burst_limit,
time_limit_ms=all_time_limit_ms, time_limit_ms=all_time_limit_ms,
exc_route=exc_route, exc_route=exc_route,
autostart=autostart) autostart=autostart,
)
self._group_delayq = DelayQueue( self._group_delayq = DelayQueue(
burst_limit=group_burst_limit, burst_limit=group_burst_limit,
time_limit_ms=group_time_limit_ms, time_limit_ms=group_time_limit_ms,
exc_route=exc_route, exc_route=exc_route,
autostart=autostart) autostart=autostart,
)
def start(self) -> None: def start(self) -> None:
"""Method is used to manually start the ``MessageQueue`` processing.""" """Method is used to manually start the ``MessageQueue`` processing."""
@ -297,11 +304,12 @@ def queuedmessage(method: Callable) -> Callable:
@functools.wraps(method) @functools.wraps(method)
def wrapped(self: 'Bot', *args: Any, **kwargs: Any) -> Any: def wrapped(self: 'Bot', *args: Any, **kwargs: Any) -> Any:
queued = kwargs.pop('queued', queued = kwargs.pop(
self._is_messages_queued_default) # type: ignore[attr-defined] 'queued', self._is_messages_queued_default # type: ignore[attr-defined]
)
isgroup = kwargs.pop('isgroup', False) isgroup = kwargs.pop('isgroup', False)
if queued: if queued:
prom = promise.Promise(method, (self, ) + args, kwargs) prom = promise.Promise(method, (self,) + args, kwargs)
return self._msg_queue(prom, isgroup) # type: ignore[attr-defined] return self._msg_queue(prom, isgroup) # type: ignore[attr-defined]
return method(self, *args, **kwargs) return method(self, *args, **kwargs)

View file

@ -74,16 +74,20 @@ class PicklePersistence(BasePersistence):
Default is :obj:`False`. Default is :obj:`False`.
""" """
def __init__(self, def __init__(
filename: str, self,
store_user_data: bool = True, filename: str,
store_chat_data: bool = True, store_user_data: bool = True,
store_bot_data: bool = True, store_chat_data: bool = True,
single_file: bool = True, store_bot_data: bool = True,
on_flush: bool = False): single_file: bool = True,
super().__init__(store_user_data=store_user_data, on_flush: bool = False,
store_chat_data=store_chat_data, ):
store_bot_data=store_bot_data) super().__init__(
store_user_data=store_user_data,
store_chat_data=store_chat_data,
store_bot_data=store_bot_data,
)
self.filename = filename self.filename = filename
self.single_file = single_file self.single_file = single_file
self.on_flush = on_flush self.on_flush = on_flush
@ -125,8 +129,12 @@ class PicklePersistence(BasePersistence):
def dump_singlefile(self) -> None: def dump_singlefile(self) -> None:
with open(self.filename, "wb") as f: with open(self.filename, "wb") as f:
data = {'conversations': self.conversations, 'user_data': self.user_data, data = {
'chat_data': self.chat_data, 'bot_data': self.bot_data} 'conversations': self.conversations,
'user_data': self.user_data,
'chat_data': self.chat_data,
'bot_data': self.bot_data,
}
pickle.dump(data, f) pickle.dump(data, f)
def dump_file(self, filename: str, data: Any) -> None: def dump_file(self, filename: str, data: Any) -> None:
@ -212,9 +220,9 @@ class PicklePersistence(BasePersistence):
self.load_singlefile() self.load_singlefile()
return self.conversations.get(name, {}).copy() # type: ignore[union-attr] return self.conversations.get(name, {}).copy() # type: ignore[union-attr]
def update_conversation(self, def update_conversation(
name: str, key: Tuple[int, ...], self, name: str, key: Tuple[int, ...], new_state: Optional[object]
new_state: Optional[object]) -> None: ) -> None:
"""Will update the conversations for the given handler and depending on :attr:`on_flush` """Will update the conversations for the given handler and depending on :attr:`on_flush`
save the pickle file. save the pickle file.
@ -290,8 +298,7 @@ class PicklePersistence(BasePersistence):
self.dump_singlefile() self.dump_singlefile()
def flush(self) -> None: def flush(self) -> None:
""" Will save all data in memory to pickle file(s). """Will save all data in memory to pickle file(s)."""
"""
if self.single_file: if self.single_file:
if self.user_data or self.chat_data or self.bot_data or self.conversations: if self.user_data or self.chat_data or self.bot_data or self.conversations:
self.dump_singlefile() self.dump_singlefile()

View file

@ -27,6 +27,7 @@ from telegram.ext import MessageHandler, Filters
from telegram.utils.types import HandlerArg from telegram.utils.types import HandlerArg
from typing import Callable, TYPE_CHECKING, Any, Optional, Union, TypeVar, Dict, Pattern from typing import Callable, TYPE_CHECKING, Any, Optional, Union, TypeVar, Dict, Pattern
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram.ext import CallbackContext, Dispatcher from telegram.ext import CallbackContext, Dispatcher
@ -108,41 +109,48 @@ class RegexHandler(MessageHandler):
""" """
def __init__(self, def __init__(
pattern: Union[str, Pattern], self,
callback: Callable[[HandlerArg, 'CallbackContext'], RT], pattern: Union[str, Pattern],
pass_groups: bool = False, callback: Callable[[HandlerArg, 'CallbackContext'], RT],
pass_groupdict: bool = False, pass_groups: bool = False,
pass_update_queue: bool = False, pass_groupdict: bool = False,
pass_job_queue: bool = False, pass_update_queue: bool = False,
pass_user_data: bool = False, pass_job_queue: bool = False,
pass_chat_data: bool = False, pass_user_data: bool = False,
allow_edited: bool = False, pass_chat_data: bool = False,
message_updates: bool = True, allow_edited: bool = False,
channel_post_updates: bool = False, message_updates: bool = True,
edited_updates: bool = False, channel_post_updates: bool = False,
run_async: bool = False): edited_updates: bool = False,
warnings.warn('RegexHandler is deprecated. See https://git.io/fxJuV for more info', run_async: bool = False,
TelegramDeprecationWarning, ):
stacklevel=2) warnings.warn(
super().__init__(Filters.regex(pattern), 'RegexHandler is deprecated. See https://git.io/fxJuV for more info',
callback, TelegramDeprecationWarning,
pass_update_queue=pass_update_queue, stacklevel=2,
pass_job_queue=pass_job_queue, )
pass_user_data=pass_user_data, super().__init__(
pass_chat_data=pass_chat_data, Filters.regex(pattern),
message_updates=message_updates, callback,
channel_post_updates=channel_post_updates, pass_update_queue=pass_update_queue,
edited_updates=edited_updates, pass_job_queue=pass_job_queue,
run_async=run_async) pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data,
message_updates=message_updates,
channel_post_updates=channel_post_updates,
edited_updates=edited_updates,
run_async=run_async,
)
self.pass_groups = pass_groups self.pass_groups = pass_groups
self.pass_groupdict = pass_groupdict self.pass_groupdict = pass_groupdict
def collect_optional_args( def collect_optional_args(
self, self,
dispatcher: 'Dispatcher', dispatcher: 'Dispatcher',
update: HandlerArg = None, update: HandlerArg = None,
check_result: Optional[Union[bool, Dict[str, Any]]] = None) -> Dict[str, Any]: check_result: Optional[Union[bool, Dict[str, Any]]] = None,
) -> Dict[str, Any]:
optional_args = super().collect_optional_args(dispatcher, update, check_result) optional_args = super().collect_optional_args(dispatcher, update, check_result)
if isinstance(check_result, dict): if isinstance(check_result, dict):
if self.pass_groups: if self.pass_groups:

View file

@ -22,6 +22,7 @@ from .handler import Handler
from telegram.utils.types import HandlerArg from telegram.utils.types import HandlerArg
from typing import Callable, TYPE_CHECKING, Any, Optional, TypeVar, Dict, List from typing import Callable, TYPE_CHECKING, Any, Optional, TypeVar, Dict, List
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram.ext import CallbackContext, Dispatcher from telegram.ext import CallbackContext, Dispatcher
@ -80,18 +81,21 @@ class StringCommandHandler(Handler):
""" """
def __init__(self, def __init__(
command: str, self,
callback: Callable[[HandlerArg, 'CallbackContext'], RT], command: str,
pass_args: bool = False, callback: Callable[[HandlerArg, 'CallbackContext'], RT],
pass_update_queue: bool = False, pass_args: bool = False,
pass_job_queue: bool = False, pass_update_queue: bool = False,
run_async: bool = False): pass_job_queue: bool = False,
run_async: bool = False,
):
super().__init__( super().__init__(
callback, callback,
pass_update_queue=pass_update_queue, pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue, pass_job_queue=pass_job_queue,
run_async=run_async) run_async=run_async,
)
self.command = command self.command = command
self.pass_args = pass_args self.pass_args = pass_args
@ -111,18 +115,22 @@ class StringCommandHandler(Handler):
return args[1:] return args[1:]
return None return None
def collect_optional_args(self, def collect_optional_args(
dispatcher: 'Dispatcher', self,
update: HandlerArg = None, dispatcher: 'Dispatcher',
check_result: Optional[List[str]] = None) -> Dict[str, Any]: update: HandlerArg = None,
check_result: Optional[List[str]] = None,
) -> Dict[str, Any]:
optional_args = super().collect_optional_args(dispatcher, update, check_result) optional_args = super().collect_optional_args(dispatcher, update, check_result)
if self.pass_args: if self.pass_args:
optional_args['args'] = check_result optional_args['args'] = check_result
return optional_args return optional_args
def collect_additional_context(self, def collect_additional_context(
context: 'CallbackContext', self,
update: HandlerArg, context: 'CallbackContext',
dispatcher: 'Dispatcher', update: HandlerArg,
check_result: Optional[List[str]]) -> None: dispatcher: 'Dispatcher',
check_result: Optional[List[str]],
) -> None:
context.args = check_result context.args = check_result

View file

@ -24,6 +24,7 @@ from .handler import Handler
from typing import Callable, TYPE_CHECKING, Optional, TypeVar, Match, Dict, Any, Union, Pattern from typing import Callable, TYPE_CHECKING, Optional, TypeVar, Match, Dict, Any, Union, Pattern
from telegram.utils.types import HandlerArg from telegram.utils.types import HandlerArg
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram.ext import CallbackContext, Dispatcher from telegram.ext import CallbackContext, Dispatcher
@ -90,19 +91,22 @@ class StringRegexHandler(Handler):
""" """
def __init__(self, def __init__(
pattern: Union[str, Pattern], self,
callback: Callable[[HandlerArg, 'CallbackContext'], RT], pattern: Union[str, Pattern],
pass_groups: bool = False, callback: Callable[[HandlerArg, 'CallbackContext'], RT],
pass_groupdict: bool = False, pass_groups: bool = False,
pass_update_queue: bool = False, pass_groupdict: bool = False,
pass_job_queue: bool = False, pass_update_queue: bool = False,
run_async: bool = False): pass_job_queue: bool = False,
run_async: bool = False,
):
super().__init__( super().__init__(
callback, callback,
pass_update_queue=pass_update_queue, pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue, pass_job_queue=pass_job_queue,
run_async=run_async) run_async=run_async,
)
if isinstance(pattern, str): if isinstance(pattern, str):
pattern = re.compile(pattern) pattern = re.compile(pattern)
@ -127,10 +131,12 @@ class StringRegexHandler(Handler):
return match return match
return None return None
def collect_optional_args(self, def collect_optional_args(
dispatcher: 'Dispatcher', self,
update: HandlerArg = None, dispatcher: 'Dispatcher',
check_result: Optional[Match] = None) -> Dict[str, Any]: update: HandlerArg = None,
check_result: Optional[Match] = None,
) -> Dict[str, Any]:
optional_args = super().collect_optional_args(dispatcher, update, check_result) optional_args = super().collect_optional_args(dispatcher, update, check_result)
if self.pattern: if self.pattern:
if self.pass_groups and check_result: if self.pass_groups and check_result:
@ -139,10 +145,12 @@ class StringRegexHandler(Handler):
optional_args['groupdict'] = check_result.groupdict() optional_args['groupdict'] = check_result.groupdict()
return optional_args return optional_args
def collect_additional_context(self, def collect_additional_context(
context: 'CallbackContext', self,
update: HandlerArg, context: 'CallbackContext',
dispatcher: 'Dispatcher', update: HandlerArg,
check_result: Optional[Match]) -> None: dispatcher: 'Dispatcher',
check_result: Optional[Match],
) -> None:
if self.pattern and check_result: if self.pattern and check_result:
context.matches = [check_result] context.matches = [check_result]

View file

@ -74,18 +74,21 @@ class TypeHandler(Handler):
""" """
def __init__(self, def __init__(
type: Type, self,
callback: Callable[[Any, 'CallbackContext'], RT], type: Type,
strict: bool = False, callback: Callable[[Any, 'CallbackContext'], RT],
pass_update_queue: bool = False, strict: bool = False,
pass_job_queue: bool = False, pass_update_queue: bool = False,
run_async: bool = False): pass_job_queue: bool = False,
run_async: bool = False,
):
super().__init__( super().__init__(
callback, callback,
pass_update_queue=pass_update_queue, pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue, pass_job_queue=pass_job_queue,
run_async=run_async) run_async=run_async,
)
self.type = type self.type = type
self.strict = strict self.strict = strict

View file

@ -32,7 +32,7 @@ from telegram.error import Unauthorized, InvalidToken, RetryAfter, TimedOut
from telegram.utils.deprecate import TelegramDeprecationWarning from telegram.utils.deprecate import TelegramDeprecationWarning
from telegram.utils.helpers import get_signal_name from telegram.utils.helpers import get_signal_name
from telegram.utils.request import Request from telegram.utils.request import Request
from telegram.utils.webhookhandler import (WebhookServer, WebhookAppClass) from telegram.utils.webhookhandler import WebhookServer, WebhookAppClass
from typing import Callable, Dict, TYPE_CHECKING, Any, List, Union, Tuple, no_type_check, Optional from typing import Callable, Dict, TYPE_CHECKING, Any, List, Union, Tuple, no_type_check, Optional
@ -108,26 +108,30 @@ class Updater:
_request = None _request = None
def __init__(self, def __init__(
token: str = None, self,
base_url: str = None, token: str = None,
workers: int = 4, base_url: str = None,
bot: Bot = None, workers: int = 4,
private_key: bytes = None, bot: Bot = None,
private_key_password: bytes = None, private_key: bytes = None,
user_sig_handler: Callable = None, private_key_password: bytes = None,
request_kwargs: Dict[str, Any] = None, user_sig_handler: Callable = None,
persistence: 'BasePersistence' = None, request_kwargs: Dict[str, Any] = None,
defaults: 'Defaults' = None, persistence: 'BasePersistence' = None,
use_context: bool = True, defaults: 'Defaults' = None,
dispatcher: Dispatcher = None, use_context: bool = True,
base_file_url: str = None): dispatcher: Dispatcher = None,
base_file_url: str = None,
):
if defaults and bot: if defaults and bot:
warnings.warn('Passing defaults to an Updater has no effect when a Bot is passed ' warnings.warn(
'as well. Pass them to the Bot instead.', 'Passing defaults to an Updater has no effect when a Bot is passed '
TelegramDeprecationWarning, 'as well. Pass them to the Bot instead.',
stacklevel=2) TelegramDeprecationWarning,
stacklevel=2,
)
if dispatcher is None: if dispatcher is None:
if (token is None) and (bot is None): if (token is None) and (bot is None):
@ -156,7 +160,8 @@ class Updater:
if bot.request.con_pool_size < con_pool_size: if bot.request.con_pool_size < con_pool_size:
self.logger.warning( self.logger.warning(
'Connection pool of Request object is smaller than optimal value (%s)', 'Connection pool of Request object is smaller than optimal value (%s)',
con_pool_size) con_pool_size,
)
else: else:
# we need a connection pool the size of: # we need a connection pool the size of:
# * for each of the workers # * for each of the workers
@ -169,24 +174,28 @@ class Updater:
if 'con_pool_size' not in request_kwargs: if 'con_pool_size' not in request_kwargs:
request_kwargs['con_pool_size'] = con_pool_size request_kwargs['con_pool_size'] = con_pool_size
self._request = Request(**request_kwargs) self._request = Request(**request_kwargs)
self.bot = Bot(token, # type: ignore[arg-type] self.bot = Bot(
base_url, token, # type: ignore[arg-type]
base_file_url=base_file_url, base_url,
request=self._request, base_file_url=base_file_url,
private_key=private_key, request=self._request,
private_key_password=private_key_password, private_key=private_key,
defaults=defaults) private_key_password=private_key_password,
defaults=defaults,
)
self.update_queue: Queue = Queue() self.update_queue: Queue = Queue()
self.job_queue = JobQueue() self.job_queue = JobQueue()
self.__exception_event = Event() self.__exception_event = Event()
self.persistence = persistence self.persistence = persistence
self.dispatcher = Dispatcher(self.bot, self.dispatcher = Dispatcher(
self.update_queue, self.bot,
job_queue=self.job_queue, self.update_queue,
workers=workers, job_queue=self.job_queue,
exception_event=self.__exception_event, workers=workers,
persistence=persistence, exception_event=self.__exception_event,
use_context=use_context) persistence=persistence,
use_context=use_context,
)
self.job_queue.set_dispatcher(self.dispatcher) self.job_queue.set_dispatcher(self.dispatcher)
else: else:
con_pool_size = dispatcher.workers + 4 con_pool_size = dispatcher.workers + 4
@ -195,7 +204,8 @@ class Updater:
if self.bot.request.con_pool_size < con_pool_size: if self.bot.request.con_pool_size < con_pool_size:
self.logger.warning( self.logger.warning(
'Connection pool of Request object is smaller than optimal value (%s)', 'Connection pool of Request object is smaller than optimal value (%s)',
con_pool_size) con_pool_size,
)
self.update_queue = dispatcher.update_queue self.update_queue = dispatcher.update_queue
self.__exception_event = dispatcher.exception_event self.__exception_event = dispatcher.exception_event
self.persistence = dispatcher.persistence self.persistence = dispatcher.persistence
@ -211,10 +221,12 @@ class Updater:
self.__threads: List[Thread] = [] self.__threads: List[Thread] = []
def _init_thread(self, target: Callable, name: str, *args: Any, **kwargs: Any) -> None: def _init_thread(self, target: Callable, name: str, *args: Any, **kwargs: Any) -> None:
thr = Thread(target=self._thread_wrapper, thr = Thread(
name="Bot:{}:{}".format(self.bot.id, name), target=self._thread_wrapper,
args=(target,) + args, name="Bot:{}:{}".format(self.bot.id, name),
kwargs=kwargs) args=(target,) + args,
kwargs=kwargs,
)
thr.start() thr.start()
self.__threads.append(thr) self.__threads.append(thr)
@ -229,13 +241,15 @@ class Updater:
raise raise
self.logger.debug('{} - ended'.format(thr_name)) self.logger.debug('{} - ended'.format(thr_name))
def start_polling(self, def start_polling(
poll_interval: float = 0.0, self,
timeout: float = 10, poll_interval: float = 0.0,
clean: bool = False, timeout: float = 10,
bootstrap_retries: int = -1, clean: bool = False,
read_latency: float = 2., bootstrap_retries: int = -1,
allowed_updates: List[str] = None) -> Optional[Queue]: read_latency: float = 2.0,
allowed_updates: List[str] = None,
) -> Optional[Queue]:
"""Starts polling updates from Telegram. """Starts polling updates from Telegram.
Args: Args:
@ -270,9 +284,17 @@ class Updater:
dispatcher_ready = Event() dispatcher_ready = Event()
polling_ready = Event() polling_ready = Event()
self._init_thread(self.dispatcher.start, "dispatcher", ready=dispatcher_ready) self._init_thread(self.dispatcher.start, "dispatcher", ready=dispatcher_ready)
self._init_thread(self._start_polling, "updater", poll_interval, timeout, self._init_thread(
read_latency, bootstrap_retries, clean, allowed_updates, self._start_polling,
ready=polling_ready) "updater",
poll_interval,
timeout,
read_latency,
bootstrap_retries,
clean,
allowed_updates,
ready=polling_ready,
)
self.logger.debug('Waiting for Dispatcher and polling to start') self.logger.debug('Waiting for Dispatcher and polling to start')
dispatcher_ready.wait() dispatcher_ready.wait()
@ -282,17 +304,19 @@ class Updater:
return self.update_queue return self.update_queue
return None return None
def start_webhook(self, def start_webhook(
listen: str = '127.0.0.1', self,
port: int = 80, listen: str = '127.0.0.1',
url_path: str = '', port: int = 80,
cert: str = None, url_path: str = '',
key: str = None, cert: str = None,
clean: bool = False, key: str = None,
bootstrap_retries: int = 0, clean: bool = False,
webhook_url: str = None, bootstrap_retries: int = 0,
allowed_updates: List[str] = None, webhook_url: str = None,
force_event_loop: bool = False) -> Optional[Queue]: allowed_updates: List[str] = None,
force_event_loop: bool = False,
) -> Optional[Queue]:
""" """
Starts a small http server to listen for updates via webhook. If cert Starts a small http server to listen for updates via webhook. If cert
and key are not provided, the webhook will be started directly on and key are not provided, the webhook will be started directly on
@ -344,9 +368,21 @@ class Updater:
dispatcher_ready = Event() dispatcher_ready = Event()
self.job_queue.start() self.job_queue.start()
self._init_thread(self.dispatcher.start, "dispatcher", dispatcher_ready) self._init_thread(self.dispatcher.start, "dispatcher", dispatcher_ready)
self._init_thread(self._start_webhook, "updater", listen, port, url_path, cert, self._init_thread(
key, bootstrap_retries, clean, webhook_url, allowed_updates, self._start_webhook,
ready=webhook_ready, force_event_loop=force_event_loop) "updater",
listen,
port,
url_path,
cert,
key,
bootstrap_retries,
clean,
webhook_url,
allowed_updates,
ready=webhook_ready,
force_event_loop=force_event_loop,
)
self.logger.debug('Waiting for Dispatcher and Webhook to start') self.logger.debug('Waiting for Dispatcher and Webhook to start')
webhook_ready.wait() webhook_ready.wait()
@ -357,8 +393,16 @@ class Updater:
return None return None
@no_type_check @no_type_check
def _start_polling(self, poll_interval, timeout, read_latency, bootstrap_retries, clean, def _start_polling(
allowed_updates, ready=None): # pragma: no cover self,
poll_interval,
timeout,
read_latency,
bootstrap_retries,
clean,
allowed_updates,
ready=None,
): # pragma: no cover
# Thread target of thread 'updater'. Runs in background, pulls # Thread target of thread 'updater'. Runs in background, pulls
# updates from Telegram and inserts them in the update queue of the # updates from Telegram and inserts them in the update queue of the
# Dispatcher. # Dispatcher.
@ -370,10 +414,12 @@ class Updater:
self.logger.debug('Bootstrap done') self.logger.debug('Bootstrap done')
def polling_action_cb(): def polling_action_cb():
updates = self.bot.get_updates(self.last_update_id, updates = self.bot.get_updates(
timeout=timeout, self.last_update_id,
read_latency=read_latency, timeout=timeout,
allowed_updates=allowed_updates) read_latency=read_latency,
allowed_updates=allowed_updates,
)
if updates: if updates:
if not self.running: if not self.running:
@ -393,8 +439,9 @@ class Updater:
if ready is not None: if ready is not None:
ready.set() ready.set()
self._network_loop_retry(polling_action_cb, polling_onerr_cb, 'getting Updates', self._network_loop_retry(
poll_interval) polling_action_cb, polling_onerr_cb, 'getting Updates', poll_interval
)
@no_type_check @no_type_check
def _network_loop_retry(self, action_cb, onerr_cb, description, interval): def _network_loop_retry(self, action_cb, onerr_cb, description, interval):
@ -450,8 +497,20 @@ class Updater:
return current_interval return current_interval
@no_type_check @no_type_check
def _start_webhook(self, listen, port, url_path, cert, key, bootstrap_retries, clean, def _start_webhook(
webhook_url, allowed_updates, ready=None, force_event_loop=False): self,
listen,
port,
url_path,
cert,
key,
bootstrap_retries,
clean,
webhook_url,
allowed_updates,
ready=None,
force_event_loop=False,
):
self.logger.debug('Updater thread started (webhook)') self.logger.debug('Updater thread started (webhook)')
use_ssl = cert is not None and key is not None use_ssl = cert is not None and key is not None
if not url_path.startswith('/'): if not url_path.startswith('/'):
@ -479,14 +538,18 @@ class Updater:
if not webhook_url: if not webhook_url:
webhook_url = self._gen_webhook_url(listen, port, url_path) webhook_url = self._gen_webhook_url(listen, port, url_path)
self._bootstrap(max_retries=bootstrap_retries, self._bootstrap(
clean=clean, max_retries=bootstrap_retries,
webhook_url=webhook_url, clean=clean,
cert=open(cert, 'rb'), webhook_url=webhook_url,
allowed_updates=allowed_updates) cert=open(cert, 'rb'),
allowed_updates=allowed_updates,
)
elif clean: elif clean:
self.logger.warning("cleaning updates is not supported if " self.logger.warning(
"SSL-termination happens elsewhere; skipping") "cleaning updates is not supported if "
"SSL-termination happens elsewhere; skipping"
)
self.httpd.serve_forever(force_event_loop=force_event_loop, ready=ready) self.httpd.serve_forever(force_event_loop=force_event_loop, ready=ready)
@ -495,13 +558,9 @@ class Updater:
return 'https://{listen}:{port}{path}'.format(listen=listen, port=port, path=url_path) return 'https://{listen}:{port}{path}'.format(listen=listen, port=port, path=url_path)
@no_type_check @no_type_check
def _bootstrap(self, def _bootstrap(
max_retries, self, max_retries, clean, webhook_url, allowed_updates, cert=None, bootstrap_interval=5
clean, ):
webhook_url,
allowed_updates,
cert=None,
bootstrap_interval=5):
retries = [0] retries = [0]
def bootstrap_del_webhook(): def bootstrap_del_webhook():
@ -516,16 +575,17 @@ class Updater:
return False return False
def bootstrap_set_webhook(): def bootstrap_set_webhook():
self.bot.set_webhook(url=webhook_url, self.bot.set_webhook(
certificate=cert, url=webhook_url, certificate=cert, allowed_updates=allowed_updates
allowed_updates=allowed_updates) )
return False return False
def bootstrap_onerr_cb(exc): def bootstrap_onerr_cb(exc):
if not isinstance(exc, Unauthorized) and (max_retries < 0 or retries[0] < max_retries): if not isinstance(exc, Unauthorized) and (max_retries < 0 or retries[0] < max_retries):
retries[0] += 1 retries[0] += 1
self.logger.warning('Failed bootstrap phase; try=%s max_retries=%s', retries[0], self.logger.warning(
max_retries) 'Failed bootstrap phase; try=%s max_retries=%s', retries[0], max_retries
)
else: else:
self.logger.error('Failed bootstrap phase after %s retries (%s)', retries[0], exc) self.logger.error('Failed bootstrap phase after %s retries (%s)', retries[0], exc)
raise exc raise exc
@ -535,22 +595,34 @@ class Updater:
# We also take this chance to delete pre-configured webhook if this is a polling Updater. # We also take this chance to delete pre-configured webhook if this is a polling Updater.
# NOTE: We don't know ahead if a webhook is configured, so we just delete. # NOTE: We don't know ahead if a webhook is configured, so we just delete.
if clean or not webhook_url: if clean or not webhook_url:
self._network_loop_retry(bootstrap_del_webhook, bootstrap_onerr_cb, self._network_loop_retry(
'bootstrap del webhook', bootstrap_interval) bootstrap_del_webhook,
bootstrap_onerr_cb,
'bootstrap del webhook',
bootstrap_interval,
)
retries[0] = 0 retries[0] = 0
# Clean pending messages, if requested. # Clean pending messages, if requested.
if clean: if clean:
self._network_loop_retry(bootstrap_clean_updates, bootstrap_onerr_cb, self._network_loop_retry(
'bootstrap clean updates', bootstrap_interval) bootstrap_clean_updates,
bootstrap_onerr_cb,
'bootstrap clean updates',
bootstrap_interval,
)
retries[0] = 0 retries[0] = 0
sleep(1) sleep(1)
# Restore/set webhook settings, if needed. Again, we don't know ahead if a webhook is set, # Restore/set webhook settings, if needed. Again, we don't know ahead if a webhook is set,
# so we set it anyhow. # so we set it anyhow.
if webhook_url: if webhook_url:
self._network_loop_retry(bootstrap_set_webhook, bootstrap_onerr_cb, self._network_loop_retry(
'bootstrap set webhook', bootstrap_interval) bootstrap_set_webhook,
bootstrap_onerr_cb,
'bootstrap set webhook',
bootstrap_interval,
)
def stop(self) -> None: def stop(self) -> None:
"""Stops the polling/webhook thread, the dispatcher and the job queue.""" """Stops the polling/webhook thread, the dispatcher and the job queue."""
@ -573,9 +645,11 @@ class Updater:
@no_type_check @no_type_check
def _stop_httpd(self) -> None: def _stop_httpd(self) -> None:
if self.httpd: if self.httpd:
self.logger.debug('Waiting for current webhook connection to be ' self.logger.debug(
'closed... Send a Telegram message to the bot to exit ' 'Waiting for current webhook connection to be '
'immediately.') 'closed... Send a Telegram message to the bot to exit '
'immediately.'
)
self.httpd.shutdown() self.httpd.shutdown()
self.httpd = None self.httpd = None
@ -596,8 +670,9 @@ class Updater:
def signal_handler(self, signum, frame) -> None: def signal_handler(self, signum, frame) -> None:
self.is_idle = False self.is_idle = False
if self.running: if self.running:
self.logger.info('Received signal {} ({}), stopping...'.format( self.logger.info(
signum, get_signal_name(signum))) 'Received signal {} ({}), stopping...'.format(signum, get_signal_name(signum))
)
if self.persistence: if self.persistence:
# Update user_data, chat_data and bot_data before flushing # Update user_data, chat_data and bot_data before flushing
self.dispatcher.update_persistence() self.dispatcher.update_persistence()
@ -608,6 +683,7 @@ class Updater:
else: else:
self.logger.warning('Exiting immediately!') self.logger.warning('Exiting immediately!')
import os import os
os._exit(1) os._exit(1)
def idle(self, stop_signals: Union[List, Tuple] = (SIGINT, SIGTERM, SIGABRT)) -> None: def idle(self, stop_signals: Union[List, Tuple] = (SIGINT, SIGTERM, SIGABRT)) -> None:

View file

@ -22,6 +22,7 @@ from telegram import TelegramObject
from telegram.utils.types import JSONDict from telegram.utils.types import JSONDict
from typing import Any, Optional, TYPE_CHECKING from typing import Any, Optional, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot, File from telegram import Bot, File
@ -64,18 +65,20 @@ class Animation(TelegramObject):
""" """
def __init__(self, def __init__(
file_id: str, self,
file_unique_id: str, file_id: str,
width: int, file_unique_id: str,
height: int, width: int,
duration: int, height: int,
thumb: PhotoSize = None, duration: int,
file_name: str = None, thumb: PhotoSize = None,
mime_type: str = None, file_name: str = None,
file_size: int = None, mime_type: str = None,
bot: 'Bot' = None, file_size: int = None,
**kwargs: Any): bot: 'Bot' = None,
**kwargs: Any,
):
# Required # Required
self.file_id = str(file_id) self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id) self.file_unique_id = str(file_unique_id)

View file

@ -22,6 +22,7 @@ from telegram import TelegramObject, PhotoSize
from telegram.utils.types import JSONDict from telegram.utils.types import JSONDict
from typing import Any, Optional, TYPE_CHECKING from typing import Any, Optional, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot, File from telegram import Bot, File
@ -65,17 +66,19 @@ class Audio(TelegramObject):
""" """
def __init__(self, def __init__(
file_id: str, self,
file_unique_id: str, file_id: str,
duration: int, file_unique_id: str,
performer: str = None, duration: int,
title: str = None, performer: str = None,
mime_type: str = None, title: str = None,
file_size: int = None, mime_type: str = None,
thumb: PhotoSize = None, file_size: int = None,
bot: 'Bot' = None, thumb: PhotoSize = None,
**kwargs: Any): bot: 'Bot' = None,
**kwargs: Any,
):
# Required # Required
self.file_id = str(file_id) self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id) self.file_unique_id = str(file_unique_id)

View file

@ -20,6 +20,7 @@
from telegram import TelegramObject from telegram import TelegramObject
from typing import Any, TYPE_CHECKING from typing import Any, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot, File from telegram import Bot, File
@ -61,13 +62,15 @@ class ChatPhoto(TelegramObject):
""" """
def __init__(self, def __init__(
small_file_id: str, self,
small_file_unique_id: str, small_file_id: str,
big_file_id: str, small_file_unique_id: str,
big_file_unique_id: str, big_file_id: str,
bot: 'Bot' = None, big_file_unique_id: str,
**kwargs: Any): bot: 'Bot' = None,
**kwargs: Any,
):
self.small_file_id = small_file_id self.small_file_id = small_file_id
self.small_file_unique_id = small_file_unique_id self.small_file_unique_id = small_file_unique_id
self.big_file_id = big_file_id self.big_file_id = big_file_id
@ -75,7 +78,10 @@ class ChatPhoto(TelegramObject):
self.bot = bot self.bot = bot
self._id_attrs = (self.small_file_unique_id, self.big_file_unique_id,) self._id_attrs = (
self.small_file_unique_id,
self.big_file_unique_id,
)
def get_small_file(self, timeout: int = None, **kwargs: Any) -> 'File': def get_small_file(self, timeout: int = None, **kwargs: Any) -> 'File':
"""Convenience wrapper over :attr:`telegram.Bot.get_file` for getting the """Convenience wrapper over :attr:`telegram.Bot.get_file` for getting the

View file

@ -45,13 +45,15 @@ class Contact(TelegramObject):
""" """
def __init__(self, def __init__(
phone_number: str, self,
first_name: str, phone_number: str,
last_name: str = None, first_name: str,
user_id: int = None, last_name: str = None,
vcard: str = None, user_id: int = None,
**kwargs: Any): vcard: str = None,
**kwargs: Any,
):
# Required # Required
self.phone_number = str(phone_number) self.phone_number = str(phone_number)
self.first_name = first_name self.first_name = first_name

View file

@ -22,6 +22,7 @@ from telegram import PhotoSize, TelegramObject
from telegram.utils.types import JSONDict from telegram.utils.types import JSONDict
from typing import Any, Optional, TYPE_CHECKING from typing import Any, Optional, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot, File from telegram import Bot, File
@ -57,17 +58,20 @@ class Document(TelegramObject):
**kwargs (:obj:`dict`): Arbitrary keyword arguments. **kwargs (:obj:`dict`): Arbitrary keyword arguments.
""" """
_id_keys = ('file_id',) _id_keys = ('file_id',)
def __init__(self, def __init__(
file_id: str, self,
file_unique_id: str, file_id: str,
thumb: PhotoSize = None, file_unique_id: str,
file_name: str = None, thumb: PhotoSize = None,
mime_type: str = None, file_name: str = None,
file_size: int = None, mime_type: str = None,
bot: 'Bot' = None, file_size: int = None,
**kwargs: Any): bot: 'Bot' = None,
**kwargs: Any,
):
# Required # Required
self.file_id = str(file_id) self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id) self.file_unique_id = str(file_unique_id)

View file

@ -27,6 +27,7 @@ from telegram import TelegramObject
from telegram.passport.credentials import decrypt from telegram.passport.credentials import decrypt
from typing import Any, Optional, IO, Union, TYPE_CHECKING from typing import Any, Optional, IO, Union, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot, FileCredentials from telegram import Bot, FileCredentials
@ -68,13 +69,15 @@ class File(TelegramObject):
""" """
def __init__(self, def __init__(
file_id: str, self,
file_unique_id: str, file_id: str,
bot: 'Bot' = None, file_unique_id: str,
file_size: int = None, bot: 'Bot' = None,
file_path: str = None, file_size: int = None,
**kwargs: Any): file_path: str = None,
**kwargs: Any,
):
# Required # Required
self.file_id = str(file_id) self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id) self.file_unique_id = str(file_unique_id)
@ -86,10 +89,9 @@ class File(TelegramObject):
self._id_attrs = (self.file_unique_id,) self._id_attrs = (self.file_unique_id,)
def download(self, def download(
custom_path: str = None, self, custom_path: str = None, out: IO = None, timeout: int = None
out: IO = None, ) -> Union[str, IO]:
timeout: int = None) -> Union[str, IO]:
""" """
Download this file. By default, the file is saved in the current working directory with its Download this file. By default, the file is saved in the current working directory with its
original filename as reported by Telegram. If the file has no filename, it the file ID will original filename as reported by Telegram. If the file has no filename, it the file ID will
@ -125,9 +127,9 @@ class File(TelegramObject):
if out: if out:
buf = self.bot.request.retrieve(url) buf = self.bot.request.retrieve(url)
if self._credentials: if self._credentials:
buf = decrypt(b64decode(self._credentials.secret), buf = decrypt(
b64decode(self._credentials.hash), b64decode(self._credentials.secret), b64decode(self._credentials.hash), buf
buf) )
out.write(buf) out.write(buf)
return out return out
else: else:
@ -140,9 +142,9 @@ class File(TelegramObject):
buf = self.bot.request.retrieve(url, timeout=timeout) buf = self.bot.request.retrieve(url, timeout=timeout)
if self._credentials: if self._credentials:
buf = decrypt(b64decode(self._credentials.secret), buf = decrypt(
b64decode(self._credentials.hash), b64decode(self._credentials.secret), b64decode(self._credentials.hash), buf
buf) )
with open(filename, 'wb') as fobj: with open(filename, 'wb') as fobj:
fobj.write(buf) fobj.write(buf)
return filename return filename
@ -150,8 +152,11 @@ class File(TelegramObject):
def _get_encoded_url(self) -> str: def _get_encoded_url(self) -> str:
"""Convert any UTF-8 char in :obj:`File.file_path` into a url encoded ASCII string.""" """Convert any UTF-8 char in :obj:`File.file_path` into a url encoded ASCII string."""
sres = urllib_parse.urlsplit(self.file_path) sres = urllib_parse.urlsplit(self.file_path)
return urllib_parse.urlunsplit(urllib_parse.SplitResult( return urllib_parse.urlunsplit(
sres.scheme, sres.netloc, urllib_parse.quote(sres.path), sres.query, sres.fragment)) urllib_parse.SplitResult(
sres.scheme, sres.netloc, urllib_parse.quote(sres.path), sres.query, sres.fragment
)
)
def download_as_bytearray(self, buf: bytearray = None) -> bytes: def download_as_bytearray(self, buf: bytearray = None) -> bytes:
"""Download this file and return it as a bytearray. """Download this file and return it as a bytearray.

View file

@ -57,15 +57,14 @@ class InputFile:
if filename: if filename:
self.filename = filename self.filename = filename
elif (hasattr(obj, 'name') and not isinstance(obj.name, int)): elif hasattr(obj, 'name') and not isinstance(obj.name, int):
self.filename = os.path.basename(obj.name) self.filename = os.path.basename(obj.name)
try: try:
self.mimetype = self.is_image(self.input_file_content) self.mimetype = self.is_image(self.input_file_content)
except TelegramError: except TelegramError:
if self.filename: if self.filename:
self.mimetype = mimetypes.guess_type( self.mimetype = mimetypes.guess_type(self.filename)[0] or DEFAULT_MIME_TYPE
self.filename)[0] or DEFAULT_MIME_TYPE
else: else:
self.mimetype = DEFAULT_MIME_TYPE self.mimetype = DEFAULT_MIME_TYPE
if not self.filename: if not self.filename:

View file

@ -34,6 +34,7 @@ class InputMedia(TelegramObject):
:class:`telegram.InputMediaVideo` for detailed use. :class:`telegram.InputMediaVideo` for detailed use.
""" """
pass pass
@ -76,14 +77,16 @@ class InputMediaAnimation(InputMedia):
arguments. arguments.
""" """
def __init__(self, def __init__(
media: Union[str, FileLike, Animation], self,
thumb: FileLike = None, media: Union[str, FileLike, Animation],
caption: str = None, thumb: FileLike = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, caption: str = None,
width: int = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
height: int = None, width: int = None,
duration: int = None): height: int = None,
duration: int = None,
):
self.type = 'animation' self.type = 'animation'
if isinstance(media, Animation): if isinstance(media, Animation):
@ -136,10 +139,12 @@ class InputMediaPhoto(InputMedia):
in :class:`telegram.ParseMode` for the available modes. in :class:`telegram.ParseMode` for the available modes.
""" """
def __init__(self, def __init__(
media: Union[str, FileLike, PhotoSize], self,
caption: str = None, media: Union[str, FileLike, PhotoSize],
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE): caption: str = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
):
self.type = 'photo' self.type = 'photo'
if isinstance(media, PhotoSize): if isinstance(media, PhotoSize):
@ -200,15 +205,17 @@ class InputMediaVideo(InputMedia):
by Telegram. by Telegram.
""" """
def __init__(self, def __init__(
media: Union[str, FileLike, Video], self,
caption: str = None, media: Union[str, FileLike, Video],
width: int = None, caption: str = None,
height: int = None, width: int = None,
duration: int = None, height: int = None,
supports_streaming: bool = None, duration: int = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, supports_streaming: bool = None,
thumb: FileLike = None): parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
thumb: FileLike = None,
):
self.type = 'video' self.type = 'video'
if isinstance(media, Video): if isinstance(media, Video):
@ -282,14 +289,16 @@ class InputMediaAudio(InputMedia):
optional arguments. optional arguments.
""" """
def __init__(self, def __init__(
media: Union[str, FileLike, Audio], self,
thumb: FileLike = None, media: Union[str, FileLike, Audio],
caption: str = None, thumb: FileLike = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, caption: str = None,
duration: int = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
performer: str = None, duration: int = None,
title: str = None): performer: str = None,
title: str = None,
):
self.type = 'audio' self.type = 'audio'
if isinstance(media, Audio): if isinstance(media, Audio):
@ -348,11 +357,13 @@ class InputMediaDocument(InputMedia):
Thumbnails can't be reused and can be only uploaded as a new file. Thumbnails can't be reused and can be only uploaded as a new file.
""" """
def __init__(self, def __init__(
media: Union[str, FileLike, Document], self,
thumb: FileLike = None, media: Union[str, FileLike, Document],
caption: str = None, thumb: FileLike = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE): caption: str = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
):
self.type = 'document' self.type = 'document'
if isinstance(media, Document): if isinstance(media, Document):

View file

@ -21,6 +21,7 @@
from telegram import TelegramObject from telegram import TelegramObject
from telegram.utils.types import JSONDict from telegram.utils.types import JSONDict
from typing import Any, TYPE_CHECKING from typing import Any, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot, File from telegram import Bot, File
@ -55,14 +56,16 @@ class PhotoSize(TelegramObject):
""" """
def __init__(self, def __init__(
file_id: str, self,
file_unique_id: str, file_id: str,
width: int, file_unique_id: str,
height: int, width: int,
file_size: int = None, height: int,
bot: 'Bot' = None, file_size: int = None,
**kwargs: Any): bot: 'Bot' = None,
**kwargs: Any,
):
# Required # Required
self.file_id = str(file_id) self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id) self.file_unique_id = str(file_unique_id)

View file

@ -21,6 +21,7 @@
from telegram import PhotoSize, TelegramObject from telegram import PhotoSize, TelegramObject
from telegram.utils.types import JSONDict from telegram.utils.types import JSONDict
from typing import Any, Optional, List, TYPE_CHECKING from typing import Any, Optional, List, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot, File from telegram import Bot, File
@ -71,19 +72,21 @@ class Sticker(TelegramObject):
""" """
def __init__(self, def __init__(
file_id: str, self,
file_unique_id: str, file_id: str,
width: int, file_unique_id: str,
height: int, width: int,
is_animated: bool, height: int,
thumb: PhotoSize = None, is_animated: bool,
emoji: str = None, thumb: PhotoSize = None,
file_size: int = None, emoji: str = None,
set_name: str = None, file_size: int = None,
mask_position: 'MaskPosition' = None, set_name: str = None,
bot: 'Bot' = None, mask_position: 'MaskPosition' = None,
**kwargs: Any): bot: 'Bot' = None,
**kwargs: Any,
):
# Required # Required
self.file_id = str(file_id) self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id) self.file_unique_id = str(file_unique_id)
@ -158,15 +161,17 @@ class StickerSet(TelegramObject):
""" """
def __init__(self, def __init__(
name: str, self,
title: str, name: str,
is_animated: bool, title: str,
contains_masks: bool, is_animated: bool,
stickers: List[Sticker], contains_masks: bool,
bot: 'Bot' = None, stickers: List[Sticker],
thumb: PhotoSize = None, bot: 'Bot' = None,
**kwargs: Any): thumb: PhotoSize = None,
**kwargs: Any,
):
self.name = name self.name = name
self.title = title self.title = title
self.is_animated = is_animated self.is_animated = is_animated
@ -227,6 +232,7 @@ class MaskPosition(TelegramObject):
scale (:obj:`float`): Mask scaling coefficient. For example, 2.0 means double size. scale (:obj:`float`): Mask scaling coefficient. For example, 2.0 means double size.
""" """
FOREHEAD: str = 'forehead' FOREHEAD: str = 'forehead'
""":obj:`str`: 'forehead'""" """:obj:`str`: 'forehead'"""
EYES: str = 'eyes' EYES: str = 'eyes'

View file

@ -21,6 +21,7 @@
from telegram import TelegramObject, Location from telegram import TelegramObject, Location
from telegram.utils.types import JSONDict from telegram.utils.types import JSONDict
from typing import Any, Optional, TYPE_CHECKING from typing import Any, Optional, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot from telegram import Bot
@ -50,13 +51,15 @@ class Venue(TelegramObject):
""" """
def __init__(self, def __init__(
location: Location, self,
title: str, location: Location,
address: str, title: str,
foursquare_id: str = None, address: str,
foursquare_type: str = None, foursquare_id: str = None,
**kwargs: Any): foursquare_type: str = None,
**kwargs: Any,
):
# Required # Required
self.location = location self.location = location
self.title = title self.title = title

View file

@ -21,6 +21,7 @@
from telegram import PhotoSize, TelegramObject from telegram import PhotoSize, TelegramObject
from telegram.utils.types import JSONDict from telegram.utils.types import JSONDict
from typing import Any, Optional, TYPE_CHECKING from typing import Any, Optional, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot, File from telegram import Bot, File
@ -61,17 +62,19 @@ class Video(TelegramObject):
""" """
def __init__(self, def __init__(
file_id: str, self,
file_unique_id: str, file_id: str,
width: int, file_unique_id: str,
height: int, width: int,
duration: int, height: int,
thumb: PhotoSize = None, duration: int,
mime_type: str = None, thumb: PhotoSize = None,
file_size: int = None, mime_type: str = None,
bot: 'Bot' = None, file_size: int = None,
**kwargs: Any): bot: 'Bot' = None,
**kwargs: Any,
):
# Required # Required
self.file_id = str(file_id) self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id) self.file_unique_id = str(file_unique_id)

View file

@ -21,6 +21,7 @@
from telegram import PhotoSize, TelegramObject from telegram import PhotoSize, TelegramObject
from telegram.utils.types import JSONDict from telegram.utils.types import JSONDict
from typing import Any, Optional, TYPE_CHECKING from typing import Any, Optional, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot, File from telegram import Bot, File
@ -58,15 +59,17 @@ class VideoNote(TelegramObject):
""" """
def __init__(self, def __init__(
file_id: str, self,
file_unique_id: str, file_id: str,
length: int, file_unique_id: str,
duration: int, length: int,
thumb: PhotoSize = None, duration: int,
file_size: int = None, thumb: PhotoSize = None,
bot: 'Bot' = None, file_size: int = None,
**kwargs: Any): bot: 'Bot' = None,
**kwargs: Any,
):
# Required # Required
self.file_id = str(file_id) self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id) self.file_unique_id = str(file_unique_id)

View file

@ -21,6 +21,7 @@
from telegram import TelegramObject from telegram import TelegramObject
from telegram.utils.types import JSONDict from telegram.utils.types import JSONDict
from typing import Any, TYPE_CHECKING from typing import Any, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot, File from telegram import Bot, File
@ -55,14 +56,16 @@ class Voice(TelegramObject):
""" """
def __init__(self, def __init__(
file_id: str, self,
file_unique_id: str, file_id: str,
duration: int, file_unique_id: str,
mime_type: str = None, duration: int,
file_size: int = None, mime_type: str = None,
bot: 'Bot' = None, file_size: int = None,
**kwargs: Any): bot: 'Bot' = None,
**kwargs: Any,
):
# Required # Required
self.file_id = str(file_id) self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id) self.file_unique_id = str(file_unique_id)

View file

@ -23,6 +23,7 @@ import sys
from telegram import MessageEntity, TelegramObject, Animation, PhotoSize from telegram import MessageEntity, TelegramObject, Animation, PhotoSize
from telegram.utils.types import JSONDict from telegram.utils.types import JSONDict
from typing import List, Any, Dict, Optional, TYPE_CHECKING from typing import List, Any, Dict, Optional, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot from telegram import Bot
@ -66,14 +67,16 @@ class Game(TelegramObject):
""" """
def __init__(self, def __init__(
title: str, self,
description: str, title: str,
photo: List[PhotoSize], description: str,
text: str = None, photo: List[PhotoSize],
text_entities: List[MessageEntity] = None, text: str = None,
animation: Animation = None, text_entities: List[MessageEntity] = None,
**kwargs: Any): animation: Animation = None,
**kwargs: Any,
):
# Required # Required
self.title = title self.title = title
self.description = description self.description = description
@ -130,11 +133,11 @@ class Game(TelegramObject):
raise RuntimeError("This Game has no 'text'.") raise RuntimeError("This Game has no 'text'.")
# Is it a narrow build, if so we don't need to convert # Is it a narrow build, if so we don't need to convert
if sys.maxunicode == 0xffff: if sys.maxunicode == 0xFFFF:
return self.text[entity.offset:entity.offset + entity.length] return self.text[entity.offset : entity.offset + entity.length]
else: else:
entity_text = self.text.encode('utf-16-le') entity_text = self.text.encode('utf-16-le')
entity_text = entity_text[entity.offset * 2:(entity.offset + entity.length) * 2] entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2]
return entity_text.decode('utf-16-le') return entity_text.decode('utf-16-le')
@ -164,7 +167,8 @@ class Game(TelegramObject):
return { return {
entity: self.parse_text_entity(entity) entity: self.parse_text_entity(entity)
for entity in (self.text_entities or []) if entity.type in types for entity in (self.text_entities or [])
if entity.type in types
} }
def __hash__(self) -> int: def __hash__(self) -> int:

View file

@ -21,6 +21,7 @@
from telegram import TelegramObject, User from telegram import TelegramObject, User
from telegram.utils.types import JSONDict from telegram.utils.types import JSONDict
from typing import Optional, TYPE_CHECKING from typing import Optional, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot from telegram import Bot

View file

@ -20,6 +20,7 @@
from telegram import TelegramObject from telegram import TelegramObject
from typing import Any, TYPE_CHECKING from typing import Any, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import CallbackGame, LoginUrl from telegram import CallbackGame, LoginUrl
@ -81,16 +82,18 @@ class InlineKeyboardButton(TelegramObject):
""" """
def __init__(self, def __init__(
text: str, self,
url: str = None, text: str,
callback_data: str = None, url: str = None,
switch_inline_query: str = None, callback_data: str = None,
switch_inline_query_current_chat: str = None, switch_inline_query: str = None,
callback_game: 'CallbackGame' = None, switch_inline_query_current_chat: str = None,
pay: bool = None, callback_game: 'CallbackGame' = None,
login_url: 'LoginUrl' = None, pay: bool = None,
**kwargs: Any): login_url: 'LoginUrl' = None,
**kwargs: Any,
):
# Required # Required
self.text = text self.text = text

View file

@ -21,6 +21,7 @@
from telegram import ReplyMarkup, InlineKeyboardButton from telegram import ReplyMarkup, InlineKeyboardButton
from telegram.utils.types import JSONDict from telegram.utils.types import JSONDict
from typing import Any, List, Optional, TYPE_CHECKING from typing import Any, List, Optional, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot from telegram import Bot
@ -57,8 +58,7 @@ class InlineKeyboardMarkup(ReplyMarkup):
return data return data
@classmethod @classmethod
def de_json(cls, data: Optional[JSONDict], def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['InlineKeyboardMarkup']:
bot: 'Bot') -> Optional['InlineKeyboardMarkup']:
data = cls.parse_data(data) data = cls.parse_data(data)
if not data: if not data:
@ -91,8 +91,9 @@ class InlineKeyboardMarkup(ReplyMarkup):
return cls([[button]], **kwargs) return cls([[button]], **kwargs)
@classmethod @classmethod
def from_row(cls, button_row: List[InlineKeyboardButton], def from_row(
**kwargs: Any) -> 'InlineKeyboardMarkup': cls, button_row: List[InlineKeyboardButton], **kwargs: Any
) -> 'InlineKeyboardMarkup':
"""Shortcut for:: """Shortcut for::
InlineKeyboardMarkup([button_row], **kwargs) InlineKeyboardMarkup([button_row], **kwargs)
@ -108,8 +109,9 @@ class InlineKeyboardMarkup(ReplyMarkup):
return cls([button_row], **kwargs) return cls([button_row], **kwargs)
@classmethod @classmethod
def from_column(cls, button_column: List[InlineKeyboardButton], def from_column(
**kwargs: Any) -> 'InlineKeyboardMarkup': cls, button_column: List[InlineKeyboardButton], **kwargs: Any
) -> 'InlineKeyboardMarkup':
"""Shortcut for:: """Shortcut for::
InlineKeyboardMarkup([[button] for button in button_column], **kwargs) InlineKeyboardMarkup([[button] for button in button_column], **kwargs)

View file

@ -22,6 +22,7 @@
from telegram import TelegramObject, User, Location from telegram import TelegramObject, User, Location
from telegram.utils.types import JSONDict from telegram.utils.types import JSONDict
from typing import Any, Optional, TYPE_CHECKING from typing import Any, Optional, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot from telegram import Bot
@ -57,14 +58,16 @@ class InlineQuery(TelegramObject):
""" """
def __init__(self, def __init__(
id: str, self,
from_user: User, id: str,
query: str, from_user: User,
offset: str, query: str,
location: Location = None, offset: str,
bot: 'Bot' = None, location: Location = None,
**kwargs: Any): bot: 'Bot' = None,
**kwargs: Any,
):
# Required # Required
self.id = id self.id = id
self.from_user = from_user self.from_user = from_user
@ -125,8 +128,5 @@ class InlineQuery(TelegramObject):
""" """
return self.bot.answer_inline_query( return self.bot.answer_inline_query(
self.id, self.id, *args, current_offset=self.offset if auto_pagination else None, **kwargs
*args,
current_offset=self.offset if auto_pagination else None,
**kwargs
) )

View file

@ -20,6 +20,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from typing import Any, TYPE_CHECKING from typing import Any, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import InputMessageContent, ReplyMarkup from telegram import InputMessageContent, ReplyMarkup
@ -61,18 +62,20 @@ class InlineQueryResultArticle(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
title: str, id: str,
input_message_content: 'InputMessageContent', title: str,
reply_markup: 'ReplyMarkup' = None, input_message_content: 'InputMessageContent',
url: str = None, reply_markup: 'ReplyMarkup' = None,
hide_url: bool = None, url: str = None,
description: str = None, hide_url: bool = None,
thumb_url: str = None, description: str = None,
thumb_width: int = None, thumb_url: str = None,
thumb_height: int = None, thumb_width: int = None,
**kwargs: Any): thumb_height: int = None,
**kwargs: Any,
):
# Required # Required
super().__init__('article', id) super().__init__('article', id)

View file

@ -21,6 +21,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE, DefaultValue from telegram.utils.helpers import DEFAULT_NONE, DefaultValue
from typing import Any, Union, TYPE_CHECKING from typing import Any, Union, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import InputMessageContent, ReplyMarkup from telegram import InputMessageContent, ReplyMarkup
@ -65,17 +66,19 @@ class InlineQueryResultAudio(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
audio_url: str, id: str,
title: str, audio_url: str,
performer: str = None, title: str,
audio_duration: int = None, performer: str = None,
caption: str = None, audio_duration: int = None,
reply_markup: 'ReplyMarkup' = None, caption: str = None,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, input_message_content: 'InputMessageContent' = None,
**kwargs: Any): parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
**kwargs: Any,
):
# Required # Required
super().__init__('audio', id) super().__init__('audio', id)

View file

@ -21,6 +21,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE, DefaultValue from telegram.utils.helpers import DEFAULT_NONE, DefaultValue
from typing import Any, Union, TYPE_CHECKING from typing import Any, Union, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import InputMessageContent, ReplyMarkup from telegram import InputMessageContent, ReplyMarkup
@ -59,14 +60,16 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
audio_file_id: str, id: str,
caption: str = None, audio_file_id: str,
reply_markup: 'ReplyMarkup' = None, caption: str = None,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, input_message_content: 'InputMessageContent' = None,
**kwargs: Any): parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
**kwargs: Any,
):
# Required # Required
super().__init__('audio', id) super().__init__('audio', id)
self.audio_file_id = audio_file_id self.audio_file_id = audio_file_id

View file

@ -21,6 +21,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE, DefaultValue from telegram.utils.helpers import DEFAULT_NONE, DefaultValue
from typing import Any, Union, TYPE_CHECKING from typing import Any, Union, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import InputMessageContent, ReplyMarkup from telegram import InputMessageContent, ReplyMarkup
@ -65,16 +66,18 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
title: str, id: str,
document_file_id: str, title: str,
description: str = None, document_file_id: str,
caption: str = None, description: str = None,
reply_markup: 'ReplyMarkup' = None, caption: str = None,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, input_message_content: 'InputMessageContent' = None,
**kwargs: Any): parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
**kwargs: Any,
):
# Required # Required
super().__init__('document', id) super().__init__('document', id)
self.title = title self.title = title

View file

@ -21,6 +21,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE, DefaultValue from telegram.utils.helpers import DEFAULT_NONE, DefaultValue
from typing import Any, Union, TYPE_CHECKING from typing import Any, Union, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import InputMessageContent, ReplyMarkup from telegram import InputMessageContent, ReplyMarkup
@ -64,15 +65,17 @@ class InlineQueryResultCachedGif(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
gif_file_id: str, id: str,
title: str = None, gif_file_id: str,
caption: str = None, title: str = None,
reply_markup: 'ReplyMarkup' = None, caption: str = None,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, input_message_content: 'InputMessageContent' = None,
**kwargs: Any): parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
**kwargs: Any,
):
# Required # Required
super().__init__('gif', id) super().__init__('gif', id)
self.gif_file_id = gif_file_id self.gif_file_id = gif_file_id

View file

@ -21,6 +21,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE, DefaultValue from telegram.utils.helpers import DEFAULT_NONE, DefaultValue
from typing import Any, Union, TYPE_CHECKING from typing import Any, Union, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import InputMessageContent, ReplyMarkup from telegram import InputMessageContent, ReplyMarkup
@ -64,15 +65,17 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
mpeg4_file_id: str, id: str,
title: str = None, mpeg4_file_id: str,
caption: str = None, title: str = None,
reply_markup: 'ReplyMarkup' = None, caption: str = None,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, input_message_content: 'InputMessageContent' = None,
**kwargs: Any): parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
**kwargs: Any,
):
# Required # Required
super().__init__('mpeg4_gif', id) super().__init__('mpeg4_gif', id)
self.mpeg4_file_id = mpeg4_file_id self.mpeg4_file_id = mpeg4_file_id

View file

@ -21,6 +21,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE, DefaultValue from telegram.utils.helpers import DEFAULT_NONE, DefaultValue
from typing import Any, Union, TYPE_CHECKING from typing import Any, Union, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import InputMessageContent, ReplyMarkup from telegram import InputMessageContent, ReplyMarkup
@ -66,16 +67,18 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
photo_file_id: str, id: str,
title: str = None, photo_file_id: str,
description: str = None, title: str = None,
caption: str = None, description: str = None,
reply_markup: 'ReplyMarkup' = None, caption: str = None,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, input_message_content: 'InputMessageContent' = None,
**kwargs: Any): parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
**kwargs: Any,
):
# Required # Required
super().__init__('photo', id) super().__init__('photo', id)
self.photo_file_id = photo_file_id self.photo_file_id = photo_file_id

View file

@ -20,6 +20,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from typing import Any, TYPE_CHECKING from typing import Any, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import ReplyMarkup, InputMessageContent from telegram import ReplyMarkup, InputMessageContent
@ -50,12 +51,14 @@ class InlineQueryResultCachedSticker(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
sticker_file_id: str, id: str,
reply_markup: 'ReplyMarkup' = None, sticker_file_id: str,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
**kwargs: Any): input_message_content: 'InputMessageContent' = None,
**kwargs: Any,
):
# Required # Required
super().__init__('sticker', id) super().__init__('sticker', id)
self.sticker_file_id = sticker_file_id self.sticker_file_id = sticker_file_id

View file

@ -21,6 +21,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE, DefaultValue from telegram.utils.helpers import DEFAULT_NONE, DefaultValue
from typing import Any, Union, TYPE_CHECKING from typing import Any, Union, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import InputMessageContent, ReplyMarkup from telegram import InputMessageContent, ReplyMarkup
@ -66,16 +67,18 @@ class InlineQueryResultCachedVideo(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
video_file_id: str, id: str,
title: str, video_file_id: str,
description: str = None, title: str,
caption: str = None, description: str = None,
reply_markup: 'ReplyMarkup' = None, caption: str = None,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, input_message_content: 'InputMessageContent' = None,
**kwargs: Any): parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
**kwargs: Any,
):
# Required # Required
super().__init__('video', id) super().__init__('video', id)
self.video_file_id = video_file_id self.video_file_id = video_file_id

View file

@ -21,6 +21,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE, DefaultValue from telegram.utils.helpers import DEFAULT_NONE, DefaultValue
from typing import Any, Union, TYPE_CHECKING from typing import Any, Union, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import InputMessageContent, ReplyMarkup from telegram import InputMessageContent, ReplyMarkup
@ -61,15 +62,17 @@ class InlineQueryResultCachedVoice(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
voice_file_id: str, id: str,
title: str, voice_file_id: str,
caption: str = None, title: str,
reply_markup: 'ReplyMarkup' = None, caption: str = None,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, input_message_content: 'InputMessageContent' = None,
**kwargs: Any): parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
**kwargs: Any,
):
# Required # Required
super().__init__('voice', id) super().__init__('voice', id)
self.voice_file_id = voice_file_id self.voice_file_id = voice_file_id

View file

@ -20,6 +20,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from typing import Any, TYPE_CHECKING from typing import Any, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import ReplyMarkup, InputMessageContent from telegram import ReplyMarkup, InputMessageContent
@ -64,18 +65,20 @@ class InlineQueryResultContact(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
phone_number: str, id: str,
first_name: str, phone_number: str,
last_name: str = None, first_name: str,
reply_markup: 'ReplyMarkup' = None, last_name: str = None,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
thumb_url: str = None, input_message_content: 'InputMessageContent' = None,
thumb_width: int = None, thumb_url: str = None,
thumb_height: int = None, thumb_width: int = None,
vcard: str = None, thumb_height: int = None,
**kwargs: Any): vcard: str = None,
**kwargs: Any,
):
# Required # Required
super().__init__('contact', id) super().__init__('contact', id)
self.phone_number = phone_number self.phone_number = phone_number

View file

@ -21,6 +21,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE, DefaultValue from telegram.utils.helpers import DEFAULT_NONE, DefaultValue
from typing import Any, Union, TYPE_CHECKING from typing import Any, Union, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import InputMessageContent, ReplyMarkup from telegram import InputMessageContent, ReplyMarkup
@ -76,20 +77,22 @@ class InlineQueryResultDocument(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
document_url: str, id: str,
title: str, document_url: str,
mime_type: str, title: str,
caption: str = None, mime_type: str,
description: str = None, caption: str = None,
reply_markup: 'ReplyMarkup' = None, description: str = None,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
thumb_url: str = None, input_message_content: 'InputMessageContent' = None,
thumb_width: int = None, thumb_url: str = None,
thumb_height: int = None, thumb_width: int = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, thumb_height: int = None,
**kwargs: Any): parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
**kwargs: Any,
):
# Required # Required
super().__init__('document', id) super().__init__('document', id)
self.document_url = document_url self.document_url = document_url

View file

@ -20,6 +20,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from typing import Any, TYPE_CHECKING from typing import Any, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import ReplyMarkup from telegram import ReplyMarkup
@ -43,11 +44,9 @@ class InlineQueryResultGame(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self, id: str, game_short_name: str, reply_markup: 'ReplyMarkup' = None, **kwargs: Any
game_short_name: str, ):
reply_markup: 'ReplyMarkup' = None,
**kwargs: Any):
# Required # Required
super().__init__('game', id) super().__init__('game', id)
self.id = id self.id = id

View file

@ -21,6 +21,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE, DefaultValue from telegram.utils.helpers import DEFAULT_NONE, DefaultValue
from typing import Any, Union, TYPE_CHECKING from typing import Any, Union, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import InputMessageContent, ReplyMarkup from telegram import InputMessageContent, ReplyMarkup
@ -76,20 +77,22 @@ class InlineQueryResultGif(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
gif_url: str, id: str,
thumb_url: str, gif_url: str,
gif_width: int = None, thumb_url: str,
gif_height: int = None, gif_width: int = None,
title: str = None, gif_height: int = None,
caption: str = None, title: str = None,
reply_markup: 'ReplyMarkup' = None, caption: str = None,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
gif_duration: int = None, input_message_content: 'InputMessageContent' = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, gif_duration: int = None,
thumb_mime_type: str = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
**kwargs: Any): thumb_mime_type: str = None,
**kwargs: Any,
):
# Required # Required
super().__init__('gif', id) super().__init__('gif', id)

View file

@ -20,6 +20,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from typing import Any, TYPE_CHECKING from typing import Any, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import ReplyMarkup, InputMessageContent from telegram import ReplyMarkup, InputMessageContent
@ -64,18 +65,20 @@ class InlineQueryResultLocation(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
latitude: float, id: str,
longitude: float, latitude: float,
title: str, longitude: float,
live_period: int = None, title: str,
reply_markup: 'ReplyMarkup' = None, live_period: int = None,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
thumb_url: str = None, input_message_content: 'InputMessageContent' = None,
thumb_width: int = None, thumb_url: str = None,
thumb_height: int = None, thumb_width: int = None,
**kwargs: Any): thumb_height: int = None,
**kwargs: Any,
):
# Required # Required
super().__init__('location', id) super().__init__('location', id)
self.latitude = latitude self.latitude = latitude

View file

@ -21,6 +21,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE, DefaultValue from telegram.utils.helpers import DEFAULT_NONE, DefaultValue
from typing import Any, Union, TYPE_CHECKING from typing import Any, Union, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import InputMessageContent, ReplyMarkup from telegram import InputMessageContent, ReplyMarkup
@ -76,20 +77,22 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
mpeg4_url: str, id: str,
thumb_url: str, mpeg4_url: str,
mpeg4_width: int = None, thumb_url: str,
mpeg4_height: int = None, mpeg4_width: int = None,
title: str = None, mpeg4_height: int = None,
caption: str = None, title: str = None,
reply_markup: 'ReplyMarkup' = None, caption: str = None,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
mpeg4_duration: int = None, input_message_content: 'InputMessageContent' = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, mpeg4_duration: int = None,
thumb_mime_type: str = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
**kwargs: Any): thumb_mime_type: str = None,
**kwargs: Any,
):
# Required # Required
super().__init__('mpeg4_gif', id) super().__init__('mpeg4_gif', id)

View file

@ -21,6 +21,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE, DefaultValue from telegram.utils.helpers import DEFAULT_NONE, DefaultValue
from typing import Any, Union, TYPE_CHECKING from typing import Any, Union, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import InputMessageContent, ReplyMarkup from telegram import InputMessageContent, ReplyMarkup
@ -73,26 +74,28 @@ class InlineQueryResultPhoto(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
photo_url: str, id: str,
thumb_url: str, photo_url: str,
photo_width: int = None, thumb_url: str,
photo_height: int = None, photo_width: int = None,
title: str = None, photo_height: int = None,
description: str = None, title: str = None,
caption: str = None, description: str = None,
reply_markup: 'ReplyMarkup' = None, caption: str = None,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, input_message_content: 'InputMessageContent' = None,
**kwargs: Any): parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
**kwargs: Any,
):
# Required # Required
super().__init__('photo', id) super().__init__('photo', id)
self.photo_url = photo_url self.photo_url = photo_url
self.thumb_url = thumb_url self.thumb_url = thumb_url
# Optionals # Optionals
self.photo_width = int(photo_width)if photo_width is not None else None self.photo_width = int(photo_width) if photo_width is not None else None
self.photo_height = int(photo_height) if photo_height is not None else None self.photo_height = int(photo_height) if photo_height is not None else None
self.title = title self.title = title
self.description = description self.description = description

View file

@ -20,6 +20,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from typing import Any, TYPE_CHECKING from typing import Any, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import ReplyMarkup, InputMessageContent from telegram import ReplyMarkup, InputMessageContent
@ -70,20 +71,22 @@ class InlineQueryResultVenue(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
latitude: float, id: str,
longitude: float, latitude: float,
title: str, longitude: float,
address: str, title: str,
foursquare_id: str = None, address: str,
foursquare_type: str = None, foursquare_id: str = None,
reply_markup: 'ReplyMarkup' = None, foursquare_type: str = None,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
thumb_url: str = None, input_message_content: 'InputMessageContent' = None,
thumb_width: int = None, thumb_url: str = None,
thumb_height: int = None, thumb_width: int = None,
**kwargs: Any): thumb_height: int = None,
**kwargs: Any,
):
# Required # Required
super().__init__('venue', id) super().__init__('venue', id)

View file

@ -21,6 +21,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE, DefaultValue from telegram.utils.helpers import DEFAULT_NONE, DefaultValue
from typing import Any, Union, TYPE_CHECKING from typing import Any, Union, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import InputMessageContent, ReplyMarkup from telegram import InputMessageContent, ReplyMarkup
@ -83,21 +84,23 @@ class InlineQueryResultVideo(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
video_url: str, id: str,
mime_type: str, video_url: str,
thumb_url: str, mime_type: str,
title: str, thumb_url: str,
caption: str = None, title: str,
video_width: int = None, caption: str = None,
video_height: int = None, video_width: int = None,
video_duration: int = None, video_height: int = None,
description: str = None, video_duration: int = None,
reply_markup: 'ReplyMarkup' = None, description: str = None,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, input_message_content: 'InputMessageContent' = None,
**kwargs: Any): parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
**kwargs: Any,
):
# Required # Required
super().__init__('video', id) super().__init__('video', id)

View file

@ -21,6 +21,7 @@
from telegram import InlineQueryResult from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE, DefaultValue from telegram.utils.helpers import DEFAULT_NONE, DefaultValue
from typing import Any, Union, TYPE_CHECKING from typing import Any, Union, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import InputMessageContent, ReplyMarkup from telegram import InputMessageContent, ReplyMarkup
@ -64,16 +65,18 @@ class InlineQueryResultVoice(InlineQueryResult):
""" """
def __init__(self, def __init__(
id: str, self,
voice_url: str, id: str,
title: str, voice_url: str,
voice_duration: int = None, title: str,
caption: str = None, voice_duration: int = None,
reply_markup: 'ReplyMarkup' = None, caption: str = None,
input_message_content: 'InputMessageContent' = None, reply_markup: 'ReplyMarkup' = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, input_message_content: 'InputMessageContent' = None,
**kwargs: Any): parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
**kwargs: Any,
):
# Required # Required
super().__init__('voice', id) super().__init__('voice', id)

View file

@ -45,12 +45,14 @@ class InputContactMessageContent(InputMessageContent):
""" """
def __init__(self, def __init__(
phone_number: str, self,
first_name: str, phone_number: str,
last_name: str = None, first_name: str,
vcard: str = None, last_name: str = None,
**kwargs: Any): vcard: str = None,
**kwargs: Any,
):
# Required # Required
self.phone_number = phone_number self.phone_number = phone_number
self.first_name = first_name self.first_name = first_name

View file

@ -23,6 +23,7 @@ from typing import Any
class InputLocationMessageContent(InputMessageContent): class InputLocationMessageContent(InputMessageContent):
# fmt: off
""" """
Represents the content of a location message to be sent as the result of an inline query. Represents the content of a location message to be sent as the result of an inline query.
@ -43,6 +44,7 @@ class InputLocationMessageContent(InputMessageContent):
**kwargs (:obj:`dict`): Arbitrary keyword arguments. **kwargs (:obj:`dict`): Arbitrary keyword arguments.
""" """
# fmt: on
def __init__(self, latitude: float, longitude: float, live_period: int = None, **kwargs: Any): def __init__(self, latitude: float, longitude: float, live_period: int = None, **kwargs: Any):
# Required # Required

View file

@ -29,6 +29,7 @@ class InputMessageContent(TelegramObject):
:class:`telegram.InputVenueMessageContent` for more details. :class:`telegram.InputVenueMessageContent` for more details.
""" """
@property @property
def _has_parse_mode(self) -> bool: def _has_parse_mode(self) -> bool:
return hasattr(self, 'parse_mode') return hasattr(self, 'parse_mode')

View file

@ -51,11 +51,13 @@ class InputTextMessageContent(InputMessageContent):
""" """
def __init__(self, def __init__(
message_text: str, self,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, message_text: str,
disable_web_page_preview: Union[bool, DefaultValue] = DEFAULT_NONE, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
**kwargs: Any): disable_web_page_preview: Union[bool, DefaultValue] = DEFAULT_NONE,
**kwargs: Any,
):
# Required # Required
self.message_text = message_text self.message_text = message_text
# Optionals # Optionals

View file

@ -52,14 +52,16 @@ class InputVenueMessageContent(InputMessageContent):
""" """
def __init__(self, def __init__(
latitude: float, self,
longitude: float, latitude: float,
title: str, longitude: float,
address: str, title: str,
foursquare_id: str = None, address: str,
foursquare_type: str = None, foursquare_id: str = None,
**kwargs: Any): foursquare_type: str = None,
**kwargs: Any,
):
# Required # Required
self.latitude = latitude self.latitude = latitude
self.longitude = longitude self.longitude = longitude

View file

@ -60,12 +60,14 @@ class KeyboardButton(TelegramObject):
""" """
def __init__(self, def __init__(
text: str, self,
request_contact: bool = None, text: str,
request_location: bool = None, request_contact: bool = None,
request_poll: bool = None, request_location: bool = None,
**kwargs: Any): request_poll: bool = None,
**kwargs: Any,
):
# Required # Required
self.text = text self.text = text
# Optionals # Optionals
@ -73,5 +75,9 @@ class KeyboardButton(TelegramObject):
self.request_location = request_location self.request_location = request_location
self.request_poll = request_poll self.request_poll = request_poll
self._id_attrs = (self.text, self.request_contact, self.request_location, self._id_attrs = (
self.request_poll) self.text,
self.request_contact,
self.request_location,
self.request_poll,
)

View file

@ -35,6 +35,7 @@ class KeyboardButtonPollType(TelegramObject):
passed, only regular polls will be allowed. Otherwise, the user will be allowed to passed, only regular polls will be allowed. Otherwise, the user will be allowed to
create a poll of any type. create a poll of any type.
""" """
def __init__(self, type: str = None, **kwargs: Any): def __init__(self, type: str = None, **kwargs: Any):
self.type = type self.type = type

Some files were not shown because too many files have changed in this diff Show more