mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-12-28 07:20:17 +01:00
API 4.6 (#1723)
* First take on 4.6 support * improved docs * Minor doc formattings * added poll and poll_answer to filters * added tests, fixed mentioned issues * added poll_answer + poll filter tests * Update docs according to official API docs * introducing pollhandler and pollanswerhandler * First take on 4.6 support * improved docs * Minor doc formattings * added poll and poll_answer to filters * added tests, fixed mentioned issues * added poll_answer + poll filter tests * Update docs according to official API docs * introducing pollhandler and pollanswerhandler * correct_option_id validated with None when trying to send a poll with correct option id 0 it was failing. Now None check is done so that even when 0 is passed it is assigned. * improving example * improving code * adding poll filter example to the pollbot.py * Update Readme * simplify pollbot.py and add some comments * add tests for Poll(Answer)Handler * We just want Filters.poll, not Filters.update.poll * Make test_official fail again * Handle ME.language in M._parse_* Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de> Co-authored-by: Sharun Kumar <715417+sharunkumar@users.noreply.github.com>
This commit is contained in:
parent
8d2c7af1f3
commit
55e3ecf9f8
28 changed files with 1068 additions and 54 deletions
1
.github/workflows/test.yml
vendored
1
.github/workflows/test.yml
vendored
|
@ -90,7 +90,6 @@ jobs:
|
|||
run: |
|
||||
pytest -v tests/test_official.py
|
||||
exit $?
|
||||
continue-on-error: True
|
||||
env:
|
||||
TEST_OFFICIAL: "true"
|
||||
shell: bash --noprofile --norc {0}
|
||||
|
|
|
@ -93,7 +93,7 @@ make the development of bots easy and straightforward. These classes are contain
|
|||
Telegram API support
|
||||
====================
|
||||
|
||||
All types and methods of the Telegram Bot API **4.5** are supported.
|
||||
All types and methods of the Telegram Bot API **4.6** are supported.
|
||||
|
||||
==========
|
||||
Installing
|
||||
|
|
6
docs/source/telegram.keyboardbuttonpolltype.rst
Normal file
6
docs/source/telegram.keyboardbuttonpolltype.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
telegram.KeyboardButtonPollType
|
||||
===============================
|
||||
|
||||
.. autoclass:: telegram.KeyboardButtonPollType
|
||||
:members:
|
||||
:show-inheritance:
|
6
docs/source/telegram.pollanswer.rst
Normal file
6
docs/source/telegram.pollanswer.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
telegram.PollAnswer
|
||||
===================
|
||||
|
||||
.. autoclass:: telegram.PollAnswer
|
||||
:members:
|
||||
:show-inheritance:
|
|
@ -31,6 +31,7 @@ telegram package
|
|||
telegram.inputmediaphoto
|
||||
telegram.inputmediavideo
|
||||
telegram.keyboardbutton
|
||||
telegram.keyboardbuttonpolltype
|
||||
telegram.location
|
||||
telegram.loginurl
|
||||
telegram.message
|
||||
|
@ -38,6 +39,7 @@ telegram package
|
|||
telegram.parsemode
|
||||
telegram.photosize
|
||||
telegram.poll
|
||||
telegram.pollanswer
|
||||
telegram.polloption
|
||||
telegram.replykeyboardremove
|
||||
telegram.replykeyboardmarkup
|
||||
|
|
147
examples/pollbot.py
Normal file
147
examples/pollbot.py
Normal file
|
@ -0,0 +1,147 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
Basic example for a bot that works with polls. Only 3 people are allowed to interact with each
|
||||
poll/quiz the bot generates. The preview command generates a closed poll/quiz, excatly like the
|
||||
one the user sends the bot
|
||||
"""
|
||||
import logging
|
||||
|
||||
from telegram import (Poll, ParseMode, KeyboardButton, KeyboardButtonPollType,
|
||||
ReplyKeyboardMarkup, ReplyKeyboardRemove)
|
||||
from telegram.ext import (Updater, CommandHandler, PollAnswerHandler, PollHandler, MessageHandler,
|
||||
Filters)
|
||||
from telegram.utils.helpers import mention_html
|
||||
|
||||
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def start(update, context):
|
||||
"""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'
|
||||
' to generate a preview for your poll')
|
||||
|
||||
|
||||
def poll(update, context):
|
||||
"""Sends a predefined poll"""
|
||||
questions = ["Good", "Really good", "Fantastic", "Great"]
|
||||
message = context.bot.send_poll(update.effective_user.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
|
||||
payload = {message.poll.id: {"questions": questions, "message_id": message.message_id,
|
||||
"chat_id": update.effective_chat.id, "answers": 0}}
|
||||
context.bot_data.update(payload)
|
||||
|
||||
|
||||
def receive_poll_answer(update, context):
|
||||
"""Summarize a users poll vote"""
|
||||
answer = update.poll_answer
|
||||
poll_id = answer.poll_id
|
||||
try:
|
||||
questions = context.bot_data[poll_id]["questions"]
|
||||
# this means this poll answer update is from an old poll, we can't do our answering then
|
||||
except KeyError:
|
||||
return
|
||||
selected_options = answer.option_ids
|
||||
answer_string = ""
|
||||
for question_id in selected_options:
|
||||
if question_id != selected_options[-1]:
|
||||
answer_string += questions[question_id] + " and "
|
||||
else:
|
||||
answer_string += questions[question_id]
|
||||
user_mention = mention_html(update.effective_user.id, update.effective_user.full_name)
|
||||
context.bot.send_message(context.bot_data[poll_id]["chat_id"],
|
||||
"{} feels {}!".format(user_mention, answer_string),
|
||||
parse_mode=ParseMode.HTML)
|
||||
context.bot_data[poll_id]["answers"] += 1
|
||||
# Close poll after three participants voted
|
||||
if context.bot_data[poll_id]["answers"] == 3:
|
||||
context.bot.stop_poll(context.bot_data[poll_id]["chat_id"],
|
||||
context.bot_data[poll_id]["message_id"])
|
||||
|
||||
|
||||
def quiz(update, context):
|
||||
"""Send a predefined poll"""
|
||||
questions = ["1", "2", "4", "20"]
|
||||
message = update.effective_message.reply_poll("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
|
||||
payload = {message.poll.id: {"chat_id": update.effective_chat.id,
|
||||
"message_id": message.message_id}}
|
||||
context.bot_data.update(payload)
|
||||
|
||||
|
||||
def receive_quiz_answer(update, context):
|
||||
"""Close quiz after three participants took it"""
|
||||
# the bot can receive closed poll updates we don't care about
|
||||
if update.poll.is_closed:
|
||||
return
|
||||
if update.poll.total_voter_count == 3:
|
||||
try:
|
||||
quiz_data = context.bot_data[update.poll.id]
|
||||
# this means this poll answer update is from an old poll, we can't stop it then
|
||||
except KeyError:
|
||||
return
|
||||
context.bot.stop_poll(quiz_data["chat_id"], quiz_data["message_id"])
|
||||
|
||||
|
||||
def preview(update, context):
|
||||
"""Ask user to create a poll and display a preview of it"""
|
||||
# using this without a type lets the user chooses what he wants (quiz or poll)
|
||||
button = [[KeyboardButton("Press me!", request_poll=KeyboardButtonPollType())]]
|
||||
message = "Press the button to let the bot generate a preview for your poll"
|
||||
# using one_time_keyboard to hide the keyboard
|
||||
update.effective_message.reply_text(message,
|
||||
reply_markup=ReplyKeyboardMarkup(button,
|
||||
one_time_keyboard=True))
|
||||
|
||||
|
||||
def receive_poll(update, context):
|
||||
"""On receiving polls, reply to it by a closed poll copying the received poll"""
|
||||
actual_poll = update.effective_message.poll
|
||||
# Only need to set the question and options, since all other parameters don't matter for
|
||||
# a closed poll
|
||||
update.effective_message.reply_poll(
|
||||
question=actual_poll.question,
|
||||
options=[o.text for o in actual_poll.options],
|
||||
# with is_closed true, the poll/quiz is immediately closed
|
||||
is_closed=True,
|
||||
reply_markup=ReplyKeyboardRemove()
|
||||
)
|
||||
|
||||
|
||||
def help_handler(update, context):
|
||||
"""Display a help message"""
|
||||
update.message.reply_text("Use /quiz, /poll or /preview to test this "
|
||||
"bot.")
|
||||
|
||||
|
||||
def main():
|
||||
# Create the Updater and pass it your bot's token.
|
||||
# Make sure to set use_context=True to use the new context based callbacks
|
||||
# Post version 12 this will no longer be necessary
|
||||
updater = Updater("TOKEN", use_context=True)
|
||||
dp = updater.dispatcher
|
||||
dp.add_handler(CommandHandler('start', start))
|
||||
dp.add_handler(CommandHandler('poll', poll))
|
||||
dp.add_handler(PollAnswerHandler(receive_poll_answer))
|
||||
dp.add_handler(CommandHandler('quiz', quiz))
|
||||
dp.add_handler(PollHandler(receive_quiz_answer))
|
||||
dp.add_handler(CommandHandler('preview', preview))
|
||||
dp.add_handler(MessageHandler(Filters.poll, receive_poll))
|
||||
dp.add_handler(CommandHandler('help', help_handler))
|
||||
|
||||
# Start the Bot
|
||||
updater.start_polling()
|
||||
|
||||
# Run the bot until the user presses Ctrl-C or the process receives SIGINT,
|
||||
# SIGTERM or SIGABRT
|
||||
updater.idle()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -38,6 +38,7 @@ from .files.videonote import VideoNote
|
|||
from .chataction import ChatAction
|
||||
from .userprofilephotos import UserProfilePhotos
|
||||
from .keyboardbutton import KeyboardButton
|
||||
from .keyboardbuttonpolltype import KeyboardButtonPollType
|
||||
from .replymarkup import ReplyMarkup
|
||||
from .replykeyboardmarkup import ReplyKeyboardMarkup
|
||||
from .replykeyboardremove import ReplyKeyboardRemove
|
||||
|
@ -48,7 +49,7 @@ from .files.file import File
|
|||
from .parsemode import ParseMode
|
||||
from .messageentity import MessageEntity
|
||||
from .games.game import Game
|
||||
from .poll import Poll, PollOption
|
||||
from .poll import Poll, PollOption, PollAnswer
|
||||
from .loginurl import LoginUrl
|
||||
from .games.callbackgame import CallbackGame
|
||||
from .payment.shippingaddress import ShippingAddress
|
||||
|
@ -138,7 +139,7 @@ __all__ = [
|
|||
'InlineQueryResultPhoto', 'InlineQueryResultVenue', 'InlineQueryResultVideo',
|
||||
'InlineQueryResultVoice', 'InlineQueryResultGame', 'InputContactMessageContent', 'InputFile',
|
||||
'InputLocationMessageContent', 'InputMessageContent', 'InputTextMessageContent',
|
||||
'InputVenueMessageContent', 'KeyboardButton', 'Location', 'EncryptedCredentials',
|
||||
'InputVenueMessageContent', 'Location', 'EncryptedCredentials',
|
||||
'PassportFile', 'EncryptedPassportElement', 'PassportData', 'Message', 'MessageEntity',
|
||||
'ParseMode', 'PhotoSize', 'ReplyKeyboardRemove', 'ReplyKeyboardMarkup', 'ReplyMarkup',
|
||||
'Sticker', 'TelegramError', 'TelegramObject', 'Update', 'User', 'UserProfilePhotos', 'Venue',
|
||||
|
@ -156,5 +157,5 @@ __all__ = [
|
|||
'InputMediaAudio', 'InputMediaDocument', 'TelegramDecryptionError',
|
||||
'PassportElementErrorSelfie', 'PassportElementErrorTranslationFile',
|
||||
'PassportElementErrorTranslationFiles', 'PassportElementErrorUnspecified', 'Poll',
|
||||
'PollOption', 'LoginUrl'
|
||||
'PollOption', 'PollAnswer', 'LoginUrl', 'KeyboardButton', 'KeyboardButtonPollType',
|
||||
]
|
||||
|
|
|
@ -230,6 +230,27 @@ class Bot(TelegramObject):
|
|||
|
||||
return "https://t.me/{}".format(self.username)
|
||||
|
||||
@property
|
||||
@info
|
||||
def can_join_groups(self):
|
||||
""":obj:`str`: Bot's can_join_groups attribute."""
|
||||
|
||||
return self.bot.can_join_groups
|
||||
|
||||
@property
|
||||
@info
|
||||
def can_read_all_group_messages(self):
|
||||
""":obj:`str`: Bot's can_read_all_group_messages attribute."""
|
||||
|
||||
return self.bot.can_read_all_group_messages
|
||||
|
||||
@property
|
||||
@info
|
||||
def supports_inline_queries(self):
|
||||
""":obj:`str`: Bot's supports_inline_queries attribute."""
|
||||
|
||||
return self.bot.supports_inline_queries
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""":obj:`str`: Bot's @username."""
|
||||
|
@ -3492,18 +3513,33 @@ class Bot(TelegramObject):
|
|||
chat_id,
|
||||
question,
|
||||
options,
|
||||
is_anonymous=True,
|
||||
type=Poll.REGULAR,
|
||||
allows_multiple_answers=False,
|
||||
correct_option_id=None,
|
||||
is_closed=None,
|
||||
disable_notification=None,
|
||||
reply_to_message_id=None,
|
||||
reply_markup=None,
|
||||
timeout=None,
|
||||
**kwargs):
|
||||
"""
|
||||
Use this method to send a native poll. A native poll can't be sent to a private chat.
|
||||
Use this method to send a native poll.
|
||||
|
||||
Args:
|
||||
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target private chat.
|
||||
question (:obj:`str`): Poll question, 1-255 characters.
|
||||
options (List[:obj:`str`]): List of answer options, 2-10 strings 1-100 characters each.
|
||||
is_anonymous (:obj:`bool`, optional): True, if the poll needs to be anonymous,
|
||||
defaults to True.
|
||||
type (:obj:`str`, optional): Poll type, :attr:`telegram.Poll.QUIZ` or
|
||||
:attr:`telegram.Poll.REGULAR`, defaults to :attr:`telegram.Poll.REGULAR`.
|
||||
allows_multiple_answers (:obj:`bool`, optional): True, if the poll allows multiple
|
||||
answers, ignored for polls in quiz mode, defaults to False
|
||||
correct_option_id (:obj:`int`, optional): 0-based identifier of the correct answer
|
||||
option, required for polls in quiz mode
|
||||
is_closed (:obj:`bool`, optional): Pass True, if the poll needs to be immediately
|
||||
closed. This can be useful for poll preview.
|
||||
disable_notification (:obj:`bool`, optional): Sends the message silently. Users will
|
||||
receive a notification with no sound.
|
||||
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
|
||||
|
@ -3531,6 +3567,17 @@ class Bot(TelegramObject):
|
|||
'options': options
|
||||
}
|
||||
|
||||
if not is_anonymous:
|
||||
data['is_anonymous'] = is_anonymous
|
||||
if type:
|
||||
data['type'] = type
|
||||
if allows_multiple_answers:
|
||||
data['allows_multiple_answers'] = allows_multiple_answers
|
||||
if correct_option_id is not None:
|
||||
data['correct_option_id'] = correct_option_id
|
||||
if is_closed:
|
||||
data['is_closed'] = is_closed
|
||||
|
||||
return self._message(url, data, timeout=timeout, disable_notification=disable_notification,
|
||||
reply_to_message_id=reply_to_message_id, reply_markup=reply_markup,
|
||||
**kwargs)
|
||||
|
|
|
@ -41,6 +41,8 @@ from .precheckoutqueryhandler import PreCheckoutQueryHandler
|
|||
from .shippingqueryhandler import ShippingQueryHandler
|
||||
from .messagequeue import MessageQueue
|
||||
from .messagequeue import DelayQueue
|
||||
from .pollanswerhandler import PollAnswerHandler
|
||||
from .pollhandler import PollHandler
|
||||
from .defaults import Defaults
|
||||
|
||||
__all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler',
|
||||
|
@ -49,4 +51,5 @@ __all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler',
|
|||
'StringRegexHandler', 'TypeHandler', 'ConversationHandler',
|
||||
'PreCheckoutQueryHandler', 'ShippingQueryHandler', 'MessageQueue', 'DelayQueue',
|
||||
'DispatcherHandlerStop', 'run_async', 'CallbackContext', 'BasePersistence',
|
||||
'PicklePersistence', 'DictPersistence', 'PrefixHandler', 'Defaults')
|
||||
'PicklePersistence', 'DictPersistence', 'PrefixHandler', 'PollAnswerHandler',
|
||||
'PollHandler', 'Defaults')
|
||||
|
|
85
telegram/ext/pollanswerhandler.py
Normal file
85
telegram/ext/pollanswerhandler.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2019
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
from telegram import Update
|
||||
from .handler import Handler
|
||||
|
||||
|
||||
class PollAnswerHandler(Handler):
|
||||
"""Handler class to handle Telegram updates that contain a poll answer.
|
||||
|
||||
Attributes:
|
||||
callback (:obj:`callable`): The callback function for this handler.
|
||||
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
|
||||
passed to the callback function.
|
||||
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
|
||||
the callback function.
|
||||
pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to
|
||||
the callback function.
|
||||
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
|
||||
the callback function.
|
||||
|
||||
Note:
|
||||
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
||||
can use to keep any data in will be sent to the :attr:`callback` function. Related to
|
||||
either the user or the chat that the update was sent in. For each update from the same user
|
||||
or in the same chat, it will be the same ``dict``.
|
||||
|
||||
Note that this is DEPRECATED, and you should use context based callbacks. See
|
||||
https://git.io/fxJuV for more info.
|
||||
|
||||
Args:
|
||||
callback (:obj:`callable`): The callback function for this handler. Will be called when
|
||||
:attr:`check_update` has determined that an update should be processed by this handler.
|
||||
Callback signature for context based API:
|
||||
|
||||
``def callback(update: Update, context: CallbackContext)``
|
||||
|
||||
The return value of the callback is usually ignored except for the special case of
|
||||
:class:`telegram.ext.ConversationHandler`.
|
||||
pass_update_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called
|
||||
``update_queue`` will be passed to the callback function. It will be the ``Queue``
|
||||
instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher`
|
||||
that contains new updates which can be used to insert updates. Default is ``False``.
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
pass_job_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called
|
||||
``job_queue`` will be passed to the callback function. It will be a
|
||||
:class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
|
||||
which can be used to schedule new jobs. Default is ``False``.
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
pass_user_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
|
||||
``user_data`` will be passed to the callback function. Default is ``False``.
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
pass_chat_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
|
||||
``chat_data`` will be passed to the callback function. Default is ``False``.
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
|
||||
"""
|
||||
|
||||
def check_update(self, update):
|
||||
"""Determines whether an update should be passed to this handlers :attr:`callback`.
|
||||
|
||||
Args:
|
||||
update (:class:`telegram.Update`): Incoming telegram update.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`
|
||||
|
||||
"""
|
||||
return isinstance(update, Update) and update.poll_answer
|
85
telegram/ext/pollhandler.py
Normal file
85
telegram/ext/pollhandler.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2019
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
from telegram import Update
|
||||
from .handler import Handler
|
||||
|
||||
|
||||
class PollHandler(Handler):
|
||||
"""Handler class to handle Telegram updates that contain a poll.
|
||||
|
||||
Attributes:
|
||||
callback (:obj:`callable`): The callback function for this handler.
|
||||
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
|
||||
passed to the callback function.
|
||||
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
|
||||
the callback function.
|
||||
pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to
|
||||
the callback function.
|
||||
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
|
||||
the callback function.
|
||||
|
||||
Note:
|
||||
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
||||
can use to keep any data in will be sent to the :attr:`callback` function. Related to
|
||||
either the user or the chat that the update was sent in. For each update from the same user
|
||||
or in the same chat, it will be the same ``dict``.
|
||||
|
||||
Note that this is DEPRECATED, and you should use context based callbacks. See
|
||||
https://git.io/fxJuV for more info.
|
||||
|
||||
Args:
|
||||
callback (:obj:`callable`): The callback function for this handler. Will be called when
|
||||
:attr:`check_update` has determined that an update should be processed by this handler.
|
||||
Callback signature for context based API:
|
||||
|
||||
``def callback(update: Update, context: CallbackContext)``
|
||||
|
||||
The return value of the callback is usually ignored except for the special case of
|
||||
:class:`telegram.ext.ConversationHandler`.
|
||||
pass_update_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called
|
||||
``update_queue`` will be passed to the callback function. It will be the ``Queue``
|
||||
instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher`
|
||||
that contains new updates which can be used to insert updates. Default is ``False``.
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
pass_job_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called
|
||||
``job_queue`` will be passed to the callback function. It will be a
|
||||
:class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
|
||||
which can be used to schedule new jobs. Default is ``False``.
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
pass_user_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
|
||||
``user_data`` will be passed to the callback function. Default is ``False``.
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
pass_chat_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
|
||||
``chat_data`` will be passed to the callback function. Default is ``False``.
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
|
||||
"""
|
||||
|
||||
def check_update(self, update):
|
||||
"""Determines whether an update should be passed to this handlers :attr:`callback`.
|
||||
|
||||
Args:
|
||||
update (:class:`telegram.Update`): Incoming telegram update.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`
|
||||
|
||||
"""
|
||||
return isinstance(update, Update) and update.poll
|
|
@ -33,6 +33,7 @@ class KeyboardButton(TelegramObject):
|
|||
text (:obj:`str`): Text of the button.
|
||||
request_contact (:obj:`bool`): Optional. If the user's phone number will be sent.
|
||||
request_location (:obj:`bool`): Optional. If the user's current location will be sent.
|
||||
request_poll (:class:`KeyboardButtonPollType`): Optional. If the user should create a poll.
|
||||
|
||||
Args:
|
||||
text (:obj:`str`): Text of the button. If none of the optional fields are used, it will be
|
||||
|
@ -41,16 +42,24 @@ class KeyboardButton(TelegramObject):
|
|||
a contact when the button is pressed. Available in private chats only.
|
||||
request_location (:obj:`bool`, optional): If True, the user's current location will be sent
|
||||
when the button is pressed. Available in private chats only.
|
||||
request_poll (:class:`KeyboardButtonPollType`, optional): If specified, the user will be
|
||||
asked to create a poll and send it to the bot when the button is pressed. Available in
|
||||
private chats only.
|
||||
|
||||
Note:
|
||||
:attr:`request_contact` and :attr:`request_location` options will only work in Telegram
|
||||
versions released after 9 April, 2016. Older clients will ignore them.
|
||||
|
||||
:attr:`request_poll` option will only work in Telegram versions released after 23 January,
|
||||
2020. Older clients will receive unsupported message.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, text, request_contact=None, request_location=None, **kwargs):
|
||||
def __init__(self, text, request_contact=None, request_location=None, request_poll=None,
|
||||
**kwargs):
|
||||
# Required
|
||||
self.text = text
|
||||
# Optionals
|
||||
self.request_contact = request_contact
|
||||
self.request_location = request_location
|
||||
self.request_poll = request_poll
|
||||
|
|
37
telegram/keyboardbuttonpolltype.py
Normal file
37
telegram/keyboardbuttonpolltype.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/env python
|
||||
# pylint: disable=R0903
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2020
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a type of a Telegram Poll."""
|
||||
from telegram import TelegramObject
|
||||
|
||||
|
||||
class KeyboardButtonPollType(TelegramObject):
|
||||
"""This object represents type of a poll, which is allowed to be created
|
||||
and sent when the corresponding button is pressed.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Optional. If :attr:`telegram.Poll.QUIZ` is passed, the user will be
|
||||
allowed to create only polls in the quiz mode. If :attr:`telegram.Poll.REGULAR` is
|
||||
passed, only regular polls will be allowed. Otherwise, the user will be allowed to
|
||||
create a poll of any type.
|
||||
"""
|
||||
def __init__(self, type=None):
|
||||
self.type = type
|
||||
|
||||
self._id_attrs = (self.type,)
|
|
@ -923,6 +923,22 @@ class Message(TelegramObject):
|
|||
return self.bot.delete_message(
|
||||
chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs)
|
||||
|
||||
def stop_poll(self, *args, **kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
bot.stop_poll(chat_id=message.chat_id,
|
||||
message_id=message.message_id,
|
||||
*args,
|
||||
**kwargs)
|
||||
|
||||
Returns:
|
||||
:class:`telegram.Poll`: On success, the stopped Poll with the
|
||||
final results is returned.
|
||||
|
||||
"""
|
||||
return self.bot.stop_poll(
|
||||
chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs)
|
||||
|
||||
def parse_entity(self, entity):
|
||||
"""Returns the text from a given :class:`telegram.MessageEntity`.
|
||||
|
||||
|
@ -1078,7 +1094,11 @@ class Message(TelegramObject):
|
|||
elif entity.type == MessageEntity.CODE:
|
||||
insert = '<code>' + text + '</code>'
|
||||
elif entity.type == MessageEntity.PRE:
|
||||
insert = '<pre>' + text + '</pre>'
|
||||
if entity.language:
|
||||
insert = '<pre><code class="{}">{}</code></pre>'.format(entity.language,
|
||||
text)
|
||||
else:
|
||||
insert = '<pre>' + text + '</pre>'
|
||||
elif entity.type == MessageEntity.UNDERLINE:
|
||||
insert = '<u>' + text + '</u>'
|
||||
elif entity.type == MessageEntity.STRIKETHROUGH:
|
||||
|
@ -1236,10 +1256,13 @@ class Message(TelegramObject):
|
|||
# Monospace needs special escaping. Also can't have entities nested within
|
||||
code = escape_markdown(orig_text, version=version,
|
||||
entity_type=MessageEntity.PRE)
|
||||
if code.startswith('\\'):
|
||||
prefix = '```'
|
||||
if entity.language:
|
||||
prefix = '```' + entity.language + '\n'
|
||||
else:
|
||||
prefix = '```\n'
|
||||
if code.startswith('\\'):
|
||||
prefix = '```'
|
||||
else:
|
||||
prefix = '```\n'
|
||||
insert = prefix + code + '```'
|
||||
elif entity.type == MessageEntity.UNDERLINE:
|
||||
if version == 1:
|
||||
|
|
|
@ -32,6 +32,8 @@ class MessageEntity(TelegramObject):
|
|||
length (:obj:`int`): Length of the entity in UTF-16 code units.
|
||||
url (:obj:`str`): Optional. Url that will be opened after user taps on the text.
|
||||
user (:class:`telegram.User`): Optional. The mentioned user.
|
||||
language (:obj:`str`): Optional. Programming language of the entity
|
||||
text
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): Type of the entity. Can be mention (@username), hashtag, bot_command,
|
||||
|
@ -40,13 +42,16 @@ class MessageEntity(TelegramObject):
|
|||
without usernames).
|
||||
offset (:obj:`int`): Offset in UTF-16 code units to the start of the entity.
|
||||
length (:obj:`int`): Length of the entity in UTF-16 code units.
|
||||
url (:obj:`str`, optional): For "text_link" only, url that will be opened after usertaps on
|
||||
the text.
|
||||
user (:class:`telegram.User`, optional): For "text_mention" only, the mentioned user.
|
||||
url (:obj:`str`, optional): For :attr:`TEXT_LINK` only, url that will be opened after
|
||||
usertaps on the text.
|
||||
user (:class:`telegram.User`, optional): For :attr:`TEXT_MENTION` only, the mentioned
|
||||
user.
|
||||
language (:obj:`str`, optional): For :attr:`PRE` only, the programming language of
|
||||
the entity text
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, type, offset, length, url=None, user=None, **kwargs):
|
||||
def __init__(self, type, offset, length, url=None, user=None, language=None, **kwargs):
|
||||
# Required
|
||||
self.type = type
|
||||
self.offset = offset
|
||||
|
@ -54,6 +59,7 @@ class MessageEntity(TelegramObject):
|
|||
# Optionals
|
||||
self.url = url
|
||||
self.user = user
|
||||
self.language = language
|
||||
|
||||
self._id_attrs = (self.type, self.offset, self.length)
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram Poll."""
|
||||
|
||||
from telegram import (TelegramObject)
|
||||
from telegram import (TelegramObject, User)
|
||||
|
||||
|
||||
class PollOption(TelegramObject):
|
||||
|
@ -48,6 +48,39 @@ class PollOption(TelegramObject):
|
|||
return cls(**data)
|
||||
|
||||
|
||||
class PollAnswer(TelegramObject):
|
||||
"""
|
||||
This object represents an answer of a user in a non-anonymous poll.
|
||||
|
||||
Attributes:
|
||||
poll_id (:obj:`str`): Unique poll identifier.
|
||||
user (:class:`telegram.User`): The user, who changed the answer to the poll.
|
||||
option_ids (List[:obj:`int`]): Identifiers of answer options, chosen by the user.
|
||||
|
||||
Args:
|
||||
poll_id (:obj:`str`): Unique poll identifier.
|
||||
user (:class:`telegram.User`): The user, who changed the answer to the poll.
|
||||
option_ids (List[:obj:`int`]): 0-based identifiers of answer options, chosen by the user.
|
||||
May be empty if the user retracted their vote.
|
||||
|
||||
"""
|
||||
def __init__(self, poll_id, user, option_ids, **kwargs):
|
||||
self.poll_id = poll_id
|
||||
self.user = user
|
||||
self.option_ids = option_ids
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data, bot):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = super(PollAnswer, cls).de_json(data, bot)
|
||||
|
||||
data['user'] = User.de_json(data.get('user'), bot)
|
||||
|
||||
return cls(**data)
|
||||
|
||||
|
||||
class Poll(TelegramObject):
|
||||
"""
|
||||
This object contains information about a poll.
|
||||
|
@ -56,21 +89,38 @@ class Poll(TelegramObject):
|
|||
id (:obj:`str`): Unique poll identifier.
|
||||
question (:obj:`str`): Poll question, 1-255 characters.
|
||||
options (List[:class:`PollOption`]): List of poll options.
|
||||
total_voter_count (:obj:`int`): Total number of users that voted in the poll.
|
||||
is_closed (:obj:`bool`): True, if the poll is closed.
|
||||
is_anonymous (:obj:`bool`): True, if the poll is anonymous.
|
||||
type (:obj:`str`): Poll type, currently can be :attr:`REGULAR` or :attr:`QUIZ`.
|
||||
allows_multiple_answers (:obj:`bool`): True, if the poll allows multiple answers.
|
||||
correct_option_id (:obj:`int`): Optional. Identifier of the correct answer option.
|
||||
|
||||
Args:
|
||||
id (:obj:`str`): Unique poll identifier.
|
||||
question (:obj:`str`): Poll question, 1-255 characters.
|
||||
options (List[:class:`PollOption`]): List of poll options.
|
||||
is_closed (:obj:`bool`): True, if the poll is closed.
|
||||
is_anonymous (:obj:`bool`): True, if the poll is anonymous.
|
||||
type (:obj:`str`): Poll type, currently can be :attr:`REGULAR` or :attr:`QUIZ`.
|
||||
allows_multiple_answers (:obj:`bool`): True, if the poll allows multiple answers.
|
||||
correct_option_id (:obj:`int`, optional): 0-based identifier of the correct answer option.
|
||||
Available only for polls in the quiz mode, which are closed, or was sent (not
|
||||
forwarded) by the bot or to the private chat with the bot.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, id, question, options, is_closed, **kwargs):
|
||||
def __init__(self, id, question, options, total_voter_count, is_closed, is_anonymous, type,
|
||||
allows_multiple_answers, correct_option_id=None, **kwargs):
|
||||
self.id = id
|
||||
self.question = question
|
||||
self.options = options
|
||||
self.total_voter_count = total_voter_count
|
||||
self.is_closed = is_closed
|
||||
self.is_anonymous = is_anonymous
|
||||
self.type = type
|
||||
self.allows_multiple_answers = allows_multiple_answers
|
||||
self.correct_option_id = correct_option_id
|
||||
|
||||
self._id_attrs = (self.id,)
|
||||
|
||||
|
@ -91,3 +141,8 @@ class Poll(TelegramObject):
|
|||
data['options'] = [x.to_dict() for x in self.options]
|
||||
|
||||
return data
|
||||
|
||||
REGULAR = "regular"
|
||||
""":obj:`str`: 'regular'"""
|
||||
QUIZ = "quiz"
|
||||
""":obj:`str`: 'quiz'"""
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
from telegram import (Message, TelegramObject, InlineQuery, ChosenInlineResult,
|
||||
CallbackQuery, ShippingQuery, PreCheckoutQuery, Poll)
|
||||
from telegram.poll import PollAnswer
|
||||
|
||||
|
||||
class Update(TelegramObject):
|
||||
|
@ -42,7 +43,10 @@ class Update(TelegramObject):
|
|||
pre_checkout_query (:class:`telegram.PreCheckoutQuery`): Optional. New incoming
|
||||
pre-checkout query.
|
||||
poll (:class:`telegram.Poll`): Optional. New poll state. Bots receive only updates
|
||||
about polls, which are sent or stopped by the bot
|
||||
about stopped polls and polls, which are sent by the bot
|
||||
poll_answer (:class:`telegram.PollAnswer`): Optional. A user changed their answer
|
||||
in a non-anonymous poll. Bots receive new votes only in polls that were sent
|
||||
by the bot itself.
|
||||
|
||||
Args:
|
||||
update_id (:obj:`int`): The update's unique identifier. Update identifiers start from a
|
||||
|
@ -67,6 +71,9 @@ class Update(TelegramObject):
|
|||
pre-checkout query. Contains full information about checkout
|
||||
poll (:class:`telegram.Poll`, optional): New poll state. Bots receive only updates
|
||||
about polls, which are sent or stopped by the bot
|
||||
poll_answer (:class:`telegram.PollAnswer`, optional): A user changed their answer
|
||||
in a non-anonymous poll. Bots receive new votes only in polls that were sent
|
||||
by the bot itself.
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
"""
|
||||
|
@ -83,6 +90,7 @@ class Update(TelegramObject):
|
|||
shipping_query=None,
|
||||
pre_checkout_query=None,
|
||||
poll=None,
|
||||
poll_answer=None,
|
||||
**kwargs):
|
||||
# Required
|
||||
self.update_id = int(update_id)
|
||||
|
@ -97,6 +105,7 @@ class Update(TelegramObject):
|
|||
self.channel_post = channel_post
|
||||
self.edited_channel_post = edited_channel_post
|
||||
self.poll = poll
|
||||
self.poll_answer = poll_answer
|
||||
|
||||
self._effective_user = None
|
||||
self._effective_chat = None
|
||||
|
@ -137,6 +146,9 @@ class Update(TelegramObject):
|
|||
elif self.pre_checkout_query:
|
||||
user = self.pre_checkout_query.from_user
|
||||
|
||||
elif self.poll_answer:
|
||||
user = self.poll_answer.user
|
||||
|
||||
self._effective_user = user
|
||||
return user
|
||||
|
||||
|
@ -146,7 +158,8 @@ class Update(TelegramObject):
|
|||
:class:`telegram.Chat`: The chat that this update was sent in, no matter what kind of
|
||||
update this is. Will be ``None`` for :attr:`inline_query`,
|
||||
:attr:`chosen_inline_result`, :attr:`callback_query` from inline messages,
|
||||
:attr:`shipping_query`, :attr:`pre_checkout_query` and :attr:`poll`.
|
||||
:attr:`shipping_query`, :attr:`pre_checkout_query`, :attr:`poll` and
|
||||
:attr:`poll_answer`.
|
||||
|
||||
"""
|
||||
if self._effective_chat:
|
||||
|
@ -178,7 +191,8 @@ class Update(TelegramObject):
|
|||
:class:`telegram.Message`: The message included in this update, no matter what kind of
|
||||
update this is. Will be ``None`` for :attr:`inline_query`,
|
||||
:attr:`chosen_inline_result`, :attr:`callback_query` from inline messages,
|
||||
:attr:`shipping_query`, :attr:`pre_checkout_query` and :attr:`poll`.
|
||||
:attr:`shipping_query`, :attr:`pre_checkout_query`, :attr:`poll` and
|
||||
:attr:`poll_answer`.
|
||||
|
||||
"""
|
||||
if self._effective_message:
|
||||
|
@ -237,5 +251,6 @@ class Update(TelegramObject):
|
|||
edited_channel_post['default_quote'] = data.get('default_quote')
|
||||
data['edited_channel_post'] = Message.de_json(edited_channel_post, bot)
|
||||
data['poll'] = Poll.de_json(data.get('poll'), bot)
|
||||
data['poll_answer'] = PollAnswer.de_json(data.get('poll_answer'), bot)
|
||||
|
||||
return cls(**data)
|
||||
|
|
|
@ -34,6 +34,12 @@ class User(TelegramObject):
|
|||
last_name (:obj:`str`): Optional. User's or bot's last name.
|
||||
username (:obj:`str`): Optional. User's or bot's username.
|
||||
language_code (:obj:`str`): Optional. IETF language tag of the user's language.
|
||||
can_join_groups (:obj:`str`): Optional. True, if the bot can be invited to groups.
|
||||
Returned only in :attr:`telegram.Bot.get_me` requests.
|
||||
can_read_all_group_messages (:obj:`str`): Optional. True, if privacy mode is disabled
|
||||
for the bot. Returned only in :attr:`telegram.Bot.get_me` requests.
|
||||
supports_inline_queries (:obj:`str`): Optional. True, if the bot supports inline queries.
|
||||
Returned only in :attr:`telegram.Bot.get_me` requests.
|
||||
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
|
||||
|
||||
Args:
|
||||
|
@ -43,6 +49,12 @@ class User(TelegramObject):
|
|||
last_name (:obj:`str`, optional): User's or bot's last name.
|
||||
username (:obj:`str`, optional): User's or bot's username.
|
||||
language_code (:obj:`str`, optional): IETF language tag of the user's language.
|
||||
can_join_groups (:obj:`str`, optional): True, if the bot can be invited to groups.
|
||||
Returned only in :attr:`telegram.Bot.get_me` requests.
|
||||
can_read_all_group_messages (:obj:`str`, optional): True, if privacy mode is disabled
|
||||
for the bot. Returned only in :attr:`telegram.Bot.get_me` requests.
|
||||
supports_inline_queries (:obj:`str`, optional): True, if the bot supports inline queries.
|
||||
Returned only in :attr:`telegram.Bot.get_me` requests.
|
||||
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
|
||||
|
||||
"""
|
||||
|
@ -54,6 +66,9 @@ class User(TelegramObject):
|
|||
last_name=None,
|
||||
username=None,
|
||||
language_code=None,
|
||||
can_join_groups=None,
|
||||
can_read_all_group_messages=None,
|
||||
supports_inline_queries=None,
|
||||
bot=None,
|
||||
**kwargs):
|
||||
# Required
|
||||
|
@ -64,7 +79,9 @@ class User(TelegramObject):
|
|||
self.last_name = last_name
|
||||
self.username = username
|
||||
self.language_code = language_code
|
||||
|
||||
self.can_join_groups = can_join_groups
|
||||
self.can_read_all_group_messages = can_read_all_group_messages
|
||||
self.supports_inline_queries = supports_inline_queries
|
||||
self.bot = bot
|
||||
|
||||
self._id_attrs = (self.id,)
|
||||
|
@ -99,7 +116,6 @@ class User(TelegramObject):
|
|||
def de_json(cls, data, bot):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = super(User, cls).de_json(data, bot)
|
||||
|
||||
return cls(bot=bot, **data)
|
||||
|
|
|
@ -87,6 +87,9 @@ class TestBot(object):
|
|||
assert get_me_bot.first_name == bot.first_name
|
||||
assert get_me_bot.last_name == bot.last_name
|
||||
assert get_me_bot.name == bot.name
|
||||
assert get_me_bot.can_join_groups == bot.can_join_groups
|
||||
assert get_me_bot.can_read_all_group_messages == bot.can_read_all_group_messages
|
||||
assert get_me_bot.supports_inline_queries == bot.supports_inline_queries
|
||||
assert 'https://t.me/{}'.format(get_me_bot.username) == bot.link
|
||||
|
||||
@flaky(3, 1)
|
||||
|
@ -175,14 +178,17 @@ class TestBot(object):
|
|||
question = 'Is this a test?'
|
||||
answers = ['Yes', 'No', 'Maybe']
|
||||
message = bot.send_poll(chat_id=super_group_id, question=question, options=answers,
|
||||
timeout=60)
|
||||
is_anonymous=False, allows_multiple_answers=True, timeout=60)
|
||||
|
||||
assert message.poll
|
||||
assert message.poll.question == question
|
||||
assert message.poll.options[0].text == answers[0]
|
||||
assert message.poll.options[1].text == answers[1]
|
||||
assert message.poll.options[2].text == answers[2]
|
||||
assert not message.poll.is_anonymous
|
||||
assert message.poll.allows_multiple_answers
|
||||
assert not message.poll.is_closed
|
||||
assert message.poll.type == Poll.REGULAR
|
||||
|
||||
poll = bot.stop_poll(chat_id=super_group_id, message_id=message.message_id, timeout=60)
|
||||
assert isinstance(poll, Poll)
|
||||
|
@ -194,6 +200,13 @@ class TestBot(object):
|
|||
assert poll.options[2].text == answers[2]
|
||||
assert poll.options[2].voter_count == 0
|
||||
assert poll.question == question
|
||||
assert poll.total_voter_count == 0
|
||||
|
||||
message_quiz = bot.send_poll(chat_id=super_group_id, question=question, options=answers,
|
||||
type=Poll.QUIZ, correct_option_id=2, is_closed=True)
|
||||
assert message_quiz.poll.correct_option_id == 2
|
||||
assert message_quiz.poll.type == Poll.QUIZ
|
||||
assert message_quiz.poll.is_closed
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
|
|
|
@ -20,24 +20,28 @@
|
|||
import pytest
|
||||
|
||||
from telegram import KeyboardButton
|
||||
from telegram.keyboardbuttonpolltype import KeyboardButtonPollType
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def keyboard_button():
|
||||
return KeyboardButton(TestKeyboardButton.text,
|
||||
request_location=TestKeyboardButton.request_location,
|
||||
request_contact=TestKeyboardButton.request_contact)
|
||||
request_contact=TestKeyboardButton.request_contact,
|
||||
request_poll=TestKeyboardButton.request_poll)
|
||||
|
||||
|
||||
class TestKeyboardButton(object):
|
||||
text = 'text'
|
||||
request_location = True
|
||||
request_contact = True
|
||||
request_poll = KeyboardButtonPollType("quiz")
|
||||
|
||||
def test_expected_values(self, keyboard_button):
|
||||
assert keyboard_button.text == self.text
|
||||
assert keyboard_button.request_location == self.request_location
|
||||
assert keyboard_button.request_contact == self.request_contact
|
||||
assert keyboard_button.request_poll == self.request_poll
|
||||
|
||||
def test_to_dict(self, keyboard_button):
|
||||
keyboard_button_dict = keyboard_button.to_dict()
|
||||
|
@ -46,3 +50,4 @@ class TestKeyboardButton(object):
|
|||
assert keyboard_button_dict['text'] == keyboard_button.text
|
||||
assert keyboard_button_dict['request_location'] == keyboard_button.request_location
|
||||
assert keyboard_button_dict['request_contact'] == keyboard_button.request_contact
|
||||
assert keyboard_button_dict['request_poll'] == keyboard_button.request_poll.to_dict()
|
||||
|
|
47
tests/test_keyboardbuttonpolltype.py
Normal file
47
tests/test_keyboardbuttonpolltype.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2020
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import pytest
|
||||
|
||||
from telegram import KeyboardButtonPollType, Poll
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def keyboard_button_poll_type():
|
||||
return KeyboardButtonPollType(TestKeyboardButtonPollType.type)
|
||||
|
||||
|
||||
class TestKeyboardButtonPollType(object):
|
||||
type = Poll.QUIZ
|
||||
|
||||
def test_to_dict(self, keyboard_button_poll_type):
|
||||
keyboard_button_poll_type_dict = keyboard_button_poll_type.to_dict()
|
||||
assert isinstance(keyboard_button_poll_type_dict, dict)
|
||||
assert keyboard_button_poll_type_dict['type'] == self.type
|
||||
|
||||
def test_equality(self):
|
||||
a = KeyboardButtonPollType(Poll.QUIZ)
|
||||
b = KeyboardButtonPollType(Poll.QUIZ)
|
||||
c = KeyboardButtonPollType(Poll.REGULAR)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
|
@ -90,7 +90,9 @@ def message(bot):
|
|||
{'passport_data': PassportData.de_json(RAW_PASSPORT_DATA, None)},
|
||||
{'poll': Poll(id='abc', question='What is this?',
|
||||
options=[PollOption(text='a', voter_count=1),
|
||||
PollOption(text='b', voter_count=2)], is_closed=False)},
|
||||
PollOption(text='b', voter_count=2)], is_closed=False,
|
||||
total_voter_count=0, is_anonymous=False, type=Poll.REGULAR,
|
||||
allows_multiple_answers=True)},
|
||||
{'text': 'a text message', 'reply_markup': {'inline_keyboard': [[{
|
||||
'text': 'start', 'url': 'http://google.com'}, {
|
||||
'text': 'next', 'callback_data': 'abcd'}],
|
||||
|
@ -126,7 +128,7 @@ class TestMessage(object):
|
|||
'url': 'http://github.com/ab_'},
|
||||
{'length': 12, 'offset': 38, 'type': 'text_mention',
|
||||
'user': User(123456789, 'mentioned user', False)},
|
||||
{'length': 3, 'offset': 55, 'type': 'pre'},
|
||||
{'length': 3, 'offset': 55, 'type': 'pre', 'language': 'python'},
|
||||
{'length': 21, 'offset': 60, 'type': 'url'}]
|
||||
test_text = 'Test for <bold, ita_lic, code, links, text-mention and pre. http://google.com/ab_'
|
||||
test_entities_v2 = [{'length': 4, 'offset': 0, 'type': 'underline'},
|
||||
|
@ -141,9 +143,10 @@ class TestMessage(object):
|
|||
{'length': 17, 'offset': 64, 'type': 'url'},
|
||||
{'length': 36, 'offset': 86, 'type': 'italic'},
|
||||
{'length': 24, 'offset': 91, 'type': 'bold'},
|
||||
{'length': 4, 'offset': 101, 'type': 'strikethrough'}]
|
||||
{'length': 4, 'offset': 101, 'type': 'strikethrough'},
|
||||
{'length': 10, 'offset': 124, 'type': 'pre', 'language': 'python'}]
|
||||
test_text_v2 = ('Test for <bold, ita_lic, \`code, links, text-mention and `\pre. '
|
||||
'http://google.com and bold nested in strk nested in italic.')
|
||||
'http://google.com and bold nested in strk nested in italic. Python pre.')
|
||||
test_message = Message(message_id=1,
|
||||
from_user=None,
|
||||
date=None,
|
||||
|
@ -213,7 +216,8 @@ class TestMessage(object):
|
|||
' <a href="http://github.com/abc\)def">links</a>, '
|
||||
'<a href="tg://user?id=123456789">text-mention</a> and '
|
||||
'<pre>`\pre</pre>. http://google.com '
|
||||
'and <i>bold <b>nested in <s>strk</s> nested in</b> italic</i>.')
|
||||
'and <i>bold <b>nested in <s>strk</s> nested in</b> italic</i>. '
|
||||
'<pre><code class="python">Python pre</code></pre>.')
|
||||
text_html = self.test_message_v2.text_html
|
||||
assert text_html == test_html_string
|
||||
|
||||
|
@ -227,13 +231,14 @@ class TestMessage(object):
|
|||
' <a href="http://github.com/abc\)def">links</a>, '
|
||||
'<a href="tg://user?id=123456789">text-mention</a> and '
|
||||
'<pre>`\pre</pre>. <a href="http://google.com">http://google.com</a> '
|
||||
'and <i>bold <b>nested in <s>strk</s> nested in</b> italic</i>.')
|
||||
'and <i>bold <b>nested in <s>strk</s> nested in</b> italic</i>. '
|
||||
'<pre><code class="python">Python pre</code></pre>.')
|
||||
text_html = self.test_message_v2.text_html_urled
|
||||
assert text_html == test_html_string
|
||||
|
||||
def test_text_markdown_simple(self):
|
||||
test_md_string = ('Test for <*bold*, _ita_\__lic_, `code`, [links](http://github.com/ab_),'
|
||||
' [text-mention](tg://user?id=123456789) and ```\npre```. '
|
||||
' [text-mention](tg://user?id=123456789) and ```python\npre```. '
|
||||
'http://google.com/ab\_')
|
||||
text_markdown = self.test_message.text_markdown
|
||||
assert text_markdown == test_md_string
|
||||
|
@ -242,7 +247,8 @@ class TestMessage(object):
|
|||
test_md_string = (r'__Test__ for <*bold*, _ita\_lic_, `\\\`code`, '
|
||||
'[links](http://github.com/abc\\\\\)def), '
|
||||
'[text\-mention](tg://user?id=123456789) and ```\`\\\\pre```\. '
|
||||
'http://google\.com and _bold *nested in ~strk~ nested in* italic_\.')
|
||||
'http://google\.com and _bold *nested in ~strk~ nested in* italic_\. '
|
||||
'```python\nPython pre```\.')
|
||||
text_markdown = self.test_message_v2.text_markdown_v2
|
||||
assert text_markdown == test_md_string
|
||||
|
||||
|
@ -271,7 +277,7 @@ class TestMessage(object):
|
|||
|
||||
def test_text_markdown_urled(self):
|
||||
test_md_string = ('Test for <*bold*, _ita_\__lic_, `code`, [links](http://github.com/ab_),'
|
||||
' [text-mention](tg://user?id=123456789) and ```\npre```. '
|
||||
' [text-mention](tg://user?id=123456789) and ```python\npre```. '
|
||||
'[http://google.com/ab_](http://google.com/ab_)')
|
||||
text_markdown = self.test_message.text_markdown_urled
|
||||
assert text_markdown == test_md_string
|
||||
|
@ -281,7 +287,7 @@ class TestMessage(object):
|
|||
'[links](http://github.com/abc\\\\\)def), '
|
||||
'[text\-mention](tg://user?id=123456789) and ```\`\\\\pre```\. '
|
||||
'[http://google\.com](http://google.com) and _bold *nested in ~strk~ '
|
||||
'nested in* italic_\.')
|
||||
'nested in* italic_\. ```python\nPython pre```\.')
|
||||
text_markdown = self.test_message_v2.text_markdown_v2_urled
|
||||
assert text_markdown == test_md_string
|
||||
|
||||
|
@ -306,7 +312,8 @@ class TestMessage(object):
|
|||
' <a href="http://github.com/abc\)def">links</a>, '
|
||||
'<a href="tg://user?id=123456789">text-mention</a> and '
|
||||
'<pre>`\pre</pre>. http://google.com '
|
||||
'and <i>bold <b>nested in <s>strk</s> nested in</b> italic</i>.')
|
||||
'and <i>bold <b>nested in <s>strk</s> nested in</b> italic</i>. '
|
||||
'<pre><code class="python">Python pre</code></pre>.')
|
||||
caption_html = self.test_message_v2.caption_html
|
||||
assert caption_html == test_html_string
|
||||
|
||||
|
@ -320,13 +327,14 @@ class TestMessage(object):
|
|||
' <a href="http://github.com/abc\)def">links</a>, '
|
||||
'<a href="tg://user?id=123456789">text-mention</a> and '
|
||||
'<pre>`\pre</pre>. <a href="http://google.com">http://google.com</a> '
|
||||
'and <i>bold <b>nested in <s>strk</s> nested in</b> italic</i>.')
|
||||
'and <i>bold <b>nested in <s>strk</s> nested in</b> italic</i>. '
|
||||
'<pre><code class="python">Python pre</code></pre>.')
|
||||
caption_html = self.test_message_v2.caption_html_urled
|
||||
assert caption_html == test_html_string
|
||||
|
||||
def test_caption_markdown_simple(self):
|
||||
test_md_string = ('Test for <*bold*, _ita_\__lic_, `code`, [links](http://github.com/ab_),'
|
||||
' [text-mention](tg://user?id=123456789) and ```\npre```. '
|
||||
' [text-mention](tg://user?id=123456789) and ```python\npre```. '
|
||||
'http://google.com/ab\_')
|
||||
caption_markdown = self.test_message.caption_markdown
|
||||
assert caption_markdown == test_md_string
|
||||
|
@ -335,7 +343,8 @@ class TestMessage(object):
|
|||
test_md_string = (r'__Test__ for <*bold*, _ita\_lic_, `\\\`code`, '
|
||||
'[links](http://github.com/abc\\\\\\)def), '
|
||||
'[text\-mention](tg://user?id=123456789) and ```\`\\\\pre```\. '
|
||||
'http://google\.com and _bold *nested in ~strk~ nested in* italic_\.')
|
||||
'http://google\.com and _bold *nested in ~strk~ nested in* italic_\. '
|
||||
'```python\nPython pre```\.')
|
||||
caption_markdown = self.test_message_v2.caption_markdown_v2
|
||||
assert caption_markdown == test_md_string
|
||||
|
||||
|
@ -347,7 +356,7 @@ class TestMessage(object):
|
|||
|
||||
def test_caption_markdown_urled(self):
|
||||
test_md_string = ('Test for <*bold*, _ita_\__lic_, `code`, [links](http://github.com/ab_),'
|
||||
' [text-mention](tg://user?id=123456789) and ```\npre```. '
|
||||
' [text-mention](tg://user?id=123456789) and ```python\npre```. '
|
||||
'[http://google.com/ab_](http://google.com/ab_)')
|
||||
caption_markdown = self.test_message.caption_markdown_urled
|
||||
assert caption_markdown == test_md_string
|
||||
|
@ -357,7 +366,7 @@ class TestMessage(object):
|
|||
'[links](http://github.com/abc\\\\\\)def), '
|
||||
'[text\-mention](tg://user?id=123456789) and ```\`\\\\pre```\. '
|
||||
'[http://google\.com](http://google.com) and _bold *nested in ~strk~ '
|
||||
'nested in* italic_\.')
|
||||
'nested in* italic_\. ```python\nPython pre```\.')
|
||||
caption_markdown = self.test_message_v2.caption_markdown_v2_urled
|
||||
assert caption_markdown == test_md_string
|
||||
|
||||
|
@ -444,7 +453,7 @@ class TestMessage(object):
|
|||
|
||||
def test_reply_markdown(self, monkeypatch, message):
|
||||
test_md_string = ('Test for <*bold*, _ita_\__lic_, `code`, [links](http://github.com/ab_),'
|
||||
' [text-mention](tg://user?id=123456789) and ```\npre```. '
|
||||
' [text-mention](tg://user?id=123456789) and ```python\npre```. '
|
||||
'http://google.com/ab\_')
|
||||
|
||||
def test(*args, **kwargs):
|
||||
|
@ -471,7 +480,8 @@ class TestMessage(object):
|
|||
test_md_string = (r'__Test__ for <*bold*, _ita\_lic_, `\\\`code`, '
|
||||
'[links](http://github.com/abc\\\\\)def), '
|
||||
'[text\-mention](tg://user?id=123456789) and ```\`\\\\pre```\. '
|
||||
'http://google\.com and _bold *nested in ~strk~ nested in* italic_\.')
|
||||
'http://google\.com and _bold *nested in ~strk~ nested in* italic_\. '
|
||||
'```python\nPython pre```\.')
|
||||
|
||||
def test(*args, **kwargs):
|
||||
cid = args[0] == message.chat_id
|
||||
|
@ -498,7 +508,8 @@ class TestMessage(object):
|
|||
' <a href="http://github.com/abc\)def">links</a>, '
|
||||
'<a href="tg://user?id=123456789">text-mention</a> and '
|
||||
'<pre>`\pre</pre>. http://google.com '
|
||||
'and <i>bold <b>nested in <s>strk</s> nested in</b> italic</i>.')
|
||||
'and <i>bold <b>nested in <s>strk</s> nested in</b> italic</i>. '
|
||||
'<pre><code class="python">Python pre</code></pre>.')
|
||||
|
||||
def test(*args, **kwargs):
|
||||
cid = args[0] == message.chat_id
|
||||
|
|
|
@ -32,7 +32,10 @@ def message_entity(request):
|
|||
user = None
|
||||
if type_ == MessageEntity.TEXT_MENTION:
|
||||
user = User(1, 'test_user', False)
|
||||
return MessageEntity(type, 1, 3, url=url, user=user)
|
||||
language = None
|
||||
if type == MessageEntity.PRE:
|
||||
language = "python"
|
||||
return MessageEntity(type, 1, 3, url=url, user=user, language=language)
|
||||
|
||||
|
||||
class TestMessageEntity(object):
|
||||
|
@ -64,6 +67,8 @@ class TestMessageEntity(object):
|
|||
assert entity_dict['url'] == message_entity.url
|
||||
if message_entity.user:
|
||||
assert entity_dict['user'] == message_entity.user.to_dict()
|
||||
if message_entity.language:
|
||||
assert entity_dict['language'] == message_entity.language
|
||||
|
||||
def test_equality(self):
|
||||
a = MessageEntity(MessageEntity.BOLD, 2, 3)
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import pytest
|
||||
|
||||
from telegram import Poll, PollOption
|
||||
from telegram import Poll, PollOption, PollAnswer, User
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
|
@ -50,26 +50,71 @@ class TestPollOption(object):
|
|||
assert poll_option_dict['voter_count'] == poll_option.voter_count
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def poll_answer():
|
||||
return PollAnswer(poll_id=TestPollAnswer.poll_id, user=TestPollAnswer.user,
|
||||
option_ids=TestPollAnswer.poll_id)
|
||||
|
||||
|
||||
class TestPollAnswer(object):
|
||||
poll_id = 'id'
|
||||
user = User(1, '', False)
|
||||
option_ids = [2]
|
||||
|
||||
def test_de_json(self):
|
||||
json_dict = {
|
||||
'poll_id': self.poll_id,
|
||||
'user': self.user.to_dict(),
|
||||
'option_ids': self.option_ids
|
||||
}
|
||||
poll_answer = PollAnswer.de_json(json_dict, None)
|
||||
|
||||
assert poll_answer.poll_id == self.poll_id
|
||||
assert poll_answer.user == self.user
|
||||
assert poll_answer.option_ids == self.option_ids
|
||||
|
||||
def test_to_dict(self, poll_answer):
|
||||
poll_answer_dict = poll_answer.to_dict()
|
||||
|
||||
assert isinstance(poll_answer_dict, dict)
|
||||
assert poll_answer_dict['poll_id'] == poll_answer.poll_id
|
||||
assert poll_answer_dict['user'] == poll_answer.user.to_dict()
|
||||
assert poll_answer_dict['option_ids'] == poll_answer.option_ids
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def poll():
|
||||
return Poll(TestPoll.id_,
|
||||
TestPoll.question,
|
||||
TestPoll.options,
|
||||
TestPoll.is_closed)
|
||||
TestPoll.total_voter_count,
|
||||
TestPoll.is_closed,
|
||||
TestPoll.is_anonymous,
|
||||
TestPoll.type,
|
||||
TestPoll.allows_multiple_answers
|
||||
)
|
||||
|
||||
|
||||
class TestPoll(object):
|
||||
id_ = 'id'
|
||||
question = 'Test?'
|
||||
options = [PollOption('test', 10), PollOption('test2', 11)]
|
||||
total_voter_count = 0
|
||||
is_closed = True
|
||||
is_anonymous = False
|
||||
type = Poll.REGULAR
|
||||
allows_multiple_answers = True
|
||||
|
||||
def test_de_json(self):
|
||||
json_dict = {
|
||||
'id': self.id_,
|
||||
'question': self.question,
|
||||
'options': [o.to_dict() for o in self.options],
|
||||
'is_closed': self.is_closed
|
||||
'total_voter_count': self.total_voter_count,
|
||||
'is_closed': self.is_closed,
|
||||
'is_anonymous': self.is_anonymous,
|
||||
'type': self.type,
|
||||
'allows_multiple_answers': self.allows_multiple_answers
|
||||
}
|
||||
poll = Poll.de_json(json_dict, None)
|
||||
|
||||
|
@ -80,7 +125,11 @@ class TestPoll(object):
|
|||
assert poll.options[0].voter_count == self.options[0].voter_count
|
||||
assert poll.options[1].text == self.options[1].text
|
||||
assert poll.options[1].voter_count == self.options[1].voter_count
|
||||
assert poll.total_voter_count == self.total_voter_count
|
||||
assert poll.is_closed == self.is_closed
|
||||
assert poll.is_anonymous == self.is_anonymous
|
||||
assert poll.type == self.type
|
||||
assert poll.allows_multiple_answers == self.allows_multiple_answers
|
||||
|
||||
def test_to_dict(self, poll):
|
||||
poll_dict = poll.to_dict()
|
||||
|
@ -89,4 +138,8 @@ class TestPoll(object):
|
|||
assert poll_dict['id'] == poll.id
|
||||
assert poll_dict['question'] == poll.question
|
||||
assert poll_dict['options'] == [o.to_dict() for o in poll.options]
|
||||
assert poll_dict['total_voter_count'] == poll.total_voter_count
|
||||
assert poll_dict['is_closed'] == poll.is_closed
|
||||
assert poll_dict['is_anonymous'] == poll.is_anonymous
|
||||
assert poll_dict['type'] == poll.type
|
||||
assert poll_dict['allows_multiple_answers'] == poll.allows_multiple_answers
|
||||
|
|
158
tests/test_pollanswerhandler.py
Normal file
158
tests/test_pollanswerhandler.py
Normal file
|
@ -0,0 +1,158 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2020
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
from queue import Queue
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import (Update, CallbackQuery, Bot, Message, User, Chat, PollAnswer,
|
||||
ChosenInlineResult, ShippingQuery, PreCheckoutQuery)
|
||||
from telegram.ext import PollAnswerHandler, CallbackContext, JobQueue
|
||||
|
||||
message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
|
||||
|
||||
params = [
|
||||
{'message': message},
|
||||
{'edited_message': message},
|
||||
{'callback_query': CallbackQuery(1, User(1, '', False), 'chat', message=message)},
|
||||
{'channel_post': message},
|
||||
{'edited_channel_post': message},
|
||||
{'chosen_inline_result': ChosenInlineResult('id', User(1, '', False), '')},
|
||||
{'shipping_query': ShippingQuery('id', User(1, '', False), '', None)},
|
||||
{'pre_checkout_query': PreCheckoutQuery('id', User(1, '', False), '', 0, '')},
|
||||
{'callback_query': CallbackQuery(1, User(1, '', False), 'chat')}
|
||||
]
|
||||
|
||||
ids = ('message', 'edited_message', 'callback_query', 'channel_post',
|
||||
'edited_channel_post', 'chosen_inline_result',
|
||||
'shipping_query', 'pre_checkout_query', 'callback_query_without_message')
|
||||
|
||||
|
||||
@pytest.fixture(scope='class', params=params, ids=ids)
|
||||
def false_update(request):
|
||||
return Update(update_id=2, **request.param)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def poll_answer(bot):
|
||||
return Update(0, poll_answer=PollAnswer(1, User(2, 'test user', False), [0, 1]))
|
||||
|
||||
|
||||
class TestPollAnswerHandler(object):
|
||||
test_flag = False
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset(self):
|
||||
self.test_flag = False
|
||||
|
||||
def callback_basic(self, bot, update):
|
||||
test_bot = isinstance(bot, Bot)
|
||||
test_update = isinstance(update, Update)
|
||||
self.test_flag = test_bot and test_update
|
||||
|
||||
def callback_data_1(self, bot, update, user_data=None, chat_data=None):
|
||||
self.test_flag = (user_data is not None) or (chat_data is not None)
|
||||
|
||||
def callback_data_2(self, bot, update, user_data=None, chat_data=None):
|
||||
self.test_flag = (user_data is not None) and (chat_data is not None)
|
||||
|
||||
def callback_queue_1(self, bot, update, job_queue=None, update_queue=None):
|
||||
self.test_flag = (job_queue is not None) or (update_queue is not None)
|
||||
|
||||
def callback_queue_2(self, bot, update, job_queue=None, update_queue=None):
|
||||
self.test_flag = (job_queue is not None) and (update_queue is not None)
|
||||
|
||||
def callback_context(self, update, context):
|
||||
self.test_flag = (isinstance(context, CallbackContext)
|
||||
and isinstance(context.bot, Bot)
|
||||
and isinstance(update, Update)
|
||||
and isinstance(context.update_queue, Queue)
|
||||
and isinstance(context.job_queue, JobQueue)
|
||||
and isinstance(context.user_data, dict)
|
||||
and context.chat_data is None
|
||||
and isinstance(context.bot_data, dict)
|
||||
and isinstance(update.poll_answer, PollAnswer))
|
||||
|
||||
def test_basic(self, dp, poll_answer):
|
||||
handler = PollAnswerHandler(self.callback_basic)
|
||||
dp.add_handler(handler)
|
||||
|
||||
assert handler.check_update(poll_answer)
|
||||
|
||||
dp.process_update(poll_answer)
|
||||
assert self.test_flag
|
||||
|
||||
def test_pass_user_or_chat_data(self, dp, poll_answer):
|
||||
handler = PollAnswerHandler(self.callback_data_1, pass_user_data=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
dp.process_update(poll_answer)
|
||||
assert self.test_flag
|
||||
|
||||
dp.remove_handler(handler)
|
||||
handler = PollAnswerHandler(self.callback_data_1, pass_chat_data=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
self.test_flag = False
|
||||
dp.process_update(poll_answer)
|
||||
assert self.test_flag
|
||||
|
||||
dp.remove_handler(handler)
|
||||
handler = PollAnswerHandler(self.callback_data_2, pass_chat_data=True,
|
||||
pass_user_data=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
self.test_flag = False
|
||||
dp.process_update(poll_answer)
|
||||
assert self.test_flag
|
||||
|
||||
def test_pass_job_or_update_queue(self, dp, poll_answer):
|
||||
handler = PollAnswerHandler(self.callback_queue_1, pass_job_queue=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
dp.process_update(poll_answer)
|
||||
assert self.test_flag
|
||||
|
||||
dp.remove_handler(handler)
|
||||
handler = PollAnswerHandler(self.callback_queue_1,
|
||||
pass_update_queue=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
self.test_flag = False
|
||||
dp.process_update(poll_answer)
|
||||
assert self.test_flag
|
||||
|
||||
dp.remove_handler(handler)
|
||||
handler = PollAnswerHandler(self.callback_queue_2, pass_job_queue=True,
|
||||
pass_update_queue=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
self.test_flag = False
|
||||
dp.process_update(poll_answer)
|
||||
assert self.test_flag
|
||||
|
||||
def test_other_update_types(self, false_update):
|
||||
handler = PollAnswerHandler(self.callback_basic)
|
||||
assert not handler.check_update(false_update)
|
||||
|
||||
def test_context(self, cdp, poll_answer):
|
||||
handler = PollAnswerHandler(self.callback_context)
|
||||
cdp.add_handler(handler)
|
||||
|
||||
cdp.process_update(poll_answer)
|
||||
assert self.test_flag
|
159
tests/test_pollhandler.py
Normal file
159
tests/test_pollhandler.py
Normal file
|
@ -0,0 +1,159 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2020
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
from queue import Queue
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import (Update, Poll, PollOption, Bot, Message, User, Chat, CallbackQuery,
|
||||
ChosenInlineResult, ShippingQuery, PreCheckoutQuery)
|
||||
from telegram.ext import PollHandler, CallbackContext, JobQueue
|
||||
|
||||
message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
|
||||
|
||||
params = [
|
||||
{'message': message},
|
||||
{'edited_message': message},
|
||||
{'callback_query': CallbackQuery(1, User(1, '', False), 'chat', message=message)},
|
||||
{'channel_post': message},
|
||||
{'edited_channel_post': message},
|
||||
{'chosen_inline_result': ChosenInlineResult('id', User(1, '', False), '')},
|
||||
{'shipping_query': ShippingQuery('id', User(1, '', False), '', None)},
|
||||
{'pre_checkout_query': PreCheckoutQuery('id', User(1, '', False), '', 0, '')},
|
||||
{'callback_query': CallbackQuery(1, User(1, '', False), 'chat')}
|
||||
]
|
||||
|
||||
ids = ('message', 'edited_message', 'callback_query', 'channel_post',
|
||||
'edited_channel_post', 'chosen_inline_result',
|
||||
'shipping_query', 'pre_checkout_query', 'callback_query_without_message')
|
||||
|
||||
|
||||
@pytest.fixture(scope='class', params=params, ids=ids)
|
||||
def false_update(request):
|
||||
return Update(update_id=2, **request.param)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def poll(bot):
|
||||
return Update(0, poll=Poll(1, 'question', [PollOption('1', 0), PollOption('2', 0)], 0, False,
|
||||
False, Poll.REGULAR, True))
|
||||
|
||||
|
||||
class TestPollHandler(object):
|
||||
test_flag = False
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset(self):
|
||||
self.test_flag = False
|
||||
|
||||
def callback_basic(self, bot, update):
|
||||
test_bot = isinstance(bot, Bot)
|
||||
test_update = isinstance(update, Update)
|
||||
self.test_flag = test_bot and test_update
|
||||
|
||||
def callback_data_1(self, bot, update, user_data=None, chat_data=None):
|
||||
self.test_flag = (user_data is not None) or (chat_data is not None)
|
||||
|
||||
def callback_data_2(self, bot, update, user_data=None, chat_data=None):
|
||||
self.test_flag = (user_data is not None) and (chat_data is not None)
|
||||
|
||||
def callback_queue_1(self, bot, update, job_queue=None, update_queue=None):
|
||||
self.test_flag = (job_queue is not None) or (update_queue is not None)
|
||||
|
||||
def callback_queue_2(self, bot, update, job_queue=None, update_queue=None):
|
||||
self.test_flag = (job_queue is not None) and (update_queue is not None)
|
||||
|
||||
def callback_context(self, update, context):
|
||||
self.test_flag = (isinstance(context, CallbackContext)
|
||||
and isinstance(context.bot, Bot)
|
||||
and isinstance(update, Update)
|
||||
and isinstance(context.update_queue, Queue)
|
||||
and isinstance(context.job_queue, JobQueue)
|
||||
and context.user_data is None
|
||||
and context.chat_data is None
|
||||
and isinstance(context.bot_data, dict)
|
||||
and isinstance(update.poll, Poll))
|
||||
|
||||
def test_basic(self, dp, poll):
|
||||
handler = PollHandler(self.callback_basic)
|
||||
dp.add_handler(handler)
|
||||
|
||||
assert handler.check_update(poll)
|
||||
|
||||
dp.process_update(poll)
|
||||
assert self.test_flag
|
||||
|
||||
def test_pass_user_or_chat_data(self, dp, poll):
|
||||
handler = PollHandler(self.callback_data_1, pass_user_data=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
dp.process_update(poll)
|
||||
assert self.test_flag
|
||||
|
||||
dp.remove_handler(handler)
|
||||
handler = PollHandler(self.callback_data_1, pass_chat_data=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
self.test_flag = False
|
||||
dp.process_update(poll)
|
||||
assert self.test_flag
|
||||
|
||||
dp.remove_handler(handler)
|
||||
handler = PollHandler(self.callback_data_2, pass_chat_data=True,
|
||||
pass_user_data=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
self.test_flag = False
|
||||
dp.process_update(poll)
|
||||
assert self.test_flag
|
||||
|
||||
def test_pass_job_or_update_queue(self, dp, poll):
|
||||
handler = PollHandler(self.callback_queue_1, pass_job_queue=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
dp.process_update(poll)
|
||||
assert self.test_flag
|
||||
|
||||
dp.remove_handler(handler)
|
||||
handler = PollHandler(self.callback_queue_1,
|
||||
pass_update_queue=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
self.test_flag = False
|
||||
dp.process_update(poll)
|
||||
assert self.test_flag
|
||||
|
||||
dp.remove_handler(handler)
|
||||
handler = PollHandler(self.callback_queue_2, pass_job_queue=True,
|
||||
pass_update_queue=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
self.test_flag = False
|
||||
dp.process_update(poll)
|
||||
assert self.test_flag
|
||||
|
||||
def test_other_update_types(self, false_update):
|
||||
handler = PollHandler(self.callback_basic)
|
||||
assert not handler.check_update(false_update)
|
||||
|
||||
def test_context(self, cdp, poll):
|
||||
handler = PollHandler(self.callback_context)
|
||||
cdp.add_handler(handler)
|
||||
|
||||
cdp.process_update(poll)
|
||||
assert self.test_flag
|
|
@ -21,6 +21,7 @@ import pytest
|
|||
|
||||
from telegram import (Message, User, Update, Chat, CallbackQuery, InlineQuery,
|
||||
ChosenInlineResult, ShippingQuery, PreCheckoutQuery, Poll, PollOption)
|
||||
from telegram.poll import PollAnswer
|
||||
|
||||
message = Message(1, User(1, '', False), None, Chat(1, ''), text='Text')
|
||||
|
||||
|
@ -35,12 +36,13 @@ params = [
|
|||
{'shipping_query': ShippingQuery('id', User(1, '', False), '', None)},
|
||||
{'pre_checkout_query': PreCheckoutQuery('id', User(1, '', False), '', 0, '')},
|
||||
{'callback_query': CallbackQuery(1, User(1, '', False), 'chat')},
|
||||
{'poll': Poll('id', '?', [PollOption('.', 1)], False)}
|
||||
{'poll': Poll('id', '?', [PollOption('.', 1)], False, False, False, Poll.REGULAR, True)},
|
||||
{'poll_answer': PollAnswer("id", User(1, '', False), [1])}
|
||||
]
|
||||
|
||||
all_types = ('message', 'edited_message', 'callback_query', 'channel_post',
|
||||
'edited_channel_post', 'inline_query', 'chosen_inline_result',
|
||||
'shipping_query', 'pre_checkout_query', 'poll')
|
||||
'shipping_query', 'pre_checkout_query', 'poll', 'poll_answer')
|
||||
|
||||
ids = all_types + ('callback_query_without_message',)
|
||||
|
||||
|
@ -101,7 +103,8 @@ class TestUpdate(object):
|
|||
and update.callback_query.message is None)
|
||||
or update.shipping_query is not None
|
||||
or update.pre_checkout_query is not None
|
||||
or update.poll is not None):
|
||||
or update.poll is not None
|
||||
or update.poll_answer is not None):
|
||||
assert chat.id == 1
|
||||
else:
|
||||
assert chat is None
|
||||
|
@ -125,7 +128,8 @@ class TestUpdate(object):
|
|||
and update.callback_query.message is None)
|
||||
or update.shipping_query is not None
|
||||
or update.pre_checkout_query is not None
|
||||
or update.poll is not None):
|
||||
or update.poll is not None
|
||||
or update.poll_answer is not None):
|
||||
assert eff_message.message_id == message.message_id
|
||||
else:
|
||||
assert eff_message is None
|
||||
|
|
|
@ -30,7 +30,10 @@ def json_dict():
|
|||
'first_name': TestUser.first_name,
|
||||
'last_name': TestUser.last_name,
|
||||
'username': TestUser.username,
|
||||
'language_code': TestUser.language_code
|
||||
'language_code': TestUser.language_code,
|
||||
'can_join_groups': TestUser.can_join_groups,
|
||||
'can_read_all_group_messages': TestUser.can_read_all_group_messages,
|
||||
'supports_inline_queries': TestUser.supports_inline_queries
|
||||
}
|
||||
|
||||
|
||||
|
@ -38,7 +41,9 @@ def json_dict():
|
|||
def user(bot):
|
||||
return User(id=TestUser.id_, first_name=TestUser.first_name, is_bot=TestUser.is_bot,
|
||||
last_name=TestUser.last_name, username=TestUser.username,
|
||||
language_code=TestUser.language_code, bot=bot)
|
||||
language_code=TestUser.language_code, can_join_groups=TestUser.can_join_groups,
|
||||
can_read_all_group_messages=TestUser.can_read_all_group_messages,
|
||||
supports_inline_queries=TestUser.supports_inline_queries, bot=bot)
|
||||
|
||||
|
||||
class TestUser(object):
|
||||
|
@ -48,6 +53,9 @@ class TestUser(object):
|
|||
last_name = u'last\u2022name'
|
||||
username = 'username'
|
||||
language_code = 'en_us'
|
||||
can_join_groups = True
|
||||
can_read_all_group_messages = True
|
||||
supports_inline_queries = False
|
||||
|
||||
def test_de_json(self, json_dict, bot):
|
||||
user = User.de_json(json_dict, bot)
|
||||
|
@ -58,6 +66,9 @@ class TestUser(object):
|
|||
assert user.last_name == self.last_name
|
||||
assert user.username == self.username
|
||||
assert user.language_code == self.language_code
|
||||
assert user.can_join_groups == self.can_join_groups
|
||||
assert user.can_read_all_group_messages == self.can_read_all_group_messages
|
||||
assert user.supports_inline_queries == self.supports_inline_queries
|
||||
|
||||
def test_de_json_without_username(self, json_dict, bot):
|
||||
del json_dict['username']
|
||||
|
@ -70,6 +81,9 @@ class TestUser(object):
|
|||
assert user.last_name == self.last_name
|
||||
assert user.username is None
|
||||
assert user.language_code == self.language_code
|
||||
assert user.can_join_groups == self.can_join_groups
|
||||
assert user.can_read_all_group_messages == self.can_read_all_group_messages
|
||||
assert user.supports_inline_queries == self.supports_inline_queries
|
||||
|
||||
def test_de_json_without_username_and_last_name(self, json_dict, bot):
|
||||
del json_dict['username']
|
||||
|
@ -83,6 +97,9 @@ class TestUser(object):
|
|||
assert user.last_name is None
|
||||
assert user.username is None
|
||||
assert user.language_code == self.language_code
|
||||
assert user.can_join_groups == self.can_join_groups
|
||||
assert user.can_read_all_group_messages == self.can_read_all_group_messages
|
||||
assert user.supports_inline_queries == self.supports_inline_queries
|
||||
|
||||
def test_name(self, user):
|
||||
assert user.name == '@username'
|
||||
|
|
Loading…
Reference in a new issue