Compare commits

...

5 Commits

Author SHA1 Message Date
Pieter Schutz 72ecc696cb Bump to v12.4.2 2020-02-10 11:39:26 +01:00
Bibo-Joshi a447760411 Make sure PP can read files that dont have bot_data (#1760)
* Make sure PP can read files that dont have bot_data

* Improve workaround
2020-02-08 19:24:35 +02:00
Bibo-Joshi 6da529c46b Pass correct parse_mode to InlineResults if bot.defaults is None (#1763)
* Pass correct parse_mode to InlineResults if bot.defaults is None

* Add tests for inlinequeryresults with (default)parse_mode

* enhance tests
2020-02-08 18:52:22 +02:00
Noam Meltzer bd1b2fb6c1 Bump version to v12.4.1 2020-02-08 15:06:05 +02:00
Bibo-Joshi f9a8cd924c Make Filters.command only accept MessageEntity commands (#1744)
* Make Filters.command only accept MessageEntitie commands

* Add option to filters.command to allow cmds anywhere in the message

* Make codecov happy, also retroactive for #1631
2020-02-08 14:54:21 +02:00
9 changed files with 220 additions and 18 deletions
+20
View File
@@ -2,6 +2,26 @@
Changelog
=========
Version 12.4.2
==============
*Released 2020-02-10*
**Bug Fixes**
- Pass correct parse_mode to InlineResults if bot.defaults is None (`#1763`_)
- Make sure PP can read files that dont have bot_data (`#1760`_)
.. _`#1763`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1763
.. _`#1760`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1760
Version 12.4.1
==============
*Released 2020-02-08*
This is a quick release for `#1744`_ which was accidently left out of v12.4.0 though mentioned in the
release notes.
Version 12.4.0
==============
*Released 2020-02-08*
+1 -1
View File
@@ -60,7 +60,7 @@ author = u'Leandro Toledo'
# The short X.Y version.
version = '12.4' # telegram.__version__[:3]
# The full version, including alpha/beta/rc tags.
release = '12.4.0' # telegram.__version__
release = '12.4.2' # telegram.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
+4 -1
View File
@@ -1512,7 +1512,10 @@ class Bot(TelegramObject):
for res in results:
if res._has_parse_mode and res.parse_mode == DEFAULT_NONE:
res.parse_mode = self.defaults.parse_mode
if self.defaults:
res.parse_mode = self.defaults.parse_mode
else:
res.parse_mode = None
if res._has_input_message_content and res.input_message_content:
if (res.input_message_content._has_parse_mode
and res.input_message_content.parse_mode == DEFAULT_NONE):
+35 -11
View File
@@ -22,7 +22,7 @@ import re
from future.utils import string_types
from telegram import Chat, Update
from telegram import Chat, Update, MessageEntity
__all__ = ['Filters', 'BaseFilter', 'InvertedFilter', 'MergedFilter']
@@ -249,10 +249,7 @@ class Filters(object):
def __call__(self, update):
if isinstance(update, Update):
if self.update_filter:
return self.filter(update)
else:
return self.filter(update.effective_message)
return self.filter(update.effective_message)
else:
return self._TextIterable(update)
@@ -296,10 +293,7 @@ class Filters(object):
def __call__(self, update):
if isinstance(update, Update):
if self.update_filter:
return self.filter(update)
else:
return self.filter(update.effective_message)
return self.filter(update.effective_message)
else:
return self._CaptionIterable(update)
@@ -321,11 +315,41 @@ class Filters(object):
class _Command(BaseFilter):
name = 'Filters.command'
class _CommandOnlyStart(BaseFilter):
def __init__(self, only_start):
self.only_start = only_start
self.name = 'Filters.command({})'.format(only_start)
def filter(self, message):
return (message.entities
and any([e.type == MessageEntity.BOT_COMMAND for e in message.entities]))
def __call__(self, update):
if isinstance(update, Update):
return self.filter(update.effective_message)
else:
return self._CommandOnlyStart(update)
def filter(self, message):
return bool(message.text and message.text.startswith('/'))
return (message.entities and message.entities[0].type == MessageEntity.BOT_COMMAND
and message.entities[0].offset == 0)
command = _Command()
"""Messages starting with ``/``."""
"""
Messages with a :attr:`telegram.MessageEntity.BOT_COMMAND`. By default only allows
messages `starting` with a bot command. Pass ``False`` to also allow messages that contain a
bot command `anywhere` in the text.
Examples::
MessageHandler(Filters.command, command_at_start_callback)
MessageHandler(Filters.command(False), command_anywhere_callback)
Args:
update (:obj:`bool`, optional): Whether to only allow messages that `start` with a bot
command. Defaults to ``True``.
"""
class regex(BaseFilter):
"""
+2 -1
View File
@@ -84,7 +84,8 @@ class PicklePersistence(BasePersistence):
all = pickle.load(f)
self.user_data = defaultdict(dict, all['user_data'])
self.chat_data = defaultdict(dict, all['chat_data'])
self.bot_data = all['bot_data']
# For backwards compatibility with files not containing bot data
self.bot_data = all.get('bot_data', {})
self.conversations = all['conversations']
except IOError:
self.conversations = {}
+1 -1
View File
@@ -17,4 +17,4 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
__version__ = '12.4.0'
__version__ = '12.4.2'
+63 -1
View File
@@ -28,7 +28,8 @@ from future.utils import string_types
from telegram import (Bot, Update, ChatAction, TelegramError, User, InlineKeyboardMarkup,
InlineKeyboardButton, InlineQueryResultArticle, InputTextMessageContent,
ShippingOption, LabeledPrice, ChatPermissions, Poll)
ShippingOption, LabeledPrice, ChatPermissions, Poll,
InlineQueryResultDocument)
from telegram.error import BadRequest, InvalidToken, NetworkError, RetryAfter
from telegram.utils.helpers import from_timestamp, escape_markdown
@@ -237,6 +238,67 @@ class TestBot(object):
switch_pm_text='switch pm',
switch_pm_parameter='start_pm')
def test_answer_inline_query_no_default_parse_mode(self, monkeypatch, bot):
def test(_, url, data, *args, **kwargs):
return data == {'cache_time': 300,
'results': [{'title': 'test_result', 'id': '123', 'type': 'document',
'document_url': 'https://raw.githubusercontent.com/'
'python-telegram-bot/logos/master/logo/png/'
'ptb-logo_240.png', 'mime_type': 'image/png',
'caption': 'ptb_logo'}],
'next_offset': '42', 'switch_pm_parameter': 'start_pm',
'inline_query_id': 1234, 'is_personal': True,
'switch_pm_text': 'switch pm'}
monkeypatch.setattr('telegram.utils.request.Request.post', test)
results = [InlineQueryResultDocument(
id='123',
document_url='https://raw.githubusercontent.com/python-telegram-bot/logos/master/'
'logo/png/ptb-logo_240.png',
title='test_result',
mime_type='image/png',
caption='ptb_logo',
)]
assert bot.answer_inline_query(1234,
results=results,
cache_time=300,
is_personal=True,
next_offset='42',
switch_pm_text='switch pm',
switch_pm_parameter='start_pm')
@pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True)
def test_answer_inline_query_default_parse_mode(self, monkeypatch, default_bot):
def test(_, url, data, *args, **kwargs):
return data == {'cache_time': 300,
'results': [{'title': 'test_result', 'id': '123', 'type': 'document',
'document_url': 'https://raw.githubusercontent.com/'
'python-telegram-bot/logos/master/logo/png/'
'ptb-logo_240.png', 'mime_type': 'image/png',
'caption': 'ptb_logo', 'parse_mode': 'Markdown'}],
'next_offset': '42', 'switch_pm_parameter': 'start_pm',
'inline_query_id': 1234, 'is_personal': True,
'switch_pm_text': 'switch pm'}
monkeypatch.setattr('telegram.utils.request.Request.post', test)
results = [InlineQueryResultDocument(
id='123',
document_url='https://raw.githubusercontent.com/python-telegram-bot/logos/master/'
'logo/png/ptb-logo_240.png',
title='test_result',
mime_type='image/png',
caption='ptb_logo',
)]
assert default_bot.answer_inline_query(1234,
results=results,
cache_time=300,
is_personal=True,
next_offset='42',
switch_pm_text='switch pm',
switch_pm_parameter='start_pm')
@flaky(3, 1)
@pytest.mark.timeout(10)
def test_get_user_profile_photos(self, bot, chat_id):
+28 -1
View File
@@ -63,12 +63,23 @@ class TestFilters(object):
assert Filters.caption({'test', 'test1'})(update)
assert not Filters.caption(['test1', 'test2'])(update)
def test_filters_command(self, update):
def test_filters_command_default(self, update):
update.message.text = 'test'
assert not Filters.command(update)
update.message.text = '/test'
assert not Filters.command(update)
# Only accept commands at the beginning
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 3, 5)]
assert not Filters.command(update)
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 5)]
assert Filters.command(update)
def test_filters_command_anywhere(self, update):
update.message.text = 'test /cmd'
assert not (Filters.command(False))(update)
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 5, 4)]
assert (Filters.command(False))(update)
def test_filters_regex(self, update):
SRE_TYPE = type(re.match("", ""))
update.message.text = '/start deep-linked param'
@@ -120,6 +131,7 @@ class TestFilters(object):
def test_filters_merged_with_regex(self, update):
SRE_TYPE = type(re.match("", ""))
update.message.text = '/start deep-linked param'
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)]
result = (Filters.command & Filters.regex(r'linked param'))(update)
assert result
assert isinstance(result, dict)
@@ -216,6 +228,7 @@ class TestFilters(object):
result = filter(update)
assert not result
update.message.text = '/start'
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)]
result = filter(update)
assert result
assert isinstance(result, bool)
@@ -230,6 +243,7 @@ class TestFilters(object):
def test_regex_inverted(self, update):
update.message.text = '/start deep-linked param'
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 5)]
filter = ~Filters.regex(r'deep-linked param')
result = filter(update)
assert not result
@@ -243,6 +257,7 @@ class TestFilters(object):
result = filter(update)
assert not result
update.message.text = '/start'
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)]
result = filter(update)
assert result
update.message.text = '/linked'
@@ -251,15 +266,18 @@ class TestFilters(object):
filter = (~Filters.regex('linked') | Filters.command)
update.message.text = "it's linked"
update.message.entities = []
result = filter(update)
assert not result
update.message.text = '/start linked'
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)]
result = filter(update)
assert result
update.message.text = '/start'
result = filter(update)
assert result
update.message.text = 'nothig'
update.message.entities = []
result = filter(update)
assert result
@@ -664,14 +682,17 @@ class TestFilters(object):
def test_inverted_filters(self, update):
update.message.text = '/test'
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 5)]
assert Filters.command(update)
assert not (~Filters.command)(update)
update.message.text = 'test'
update.message.entities = []
assert not Filters.command(update)
assert (~Filters.command)(update)
def test_inverted_and_filters(self, update):
update.message.text = '/test'
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 5)]
update.message.forward_date = 1
assert (Filters.forwarded & Filters.command)(update)
assert not (~Filters.forwarded & Filters.command)(update)
@@ -683,6 +704,7 @@ class TestFilters(object):
assert not (Filters.forwarded & ~Filters.command)(update)
assert (~(Filters.forwarded & Filters.command))(update)
update.message.text = 'test'
update.message.entities = []
assert not (Filters.forwarded & Filters.command)(update)
assert not (~Filters.forwarded & Filters.command)(update)
assert not (Filters.forwarded & ~Filters.command)(update)
@@ -746,6 +768,7 @@ class TestFilters(object):
def test_merged_short_circuit_and(self, update):
update.message.text = '/test'
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 5)]
class TestException(Exception):
pass
@@ -760,6 +783,7 @@ class TestFilters(object):
(Filters.command & raising_filter)(update)
update.message.text = 'test'
update.message.entities = []
(Filters.command & raising_filter)(update)
def test_merged_short_circuit_or(self, update):
@@ -778,10 +802,12 @@ class TestFilters(object):
(Filters.command | raising_filter)(update)
update.message.text = '/test'
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 5)]
(Filters.command | raising_filter)(update)
def test_merged_data_merging_and(self, update):
update.message.text = '/test'
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 5)]
class DataFilter(BaseFilter):
data_filter = True
@@ -799,6 +825,7 @@ class TestFilters(object):
assert result['test'] == ['blah1', 'blah2']
update.message.text = 'test'
update.message.entities = []
result = (Filters.command & DataFilter('blah'))(update)
assert not result
+66 -1
View File
@@ -335,6 +335,23 @@ def good_pickle_files(user_data, chat_data, bot_data, conversations):
os.remove(name)
@pytest.fixture(scope='function')
def pickle_files_wo_bot_data(user_data, chat_data, conversations):
all = {'user_data': user_data, 'chat_data': chat_data, 'conversations': conversations}
with open('pickletest_user_data', 'wb') as f:
pickle.dump(user_data, f)
with open('pickletest_chat_data', 'wb') as f:
pickle.dump(chat_data, f)
with open('pickletest_conversations', 'wb') as f:
pickle.dump(conversations, f)
with open('pickletest', 'wb') as f:
pickle.dump(all, f)
yield True
for name in ['pickletest_user_data', 'pickletest_chat_data',
'pickletest_conversations', 'pickletest']:
os.remove(name)
@pytest.fixture(scope='function')
def update(bot):
user = User(id=321, first_name='test_user', is_bot=False)
@@ -447,6 +464,54 @@ class TestPickelPersistence(object):
with pytest.raises(KeyError):
conversation2[(123, 123)]
def test_with_multi_file_wo_bot_data(self, pickle_persistence, pickle_files_wo_bot_data):
user_data = pickle_persistence.get_user_data()
assert isinstance(user_data, defaultdict)
assert user_data[12345]['test1'] == 'test2'
assert user_data[67890][3] == 'test4'
assert user_data[54321] == {}
chat_data = pickle_persistence.get_chat_data()
assert isinstance(chat_data, defaultdict)
assert chat_data[-12345]['test1'] == 'test2'
assert chat_data[-67890][3] == 'test4'
assert chat_data[-54321] == {}
bot_data = pickle_persistence.get_bot_data()
assert isinstance(bot_data, dict)
assert not bot_data.keys()
conversation1 = pickle_persistence.get_conversations('name1')
assert isinstance(conversation1, dict)
assert conversation1[(123, 123)] == 3
assert conversation1[(456, 654)] == 4
with pytest.raises(KeyError):
conversation1[(890, 890)]
conversation2 = pickle_persistence.get_conversations('name2')
assert isinstance(conversation1, dict)
assert conversation2[(123, 321)] == 1
assert conversation2[(890, 890)] == 2
with pytest.raises(KeyError):
conversation2[(123, 123)]
def test_with_single_file_wo_bot_data(self, pickle_persistence, pickle_files_wo_bot_data):
pickle_persistence.single_file = True
user_data = pickle_persistence.get_user_data()
assert isinstance(user_data, defaultdict)
assert user_data[12345]['test1'] == 'test2'
assert user_data[67890][3] == 'test4'
assert user_data[54321] == {}
chat_data = pickle_persistence.get_chat_data()
assert isinstance(chat_data, defaultdict)
assert chat_data[-12345]['test1'] == 'test2'
assert chat_data[-67890][3] == 'test4'
assert chat_data[-54321] == {}
bot_data = pickle_persistence.get_bot_data()
assert isinstance(bot_data, dict)
assert not bot_data.keys()
def test_updating_multi_file(self, pickle_persistence, good_pickle_files):
user_data = pickle_persistence.get_user_data()
user_data[54321]['test9'] = 'test 10'
@@ -659,7 +724,7 @@ class TestPickelPersistence(object):
if not context.chat_data == {}:
pytest.fail()
if not context.bot_data == bot_data:
pytest.failt()
pytest.fail()
context.user_data['test1'] = 'test2'
context.chat_data['test3'] = 'test4'
context.bot_data['test1'] = 'test0'