mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-12-22 14:35:00 +01:00
Merge remote-tracking branch 'origin/master' into payment
This commit is contained in:
commit
137ffe2e73
36 changed files with 737 additions and 105 deletions
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
35
CHANGES.rst
35
CHANGES.rst
|
@ -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**
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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, '
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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!')
|
||||
|
||||
|
|
5
setup.py
5
setup.py
|
@ -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')
|
||||
|
|
|
@ -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'
|
||||
]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
0
telegram/vendor/__init__.py
vendored
Normal file
1
telegram/vendor/ptb_urllib3
vendored
Submodule
1
telegram/vendor/ptb_urllib3
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit ddb495340152a4e51800226dc68a479e48c055ff
|
1
telegram/vendor/urllib3
vendored
1
telegram/vendor/urllib3
vendored
|
@ -1 +0,0 @@
|
|||
Subproject commit 4b076eedffc1afabf0215ced3820603de73d1ce7
|
|
@ -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
63
telegram/videonote.py
Normal 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)
|
|
@ -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'
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 <<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"""
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
212
tests/test_videonote.py
Normal 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()
|
Loading…
Reference in a new issue