Merge remote-tracking branch 'origin/master' into payment

This commit is contained in:
Noam Meltzer 2017-06-01 22:01:04 +03:00
commit 137ffe2e73
36 changed files with 737 additions and 105 deletions

2
.gitmodules vendored
View file

@ -1,4 +1,4 @@
[submodule "telegram/vendor/urllib3"]
path = telegram/vendor/urllib3
path = telegram/vendor/ptb_urllib3
url = https://github.com/python-telegram-bot/urllib3.git
branch = ptb

View file

@ -3,6 +3,8 @@
hooks:
- id: yapf
files: ^(telegram|tests)/.*\.py$
args:
- --diff
- repo: git://github.com/pre-commit/pre-commit-hooks
sha: 18d7035de5388cc7775be57f529c154bf541aab9
hooks:

View file

@ -2,6 +2,40 @@
Changes
=======
**2017-05-29**
*Released 6.0.2*
- Avoid confusion with user's ``urllib3`` by renaming vendored ``urllib3`` to ``ptb_urllib3``
**2017-05-19**
*Released 6.0.1*
- Add support for ``User.language_code``
- Fix ``Message.text_html`` and ``Message.text_markdown`` for messages with emoji
**2017-05-19**
*Released 6.0.0*
- Add support for Bot API 2.3.1
- Add support for ``deleteMessage`` API method
- New, simpler API for ``JobQueue`` - https://github.com/python-telegram-bot/python-telegram-bot/pull/484
- Download files into file-like objects - https://github.com/python-telegram-bot/python-telegram-bot/pull/459
- Use vendor ``urllib3`` to address issues with timeouts
- The default timeout for messages is now 5 seconds. For sending media, the default timeout is now 20 seconds.
- String attributes that are not set are now ``None`` by default, instead of empty strings
- Add ``text_markdown`` and ``text_html`` properties to ``Message`` - https://github.com/python-telegram-bot/python-telegram-bot/pull/507
- Add support for Socks5 proxy - https://github.com/python-telegram-bot/python-telegram-bot/pull/518
- Add support for filters in ``CommandHandler`` - https://github.com/python-telegram-bot/python-telegram-bot/pull/536
- Add the ability to invert (not) filters - https://github.com/python-telegram-bot/python-telegram-bot/pull/552
- Add ``Filters.group`` and ``Filters.private``
- Compatibility with GAE via ``urllib3.contrib`` package - https://github.com/python-telegram-bot/python-telegram-bot/pull/583
- Add equality rich comparision operators to telegram objects - https://github.com/python-telegram-bot/python-telegram-bot/pull/604
- Several bugfixes and other improvements
- Remove some deprecated code
**2017-04-17**
*Released 5.3.1*
@ -52,6 +86,7 @@ Changes
- Rework ``JobQueue``
- Introduce ``ConversationHandler``
- Introduce ``telegram.constants`` - https://github.com/python-telegram-bot/python-telegram-bot/pull/342
**2016-07-12**

View file

@ -84,7 +84,13 @@ make the development of bots easy and straightforward. These classes are contain
Telegram API support
====================
As of **4. Dec 2016**, all types and methods of the Telegram Bot API are supported.
As of **21. May 2017**, all types and methods of the Telegram Bot API 2.3.1 are supported. Additionally, the ``deleteMessage`` API function and the field ``User.language_code`` are supported.
Also, version 6.1 beta 0 is available, offering full but experimental Bot API 3.0 coverage:
.. code:: shell
$ pip install python-telegram-bot==6.1b0
==========
Installing

View file

@ -59,9 +59,9 @@ author = u'Leandro Toledo'
# built documents.
#
# The short X.Y version.
version = '5.3' # telegram.__version__[:3]
version = '6.0' # telegram.__version__[:3]
# The full version, including alpha/beta/rc tags.
release = '5.3.1' # telegram.__version__
release = '6.0.2' # telegram.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View file

@ -56,7 +56,7 @@ def gender(bot, update):
def photo(bot, update):
user = update.message.from_user
photo_file = bot.getFile(update.message.photo[-1].file_id)
photo_file = bot.get_file(update.message.photo[-1].file_id)
photo_file.download('user_photo.jpg')
logger.info("Photo of %s: %s" % (user.first_name, 'user_photo.jpg'))
update.message.reply_text('Gorgeous! Now, send me your location please, '

View file

@ -20,7 +20,7 @@ def main():
# get the first pending update_id, this is so we can skip over it in case
# we get an "Unauthorized" exception.
try:
update_id = bot.getUpdates()[0].update_id
update_id = bot.get_updates()[0].update_id
except IndexError:
update_id = None
@ -39,9 +39,7 @@ def main():
def echo(bot):
global update_id
# Request updates after the last update_id
for update in bot.getUpdates(offset=update_id, timeout=10):
# chat_id is required to reply to any message
chat_id = update.message.chat_id
for update in bot.get_updates(offset=update_id, timeout=10):
update_id = update.update_id + 1
if update.message: # your bot can receive updates without messages

View file

@ -72,7 +72,7 @@ def inlinequery(bot, update):
def error(bot, update, error):
logger.warn('Update "%s" caused error "%s"' % (update, error))
logger.warning('Update "%s" caused error "%s"' % (update, error))
def main():

View file

@ -26,9 +26,9 @@ def start(bot, update):
def button(bot, update):
query = update.callback_query
bot.editMessageText(text="Selected option: %s" % query.data,
chat_id=query.message.chat_id,
message_id=query.message.message_id)
bot.edit_message_text(text="Selected option: %s" % query.data,
chat_id=query.message.chat_id,
message_id=query.message.message_id)
def help(bot, update):

View file

@ -35,7 +35,7 @@ def start(bot, update):
def alarm(bot, job):
"""Function to send the alarm message"""
bot.sendMessage(job.context, text='Beep!')
bot.send_message(job.context, text='Beep!')
def set(bot, update, args, job_queue, chat_data):
@ -49,9 +49,8 @@ def set(bot, update, args, job_queue, chat_data):
return
# Add job to queue
job = Job(alarm, due, repeat=False, context=chat_id)
job = job_queue.run_once(alarm, due, context=chat_id)
chat_data['job'] = job
job_queue.put(job)
update.message.reply_text('Timer successfully set!')

View file

@ -18,11 +18,6 @@ def requirements():
packages = find_packages(exclude=['tests*'])
packages.extend(['telegram.vendor.urllib3.urllib3',
'telegram.vendor.urllib3.urllib3.packages', 'telegram.vendor.urllib3.urllib3.packages.ssl_match_hostname',
'telegram.vendor.urllib3.urllib3.packages.backports', 'telegram.vendor.urllib3.urllib3.contrib',
'telegram.vendor.urllib3.urllib3.util',
])
with codecs.open('README.rst', 'r', 'utf-8') as fd:
fn = os.path.join('telegram', 'version.py')

View file

@ -22,8 +22,6 @@ from sys import version_info
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'vendor', 'urllib3'))
from .base import TelegramObject
from .user import User
from .chat import Chat
@ -94,6 +92,7 @@ from .precheckoutquery import PreCheckoutQuery
from .shippingquery import ShippingQuery
from .webhookinfo import WebhookInfo
from .gamehighscore import GameHighScore
from .videonote import VideoNote
from .update import Update
from .bot import Bot
from .constants import (MAX_MESSAGE_LENGTH, MAX_CAPTION_LENGTH, SUPPORTED_WEBHOOK_PORTS,
@ -123,6 +122,6 @@ __all__ = [
'Video', 'Voice', 'MAX_MESSAGE_LENGTH', 'MAX_CAPTION_LENGTH', 'SUPPORTED_WEBHOOK_PORTS',
'MAX_FILESIZE_DOWNLOAD', 'MAX_FILESIZE_UPLOAD', 'MAX_MESSAGES_PER_SECOND_PER_CHAT',
'MAX_MESSAGES_PER_SECOND', 'MAX_MESSAGES_PER_MINUTE_PER_GROUP', 'WebhookInfo', 'Animation',
'Game', 'GameHighScore', 'LabeledPrice', 'SuccessfulPayment', 'ShippingOption',
'Game', 'GameHighScore', 'VideoNote', 'LabeledPrice', 'SuccessfulPayment', 'ShippingOption',
'ShippingAddress', 'PreCheckoutQuery', 'OrderInfo', 'Invoice', 'ShippingQuery'
]

View file

@ -671,6 +671,66 @@ class Bot(TelegramObject):
timeout=timeout,
**kwargs)
@log
def send_video_note(self,
chat_id,
video_note,
duration=None,
length=None,
disable_notification=False,
reply_to_message_id=None,
reply_markup=None,
timeout=20.,
**kwargs):
"""As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute
long. Use this method to send video messages
Args:
chat_id (int|str): Unique identifier for the message recipient - Chat id.
video_note (InputFile|str): Video note to send. Pass a file_id as String to send a
video note that exists on the Telegram servers (recommended) or upload a new video.
Sending video notes by a URL is currently unsupported
duration (Optional[int]): Duration of sent audio in seconds.
length (Optional[int]): Video width and height
disable_notification (Optional[bool]): Sends the message silently. iOS users will not
receive a notification, Android users will receive a notification with no sound.
reply_to_message_id (Optional[int]): If the message is a reply, ID of the original
message.
reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional interface options. A
JSON-serialized object for an inline keyboard, custom reply keyboard, instructions
to remove reply keyboard or to force a reply from the user.
timeout (Optional[int|float]): Send file timeout (default: 20 seconds).
**kwargs (dict): Arbitrary keyword arguments.
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/sendVideoNote'.format(self.base_url)
data = {'chat_id': chat_id, 'video_note': video_note}
if duration is not None:
data['duration'] = duration
if length is not None:
data['length'] = length
return self._message_wrapper(
url,
data,
chat_id=chat_id,
video_note=video_note,
duration=duration,
length=length,
disable_notification=disable_notification,
reply_to_message_id=reply_to_message_id,
reply_markup=reply_markup,
timeout=timeout,
**kwargs)
@log
@message
def send_location(self,
@ -1963,6 +2023,7 @@ class Bot(TelegramObject):
sendSticker = send_sticker
sendVideo = send_video
sendVoice = send_voice
sendVideoNote = send_video_note
sendLocation = send_location
sendVenue = send_venue
sendContact = send_contact

View file

@ -31,3 +31,5 @@ class ChatAction(object):
UPLOAD_AUDIO = 'upload_audio'
UPLOAD_DOCUMENT = 'upload_document'
FIND_LOCATION = 'find_location'
RECORD_VIDEO_NOTE = 'record_video_note'
UPLOAD_VIDEO_NOTE = 'upload_video_note'

View file

@ -19,6 +19,11 @@
""" This module contains the Filters for use with the MessageHandler class """
from telegram import Chat
try:
str_type = base_string
except NameError:
str_type = str
class BaseFilter(object):
"""Base class for all Message Filters
@ -209,13 +214,68 @@ class Filters(object):
class _StatusUpdate(BaseFilter):
class _NewChatMembers(BaseFilter):
def filter(self, message):
return bool(message.new_chat_members)
new_chat_members = _NewChatMembers()
class _LeftChatMember(BaseFilter):
def filter(self, message):
return bool(message.left_chat_member)
left_chat_member = _LeftChatMember()
class _NewChatTitle(BaseFilter):
def filter(self, message):
return bool(message.new_chat_title)
new_chat_title = _NewChatTitle()
class _NewChatPhoto(BaseFilter):
def filter(self, message):
return bool(message.new_chat_photo)
new_chat_photo = _NewChatPhoto()
class _DeleteChatPhoto(BaseFilter):
def filter(self, message):
return bool(message.delete_chat_photo)
delete_chat_photo = _DeleteChatPhoto()
class _ChatCreated(BaseFilter):
def filter(self, message):
return bool(message.group_chat_created or message.supergroup_chat_created or
message.channel_chat_created)
chat_created = _ChatCreated()
class _Migrate(BaseFilter):
def filter(self, message):
return bool(message.migrate_from_chat_id or message.migrate_to_chat_id)
migrate = _Migrate()
class _PinnedMessage(BaseFilter):
def filter(self, message):
return bool(message.pinned_message)
pinned_message = _PinnedMessage()
def filter(self, message):
return bool(message.new_chat_member or message.left_chat_member
or message.new_chat_title or message.new_chat_photo
or message.delete_chat_photo or message.group_chat_created
or message.supergroup_chat_created or message.channel_chat_created
or message.migrate_to_chat_id or message.migrate_from_chat_id
or message.pinned_message)
return bool(self.new_chat_members(message) or self.left_chat_member(message) or
self.new_chat_title(message) or self.new_chat_photo(message) or
self.delete_chat_photo(message) or self.chat_created(message) or
self.migrate(message) or self.pinned_message(message))
status_update = _StatusUpdate()
@ -277,3 +337,24 @@ class Filters(object):
return bool(message.successful_payment)
successful_payment = _SuccessfulPayment()
class language(BaseFilter):
"""
Filters messages to only allow those which are from users with a certain language code.
Note that according to telegrams documentation, every single user does not have the
language_code attribute.
Args:
lang (str|list): Which language code(s) to allow through. This will be matched using
.startswith meaning that 'en' will match both 'en_US' and 'en_GB'
"""
def __init__(self, lang):
if isinstance(lang, str_type):
self.lang = [lang]
else:
self.lang = lang
def filter(self, message):
return message.from_user.language_code and any(
[message.from_user.language_code.startswith(x) for x in self.lang])

View file

@ -37,8 +37,6 @@ class MessageHandler(Handler):
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
allow_edited (Optional[bool]): If the handler should also accept edited messages.
Default is ``False``
pass_update_queue (optional[bool]): If the handler should be passed the
update queue as a keyword argument called ``update_queue``. It can
be used to insert updates. Default is ``False``
@ -52,8 +50,12 @@ class MessageHandler(Handler):
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
message_updates (Optional[bool]): Should "normal" message updates be handled? Default is
``True``.
allow_edited (Optional[bool]): If the handler should also accept edited messages.
Default is ``False`` - Deprecated. use edited updates instead.
channel_post_updates (Optional[bool]): Should channel posts updates be handled? Default is
``True``.
edited_updates (Optional[bool]): Should "edited" message updates be handled? Default is
``False``.
"""
@ -66,9 +68,14 @@ class MessageHandler(Handler):
pass_user_data=False,
pass_chat_data=False,
message_updates=True,
channel_post_updates=True):
if not message_updates and not channel_post_updates:
raise ValueError('Both message_updates & channel_post_updates are False')
channel_post_updates=True,
edited_updates=False):
if not message_updates and not channel_post_updates and not edited_updates:
raise ValueError(
'message_updates, channel_post_updates and edited_updates are all False')
if allow_edited:
warnings.warn('allow_edited is getting deprecated, please use edited_updates instead')
edited_updates = allow_edited
super(MessageHandler, self).__init__(
callback,
@ -77,9 +84,9 @@ class MessageHandler(Handler):
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
self.filters = filters
self.allow_edited = allow_edited
self.message_updates = message_updates
self.channel_post_updates = channel_post_updates
self.edited_updates = edited_updates
# We put this up here instead of with the rest of checking code
# in check_update since we don't wanna spam a ton
@ -88,17 +95,13 @@ class MessageHandler(Handler):
'deprecated, please use bitwise operators (& and |) '
'instead. More info: https://git.io/vPTbc.')
def _is_allowed_message(self, update):
return (self.message_updates
and (update.message or (update.edited_message and self.allow_edited)))
def _is_allowed_channel_post(self, update):
return (self.channel_post_updates
and (update.channel_post or (update.edited_channel_post and self.allow_edited)))
def _is_allowed_update(self, update):
return any([(self.message_updates and update.message),
(self.edited_updates and update.edited_message),
(self.channel_post_updates and update.channel_post)])
def check_update(self, update):
if (isinstance(update, Update)
and (self._is_allowed_message(update) or self._is_allowed_channel_post(update))):
if isinstance(update, Update) and self._is_allowed_update(update):
if not self.filters:
res = True

View file

@ -32,6 +32,7 @@ class InlineQueryResultGif(InlineQueryResult):
thumb_url (str): URL of the static thumbnail for the result (jpeg or gif).
gif_width (Optional[int]): Width of the GIF.
gif_height (Optional[int]): Height of the GIF.
gif_duration (Optional[int]): Duration of the GIF.
title (Optional[str]): Title for the result.
caption (Optional[str]): Caption of the GIF file to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
@ -45,6 +46,7 @@ class InlineQueryResultGif(InlineQueryResult):
thumb_url (str):
gif_width (Optional[int]):
gif_height (Optional[int]):
gif_duration (Optional[int]):
title (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
@ -63,6 +65,7 @@ class InlineQueryResultGif(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
gif_duration=None,
**kwargs):
# Required
@ -75,6 +78,8 @@ class InlineQueryResultGif(InlineQueryResult):
self.gif_width = gif_width
if gif_height:
self.gif_height = gif_height
if gif_duration:
self.gif_duration = gif_duration
if title:
self.title = title
if caption:

View file

@ -32,6 +32,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
thumb_url (str): URL of the static thumbnail (jpeg or gif) for the result.
mpeg4_width (Optional[int]): Video width.
mpeg4_height (Optional[int]): Video height.
mpeg4_duration (Optional[int]): Video duration
title (Optional[str]): Title for the result.
caption (Optional[str]): Caption of the MPEG-4 file to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
@ -44,6 +45,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
thumb_url (str): URL of the static thumbnail (jpeg or gif) for the result.
mpeg4_width (Optional[int]): Video width.
mpeg4_height (Optional[int]): Video height.
mpeg4_duration (Optional[int]): Video duration
title (Optional[str]): Title for the result.
caption (Optional[str]): Caption of the MPEG-4 file to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
@ -64,6 +66,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
mpeg4_duration=None,
**kwargs):
# Required
@ -76,6 +79,8 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
self.mpeg4_width = mpeg4_width
if mpeg4_height:
self.mpeg4_height = mpeg4_height
if mpeg4_duration:
self.mpeg4_duration = mpeg4_duration
if title:
self.title = title
if caption:

View file

@ -35,7 +35,8 @@ from telegram import TelegramError
DEFAULT_MIME_TYPE = 'application/octet-stream'
USER_AGENT = 'Python Telegram Bot (https://github.com/python-telegram-bot/python-telegram-bot)'
FILE_TYPES = ('audio', 'document', 'photo', 'sticker', 'video', 'voice', 'certificate')
FILE_TYPES = ('audio', 'document', 'photo', 'sticker', 'video', 'voice', 'certificate',
'video_note')
class InputFile(object):
@ -45,27 +46,11 @@ class InputFile(object):
self.data = data
self.boundary = choose_boundary()
if 'audio' in data:
self.input_name = 'audio'
self.input_file = data.pop('audio')
elif 'document' in data:
self.input_name = 'document'
self.input_file = data.pop('document')
elif 'photo' in data:
self.input_name = 'photo'
self.input_file = data.pop('photo')
elif 'sticker' in data:
self.input_name = 'sticker'
self.input_file = data.pop('sticker')
elif 'video' in data:
self.input_name = 'video'
self.input_file = data.pop('video')
elif 'voice' in data:
self.input_name = 'voice'
self.input_file = data.pop('voice')
elif 'certificate' in data:
self.input_name = 'certificate'
self.input_file = data.pop('certificate')
for t in FILE_TYPES:
if t in data:
self.input_name = t
self.input_file = data.pop(t)
break
else:
raise TelegramError('Unknown inputfile type')

View file

@ -24,7 +24,9 @@ from time import mktime
from telegram import (Audio, Contact, Document, Chat, Location, PhotoSize, Sticker, TelegramObject,
User, Video, Voice, Venue, MessageEntity, Game, Invoice, SuccessfulPayment)
from telegram.utils.deprecate import warn_deprecate_obj
from telegram.utils.helpers import escape_html, escape_markdown
from telegram.videonote import VideoNote
class Message(TelegramObject):
@ -54,6 +56,8 @@ class Message(TelegramObject):
entities (List[:class:`telegram.MessageEntity`]): For text messages, special entities
like usernames, URLs, bot commands, etc. that appear in the text. See
parse_entity and parse_entities methods for how to use properly
video_note (:class:`telegram.VideoNote`): Message is a video note, information about the
video message
audio (:class:`telegram.Audio`): Message is an audio file, information about the file
document (:class:`telegram.Document`): Message is a general file, information about the
file
@ -128,6 +132,7 @@ class Message(TelegramObject):
location=None,
venue=None,
new_chat_member=None,
new_chat_members=None,
left_chat_member=None,
new_chat_title=None,
new_chat_photo=None,
@ -142,6 +147,7 @@ class Message(TelegramObject):
invoice=None,
successful_payment=None,
bot=None,
video_note=None,
**kwargs):
# Required
self.message_id = int(message_id)
@ -163,11 +169,13 @@ class Message(TelegramObject):
self.sticker = sticker
self.video = video
self.voice = voice
self.video_note = video_note
self.caption = caption
self.contact = contact
self.location = location
self.venue = venue
self.new_chat_member = new_chat_member
self._new_chat_member = new_chat_member
self.new_chat_members = new_chat_members
self.left_chat_member = left_chat_member
self.new_chat_title = new_chat_title
self.new_chat_photo = new_chat_photo
@ -222,10 +230,12 @@ class Message(TelegramObject):
data['sticker'] = Sticker.de_json(data.get('sticker'), bot)
data['video'] = Video.de_json(data.get('video'), bot)
data['voice'] = Voice.de_json(data.get('voice'), bot)
data['video_note'] = VideoNote.de_json(data.get('video_note'), bot)
data['contact'] = Contact.de_json(data.get('contact'), bot)
data['location'] = Location.de_json(data.get('location'), bot)
data['venue'] = Venue.de_json(data.get('venue'), bot)
data['new_chat_member'] = User.de_json(data.get('new_chat_member'), bot)
data['new_chat_members'] = User.de_list(data.get('new_chat_members'), bot)
data['left_chat_member'] = User.de_json(data.get('left_chat_member'), bot)
data['new_chat_photo'] = PhotoSize.de_list(data.get('new_chat_photo'), bot)
data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot)
@ -261,6 +271,9 @@ class Message(TelegramObject):
data['entities'] = [e.to_dict() for e in self.entities]
if self.new_chat_photo:
data['new_chat_photo'] = [p.to_dict() for p in self.new_chat_photo]
data['new_chat_member'] = data.pop('_new_chat_member', None)
if self.new_chat_members:
data['new_chat_members'] = [u.to_dict() for u in self.new_chat_members]
return data
@ -412,6 +425,23 @@ class Message(TelegramObject):
self._quote(kwargs)
return self.bot.sendVideo(self.chat_id, *args, **kwargs)
def reply_video_note(self, *args, **kwargs):
"""
Shortcut for ``bot.send_video_note(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the video is sent as an actual reply to
this message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter
will be ignored. Default: ``True`` in group chats and ``False`` in private chats.
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
"""
self._quote(kwargs)
return self.bot.send_video_note(self.chat_id, *args, **kwargs)
def reply_voice(self, *args, **kwargs):
"""
Shortcut for ``bot.sendVoice(update.message.chat_id, *args, **kwargs)``
@ -632,6 +662,9 @@ class Message(TelegramObject):
"""
entities = self.parse_entities()
message_text = self.text
if not sys.maxunicode == 0xffff:
message_text = message_text.encode('utf-16-le')
markdown_text = ''
last_offset = 0
@ -651,10 +684,18 @@ class Message(TelegramObject):
else:
insert = text
markdown_text += escape_html(message_text[last_offset:entity.offset]) + insert
if sys.maxunicode == 0xffff:
markdown_text += escape_html(message_text[last_offset:entity.offset]) + insert
else:
markdown_text += escape_html(message_text[last_offset * 2:entity.offset * 2]
.decode('utf-16-le')) + insert
last_offset = entity.offset + entity.length
markdown_text += message_text[last_offset:]
if sys.maxunicode == 0xffff:
markdown_text += escape_html(message_text[last_offset:])
else:
markdown_text += escape_html(message_text[last_offset * 2:].decode('utf-16-le'))
return markdown_text
@property
@ -671,6 +712,9 @@ class Message(TelegramObject):
"""
entities = self.parse_entities()
message_text = self.text
if not sys.maxunicode == 0xffff:
message_text = message_text.encode('utf-16-le')
markdown_text = ''
last_offset = 0
@ -689,9 +733,21 @@ class Message(TelegramObject):
insert = '```' + text + '```'
else:
insert = text
if sys.maxunicode == 0xffff:
markdown_text += escape_markdown(message_text[last_offset:entity.offset]) + insert
else:
markdown_text += escape_markdown(message_text[last_offset * 2:entity.offset * 2]
.decode('utf-16-le')) + insert
markdown_text += escape_markdown(message_text[last_offset:entity.offset]) + insert
last_offset = entity.offset + entity.length
markdown_text += message_text[last_offset:]
if sys.maxunicode == 0xffff:
markdown_text += escape_markdown(message_text[last_offset:])
else:
markdown_text += escape_markdown(message_text[last_offset * 2:].decode('utf-16-le'))
return markdown_text
@property
def new_chat_member(self):
warn_deprecate_obj('new_chat_member', 'new_chat_members')
return self._new_chat_member

View file

@ -26,21 +26,23 @@ class User(TelegramObject):
"""This object represents a Telegram User.
Attributes:
id (int):
first_name (str):
last_name (str):
username (str):
type (str):
id (int): Unique identifier for this user or bot
first_name (str): User's or bot's first name
last_name (str): User's or bot's last name
username (str): User's or bot's username
language_code (str): IETF language tag of the user's language
type (str): Deprecated
Args:
id (int):
first_name (str):
id (int): Unique identifier for this user or bot
first_name (str): User's or bot's first name
**kwargs: Arbitrary keyword arguments.
Keyword Args:
type (Optional[str]):
last_name (Optional[str]):
username (Optional[str]):
type (Optional[str]): Deprecated
last_name (Optional[str]): User's or bot's last name
username (Optional[str]): User's or bot's username
language_code (Optional[str]): IETF language tag of the user's language
bot (Optional[Bot]): The Bot to use for instance methods
"""
@ -50,6 +52,7 @@ class User(TelegramObject):
type=None,
last_name=None,
username=None,
language_code=None,
bot=None,
**kwargs):
# Required
@ -59,6 +62,7 @@ class User(TelegramObject):
self.type = type
self.last_name = last_name
self.username = username
self.language_code = language_code
self.bot = bot
@ -95,3 +99,22 @@ class User(TelegramObject):
Shortcut for ``bot.getUserProfilePhotos(update.message.from_user.id, *args, **kwargs)``
"""
return self.bot.getUserProfilePhotos(self.id, *args, **kwargs)
@staticmethod
def de_list(data, bot):
"""
Args:
data (list):
bot (telegram.Bot):
Returns:
List<telegram.User>:
"""
if not data:
return []
users = list()
for user in data:
users.append(User.de_json(user, bot))
return users

View file

@ -21,8 +21,18 @@
import warnings
def warn_deprecate_obj(old, new):
warnings.warn('{0} is being deprecated, please use {1} from now on'.format(old, new))
# We use our own DeprecationWarning since they are muted by default and "UserWarning" makes it
# seem like it's the user that issued the warning
# We name it something else so that you don't get confused when you attempt to suppress it
class TelegramDeprecationWarning(Warning):
pass
def warn_deprecate_obj(old, new, stacklevel=3):
warnings.warn(
'{0} is being deprecated, please use {1} from now on.'.format(old, new),
category=TelegramDeprecationWarning,
stacklevel=stacklevel)
def deprecate(func, old, new):

View file

@ -20,6 +20,7 @@
import os
import socket
import logging
import warnings
try:
import ujson as json
@ -27,10 +28,15 @@ except ImportError:
import json
import certifi
import urllib3
import urllib3.contrib.appengine
from urllib3.connection import HTTPConnection
from urllib3.util.timeout import Timeout
try:
import telegram.vendor.ptb_urllib3.urllib3 as urllib3
import telegram.vendor.ptb_urllib3.urllib3.contrib.appengine as appengine
from telegram.vendor.ptb_urllib3.urllib3.connection import HTTPConnection
from telegram.vendor.ptb_urllib3.urllib3.util.timeout import Timeout
except ImportError:
warnings.warn("python-telegram-bot wasn't properly installed. Please refer to README.rst on "
"how to properly install.")
raise
from telegram import (InputFile, TelegramError)
from telegram.error import (Unauthorized, NetworkError, TimedOut, BadRequest, ChatMigrated,
@ -90,16 +96,16 @@ class Request(object):
proxy_url = os.environ.get('HTTPS_PROXY') or os.environ.get('https_proxy')
if not proxy_url:
if urllib3.contrib.appengine.is_appengine_sandbox():
if appengine.is_appengine_sandbox():
# Use URLFetch service if running in App Engine
mgr = urllib3.contrib.appengine.AppEngineManager()
mgr = appengine.AppEngineManager()
else:
mgr = urllib3.PoolManager(**kwargs)
else:
kwargs.update(urllib3_proxy_kwargs)
if proxy_url.startswith('socks'):
try:
from urllib3.contrib.socks import SOCKSProxyManager
from telegram.vendor.ptb_urllib3.urllib3.contrib.socks import SOCKSProxyManager
except ImportError:
raise RuntimeError('PySocks is missing')
mgr = SOCKSProxyManager(proxy_url, **kwargs)

0
telegram/vendor/__init__.py vendored Normal file
View file

1
telegram/vendor/ptb_urllib3 vendored Submodule

@ -0,0 +1 @@
Subproject commit ddb495340152a4e51800226dc68a479e48c055ff

@ -1 +0,0 @@
Subproject commit 4b076eedffc1afabf0215ced3820603de73d1ce7

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__ = '5.3.1'
__version__ = '6.0.2'

63
telegram/videonote.py Normal file
View file

@ -0,0 +1,63 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# 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 VideoNote."""
from telegram import PhotoSize, TelegramObject
class VideoNote(TelegramObject):
"""This object represents a Telegram VideoNote.
Attributes:
file_id (str): Unique identifier for this file
length (int): Video width and height as defined by sender
duration (int): Duration of the video in seconds as defined by sender
thumb (Optional[:class:`telegram.PhotoSize`]): Video thumbnail
file_size (Optional[int]): File size
"""
def __init__(self, file_id, length, duration, thumb=None, file_size=None, **kwargs):
# Required
self.file_id = str(file_id)
self.length = int(length)
self.duration = int(duration)
# Optionals
self.thumb = thumb
self.file_size = file_size
self._id_attrs = (self.file_id,)
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.VideoNote:
"""
if not data:
return None
data = super(VideoNote, VideoNote).de_json(data, bot)
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
return VideoNote(**data)

View file

@ -317,7 +317,7 @@ class BotTest(BaseTest, unittest.TestCase):
self.assertEqual(text, fwdmsg.text)
self.assertEqual(fwdmsg.forward_from_message_id, msg.message_id)
@flaky(3, 1)
@flaky(20, 1)
@timeout(10)
def test_set_webhook_get_webhook_info(self):
url = 'https://python-telegram-bot.org/test/webhook'

View file

@ -121,48 +121,59 @@ class FiltersTest(BaseTest, unittest.TestCase):
def test_filters_status_update(self):
self.assertFalse(Filters.status_update(self.message))
self.message.new_chat_member = 'test'
self.message.new_chat_members = ['test']
self.assertTrue(Filters.status_update(self.message))
self.message.new_chat_member = None
self.assertTrue(Filters.status_update.new_chat_members(self.message))
self.message.new_chat_members = None
self.message.left_chat_member = 'test'
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.left_chat_member(self.message))
self.message.left_chat_member = None
self.message.new_chat_title = 'test'
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.new_chat_title(self.message))
self.message.new_chat_title = ''
self.message.new_chat_photo = 'test'
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.new_chat_photo(self.message))
self.message.new_chat_photo = None
self.message.delete_chat_photo = True
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.delete_chat_photo(self.message))
self.message.delete_chat_photo = False
self.message.group_chat_created = True
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.chat_created(self.message))
self.message.group_chat_created = False
self.message.supergroup_chat_created = True
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.chat_created(self.message))
self.message.supergroup_chat_created = False
self.message.migrate_to_chat_id = 100
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.migrate(self.message))
self.message.migrate_to_chat_id = 0
self.message.migrate_from_chat_id = 100
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.migrate(self.message))
self.message.migrate_from_chat_id = 0
self.message.channel_chat_created = True
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.chat_created(self.message))
self.message.channel_chat_created = False
self.message.pinned_message = 'test'
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.pinned_message(self.message))
self.message.pinned_message = None
def test_entities_filter(self):
@ -274,6 +285,32 @@ class FiltersTest(BaseTest, unittest.TestCase):
with self.assertRaises(NotImplementedError):
(custom & Filters.text)(self.message)
def test_language_filter_single(self):
self.message.from_user.language_code = 'en_US'
self.assertTrue((Filters.language('en_US'))(self.message))
self.assertTrue((Filters.language('en'))(self.message))
self.assertFalse((Filters.language('en_GB'))(self.message))
self.assertFalse((Filters.language('da'))(self.message))
self.message.from_user.language_code = 'en_GB'
self.assertFalse((Filters.language('en_US'))(self.message))
self.assertTrue((Filters.language('en'))(self.message))
self.assertTrue((Filters.language('en_GB'))(self.message))
self.assertFalse((Filters.language('da'))(self.message))
self.message.from_user.language_code = 'da'
self.assertFalse((Filters.language('en_US'))(self.message))
self.assertFalse((Filters.language('en'))(self.message))
self.assertFalse((Filters.language('en_GB'))(self.message))
self.assertTrue((Filters.language('da'))(self.message))
def test_language_filter_multiple(self):
f = Filters.language(['en_US', 'da'])
self.message.from_user.language_code = 'en_US'
self.assertTrue(f(self.message))
self.message.from_user.language_code = 'en_GB'
self.assertFalse(f(self.message))
self.message.from_user.language_code = 'da'
self.assertTrue(f(self.message))
if __name__ == '__main__':
unittest.main()

View file

@ -37,6 +37,7 @@ class InlineQueryResultGifTest(BaseTest, unittest.TestCase):
self.gif_url = 'gif url'
self.gif_width = 10
self.gif_height = 15
self.gif_duration = 1
self.thumb_url = 'thumb url'
self.title = 'title'
self.caption = 'caption'
@ -50,6 +51,7 @@ class InlineQueryResultGifTest(BaseTest, unittest.TestCase):
'gif_url': self.gif_url,
'gif_width': self.gif_width,
'gif_height': self.gif_height,
'gif_duration': self.gif_duration,
'thumb_url': self.thumb_url,
'title': self.title,
'caption': self.caption,
@ -65,6 +67,7 @@ class InlineQueryResultGifTest(BaseTest, unittest.TestCase):
self.assertEqual(gif.gif_url, self.gif_url)
self.assertEqual(gif.gif_width, self.gif_width)
self.assertEqual(gif.gif_height, self.gif_height)
self.assertEqual(gif.gif_duration, self.gif_duration)
self.assertEqual(gif.thumb_url, self.thumb_url)
self.assertEqual(gif.title, self.title)
self.assertEqual(gif.caption, self.caption)

View file

@ -37,6 +37,7 @@ class InlineQueryResultMpeg4GifTest(BaseTest, unittest.TestCase):
self.mpeg4_url = 'mpeg4 url'
self.mpeg4_width = 10
self.mpeg4_height = 15
self.mpeg4_duration = 1
self.thumb_url = 'thumb url'
self.title = 'title'
self.caption = 'caption'
@ -50,6 +51,7 @@ class InlineQueryResultMpeg4GifTest(BaseTest, unittest.TestCase):
'mpeg4_url': self.mpeg4_url,
'mpeg4_width': self.mpeg4_width,
'mpeg4_height': self.mpeg4_height,
'mpeg4_duration': self.mpeg4_duration,
'thumb_url': self.thumb_url,
'title': self.title,
'caption': self.caption,
@ -65,6 +67,7 @@ class InlineQueryResultMpeg4GifTest(BaseTest, unittest.TestCase):
self.assertEqual(mpeg4.mpeg4_url, self.mpeg4_url)
self.assertEqual(mpeg4.mpeg4_width, self.mpeg4_width)
self.assertEqual(mpeg4.mpeg4_height, self.mpeg4_height)
self.assertEqual(mpeg4.mpeg4_duration, self.mpeg4_duration)
self.assertEqual(mpeg4.thumb_url, self.thumb_url)
self.assertEqual(mpeg4.title, self.title)
self.assertEqual(mpeg4.caption, self.caption)

View file

@ -98,16 +98,46 @@ class MessageTest(BaseTest, unittest.TestCase):
{entity: 'http://google.com',
entity_2: 'h'})
def test_text_html(self):
def test_text_html_simple(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>.'
text_html = self.test_message.text_html
self.assertEquals(test_html_string, text_html)
def test_text_markdown(self):
def test_text_markdown_simple(self):
test_md_string = 'Test for <*bold*, _ita\_lic_, `code`, [links](http://github.com/) and ```pre```.'
text_markdown = self.test_message.text_markdown
self.assertEquals(test_md_string, text_markdown)
def test_text_html_emoji(self):
text = (b'\\U0001f469\\u200d\\U0001f469\\u200d ABC').decode('unicode-escape')
expected = (b'\\U0001f469\\u200d\\U0001f469\\u200d <b>ABC</b>').decode('unicode-escape')
bold_entity = telegram.MessageEntity(type=telegram.MessageEntity.BOLD, offset=7, length=3)
message = telegram.Message(
message_id=1, from_user=None, date=None, chat=None, text=text, entities=[bold_entity])
self.assertEquals(expected, message.text_html)
def test_text_markdown_emoji(self):
text = (b'\\U0001f469\\u200d\\U0001f469\\u200d ABC').decode('unicode-escape')
expected = (b'\\U0001f469\\u200d\\U0001f469\\u200d *ABC*').decode('unicode-escape')
bold_entity = telegram.MessageEntity(type=telegram.MessageEntity.BOLD, offset=7, length=3)
message = telegram.Message(
message_id=1, from_user=None, date=None, chat=None, text=text, entities=[bold_entity])
self.assertEquals(expected, message.text_markdown)
def test_parse_entities_url_emoji(self):
url = b'http://github.com/?unicode=\\u2713\\U0001f469'.decode('unicode-escape')
text = 'some url'
link_entity = telegram.MessageEntity(type=telegram.MessageEntity.URL, offset=0, length=8, url=url)
message = telegram.Message(
message_id=1,
from_user=None,
date=None,
chat=None,
text=text,
entities=[link_entity])
self.assertDictEqual(message.parse_entities(), {link_entity: text})
self.assertEqual(next(iter(message.parse_entities())).url, url)
@flaky(3, 1)
def test_reply_text(self):
"""Test for Message.reply_text"""

View file

@ -108,7 +108,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
self.message_count += 1
def telegramHandlerEditedTest(self, bot, update):
self.received_message = update.edited_message.text
self.received_message = update.effective_message.text
self.message_count += 1
def telegramInlineHandlerTest(self, bot, update):
@ -188,22 +188,31 @@ class UpdaterTest(BaseTest, unittest.TestCase):
self._setup_updater('Test', edited=True)
d = self.updater.dispatcher
from telegram.ext import Filters
handler = MessageHandler(Filters.text, self.telegramHandlerEditedTest, allow_edited=True)
handler = MessageHandler(Filters.text, self.telegramHandlerEditedTest, edited_updates=True)
d.add_handler(handler)
self.updater.start_polling(0.01)
sleep(.1)
self.assertEqual(self.received_message, 'Test')
# Remove handler
d.remove_handler(handler)
handler = MessageHandler(Filters.text, self.telegramHandlerEditedTest, allow_edited=False)
d.add_handler(handler)
self.reset()
d.remove_handler(handler)
handler = MessageHandler(
Filters.text,
self.telegramHandlerEditedTest,
edited_updates=False,
message_updates=False)
d.add_handler(handler)
self.updater.bot.send_messages = 1
sleep(.1)
self.assertTrue(None is self.received_message)
handler = MessageHandler(Filters.text, self.telegramHandlerEditedTest, allow_edited=True)
d.add_handler(handler)
self.reset()
self.updater.bot.send_messages = 1
sleep(.1)
self.assertEqual(self.received_message, 'Test')
def test_addTelegramMessageHandlerMultipleMessages(self):
self._setup_updater('Multiple', 100)
self.updater.dispatcher.add_handler(MessageHandler(Filters.all, self.telegramHandlerTest))

View file

@ -37,6 +37,7 @@ class UserTest(BaseTest, unittest.TestCase):
self.first_name = "Leandro"
self.last_name = "S."
self.username = "leandrotoledo"
self.language_code = "pt-BR"
self.type = "private"
self.json_dict = {
@ -44,6 +45,7 @@ class UserTest(BaseTest, unittest.TestCase):
'first_name': self.first_name,
'last_name': self.last_name,
'username': self.username,
'language_code': self.language_code,
'type': self.type
}
@ -54,6 +56,7 @@ class UserTest(BaseTest, unittest.TestCase):
self.assertEqual(user.first_name, self.first_name)
self.assertEqual(user.last_name, self.last_name)
self.assertEqual(user.username, self.username)
self.assertEqual(user.language_code, self.language_code)
self.assertEqual(user.type, self.type)
self.assertEqual(user.name, '@leandrotoledo')
@ -98,6 +101,7 @@ class UserTest(BaseTest, unittest.TestCase):
self.assertEqual(user['first_name'], self.first_name)
self.assertEqual(user['last_name'], self.last_name)
self.assertEqual(user['username'], self.username)
self.assertEqual(user['language_code'], self.language_code)
self.assertEqual(user['type'], self.type)
@flaky(3, 1)

212
tests/test_videonote.py Normal file
View file

@ -0,0 +1,212 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# 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 General 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents Tests for Telegram VideoNote"""
import sys
import unittest
import os
from flaky import flaky
sys.path.append('.')
import telegram
from tests.base import BaseTest, timeout
class VideoNoteTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram VideoNote."""
def setUp(self):
self.videonote_file = open('tests/data/telegram.mp4', 'rb')
self.videonote_file_id = 'DQADAQADBwAD5VIIRYemhHpbPmIQAg'
self.duration = 5
self.length = 1 # No bloody clue what this does, see note in first test
self.thumb = telegram.PhotoSize.de_json({
'file_id': 'AAQBABOMsecvAAQqqoY1Pee_MqcyAAIC',
'width': 51,
'file_size': 645,
'height': 90
}, self._bot)
self.file_size = 326534
self.json_dict = {
'file_id': self.videonote_file_id,
'duration': self.duration,
'length': self.length,
'thumb': self.thumb.to_dict(),
'file_size': self.file_size
}
@flaky(3, 1)
@timeout(10)
def test_error_send_videonote_required_args_only(self):
# This is where it gets weird....
# According to telegram length is Video width and height.. but how that works with one
# parameter I couldn't tell you
# It would also seem that it is in fact a required parameter, so the original test below
# fails. Therefore I decided I check for the error instead - that way we'll also know
# when telegram fixes their shit
with self.assertRaisesRegexp(telegram.error.BadRequest, r'Wrong video note length'):
message = self._bot.sendVideoNote(self._chat_id, self.videonote_file, timeout=10)
# videonote = message.videonote
#
# self.assertTrue(isinstance(videonote.file_id, str))
# self.assertNotEqual(videonote.file_id, None)
# self.assertEqual(videonote.duration, self.duration)
# self.assertEqual(videonote.length, self.length)
# self.assertEqual(videonote.thumb, self.thumb)
# self.assertEqual(videonote.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_videonote_actual_required_args_only(self):
# See above test... if you pass any number that's > 0 and < some high number, it seems
# to work
message = self._bot.sendVideoNote(
self._chat_id, self.videonote_file, length=self.length, timeout=10)
videonote = message.video_note
self.assertTrue(isinstance(videonote.file_id, str))
self.assertNotEqual(videonote.file_id, None)
self.assertEqual(videonote.duration, self.duration)
# self.assertEqual(videonote.length, self.length)
self.assertEqual(videonote.thumb, self.thumb)
self.assertEqual(videonote.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_videonote_all_args(self):
message = self._bot.sendVideoNote(
self._chat_id,
self.videonote_file,
timeout=10,
duration=self.duration,
length=self.length)
videonote = message.video_note
self.assertTrue(isinstance(videonote.file_id, str))
self.assertNotEqual(videonote.file_id, None)
# self.assertEqual(videonote.length, self.length)
self.assertEqual(videonote.duration, self.duration)
self.assertEqual(videonote.thumb, self.thumb)
self.assertEqual(videonote.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_videonote_resend(self):
message = self._bot.sendVideoNote(
chat_id=self._chat_id,
video_note=self.videonote_file_id,
timeout=10,
duration=self.duration,
length=self.length)
videonote = message.video_note
self.assertEqual(videonote.file_id, self.videonote_file_id)
# self.assertEqual(videonote.length, self.length)
self.assertEqual(videonote.duration, self.duration)
self.assertEqual(videonote.thumb, self.thumb)
self.assertEqual(videonote.file_size, self.file_size)
def test_videonote_de_json(self):
videonote = telegram.VideoNote.de_json(self.json_dict, self._bot)
self.assertEqual(videonote.file_id, self.videonote_file_id)
# self.assertEqual(videonote.duration, self.duration)
self.assertEqual(videonote.thumb, self.thumb)
self.assertEqual(videonote.length, self.length)
self.assertEqual(videonote.file_size, self.file_size)
def test_videonote_to_json(self):
videonote = telegram.VideoNote.de_json(self.json_dict, self._bot)
self.assertTrue(self.is_json(videonote.to_json()))
def test_videonote_to_dict(self):
videonote = telegram.VideoNote.de_json(self.json_dict, self._bot)
self.assertTrue(self.is_dict(videonote.to_dict()))
self.assertEqual(videonote['file_id'], self.videonote_file_id)
self.assertEqual(videonote['duration'], self.duration)
self.assertEqual(videonote['length'], self.length)
self.assertEqual(videonote['file_size'], self.file_size)
@flaky(3, 1)
@timeout(10)
def test_error_send_videonote_empty_file(self):
json_dict = self.json_dict
del (json_dict['file_id'])
json_dict['video_note'] = open(os.devnull, 'rb')
self.assertRaises(telegram.TelegramError,
lambda: self._bot.sendVideoNote(chat_id=self._chat_id,
timeout=10,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_error_send_videonote_empty_file_id(self):
json_dict = self.json_dict
del (json_dict['file_id'])
json_dict['video_note'] = ''
self.assertRaises(telegram.TelegramError,
lambda: self._bot.sendVideoNote(chat_id=self._chat_id,
timeout=10,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_reply_videonote(self):
"""Test for Message.reply_videonote"""
message = self._bot.sendMessage(self._chat_id, '.')
# Length is needed... see first test
message = message.reply_video_note(self.videonote_file, length=self.length)
self.assertNotEqual(message.video_note.file_id, None)
def test_equality(self):
a = telegram.VideoNote(self.videonote_file_id, self.length, self.duration)
b = telegram.VideoNote(self.videonote_file_id, self.length, self.duration)
c = telegram.VideoNote(self.videonote_file_id, 0, 0, 0)
d = telegram.VideoNote("", self.length, self.duration)
e = telegram.Voice(self.videonote_file_id, self.duration)
self.assertEqual(a, b)
self.assertEqual(hash(a), hash(b))
self.assertIsNot(a, b)
self.assertEqual(a, c)
self.assertEqual(hash(a), hash(c))
self.assertNotEqual(a, d)
self.assertNotEqual(hash(a), hash(d))
self.assertNotEqual(a, e)
self.assertNotEqual(hash(a), hash(e))
if __name__ == '__main__':
unittest.main()