Merge branch '4.3' into RC1

This commit is contained in:
Pieter Schutz 2019-06-05 22:19:48 +02:00
commit 3fc57479f3
9 changed files with 246 additions and 18 deletions

View file

@ -48,6 +48,7 @@ from .parsemode import ParseMode
from .messageentity import MessageEntity
from .games.game import Game
from .poll import Poll, PollOption
from .loginurl import LoginUrl
from .games.callbackgame import CallbackGame
from .payment.shippingaddress import ShippingAddress
from .payment.orderinfo import OrderInfo
@ -58,11 +59,11 @@ from .passport.passportfile import PassportFile
from .passport.data import IdDocumentData, PersonalDetails, ResidentialAddress
from .passport.encryptedpassportelement import EncryptedPassportElement
from .passport.passportdata import PassportData
from .inline.inlinekeyboardbutton import InlineKeyboardButton
from .inline.inlinekeyboardmarkup import InlineKeyboardMarkup
from .message import Message
from .callbackquery import CallbackQuery
from .choseninlineresult import ChosenInlineResult
from .inline.inlinekeyboardbutton import InlineKeyboardButton
from .inline.inlinekeyboardmarkup import InlineKeyboardMarkup
from .inline.inputmessagecontent import InputMessageContent
from .inline.inlinequery import InlineQuery
from .inline.inlinequeryresult import InlineQueryResult
@ -153,5 +154,6 @@ __all__ = [
'PersonalDetails', 'ResidentialAddress', 'InputMediaVideo', 'InputMediaAnimation',
'InputMediaAudio', 'InputMediaDocument', 'TelegramDecryptionError',
'PassportElementErrorSelfie', 'PassportElementErrorTranslationFile',
'PassportElementErrorTranslationFiles', 'PassportElementErrorUnspecified', 'Poll', 'PollOption'
'PassportElementErrorTranslationFiles', 'PassportElementErrorUnspecified', 'Poll',
'PollOption', 'LoginUrl'
]

View file

@ -31,6 +31,8 @@ class InlineKeyboardButton(TelegramObject):
Attributes:
text (:obj:`str`): Label text on the button.
url (:obj:`str`): Optional. HTTP url to be opened when button is pressed.
login_url (:class:`telegram.LoginUrl`) Optional. An HTTP URL used to automatically
authorize the user.
callback_data (:obj:`str`): Optional. Data to be sent in a callback query to the bot when
button is pressed, UTF-8 1-64 bytes.
switch_inline_query (:obj:`str`): Optional. Will prompt the user to select one of their
@ -45,6 +47,8 @@ class InlineKeyboardButton(TelegramObject):
Args:
text (:obj:`str`): Label text on the button.
url (:obj:`str`): HTTP url to be opened when button is pressed.
login_url (:class:`telegram.LoginUrl`, optional) An HTTP URL used to automatically
authorize the user.
callback_data (:obj:`str`, optional): Data to be sent in a callback query to the bot when
button is pressed, 1-64 UTF-8 bytes.
switch_inline_query (:obj:`str`, optional): If set, pressing the button will prompt the
@ -76,14 +80,30 @@ class InlineKeyboardButton(TelegramObject):
switch_inline_query_current_chat=None,
callback_game=None,
pay=None,
login_url=None,
**kwargs):
# Required
self.text = text
# Optionals
self.url = url
self.callback_data = callback_data
self.switch_inline_query = switch_inline_query
self.switch_inline_query_current_chat = switch_inline_query_current_chat
self.callback_game = callback_game
self.pay = pay
if url:
self.url = url
if login_url:
self.login_url = login_url
if callback_data:
self.callback_data = callback_data
if switch_inline_query:
self.switch_inline_query = switch_inline_query
if switch_inline_query_current_chat:
self.switch_inline_query_current_chat = switch_inline_query_current_chat
if callback_game:
self.callback_game = callback_game
if pay:
self.pay = pay
@classmethod
def de_json(cls, data, bot):
if not data:
return None
return cls(**data)

View file

@ -18,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram InlineKeyboardMarkup."""
from telegram import ReplyMarkup
from telegram import ReplyMarkup, InlineKeyboardButton
class InlineKeyboardMarkup(ReplyMarkup):
@ -49,6 +49,19 @@ class InlineKeyboardMarkup(ReplyMarkup):
return data
@classmethod
def de_json(cls, data, bot):
if not data:
return None
keyboard = []
for row in data['inline_keyboard']:
tmp = []
for col in row:
tmp.append(InlineKeyboardButton.de_json(col, bot))
keyboard.append(tmp)
return cls(keyboard)
@classmethod
def from_button(cls, button, **kwargs):
"""Shortcut for::

65
telegram/loginurl.py Normal file
View file

@ -0,0 +1,65 @@
#!/usr/bin/env python
# pylint: disable=R0903
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-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/].
"""This module contains an object that represents a Telegram LoginUrl."""
from telegram import TelegramObject
class LoginUrl(TelegramObject):
"""This object represents a parameter of the inline keyboard button used to automatically
authorize a user. Serves as a great replacement for the Telegram Login Widget when the user is
coming from Telegram. All the user needs to do is tap/click a button and confirm that they want
to log in. Telegram apps support these buttons as of version 5.7.
Sample bot: @discussbot
Attributes:
url (:obj:`str`): An HTTP URL to be opened with user authorization data.
forward_text (:obj:`str`): Optional. New text of the button in forwarded messages.
bot_username (:obj:`str`): Optional. Username of a bot, which will be used for user
authorization.
request_write_access (:obj:`bool`): Optional. Pass True to request the permission for your
bot to send messages to the user.
Args:
url (:obj:`str`): An HTTP URL to be opened with user authorization data added to the query
string when the button is pressed. If the user refuses to provide authorization data,
the original URL without information about the user will be opened. The data added is
the same as described in Receiving authorization data.
NOTE: You must always check the hash of the received data to verify the authentication
and the integrity of the data as described in Checking authorization.
forward_text (:obj:`str`, optional): New text of the button in forwarded messages.
bot_username (:obj:`str`, optional): Username of a bot, which will be used for user
authorization. See Setting up a bot for more details. If not specified, the current
bot's username will be assumed. The url's domain must be the same as the domain linked
with the bot. See Linking your domain to the bot for more details.
request_write_access (:obj:`bool`, optional): Pass True to request the permission for your
bot to send messages to the user.
"""
def __init__(self, url, forward_text=None, bot_username=None, request_write_access=None):
self.url = url
if forward_text:
self.forward_text = forward_text
if bot_username:
self.bot_username = bot_username
if request_write_access:
self.request_write_access = request_write_access
self._id_attrs = (self.url,)

View file

@ -23,7 +23,7 @@ from html import escape
from telegram import (Animation, Audio, Contact, Document, Chat, Location, PhotoSize, Sticker,
TelegramObject, User, Video, Voice, Venue, MessageEntity, Game, Invoice,
SuccessfulPayment, VideoNote, PassportData, Poll)
SuccessfulPayment, VideoNote, PassportData, Poll, InlineKeyboardMarkup)
from telegram import ParseMode
from telegram.utils.helpers import escape_markdown, to_timestamp, from_timestamp
@ -106,6 +106,8 @@ class Message(TelegramObject):
passport_data (:class:`telegram.PassportData`): Optional. Telegram Passport data.
poll (:class:`telegram.Poll`): Optional. Message is a native poll,
information about the poll.
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
to the message.
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
@ -210,6 +212,9 @@ class Message(TelegramObject):
passport_data (:class:`telegram.PassportData`, optional): Telegram Passport data.
poll (:class:`telegram.Poll`, optional): Message is a native poll,
information about the poll.
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
to the message. login_url buttons are represented as ordinary url buttons.
"""
_effective_attachment = _UNDEFINED
@ -270,6 +275,7 @@ class Message(TelegramObject):
passport_data=None,
poll=None,
forward_sender_name=None,
reply_markup=None,
bot=None,
**kwargs):
# Required
@ -320,7 +326,7 @@ class Message(TelegramObject):
self.animation = animation
self.passport_data = passport_data
self.poll = poll
self.reply_markup = reply_markup
self.bot = bot
self._id_attrs = (self.message_id,)
@ -375,6 +381,7 @@ class Message(TelegramObject):
data['successful_payment'] = SuccessfulPayment.de_json(data.get('successful_payment'), bot)
data['passport_data'] = PassportData.de_json(data.get('passport_data'), bot)
data['poll'] = Poll.de_json(data.get('poll'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
return cls(bot=bot, **data)

View file

@ -19,7 +19,7 @@
import pytest
from telegram import InlineKeyboardButton
from telegram import InlineKeyboardButton, LoginUrl
@pytest.fixture(scope='class')
@ -31,7 +31,8 @@ def inline_keyboard_button():
switch_inline_query_current_chat=TestInlineKeyboardButton
.switch_inline_query_current_chat,
callback_game=TestInlineKeyboardButton.callback_game,
pay=TestInlineKeyboardButton.pay)
pay=TestInlineKeyboardButton.pay,
login_url=TestInlineKeyboardButton.login_url)
class TestInlineKeyboardButton(object):
@ -42,6 +43,7 @@ class TestInlineKeyboardButton(object):
switch_inline_query_current_chat = 'switch_inline_query_current_chat'
callback_game = 'callback_game'
pay = 'pay'
login_url = LoginUrl("http://google.com")
def test_expected_values(self, inline_keyboard_button):
assert inline_keyboard_button.text == self.text
@ -52,6 +54,7 @@ class TestInlineKeyboardButton(object):
== self.switch_inline_query_current_chat)
assert inline_keyboard_button.callback_game == self.callback_game
assert inline_keyboard_button.pay == self.pay
assert inline_keyboard_button.login_url == self.login_url
def test_to_dict(self, inline_keyboard_button):
inline_keyboard_button_dict = inline_keyboard_button.to_dict()
@ -66,3 +69,26 @@ class TestInlineKeyboardButton(object):
== inline_keyboard_button.switch_inline_query_current_chat)
assert inline_keyboard_button_dict['callback_game'] == inline_keyboard_button.callback_game
assert inline_keyboard_button_dict['pay'] == inline_keyboard_button.pay
assert inline_keyboard_button_dict['login_url'] == \
inline_keyboard_button.login_url.to_dict() # NOQA: E127
def test_de_json(self, bot):
json_dict = {
'text': self.text,
'url': self.url,
'callback_data': self.callback_data,
'switch_inline_query': self.switch_inline_query,
'switch_inline_query_current_chat': self.switch_inline_query_current_chat,
'callback_game': self.callback_game,
'pay': self.pay
}
inline_keyboard_button = InlineKeyboardButton.de_json(json_dict, None)
assert inline_keyboard_button.text == self.text
assert inline_keyboard_button.url == self.url
assert inline_keyboard_button.callback_data == self.callback_data
assert inline_keyboard_button.switch_inline_query == self.switch_inline_query
assert (inline_keyboard_button.switch_inline_query_current_chat
== self.switch_inline_query_current_chat)
assert inline_keyboard_button.callback_game == self.callback_game
assert inline_keyboard_button.pay == self.pay

View file

@ -78,3 +78,34 @@ class TestInlineKeyboardMarkup(object):
self.inline_keyboard[0][1].to_dict()
]
]
def test_de_json(self):
json_dict = {
'inline_keyboard': [[
{
'text': 'start',
'url': 'http://google.com'
},
{
'text': 'next',
'callback_data': 'abcd'
}],
[{
'text': 'Cancel',
'callback_data': 'Cancel'
}]
]}
inline_keyboard_markup = InlineKeyboardMarkup.de_json(json_dict, None)
assert isinstance(inline_keyboard_markup, InlineKeyboardMarkup)
keyboard = inline_keyboard_markup.inline_keyboard
assert len(keyboard) == 2
assert len(keyboard[0]) == 2
assert len(keyboard[1]) == 1
assert isinstance(keyboard[0][0], InlineKeyboardButton)
assert isinstance(keyboard[0][1], InlineKeyboardButton)
assert isinstance(keyboard[1][0], InlineKeyboardButton)
assert keyboard[0][0].text == 'start'
assert keyboard[0][0].url == 'http://google.com'

61
tests/test_loginurl.py Normal file
View file

@ -0,0 +1,61 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-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/].
import pytest
from telegram import LoginUrl
@pytest.fixture(scope='class')
def login_url():
return LoginUrl(url=TestLoginUrl.url, forward_text=TestLoginUrl.forward_text,
bot_username=TestLoginUrl.bot_username,
request_write_access=TestLoginUrl.request_write_access)
class TestLoginUrl(object):
url = "http://www.google.com"
forward_text = "Send me forward!"
bot_username = "botname"
request_write_access = True
def test_to_dict(self, login_url):
login_url_dict = login_url.to_dict()
assert isinstance(login_url_dict, dict)
assert login_url_dict['url'] == self.url
assert login_url_dict['forward_text'] == self.forward_text
assert login_url_dict['bot_username'] == self.bot_username
assert login_url_dict['request_write_access'] == self.request_write_access
def test_equality(self):
a = LoginUrl(self.url, self.forward_text, self.bot_username, self.request_write_access)
b = LoginUrl(self.url, self.forward_text, self.bot_username, self.request_write_access)
c = LoginUrl(self.url)
d = LoginUrl("text.com", self.forward_text, self.bot_username, self.request_write_access)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a == c
assert hash(a) == hash(c)
assert a != d
assert hash(a) != hash(d)

View file

@ -20,10 +20,9 @@ from datetime import datetime
import pytest
from telegram import ParseMode, Poll, PollOption
from telegram import (Update, Message, User, MessageEntity, Chat, Audio, Document, Animation,
Game, PhotoSize, Sticker, Video, Voice, VideoNote, Contact, Location, Venue,
Invoice, SuccessfulPayment, PassportData)
Invoice, SuccessfulPayment, PassportData, ParseMode, Poll, PollOption)
from tests.test_passport import RAW_PASSPORT_DATA
@ -91,7 +90,11 @@ 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)},
{'text': 'a text message', 'reply_markup': {'inline_keyboard': [[{
'text': 'start', 'url': 'http://google.com'}, {
'text': 'next', 'callback_data': 'abcd'}],
[{'text': 'Cancel', 'callback_data': 'Cancel'}]]}}
],
ids=['forwarded_user', 'forwarded_channel', 'reply', 'edited', 'text',
'caption_entities', 'audio', 'document', 'animation', 'game', 'photo',
@ -100,7 +103,7 @@ def message(bot):
'group_created', 'supergroup_created', 'channel_created', 'migrated_to',
'migrated_from', 'pinned', 'invoice', 'successful_payment',
'connected_website', 'forward_signature', 'author_signature',
'photo_from_media_group', 'passport_data', 'poll'])
'photo_from_media_group', 'passport_data', 'poll', 'reply_markup'])
def message_params(bot, request):
return Message(message_id=TestMessage.id,
from_user=TestMessage.from_user,