mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-12-23 06:50:29 +01:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
ed33c4a7a9
9 changed files with 263 additions and 7 deletions
|
@ -16,6 +16,7 @@ Contributors
|
|||
The following wonderful people contributed directly or indirectly to this project:
|
||||
|
||||
- `Alateas <https://github.com/alateas>`_
|
||||
- `Ambro17 <https://github.com/Ambro17>`_
|
||||
- `Anton Tagunov <https://github.com/anton-tagunov>`_
|
||||
- `Avanatiker <https://github.com/Avanatiker>`_
|
||||
- `Balduro <https://github.com/Balduro>`_
|
||||
|
|
|
@ -62,7 +62,7 @@ class JobQueue(object):
|
|||
raise ValueError('next_t is None')
|
||||
|
||||
if isinstance(next_t, datetime.datetime):
|
||||
next_t = (next_t - datetime.datetime.now()).total_seconds()
|
||||
next_t = (next_t - datetime.datetime.now(next_t.tzinfo)).total_seconds()
|
||||
|
||||
elif isinstance(next_t, datetime.time):
|
||||
next_datetime = datetime.datetime.combine(datetime.date.today(), next_t)
|
||||
|
|
|
@ -48,3 +48,51 @@ class InlineKeyboardMarkup(ReplyMarkup):
|
|||
data['inline_keyboard'].append([x.to_dict() for x in inline_keyboard])
|
||||
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def from_button(cls, button, **kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
InlineKeyboardMarkup([[button]], **kwargs)
|
||||
|
||||
Return an InlineKeyboardMarkup from a single InlineKeyboardButton
|
||||
|
||||
Args:
|
||||
button (:class:`telegram.InlineKeyboardButton`): The button to use in the markup
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
"""
|
||||
return cls([[button]], **kwargs)
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, button_row, **kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
InlineKeyboardMarkup([button_row], **kwargs)
|
||||
|
||||
Return an InlineKeyboardMarkup from a single row of InlineKeyboardButtons
|
||||
|
||||
Args:
|
||||
button_row (List[:class:`telegram.InlineKeyboardButton`]): The button to use in the
|
||||
markup
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
"""
|
||||
return cls([button_row], **kwargs)
|
||||
|
||||
@classmethod
|
||||
def from_column(cls, button_column, **kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
InlineKeyboardMarkup([[button] for button in button_column], **kwargs)
|
||||
|
||||
Return an InlineKeyboardMarkup from a single column of InlineKeyboardButtons
|
||||
|
||||
Args:
|
||||
button_column (List[:class:`telegram.InlineKeyboardButton`]): The button to use in the
|
||||
markup
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
"""
|
||||
button_grid = [[button] for button in button_column]
|
||||
return cls(button_grid, **kwargs)
|
||||
|
|
|
@ -85,3 +85,125 @@ class ReplyKeyboardMarkup(ReplyMarkup):
|
|||
r.append(button) # str
|
||||
data['keyboard'].append(r)
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def from_button(cls,
|
||||
button,
|
||||
resize_keyboard=False,
|
||||
one_time_keyboard=False,
|
||||
selective=False,
|
||||
**kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
ReplyKeyboardMarkup([[button]], **kwargs)
|
||||
|
||||
Return an ReplyKeyboardMarkup from a single KeyboardButton
|
||||
|
||||
Args:
|
||||
button (:class:`telegram.KeyboardButton` | :obj:`str`): The button to use in the markup
|
||||
resize_keyboard (:obj:`bool`, optional): Requests clients to resize the keyboard vertically
|
||||
for optimal fit (e.g., make the keyboard smaller if there are just two rows of
|
||||
buttons). Defaults to false, in which case the custom keyboard is always of the same
|
||||
height as the app's standard keyboard. Defaults to ``False``
|
||||
one_time_keyboard (:obj:`bool`, optional): Requests clients to hide the keyboard as soon as
|
||||
it's been used. The keyboard will still be available, but clients will automatically
|
||||
display the usual letter-keyboard in the chat - the user can press a special button in
|
||||
the input field to see the custom keyboard again. Defaults to ``False``.
|
||||
selective (:obj:`bool`, optional): Use this parameter if you want to show the keyboard to
|
||||
specific users only. Targets:
|
||||
|
||||
1) users that are @mentioned in the text of the Message object
|
||||
2) if the bot's message is a reply (has reply_to_message_id), sender of the original
|
||||
message.
|
||||
|
||||
Defaults to ``False``.
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
"""
|
||||
return cls([[button]],
|
||||
resize_keyboard=resize_keyboard,
|
||||
one_time_keyboard=one_time_keyboard,
|
||||
selective=selective,
|
||||
**kwargs)
|
||||
|
||||
@classmethod
|
||||
def from_row(cls,
|
||||
button_row,
|
||||
resize_keyboard=False,
|
||||
one_time_keyboard=False,
|
||||
selective=False,
|
||||
**kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
ReplyKeyboardMarkup([button_row], **kwargs)
|
||||
|
||||
Return an ReplyKeyboardMarkup from a single row of KeyboardButtons
|
||||
|
||||
Args:
|
||||
button_row (List[:class:`telegram.KeyboardButton` | :obj:`str`]): The button to use in the
|
||||
markup
|
||||
resize_keyboard (:obj:`bool`, optional): Requests clients to resize the keyboard vertically
|
||||
for optimal fit (e.g., make the keyboard smaller if there are just two rows of
|
||||
buttons). Defaults to false, in which case the custom keyboard is always of the same
|
||||
height as the app's standard keyboard. Defaults to ``False``
|
||||
one_time_keyboard (:obj:`bool`, optional): Requests clients to hide the keyboard as soon as
|
||||
it's been used. The keyboard will still be available, but clients will automatically
|
||||
display the usual letter-keyboard in the chat - the user can press a special button in
|
||||
the input field to see the custom keyboard again. Defaults to ``False``.
|
||||
selective (:obj:`bool`, optional): Use this parameter if you want to show the keyboard to
|
||||
specific users only. Targets:
|
||||
|
||||
1) users that are @mentioned in the text of the Message object
|
||||
2) if the bot's message is a reply (has reply_to_message_id), sender of the original
|
||||
message.
|
||||
|
||||
Defaults to ``False``.
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
"""
|
||||
return cls([button_row],
|
||||
resize_keyboard=resize_keyboard,
|
||||
one_time_keyboard=one_time_keyboard,
|
||||
selective=selective,
|
||||
**kwargs)
|
||||
|
||||
@classmethod
|
||||
def from_column(cls,
|
||||
button_column,
|
||||
resize_keyboard=False,
|
||||
one_time_keyboard=False,
|
||||
selective=False,
|
||||
**kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
ReplyKeyboardMarkup([[button] for button in button_column], **kwargs)
|
||||
|
||||
Return an ReplyKeyboardMarkup from a single column of KeyboardButtons
|
||||
|
||||
Args:
|
||||
button_column (List[:class:`telegram.KeyboardButton` | :obj:`str`]): The button to use in the
|
||||
markup
|
||||
resize_keyboard (:obj:`bool`, optional): Requests clients to resize the keyboard vertically
|
||||
for optimal fit (e.g., make the keyboard smaller if there are just two rows of
|
||||
buttons). Defaults to false, in which case the custom keyboard is always of the same
|
||||
height as the app's standard keyboard. Defaults to ``False``
|
||||
one_time_keyboard (:obj:`bool`, optional): Requests clients to hide the keyboard as soon as
|
||||
it's been used. The keyboard will still be available, but clients will automatically
|
||||
display the usual letter-keyboard in the chat - the user can press a special button in
|
||||
the input field to see the custom keyboard again. Defaults to ``False``.
|
||||
selective (:obj:`bool`, optional): Use this parameter if you want to show the keyboard to
|
||||
specific users only. Targets:
|
||||
|
||||
1) users that are @mentioned in the text of the Message object
|
||||
2) if the bot's message is a reply (has reply_to_message_id), sender of the original
|
||||
message.
|
||||
|
||||
Defaults to ``False``.
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
"""
|
||||
button_grid = [[button] for button in button_column]
|
||||
return cls(button_grid,
|
||||
resize_keyboard=resize_keyboard,
|
||||
one_time_keyboard=one_time_keyboard,
|
||||
selective=selective,
|
||||
**kwargs)
|
||||
|
|
|
@ -323,7 +323,8 @@ class Request(object):
|
|||
else:
|
||||
result = self._request_wrapper('POST', url,
|
||||
body=json.dumps(data).encode('utf-8'),
|
||||
headers={'Content-Type': 'application/json'})
|
||||
headers={'Content-Type': 'application/json'},
|
||||
**urlopen_kwargs)
|
||||
|
||||
return self._parse(result)
|
||||
|
||||
|
|
|
@ -618,16 +618,27 @@ class TestBot(object):
|
|||
# test_sticker module.
|
||||
|
||||
def test_timeout_propagation(self, monkeypatch, bot, chat_id):
|
||||
|
||||
from telegram.vendor.ptb_urllib3.urllib3.util.timeout import Timeout
|
||||
|
||||
class OkException(Exception):
|
||||
pass
|
||||
|
||||
timeout = 500
|
||||
TIMEOUT = 500
|
||||
|
||||
def post(*args, **kwargs):
|
||||
if kwargs.get('timeout') == 500:
|
||||
def request_wrapper(*args, **kwargs):
|
||||
obj = kwargs.get('timeout')
|
||||
if isinstance(obj, Timeout) and obj._read == TIMEOUT:
|
||||
raise OkException
|
||||
|
||||
monkeypatch.setattr('telegram.utils.request.Request.post', post)
|
||||
return b'{"ok": true, "result": []}'
|
||||
|
||||
monkeypatch.setattr('telegram.utils.request.Request._request_wrapper', request_wrapper)
|
||||
|
||||
# Test file uploading
|
||||
with pytest.raises(OkException):
|
||||
bot.send_photo(chat_id, open('tests/data/telegram.jpg', 'rb'), timeout=timeout)
|
||||
bot.send_photo(chat_id, open('tests/data/telegram.jpg', 'rb'), timeout=TIMEOUT)
|
||||
|
||||
# Test JSON submition
|
||||
with pytest.raises(OkException):
|
||||
bot.get_chat_administrators(chat_id, timeout=TIMEOUT)
|
||||
|
|
|
@ -44,6 +44,27 @@ class TestInlineKeyboardMarkup(object):
|
|||
|
||||
assert message.text == 'Testing InlineKeyboardMarkup'
|
||||
|
||||
def test_from_button(self):
|
||||
inline_keyboard_markup = InlineKeyboardMarkup.from_button(
|
||||
InlineKeyboardButton(text='button1', callback_data='data1')).inline_keyboard
|
||||
assert len(inline_keyboard_markup) == 1
|
||||
assert len(inline_keyboard_markup[0]) == 1
|
||||
|
||||
def test_from_row(self):
|
||||
inline_keyboard_markup = InlineKeyboardMarkup.from_row([
|
||||
InlineKeyboardButton(text='button1', callback_data='data1'),
|
||||
InlineKeyboardButton(text='button1', callback_data='data1')]).inline_keyboard
|
||||
assert len(inline_keyboard_markup) == 1
|
||||
assert len(inline_keyboard_markup[0]) == 2
|
||||
|
||||
def test_from_column(self):
|
||||
inline_keyboard_markup = InlineKeyboardMarkup.from_column([
|
||||
InlineKeyboardButton(text='button1', callback_data='data1'),
|
||||
InlineKeyboardButton(text='button1', callback_data='data1')]).inline_keyboard
|
||||
assert len(inline_keyboard_markup) == 2
|
||||
assert len(inline_keyboard_markup[0]) == 1
|
||||
assert len(inline_keyboard_markup[1]) == 1
|
||||
|
||||
def test_expected_values(self, inline_keyboard_markup):
|
||||
assert inline_keyboard_markup.inline_keyboard == self.inline_keyboard
|
||||
|
||||
|
|
|
@ -184,6 +184,24 @@ class TestJobQueue(object):
|
|||
sleep(0.06)
|
||||
assert pytest.approx(self.job_time) == expected_time
|
||||
|
||||
def test_datetime_with_timezone_job_run_once(self, job_queue):
|
||||
# Test that run_once jobs work with timezone aware datetimes.
|
||||
offset = datetime.timedelta(hours=-3)
|
||||
when = datetime.datetime.now(datetime.timezone(offset))
|
||||
|
||||
job_queue.run_once(self.job_run_once, when)
|
||||
sleep(0.01)
|
||||
assert self.result == 1
|
||||
|
||||
def test_datetime_with_timezone_job_run_repeating(self, job_queue):
|
||||
# Test that run_repeating jobs work with timezone aware datetimes.
|
||||
offset = datetime.timedelta(hours=5)
|
||||
now_with_offset = datetime.datetime.now(datetime.timezone(offset))
|
||||
|
||||
job_queue.run_repeating(self.job_run_once, interval=0.01, first=now_with_offset)
|
||||
sleep(0.015)
|
||||
assert self.result == 2
|
||||
|
||||
def test_time_unit_dt_time_today(self, job_queue):
|
||||
# Testing running at a specific time today
|
||||
delta = 0.05
|
||||
|
|
|
@ -51,6 +51,40 @@ class TestReplyKeyboardMarkup(object):
|
|||
|
||||
assert message.text == 'text 2'
|
||||
|
||||
def test_from_button(self):
|
||||
reply_keyboard_markup = ReplyKeyboardMarkup.from_button(
|
||||
KeyboardButton(text='button1')).keyboard
|
||||
assert len(reply_keyboard_markup) == 1
|
||||
assert len(reply_keyboard_markup[0]) == 1
|
||||
|
||||
reply_keyboard_markup = ReplyKeyboardMarkup.from_button('button1').keyboard
|
||||
assert len(reply_keyboard_markup) == 1
|
||||
assert len(reply_keyboard_markup[0]) == 1
|
||||
|
||||
def test_from_row(self):
|
||||
reply_keyboard_markup = ReplyKeyboardMarkup.from_row([
|
||||
KeyboardButton(text='button1'),
|
||||
KeyboardButton(text='button2')]).keyboard
|
||||
assert len(reply_keyboard_markup) == 1
|
||||
assert len(reply_keyboard_markup[0]) == 2
|
||||
|
||||
reply_keyboard_markup = ReplyKeyboardMarkup.from_row(['button1', 'button2']).keyboard
|
||||
assert len(reply_keyboard_markup) == 1
|
||||
assert len(reply_keyboard_markup[0]) == 2
|
||||
|
||||
def test_from_column(self):
|
||||
reply_keyboard_markup = ReplyKeyboardMarkup.from_column([
|
||||
KeyboardButton(text='button1'),
|
||||
KeyboardButton(text='button2')]).keyboard
|
||||
assert len(reply_keyboard_markup) == 2
|
||||
assert len(reply_keyboard_markup[0]) == 1
|
||||
assert len(reply_keyboard_markup[1]) == 1
|
||||
|
||||
reply_keyboard_markup = ReplyKeyboardMarkup.from_column(['button1', 'button2']).keyboard
|
||||
assert len(reply_keyboard_markup) == 2
|
||||
assert len(reply_keyboard_markup[0]) == 1
|
||||
assert len(reply_keyboard_markup[1]) == 1
|
||||
|
||||
def test_expected_values(self, reply_keyboard_markup):
|
||||
assert isinstance(reply_keyboard_markup.keyboard, list)
|
||||
assert isinstance(reply_keyboard_markup.keyboard[0][0], KeyboardButton)
|
||||
|
|
Loading…
Reference in a new issue