Compare commits

...

27 Commits

Author SHA1 Message Date
Noam Meltzer 3ff4cc4445 requirements-dev.txt: Added missing 'wheel' package
Without it we can't run 'setup.py bdist_wheel'
2018-05-05 00:47:34 +03:00
Noam Meltzer 32afc007aa Bump version to v10.1.0 2018-05-05 00:37:13 +03:00
Noam Meltzer c758c19ef4 bot.py: Remove an inaccurate note from docstring 2018-05-05 00:22:23 +03:00
Eldinnie 3bef2fa752 Updating sphinx to py3 (#1096)
* Use python 3 on read the docs

This has multiple improvements on the inspection of classes. Mainly that method signatures propely display the arguments for the method.

* Add note to telegram.Bot about camelCase

Switching to py3 means that the camelCase method descriptors will no longer be displayed in the docs. Adding a note to Bot to clarify they exist.

* Add Alias links for all camelCase
2018-05-03 15:34:50 +02:00
Jacob Bom b4cabecca2 Remove pytest-catchlog from requirements (#1099) 2018-05-03 16:28:57 +03:00
Noam Meltzer 0a8abccc78 InputFile: Fix proper naming of file when reading from subprocess.PIPE (#1079) 2018-05-03 00:17:54 +03:00
Igor Strebezhev 5ff34fc0ba Fix send_sticker() timeout=20 (#1088)
Documentation was correct. Code wasn't.
2018-05-01 22:48:31 +03:00
Trainer Jono 78fee3c1dc Fixed docstring of {User,Chat}.send_* methods (#1081)
* Changed all "User.chat_id"s to "User.id"
* Chat.chat_id --> Chat.id
2018-04-25 23:21:11 +03:00
Noam Meltzer 38d6f4d9f2 Inputfile encode filenames (#1086)
* Unitest to recreate issue #1083

* InputFile: Encode unicode filenames

Fixes #1083
2018-04-25 23:13:00 +03:00
Noam Meltzer 1ec12343f0 Add urllib3 fix for socks5h support (#1085)
FIxes #1077
Changes semantics of socks proxy behaviour.
Until now - socks5:// was actually socs5h:// and we didn't really have socks5://
2018-04-25 21:23:48 +03:00
Paul Larsen b5196f00b2 Add a caption_entity filter for filtering caption entities (#1068)
* Add a caption_entity filter for filtering caption entities

* remove unneeded list comprehensions
2018-04-20 13:24:40 +02:00
Noam Meltzer 39c679e519 Update all appveyor links in README 2018-04-19 16:56:12 +03:00
Noam Meltzer 7a6d2be101 README fixes (#1078)
* Link for the group on the README top

* Fix widget to appveyor build
2018-04-19 16:52:38 +03:00
Dillon Flamand ef74c525b2 Simplify sentence in examples/README.md (#1061) 2018-04-19 15:20:16 +03:00
Pieter Schutz 6d453b7fa2 Bump to v10.0.2 2018-04-17 09:07:19 +02:00
Rahiel Kasim 716f52684f pypi.python.org -> pypi.org (#1059) 2018-04-17 07:45:05 +03:00
Joel Williams 42497367c1 Correct documentation of Dispatcher.add_handler (#1071) 2018-04-17 07:42:09 +03:00
Jannes Höke b77b329dd6 Handle utf8 decoding errors (#1076) 2018-04-17 07:40:02 +03:00
Wagner Macedo 5efd5e2586 CommandHandler faster check (#1074)
Fixes #1073
2018-04-17 07:37:29 +03:00
Daniel Reed cbfb7df643 Explicitly make Bot.full_name return a unicode object, rather than implicitly a unicode object in Python 3 and a str object on Python 2. (#1063) 2018-04-16 10:37:41 +02:00
Or Bin 712baf0c07 Added video note filter (#1067) 2018-04-14 21:53:54 +02:00
Eldinnie 59659eaf5e Update README.rst 2018-04-09 21:30:37 +02:00
Emilio Molinari 3ccf40e8cc Make chat_id a positional argument inside shortcut methods of Chat and User classes #1048 (#1050)
* Make chat_id a positional argument #1048

* Fixed tests
2018-03-17 00:10:11 +01:00
Jannik 38e3b91a87 Filters for Category and file types (#1046)
* Added extra Filters for File Type and Category

Added extra Filters for File Type and Category, based on the provided Mime-Type

* Added tests for the new Filters

Added Tests for the Category and File Types Filters.

* Fixed Tests

Fixed the Tests from the last commit

* Little Fix

* Revert of unwanted changes

* Update filters.py

* Changed the strings

* Changed file_type to mime_type

* Fixed Tests
2018-03-16 23:41:48 +01:00
Eldinnie e182046376 Fix in telegram.Message (#1042)
* Fix in telegram.Message

The `_parse_(html|markdown)` methods now properly return `None` on an empty `Message.text` or an empty `Message.caption`

* As per CR
2018-03-16 21:42:39 +01:00
Joscha Götzer 1530ed20e5 New filter: regex (#1028) 2018-03-15 06:59:27 +02:00
Eldinnie 2b221da9b9 extra whitespace needed on header 2018-03-05 13:57:21 +01:00
26 changed files with 632 additions and 96 deletions
+1
View File
@@ -5,5 +5,6 @@ formats:
python:
setup_py_install: true
version: 3
requirements_file: docs/requirements-docs.txt
+4
View File
@@ -24,6 +24,7 @@ The following wonderful people contributed directly or indirectly to this projec
- `daimajia <https://github.com/daimajia>`_
- `Daniel Reed <https://github.com/nmlorg>`_
- `Eli Gao <https://github.com/eligao>`_
- `Emilio Molinari <https://github.com/xates>`_
- `ErgoZ Riftbit Vaper <https://github.com/ergoz>`_
- `Eugene Lisitsky <https://github.com/lisitsky>`_
- `Eugenio Panadero <https://github.com/azogue>`_
@@ -52,6 +53,7 @@ The following wonderful people contributed directly or indirectly to this projec
- `Noam Meltzer <https://github.com/tsnoam>`_
- `Oleg Shlyazhko <https://github.com/ollmer>`_
- `Oleg Sushchenko <https://github.com/feuillemorte>`_
- `Or Bin <https://github.com/OrBin>`_
- `overquota <https://github.com/overquota>`_
- `Patrick Hofmann <https://github.com/PH89>`_
- `Paul Larsen <https://github.com/PaulSonOfLars>`_
@@ -62,8 +64,10 @@ The following wonderful people contributed directly or indirectly to this projec
- `Simon Schürrle <https://github.com/SitiSchu>`_
- `sooyhwang <https://github.com/sooyhwang>`_
- `thodnev <https://github.com/thodnev>`_
- `Trainer Jono <https://github.com/Tr-Jono>`_
- `Valentijn <https://github.com/Faalentijn>`_
- `voider1 <https://github.com/voider1>`_
- `Wagner Macedo <https://github.com/wagnerluis1982>`_
- `wjt <https://github.com/wjt>`_
Please add yourself here alphabetically when you submit your first pull request.
+60
View File
@@ -1,10 +1,70 @@
=======
Changes
=======
**2018-05-02**
*Released 10.1.0*
Fixes changing previous behaviour:
- Add urllib3 fix for socks5h support (`#1085`_)
- Fix send_sticker() timeout=20 (`#1088`_)
Fixes:
- Add a caption_entity filter for filtering caption entities (`#1068`_)
- Inputfile encode filenames (`#1086`_)
- InputFile: Fix proper naming of file when reading from subprocess.PIPE (`#1079`_)
- Remove pytest-catchlog from requirements (`#1099`_)
- Documentation fixes (`#1061`_, `#1078`_, `#1081`_, `#1096`_)
.. _`#1061`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1061
.. _`#1068`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1068
.. _`#1078`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1078
.. _`#1079`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1079
.. _`#1081`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1081
.. _`#1085`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1085
.. _`#1086`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1086
.. _`#1088`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1088
.. _`#1096`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1096
.. _`#1099`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1099
**2018-04-17**
*Released 10.0.2*
Important fix:
- Handle utf8 decoding errors (`#1076`_)
New features:
- Added Filter.regex (`#1028`_)
- Filters for Category and file types (`#1046`_)
- Added video note filter (`#1067`_)
Fixes:
- Fix in telegram.Message (`#1042`_)
- Make chat_id a positional argument inside shortcut methods of Chat and User classes (`#1050`_)
- Make Bot.full_name return a unicode object. (`#1063`_)
- CommandHandler faster check (`#1074`_)
- Correct documentation of Dispatcher.add_handler (`#1071`_)
- Various small fixes to documentation.
.. _`#1028`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1028
.. _`#1042`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1042
.. _`#1046`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1046
.. _`#1050`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1050
.. _`#1067`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1067
.. _`#1063`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1063
.. _`#1074`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1074
.. _`#1076`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1076
.. _`#1071`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1071
**2018-03-05**
*Released 10.0.1*
Fixes:
- Fix conversationhandler timeout (PR `#1032`_)
- Add missing docs utils (PR `#912`_)
+8 -6
View File
@@ -5,15 +5,17 @@
We have made you a wrapper you can't refuse
We have a vibrant community of developers helping each other in our `Telegram group <https://telegram.me/pythontelegrambotgroup>`_. Join us!
*Stay tuned for library updates and new releases on our* `Telegram Channel <https://telegram.me/pythontelegrambotchannel>`_.
.. image:: https://img.shields.io/pypi/v/python-telegram-bot.svg
:target: https://pypi.python.org/pypi/python-telegram-bot
:target: https://pypi.org/project/python-telegram-bot/
:alt: PyPi Package Version
.. image:: https://img.shields.io/pypi/pyversions/python-telegram-bot.svg
:target: https://pypi.python.org/pypi/python-telegram-bot
:alt: Supported python versions
:target: https://pypi.org/project/python-telegram-bot/
:alt: Supported Python versions
.. image:: https://www.cpu.re/static/python-telegram-bot/downloads.svg
:target: https://www.cpu.re/static/python-telegram-bot/downloads-by-python-version.txt
@@ -31,8 +33,8 @@ We have made you a wrapper you can't refuse
:target: https://travis-ci.org/python-telegram-bot/python-telegram-bot
:alt: Travis CI Status
.. image:: https://img.shields.io/appveyor/ci/Eldinnie/python-telegram-bot/master.svg?logo=appveyor
:target: https://ci.appveyor.com/project/Eldinnie/python-telegram-bot
.. image:: https://img.shields.io/appveyor/ci/python-telegram-bot/python-telegram-bot/master.svg?logo=appveyor
:target: https://ci.appveyor.com/project/python-telegram-bot/python-telegram-bot
:alt: AppVeyor CI Status
@@ -93,7 +95,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 3.4 are supported.
All types and methods of the Telegram Bot API 3.6 are supported.
==========
Installing
+3 -11
View File
@@ -58,9 +58,9 @@ author = u'Leandro Toledo'
# built documents.
#
# The short X.Y version.
version = '10.0' # telegram.__version__[:3]
version = '10.1' # telegram.__version__[:3]
# The full version, including alpha/beta/rc tags.
release = '10.0.1' # telegram.__version__
release = '10.1.0' # telegram.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -291,17 +291,9 @@ texinfo_documents = [
# -- script stuff --------------------------------------------------------
import inspect
def autodoc_skip_member(app, what, name, obj, skip, options):
try:
if inspect.getmodule(obj).__name__.startswith('telegram') and inspect.isfunction(obj):
if name.lower() != name:
return True
except AttributeError:
pass
# Return None so napoleon can handle it
pass
def setup(app):
+1 -1
View File
@@ -1,6 +1,6 @@
# Examples
The examples in this folder are small bots meant to show you how a bot that is written with `python-telegram-bot` looks like. Some bots focus on one specific aspect of the Telegram Bot API while others focus on one of the mechanics of this library. Except for the [`echobot.py`](#pure-api) example, they all use the high-level framework this library provides with the [`telegram.ext`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.html) submodule.
In this folder there are small examples to show what a bot written with `python-telegram-bot` looks like. Some bots focus on one specific aspect of the Telegram Bot API while others focus on one of the mechanics of this library. Except for the [`echobot.py`](#pure-api) example, they all use the high-level framework this library provides with the [`telegram.ext`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.html) submodule.
All examples are licensed under the [CC0 License](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/LICENSE.txt) and are therefore fully dedicated to the public domain. You can use them as the base for your own bots without worrying about copyrights.
+1 -1
View File
@@ -6,5 +6,5 @@ yapf
pre-commit
beautifulsoup4
pytest
pytest-catchlog
pytest-timeout
wheel
+60 -1
View File
@@ -545,7 +545,7 @@ class Bot(TelegramObject):
disable_notification=False,
reply_to_message_id=None,
reply_markup=None,
timeout=None,
timeout=20,
**kwargs):
"""Use this method to send .webp stickers.
@@ -3070,61 +3070,120 @@ class Bot(TelegramObject):
# camelCase aliases
getMe = get_me
"""Alias for :attr:`get_me`"""
sendMessage = send_message
"""Alias for :attr:`send_message`"""
deleteMessage = delete_message
"""Alias for :attr:`delete_message`"""
forwardMessage = forward_message
"""Alias for :attr:`forward_message`"""
sendPhoto = send_photo
"""Alias for :attr:`send_photo`"""
sendAudio = send_audio
"""Alias for :attr:`send_audio`"""
sendDocument = send_document
"""Alias for :attr:`send_document`"""
sendSticker = send_sticker
"""Alias for :attr:`send_sticker`"""
sendVideo = send_video
"""Alias for :attr:`send_video`"""
sendVoice = send_voice
"""Alias for :attr:`send_voice`"""
sendVideoNote = send_video_note
"""Alias for :attr:`send_video_note`"""
sendMediaGroup = send_media_group
"""Alias for :attr:`send_media_group`"""
sendLocation = send_location
"""Alias for :attr:`send_location`"""
editMessageLiveLocation = edit_message_live_location
"""Alias for :attr:`edit_message_live_location`"""
stopMessageLiveLocation = stop_message_live_location
"""Alias for :attr:`stop_message_live_location`"""
sendVenue = send_venue
"""Alias for :attr:`send_venue`"""
sendContact = send_contact
"""Alias for :attr:`send_contact`"""
sendGame = send_game
"""Alias for :attr:`send_game`"""
sendChatAction = send_chat_action
"""Alias for :attr:`send_chat_action`"""
answerInlineQuery = answer_inline_query
"""Alias for :attr:`answer_inline_query`"""
getUserProfilePhotos = get_user_profile_photos
"""Alias for :attr:`get_user_profile_photos`"""
getFile = get_file
"""Alias for :attr:`get_file`"""
kickChatMember = kick_chat_member
"""Alias for :attr:`kick_chat_member`"""
unbanChatMember = unban_chat_member
"""Alias for :attr:`unban_chat_member`"""
answerCallbackQuery = answer_callback_query
"""Alias for :attr:`answer_callback_query`"""
editMessageText = edit_message_text
"""Alias for :attr:`edit_message_text`"""
editMessageCaption = edit_message_caption
"""Alias for :attr:`edit_message_caption`"""
editMessageReplyMarkup = edit_message_reply_markup
"""Alias for :attr:`edit_message_reply_markup`"""
getUpdates = get_updates
"""Alias for :attr:`get_updates`"""
setWebhook = set_webhook
"""Alias for :attr:`set_webhook`"""
deleteWebhook = delete_webhook
"""Alias for :attr:`delete_webhook`"""
leaveChat = leave_chat
"""Alias for :attr:`leave_chat`"""
getChat = get_chat
"""Alias for :attr:`get_chat`"""
getChatAdministrators = get_chat_administrators
"""Alias for :attr:`get_chat_administrators`"""
getChatMember = get_chat_member
"""Alias for :attr:`get_chat_member`"""
setChatStickerSet = set_chat_sticker_set
"""Alias for :attr:`set_chat_sticker_set`"""
deleteChatStickerSet = delete_chat_sticker_set
"""Alias for :attr:`delete_chat_sticker_set`"""
getChatMembersCount = get_chat_members_count
"""Alias for :attr:`get_chat_members_count`"""
getWebhookInfo = get_webhook_info
"""Alias for :attr:`get_webhook_info`"""
setGameScore = set_game_score
"""Alias for :attr:`set_game_score`"""
getGameHighScores = get_game_high_scores
"""Alias for :attr:`get_game_high_scores`"""
sendInvoice = send_invoice
"""Alias for :attr:`send_invoice`"""
answerShippingQuery = answer_shipping_query
"""Alias for :attr:`answer_shipping_query`"""
answerPreCheckoutQuery = answer_pre_checkout_query
"""Alias for :attr:`answer_pre_checkout_query`"""
restrictChatMember = restrict_chat_member
"""Alias for :attr:`restrict_chat_member`"""
promoteChatMember = promote_chat_member
"""Alias for :attr:`promote_chat_member`"""
exportChatInviteLink = export_chat_invite_link
"""Alias for :attr:`export_chat_invite_link`"""
setChatPhoto = set_chat_photo
"""Alias for :attr:`set_chat_photo`"""
deleteChatPhoto = delete_chat_photo
"""Alias for :attr:`delete_chat_photo`"""
setChatTitle = set_chat_title
"""Alias for :attr:`set_chat_title`"""
setChatDescription = set_chat_description
"""Alias for :attr:`set_chat_description`"""
pinChatMessage = pin_chat_message
"""Alias for :attr:`pin_chat_message`"""
unpinChatMessage = unpin_chat_message
"""Alias for :attr:`unpin_chat_message`"""
getStickerSet = get_sticker_set
"""Alias for :attr:`get_sticker_set`"""
uploadStickerFile = upload_sticker_file
"""Alias for :attr:`upload_sticker_file`"""
createNewStickerSet = create_new_sticker_set
"""Alias for :attr:`create_new_sticker_set`"""
addStickerToSet = add_sticker_to_set
"""Alias for :attr:`add_sticker_to_set`"""
setStickerPositionInSet = set_sticker_position_in_set
"""Alias for :attr:`set_sticker_position_in_set`"""
deleteStickerFromSet = delete_sticker_from_set
"""Alias for :attr:`delete_sticker_from_set`"""
+16 -16
View File
@@ -216,7 +216,7 @@ class Chat(TelegramObject):
def send_message(self, *args, **kwargs):
"""Shortcut for::
bot.send_message(Chat.chat_id, *args, **kwargs)
bot.send_message(Chat.id, *args, **kwargs)
Where Chat is the current instance.
@@ -224,12 +224,12 @@ class Chat(TelegramObject):
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_message(chat_id=self.id, *args, **kwargs)
return self.bot.send_message(self.id, *args, **kwargs)
def send_photo(self, *args, **kwargs):
"""Shortcut for::
bot.send_photo(Chat.chat_id, *args, **kwargs)
bot.send_photo(Chat.id, *args, **kwargs)
Where Chat is the current instance.
@@ -237,12 +237,12 @@ class Chat(TelegramObject):
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_photo(chat_id=self.id, *args, **kwargs)
return self.bot.send_photo(self.id, *args, **kwargs)
def send_audio(self, *args, **kwargs):
"""Shortcut for::
bot.send_audio(Chat.chat_id, *args, **kwargs)
bot.send_audio(Chat.id, *args, **kwargs)
Where Chat is the current instance.
@@ -250,12 +250,12 @@ class Chat(TelegramObject):
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_audio(chat_id=self.id, *args, **kwargs)
return self.bot.send_audio(self.id, *args, **kwargs)
def send_document(self, *args, **kwargs):
"""Shortcut for::
bot.send_document(Chat.chat_id, *args, **kwargs)
bot.send_document(Chat.id, *args, **kwargs)
Where Chat is the current instance.
@@ -263,12 +263,12 @@ class Chat(TelegramObject):
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_document(chat_id=self.id, *args, **kwargs)
return self.bot.send_document(self.id, *args, **kwargs)
def send_sticker(self, *args, **kwargs):
"""Shortcut for::
bot.send_sticker(Chat.chat_id, *args, **kwargs)
bot.send_sticker(Chat.id, *args, **kwargs)
Where Chat is the current instance.
@@ -276,12 +276,12 @@ class Chat(TelegramObject):
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_sticker(chat_id=self.id, *args, **kwargs)
return self.bot.send_sticker(self.id, *args, **kwargs)
def send_video(self, *args, **kwargs):
"""Shortcut for::
bot.send_video(Chat.chat_id, *args, **kwargs)
bot.send_video(Chat.id, *args, **kwargs)
Where Chat is the current instance.
@@ -289,12 +289,12 @@ class Chat(TelegramObject):
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_video(chat_id=self.id, *args, **kwargs)
return self.bot.send_video(self.id, *args, **kwargs)
def send_video_note(self, *args, **kwargs):
"""Shortcut for::
bot.send_video_note(Chat.chat_id, *args, **kwargs)
bot.send_video_note(Chat.id, *args, **kwargs)
Where Chat is the current instance.
@@ -302,12 +302,12 @@ class Chat(TelegramObject):
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_video_note(chat_id=self.id, *args, **kwargs)
return self.bot.send_video_note(self.id, *args, **kwargs)
def send_voice(self, *args, **kwargs):
"""Shortcut for::
bot.send_voice(Chat.chat_id, *args, **kwargs)
bot.send_voice(Chat.id, *args, **kwargs)
Where Chat is the current instance.
@@ -315,4 +315,4 @@ class Chat(TelegramObject):
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_voice(chat_id=self.id, *args, **kwargs)
return self.bot.send_voice(self.id, *args, **kwargs)
+5 -2
View File
@@ -140,6 +140,10 @@ class CommandHandler(Handler):
command.append(
message.bot.username) # in case the command was sent without a username
if not (command[0].lower() in self.command
and command[1].lower() == message.bot.username.lower()):
return False
if self.filters is None:
res = True
elif isinstance(self.filters, list):
@@ -147,8 +151,7 @@ class CommandHandler(Handler):
else:
res = self.filters(message)
return res and (command[0].lower() in self.command
and command[1].lower() == message.bot.username.lower())
return res
return False
+1 -1
View File
@@ -308,7 +308,7 @@ class Dispatcher(object):
A handler must be an instance of a subclass of :class:`telegram.ext.Handler`. All handlers
are organized in groups with a numeric value. The default group is 0. All groups will be
evaluated for handling an update, but only 0 or 1 handler per group will be used. If
:class:`telegram.DispatcherHandlerStop` is raised from one of the handlers, no further
:class:`telegram.ext.DispatcherHandlerStop` is raised from one of the handlers, no further
handlers (regardless of the group) will be called.
The priority/order of handlers is determined as follows:
+133 -1
View File
@@ -17,6 +17,8 @@
# 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 the Filters for use with the MessageHandler class."""
import re
from telegram import Chat
from future.utils import string_types
@@ -171,6 +173,33 @@ class Filters(object):
command = _Command()
""":obj:`Filter`: Messages starting with ``/``."""
class regex(BaseFilter):
"""
Filters updates by searching for an occurence of ``pattern`` in the message text.
The ``re.search`` function is used to determine whether an update should be filtered.
Refer to the documentation of the ``re`` module for more information.
Note: Does not allow passing groups or a groupdict like the ``RegexHandler`` yet,
but this will probably be implemented in a future update, gradually phasing out the
RegexHandler (see https://github.com/python-telegram-bot/python-telegram-bot/issues/835).
Examples:
Example ``CommandHandler("start", deep_linked_callback, Filters.regex('parameter'))``
Args:
pattern (:obj:`str` | :obj:`Pattern`): The regex pattern.
"""
def __init__(self, pattern):
self.pattern = re.compile(pattern)
self.name = 'Filters.regex({})'.format(self.pattern)
# TODO: Once the callback revamp (#1026) is done, the regex filter should be able to pass
# the matched groups and groupdict to the context object.
def filter(self, message):
return bool(self.pattern.search(message.text))
class _Reply(BaseFilter):
name = 'Filters.reply'
@@ -192,6 +221,79 @@ class Filters(object):
class _Document(BaseFilter):
name = 'Filters.document'
class category(BaseFilter):
"""This Filter filters documents by their category in the mime-type attribute
Note:
This Filter only filters by the mime_type of the document,
it doesn't check the validity of the document.
The user can manipulate the mime-type of a message and
send media with wrong types that don't fit to this handler.
Examples:
Filters.documents.category('audio/') returnes `True` for all types
of audio sent as file, for example 'audio/mpeg' or 'audio/x-wav'
"""
def __init__(self, category):
"""Initialize the category you want to filter
Args:
category (str, optional): category of the media you want to filter"""
self.category = category
self.name = "Filters.document.category('{}')".format(self.category)
def filter(self, message):
if message.document:
return message.document.mime_type.startswith(self.category)
application = category('application/')
audio = category('audio/')
image = category('image/')
video = category('video/')
text = category('text/')
class mime_type(BaseFilter):
"""This Filter filters documents by their mime-type attribute
Note:
This Filter only filters by the mime_type of the document,
it doesn't check the validity of document.
The user can manipulate the mime-type of a message and
send media with wrong types that don't fit to this handler.
Examples:
Filters.documents.mime_type('audio/mpeg') filters all audio in mp3 format.
"""
def __init__(self, mimetype):
"""Initialize the category you want to filter
Args:
filetype (str, optional): mime_type of the media you want to filter"""
self.mimetype = mimetype
self.name = "Filters.document.mime_type('{}')".format(self.mimetype)
def filter(self, message):
if message.document:
return message.document.mime_type == self.mimetype
apk = mime_type('application/vnd.android.package-archive')
doc = mime_type('application/msword')
docx = mime_type('application/vnd.openxmlformats-officedocument.wordprocessingml.document')
exe = mime_type('application/x-ms-dos-executable')
gif = mime_type('video/mp4')
jpg = mime_type('image/jpeg')
mp3 = mime_type('audio/mpeg')
pdf = mime_type('application/pdf')
py = mime_type('text/x-python')
svg = mime_type('image/svg+xml')
txt = mime_type('text/plain')
targz = mime_type('application/x-compressed-tar')
wav = mime_type('audio/x-wav')
xml = mime_type('application/xml')
zip = mime_type('application/zip')
def filter(self, message):
return bool(message.document)
@@ -234,6 +336,15 @@ class Filters(object):
voice = _Voice()
""":obj:`Filter`: Messages that contain :class:`telegram.Voice`."""
class _VideoNote(BaseFilter):
name = 'Filters.video_note'
def filter(self, message):
return bool(message.video_note)
video_note = _VideoNote()
""":obj:`Filter`: Messages that contain :class:`telegram.VideoNote`."""
class _Contact(BaseFilter):
name = 'Filters.contact'
@@ -430,7 +541,28 @@ class Filters(object):
self.name = 'Filters.entity({})'.format(self.entity_type)
def filter(self, message):
return any([entity.type == self.entity_type for entity in message.entities])
return any(entity.type == self.entity_type for entity in message.entities)
class caption_entity(BaseFilter):
"""
Filters media messages to only allow those which have a :class:`telegram.MessageEntity`
where their `type` matches `entity_type`.
Examples:
Example ``MessageHandler(Filters.caption_entity("hashtag"), callback_method)``
Args:
entity_type: Caption Entity type to check for. All types can be found as constants
in :class:`telegram.MessageEntity`.
"""
def __init__(self, entity_type):
self.entity_type = entity_type
self.name = 'Filters.caption_entity({})'.format(self.entity_type)
def filter(self, message):
return any(entity.type == self.entity_type for entity in message.caption_entities)
class _Private(BaseFilter):
name = 'Filters.private'
+7 -1
View File
@@ -70,7 +70,9 @@ class InputFile(object):
self.input_file_content = self.input_file.read()
if 'filename' in data:
self.filename = self.data.pop('filename')
elif hasattr(self.input_file, 'name'):
elif (hasattr(self.input_file, 'name') and
not isinstance(self.input_file.name, int) and # py3
self.input_file.name != '<fdopen>'): # py2
# on py2.7, pylint fails to understand this properly
# pylint: disable=E1101
self.filename = os.path.basename(self.input_file.name)
@@ -86,6 +88,10 @@ class InputFile(object):
else:
self.mimetype = DEFAULT_MIME_TYPE
if sys.version_info < (3,):
if isinstance(self.filename, unicode): # flake8: noqa pylint: disable=E0602
self.filename = self.filename.encode('utf-8', 'replace')
@property
def headers(self):
""":obj:`dict`: Headers."""
+6
View File
@@ -868,6 +868,9 @@ class Message(TelegramObject):
@staticmethod
def _parse_html(message_text, entities, urled=False):
if message_text is None:
return None
if not sys.maxunicode == 0xffff:
message_text = message_text.encode('utf-16-le')
@@ -962,6 +965,9 @@ class Message(TelegramObject):
@staticmethod
def _parse_markdown(message_text, entities, urled=False):
if message_text is None:
return None
if not sys.maxunicode == 0xffff:
message_text = message_text.encode('utf-16-le')
+17 -17
View File
@@ -88,7 +88,7 @@ class User(TelegramObject):
"""
if self.last_name:
return '{} {}'.format(self.first_name, self.last_name)
return u'{} {}'.format(self.first_name, self.last_name)
return self.first_name
@classmethod
@@ -150,7 +150,7 @@ class User(TelegramObject):
def send_message(self, *args, **kwargs):
"""Shortcut for::
bot.send_message(User.chat_id, *args, **kwargs)
bot.send_message(User.id, *args, **kwargs)
Where User is the current instance.
@@ -158,12 +158,12 @@ class User(TelegramObject):
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_message(chat_id=self.id, *args, **kwargs)
return self.bot.send_message(self.id, *args, **kwargs)
def send_photo(self, *args, **kwargs):
"""Shortcut for::
bot.send_photo(User.chat_id, *args, **kwargs)
bot.send_photo(User.id, *args, **kwargs)
Where User is the current instance.
@@ -171,12 +171,12 @@ class User(TelegramObject):
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_photo(chat_id=self.id, *args, **kwargs)
return self.bot.send_photo(self.id, *args, **kwargs)
def send_audio(self, *args, **kwargs):
"""Shortcut for::
bot.send_audio(User.chat_id, *args, **kwargs)
bot.send_audio(User.id, *args, **kwargs)
Where User is the current instance.
@@ -184,12 +184,12 @@ class User(TelegramObject):
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_audio(chat_id=self.id, *args, **kwargs)
return self.bot.send_audio(self.id, *args, **kwargs)
def send_document(self, *args, **kwargs):
"""Shortcut for::
bot.send_document(User.chat_id, *args, **kwargs)
bot.send_document(User.id, *args, **kwargs)
Where User is the current instance.
@@ -197,12 +197,12 @@ class User(TelegramObject):
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_document(chat_id=self.id, *args, **kwargs)
return self.bot.send_document(self.id, *args, **kwargs)
def send_sticker(self, *args, **kwargs):
"""Shortcut for::
bot.send_sticker(User.chat_id, *args, **kwargs)
bot.send_sticker(User.id, *args, **kwargs)
Where User is the current instance.
@@ -210,12 +210,12 @@ class User(TelegramObject):
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_sticker(chat_id=self.id, *args, **kwargs)
return self.bot.send_sticker(self.id, *args, **kwargs)
def send_video(self, *args, **kwargs):
"""Shortcut for::
bot.send_video(User.chat_id, *args, **kwargs)
bot.send_video(User.id, *args, **kwargs)
Where User is the current instance.
@@ -223,12 +223,12 @@ class User(TelegramObject):
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_video(chat_id=self.id, *args, **kwargs)
return self.bot.send_video(self.id, *args, **kwargs)
def send_video_note(self, *args, **kwargs):
"""Shortcut for::
bot.send_video_note(User.chat_id, *args, **kwargs)
bot.send_video_note(User.id, *args, **kwargs)
Where User is the current instance.
@@ -236,12 +236,12 @@ class User(TelegramObject):
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_video_note(chat_id=self.id, *args, **kwargs)
return self.bot.send_video_note(self.id, *args, **kwargs)
def send_voice(self, *args, **kwargs):
"""Shortcut for::
bot.send_voice(User.chat_id, *args, **kwargs)
bot.send_voice(User.id, *args, **kwargs)
Where User is the current instance.
@@ -249,4 +249,4 @@ class User(TelegramObject):
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_voice(chat_id=self.id, *args, **kwargs)
return self.bot.send_voice(self.id, *args, **kwargs)
+6 -1
View File
@@ -145,9 +145,14 @@ class Request(object):
dict: A JSON parsed as Python dict with results - on error this dict will be empty.
"""
decoded_s = json_data.decode('utf-8')
try:
decoded_s = json_data.decode('utf-8')
data = json.loads(decoded_s)
except UnicodeDecodeError:
logging.getLogger(__name__).debug(
'Logging raw invalid UTF-8 response:\n%r', json_data)
raise TelegramError('Server response could not be decoded using UTF-8')
except ValueError:
raise TelegramError('Invalid server response')
+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__ = '10.0.1'
__version__ = '10.1.0'
+20 -13
View File
@@ -126,52 +126,59 @@ class TestChat(object):
def test_instance_method_send_message(self, monkeypatch, chat):
def test(*args, **kwargs):
return kwargs['chat_id'] == chat.id and args[1] == 'test'
return args[1] == chat.id and args[2] == 'test'
monkeypatch.setattr('telegram.Bot.send_message', test)
assert chat.send_message('test')
def test_instance_method_send_photo(self, monkeypatch, chat):
def test(*args, **kwargs):
return args[1] == chat.id and args[2] == 'test_photo'
monkeypatch.setattr('telegram.Bot.send_photo', test)
assert chat.send_photo('test_photo')
def test_instance_method_send_audio(self, monkeypatch, chat):
def test(*args, **kwargs):
return kwargs['chat_id'] == chat.id and kwargs['audio'] == 'test_audio'
return args[1] == chat.id and args[2] == 'test_audio'
monkeypatch.setattr('telegram.Bot.send_audio', test)
assert chat.send_audio(audio='test_audio')
assert chat.send_audio('test_audio')
def test_instance_method_send_document(self, monkeypatch, chat):
def test(*args, **kwargs):
return kwargs['chat_id'] == chat.id and kwargs['document'] == 'test_document'
return args[1] == chat.id and args[2] == 'test_document'
monkeypatch.setattr('telegram.Bot.send_document', test)
assert chat.send_document(document='test_document')
assert chat.send_document('test_document')
def test_instance_method_send_sticker(self, monkeypatch, chat):
def test(*args, **kwargs):
return kwargs['chat_id'] == chat.id and kwargs['sticker'] == 'test_sticker'
return args[1] == chat.id and args[2] == 'test_sticker'
monkeypatch.setattr('telegram.Bot.send_sticker', test)
assert chat.send_sticker(sticker='test_sticker')
assert chat.send_sticker('test_sticker')
def test_instance_method_send_video(self, monkeypatch, chat):
def test(*args, **kwargs):
return kwargs['chat_id'] == chat.id and kwargs['video'] == 'test_video'
return args[1] == chat.id and args[2] == 'test_video'
monkeypatch.setattr('telegram.Bot.send_video', test)
assert chat.send_video(video='test_video')
assert chat.send_video('test_video')
def test_instance_method_send_video_note(self, monkeypatch, chat):
def test(*args, **kwargs):
return kwargs['chat_id'] == chat.id and kwargs['video_note'] == 'test_video_note'
return args[1] == chat.id and args[2] == 'test_video_note'
monkeypatch.setattr('telegram.Bot.send_video_note', test)
assert chat.send_video_note(video_note='test_video_note')
assert chat.send_video_note('test_video_note')
def test_instance_method_send_voice(self, monkeypatch, chat):
def test(*args, **kwargs):
return kwargs['chat_id'] == chat.id and kwargs['voice'] == 'test_voice'
return args[1] == chat.id and args[2] == 'test_voice'
monkeypatch.setattr('telegram.Bot.send_voice', test)
assert chat.send_voice(voice='test_voice')
assert chat.send_voice('test_voice')
def test_equality(self):
a = Chat(self.id, self.title, self.type)
+20 -1
View File
@@ -21,7 +21,7 @@ import pytest
from telegram import (Message, Update, Chat, Bot, User, CallbackQuery, InlineQuery,
ChosenInlineResult, ShippingQuery, PreCheckoutQuery)
from telegram.ext import CommandHandler, Filters
from telegram.ext import CommandHandler, Filters, BaseFilter
message = Message(1, User(1, '', False), None, Chat(1, ''), text='test')
@@ -246,3 +246,22 @@ class TestCommandHandler(object):
def test_other_update_types(self, false_update):
handler = CommandHandler('test', self.callback_basic)
assert not handler.check_update(false_update)
def test_filters_for_wrong_command(self, message):
"""Filters should not be executed if the command does not match the handler"""
class TestFilter(BaseFilter):
def __init__(self):
self.tested = False
def filter(self, message):
self.tested = True
test_filter = TestFilter()
handler = CommandHandler('foo', self.callback_basic, filters=test_filter)
message.text = '/bar'
handler.check_update(Update(0, message=message))
assert not test_filter.tested
+137 -1
View File
@@ -20,8 +20,9 @@ import datetime
import pytest
from telegram import Message, User, Chat, MessageEntity
from telegram import Message, User, Chat, MessageEntity, Document
from telegram.ext import Filters, BaseFilter
import re
@pytest.fixture(scope='function')
@@ -51,6 +52,22 @@ class TestFilters(object):
message.text = '/test'
assert Filters.command(message)
def test_filters_regex(self, message):
message.text = '/start deep-linked param'
assert Filters.regex(r'deep-linked param')(message)
message.text = '/help'
assert Filters.regex(r'help')(message)
message.text = '/help'
assert Filters.regex('help')(message)
message.text = 'test'
assert not Filters.regex(r'fail')(message)
assert Filters.regex(r'test')(message)
assert Filters.regex(re.compile(r'test'))(message)
message.text = 'i love python'
assert Filters.regex(r'.\b[lo]{2}ve python')(message)
def test_filters_reply(self, message):
another_message = Message(1, User(1, 'TestOther', False), datetime.datetime.now(),
Chat(0, 'private'))
@@ -69,6 +86,105 @@ class TestFilters(object):
message.document = 'test'
assert Filters.document(message)
def test_filters_document_type(self, message):
message.document = Document("file_id", mime_type="application/vnd.android.package-archive")
assert Filters.document.apk(message)
assert Filters.document.application(message)
assert not Filters.document.doc(message)
assert not Filters.document.audio(message)
message.document.mime_type = "application/msword"
assert Filters.document.doc(message)
assert Filters.document.application(message)
assert not Filters.document.docx(message)
assert not Filters.document.audio(message)
message.document.mime_type = "application/vnd.openxmlformats-" \
"officedocument.wordprocessingml.document"
assert Filters.document.docx(message)
assert Filters.document.application(message)
assert not Filters.document.exe(message)
assert not Filters.document.audio(message)
message.document.mime_type = "application/x-ms-dos-executable"
assert Filters.document.exe(message)
assert Filters.document.application(message)
assert not Filters.document.docx(message)
assert not Filters.document.audio(message)
message.document.mime_type = "video/mp4"
assert Filters.document.gif(message)
assert Filters.document.video(message)
assert not Filters.document.jpg(message)
assert not Filters.document.text(message)
message.document.mime_type = "image/jpeg"
assert Filters.document.jpg(message)
assert Filters.document.image(message)
assert not Filters.document.mp3(message)
assert not Filters.document.video(message)
message.document.mime_type = "audio/mpeg"
assert Filters.document.mp3(message)
assert Filters.document.audio(message)
assert not Filters.document.pdf(message)
assert not Filters.document.image(message)
message.document.mime_type = "application/pdf"
assert Filters.document.pdf(message)
assert Filters.document.application(message)
assert not Filters.document.py(message)
assert not Filters.document.audio(message)
message.document.mime_type = "text/x-python"
assert Filters.document.py(message)
assert Filters.document.text(message)
assert not Filters.document.svg(message)
assert not Filters.document.application(message)
message.document.mime_type = "image/svg+xml"
assert Filters.document.svg(message)
assert Filters.document.image(message)
assert not Filters.document.txt(message)
assert not Filters.document.video(message)
message.document.mime_type = "text/plain"
assert Filters.document.txt(message)
assert Filters.document.text(message)
assert not Filters.document.targz(message)
assert not Filters.document.application(message)
message.document.mime_type = "application/x-compressed-tar"
assert Filters.document.targz(message)
assert Filters.document.application(message)
assert not Filters.document.wav(message)
assert not Filters.document.audio(message)
message.document.mime_type = "audio/x-wav"
assert Filters.document.wav(message)
assert Filters.document.audio(message)
assert not Filters.document.xml(message)
assert not Filters.document.image(message)
message.document.mime_type = "application/xml"
assert Filters.document.xml(message)
assert Filters.document.application(message)
assert not Filters.document.zip(message)
assert not Filters.document.audio(message)
message.document.mime_type = "application/zip"
assert Filters.document.zip(message)
assert Filters.document.application(message)
assert not Filters.document.apk(message)
assert not Filters.document.audio(message)
message.document.mime_type = "image/x-rgb"
assert not Filters.document.category("application/")(message)
assert not Filters.document.mime_type("application/x-sh")(message)
message.document.mime_type = "application/x-sh"
assert Filters.document.category("application/")(message)
assert Filters.document.mime_type("application/x-sh")(message)
def test_filters_photo(self, message):
assert not Filters.photo(message)
message.photo = 'test'
@@ -89,6 +205,11 @@ class TestFilters(object):
message.voice = 'test'
assert Filters.voice(message)
def test_filters_video_note(self, message):
assert not Filters.video_note(message)
message.video_note = 'test'
assert Filters.video_note(message)
def test_filters_contact(self, message):
assert not Filters.contact(message)
message.contact = 'test'
@@ -189,6 +310,21 @@ class TestFilters(object):
second = MessageEntity.de_json(second, None)
message.entities = [message_entity, second]
assert Filters.entity(message_entity.type)(message)
assert not Filters.caption_entity(message_entity.type)(message)
def test_caption_entities_filter(self, message, message_entity):
message.caption_entities = [message_entity]
assert Filters.caption_entity(message_entity.type)(message)
message.caption_entities = []
assert not Filters.caption_entity(MessageEntity.MENTION)(message)
second = message_entity.to_dict()
second['type'] = 'bold'
second = MessageEntity.de_json(second, None)
message.caption_entities = [message_entity, second]
assert Filters.caption_entity(message_entity.type)(message)
assert not Filters.entity(message_entity.type)(message)
def test_private_filter(self, message):
assert Filters.private(message)
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# 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 os
import subprocess
import sys
from telegram import InputFile
class TestInputFile(object):
png = os.path.join('tests', 'data', 'game.png')
def test_subprocess_pipe(self):
if sys.platform == 'win32':
cmd = ['type', self.png]
else:
cmd = ['cat', self.png]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=(sys.platform == 'win32'))
in_file = InputFile({'photo': proc.stdout})
assert in_file.input_file_content == open(self.png, 'rb').read()
assert in_file.mimetype == 'image/png'
assert in_file.filename == 'image.png'
proc.kill()
+20
View File
@@ -173,6 +173,11 @@ class TestMessage(object):
text_html = self.test_message.text_html
assert text_html == test_html_string
def test_text_html_empty(self, message):
message.text = None
message.caption = "test"
assert message.text_html is None
def test_text_html_urled(self):
test_html_string = ('Test for &lt;<b>bold</b>, <i>ita_lic</i>, <code>code</code>, '
'<a href="http://github.com/">links</a> and <pre>pre</pre>. '
@@ -186,6 +191,11 @@ class TestMessage(object):
text_markdown = self.test_message.text_markdown
assert text_markdown == test_md_string
def test_text_markdown_empty(self, message):
message.text = None
message.caption = "test"
assert message.text_markdown is None
def test_text_markdown_urled(self):
test_md_string = ('Test for <*bold*, _ita\_lic_, `code`, [links](http://github.com/) and '
'```pre```. [http://google.com](http://google.com)')
@@ -215,6 +225,11 @@ class TestMessage(object):
caption_html = self.test_message.caption_html
assert caption_html == test_html_string
def test_caption_html_empty(self, message):
message.text = "test"
message.caption = None
assert message.caption_html is None
def test_caption_html_urled(self):
test_html_string = ('Test for &lt;<b>bold</b>, <i>ita_lic</i>, <code>code</code>, '
'<a href="http://github.com/">links</a> and <pre>pre</pre>. '
@@ -228,6 +243,11 @@ class TestMessage(object):
caption_markdown = self.test_message.caption_markdown
assert caption_markdown == test_md_string
def test_caption_markdown_empty(self, message):
message.text = "test"
message.caption = None
assert message.caption_markdown is None
def test_caption_markdown_urled(self):
test_md_string = ('Test for <*bold*, _ita\_lic_, `code`, [links](http://github.com/) and '
'```pre```. [http://google.com](http://google.com)')
+1 -1
View File
@@ -27,7 +27,7 @@ from telegram import Sticker, TelegramError, PhotoSize, InputFile
@pytest.fixture(scope='function')
def photo_file():
f = open('tests/data/telegram.jpg', 'rb')
f = open(u'tests/data/telegram.jpg', 'rb')
yield f
f.close()
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# 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 TelegramError
from telegram.utils.request import Request
def test_parse_illegal_callback_data():
"""
Clients can send arbitrary bytes in callback data.
Make sure the correct error is raised in this case.
"""
server_response = b'{"invalid utf-8": "\x80"}'
with pytest.raises(TelegramError, match='Server response could not be decoded using UTF-8'):
Request._parse(server_response)
+26 -19
View File
@@ -42,8 +42,8 @@ def user(bot):
class TestUser(object):
id = 1
is_bot = True
first_name = 'first_name'
last_name = 'last_name'
first_name = u'first\u2022name'
last_name = u'last\u2022name'
username = 'username'
language_code = 'en_us'
@@ -85,16 +85,16 @@ class TestUser(object):
def test_name(self, user):
assert user.name == '@username'
user.username = None
assert user.name == 'first_name last_name'
assert user.name == u'first\u2022name last\u2022name'
user.last_name = None
assert user.name == 'first_name'
assert user.name == u'first\u2022name'
user.username = self.username
assert user.name == '@username'
def test_full_name(self, user):
assert user.full_name == 'first_name last_name'
assert user.full_name == u'first\u2022name last\u2022name'
user.last_name = None
assert user.full_name == 'first_name'
assert user.full_name == u'first\u2022name'
def test_get_profile_photos(self, monkeypatch, user):
def test(_, *args, **kwargs):
@@ -105,52 +105,59 @@ class TestUser(object):
def test_instance_method_send_message(self, monkeypatch, user):
def test(*args, **kwargs):
return kwargs['chat_id'] == user.id and args[1] == 'test'
return args[1] == user.id and args[2] == 'test'
monkeypatch.setattr('telegram.Bot.send_message', test)
assert user.send_message('test')
def test_instance_method_send_photo(self, monkeypatch, user):
def test(*args, **kwargs):
return args[1] == user.id and args[2] == 'test_photo'
monkeypatch.setattr('telegram.Bot.send_photo', test)
assert user.send_photo('test_photo')
def test_instance_method_send_audio(self, monkeypatch, user):
def test(*args, **kwargs):
return kwargs['chat_id'] == user.id and kwargs['audio'] == 'test_audio'
return args[1] == user.id and args[2] == 'test_audio'
monkeypatch.setattr('telegram.Bot.send_audio', test)
assert user.send_audio(audio='test_audio')
assert user.send_audio('test_audio')
def test_instance_method_send_document(self, monkeypatch, user):
def test(*args, **kwargs):
return kwargs['chat_id'] == user.id and kwargs['document'] == 'test_document'
return args[1] == user.id and args[2] == 'test_document'
monkeypatch.setattr('telegram.Bot.send_document', test)
assert user.send_document(document='test_document')
assert user.send_document('test_document')
def test_instance_method_send_sticker(self, monkeypatch, user):
def test(*args, **kwargs):
return kwargs['chat_id'] == user.id and kwargs['sticker'] == 'test_sticker'
return args[1] == user.id and args[2] == 'test_sticker'
monkeypatch.setattr('telegram.Bot.send_sticker', test)
assert user.send_sticker(sticker='test_sticker')
assert user.send_sticker('test_sticker')
def test_instance_method_send_video(self, monkeypatch, user):
def test(*args, **kwargs):
return kwargs['chat_id'] == user.id and kwargs['video'] == 'test_video'
return args[1] == user.id and args[2] == 'test_video'
monkeypatch.setattr('telegram.Bot.send_video', test)
assert user.send_video(video='test_video')
assert user.send_video('test_video')
def test_instance_method_send_video_note(self, monkeypatch, user):
def test(*args, **kwargs):
return kwargs['chat_id'] == user.id and kwargs['video_note'] == 'test_video_note'
return args[1] == user.id and args[2] == 'test_video_note'
monkeypatch.setattr('telegram.Bot.send_video_note', test)
assert user.send_video_note(video_note='test_video_note')
assert user.send_video_note('test_video_note')
def test_instance_method_send_voice(self, monkeypatch, user):
def test(*args, **kwargs):
return kwargs['chat_id'] == user.id and kwargs['voice'] == 'test_voice'
return args[1] == user.id and args[2] == 'test_voice'
monkeypatch.setattr('telegram.Bot.send_voice', test)
assert user.send_voice(voice='test_voice')
assert user.send_voice('test_voice')
def test_equality(self):
a = User(self.id, self.first_name, self.is_bot, self.last_name)