diff --git a/.travis.yml b/.travis.yml index a7627958f..e3323617e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ install: - pip install -r requirements.txt - pip install -r requirements-dev.txt script: - - nosetests -v --with-coverage --cover-package telegram/ + - nosetests -v --with-flaky --no-flaky-report --with-coverage --cover-package=telegram/ - flake8 telegram - 'if [[ $TRAVIS_PYTHON_VERSION != 2.6 ]]; then pylint -E telegram --disable=no-name-in-module,import-error; fi' after_success: diff --git a/CHANGES.rst b/CHANGES.rst index 07db7988d..7b4c47e69 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +**2016-01-09** + +*Released 3.3b1* + +- Implement inline bots (beta) + **2016-01-05** *Released 3.2.0* diff --git a/README.rst b/README.rst index 2f9d9f162..f75e216ac 100644 --- a/README.rst +++ b/README.rst @@ -227,6 +227,20 @@ Let's add some functionality to our bot. We want to add the ``/caps`` command, t ... >>> dispatcher.addTelegramCommandHandler('caps', caps) +To enable our bot to respond to inline queries, we can add the following (you will also have to talk to BotFather):: + + >>> from telegram import InlineQueryResultArticle + >>> def inline_caps(bot, update): + ... # If you activated inline feedback, updates might either contain + ... # inline_query or chosen_inline_result, the other one will be None + ... if update.inline_query: + ... query = bot.update.inline_query.query + ... results = list() + ... results.append(InlineQueryResultArticle(query.upper(), 'Caps', text_caps)) + ... bot.answerInlineQuery(update.inline_query.id, results) + ... + >>> dispatcher.addTelegramInlineHandler(inline_caps) + Now it's time to stop the bot:: >>> updater.stop() diff --git a/docs/source/conf.py b/docs/source/conf.py index 067dd71bd..8b2feea43 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -58,9 +58,9 @@ author = u'Leandro Toledo' # built documents. # # The short X.Y version. -version = '3.2' +version = '3.3' # The full version, including alpha/beta/rc tags. -release = '3.2.0' +release = '3.3b1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/source/telegram.choseninlineresult.rst b/docs/source/telegram.choseninlineresult.rst new file mode 100644 index 000000000..45ea766c9 --- /dev/null +++ b/docs/source/telegram.choseninlineresult.rst @@ -0,0 +1,7 @@ +telegram.choseninlineresult module +================================== + +.. automodule:: telegram.choseninlineresult + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequery.rst b/docs/source/telegram.inlinequery.rst new file mode 100644 index 000000000..fd7c90efc --- /dev/null +++ b/docs/source/telegram.inlinequery.rst @@ -0,0 +1,7 @@ +telegram.inlinequery module +=========================== + +.. automodule:: telegram.inlinequery + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresult.rst b/docs/source/telegram.inlinequeryresult.rst new file mode 100644 index 000000000..1daeba325 --- /dev/null +++ b/docs/source/telegram.inlinequeryresult.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresult module +================================= + +.. automodule:: telegram.inlinequeryresult + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.rst b/docs/source/telegram.rst index e08c0dee9..793ad6d2b 100644 --- a/docs/source/telegram.rst +++ b/docs/source/telegram.rst @@ -12,6 +12,9 @@ Submodules telegram.updater telegram.dispatcher telegram.jobqueue + telegram.inlinequery + telegram.inlinequeryresult + telegram.choseninlineresult telegram.chataction telegram.contact telegram.document diff --git a/examples/inlinebot.py b/examples/inlinebot.py new file mode 100644 index 000000000..f7bce001a --- /dev/null +++ b/examples/inlinebot.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Simple Bot to reply to Telegram messages +# This program is dedicated to the public domain under the CC0 license. + +""" +This Bot uses the Updater class to handle the bot. + +First, a few handler functions are defined. Then, those functions are passed to +the Dispatcher and registered at their respective places. +Then, the bot is started and runs until we press Ctrl-C on the command line. + +Usage: +Basic inline bot example. Applies different text transformations. +Press Ctrl-C on the command line or send a signal to the process to stop the +bot. +""" +from random import getrandbits + +import re + +from telegram import Updater, Update, InlineQueryResultArticle, ParseMode +import logging + +# Enable logging +logging.basicConfig( + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + level=logging.INFO) + +logger = logging.getLogger(__name__) + + +# Define a few command handlers. These usually take the two arguments bot and +# update. Error handlers also receive the raised TelegramError object in error. +def start(bot, update): + bot.sendMessage(update.message.chat_id, text='Hi!') + + +def help(bot, update): + bot.sendMessage(update.message.chat_id, text='Help!') + + +def escape_markdown(text): + """Helper function to escape telegram markup symbols""" + escape_chars = '\*_`\[' + return re.sub(r'([%s])' % escape_chars, r'\\\1', text) + + +def inlinequery(bot, update): + if update.inline_query is not None and update.inline_query.query: + query = update.inline_query.query + results = list() + + results.append(InlineQueryResultArticle( + id=hex(getrandbits(64))[2:], + title="Caps", + message_text=query.upper())) + + results.append(InlineQueryResultArticle( + id=hex(getrandbits(64))[2:], + title="Bold", + message_text="*%s*" % escape_markdown(query), + parse_mode=ParseMode.MARKDOWN)) + + results.append(InlineQueryResultArticle( + id=hex(getrandbits(64))[2:], + title="Italic", + message_text="_%s_" % escape_markdown(query), + parse_mode=ParseMode.MARKDOWN)) + + bot.answerInlineQuery(update.inline_query.id, results=results) + + + +def error(bot, update, error): + logger.warn('Update "%s" caused error "%s"' % (update, error)) + + +def main(): + # Create the Updater and pass it your bot's token. + updater = Updater("TOKEN") + + # Get the dispatcher to register handlers + dp = updater.dispatcher + + # on different commands - answer in Telegram + dp.addTelegramCommandHandler("start", start) + dp.addTelegramCommandHandler("help", help) + + # on noncommand i.e message - echo the message on Telegram + dp.addTelegramInlineHandler(inlinequery) + + # log all errors + dp.addErrorHandler(error) + + # Start the Bot + updater.start_polling() + + # Block until the user presses Ctrl-C or the process receives SIGINT, + # SIGTERM or SIGABRT. This should be used most of the time, since + # start_polling() is non-blocking and will stop the bot gracefully. + updater.idle() + +if __name__ == '__main__': + main() diff --git a/requirements-dev.txt b/requirements-dev.txt index 99bad0940..3cd235bb0 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -3,3 +3,4 @@ nose pep257 pylint unittest2 +flaky \ No newline at end of file diff --git a/setup.py b/setup.py index e0cc49c0a..09d198297 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ def requirements(): setup( name='python-telegram-bot', - version='3.2.0', + version='3.3b1', author='Leandro Toledo', author_email='devs@python-telegram-bot.org', license='LGPLv3', diff --git a/telegram/__init__.py b/telegram/__init__.py index a079cc8e0..42a2a985c 100644 --- a/telegram/__init__.py +++ b/telegram/__init__.py @@ -43,6 +43,10 @@ from .nullhandler import NullHandler from .emoji import Emoji from .parsemode import ParseMode from .message import Message +from .inlinequery import InlineQuery +from .choseninlineresult import ChosenInlineResult +from .inlinequeryresult import InlineQueryResultArticle, InlineQueryResultGif,\ + InlineQueryResultMpeg4Gif, InlineQueryResultPhoto, InlineQueryResultVideo from .update import Update from .bot import Bot from .dispatcher import Dispatcher @@ -52,11 +56,14 @@ from .updater import Updater __author__ = 'devs@python-telegram-bot.org' -__version__ = '3.2.0' +__version__ = '3.3b1' __all__ = ('Bot', 'Updater', 'Dispatcher', 'Emoji', 'TelegramError', 'InputFile', 'ReplyMarkup', 'ForceReply', 'ReplyKeyboardHide', 'ReplyKeyboardMarkup', 'UserProfilePhotos', 'ChatAction', 'Location', 'Contact', 'Video', 'Sticker', 'Document', 'File', 'Audio', 'PhotoSize', 'Chat', 'Update', 'ParseMode', 'Message', 'User', 'TelegramObject', 'NullHandler', 'Voice', 'JobQueue', + 'InlineQuery', 'ChosenInlineResult', 'InlineQueryResultArticle', + 'InlineQueryResultGif', 'InlineQueryResultPhoto', + 'InlineQueryResultMpeg4Gif', 'InlineQueryResultVideo', 'UpdateQueue') diff --git a/telegram/bot.py b/telegram/bot.py index 31869a555..a5f6cbdc6 100644 --- a/telegram/bot.py +++ b/telegram/bot.py @@ -27,6 +27,7 @@ from telegram import (User, Message, Update, UserProfilePhotos, File, TelegramError, ReplyMarkup, TelegramObject, NullHandler) from telegram.error import InvalidToken from telegram.utils import request +from telegram.utils.validate import validate_string H = NullHandler() logging.getLogger(__name__).addHandler(H) @@ -142,29 +143,39 @@ class Bot(TelegramObject): decorator """ url, data = func(self, *args, **kwargs) - - if not data.get('chat_id'): - raise TelegramError('Invalid chat_id') - - if kwargs.get('reply_to_message_id'): - reply_to_message_id = kwargs.get('reply_to_message_id') - data['reply_to_message_id'] = reply_to_message_id - - if kwargs.get('reply_markup'): - reply_markup = kwargs.get('reply_markup') - if isinstance(reply_markup, ReplyMarkup): - data['reply_markup'] = reply_markup.to_json() - else: - data['reply_markup'] = reply_markup - - result = request.post(url, data) - - if result is True: - return result - - return Message.de_json(result) + return Bot._post_message(url, data, kwargs) return decorator + @staticmethod + def _post_message(url, data, kwargs, timeout=None, network_delay=2.): + """Posts a message to the telegram servers. + + Returns: + telegram.Message + + """ + if not data.get('chat_id'): + raise TelegramError('Invalid chat_id') + + if kwargs.get('reply_to_message_id'): + reply_to_message_id = kwargs.get('reply_to_message_id') + data['reply_to_message_id'] = reply_to_message_id + + if kwargs.get('reply_markup'): + reply_markup = kwargs.get('reply_markup') + if isinstance(reply_markup, ReplyMarkup): + data['reply_markup'] = reply_markup.to_json() + else: + data['reply_markup'] = reply_markup + + result = request.post(url, data, timeout=timeout, + network_delay=network_delay) + + if result is True: + return result + + return Message.de_json(result) + @log def getMe(self): """A simple method for testing your bot's auth token. @@ -430,12 +441,12 @@ class Bot(TelegramObject): return url, data @log - @message def sendVideo(self, chat_id, video, duration=None, caption=None, + timeout=None, **kwargs): """Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as telegram.Document). @@ -452,6 +463,9 @@ class Bot(TelegramObject): caption: Video caption (may also be used when resending videos by file_id). [Optional] + timeout: + float. If this value is specified, use it as the definitive timeout + (in seconds) for urlopen() operations. [Optional] reply_to_message_id: If the message is a reply, ID of the original message. [Optional] reply_markup: @@ -473,7 +487,7 @@ class Bot(TelegramObject): if caption: data['caption'] = caption - return url, data + return self._post_message(url, data, kwargs, timeout=timeout) @log @message @@ -585,6 +599,59 @@ class Bot(TelegramObject): return url, data + @log + def answerInlineQuery(self, + inline_query_id, + results, + cache_time=None, + is_personal=None, + next_offset=None): + """Use this method to reply to an inline query. + + Args: + inline_query_id (str): + Unique identifier for answered query + results (list[InlineQueryResult]): + A list of results for the inline query + + Keyword Args: + cache_time (Optional[int]): The maximum amount of time the result + of the inline query may be cached on the server + is_personal (Optional[bool]): Pass True, if results may be cached + on the server side only for the user that sent the query. By + default, results may be returned to any user who sends the same + query + next_offset (Optional[str]): Pass the offset that a client should + send in the next query with the same text to receive more + results. Pass an empty string if there are no more results or + if you don't support pagination. Offset length can't exceed 64 + bytes. + + Returns: + A boolean if answering was successful + """ + + validate_string(inline_query_id, 'inline_query_id') + validate_string(inline_query_id, 'next_offset') + + url = '%s/answerInlineQuery' % self.base_url + + results = [res.to_dict() for res in results] + + data = {'inline_query_id': inline_query_id, + 'results': results} + + if cache_time is not None: + data['cache_time'] = int(cache_time) + if is_personal is not None: + data['is_personal'] = bool(is_personal) + if next_offset is not None: + data['next_offset'] = next_offset + + result = request.post(url, data) + + return result + @log def getUserProfilePhotos(self, user_id, diff --git a/telegram/choseninlineresult.py b/telegram/choseninlineresult.py new file mode 100644 index 000000000..87071833e --- /dev/null +++ b/telegram/choseninlineresult.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# pylint: disable=R0902,R0912,R0913 +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# 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 ChosenInlineResult +""" + + +from telegram import TelegramObject, User + + +class ChosenInlineResult(TelegramObject): + """This object represents a Telegram ChosenInlineResult. + + Note: + * In Python `from` is a reserved word, use `from_user` instead. + + Attributes: + result_id (str): + from_user (:class:`telegram.User`): + query (str): + + Args: + result_id (str): + from_user (:class:`telegram.User`): + query (str): + + """ + + def __init__(self, + result_id, + from_user, + query): + # Required + self.result_id = result_id + self.from_user = from_user + self.query = query + + @staticmethod + def de_json(data): + """ + Args: + data (dict): + + Returns: + telegram.ChosenInlineResult: + """ + if not data: + return None + data = data.copy() + data['from_user'] = User.de_json(data.pop('from')) + + return ChosenInlineResult(**data) + + def to_dict(self): + """ + Returns: + dict: + """ + data = super(ChosenInlineResult, self).to_dict() + + # Required + data['from'] = data.pop('from_user', None) + + return data diff --git a/telegram/dispatcher.py b/telegram/dispatcher.py index a013f64c9..3b63500fb 100644 --- a/telegram/dispatcher.py +++ b/telegram/dispatcher.py @@ -109,6 +109,8 @@ class Dispatcher: a list that contains the content of the message split on spaces, except the first word (usually the command). Example: '/add item1 item2 item3' -> ['item1', 'item2', 'item3'] + For updates that contain inline queries, they will contain the + whole query split on spaces. For other updates, args will be None In some cases handlers may need some context data to process the update. To @@ -138,6 +140,7 @@ class Dispatcher: self.bot = bot self.update_queue = update_queue self.telegram_message_handlers = [] + self.telegram_inline_handlers = [] self.telegram_command_handlers = {} self.telegram_regex_handlers = {} self.string_regex_handlers = {} @@ -253,21 +256,23 @@ class Dispatcher: handled = True # Telegram update (regex) - if isinstance(update, Update): + if isinstance(update, Update) and update.message is not None: self.dispatchRegex(update, context) handled = True - # Telegram update (command) - if isinstance(update, Update) \ - and update.message.text.startswith('/'): - self.dispatchTelegramCommand(update, context) - handled = True - - # Telegram update (message) - elif isinstance(update, Update): - self.dispatchTelegramMessage(update, context) - handled = True + # Telegram update (command) + if update.message.text.startswith('/'): + self.dispatchTelegramCommand(update, context) + # Telegram update (message) + else: + self.dispatchTelegramMessage(update, context) + handled = True + elif isinstance(update, Update) and \ + (update.inline_query is not None or + update.chosen_inline_result is not None): + self.dispatchTelegramInline(update, context) + handled = True # Update not recognized if not handled: self.dispatchError(update, TelegramError( @@ -285,6 +290,17 @@ class Dispatcher: self.telegram_message_handlers.append(handler) + def addTelegramInlineHandler(self, handler): + """ + Registers an inline query handler in the Dispatcher. + + Args: + handler (function): A function that takes (Bot, Update, *args) as + arguments. + """ + + self.telegram_inline_handlers.append(handler) + def addTelegramCommandHandler(self, command, handler): """ Registers a command handler in the Dispatcher. @@ -414,6 +430,17 @@ class Dispatcher: if handler in self.telegram_message_handlers: self.telegram_message_handlers.remove(handler) + def removeTelegramInlineHandler(self, handler): + """ + De-registers an inline query handler. + + Args: + handler (any): + """ + + if handler in self.telegram_inline_handlers: + self.telegram_inline_handlers.remove(handler) + def removeTelegramCommandHandler(self, command, handler): """ De-registers a command handler. @@ -597,6 +624,17 @@ class Dispatcher: self.dispatchTo(self.telegram_message_handlers, update, context=context) + def dispatchTelegramInline(self, update, context=None): + """ + Dispatches an update that contains an inline update. + + Args: + update (telegram.Update): The Telegram update that contains the + message. + """ + + self.dispatchTo(self.telegram_inline_handlers, update, context=None) + def dispatchError(self, update, error): """ Dispatches an error. @@ -645,8 +683,10 @@ class Dispatcher: target_kwargs['update_queue'] = self.update_queue if is_async or 'args' in fargs: - if isinstance(update, Update): + if isinstance(update, Update) and update.message: args = update.message.text.split(' ')[1:] + elif isinstance(update, Update) and update.inline_query: + args = update.inline_query.query.split(' ') elif isinstance(update, str): args = update.split(' ')[1:] else: diff --git a/telegram/inlinequery.py b/telegram/inlinequery.py new file mode 100644 index 000000000..f5165cff7 --- /dev/null +++ b/telegram/inlinequery.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +# pylint: disable=R0902,R0912,R0913 +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# 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 InlineQuery""" + +from telegram import TelegramObject, User + + +class InlineQuery(TelegramObject): + """This object represents a Telegram InlineQuery. + + Note: + * In Python `from` is a reserved word, use `from_user` instead. + + Attributes: + id (str): + from_user (:class:`telegram.User`): + query (str): + offset (str): + + Args: + id (int): + from_user (:class:`telegram.User`): + query (str): + offset (str): + + """ + + def __init__(self, + id, + from_user, + query, + offset): + # Required + self.id = id + self.from_user = from_user + self.query = query + self.offset = offset + + @staticmethod + def de_json(data): + """ + Args: + data (dict): + + Returns: + telegram.InlineQuery: + """ + if not data: + return None + data = data.copy() + data['from_user'] = User.de_json(data.pop('from')) + + return InlineQuery(**data) + + def to_dict(self): + """ + Returns: + dict: + """ + data = super(InlineQuery, self).to_dict() + + # Required + data['from'] = data.pop('from_user', None) + + return data diff --git a/telegram/inlinequeryresult.py b/telegram/inlinequeryresult.py new file mode 100644 index 000000000..bc6385ce8 --- /dev/null +++ b/telegram/inlinequeryresult.py @@ -0,0 +1,499 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# 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 the classes that represent Telegram InlineQueryResults +https://core.telegram.org/bots/api#inline-mode +""" + +from telegram import TelegramObject +from telegram.utils.validate import validate_string + + +class InlineQueryResult(TelegramObject): + """This object represents a Telegram InlineQueryResult. + + Attributes: + type (str): + id (str): + + Args: + type (str): + id (str): Unique identifier for this result, 1-64 Bytes + + """ + + def __init__(self, + type, + id): + # Required + self.type = str(type) + self.id = str(id) + + @staticmethod + def de_json(data): + """ + Args: + data (dict): + + Returns: + telegram.InlineQueryResult: + """ + if not data: + return None + + return InlineQueryResult(**data) + + +class InlineQueryResultArticle(InlineQueryResult): + """This object represents a Telegram InlineQueryResultArticle. + + Attributes: + id (str): + title (str): + message_text (str): + parse_mode (str): + disable_web_page_preview (bool): + url (str): + hide_url (bool): + description (str): + thumb_url (str): + thumb_width (int): + thumb_height (int): + + Args: + id (str): Unique identifier for this result, 1-64 Bytes + title (str): + message_text (str): + + Keyword Args: + parse_mode (Optional[str]): + disable_web_page_preview (Optional[bool]): + url (Optional[str]): + hide_url (Optional[bool]): + description (Optional[str]): + thumb_url (Optional[str]): + thumb_width (Optional[int]): + thumb_height (Optional[int]): + """ + + def __init__(self, + id, + title, + message_text, + parse_mode=None, + disable_web_page_preview=None, + url=None, + hide_url=None, + description=None, + thumb_url=None, + thumb_width=None, + thumb_height=None): + + validate_string(title, 'title') + validate_string(message_text, 'message_text') + validate_string(url, 'url') + validate_string(description, 'description') + validate_string(thumb_url, 'thumb_url') + validate_string(parse_mode, 'parse_mode') + + # Required + super(InlineQueryResultArticle, self).__init__('article', id) + self.title = title + self.message_text = message_text + + # Optional + self.parse_mode = parse_mode + self.disable_web_page_preview = bool(disable_web_page_preview) + self.url = url + self.hide_url = bool(hide_url) + self.description = description + self.thumb_url = thumb_url + if thumb_width is not None: + self.thumb_width = int(thumb_width) + if thumb_height is not None: + self.thumb_height = int(thumb_height) + + @staticmethod + def de_json(data): + """ + Args: + data (dict): + + Returns: + telegram.InlineQueryResultArticle: + """ + if not data: + return None + data = data.copy() + data.pop('type', None) + + return InlineQueryResultArticle(**data) + + +class InlineQueryResultPhoto(InlineQueryResult): + """This object represents a Telegram InlineQueryResultPhoto. + + Attributes: + id (str): + photo_url (str): + mime_type (str): + photo_width (int): + photo_height (int): + thumb_url (str): + title (str): + description (str): + caption (str): + message_text (str): + parse_mode (str): + disable_web_page_preview (bool): + + Args: + id (str): Unique identifier for this result, 1-64 Bytes + photo_url (str): + thumb_url (str): + + Keyword Args: + mime_type (Optional[str]): + photo_width (Optional[int]): + photo_height (Optional[int]): + title (Optional[str]): + description (Optional[str]): + caption (Optional[str]): + message_text (Optional[str]): + parse_mode (Optional[str]): + disable_web_page_preview (Optional[bool]): + """ + + def __init__(self, + id, + photo_url, + thumb_url, + mime_type=None, + photo_width=None, + photo_height=None, + title=None, + description=None, + caption=None, + message_text=None, + parse_mode=None, + disable_web_page_preview=None): + + validate_string(photo_url, 'photo_url') + validate_string(thumb_url, 'thumb_url') + validate_string(mime_type, 'mime_type') + validate_string(title, 'title') + validate_string(description, 'description') + validate_string(caption, 'caption') + validate_string(message_text, 'message_text') + validate_string(parse_mode, 'parse_mode') + + # Required + super(InlineQueryResultPhoto, self).__init__('photo', id) + self.photo_url = photo_url + self.thumb_url = thumb_url + + # Optional + self.mime_type = mime_type + if photo_width is not None: + self.photo_width = int(photo_width) + if photo_height is not None: + self.photo_height = int(photo_height) + self.title = title + self.description = description + self.caption = caption + self.message_text = message_text + self.parse_mode = parse_mode + self.disable_web_page_preview = bool(disable_web_page_preview) + + @staticmethod + def de_json(data): + """ + Args: + data (dict): + + Returns: + telegram.InlineQueryResultPhoto: + """ + if not data: + return None + data = data.copy() + data.pop('type', None) + + return InlineQueryResultPhoto(**data) + + +class InlineQueryResultGif(InlineQueryResult): + """This object represents a Telegram InlineQueryResultGif. + + Attributes: + id (str): + gif_url (str): + gif_width (int): + gif_height (int): + thumb_url (str): + title (str): + caption (str): + message_text (str): + parse_mode (str): + disable_web_page_preview (bool): + + Args: + id (str): Unique identifier for this result, 1-64 Bytes + gif_url (str): + thumb_url (str): + + Keyword Args: + gif_width (Optional[int]): + gif_height (Optional[int]): + title (Optional[str]): + caption (Optional[str]): + message_text (Optional[str]): + parse_mode (Optional[str]): + disable_web_page_preview (Optional[bool]): + """ + + def __init__(self, + id, + gif_url, + thumb_url, + gif_width=None, + gif_height=None, + title=None, + caption=None, + message_text=None, + parse_mode=None, + disable_web_page_preview=None): + + validate_string(gif_url, 'gif_url') + validate_string(thumb_url, 'thumb_url') + validate_string(title, 'title') + validate_string(caption, 'caption') + validate_string(message_text, 'message_text') + validate_string(parse_mode, 'parse_mode') + + # Required + super(InlineQueryResultGif, self).__init__('gif', id) + self.gif_url = gif_url + self.thumb_url = thumb_url + + # Optional + if gif_width is not None: + self.gif_width = int(gif_width) + if gif_height is not None: + self.gif_height = int(gif_height) + self.title = title + self.caption = caption + self.message_text = message_text + self.parse_mode = parse_mode + self.disable_web_page_preview = bool(disable_web_page_preview) + + @staticmethod + def de_json(data): + """ + Args: + data (dict): + + Returns: + telegram.InlineQueryResultGif: + """ + if not data: + return None + data = data.copy() + data.pop('type', None) + + return InlineQueryResultGif(**data) + + +class InlineQueryResultMpeg4Gif(InlineQueryResult): + """This object represents a Telegram InlineQueryResultMpeg4Gif. + + Attributes: + id (str): + mpeg4_url (str): + mpeg4_width (int): + mpeg4_height (int): + thumb_url (str): + title (str): + caption (str): + message_text (str): + parse_mode (str): + disable_web_page_preview (bool): + + Args: + id (str): Unique identifier for this result, 1-64 Bytes + mpeg4_url (str): + thumb_url (str): + + Keyword Args: + mpeg4_width (Optional[int]): + mpeg4_height (Optional[int]): + title (Optional[str]): + caption (Optional[str]): + message_text (Optional[str]): + parse_mode (Optional[str]): + disable_web_page_preview (Optional[bool]): + """ + + def __init__(self, + id, + mpeg4_url, + thumb_url, + mpeg4_width=None, + mpeg4_height=None, + title=None, + caption=None, + message_text=None, + parse_mode=None, + disable_web_page_preview=None): + + validate_string(mpeg4_url, 'mpeg4_url') + validate_string(thumb_url, 'thumb_url') + validate_string(title, 'title') + validate_string(caption, 'caption') + validate_string(message_text, 'message_text') + validate_string(parse_mode, 'parse_mode') + + # Required + super(InlineQueryResultMpeg4Gif, self).__init__('mpeg4_gif', id) + self.mpeg4_url = mpeg4_url + self.thumb_url = thumb_url + + # Optional + if mpeg4_width is not None: + self.mpeg4_width = int(mpeg4_width) + if mpeg4_height is not None: + self.mpeg4_height = int(mpeg4_height) + self.title = title + self.caption = caption + self.message_text = message_text + self.parse_mode = parse_mode + self.disable_web_page_preview = bool(disable_web_page_preview) + + @staticmethod + def de_json(data): + """ + Args: + data (dict): + + Returns: + telegram.InlineQueryResultMpeg4Gif: + """ + if not data: + return None + data = data.copy() + data.pop('type', None) + + return InlineQueryResultMpeg4Gif(**data) + + +class InlineQueryResultVideo(InlineQueryResult): + """This object represents a Telegram InlineQueryResultVideo. + + Attributes: + id (str): + video_url (str): + mime_type (str): + video_width (int): + video_height (int): + video_duration (int): + thumb_url (str): + title (str): + description (str): + caption (str): + message_text (str): + parse_mode (str): + disable_web_page_preview (bool): + + Args: + id (str): Unique identifier for this result, 1-64 Bytes + video_url (str): + mime_type (str): + thumb_url (str): + title (str): + message_text (str): + + Keyword Args: + video_width (Optional[int]): + video_height (Optional[int]): + video_duration (Optional[int]): + description (Optional[str]): + caption (Optional[str]): + parse_mode (Optional[str]): + disable_web_page_preview (Optional[bool]): + """ + + def __init__(self, + id, + video_url, + mime_type, + thumb_url, + title, + message_text, + video_width=None, + video_height=None, + video_duration=None, + description=None, + caption=None, + parse_mode=None, + disable_web_page_preview=None): + + validate_string(video_url, 'video_url') + validate_string(mime_type, 'mime_type') + validate_string(thumb_url, 'thumb_url') + validate_string(title, 'title') + validate_string(message_text, 'message_text') + validate_string(description, 'description') + validate_string(caption, 'caption') + validate_string(parse_mode, 'parse_mode') + + # Required + super(InlineQueryResultVideo, self).__init__('video', id) + self.video_url = video_url + self.mime_type = mime_type + self.thumb_url = thumb_url + self.title = title + self.message_text = message_text + + # Optional + if video_width is not None: + self.video_width = int(video_width) + if video_height is not None: + self.video_height = int(video_height) + if video_duration is not None: + self.video_duration = int(video_duration) + self.description = description + self.caption = caption + self.parse_mode = parse_mode + self.disable_web_page_preview = bool(disable_web_page_preview) + + @staticmethod + def de_json(data): + """ + Args: + data (dict): + + Returns: + telegram.InlineQueryResultVideo: + """ + if not data: + return None + data = data.copy() + data.pop('type', None) + + return InlineQueryResultVideo(**data) diff --git a/telegram/photosize.py b/telegram/photosize.py index 275cb639c..694cb5252 100644 --- a/telegram/photosize.py +++ b/telegram/photosize.py @@ -53,6 +53,14 @@ class PhotoSize(TelegramObject): # Optionals self.file_size = int(kwargs.get('file_size', 0)) + def __eq__(self, other): + if not isinstance(other, self.__class__): + return False + return (self.file_id == other.file_id and + self.width == other.width and + self.height == other.height and + self.file_size == other.file_size) + @staticmethod def de_json(data): """ diff --git a/telegram/update.py b/telegram/update.py index 6186e0f88..f3c51e825 100644 --- a/telegram/update.py +++ b/telegram/update.py @@ -19,7 +19,7 @@ """This module contains a object that represents a Telegram Update.""" -from telegram import Message, TelegramObject +from telegram import Message, TelegramObject, InlineQuery, ChosenInlineResult class Update(TelegramObject): @@ -28,6 +28,8 @@ class Update(TelegramObject): Attributes: update_id (int): message (:class:`telegram.Message`): + inline_query (:class:`telegram.InlineQuery`): + chosen_inline_result (:class:`telegram.ChosenInlineResult`): Args: update_id (int): @@ -35,6 +37,8 @@ class Update(TelegramObject): Keyword Args: message (Optional[:class:`telegram.Message`]): + inline_query (Optional[:class:`telegram.InlineQuery`]): + chosen_inline_result (Optional[:class:`telegram.ChosenInlineResult`]) """ def __init__(self, update_id, @@ -43,12 +47,14 @@ class Update(TelegramObject): self.update_id = int(update_id) # Optionals self.message = kwargs.get('message') + self.inline_query = kwargs.get('inline_query') + self.chosen_inline_result = kwargs.get('chosen_inline_result') @staticmethod def de_json(data): """ Args: - data (str): + data (dict): Returns: telegram.Update: @@ -56,6 +62,9 @@ class Update(TelegramObject): if not data: return None - data['message'] = Message.de_json(data['message']) + data['message'] = Message.de_json(data.get('message')) + data['inline_query'] = InlineQuery.de_json(data.get('inline_query')) + data['chosen_inline_result'] = \ + ChosenInlineResult.de_json(data.get('chosen_inline_result')) return Update(**data) diff --git a/telegram/utils/request.py b/telegram/utils/request.py index 662f8f45a..8e865efd4 100644 --- a/telegram/utils/request.py +++ b/telegram/utils/request.py @@ -127,6 +127,7 @@ def get(url): @_try_except_req def post(url, data, + timeout=None, network_delay=2.): """Request an URL. Args: @@ -134,20 +135,29 @@ def post(url, The web location we want to retrieve. data: A dict of (str, unicode) key/value pairs. + timeout: + float. If this value is specified, use it as the definitive timeout (in + seconds) for urlopen() operations. [Optional] network_delay: - Additional timeout in seconds to allow the response from Telegram to - take some time. + float. If using the timeout specified in `data` (which is a timeout for + the Telegram servers operation), then `network_delay` as an extra delay + (in seconds) to compensate for network latency. + default: 2 [Optional] + + Notes: + If neither `timeout` nor `data['timeout']` is specified. The underlying + defaults are used. Returns: A JSON object. - """ - # Add time to the timeout of urlopen to allow data to be transferred over - # the network. - if 'timeout' in data: - timeout = data['timeout'] + network_delay - else: - timeout = None + """ + urlopen_kwargs = {} + + if timeout is not None: + urlopen_kwargs['timeout'] = timeout + elif 'timeout' in data: + urlopen_kwargs['timeout'] = data['timeout'] + network_delay if InputFile.is_inputfile(data): data = InputFile(data) @@ -160,7 +170,7 @@ def post(url, data=data.encode(), headers={'Content-Type': 'application/json'}) - result = urlopen(request, timeout=timeout).read() + result = urlopen(request, **urlopen_kwargs).read() return _parse(result) diff --git a/telegram/utils/validate.py b/telegram/utils/validate.py new file mode 100644 index 000000000..3b6f7c092 --- /dev/null +++ b/telegram/utils/validate.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2016 +# Leandro Toledo de Souza +# +# 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 functions to validate function arguments""" + +try: + type(basestring) +except NameError: + basestring = str + + +def validate_string(arg, name): + """ + Validate a string argument. Raises a ValueError if `arg` is neither an + instance of basestring (Python 2) or str (Python 3) nor None. + + Args: + arg (basestring): The value to be tested + name (str): The name of the argument, for the error message + """ + if not isinstance(arg, basestring) and arg is not None: + raise ValueError(name + " is not a string") diff --git a/tests/base.py b/tests/base.py index 398f64bad..75187face 100644 --- a/tests/base.py +++ b/tests/base.py @@ -21,6 +21,11 @@ import os import sys +import signal +import traceback + +from nose.tools import make_decorator + sys.path.append('.') import json @@ -54,3 +59,32 @@ class BaseTest(object): return True return False + + +class TestTimedOut(AssertionError): + + def __init__(self, time_limit, frame): + super(TestTimedOut, self).__init__('time_limit={0}'.format(time_limit)) + self.time_limit = time_limit + self.frame = frame + + +def timeout(time_limit): + def decorator(func): + def timed_out(_signum, frame): + raise TestTimedOut(time_limit, frame) + + def newfunc(*args, **kwargs): + orig_handler = signal.signal(signal.SIGALRM, timed_out) + signal.alarm(time_limit) + try: + rc = func(*args, **kwargs) + finally: + signal.alarm(0) + signal.signal(signal.SIGALRM, orig_handler) + return rc + + newfunc = make_decorator(func)(newfunc) + return newfunc + + return decorator diff --git a/tests/test_audio.py b/tests/test_audio.py index bbffe815e..da7ebeb6f 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -22,10 +22,12 @@ import os import unittest import sys +from flaky import flaky + sys.path.append('.') import telegram -from tests.base import BaseTest +from tests.base import BaseTest, timeout class AudioTest(BaseTest, unittest.TestCase): @@ -50,10 +52,9 @@ class AudioTest(BaseTest, unittest.TestCase): 'file_size': self.file_size } + @flaky(3, 1) + @timeout(10) def test_send_audio_required_args_only(self): - """Test telegram.Bot sendAudio method""" - print('Testing bot.sendAudio - With required arguments only') - message = self._bot.sendAudio(self._chat_id, self.audio_file) @@ -67,10 +68,9 @@ class AudioTest(BaseTest, unittest.TestCase): self.assertEqual(audio.mime_type, self.mime_type) self.assertEqual(audio.file_size, self.file_size) + @flaky(3, 1) + @timeout(10) def test_send_audio_all_args(self): - """Test telegram.Bot sendAudio method""" - print('Testing bot.sendAudio - With all arguments') - message = self._bot.sendAudio(self._chat_id, self.audio_file, duration=self.duration, @@ -89,10 +89,9 @@ class AudioTest(BaseTest, unittest.TestCase): self.assertEqual(audio.mime_type, self.mime_type) self.assertEqual(audio.file_size, self.file_size) + @flaky(3, 1) + @timeout(10) def test_send_audio_mp3_file(self): - """Test telegram.Bot sendAudio method""" - print('Testing bot.sendAudio - MP3 File') - message = self._bot.sendAudio(chat_id=self._chat_id, audio=self.audio_file, duration=self.duration, @@ -109,10 +108,9 @@ class AudioTest(BaseTest, unittest.TestCase): self.assertEqual(audio.mime_type, self.mime_type) self.assertEqual(audio.file_size, self.file_size) + @flaky(3, 1) + @timeout(10) def test_send_audio_mp3_file_custom_filename(self): - """Test telegram.Bot sendAudio method""" - print('Testing bot.sendAudio - MP3 File with custom filename') - message = self._bot.sendAudio(chat_id=self._chat_id, audio=self.audio_file, duration=self.duration, @@ -130,10 +128,9 @@ class AudioTest(BaseTest, unittest.TestCase): self.assertEqual(audio.mime_type, self.mime_type) self.assertEqual(audio.file_size, self.file_size) + @flaky(3, 1) + @timeout(10) def test_send_audio_mp3_url_file(self): - """Test telegram.Bot sendAudio method""" - print('Testing bot.sendAudio - MP3 File by URL') - message = self._bot.sendAudio(chat_id=self._chat_id, audio=self.audio_file_url, duration=self.duration, @@ -150,10 +147,9 @@ class AudioTest(BaseTest, unittest.TestCase): self.assertEqual(audio.mime_type, self.mime_type) self.assertEqual(audio.file_size, self.file_size) + @flaky(3, 1) + @timeout(10) def test_send_audio_resend(self): - """Test telegram.Bot sendAudio method""" - print('Testing bot.sendAudio - Resend by file_id') - message = self._bot.sendAudio(chat_id=self._chat_id, audio=self.audio_file_id, duration=self.duration, @@ -169,9 +165,6 @@ class AudioTest(BaseTest, unittest.TestCase): self.assertEqual(audio.mime_type, self.mime_type) def test_audio_de_json(self): - """Test Audio.de_json() method""" - print('Testing Audio.de_json()') - audio = telegram.Audio.de_json(self.json_dict) self.assertEqual(audio.file_id, self.audio_file_id) @@ -182,17 +175,11 @@ class AudioTest(BaseTest, unittest.TestCase): self.assertEqual(audio.file_size, self.file_size) def test_audio_to_json(self): - """Test Audio.to_json() method""" - print('Testing Audio.to_json()') - audio = telegram.Audio.de_json(self.json_dict) self.assertTrue(self.is_json(audio.to_json())) def test_audio_to_dict(self): - """Test Audio.to_dict() method""" - print('Testing Audio.to_dict()') - audio = telegram.Audio.de_json(self.json_dict) self.assertTrue(self.is_dict(audio.to_dict())) @@ -203,9 +190,9 @@ class AudioTest(BaseTest, unittest.TestCase): self.assertEqual(audio['mime_type'], self.mime_type) self.assertEqual(audio['file_size'], self.file_size) + @flaky(3, 1) + @timeout(10) def test_error_send_audio_empty_file(self): - print('Testing bot.sendAudio - Null file') - json_dict = self.json_dict del(json_dict['file_id']) @@ -215,9 +202,9 @@ class AudioTest(BaseTest, unittest.TestCase): lambda: self._bot.sendAudio(chat_id=self._chat_id, **json_dict)) + @flaky(3, 1) + @timeout(10) def test_error_send_audio_empty_file_id(self): - print('Testing bot.sendAudio - Empty file_id') - json_dict = self.json_dict del(json_dict['file_id']) @@ -227,9 +214,9 @@ class AudioTest(BaseTest, unittest.TestCase): lambda: self._bot.sendAudio(chat_id=self._chat_id, **json_dict)) + @flaky(3, 1) + @timeout(10) def test_error_audio_without_required_args(self): - print('Testing bot.sendAudio - Without required arguments') - json_dict = self.json_dict del(json_dict['file_id']) diff --git a/tests/test_bot.py b/tests/test_bot.py index 6e6fd02fb..d861df198 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -23,6 +23,7 @@ import os from datetime import datetime import sys +from flaky import flaky if sys.version_info[0:2] == (2, 6): import unittest2 as unittest @@ -32,15 +33,15 @@ else: sys.path.append('.') import telegram -from tests.base import BaseTest +from tests.base import BaseTest, timeout class BotTest(BaseTest, unittest.TestCase): """This object represents Tests for Telegram Bot.""" + @flaky(3, 1) + @timeout(10) def testGetMe(self): - '''Test the telegram.Bot getMe method''' - print('Testing getMe') bot = self._bot.getMe() self.assertTrue(self.is_json(bot.to_json())) @@ -50,9 +51,9 @@ class BotTest(BaseTest, unittest.TestCase): self.assertEqual(bot.username, 'PythonTelegramBot') self.assertEqual(bot.name, '@PythonTelegramBot') + @flaky(3, 1) + @timeout(10) def testSendMessage(self): - '''Test the telegram.Bot sendMessage method''' - print('Testing sendMessage') message = self._bot.sendMessage(chat_id=self._chat_id, text='Моё судно на воздушной подушке полно угрей') @@ -60,18 +61,18 @@ class BotTest(BaseTest, unittest.TestCase): self.assertEqual(message.text, u'Моё судно на воздушной подушке полно угрей') self.assertTrue(isinstance(message.date, datetime)) + @flaky(3, 1) + @timeout(10) def testGetUpdates(self): - '''Test the telegram.Bot getUpdates method''' - print('Testing getUpdates') updates = self._bot.getUpdates() if updates: self.assertTrue(self.is_json(updates[0].to_json())) self.assertTrue(isinstance(updates[0], telegram.Update)) + @flaky(3, 1) + @timeout(10) def testForwardMessage(self): - '''Test the telegram.Bot forwardMessage method''' - print('Testing forwardMessage') message = self._bot.forwardMessage(chat_id=self._chat_id, from_chat_id=self._chat_id, message_id=2398) @@ -81,9 +82,9 @@ class BotTest(BaseTest, unittest.TestCase): self.assertEqual(message.forward_from.username, 'leandrotoledo') self.assertTrue(isinstance(message.forward_date, datetime)) + @flaky(3, 1) + @timeout(10) def testSendPhoto(self): - '''Test the telegram.Bot sendPhoto method''' - print('Testing sendPhoto - File') message = self._bot.sendPhoto(photo=open('tests/data/telegram.png', 'rb'), caption='testSendPhoto', chat_id=self._chat_id) @@ -92,59 +93,57 @@ class BotTest(BaseTest, unittest.TestCase): self.assertEqual(message.photo[0].file_size, 1451) self.assertEqual(message.caption, 'testSendPhoto') + @flaky(3, 1) + @timeout(10) def testResendPhoto(self): - '''Test the telegram.Bot sendPhoto method''' - print('Testing sendPhoto - Resend') message = self._bot.sendPhoto(photo='AgADAQADyKcxGx8j9Qdp6d-gpUsw4Gja1i8ABEVJsVqQk8LfJ3wAAgI', chat_id=self._chat_id) self.assertTrue(self.is_json(message.to_json())) self.assertEqual(message.photo[0].file_id, 'AgADAQADyKcxGx8j9Qdp6d-gpUsw4Gja1i8ABEVJsVqQk8LfJ3wAAgI') + @flaky(3, 1) + @timeout(10) def testSendJPGURLPhoto(self): - '''Test the telegram.Bot sendPhoto method''' - print('Testing testSendJPGURLPhoto - URL') message = self._bot.sendPhoto(photo='http://dummyimage.com/600x400/000/fff.jpg&text=telegram', chat_id=self._chat_id) self.assertTrue(self.is_json(message.to_json())) self.assertEqual(message.photo[0].file_size, 822) + @flaky(3, 1) + @timeout(10) def testSendPNGURLPhoto(self): - '''Test the telegram.Bot sendPhoto method''' - print('Testing testSendPNGURLPhoto - URL') message = self._bot.sendPhoto(photo='http://dummyimage.com/600x400/000/fff.png&text=telegram', chat_id=self._chat_id) self.assertTrue(self.is_json(message.to_json())) self.assertEqual(message.photo[0].file_size, 684) + @flaky(3, 1) + @timeout(10) def testSendGIFURLPhoto(self): - '''Test the telegram.Bot sendPhoto method''' - print('Testing testSendGIFURLPhoto - URL') message = self._bot.sendPhoto(photo='http://dummyimage.com/600x400/000/fff.gif&text=telegram', chat_id=self._chat_id) self.assertTrue(self.is_json(message.to_json())) self.assertEqual(message.photo[0].file_size, 684) + @flaky(3, 1) + @timeout(10) def testSendChatAction(self): - '''Test the telegram.Bot sendChatAction method''' - print('Testing sendChatAction - ChatAction.TYPING') - self._bot.sendChatAction(action=telegram.ChatAction.TYPING, chat_id=self._chat_id) + @flaky(3, 1) + @timeout(10) def testGetUserProfilePhotos(self): - '''Test the telegram.Bot getUserProfilePhotos method''' - print('Testing getUserProfilePhotos') upf = self._bot.getUserProfilePhotos(user_id=self._chat_id) self.assertTrue(self.is_json(upf.to_json())) self.assertEqual(upf.photos[0][0].file_size, 12421) def _test_invalid_token(self, token): - print('Testing invalid token: {0}'.format(token)) self.assertRaisesRegexp(telegram.error.InvalidToken, 'Invalid token', telegram.Bot, token) def testInvalidToken1(self): @@ -157,13 +156,11 @@ class BotTest(BaseTest, unittest.TestCase): self._test_invalid_token('12:') def testUnauthToken(self): - print('Testing unauthorized token') with self.assertRaisesRegexp(telegram.error.Unauthorized, 'Unauthorized'): bot = telegram.Bot('1234:abcd1234') bot.getMe() def testInvalidSrvResp(self): - print('Testing invalid server response') with self.assertRaisesRegexp(telegram.TelegramError, 'Invalid server response'): # bypass the valid token check bot_cls = type('bot_cls', (telegram.Bot, ), {'_valid_token': lambda self, token: token}) diff --git a/tests/test_botan.py b/tests/test_botan.py index 34a142e83..7120c9097 100644 --- a/tests/test_botan.py +++ b/tests/test_botan.py @@ -5,6 +5,8 @@ import os import unittest import sys +from flaky import flaky + sys.path.append('.') from telegram.utils.botan import Botan @@ -21,22 +23,19 @@ class MessageMock(object): return "{}" +@flaky(3, 1) class BotanTest(BaseTest, unittest.TestCase): """This object represents Tests for Botan analytics integration.""" token = os.environ.get('BOTAN_TOKEN') def test_track(self): - """Test sending event to botan""" - print('Test sending event to botan') botan = Botan(self.token) message = MessageMock(self._chat_id) result = botan.track(message, 'named event') self.assertTrue(result) def test_track_fail(self): - """Test fail when sending event to botan""" - print('Test fail when sending event to botan') botan = Botan(self.token) botan.url_template = 'https://api.botan.io/traccc?token={token}&uid={uid}&name={name}' message = MessageMock(self._chat_id) @@ -44,8 +43,6 @@ class BotanTest(BaseTest, unittest.TestCase): self.assertFalse(result) def test_wrong_message(self): - """Test sending wrong message""" - print('Test sending wrong message') botan = Botan(self.token) message = MessageMock(self._chat_id) message = delattr(message, 'chat_id') @@ -53,8 +50,6 @@ class BotanTest(BaseTest, unittest.TestCase): self.assertFalse(result) def test_wrong_endpoint(self): - """Test wrong endpoint""" - print('Test wrong endpoint') botan = Botan(self.token) botan.url_template = 'https://api.botaaaaan.io/traccc?token={token}&uid={uid}&name={name}' message = MessageMock(self._chat_id) diff --git a/tests/test_chat.py b/tests/test_chat.py index de8eec90a..b5dd626b4 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -43,17 +43,11 @@ class ChatTest(BaseTest, unittest.TestCase): } def test_group_chat_de_json_empty_json(self): - """Test Chat.de_json() method""" - print('Testing Chat.de_json() - Empty JSON') - group_chat = telegram.Chat.de_json({}) self.assertEqual(group_chat, None) def test_group_chat_de_json(self): - """Test Chat.de_json() method""" - print('Testing Chat.de_json()') - group_chat = telegram.Chat.de_json(self.json_dict) self.assertEqual(group_chat.id, self.id) @@ -61,17 +55,11 @@ class ChatTest(BaseTest, unittest.TestCase): self.assertEqual(group_chat.type, self.type) def test_group_chat_to_json(self): - """Test Chat.to_json() method""" - print('Testing Chat.to_json()') - group_chat = telegram.Chat.de_json(self.json_dict) self.assertTrue(self.is_json(group_chat.to_json())) def test_group_chat_to_dict(self): - """Test Chat.to_dict() method""" - print('Testing Chat.to_dict()') - group_chat = telegram.Chat.de_json(self.json_dict) self.assertTrue(self.is_dict(group_chat.to_dict())) diff --git a/tests/test_choseninlineresult.py b/tests/test_choseninlineresult.py new file mode 100644 index 000000000..872037aae --- /dev/null +++ b/tests/test_choseninlineresult.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2016 +# Leandro Toledo de Souza +# +# 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 a object that represents Tests for Telegram +ChosenInlineResult""" + +import sys + +if sys.version_info[0:2] == (2, 6): + import unittest2 as unittest +else: + import unittest + +sys.path.append('.') + +import telegram +from tests.base import BaseTest + + +class ChosenInlineResultTest(BaseTest, unittest.TestCase): + """This object represents Tests for Telegram ChosenInlineResult.""" + + def setUp(self): + + user = telegram.User(1, 'First name') + + self.result_id = 'result id' + self.from_user = user + self.query = 'query text' + + self.json_dict = { + 'result_id': self.result_id, + 'from': self.from_user.to_dict(), + 'query': self.query + } + + def test_choseninlineresult_de_json(self): + result = telegram.ChosenInlineResult.de_json(self.json_dict) + + self.assertEqual(result.result_id, self.result_id) + self.assertDictEqual(result.from_user.to_dict(), + self.from_user.to_dict()) + self.assertEqual(result.query, self.query) + + def test_choseninlineresult_to_json(self): + result = telegram.ChosenInlineResult.de_json(self.json_dict) + + self.assertTrue(self.is_json(result.to_json())) + + def test_choseninlineresult_to_dict(self): + result = telegram.ChosenInlineResult.de_json(self.json_dict).to_dict() + + self.assertTrue(self.is_dict(result)) + self.assertEqual(result['result_id'], self.result_id) + self.assertEqual(result['from'], self.from_user.to_dict()) + self.assertEqual(result['query'], self.query) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_contact.py b/tests/test_contact.py index ccb7805e9..38e6cdca7 100644 --- a/tests/test_contact.py +++ b/tests/test_contact.py @@ -45,9 +45,6 @@ class ContactTest(BaseTest, unittest.TestCase): } def test_contact_de_json(self): - """Test Contact.de_json() method""" - print('Testing Contact.de_json()') - contact = telegram.Contact.de_json(self.json_dict) self.assertEqual(contact.phone_number, self.phone_number) @@ -56,17 +53,11 @@ class ContactTest(BaseTest, unittest.TestCase): self.assertEqual(contact.user_id, self.user_id) def test_contact_to_json(self): - """Test Contact.to_json() method""" - print('Testing Contact.to_json()') - contact = telegram.Contact.de_json(self.json_dict) self.assertTrue(self.is_json(contact.to_json())) def test_contact_to_dict(self): - """Test Contact.to_dict() method""" - print('Testing Contact.to_dict()') - contact = telegram.Contact.de_json(self.json_dict) self.assertTrue(self.is_dict(contact.to_dict())) diff --git a/tests/test_document.py b/tests/test_document.py index 9c5a4c116..a7f9e8dad 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -22,10 +22,12 @@ import os import unittest import sys +from flaky import flaky + sys.path.append('.') import telegram -from tests.base import BaseTest +from tests.base import BaseTest, timeout class DocumentTest(BaseTest, unittest.TestCase): @@ -51,10 +53,9 @@ class DocumentTest(BaseTest, unittest.TestCase): 'file_size': self.file_size } + @flaky(3, 1) + @timeout(10) def test_send_document_png_file(self): - """Test telegram.Bot sendDocument method""" - print('Testing bot.sendDocument - PNG File') - message = self._bot.sendDocument(self._chat_id, self.document_file) @@ -67,10 +68,9 @@ class DocumentTest(BaseTest, unittest.TestCase): self.assertEqual(document.mime_type, self.mime_type) self.assertEqual(document.file_size, self.file_size) + @flaky(3, 1) + @timeout(10) def test_send_document_png_file_with_custom_file_name(self): - """Test telegram.Bot sendDocument method""" - print('Testing bot.sendDocument - PNG File with custom filename') - message = self._bot.sendDocument(self._chat_id, self.document_file, filename='telegram_custom.png') @@ -84,10 +84,9 @@ class DocumentTest(BaseTest, unittest.TestCase): self.assertEqual(document.mime_type, self.mime_type) self.assertEqual(document.file_size, self.file_size) + @flaky(3, 1) + @timeout(10) def test_send_document_url_gif_file(self): - """Test telegram.Bot sendDocument method""" - print('Testing bot.sendDocument - GIF File by URL') - message = self._bot.sendDocument(self._chat_id, self.document_file_url) @@ -100,10 +99,9 @@ class DocumentTest(BaseTest, unittest.TestCase): self.assertEqual(document.mime_type, 'image/gif') self.assertEqual(document.file_size, 3878) + @flaky(3, 1) + @timeout(10) def test_send_document_resend(self): - """Test telegram.Bot sendDocument method""" - print('Testing bot.sendDocument - Resend by file_id') - message = self._bot.sendDocument(chat_id=self._chat_id, document=self.document_file_id) @@ -115,9 +113,6 @@ class DocumentTest(BaseTest, unittest.TestCase): self.assertEqual(document.mime_type, self.mime_type) def test_document_de_json(self): - """Test Document.de_json() method""" - print('Testing Document.de_json()') - document = telegram.Document.de_json(self.json_dict) self.assertEqual(document.file_id, self.document_file_id) @@ -127,17 +122,11 @@ class DocumentTest(BaseTest, unittest.TestCase): self.assertEqual(document.file_size, self.file_size) def test_document_to_json(self): - """Test Document.to_json() method""" - print('Testing Document.to_json()') - document = telegram.Document.de_json(self.json_dict) self.assertTrue(self.is_json(document.to_json())) def test_document_to_dict(self): - """Test Document.to_dict() method""" - print('Testing Document.to_dict()') - document = telegram.Document.de_json(self.json_dict) self.assertTrue(self.is_dict(document.to_dict())) @@ -147,9 +136,9 @@ class DocumentTest(BaseTest, unittest.TestCase): self.assertEqual(document['mime_type'], self.mime_type) self.assertEqual(document['file_size'], self.file_size) + @flaky(3, 1) + @timeout(10) def test_error_send_document_empty_file(self): - print('Testing bot.sendDocument - Null file') - json_dict = self.json_dict del(json_dict['file_id']) @@ -159,9 +148,9 @@ class DocumentTest(BaseTest, unittest.TestCase): lambda: self._bot.sendDocument(chat_id=self._chat_id, **json_dict)) + @flaky(3, 1) + @timeout(10) def test_error_send_document_empty_file_id(self): - print('Testing bot.sendDocument - Empty file_id') - json_dict = self.json_dict del(json_dict['file_id']) @@ -171,9 +160,9 @@ class DocumentTest(BaseTest, unittest.TestCase): lambda: self._bot.sendDocument(chat_id=self._chat_id, **json_dict)) + @flaky(3, 1) + @timeout(10) def test_error_document_without_required_args(self): - print('Testing bot.sendDocument - Without required arguments') - json_dict = self.json_dict del(json_dict['file_id']) diff --git a/tests/test_emoji.py b/tests/test_emoji.py index c8245af44..d15bd2747 100644 --- a/tests/test_emoji.py +++ b/tests/test_emoji.py @@ -33,9 +33,6 @@ class EmojiTest(BaseTest, unittest.TestCase): """This object represents Tests for Telegram Emoji.""" def test_emoji(self): - """Test Emoji class""" - print('Testing Emoji class') - for attr in dir(Emoji): if attr[0] != '_': # TODO: dirty way to filter out functions self.assertTrue(type(getattr(Emoji, attr)) is str) diff --git a/tests/test_file.py b/tests/test_file.py index 97864bd61..3347c99ad 100644 --- a/tests/test_file.py +++ b/tests/test_file.py @@ -46,9 +46,6 @@ class FileTest(BaseTest, unittest.TestCase): } def test_get_and_download_file_audio(self): - """Test telegram.Bot getFile method - Audio""" - print('Testing bot.getFile - With Audio.file_id') - newFile = self._bot.getFile(self.audio_file_id) self.assertEqual(newFile.file_size, 28232) @@ -60,9 +57,6 @@ class FileTest(BaseTest, unittest.TestCase): self.assertTrue(os.path.isfile('telegram.mp3')) def test_get_and_download_file_document(self): - """Test telegram.Bot getFile method - Document""" - print('Testing bot.getFile - With Document.file_id') - newFile = self._bot.getFile(self.document_file_id) self.assertEqual(newFile.file_size, 12948) @@ -74,9 +68,6 @@ class FileTest(BaseTest, unittest.TestCase): self.assertTrue(os.path.isfile('telegram.png')) def test_get_and_download_file_sticker(self): - """Test telegram.Bot getFile method - Sticker""" - print('Testing bot.getFile - With Sticker.file_id') - newFile = self._bot.getFile(self.sticker_file_id) self.assertEqual(newFile.file_size, 39518) @@ -88,9 +79,6 @@ class FileTest(BaseTest, unittest.TestCase): self.assertTrue(os.path.isfile('telegram.webp')) def test_get_and_download_file_video(self): - """Test telegram.Bot getFile method - Video""" - print('Testing bot.getFile - With Video.file_id') - newFile = self._bot.getFile(self.video_file_id) self.assertEqual(newFile.file_size, 326534) @@ -102,9 +90,6 @@ class FileTest(BaseTest, unittest.TestCase): self.assertTrue(os.path.isfile('telegram.mp4')) def test_get_and_download_file_voice(self): - """Test telegram.Bot getFile method - Voice""" - print('Testing bot.getFile - With Voice.file_id') - newFile = self._bot.getFile(self.voice_file_id) self.assertEqual(newFile.file_size, 9199) @@ -116,9 +101,6 @@ class FileTest(BaseTest, unittest.TestCase): self.assertTrue(os.path.isfile('telegram.ogg')) def test_file_de_json(self): - """Test File.de_json() method""" - print('Testing File.de_json()') - newFile = telegram.File.de_json(self.json_dict) self.assertEqual(newFile.file_id, self.json_dict['file_id']) @@ -126,17 +108,11 @@ class FileTest(BaseTest, unittest.TestCase): self.assertEqual(newFile.file_size, self.json_dict['file_size']) def test_file_to_json(self): - """Test File.to_json() method""" - print('Testing File.to_json()') - newFile = telegram.File.de_json(self.json_dict) self.assertTrue(self.is_json(newFile.to_json())) def test_file_to_dict(self): - """Test File.to_dict() method""" - print('Testing File.to_dict()') - newFile = telegram.File.de_json(self.json_dict) self.assertTrue(self.is_dict(newFile.to_dict())) @@ -145,10 +121,7 @@ class FileTest(BaseTest, unittest.TestCase): self.assertEqual(newFile['file_size'], self.json_dict['file_size']) def test_error_get_empty_file_id(self): - print('Testing bot.getFile - Null file_id') - json_dict = self.json_dict - json_dict['file_id'] = '' del(json_dict['file_path']) del(json_dict['file_size']) @@ -157,8 +130,6 @@ class FileTest(BaseTest, unittest.TestCase): lambda: self._bot.getFile(**json_dict)) def test_error_file_without_required_args(self): - print('Testing bot.getFile - Without required arguments') - json_dict = self.json_dict del(json_dict['file_id']) diff --git a/tests/test_force_replay.py b/tests/test_force_replay.py index e0d904f99..dddefb756 100644 --- a/tests/test_force_replay.py +++ b/tests/test_force_replay.py @@ -41,9 +41,6 @@ class ForceReplyTest(BaseTest, unittest.TestCase): } def test_send_message_with_force_reply(self): - """Test telegram.Bot sendMessage method with ForceReply""" - print('Testing bot.sendMessage - with ForceReply') - message = self._bot.sendMessage(self._chat_id, 'Моё судно на воздушной подушке полно угрей', reply_markup=telegram.ForceReply.de_json(self.json_dict)) @@ -52,26 +49,17 @@ class ForceReplyTest(BaseTest, unittest.TestCase): self.assertEqual(message.text, u'Моё судно на воздушной подушке полно угрей') def test_force_reply_de_json(self): - """Test ForceReply.de_json() method""" - print('Testing ForceReply.de_json()') - force_reply = telegram.ForceReply.de_json(self.json_dict) self.assertEqual(force_reply.force_reply, self.force_reply) self.assertEqual(force_reply.selective, self.selective) def test_force_reply_to_json(self): - """Test ForceReply.to_json() method""" - print('Testing ForceReply.to_json()') - force_reply = telegram.ForceReply.de_json(self.json_dict) self.assertTrue(self.is_json(force_reply.to_json())) def test_force_reply_to_dict(self): - """Test ForceReply.to_dict() method""" - print('Testing ForceReply.to_dict()') - force_reply = telegram.ForceReply.de_json(self.json_dict) self.assertEqual(force_reply['force_reply'], self.force_reply) diff --git a/tests/test_inlinequery.py b/tests/test_inlinequery.py new file mode 100644 index 000000000..df08998f0 --- /dev/null +++ b/tests/test_inlinequery.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2016 +# Leandro Toledo de Souza +# +# 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 a object that represents Tests for Telegram +InlineQuery""" + +import sys + +if sys.version_info[0:2] == (2, 6): + import unittest2 as unittest +else: + import unittest + +sys.path.append('.') + +import telegram +from tests.base import BaseTest + + +class InlineQueryTest(BaseTest, unittest.TestCase): + """This object represents Tests for Telegram InlineQuery.""" + + def setUp(self): + + user = telegram.User(1, 'First name') + + self.id = 'id' + self.from_user = user + self.query = 'query text' + self.offset = 'offset' + + self.json_dict = { + 'id': self.id, + 'from': self.from_user.to_dict(), + 'query': self.query, + 'offset': self.offset + } + + def test_inlinequery_de_json(self): + inlinequery = telegram.InlineQuery.de_json(self.json_dict) + + self.assertEqual(inlinequery.id, self.id) + self.assertDictEqual(inlinequery.from_user.to_dict(), + self.from_user.to_dict()) + self.assertEqual(inlinequery.query, self.query) + self.assertEqual(inlinequery.offset, self.offset) + + def test_inlinequery_to_json(self): + inlinequery = telegram.InlineQuery.de_json(self.json_dict) + + self.assertTrue(self.is_json(inlinequery.to_json())) + + def test_inlinequery_to_dict(self): + inlinequery = telegram.InlineQuery.de_json(self.json_dict).to_dict() + + self.assertTrue(self.is_dict(inlinequery)) + self.assertDictEqual(inlinequery, self.json_dict) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_inlineresult.py b/tests/test_inlineresult.py new file mode 100644 index 000000000..5e73189f4 --- /dev/null +++ b/tests/test_inlineresult.py @@ -0,0 +1,346 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2016 +# Leandro Toledo de Souza +# +# 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 a object that represents Tests for Telegram +InlineResults""" + +import sys + +if sys.version_info[0:2] == (2, 6): + import unittest2 as unittest +else: + import unittest + +sys.path.append('.') + +import telegram +from tests.base import BaseTest + + +class InlineQueryResultArticleTest(BaseTest, unittest.TestCase): + """This object represents Tests for Telegram InlineQueryResultArticle.""" + + def setUp(self): + self.id = 'id' + self.type = 'article' + self.title = 'title' + self.message_text = 'message text' + self.parse_mode = 'HTML' + self.disable_web_page_preview = True + self.url = 'url' + self.hide_url = True + self.description = 'description' + self.thumb_url = 'thumb url' + self.thumb_height = 10 + self.thumb_width = 15 + + self.json_dict = { + 'type': self.type, + 'id': self.id, + 'title': self.title, + 'message_text': self.message_text, + 'parse_mode': self.parse_mode, + 'disable_web_page_preview': self.disable_web_page_preview, + 'url': self.url, + 'hide_url': self.hide_url, + 'description': self.description, + 'thumb_url': self.thumb_url, + 'thumb_height': self.thumb_height, + 'thumb_width': self.thumb_width + } + + def test_article_de_json(self): + article = telegram.InlineQueryResultArticle.de_json(self.json_dict) + + self.assertEqual(article.type, self.type) + self.assertEqual(article.id, self.id) + self.assertEqual(article.title, self.title) + self.assertEqual(article.message_text, self.message_text) + self.assertEqual(article.parse_mode, self.parse_mode) + self.assertEqual(article.disable_web_page_preview, + self.disable_web_page_preview) + self.assertEqual(article.url, self.url) + self.assertEqual(article.hide_url, self.hide_url) + self.assertEqual(article.description, self.description) + self.assertEqual(article.thumb_url, self.thumb_url) + self.assertEqual(article.thumb_height, self.thumb_height) + self.assertEqual(article.thumb_width, self.thumb_width) + + def test_article_to_json(self): + article = telegram.InlineQueryResultArticle.de_json(self.json_dict) + + self.assertTrue(self.is_json(article.to_json())) + + def test_article_to_dict(self): + article = \ + telegram.InlineQueryResultArticle.de_json(self.json_dict).to_dict() + + self.assertTrue(self.is_dict(article)) + self.assertDictEqual(self.json_dict, article) + + +class InlineQueryResultPhotoTest(BaseTest, unittest.TestCase): + """This object represents Tests for Telegram InlineQueryResultPhoto.""" + + def setUp(self): + self.id = 'id' + self.type = 'photo' + self.photo_url = 'photo url' + self.mime_type = 'mime type' + self.photo_width = 10 + self.photo_height = 15 + self.thumb_url = 'thumb url' + self.title = 'title' + self.caption = 'caption' + self.message_text = 'message text' + self.parse_mode = 'parse mode' + self.disable_web_page_preview = True + + self.json_dict = { + 'type': self.type, + 'id': self.id, + 'photo_url': self.photo_url, + 'mime_type': self.mime_type, + 'photo_width': self.photo_width, + 'photo_height': self.photo_height, + 'thumb_url': self.thumb_url, + 'title': self.title, + 'caption': self.caption, + 'message_text': self.message_text, + 'parse_mode': self.parse_mode, + 'disable_web_page_preview': self.disable_web_page_preview + } + + def test_photo_de_json(self): + photo = telegram.InlineQueryResultPhoto.de_json(self.json_dict) + + self.assertEqual(photo.type, self.type) + self.assertEqual(photo.id, self.id) + self.assertEqual(photo.photo_url, self.photo_url) + self.assertEqual(photo.mime_type, self.mime_type) + self.assertEqual(photo.photo_width, self.photo_width) + self.assertEqual(photo.photo_height, self.photo_height) + self.assertEqual(photo.thumb_url, self.thumb_url) + self.assertEqual(photo.title, self.title) + self.assertEqual(photo.caption, self.caption) + self.assertEqual(photo.message_text, self.message_text) + self.assertEqual(photo.parse_mode, self.parse_mode) + self.assertEqual(photo.disable_web_page_preview, + self.disable_web_page_preview) + + def test_photo_to_json(self): + photo = telegram.InlineQueryResultPhoto.de_json(self.json_dict) + + self.assertTrue(self.is_json(photo.to_json())) + + def test_photo_to_dict(self): + photo = \ + telegram.InlineQueryResultPhoto.de_json(self.json_dict).to_dict() + + self.assertTrue(self.is_dict(photo)) + self.assertDictEqual(self.json_dict, photo) + + +class InlineQueryResultGifTest(BaseTest, unittest.TestCase): + """This object represents Tests for Telegram InlineQueryResultGif.""" + + def setUp(self): + self.id = 'id' + self.type = 'gif' + self.gif_url = 'gif url' + self.gif_width = 10 + self.gif_height = 15 + self.thumb_url = 'thumb url' + self.title = 'title' + self.caption = 'caption' + self.message_text = 'message text' + self.parse_mode = 'parse mode' + self.disable_web_page_preview = True + + self.json_dict = { + 'type': self.type, + 'id': self.id, + 'gif_url': self.gif_url, + 'gif_width': self.gif_width, + 'gif_height': self.gif_height, + 'thumb_url': self.thumb_url, + 'title': self.title, + 'caption': self.caption, + 'message_text': self.message_text, + 'parse_mode': self.parse_mode, + 'disable_web_page_preview': self.disable_web_page_preview + } + + def test_gif_de_json(self): + gif = telegram.InlineQueryResultGif.de_json(self.json_dict) + + self.assertEqual(gif.type, self.type) + self.assertEqual(gif.id, self.id) + 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.thumb_url, self.thumb_url) + self.assertEqual(gif.title, self.title) + self.assertEqual(gif.caption, self.caption) + self.assertEqual(gif.message_text, self.message_text) + self.assertEqual(gif.parse_mode, self.parse_mode) + self.assertEqual(gif.disable_web_page_preview, + self.disable_web_page_preview) + + def test_gif_to_json(self): + gif = telegram.InlineQueryResultGif.de_json(self.json_dict) + + self.assertTrue(self.is_json(gif.to_json())) + + def test_gif_to_dict(self): + gif = telegram.InlineQueryResultGif.de_json(self.json_dict).to_dict() + + self.assertTrue(self.is_dict(gif)) + self.assertDictEqual(self.json_dict, gif) + + +class InlineQueryResultMpeg4GifTest(BaseTest, unittest.TestCase): + """This object represents Tests for Telegram InlineQueryResultMpeg4Gif.""" + + def setUp(self): + self.id = 'id' + self.type = 'mpeg4_gif' + self.mpeg4_url = 'mpeg4 url' + self.mpeg4_width = 10 + self.mpeg4_height = 15 + self.thumb_url = 'thumb url' + self.title = 'title' + self.caption = 'caption' + self.message_text = 'message text' + self.parse_mode = 'parse mode' + self.disable_web_page_preview = True + + self.json_dict = { + 'type': self.type, + 'id': self.id, + 'mpeg4_url': self.mpeg4_url, + 'mpeg4_width': self.mpeg4_width, + 'mpeg4_height': self.mpeg4_height, + 'thumb_url': self.thumb_url, + 'title': self.title, + 'caption': self.caption, + 'message_text': self.message_text, + 'parse_mode': self.parse_mode, + 'disable_web_page_preview': self.disable_web_page_preview + } + + def test_mpeg4_de_json(self): + mpeg4 = telegram.InlineQueryResultMpeg4Gif.de_json(self.json_dict) + + self.assertEqual(mpeg4.type, self.type) + self.assertEqual(mpeg4.id, self.id) + 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.thumb_url, self.thumb_url) + self.assertEqual(mpeg4.title, self.title) + self.assertEqual(mpeg4.caption, self.caption) + self.assertEqual(mpeg4.message_text, self.message_text) + self.assertEqual(mpeg4.parse_mode, self.parse_mode) + self.assertEqual(mpeg4.disable_web_page_preview, + self.disable_web_page_preview) + + def test_mpeg4_to_json(self): + mpeg4 = telegram.InlineQueryResultMpeg4Gif.de_json(self.json_dict) + + self.assertTrue(self.is_json(mpeg4.to_json())) + + def test_mpeg4_to_dict(self): + mpeg4 = \ + telegram.InlineQueryResultMpeg4Gif.de_json(self.json_dict).to_dict() + + self.assertTrue(self.is_dict(mpeg4)) + self.assertDictEqual(self.json_dict, mpeg4) + + +class InlineQueryResultVideoTest(BaseTest, unittest.TestCase): + """This object represents Tests for Telegram InlineQueryResultVideo.""" + + def setUp(self): + self.id = 'id' + self.type = 'video' + self.video_url = 'video url' + self.mime_type = 'mime type' + self.video_width = 10 + self.video_height = 15 + self.video_duration = 15 + self.thumb_url = 'thumb url' + self.title = 'title' + self.caption = 'caption' + self.description = 'description' + self.message_text = 'message text' + self.parse_mode = 'parse mode' + self.disable_web_page_preview = True + + self.json_dict = { + 'type': self.type, + 'id': self.id, + 'video_url': self.video_url, + 'mime_type': self.mime_type, + 'video_width': self.video_width, + 'video_height': self.video_height, + 'video_duration': self.video_duration, + 'thumb_url': self.thumb_url, + 'title': self.title, + 'caption': self.caption, + 'description': self.description, + 'message_text': self.message_text, + 'parse_mode': self.parse_mode, + 'disable_web_page_preview': self.disable_web_page_preview + } + + def test_video_de_json(self): + video = telegram.InlineQueryResultVideo.de_json(self.json_dict) + + self.assertEqual(video.type, self.type) + self.assertEqual(video.id, self.id) + self.assertEqual(video.video_url, self.video_url) + self.assertEqual(video.mime_type, self.mime_type) + self.assertEqual(video.video_width, self.video_width) + self.assertEqual(video.video_height, self.video_height) + self.assertEqual(video.video_duration, self.video_duration) + self.assertEqual(video.thumb_url, self.thumb_url) + self.assertEqual(video.title, self.title) + self.assertEqual(video.description, self.description) + self.assertEqual(video.caption, self.caption) + self.assertEqual(video.message_text, self.message_text) + self.assertEqual(video.parse_mode, self.parse_mode) + self.assertEqual(video.disable_web_page_preview, + self.disable_web_page_preview) + + def test_video_to_json(self): + video = telegram.InlineQueryResultVideo.de_json(self.json_dict) + + self.assertTrue(self.is_json(video.to_json())) + + def test_video_to_dict(self): + video = \ + telegram.InlineQueryResultVideo.de_json(self.json_dict).to_dict() + + self.assertTrue(self.is_dict(video)) + self.assertDictEqual(self.json_dict, video) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_jobqueue.py b/tests/test_jobqueue.py index 0749a6980..c3562f621 100644 --- a/tests/test_jobqueue.py +++ b/tests/test_jobqueue.py @@ -72,19 +72,16 @@ class JobQueueTest(BaseTest, unittest.TestCase): raise Exception("Test Error") def test_basic(self): - print('Testing basic job queue function') self.jq.put(self.job1, 0.1) sleep(1.5) self.assertGreaterEqual(self.result, 10) def test_noRepeat(self): - print('Testing job queue without repeat') self.jq.put(self.job1, 0.1, repeat=False) sleep(0.5) self.assertEqual(1, self.result) def test_nextT(self): - print('Testing job queue with a set next_t value') self.jq.put(self.job1, 0.1, next_t=0.5) sleep(0.45) self.assertEqual(0, self.result) @@ -92,7 +89,6 @@ class JobQueueTest(BaseTest, unittest.TestCase): self.assertEqual(1, self.result) def test_multiple(self): - print('Testing job queue with multiple jobs') self.jq.put(self.job1, 0.1, repeat=False) self.jq.put(self.job1, 0.2, repeat=False) self.jq.put(self.job1, 0.4) @@ -100,7 +96,6 @@ class JobQueueTest(BaseTest, unittest.TestCase): self.assertEqual(4, self.result) def test_error(self): - print('Testing job queue starting twice with an erroneous job') self.jq.put(self.job2, 0.1) self.jq.put(self.job1, 0.2) self.jq.start() @@ -108,10 +103,9 @@ class JobQueueTest(BaseTest, unittest.TestCase): self.assertEqual(1, self.result) def test_inUpdater(self): - print('Testing job queue created by updater') u = Updater(bot="MockBot", job_queue_tick_interval=0.005) - u.job_queue.put(self.job1, 0.1) - sleep(0.15) + u.job_queue.put(self.job1, 0.11) + sleep(0.2) self.assertEqual(1, self.result) u.stop() sleep(0.4) diff --git a/tests/test_location.py b/tests/test_location.py index cdc281af8..b219ed337 100644 --- a/tests/test_location.py +++ b/tests/test_location.py @@ -41,9 +41,6 @@ class LocationTest(BaseTest, unittest.TestCase): } def test_send_location_implicit_args(self): - """Test telegram.Bot sendLocation method""" - print('Testing bot.sendLocation - Implicit arguments') - message = self._bot.sendLocation(self._chat_id, self.latitude, self.longitude) @@ -54,9 +51,6 @@ class LocationTest(BaseTest, unittest.TestCase): self.assertEqual(location.longitude, self.longitude) def test_send_location_explicit_args(self): - """Test telegram.Bot sendLocation method""" - print('Testing bot.sendLocation - Explicit arguments') - message = self._bot.sendLocation(chat_id=self._chat_id, latitude=self.latitude, longitude=self.longitude) @@ -67,34 +61,23 @@ class LocationTest(BaseTest, unittest.TestCase): self.assertEqual(location.longitude, self.longitude) def test_location_de_json(self): - """Test Location.de_json() method""" - print('Testing Location.de_json()') - location = telegram.Location.de_json(self.json_dict) self.assertEqual(location.latitude, self.latitude) self.assertEqual(location.longitude, self.longitude) def test_location_to_json(self): - """Test Location.to_json() method""" - print('Testing Location.to_json()') - location = telegram.Location.de_json(self.json_dict) self.assertTrue(self.is_json(location.to_json())) def test_location_to_dict(self): - """Test Location.to_dict() method""" - print('Testing Location.to_dict()') - location = telegram.Location.de_json(self.json_dict) self.assertEqual(location['latitude'], self.latitude) self.assertEqual(location['longitude'], self.longitude) def test_error_send_location_empty_args(self): - print('Testing bot.sendLocation - Empty arguments') - json_dict = self.json_dict json_dict['latitude'] = '' @@ -105,8 +88,6 @@ class LocationTest(BaseTest, unittest.TestCase): **json_dict)) def test_error_location_without_required_args(self): - print('Testing bot.sendLocation - Without required arguments') - json_dict = self.json_dict del(json_dict['latitude']) diff --git a/tests/test_parse_mode.py b/tests/test_parse_mode.py index 7c77219c9..b27117049 100644 --- a/tests/test_parse_mode.py +++ b/tests/test_parse_mode.py @@ -36,8 +36,6 @@ class ParseMode(BaseTest, unittest.TestCase): self.formatted_text_formatted = u'bold italic link.' def test_send_message_with_parse_mode_markdown(self): - '''Test the telegram.Bot sendMessage method with markdown parse mode''' - print('Testing sendMessage - with markdown parsemode') message = self._bot.sendMessage(chat_id=self._chat_id, text=self.markdown_text, parse_mode=telegram.ParseMode.MARKDOWN) @@ -46,8 +44,6 @@ class ParseMode(BaseTest, unittest.TestCase): self.assertEqual(message.text, self.formatted_text_formatted) def test_send_message_with_parse_mode_html(self): - '''Test the telegram.Bot sendMessage method with html parse mode''' - print('Testing sendMessage - with html parse mode') message = self._bot.sendMessage(chat_id=self._chat_id, text=self.html_text, parse_mode=telegram.ParseMode.HTML) @@ -56,4 +52,4 @@ class ParseMode(BaseTest, unittest.TestCase): self.assertEqual(message.text, self.formatted_text_formatted) if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/test_photo.py b/tests/test_photo.py index 0cb71309f..13d0d0e55 100644 --- a/tests/test_photo.py +++ b/tests/test_photo.py @@ -22,10 +22,12 @@ import os import unittest import sys +from flaky import flaky + sys.path.append('.') import telegram -from tests.base import BaseTest +from tests.base import BaseTest, timeout class PhotoTest(BaseTest, unittest.TestCase): @@ -53,10 +55,9 @@ class PhotoTest(BaseTest, unittest.TestCase): 'file_size': self.file_size } + @flaky(3, 1) + @timeout(10) def test_sendphotoo_all_args(self): - """Test telegram.Bot sendAudio method""" - print('Testing bot.sendPhoto - With all arguments') - message = self._bot.sendPhoto(self._chat_id, self.photo_file, caption=self.caption) @@ -79,10 +80,9 @@ class PhotoTest(BaseTest, unittest.TestCase): self.assertEqual(message.caption, self.caption) + @flaky(3, 1) + @timeout(10) def test_send_photo_jpg_file(self): - """Test telegram.Bot sendPhoto method""" - print('Testing bot.sendPhoto - JPG File') - message = self._bot.sendPhoto(self._chat_id, self.photo_file) @@ -102,10 +102,9 @@ class PhotoTest(BaseTest, unittest.TestCase): self.assertEqual(photo.height, self.height) self.assertEqual(photo.file_size, self.file_size) + @flaky(3, 1) + @timeout(10) def test_send_photo_url_jpg_file(self): - """Test telegram.Bot sendPhoto method""" - print('Testing bot.sendPhoto - JPG File by URL') - message = self._bot.sendPhoto(self._chat_id, self.photo_file_url) @@ -125,10 +124,9 @@ class PhotoTest(BaseTest, unittest.TestCase): self.assertEqual(photo.height, self.height) self.assertEqual(photo.file_size, self.file_size) + @flaky(3, 1) + @timeout(10) def test_send_photo_resend(self): - """Test telegram.Bot sendPhoto method""" - print('Testing bot.sendPhoto - Resend by file_id') - message = self._bot.sendPhoto(chat_id=self._chat_id, photo=self.photo_file_id) @@ -145,9 +143,6 @@ class PhotoTest(BaseTest, unittest.TestCase): self.assertEqual(photo.height, self.height) def test_photo_de_json(self): - """Test Photo.de_json() method""" - print('Testing Photo.de_json()') - photo = telegram.PhotoSize.de_json(self.json_dict) self.assertEqual(photo.file_id, self.photo_file_id) @@ -157,17 +152,11 @@ class PhotoTest(BaseTest, unittest.TestCase): self.assertEqual(photo.file_size, self.file_size) def test_photo_to_json(self): - """Test Photo.to_json() method""" - print('Testing Photo.to_json()') - photo = telegram.PhotoSize.de_json(self.json_dict) self.assertTrue(self.is_json(photo.to_json())) def test_photo_to_dict(self): - """Test Photo.to_dict() method""" - print('Testing Photo.to_dict()') - photo = telegram.PhotoSize.de_json(self.json_dict) self.assertTrue(self.is_dict(photo.to_dict())) @@ -177,9 +166,9 @@ class PhotoTest(BaseTest, unittest.TestCase): self.assertEqual(photo['height'], self.height) self.assertEqual(photo['file_size'], self.file_size) + @flaky(3, 1) + @timeout(10) def test_error_send_photo_empty_file(self): - print('Testing bot.sendPhoto - Null file') - json_dict = self.json_dict del(json_dict['file_id']) @@ -189,9 +178,9 @@ class PhotoTest(BaseTest, unittest.TestCase): lambda: self._bot.sendPhoto(chat_id=self._chat_id, **json_dict)) + @flaky(3, 1) + @timeout(10) def test_error_send_photo_empty_file_id(self): - print('Testing bot.sendPhoto - Empty file_id') - json_dict = self.json_dict del(json_dict['file_id']) @@ -201,9 +190,9 @@ class PhotoTest(BaseTest, unittest.TestCase): lambda: self._bot.sendPhoto(chat_id=self._chat_id, **json_dict)) + @flaky(3, 1) + @timeout(10) def test_error_photo_without_required_args(self): - print('Testing bot.sendPhoto - Without required arguments') - json_dict = self.json_dict del(json_dict['file_id']) diff --git a/tests/test_reply_keyboard_hide.py b/tests/test_reply_keyboard_hide.py index 4ffca8840..1fb1e4042 100644 --- a/tests/test_reply_keyboard_hide.py +++ b/tests/test_reply_keyboard_hide.py @@ -41,9 +41,6 @@ class ReplyKeyboardHideTest(BaseTest, unittest.TestCase): } def test_send_message_with_reply_keyboard_hide(self): - """Test telegram.Bot sendMessage method with ReplyKeyboardHide""" - print('Testing bot.sendMessage - with ReplyKeyboardHide') - message = self._bot.sendMessage(self._chat_id, 'Моё судно на воздушной подушке полно угрей', reply_markup=telegram.ReplyKeyboardHide.de_json(self.json_dict)) @@ -52,26 +49,17 @@ class ReplyKeyboardHideTest(BaseTest, unittest.TestCase): self.assertEqual(message.text, u'Моё судно на воздушной подушке полно угрей') def test_reply_keyboard_hide_de_json(self): - """Test ReplyKeboardHide.de_json() method""" - print('Testing ReplyKeyboardHide.de_json()') - reply_keyboard_hide = telegram.ReplyKeyboardHide.de_json(self.json_dict) self.assertEqual(reply_keyboard_hide.hide_keyboard, self.hide_keyboard) self.assertEqual(reply_keyboard_hide.selective, self.selective) def test_reply_keyboard_hide_to_json(self): - """Test ReplyKeyboardHide.to_json() method""" - print('Testing ReplyKeyboardHide.to_json()') - reply_keyboard_hide = telegram.ReplyKeyboardHide.de_json(self.json_dict) self.assertTrue(self.is_json(reply_keyboard_hide.to_json())) def test_reply_keyboard_hide_to_dict(self): - """Test ReplyKeyboardHide.to_dict() method""" - print('Testing ReplyKeyboardHide.to_dict()') - reply_keyboard_hide = telegram.ReplyKeyboardHide.de_json(self.json_dict) self.assertEqual(reply_keyboard_hide['hide_keyboard'], self.hide_keyboard) diff --git a/tests/test_reply_keyboard_markup.py b/tests/test_reply_keyboard_markup.py index 90b83c530..02fa1eba0 100644 --- a/tests/test_reply_keyboard_markup.py +++ b/tests/test_reply_keyboard_markup.py @@ -45,9 +45,6 @@ class ReplyKeyboardMarkupTest(BaseTest, unittest.TestCase): } def test_send_message_with_reply_keyboard_markup(self): - """Test telegram.Bot sendMessage method with ReplyKeyboardMarkup""" - print('Testing bot.sendMessage - with ReplyKeyboardMarkup') - message = self._bot.sendMessage(self._chat_id, 'Моё судно на воздушной подушке полно угрей', reply_markup=telegram.ReplyKeyboardMarkup.de_json(self.json_dict)) @@ -56,9 +53,6 @@ class ReplyKeyboardMarkupTest(BaseTest, unittest.TestCase): self.assertEqual(message.text, u'Моё судно на воздушной подушке полно угрей') def test_reply_keyboard_markup_de_json(self): - """Test ReplyKeboardMarkup.de_json() method""" - print('Testing ReplyKeyboardMarkup.de_json()') - reply_keyboard_markup = telegram.ReplyKeyboardMarkup.de_json(self.json_dict) self.assertEqual(reply_keyboard_markup.keyboard, self.keyboard) @@ -67,17 +61,11 @@ class ReplyKeyboardMarkupTest(BaseTest, unittest.TestCase): self.assertEqual(reply_keyboard_markup.selective, self.selective) def test_reply_keyboard_markup_to_json(self): - """Test ReplyKeyboardMarkup.to_json() method""" - print('Testing ReplyKeyboardMarkup.to_json()') - reply_keyboard_markup = telegram.ReplyKeyboardMarkup.de_json(self.json_dict) self.assertTrue(self.is_json(reply_keyboard_markup.to_json())) def test_reply_keyboard_markup_to_dict(self): - """Test ReplyKeyboardMarkup.to_dict() method""" - print('Testing ReplyKeyboardMarkup.to_dict()') - reply_keyboard_markup = telegram.ReplyKeyboardMarkup.de_json(self.json_dict) self.assertEqual(reply_keyboard_markup['keyboard'], self.keyboard) diff --git a/tests/test_sticker.py b/tests/test_sticker.py index aafa0b23c..6f766df3e 100644 --- a/tests/test_sticker.py +++ b/tests/test_sticker.py @@ -22,10 +22,12 @@ import os import unittest import sys +from flaky import flaky + sys.path.append('.') import telegram -from tests.base import BaseTest +from tests.base import BaseTest, timeout class StickerTest(BaseTest, unittest.TestCase): @@ -49,13 +51,14 @@ class StickerTest(BaseTest, unittest.TestCase): 'file_size': self.file_size } + @flaky(3, 1) + @timeout(10) def test_send_sticker_file(self): pass + @flaky(3, 1) + @timeout(10) def test_send_sticker_resend(self): - """Test telegram.Bot sendSticker method""" - print('Testing bot.sendSticker - Resend by file_id') - message = self._bot.sendSticker(chat_id=self._chat_id, sticker=self.sticker_file_id) @@ -68,9 +71,6 @@ class StickerTest(BaseTest, unittest.TestCase): self.assertEqual(sticker.file_size, self.file_size) def test_sticker_de_json(self): - """Test Sticker.de_json() method""" - print('Testing Sticker.de_json()') - sticker = telegram.Sticker.de_json(self.json_dict) self.assertEqual(sticker.file_id, self.sticker_file_id) @@ -80,17 +80,11 @@ class StickerTest(BaseTest, unittest.TestCase): self.assertEqual(sticker.file_size, self.file_size) def test_sticker_to_json(self): - """Test Sticker.to_json() method""" - print('Testing Sticker.to_json()') - sticker = telegram.Sticker.de_json(self.json_dict) self.assertTrue(self.is_json(sticker.to_json())) def test_sticker_to_dict(self): - """Test Sticker.to_dict() method""" - print('Testing Sticker.to_dict()') - sticker = telegram.Sticker.de_json(self.json_dict) self.assertEqual(sticker['file_id'], self.sticker_file_id) @@ -99,9 +93,9 @@ class StickerTest(BaseTest, unittest.TestCase): self.assertTrue(isinstance(sticker['thumb'], telegram.PhotoSize)) self.assertEqual(sticker['file_size'], self.file_size) + @flaky(3, 1) + @timeout(10) def test_error_send_sticker_empty_file(self): - print('Testing bot.sendSticker - Null file') - json_dict = self.json_dict del(json_dict['file_id']) @@ -111,9 +105,9 @@ class StickerTest(BaseTest, unittest.TestCase): lambda: self._bot.sendSticker(chat_id=self._chat_id, **json_dict)) + @flaky(3, 1) + @timeout(10) def test_error_send_sticker_empty_file_id(self): - print('Testing bot.sendSticker - Empty file_id') - json_dict = self.json_dict del(json_dict['file_id']) @@ -123,9 +117,9 @@ class StickerTest(BaseTest, unittest.TestCase): lambda: self._bot.sendSticker(chat_id=self._chat_id, **json_dict)) + @flaky(3, 1) + @timeout(10) def test_error_sticker_without_required_args(self): - print('Testing bot.sendSticker - Without required arguments') - json_dict = self.json_dict del(json_dict['file_id']) diff --git a/tests/test_update.py b/tests/test_update.py index c03c53a8a..2533985dc 100644 --- a/tests/test_update.py +++ b/tests/test_update.py @@ -52,26 +52,17 @@ class UpdateTest(BaseTest, unittest.TestCase): } def test_update_de_json(self): - """Test Update.de_json() method""" - print('Testing Update.de_json()') - update = telegram.Update.de_json(self.json_dict) self.assertEqual(update.update_id, self.update_id) self.assertTrue(isinstance(update.message, telegram.Message)) def test_update_to_json(self): - """Test Update.to_json() method""" - print('Testing Update.to_json()') - update = telegram.Update.de_json(self.json_dict) self.assertTrue(self.is_json(update.to_json())) def test_update_to_dict(self): - """Test Update.to_dict() method""" - print('Testing Update.to_dict()') - update = telegram.Update.de_json(self.json_dict) self.assertTrue(self.is_dict(update.to_dict())) diff --git a/tests/test_updater.py b/tests/test_updater.py index bcbb69467..5e26b6825 100644 --- a/tests/test_updater.py +++ b/tests/test_updater.py @@ -91,6 +91,11 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.received_message = update.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 + @run_async def asyncHandlerTest(self, bot, update, **kwargs): sleep(1) @@ -136,7 +141,6 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.message_count += 1 def test_addRemoveTelegramMessageHandler(self): - print('Testing add/removeTelegramMessageHandler') self._setup_updater('Test') d = self.updater.dispatcher d.addTelegramMessageHandler( @@ -154,7 +158,6 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertTrue(None is self.received_message) def test_addTelegramMessageHandlerMultipleMessages(self): - print('Testing addTelegramMessageHandler and send 100 messages...') self._setup_updater('Multiple', 100) self.updater.dispatcher.addTelegramMessageHandler( self.telegramHandlerTest) @@ -164,7 +167,6 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertEqual(self.message_count, 100) def test_addRemoveTelegramRegexHandler(self): - print('Testing add/removeStringRegexHandler') self._setup_updater('Test2') d = self.updater.dispatcher regobj = re.compile('Te.*') @@ -183,7 +185,6 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertTrue(None is self.received_message) def test_addRemoveTelegramCommandHandler(self): - print('Testing add/removeTelegramCommandHandler') self._setup_updater('/test') d = self.updater.dispatcher self.updater.dispatcher.addTelegramCommandHandler( @@ -201,7 +202,6 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertTrue(None is self.received_message) def test_addRemoveUnknownTelegramCommandHandler(self): - print('Testing add/removeUnknownTelegramCommandHandler') self._setup_updater('/test2') d = self.updater.dispatcher self.updater.dispatcher.addUnknownTelegramCommandHandler( @@ -219,7 +219,6 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertTrue(None is self.received_message) def test_addRemoveStringRegexHandler(self): - print('Testing add/removeStringRegexHandler') self._setup_updater('', messages=0) d = self.updater.dispatcher d.addStringRegexHandler('Te.*', self.stringHandlerTest) @@ -237,7 +236,6 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertTrue(None is self.received_message) def test_addRemoveStringCommandHandler(self): - print('Testing add/removeStringCommandHandler') self._setup_updater('', messages=0) d = self.updater.dispatcher d.addStringCommandHandler( @@ -257,7 +255,6 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertTrue(None is self.received_message) def test_addRemoveUnknownStringCommandHandler(self): - print('Testing add/removeUnknownStringCommandHandler') self._setup_updater('/test') d = self.updater.dispatcher d.addUnknownStringCommandHandler( @@ -276,7 +273,6 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertTrue(None is self.received_message) def test_addRemoveErrorHandler(self): - print('Testing add/removeErrorHandler') self._setup_updater('', messages=0) d = self.updater.dispatcher d.addErrorHandler(self.errorHandlerTest) @@ -295,11 +291,9 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertTrue(None is self.received_message) def test_errorInHandler(self): - print('Testing error in Handler') self._setup_updater('', messages=0) d = self.updater.dispatcher - d.addStringRegexHandler('.*', - self.errorRaisingHandlerTest) + d.addStringRegexHandler('.*', self.errorRaisingHandlerTest) self.updater.dispatcher.addErrorHandler(self.errorHandlerTest) queue = self.updater.start_polling(0.01) @@ -308,7 +302,6 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertEqual(self.received_message, 'Test Error 1') def test_errorOnGetUpdates(self): - print('Testing error on getUpdates') self._setup_updater('', raise_error=True) d = self.updater.dispatcher d.addErrorHandler(self.errorHandlerTest) @@ -317,7 +310,6 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertEqual(self.received_message, "Test Error 2") def test_addRemoveTypeHandler(self): - print('Testing add/removeTypeHandler') self._setup_updater('', messages=0) d = self.updater.dispatcher d.addTypeHandler(dict, self.stringHandlerTest) @@ -335,8 +327,31 @@ class UpdaterTest(BaseTest, unittest.TestCase): sleep(.1) self.assertTrue(None is self.received_message) + def test_addRemoveInlineHandlerQuery(self): + print('Testing add/removeInlineHandler') + self._setup_updater('', messages=0) + d = self.updater.dispatcher + d.addTelegramInlineHandler(self.telegramInlineHandlerTest) + queue = self.updater.start_polling(0.01) + update = Update(update_id=0, inline_query="testquery") + update2 = Update(update_id=0, chosen_inline_result="testresult") + queue.put(update) + sleep(.1) + self.assertEqual(self.received_message[0], "testquery") + + queue.put(update2) + sleep(.1) + self.assertEqual(self.received_message[1], "testresult") + + # Remove handler + d.removeTelegramInlineHandler(self.telegramInlineHandlerTest) + self.reset() + + queue.put(update) + sleep(.1) + self.assertTrue(None is self.received_message) + def test_runAsync(self): - print('Testing @run_async') self._setup_updater('Test5', messages=2) d = self.updater.dispatcher d.addTelegramMessageHandler( @@ -347,7 +362,6 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertEqual(self.message_count, 2) def test_additionalArgs(self): - print('Testing additional arguments for handlers') self._setup_updater('', messages=0) self.updater.dispatcher.addStringCommandHandler( 'test5', self.additionalArgsTest) @@ -359,7 +373,6 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertEqual(self.message_count, 2) def test_context(self): - print('Testing context for handlers') context = "context_data" self._setup_updater('', messages=0) self.updater.dispatcher.addStringCommandHandler( @@ -373,7 +386,6 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertEqual(self.context, context) def test_regexGroupHandler(self): - print('Testing optional groups and groupdict parameters') self._setup_updater('', messages=0) d = self.updater.dispatcher d.addStringRegexHandler('^(This).*?(?Pregex group).*', @@ -386,7 +398,6 @@ class UpdaterTest(BaseTest, unittest.TestCase): def test_runAsyncWithAdditionalArgs(self): - print('Testing @run_async with additional parameters') self._setup_updater('Test6', messages=2) d = self.updater.dispatcher d.addTelegramMessageHandler( @@ -397,7 +408,6 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertEqual(self.message_count, 2) def test_webhook(self): - print('Testing Webhook') self._setup_updater('', messages=0) d = self.updater.dispatcher d.addTelegramMessageHandler( @@ -443,7 +453,6 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertTrue(True) def test_webhook_no_ssl(self): - print('Testing Webhook without SSL') self._setup_updater('', messages=0) d = self.updater.dispatcher d.addTelegramMessageHandler( @@ -539,14 +548,13 @@ class UpdaterTest(BaseTest, unittest.TestCase): os.kill(os.getpid(), signal.SIGTERM) def test_idle(self): - print('Testing idle') self._setup_updater('Test6', messages=0) self.updater.start_polling(poll_interval=0.01) Thread(target=self.signalsender).start() self.updater.idle() # If we get this far, idle() ran through sleep(1) - self.updater.running = False + self.assertFalse(self.updater.running) def test_createBot(self): updater = Updater('123:abcd') diff --git a/tests/test_user.py b/tests/test_user.py index 855ab08b9..bbafe39a9 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -47,9 +47,6 @@ class UserTest(BaseTest, unittest.TestCase): } def test_user_de_json(self): - """Test User.de_json() method""" - print('Testing User.de_json()') - user = telegram.User.de_json(self.json_dict) self.assertEqual(user.id, self.id) @@ -61,9 +58,6 @@ class UserTest(BaseTest, unittest.TestCase): self.assertEqual(user.name, '@leandrotoledo') def test_user_de_json_without_username(self): - """Test User.de_json() method""" - print('Testing User.de_json() - Without username') - json_dict = self.json_dict del(json_dict['username']) @@ -79,9 +73,6 @@ class UserTest(BaseTest, unittest.TestCase): def test_user_de_json_without_username_and_lastname(self): - """Test User.de_json() method""" - print('Testing User.de_json() - Without username and last_name') - json_dict = self.json_dict del(json_dict['username']) @@ -95,17 +86,11 @@ class UserTest(BaseTest, unittest.TestCase): self.assertEqual(user.name, self.first_name) def test_user_to_json(self): - """Test User.to_json() method""" - print('Testing User.to_json()') - user = telegram.User.de_json(self.json_dict) self.assertTrue(self.is_json(user.to_json())) def test_user_to_dict(self): - """Test User.to_dict() method""" - print('Testing User.to_dict()') - user = telegram.User.de_json(self.json_dict) self.assertTrue(self.is_dict(user.to_dict())) diff --git a/tests/test_video.py b/tests/test_video.py index 4e0a1dad7..fc9dd92be 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -22,10 +22,12 @@ import os import unittest import sys +from flaky import flaky + sys.path.append('.') import telegram -from tests.base import BaseTest +from tests.base import BaseTest, timeout class VideoTest(BaseTest, unittest.TestCase): @@ -37,8 +39,12 @@ class VideoTest(BaseTest, unittest.TestCase): self.video_file_url = 'https://raw.githubusercontent.com/python-telegram-bot/python-telegram-bot/master/tests/data/telegram.mp4' self.width = 360 self.height = 640 - self.duration = 4 - self.thumb = telegram.PhotoSize.de_json({}) + self.duration = 5 + self.thumb = telegram.PhotoSize.de_json( + {'file_id': 'AAQBABOMsecvAAQqqoY1Pee_MqcyAAIC', + 'file_size': 645, + 'height': 90, + 'width': 51}) self.mime_type = 'video/mp4' self.file_size = 326534 @@ -50,35 +56,35 @@ class VideoTest(BaseTest, unittest.TestCase): 'width': self.width, 'height': self.height, 'duration': self.duration, - 'thumb': self.thumb, + 'thumb': self.thumb.to_dict(), 'mime_type': self.mime_type, 'file_size': self.file_size } + @flaky(3, 1) + @timeout(10) def test_send_video_required_args_only(self): - """Test telegram.Bot sendVideo method""" - print('Testing bot.sendVideo - With required arguments only') - - message = self._bot.sendVideo(self._chat_id, - self.video_file) - - video = message.video - - self.assertTrue(isinstance(video.file_id, str)) - self.assertNotEqual(video.file_id, '') - self.assertEqual(video.width, 0) - self.assertEqual(video.height, 0) - self.assertEqual(video.duration, 0) - self.assertEqual(video.thumb, None) - self.assertEqual(video.mime_type, '') - self.assertEqual(video.file_size, self.file_size) - - def test_send_video_all_args(self): - """Test telegram.Bot sendAudio method""" - print('Testing bot.sendVideo - With all arguments') - message = self._bot.sendVideo(self._chat_id, self.video_file, + timeout=10) + + video = message.video + + self.assertTrue(isinstance(video.file_id, str)) + self.assertNotEqual(video.file_id, '') + self.assertEqual(video.width, self.width) + self.assertEqual(video.height, self.height) + self.assertEqual(video.duration, self.duration) + self.assertEqual(video.thumb, self.thumb) + self.assertEqual(video.mime_type, '') + self.assertEqual(video.file_size, self.file_size) + + @flaky(3, 1) + @timeout(10) + def test_send_video_all_args(self): + message = self._bot.sendVideo(self._chat_id, + self.video_file, + timeout=10, duration=self.duration, caption=self.caption) @@ -86,21 +92,21 @@ class VideoTest(BaseTest, unittest.TestCase): self.assertTrue(isinstance(video.file_id, str)) self.assertNotEqual(video.file_id, '') - self.assertEqual(video.width, 0) - self.assertEqual(video.height, 0) + self.assertEqual(video.width, self.width) + self.assertEqual(video.height, self.height) self.assertEqual(video.duration, self.duration) - self.assertEqual(video.thumb, None) + self.assertEqual(video.thumb, self.thumb) self.assertEqual(video.mime_type, '') self.assertEqual(video.file_size, self.file_size) self.assertEqual(message.caption, self.caption) + @flaky(3, 1) + @timeout(10) def test_send_video_mp4_file(self): - """Test telegram.Bot sendVideo method""" - print('Testing bot.sendVideo - MP4 File') - message = self._bot.sendVideo(chat_id=self._chat_id, video=self.video_file, + timeout=10, duration=self.duration, caption=self.caption) @@ -108,21 +114,21 @@ class VideoTest(BaseTest, unittest.TestCase): self.assertTrue(isinstance(video.file_id, str)) self.assertNotEqual(video.file_id, '') - self.assertEqual(video.width, 0) - self.assertEqual(video.height, 0) + self.assertEqual(video.width, self.width) + self.assertEqual(video.height, self.height) self.assertEqual(video.duration, self.duration) - self.assertEqual(video.thumb, None) + self.assertEqual(video.thumb, self.thumb) self.assertEqual(video.mime_type, '') self.assertEqual(video.file_size, self.file_size) self.assertEqual(message.caption, self.caption) + @flaky(3, 1) + @timeout(10) def test_send_video_mp4_file_with_custom_filename(self): - """Test telegram.Bot sendVideo method""" - print('Testing bot.sendVideo - MP4 File with custom filename') - message = self._bot.sendVideo(chat_id=self._chat_id, video=self.video_file, + timeout=10, duration=self.duration, caption=self.caption, filename='telegram_custom.mp4') @@ -131,21 +137,21 @@ class VideoTest(BaseTest, unittest.TestCase): self.assertTrue(isinstance(video.file_id, str)) self.assertNotEqual(video.file_id, '') - self.assertEqual(video.width, 0) - self.assertEqual(video.height, 0) + self.assertEqual(video.width, self.width) + self.assertEqual(video.height, self.height) self.assertEqual(video.duration, self.duration) - self.assertEqual(video.thumb, None) + self.assertEqual(video.thumb, self.thumb) self.assertEqual(video.mime_type, '') self.assertEqual(video.file_size, self.file_size) self.assertEqual(message.caption, self.caption) + @flaky(3, 1) + @timeout(10) def test_send_video_mp4_file_url(self): - """Test telegram.Bot sendVideo method""" - print('Testing bot.sendVideo - MP4 File by URL') - message = self._bot.sendVideo(chat_id=self._chat_id, video=self.video_file_url, + timeout=10, duration=self.duration, caption=self.caption) @@ -153,21 +159,20 @@ class VideoTest(BaseTest, unittest.TestCase): self.assertTrue(isinstance(video.file_id, str)) self.assertNotEqual(video.file_id, '') - self.assertEqual(video.width, 0) - self.assertEqual(video.height, 0) + self.assertEqual(video.height, self.height) self.assertEqual(video.duration, self.duration) - self.assertEqual(video.thumb, None) + self.assertEqual(video.thumb, self.thumb) self.assertEqual(video.mime_type, '') self.assertEqual(video.file_size, self.file_size) self.assertEqual(message.caption, self.caption) + @flaky(3, 1) + @timeout(10) def test_send_video_resend(self): - """Test telegram.Bot sendVideo method""" - print('Testing bot.sendVideo - Resend by file_id') - message = self._bot.sendVideo(chat_id=self._chat_id, video=self.video_file_id, + timeout=10, duration=self.duration, caption=self.caption) @@ -181,31 +186,22 @@ class VideoTest(BaseTest, unittest.TestCase): self.assertEqual(message.caption, self.caption) def test_video_de_json(self): - """Test Video.de_json() method""" - print('Testing Video.de_json()') - video = telegram.Video.de_json(self.json_dict) self.assertEqual(video.file_id, self.video_file_id) self.assertEqual(video.width, self.width) self.assertEqual(video.height, self.height) self.assertEqual(video.duration, self.duration) - self.assertEqual(video.thumb, None) + self.assertEqual(video.thumb, self.thumb) self.assertEqual(video.mime_type, self.mime_type) self.assertEqual(video.file_size, self.file_size) def test_video_to_json(self): - """Test Video.to_json() method""" - print('Testing Video.to_json()') - video = telegram.Video.de_json(self.json_dict) self.assertTrue(self.is_json(video.to_json())) def test_video_to_dict(self): - """Test Video.to_dict() method""" - print('Testing Video.to_dict()') - video = telegram.Video.de_json(self.json_dict) self.assertTrue(self.is_dict(video.to_dict())) @@ -216,9 +212,9 @@ class VideoTest(BaseTest, unittest.TestCase): self.assertEqual(video['mime_type'], self.mime_type) self.assertEqual(video['file_size'], self.file_size) + @flaky(3, 1) + @timeout(10) def test_error_send_video_empty_file(self): - print('Testing bot.sendVideo - Null file') - json_dict = self.json_dict del(json_dict['file_id']) @@ -226,11 +222,12 @@ class VideoTest(BaseTest, unittest.TestCase): self.assertRaises(telegram.TelegramError, lambda: self._bot.sendVideo(chat_id=self._chat_id, + timeout=10, **json_dict)) + @flaky(3, 1) + @timeout(10) def test_error_send_video_empty_file_id(self): - print('Testing bot.sendVideo - Empty file_id') - json_dict = self.json_dict del(json_dict['file_id']) @@ -238,11 +235,12 @@ class VideoTest(BaseTest, unittest.TestCase): self.assertRaises(telegram.TelegramError, lambda: self._bot.sendVideo(chat_id=self._chat_id, + timeout=10, **json_dict)) + @flaky(3, 1) + @timeout(10) def test_error_video_without_required_args(self): - print('Testing bot.sendVideo - Without required arguments') - json_dict = self.json_dict del(json_dict['file_id']) @@ -250,6 +248,7 @@ class VideoTest(BaseTest, unittest.TestCase): self.assertRaises(TypeError, lambda: self._bot.sendVideo(chat_id=self._chat_id, + timeout=10, **json_dict)) if __name__ == '__main__': diff --git a/tests/test_voice.py b/tests/test_voice.py index cde7340cd..04617ce2b 100644 --- a/tests/test_voice.py +++ b/tests/test_voice.py @@ -22,10 +22,12 @@ import os import unittest import sys +from flaky import flaky + sys.path.append('.') import telegram -from tests.base import BaseTest +from tests.base import BaseTest, timeout class VoiceTest(BaseTest, unittest.TestCase): @@ -35,7 +37,7 @@ class VoiceTest(BaseTest, unittest.TestCase): self.voice_file = open('tests/data/telegram.ogg', 'rb') self.voice_file_id = 'AwADAQADTgADHyP1B_mbw34svXPHAg' self.voice_file_url = 'https://raw.githubusercontent.com/python-telegram-bot/python-telegram-bot/master/tests/data/telegram.ogg' - self.duration = 0 + self.duration = 3 self.mime_type = 'audio/ogg' self.file_size = 9199 @@ -46,10 +48,9 @@ class VoiceTest(BaseTest, unittest.TestCase): 'file_size': self.file_size } + @flaky(3, 1) + @timeout(10) def test_send_voice_required_args_only(self): - """Test telegram.Bot sendVoice method""" - print('Testing bot.sendVoice - With required arguments only') - message = self._bot.sendVoice(self._chat_id, self.voice_file) @@ -61,10 +62,9 @@ class VoiceTest(BaseTest, unittest.TestCase): self.assertEqual(voice.mime_type, self.mime_type) self.assertEqual(voice.file_size, self.file_size) + @flaky(3, 1) + @timeout(10) def test_send_voice_all_args(self): - """Test telegram.Bot sendAudio method""" - print('Testing bot.sendVoice - With all arguments') - message = self._bot.sendVoice(self._chat_id, self.voice_file, self.duration, @@ -79,10 +79,9 @@ class VoiceTest(BaseTest, unittest.TestCase): self.assertEqual(voice.mime_type, self.mime_type) self.assertEqual(voice.file_size, self.file_size) + @flaky(3, 1) + @timeout(10) def test_send_voice_ogg_file(self): - """Test telegram.Bot sendVoice method""" - print('Testing bot.sendVoice - Ogg File') - message = self._bot.sendVoice(chat_id=self._chat_id, voice=self.voice_file, duration=self.duration) @@ -95,10 +94,9 @@ class VoiceTest(BaseTest, unittest.TestCase): self.assertEqual(voice.mime_type, self.mime_type) self.assertEqual(voice.file_size, self.file_size) + @flaky(3, 1) + @timeout(10) def test_send_voice_ogg_file_with_custom_filename(self): - """Test telegram.Bot sendVoice method""" - print('Testing bot.sendVoice - Ogg File with custom filename') - message = self._bot.sendVoice(chat_id=self._chat_id, voice=self.voice_file, duration=self.duration, @@ -112,10 +110,9 @@ class VoiceTest(BaseTest, unittest.TestCase): self.assertEqual(voice.mime_type, self.mime_type) self.assertEqual(voice.file_size, self.file_size) + @flaky(3, 1) + @timeout(10) def test_send_voice_ogg_url_file(self): - """Test telegram.Bot sendVoice method""" - print('Testing bot.sendVoice - Ogg File by URL') - message = self._bot.sendVoice(chat_id=self._chat_id, voice=self.voice_file_url, duration=self.duration) @@ -128,10 +125,9 @@ class VoiceTest(BaseTest, unittest.TestCase): self.assertEqual(voice.mime_type, self.mime_type) self.assertEqual(voice.file_size, self.file_size) + @flaky(3, 1) + @timeout(10) def test_send_voice_resend(self): - """Test telegram.Bot sendVoice method""" - print('Testing bot.sendVoice - Resend by file_id') - message = self._bot.sendVoice(chat_id=self._chat_id, voice=self.voice_file_id, duration=self.duration) @@ -139,13 +135,10 @@ class VoiceTest(BaseTest, unittest.TestCase): voice = message.voice self.assertEqual(voice.file_id, self.voice_file_id) - self.assertEqual(voice.duration, self.duration) + self.assertEqual(voice.duration, 0) self.assertEqual(voice.mime_type, self.mime_type) def test_voice_de_json(self): - """Test Voice.de_json() method""" - print('Testing Voice.de_json()') - voice = telegram.Voice.de_json(self.json_dict) self.assertEqual(voice.file_id, self.voice_file_id) @@ -154,17 +147,11 @@ class VoiceTest(BaseTest, unittest.TestCase): self.assertEqual(voice.file_size, self.file_size) def test_voice_to_json(self): - """Test Voice.to_json() method""" - print('Testing Voice.to_json()') - voice = telegram.Voice.de_json(self.json_dict) self.assertTrue(self.is_json(voice.to_json())) def test_voice_to_dict(self): - """Test Voice.to_dict() method""" - print('Testing Voice.to_dict()') - voice = telegram.Voice.de_json(self.json_dict) self.assertTrue(self.is_dict(voice.to_dict())) @@ -173,9 +160,9 @@ class VoiceTest(BaseTest, unittest.TestCase): self.assertEqual(voice['mime_type'], self.mime_type) self.assertEqual(voice['file_size'], self.file_size) + @flaky(3, 1) + @timeout(10) def test_error_send_voice_empty_file(self): - print('Testing bot.sendVoice - Null file') - json_dict = self.json_dict del(json_dict['file_id']) @@ -185,9 +172,9 @@ class VoiceTest(BaseTest, unittest.TestCase): lambda: self._bot.sendVoice(chat_id=self._chat_id, **json_dict)) + @flaky(3, 1) + @timeout(10) def test_error_send_voice_empty_file_id(self): - print('Testing bot.sendVoice - Empty file_id') - json_dict = self.json_dict del(json_dict['file_id']) @@ -197,9 +184,9 @@ class VoiceTest(BaseTest, unittest.TestCase): lambda: self._bot.sendVoice(chat_id=self._chat_id, **json_dict)) + @flaky(3, 1) + @timeout(10) def test_error_voice_without_required_args(self): - print('Testing bot.sendVoice - Without required arguments') - json_dict = self.json_dict del(json_dict['file_id'])