Merge pull request #308 from python-telegram-bot/bot2.1

New methods for Bot 2.1 API
This commit is contained in:
Leandro Toledo 2016-05-28 12:14:01 -03:00
commit 792ad62fe8
26 changed files with 516 additions and 126 deletions

View file

@ -23,6 +23,7 @@ from sys import version_info
from .base import TelegramObject
from .user import User
from .chat import Chat
from .chatmember import ChatMember
from .photosize import PhotoSize
from .audio import Audio
from .voice import Voice
@ -82,24 +83,25 @@ from .bot import Bot
__author__ = 'devs@python-telegram-bot.org'
__version__ = '4.1.2'
__all__ = ['Audio', 'Bot', 'Chat', 'ChatAction', 'ChosenInlineResult', 'CallbackQuery', 'Contact',
'Document', 'Emoji', 'File', 'ForceReply', 'InlineKeyboardButton',
'InlineKeyboardMarkup', 'InlineQuery', 'InlineQueryResult', 'InlineQueryResult',
'InlineQueryResultArticle', 'InlineQueryResultAudio', 'InlineQueryResultCachedAudio',
'InlineQueryResultCachedDocument', 'InlineQueryResultCachedGif',
'InlineQueryResultCachedMpeg4Gif', 'InlineQueryResultCachedPhoto',
'InlineQueryResultCachedSticker', 'InlineQueryResultCachedVideo',
'InlineQueryResultCachedVoice', 'InlineQueryResultContact', 'InlineQueryResultDocument',
'InlineQueryResultGif', 'InlineQueryResultLocation', 'InlineQueryResultMpeg4Gif',
'InlineQueryResultPhoto', 'InlineQueryResultVenue', 'InlineQueryResultVideo',
'InlineQueryResultVoice', 'InputContactMessageContent', 'InputFile',
'InputLocationMessageContent', 'InputMessageContent', 'InputTextMessageContent',
'InputVenueMessageContent', 'KeyboardButton', 'Location', 'Message', 'MessageEntity',
'NullHandler', 'ParseMode', 'PhotoSize', 'ReplyKeyboardHide', 'ReplyKeyboardMarkup',
'ReplyMarkup', 'Sticker', 'TelegramError', 'TelegramObject', 'Update', 'User',
'UserProfilePhotos', 'Venue', 'Video', 'Voice']
__all__ = ['Audio', 'Bot', 'Chat', 'ChatMember', 'ChatAction', 'ChosenInlineResult',
'CallbackQuery', 'Contact', 'Document', 'Emoji', 'File', 'ForceReply',
'InlineKeyboardButton', 'InlineKeyboardMarkup', 'InlineQuery', 'InlineQueryResult',
'InlineQueryResult', 'InlineQueryResultArticle', 'InlineQueryResultAudio',
'InlineQueryResultCachedAudio', 'InlineQueryResultCachedDocument',
'InlineQueryResultCachedGif', 'InlineQueryResultCachedMpeg4Gif',
'InlineQueryResultCachedPhoto', 'InlineQueryResultCachedSticker',
'InlineQueryResultCachedVideo', 'InlineQueryResultCachedVoice',
'InlineQueryResultContact', 'InlineQueryResultDocument', 'InlineQueryResultGif',
'InlineQueryResultLocation', 'InlineQueryResultMpeg4Gif', 'InlineQueryResultPhoto',
'InlineQueryResultVenue', 'InlineQueryResultVideo', 'InlineQueryResultVoice',
'InputContactMessageContent', 'InputFile', 'InputLocationMessageContent',
'InputMessageContent', 'InputTextMessageContent', 'InputVenueMessageContent',
'KeyboardButton', 'Location', 'Message', 'MessageEntity', 'NullHandler', 'ParseMode',
'PhotoSize', 'ReplyKeyboardHide', 'ReplyKeyboardMarkup', 'ReplyMarkup', 'Sticker',
'TelegramError', 'TelegramObject', 'Update', 'User', 'UserProfilePhotos', 'Venue',
'Video', 'Voice']
if version_info < (2, 7):
from warnings import warn
warn("python-telegram-bot will stop supporting Python 2.6 in a future release. "
"Please upgrade your Python!")
"Please upgrade your Python version to at least Python 2.7!")

View file

@ -19,11 +19,11 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Bot."""
import logging
import functools
import logging
from telegram import (User, Message, Update, UserProfilePhotos, File, ReplyMarkup, TelegramObject,
NullHandler)
from telegram import (User, Message, Update, Chat, ChatMember, UserProfilePhotos, File,
ReplyMarkup, TelegramObject, NullHandler)
from telegram.error import InvalidToken
from telegram.utils import request
@ -154,7 +154,7 @@ class Bot(TelegramObject):
return decorator
@log
def getMe(self):
def getMe(self, **kwargs):
"""A simple method for testing your bot's auth token.
Returns:
@ -1182,7 +1182,7 @@ class Bot(TelegramObject):
return url, data
@log
def getUpdates(self, offset=None, limit=100, timeout=0, network_delay=.2):
def getUpdates(self, offset=None, limit=100, timeout=0, network_delay=.2, **kwargs):
"""Use this method to receive incoming updates using long polling.
Args:
@ -1271,6 +1271,166 @@ class Bot(TelegramObject):
return result
@log
def leaveChat(self, chat_id, **kwargs):
"""Use this method for your bot to leave a group, supergroup or
channel.
Args:
chat_id:
Unique identifier for the target chat or username of the target
channel (in the format @channelusername).
Keyword Args:
timeout (Optional[float]): If this value is specified, use it as
the definitive timeout (in seconds) for urlopen() operations.
Returns:
bool: On success, `True` is returned.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/leaveChat'.format(self.base_url)
data = {'chat_id': chat_id}
result = request.post(url, data, timeout=kwargs.get('timeout'))
return result
@log
def getChat(self, chat_id, **kwargs):
"""Use this method to get up to date information about the chat
(current name of the user for one-on-one conversations, current
username of a user, group or channel, etc.).
Args:
chat_id:
Unique identifier for the target chat or username of the target
channel (in the format @channelusername).
Keyword Args:
timeout (Optional[float]): If this value is specified, use it as
the definitive timeout (in seconds) for urlopen() operations.
Returns:
:class:`telegram.Chat`: On success, :class:`telegram.Chat` is
returned.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/getChat'.format(self.base_url)
data = {'chat_id': chat_id}
result = request.post(url, data, timeout=kwargs.get('timeout'))
return Chat.de_json(result)
@log
def getChatAdministrators(self, chat_id, **kwargs):
"""Use this method to get a list of administrators in a chat. On
success, returns an Array of ChatMember objects that contains
information about all chat administrators except other bots. If the
chat is a group or a supergroup and no administrators were appointed,
only the creator will be returned.
Args:
chat_id:
Unique identifier for the target chat or username of the target
channel (in the format @channelusername).
Keyword Args:
timeout (Optional[float]): If this value is specified, use it as
the definitive timeout (in seconds) for urlopen() operations.
Returns:
list[:class:`telegram.ChatMember`]: On success, a list of
:class:`telegram.ChatMember` objects are returned.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/getChatAdministrators'.format(self.base_url)
data = {'chat_id': chat_id}
result = request.post(url, data, timeout=kwargs.get('timeout'))
return [ChatMember.de_json(x) for x in result]
@log
def getChatMembersCount(self, chat_id, **kwargs):
"""Use this method to get the number of members in a chat.
Args:
chat_id:
Unique identifier for the target chat or username of the target
channel (in the format @channelusername).
Keyword Args:
timeout (Optional[float]): If this value is specified, use it as
the definitive timeout (in seconds) for urlopen() operations.
Returns:
int: On success, an `int` is returned.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/getChatMembersCount'.format(self.base_url)
data = {'chat_id': chat_id}
result = request.post(url, data, timeout=kwargs.get('timeout'))
return result
@log
def getChatMember(self, chat_id, user_id, **kwargs):
"""Use this method to get information about a member of a chat.
Args:
chat_id:
Unique identifier for the target chat or username of the target
channel (in the format @channelusername).
user_id:
Unique identifier of the target user.
Keyword Args:
timeout (Optional[float]): If this value is specified, use it as
the definitive timeout (in seconds) for urlopen() operations.
Returns:
:class:`telegram.ChatMember`: On success,
:class:`telegram.ChatMember` is returned.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/getChatMember'.format(self.base_url)
data = {'chat_id': chat_id, 'user_id': user_id}
result = request.post(url, data, timeout=kwargs.get('timeout'))
return ChatMember.de_json(result)
@staticmethod
def de_json(data):
data = super(Bot, Bot).de_json(data)
@ -1314,3 +1474,8 @@ class Bot(TelegramObject):
edit_message_reply_markup = editMessageReplyMarkup
get_updates = getUpdates
set_webhook = setWebhook
leave_chat = leaveChat
get_chat = getChat
get_chat_administrators = getChatAdministrators
get_chat_member = getChatMember
get_chat_members_count = getChatMembersCount

View file

@ -42,6 +42,11 @@ class Chat(TelegramObject):
type (Optional[str]):
"""
PRIVATE = 'private'
GROUP = 'group'
SUPERGROUP = 'supergroup'
CHANNEL = 'channel'
def __init__(self, id, type, **kwargs):
# Required
self.id = int(id)

62
telegram/chatmember.py Normal file
View file

@ -0,0 +1,62 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# 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 a object that represents a Telegram ChatMember."""
from telegram import User, TelegramObject
class ChatMember(TelegramObject):
"""This object represents a Telegram ChatMember.
Attributes:
user (:class:`telegram.User`): Information about the user.
status (str): The member's status in the chat. Can be 'creator', 'administrator', 'member',
'left' or 'kicked'.
Args:
user (:class:`telegram.User`):
status (str):
"""
CREATOR = 'creator'
ADMINISTRATOR = 'administrator'
MEMBER = 'member'
LEFT = 'left'
KICKED = 'kicked'
def __init__(self, user, status, **kwargs):
# Required
self.user = user
self.status = status
@staticmethod
def de_json(data):
"""
Args:
data (dict):
Returns:
telegram.ChatMember:
"""
if not data:
return None
data['user'] = User.de_json(data.get('user'))
return ChatMember(**data)

View file

@ -41,7 +41,13 @@ class ChosenInlineResult(TelegramObject):
"""
def __init__(self, result_id, from_user, query, location=None, inline_message_id=None):
def __init__(self,
result_id,
from_user,
query,
location=None,
inline_message_id=None,
**kwargs):
# Required
self.result_id = result_id
self.from_user = from_user

View file

@ -51,6 +51,7 @@ class TelegramError(Exception):
msg = _lstrip_str(message, 'Error: ')
msg = _lstrip_str(msg, '[Error]: ')
msg = _lstrip_str(msg, 'Bad Request: ')
if msg != message:
# api_error - capitalize the msg...
msg = msg.capitalize()
@ -76,6 +77,10 @@ class NetworkError(TelegramError):
pass
class BadRequest(NetworkError):
pass
class TimedOut(NetworkError):
def __init__(self):

View file

@ -34,6 +34,8 @@ class CommandHandler(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_args (optional[bool]): If the handler should be passed the
arguments passed to the command as a keyword argument called `
``args``. It will contain a list of strings, which is the text
@ -43,21 +45,35 @@ class CommandHandler(Handler):
be used to insert updates. Default is ``False``
"""
def __init__(self, command, callback, pass_args=False, pass_update_queue=False):
def __init__(self,
command,
callback,
allow_edited=False,
pass_args=False,
pass_update_queue=False):
super(CommandHandler, self).__init__(callback, pass_update_queue)
self.command = command
self.allow_edited = allow_edited
self.pass_args = pass_args
def check_update(self, update):
return (isinstance(update, Update) and update.message and update.message.text
and update.message.text.startswith('/')
and update.message.text[1:].split(' ')[0].split('@')[0] == self.command)
if (isinstance(update, Update)
and (update.message or update.edited_message and self.allow_edited)):
message = update.message or update.edited_message
return (message.text and message.text.startswith('/')
and message.text[1:].split(' ')[0].split('@')[0] == self.command)
else:
return False
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
message = update.message or update.edited_message
if self.pass_args:
optional_args['args'] = update.message.text.split(' ')[1:]
optional_args['args'] = message.text.split(' ')[1:]
self.callback(dispatcher.bot, update, **optional_args)

View file

@ -30,57 +30,56 @@ class Filters(object):
"""
@staticmethod
def text(update):
return update.message.text and not update.message.text.startswith('/')
def text(message):
return message.text and not message.text.startswith('/')
@staticmethod
def command(update):
return update.message.text and update.message.text.startswith('/')
def command(message):
return message.text and message.text.startswith('/')
@staticmethod
def audio(update):
return bool(update.message.audio)
def audio(message):
return bool(message.audio)
@staticmethod
def document(update):
return bool(update.message.document)
def document(message):
return bool(message.document)
@staticmethod
def photo(update):
return bool(update.message.photo)
def photo(message):
return bool(message.photo)
@staticmethod
def sticker(update):
return bool(update.message.sticker)
def sticker(message):
return bool(message.sticker)
@staticmethod
def video(update):
return bool(update.message.video)
def video(message):
return bool(message.video)
@staticmethod
def voice(update):
return bool(update.message.voice)
def voice(message):
return bool(message.voice)
@staticmethod
def contact(update):
return bool(update.message.contact)
def contact(message):
return bool(message.contact)
@staticmethod
def location(update):
return bool(update.message.location)
def location(message):
return bool(message.location)
@staticmethod
def venue(update):
return bool(update.message.venue)
def venue(message):
return bool(message.venue)
@staticmethod
def status_update(update):
return bool(update.message.new_chat_member or update.message.left_chat_member
or update.message.new_chat_title or update.message.new_chat_photo
or update.message.delete_chat_photo or update.message.group_chat_created
or update.message.supergroup_chat_created
or update.message.channel_chat_created or update.message.migrate_to_chat_id
or update.message.migrate_from_chat_id or update.message.pinned_message)
def status_update(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)
class MessageHandler(Handler):
@ -99,23 +98,32 @@ 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``
"""
def __init__(self, filters, callback, pass_update_queue=False):
def __init__(self, filters, callback, allow_edited=False, pass_update_queue=False):
super(MessageHandler, self).__init__(callback, pass_update_queue)
self.filters = filters
self.allow_edited = allow_edited
def check_update(self, update):
if isinstance(update, Update) and update.message:
if (isinstance(update, Update)
and (update.message or update.edited_message and self.allow_edited)):
if not self.filters:
res = True
else:
res = any(func(update) for func in self.filters)
message = update.message or update.edited_message
res = any(func(message) for func in self.filters)
else:
res = False
return res
def handle_update(self, update, dispatcher):

View file

@ -33,7 +33,7 @@ class InlineKeyboardMarkup(ReplyMarkup):
"""
def __init__(self, inline_keyboard):
def __init__(self, inline_keyboard, **kwargs):
# Required
self.inline_keyboard = inline_keyboard

View file

@ -25,7 +25,7 @@ from telegram import InputMessageContent
class InputContactMessageContent(InputMessageContent):
"""Base class for Telegram InputContactMessageContent Objects"""
def __init__(self, phone_number, first_name, last_name=None):
def __init__(self, phone_number, first_name, last_name=None, **kwargs):
# Required
self.phone_number = phone_number
self.first_name = first_name

View file

@ -25,7 +25,7 @@ from telegram import InputMessageContent
class InputLocationMessageContent(InputMessageContent):
"""Base class for Telegram InputLocationMessageContent Objects"""
def __init__(self, latitude, longitude):
def __init__(self, latitude, longitude, **kwargs):
# Required
self.latitude = latitude
self.longitude = longitude

View file

@ -39,14 +39,14 @@ class InputMessageContent(TelegramObject):
pass
try:
from telegram import InputLocationMessageContent
return InputLocationMessageContent.de_json(data)
from telegram import InputVenueMessageContent
return InputVenueMessageContent.de_json(data)
except TypeError:
pass
try:
from telegram import InputVenueMessageContent
return InputVenueMessageContent.de_json(data)
from telegram import InputLocationMessageContent
return InputLocationMessageContent.de_json(data)
except TypeError:
pass

View file

@ -25,7 +25,7 @@ from telegram import InputMessageContent
class InputTextMessageContent(InputMessageContent):
"""Base class for Telegram InputTextMessageContent Objects"""
def __init__(self, message_text, parse_mode=None, disable_web_page_preview=None):
def __init__(self, message_text, parse_mode=None, disable_web_page_preview=None, **kwargs):
# Required
self.message_text = message_text
# Optionals

View file

@ -25,7 +25,7 @@ from telegram import InputMessageContent
class InputVenueMessageContent(InputMessageContent):
"""Base class for Telegram InputVenueMessageContent Objects"""
def __init__(self, latitude, longitude, title, address, foursquare_id=None):
def __init__(self, latitude, longitude, title, address, foursquare_id=None, **kwargs):
# Required
self.latitude = latitude
self.longitude = longitude

View file

@ -33,7 +33,7 @@ class KeyboardButton(TelegramObject):
request_contact (Optional[bool]):
"""
def __init__(self, text, request_contact=None, request_location=None):
def __init__(self, text, request_contact=None, request_location=None, **kwargs):
# Required
self.text = text
# Optionals

View file

@ -33,7 +33,7 @@ class Location(TelegramObject):
latitude (float):
"""
def __init__(self, longitude, latitude):
def __init__(self, longitude, latitude, **kwargs):
# Required
self.longitude = float(longitude)
self.latitude = float(latitude)

View file

@ -40,6 +40,7 @@ class Message(TelegramObject):
forward_from_chat (:class:`telegram.Chat`):
forward_date (:class:`datetime.datetime`):
reply_to_message (:class:`telegram.Message`):
edit_date (:class:`datetime.datetime`):
text (str):
audio (:class:`telegram.Audio`):
document (:class:`telegram.Document`):
@ -80,6 +81,7 @@ class Message(TelegramObject):
forward_from_chat (:class:`telegram.Chat`):
forward_date (Optional[:class:`datetime.datetime`]):
reply_to_message (Optional[:class:`telegram.Message`]):
edit_date (Optional[:class:`datetime.datetime`]):
text (Optional[str]):
audio (Optional[:class:`telegram.Audio`]):
document (Optional[:class:`telegram.Document`]):
@ -113,6 +115,7 @@ class Message(TelegramObject):
self.forward_from_chat = kwargs.get('forward_from_chat')
self.forward_date = kwargs.get('forward_date')
self.reply_to_message = kwargs.get('reply_to_message')
self.edit_date = kwargs.get('edit_date')
self.text = kwargs.get('text', '')
self.entities = kwargs.get('entities', list())
self.audio = kwargs.get('audio')
@ -162,6 +165,7 @@ class Message(TelegramObject):
data['forward_from_chat'] = Chat.de_json(data.get('forward_from_chat'))
data['forward_date'] = Message._fromtimestamp(data.get('forward_date'))
data['reply_to_message'] = Message.de_json(data.get('reply_to_message'))
data['edit_date'] = Message._fromtimestamp(data.get('edit_date'))
data['audio'] = Audio.de_json(data.get('audio'))
data['document'] = Document.de_json(data.get('document'))
data['photo'] = PhotoSize.de_list(data.get('photo'))
@ -197,6 +201,8 @@ class Message(TelegramObject):
# Optionals
if self.forward_date:
data['forward_date'] = self._totimestamp(self.forward_date)
if self.edit_date:
data['edit_date'] = self._totimestamp(self.edit_date)
if self.photo:
data['photo'] = [p.to_dict() for p in self.photo]
if self.entities:

View file

@ -18,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram MessageEntity."""
from telegram import TelegramObject
from telegram import User, TelegramObject
class MessageEntity(TelegramObject):
@ -31,6 +31,7 @@ class MessageEntity(TelegramObject):
offset (int):
length (int):
url (Optional[str]):
user (Optional[:class:`telegram.User`]):
"""
def __init__(self, type, offset, length, **kwargs):
@ -40,11 +41,14 @@ class MessageEntity(TelegramObject):
self.length = length
# Optionals
self.url = kwargs.get('url')
self.user = kwargs.get('user')
@staticmethod
def de_json(data):
data = super(MessageEntity, MessageEntity).de_json(data)
data['user'] = User.de_json(data.get('user'))
return MessageEntity(**data)
@staticmethod

View file

@ -27,6 +27,7 @@ class Update(TelegramObject):
Attributes:
update_id (int):
message (:class:`telegram.Message`):
edited_message (:class:`telegram.Message`):
inline_query (:class:`telegram.InlineQuery`):
chosen_inline_result (:class:`telegram.ChosenInlineResult`):
callback_query (:class:`telegram.CallbackQuery`):
@ -37,6 +38,7 @@ class Update(TelegramObject):
Keyword Args:
message (Optional[:class:`telegram.Message`]):
edited_message (Optional[:class:`telegram.Message`]):
inline_query (Optional[:class:`telegram.InlineQuery`]):
chosen_inline_result (Optional[:class:`telegram.ChosenInlineResult`])
callback_query (Optional[:class:`telegram.CallbackQuery`]):
@ -47,6 +49,7 @@ class Update(TelegramObject):
self.update_id = int(update_id)
# Optionals
self.message = kwargs.get('message')
self.edited_message = kwargs.get('edited_message')
self.inline_query = kwargs.get('inline_query')
self.chosen_inline_result = kwargs.get('chosen_inline_result')
self.callback_query = kwargs.get('callback_query')
@ -64,6 +67,7 @@ class Update(TelegramObject):
return None
data['message'] = Message.de_json(data.get('message'))
data['edited_message'] = Message.de_json(data.get('edited_message'))
data['inline_query'] = InlineQuery.de_json(data.get('inline_query'))
data['chosen_inline_result'] = ChosenInlineResult.de_json(data.get('chosen_inline_result'))
data['callback_query'] = CallbackQuery.de_json(data.get('callback_query'))

View file

@ -34,7 +34,7 @@ class UserProfilePhotos(TelegramObject):
photos (List[List[:class:`telegram.PhotoSize`]]):
"""
def __init__(self, total_count, photos):
def __init__(self, total_count, photos, **kwargs):
# Required
self.total_count = int(total_count)
self.photos = photos

View file

@ -28,7 +28,7 @@ from future.moves.urllib.error import HTTPError, URLError
from future.moves.urllib.request import urlopen, urlretrieve, Request
from telegram import (InputFile, TelegramError)
from telegram.error import Unauthorized, NetworkError, TimedOut
from telegram.error import Unauthorized, NetworkError, TimedOut, BadRequest
def _parse(json_data):
@ -67,13 +67,15 @@ def _try_except_req(func):
# come first.
errcode = error.getcode()
if errcode in (401, 403):
raise Unauthorized()
elif errcode == 502:
raise NetworkError('Bad Gateway')
try:
message = _parse(error.read())
if errcode in (401, 403):
raise Unauthorized()
elif errcode == 400:
raise BadRequest(message)
elif errcode == 502:
raise NetworkError('Bad Gateway')
except ValueError:
message = 'Unknown HTTPError {0}'.format(error.getcode())

View file

@ -32,7 +32,7 @@ class Venue(TelegramObject):
foursquare_id (Optional[str]):
"""
def __init__(self, location, title, address, foursquare_id=None):
def __init__(self, location, title, address, foursquare_id=None, **kwargs):
# Required
self.location = location
self.title = title

View file

@ -40,6 +40,8 @@ class BaseTest(object):
'133505823:AAHZFMHno3mzVLErU5b5jJvaeG--qUyLyG0'))
chat_id = os.environ.get('CHAT_ID', '12173560')
self._group_id = os.environ.get('GROUP_ID', '-49740850')
self._channel_id = os.environ.get('CHANNEL_ID', '@pythontelegrambottests')
self._bot = bot
self._chat_id = chat_id

View file

@ -45,11 +45,7 @@ class BotTest(BaseTest, unittest.TestCase):
bot = self._bot.getMe()
self.assertTrue(self.is_json(bot.to_json()))
self.assertEqual(bot.id, 133505823)
self.assertEqual(bot.first_name, 'PythonTelegramBot')
self.assertEqual(bot.last_name, '')
self.assertEqual(bot.username, 'PythonTelegramBot')
self.assertEqual(bot.name, '@PythonTelegramBot')
self._testUserEqualsBot(bot)
@flaky(3, 1)
@timeout(10)
@ -75,7 +71,7 @@ class BotTest(BaseTest, unittest.TestCase):
@flaky(3, 1)
@timeout(10)
def testGetUpdates(self):
updates = self._bot.getUpdates()
updates = self._bot.getUpdates(timeout=1)
if updates:
self.assertTrue(self.is_json(updates[0].to_json()))
@ -212,6 +208,63 @@ class BotTest(BaseTest, unittest.TestCase):
bot.getMe()
@flaky(3, 1)
@timeout(10)
def testLeaveChat(self):
with self.assertRaisesRegexp(telegram.error.BadRequest, 'Chat not found'):
chat = self._bot.leaveChat(-123456)
with self.assertRaisesRegexp(telegram.error.NetworkError, 'Chat not found'):
chat = self._bot.leaveChat(-123456)
@flaky(3, 1)
@timeout(10)
def testGetChat(self):
chat = self._bot.getChat(self._group_id)
self.assertTrue(self.is_json(chat.to_json()))
self.assertEqual(chat.type, "group")
self.assertEqual(chat.title, ">>> telegram.Bot() - Developers")
self.assertEqual(chat.id, int(self._group_id))
@flaky(3, 1)
@timeout(10)
def testGetChatAdministrators(self):
admins = self._bot.getChatAdministrators(self._channel_id)
self.assertTrue(isinstance(admins, list))
self.assertTrue(self.is_json(admins[0].to_json()))
for a in admins:
self.assertTrue(a.status in ("administrator", "creator"))
bot = [a.user for a in admins if a.user.id == 133505823][0]
self._testUserEqualsBot(bot)
@flaky(3, 1)
@timeout(10)
def testGetChatMembersCount(self):
count = self._bot.getChatMembersCount(self._channel_id)
self.assertTrue(isinstance(count, int))
self.assertTrue(count > 3)
@flaky(3, 1)
@timeout(10)
def testGetChatMember(self):
chat_member = self._bot.getChatMember(self._channel_id, 133505823)
bot = chat_member.user
self.assertTrue(self.is_json(chat_member.to_json()))
self.assertEqual(chat_member.status, "administrator")
self._testUserEqualsBot(bot)
def _testUserEqualsBot(self, user):
"""Tests if user is our trusty @PythonTelegramBot."""
self.assertEqual(user.id, 133505823)
self.assertEqual(user.first_name, 'PythonTelegramBot')
self.assertEqual(user.last_name, '')
self.assertEqual(user.username, 'PythonTelegramBot')
self.assertEqual(user.name, '@PythonTelegramBot')
if __name__ == '__main__':
unittest.main()

View file

@ -26,7 +26,7 @@ from datetime import datetime
sys.path.append('.')
from telegram import Update, Message, User, Chat
from telegram import Message, User, Chat
from telegram.ext import Filters
from tests.base import BaseTest
@ -36,119 +36,118 @@ class FiltersTest(BaseTest, unittest.TestCase):
def setUp(self):
self.message = Message(0, User(0, "Testuser"), datetime.now(), Chat(0, 'private'))
self.update = Update(0, message=self.message)
def test_filters_text(self):
self.message.text = 'test'
self.assertTrue(Filters.text(self.update))
self.assertTrue(Filters.text(self.message))
self.message.text = '/test'
self.assertFalse(Filters.text(self.update))
self.assertFalse(Filters.text(self.message))
def test_filters_command(self):
self.message.text = 'test'
self.assertFalse(Filters.command(self.update))
self.assertFalse(Filters.command(self.message))
self.message.text = '/test'
self.assertTrue(Filters.command(self.update))
self.assertTrue(Filters.command(self.message))
def test_filters_audio(self):
self.message.audio = 'test'
self.assertTrue(Filters.audio(self.update))
self.assertTrue(Filters.audio(self.message))
self.message.audio = None
self.assertFalse(Filters.audio(self.update))
self.assertFalse(Filters.audio(self.message))
def test_filters_document(self):
self.message.document = 'test'
self.assertTrue(Filters.document(self.update))
self.assertTrue(Filters.document(self.message))
self.message.document = None
self.assertFalse(Filters.document(self.update))
self.assertFalse(Filters.document(self.message))
def test_filters_photo(self):
self.message.photo = 'test'
self.assertTrue(Filters.photo(self.update))
self.assertTrue(Filters.photo(self.message))
self.message.photo = None
self.assertFalse(Filters.photo(self.update))
self.assertFalse(Filters.photo(self.message))
def test_filters_sticker(self):
self.message.sticker = 'test'
self.assertTrue(Filters.sticker(self.update))
self.assertTrue(Filters.sticker(self.message))
self.message.sticker = None
self.assertFalse(Filters.sticker(self.update))
self.assertFalse(Filters.sticker(self.message))
def test_filters_video(self):
self.message.video = 'test'
self.assertTrue(Filters.video(self.update))
self.assertTrue(Filters.video(self.message))
self.message.video = None
self.assertFalse(Filters.video(self.update))
self.assertFalse(Filters.video(self.message))
def test_filters_voice(self):
self.message.voice = 'test'
self.assertTrue(Filters.voice(self.update))
self.assertTrue(Filters.voice(self.message))
self.message.voice = None
self.assertFalse(Filters.voice(self.update))
self.assertFalse(Filters.voice(self.message))
def test_filters_contact(self):
self.message.contact = 'test'
self.assertTrue(Filters.contact(self.update))
self.assertTrue(Filters.contact(self.message))
self.message.contact = None
self.assertFalse(Filters.contact(self.update))
self.assertFalse(Filters.contact(self.message))
def test_filters_location(self):
self.message.location = 'test'
self.assertTrue(Filters.location(self.update))
self.assertTrue(Filters.location(self.message))
self.message.location = None
self.assertFalse(Filters.location(self.update))
self.assertFalse(Filters.location(self.message))
def test_filters_venue(self):
self.message.venue = 'test'
self.assertTrue(Filters.venue(self.update))
self.assertTrue(Filters.venue(self.message))
self.message.venue = None
self.assertFalse(Filters.venue(self.update))
self.assertFalse(Filters.venue(self.message))
def test_filters_status_update(self):
self.assertFalse(Filters.status_update(self.update))
self.assertFalse(Filters.status_update(self.message))
self.message.new_chat_member = 'test'
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.new_chat_member = None
self.message.left_chat_member = 'test'
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.left_chat_member = None
self.message.new_chat_title = 'test'
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.new_chat_title = ''
self.message.new_chat_photo = 'test'
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.new_chat_photo = None
self.message.delete_chat_photo = True
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.delete_chat_photo = False
self.message.group_chat_created = True
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.group_chat_created = False
self.message.supergroup_chat_created = True
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.supergroup_chat_created = False
self.message.migrate_to_chat_id = 100
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.migrate_to_chat_id = 0
self.message.migrate_from_chat_id = 100
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.migrate_from_chat_id = 0
self.message.channel_chat_created = True
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.channel_chat_created = False
self.message.pinned_message = 'test'
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.pinned_message = None

View file

@ -93,6 +93,10 @@ class UpdaterTest(BaseTest, unittest.TestCase):
self.received_message = update.message.text
self.message_count += 1
def telegramHandlerEditedTest(self, bot, update):
self.received_message = update.edited_message.text
self.message_count += 1
def telegramInlineHandlerTest(self, bot, update):
self.received_message = (update.inline_query, update.chosen_inline_result)
self.message_count += 1
@ -157,6 +161,28 @@ class UpdaterTest(BaseTest, unittest.TestCase):
sleep(.1)
self.assertTrue(None is self.received_message)
def test_editedMessageHandler(self):
self._setup_updater('Test', edited=True)
d = self.updater.dispatcher
from telegram.ext import Filters
handler = MessageHandler([Filters.text], self.telegramHandlerEditedTest, allow_edited=True)
d.addHandler(handler)
self.updater.start_polling(0.01)
sleep(.1)
self.assertEqual(self.received_message, 'Test')
# Remove handler
d.removeHandler(handler)
handler = MessageHandler([Filters.text],
self.telegramHandlerEditedTest,
allow_edited=False)
d.addHandler(handler)
self.reset()
self.updater.bot.send_messages = 1
sleep(.1)
self.assertTrue(None is self.received_message)
def test_addTelegramMessageHandlerMultipleMessages(self):
self._setup_updater('Multiple', 100)
self.updater.dispatcher.addHandler(MessageHandler([], self.telegramHandlerTest))
@ -200,6 +226,25 @@ class UpdaterTest(BaseTest, unittest.TestCase):
sleep(.1)
self.assertTrue(None is self.received_message)
def test_editedCommandHandler(self):
self._setup_updater('/test', edited=True)
d = self.updater.dispatcher
handler = CommandHandler('test', self.telegramHandlerEditedTest, allow_edited=True)
d.addHandler(handler)
self.updater.start_polling(0.01)
sleep(.1)
self.assertEqual(self.received_message, '/test')
# Remove handler
d.removeHandler(handler)
handler = CommandHandler('test', self.telegramHandlerEditedTest, allow_edited=False)
d.addHandler(handler)
self.reset()
self.updater.bot.send_messages = 1
sleep(.1)
self.assertTrue(None is self.received_message)
def test_addRemoveStringRegexHandler(self):
self._setup_updater('', messages=0)
d = self.updater.dispatcher
@ -612,7 +657,8 @@ class MockBot(object):
messages=1,
raise_error=False,
bootstrap_retries=None,
bootstrap_err=TelegramError('test')):
bootstrap_err=TelegramError('test'),
edited=False):
self.text = text
self.send_messages = messages
self.raise_error = raise_error
@ -620,13 +666,18 @@ class MockBot(object):
self.bootstrap_retries = bootstrap_retries
self.bootstrap_attempts = 0
self.bootstrap_err = bootstrap_err
self.edited = edited
@staticmethod
def mockUpdate(text):
def mockUpdate(self, text):
message = Message(0, None, None, None)
message.text = text
update = Update(0)
if self.edited:
update.edited_message = message
else:
update.message = message
return update
def setWebhook(self, webhook_url=None, certificate=None):