diff --git a/CHANGES.rst b/CHANGES.rst index deae053e3..5b6497454 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,11 @@ +**2016-04-22** + +*Released 4.0rc1* + +- Implement Bot API 2.0 +- Almost complete recode of ``Dispatcher`` +- Please read the `Transistion Guide to 4.0 `_ + **2016-03-22** *Released 3.4* diff --git a/README.rst b/README.rst index 48c5a655b..b1345f1b0 100644 --- a/README.rst +++ b/README.rst @@ -108,6 +108,13 @@ getUserProfilePhotos Yes getFile Yes setWebhook Yes answerInlineQuery Yes +kickChatMember Yes +unbanChatMember Yes +answerCallbackQuery Yes +editMessageText Yes +editMessageCaption Yes +editMessageReplyMarkup Yes +answerCallbackQuery Yes ========================= ============ ------------------------- @@ -273,7 +280,8 @@ To tell the user that something is happening on bot's side:: To create `Custom Keyboards `_:: - >>> custom_keyboard = [[ telegram.Emoji.THUMBS_UP_SIGN, telegram.Emoji.THUMBS_DOWN_SIGN ]] + >>> custom_keyboard = [[ telegram.KeyboardButton(telegram.Emoji.THUMBS_UP_SIGN), + ... telegram.KeyboardButton(telegram.Emoji.THUMBS_DOWN_SIGN) ]] >>> reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard) >>> bot.sendMessage(chat_id=chat_id, text="Stay here, I'll be back.", reply_markup=reply_markup) @@ -314,53 +322,60 @@ Now, we need to define a function that should process a specific type of update: >>> def start(bot, update): ... bot.sendMessage(chat_id=update.message.chat_id, text="I'm a bot, please talk to me!") -We want this function to be called on a Telegram message that contains the ``/start`` command, so we need to register it in the dispatcher:: +We want this function to be called on a Telegram message that contains the ``/start`` command. To do that, we have to use a ``CommandHandler`` object and register it in the dispatcher:: - >>> dispatcher.addTelegramCommandHandler('start', start) + >>> from telegram.ext import CommandHandler + >>> start_handler = CommandHandler('start', start) + >>> dispatcher.addHandler(start_handler) The last step is to tell the ``Updater`` to start working:: >>> updater.start_polling() -Our bot is now up and running (go ahead and try it)! It's not doing anything yet, besides answering to the ``/start`` command. Let's add another handler function and register it:: +Our bot is now up and running (go ahead and try it)! It's not doing anything yet, besides answering to the ``/start`` command. Let's add another handler that listens for regular messages. We're using the `MessageHandler` here to echo to all text messages:: >>> def echo(bot, update): ... bot.sendMessage(chat_id=update.message.chat_id, text=update.message.text) ... - >>> dispatcher.addTelegramMessageHandler(echo) + >>> from telegram.ext import MessageHandler + >>> from telegram.ext import filters + >>> echo_handler = MessageHandler([filters.TEXT], echo) + >>> dispatcher.addHandler(echo_handler) -Our bot should now reply to all messages that are not a command with a message that has the same content. +Our bot should now reply to all text messages that are not a command with a message that has the same content. -People might try to send commands to the bot that it doesn't understand, so we should get that covered as well:: - - >>> def unknown(bot, update): - ... bot.sendMessage(chat_id=update.message.chat_id, text="Sorry, I didn't understand that command.") - ... - >>> dispatcher.addUnknownTelegramCommandHandler(unknown) - -Let's add some functionality to our bot. We want to add the ``/caps`` command, that will take some text as parameter and return it in all caps. We can get the arguments that were passed to the command in the handler function simply by adding it to the parameter list:: +Let's add some functionality to our bot. We want to add the ``/caps`` command, that will take some text as parameter and return it in all caps. We can get the arguments that were passed to a command in the handler function:: >>> def caps(bot, update, args): ... text_caps = ' '.join(args).upper() ... bot.sendMessage(chat_id=update.message.chat_id, text=text_caps) ... - >>> dispatcher.addTelegramCommandHandler('caps', caps) + >>> caps_handler = CommandHandler('caps', caps, pass_args=True) + >>> dispatcher.addHandler(caps_handler) 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', query.upper())) - ... bot.answerInlineQuery(update.inline_query.id, results) + ... query = bot.update.inline_query.query + ... results = list() + ... results.append(InlineQueryResultArticle(query.upper(), 'Caps', query.upper())) + ... bot.answerInlineQuery(update.inline_query.id, results) ... - >>> dispatcher.addTelegramInlineHandler(inline_caps) + >>> from telegram.ext import InlineQueryHandler + >>> inline_caps_handler = InlineQueryHandler(inline_caps) + >>> dispatcher.addHandler(inline_caps_handler) -Now it's time to stop the bot:: +People might try to send commands to the bot that it doesn't understand, so we can use a ``RegexHandler`` to recognize all commands that were not recognized by the previous handlers. **Note:** This handler has to be added last, else it will be triggered before the ``CommandHandlers`` had a chance to look at the update:: + + >>> def unknown(bot, update): + ... bot.sendMessage(chat_id=update.message.chat_id, text="Sorry, I didn't understand that command.") + ... + >>> from telegram.ext import RegexHandler + >>> unknown_handler = RegexHandler(r'/.*', unknown) + >>> dispatcher.addHandler(unknown_handler) + +If you're done playing around, stop the bot with this:: >>> updater.stop() diff --git a/docs/source/conf.py b/docs/source/conf.py index 8493bf3f0..c8d5b0172 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.4' +version = '4.0' # The full version, including alpha/beta/rc tags. -release = '3.4.0' +release = '4.0rc1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/source/telegram.bot.rst b/docs/source/telegram.bot.rst index e71a7d1c1..f03a76bad 100644 --- a/docs/source/telegram.bot.rst +++ b/docs/source/telegram.bot.rst @@ -3,5 +3,4 @@ telegram.bot module .. automodule:: telegram.bot :members: - :undoc-members: :show-inheritance: diff --git a/docs/source/telegram.ext.callbackqueryhandler.rst b/docs/source/telegram.ext.callbackqueryhandler.rst new file mode 100644 index 000000000..bf8a5b1e6 --- /dev/null +++ b/docs/source/telegram.ext.callbackqueryhandler.rst @@ -0,0 +1,7 @@ +telegram.ext.handler module +=========================== + +.. automodule:: telegram.ext.handler + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.ext.choseninlineresulthandler.rst b/docs/source/telegram.ext.choseninlineresulthandler.rst new file mode 100644 index 000000000..b81c1b75a --- /dev/null +++ b/docs/source/telegram.ext.choseninlineresulthandler.rst @@ -0,0 +1,7 @@ +telegram.ext.choseninlineresulthandler module +============================================= + +.. automodule:: telegram.ext.choseninlineresulthandler + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.ext.commandhandler.rst b/docs/source/telegram.ext.commandhandler.rst new file mode 100644 index 000000000..1330c248f --- /dev/null +++ b/docs/source/telegram.ext.commandhandler.rst @@ -0,0 +1,7 @@ +telegram.ext.commandhandler module +================================== + +.. automodule:: telegram.ext.commandhandler + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.ext.filters.rst b/docs/source/telegram.ext.filters.rst new file mode 100644 index 000000000..a04a2d1c1 --- /dev/null +++ b/docs/source/telegram.ext.filters.rst @@ -0,0 +1,7 @@ +telegram.ext.filters module +=========================== + +.. automodule:: telegram.ext.filters + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.ext.handler.rst b/docs/source/telegram.ext.handler.rst new file mode 100644 index 000000000..bf8a5b1e6 --- /dev/null +++ b/docs/source/telegram.ext.handler.rst @@ -0,0 +1,7 @@ +telegram.ext.handler module +=========================== + +.. automodule:: telegram.ext.handler + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.ext.inlinequeryhandler.rst b/docs/source/telegram.ext.inlinequeryhandler.rst new file mode 100644 index 000000000..78438b315 --- /dev/null +++ b/docs/source/telegram.ext.inlinequeryhandler.rst @@ -0,0 +1,7 @@ +telegram.ext.inlinequeryhandler module +====================================== + +.. automodule:: telegram.ext.inlinequeryhandler + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.ext.jobqueue.rst b/docs/source/telegram.ext.jobqueue.rst new file mode 100644 index 000000000..a78cc004b --- /dev/null +++ b/docs/source/telegram.ext.jobqueue.rst @@ -0,0 +1,7 @@ +telegram.ext.jobqueue module +============================ + +.. automodule:: telegram.ext.jobqueue + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.ext.messagehandler.rst b/docs/source/telegram.ext.messagehandler.rst new file mode 100644 index 000000000..17c3547d6 --- /dev/null +++ b/docs/source/telegram.ext.messagehandler.rst @@ -0,0 +1,7 @@ +telegram.ext.messagehandler module +================================== + +.. automodule:: telegram.ext.messagehandler + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.ext.regexhandler.rst b/docs/source/telegram.ext.regexhandler.rst new file mode 100644 index 000000000..bfb781299 --- /dev/null +++ b/docs/source/telegram.ext.regexhandler.rst @@ -0,0 +1,7 @@ +telegram.ext.regexhandler module +================================ + +.. automodule:: telegram.ext.regexhandler + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.ext.rst b/docs/source/telegram.ext.rst new file mode 100644 index 000000000..a924e021e --- /dev/null +++ b/docs/source/telegram.ext.rst @@ -0,0 +1,29 @@ +telegram.ext package +==================== + +Submodules +---------- + +.. toctree:: + + telegram.ext.updater + telegram.ext.dispatcher + telegram.ext.jobqueue + telegram.ext.handler + telegram.ext.choseninlineresulthandler + telegram.ext.commandhandler + telegram.ext.inlinequeryhandler + telegram.ext.messagehandler + telegram.ext.filters + telegram.ext.regexhandler + telegram.ext.stringcommandhandler + telegram.ext.stringregexhandler + telegram.ext.typehandler + +Module contents +--------------- + +.. automodule:: telegram.ext + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.ext.stringcommandhandler.rst b/docs/source/telegram.ext.stringcommandhandler.rst new file mode 100644 index 000000000..0b3f88277 --- /dev/null +++ b/docs/source/telegram.ext.stringcommandhandler.rst @@ -0,0 +1,7 @@ +telegram.ext.stringcommandhandler module +======================================== + +.. automodule:: telegram.ext.stringcommandhandler + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.ext.stringregexhandler.rst b/docs/source/telegram.ext.stringregexhandler.rst new file mode 100644 index 000000000..3faeb9187 --- /dev/null +++ b/docs/source/telegram.ext.stringregexhandler.rst @@ -0,0 +1,7 @@ +telegram.ext.stringregexhandler module +====================================== + +.. automodule:: telegram.ext.stringregexhandler + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.ext.typehandler.rst b/docs/source/telegram.ext.typehandler.rst new file mode 100644 index 000000000..6a7f58968 --- /dev/null +++ b/docs/source/telegram.ext.typehandler.rst @@ -0,0 +1,7 @@ +telegram.ext.typehandler module +=============================== + +.. automodule:: telegram.ext.typehandler + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinekeyboardbutton.rst b/docs/source/telegram.inlinekeyboardbutton.rst new file mode 100644 index 000000000..1d71fe458 --- /dev/null +++ b/docs/source/telegram.inlinekeyboardbutton.rst @@ -0,0 +1,7 @@ +telegram.inlinekeyboardbutton module +=========================== + +.. automodule:: telegram.inlinekeyboardbutton + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinekeyboardmarkup.rst b/docs/source/telegram.inlinekeyboardmarkup.rst new file mode 100644 index 000000000..3fbf80776 --- /dev/null +++ b/docs/source/telegram.inlinekeyboardmarkup.rst @@ -0,0 +1,7 @@ +telegram.inlinekeyboardmarkup module +========================== + +.. automodule:: telegram.inlinekeyboardmarkup + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultarticle.rst b/docs/source/telegram.inlinequeryresultarticle.rst new file mode 100644 index 000000000..2f7370139 --- /dev/null +++ b/docs/source/telegram.inlinequeryresultarticle.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultarticle module +================================= + +.. automodule:: telegram.inlinequeryresultarticle + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultaudio.rst b/docs/source/telegram.inlinequeryresultaudio.rst new file mode 100644 index 000000000..7458ef031 --- /dev/null +++ b/docs/source/telegram.inlinequeryresultaudio.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultaudio module +================================= + +.. automodule:: telegram.inlinequeryresultaudio + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultcachedaudio.rst b/docs/source/telegram.inlinequeryresultcachedaudio.rst new file mode 100644 index 000000000..49eb0b853 --- /dev/null +++ b/docs/source/telegram.inlinequeryresultcachedaudio.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultcachedaudio module +================================= + +.. automodule:: telegram.inlinequeryresultcachedaudio + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultcacheddocument.rst b/docs/source/telegram.inlinequeryresultcacheddocument.rst new file mode 100644 index 000000000..c574ed656 --- /dev/null +++ b/docs/source/telegram.inlinequeryresultcacheddocument.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultcacheddocument module +================================= + +.. automodule:: telegram.inlinequeryresultcacheddocument + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultcachedgif.rst b/docs/source/telegram.inlinequeryresultcachedgif.rst new file mode 100644 index 000000000..d34692e52 --- /dev/null +++ b/docs/source/telegram.inlinequeryresultcachedgif.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultcachedgif module +================================= + +.. automodule:: telegram.inlinequeryresultcachedgif + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultcachedmpeg4gif.rst b/docs/source/telegram.inlinequeryresultcachedmpeg4gif.rst new file mode 100644 index 000000000..953f754b6 --- /dev/null +++ b/docs/source/telegram.inlinequeryresultcachedmpeg4gif.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultcachedmpeg4gif module +================================= + +.. automodule:: telegram.inlinequeryresultcachedmpeg4gif + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultcachedphoto.rst b/docs/source/telegram.inlinequeryresultcachedphoto.rst new file mode 100644 index 000000000..2560e24c8 --- /dev/null +++ b/docs/source/telegram.inlinequeryresultcachedphoto.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultcachedphoto module +================================= + +.. automodule:: telegram.inlinequeryresultcachedphoto + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultcachedsticker.rst b/docs/source/telegram.inlinequeryresultcachedsticker.rst new file mode 100644 index 000000000..ca90b49a8 --- /dev/null +++ b/docs/source/telegram.inlinequeryresultcachedsticker.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultcachedsticker module +================================= + +.. automodule:: telegram.inlinequeryresultcachedsticker + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultcachedvideo.rst b/docs/source/telegram.inlinequeryresultcachedvideo.rst new file mode 100644 index 000000000..242dc36ba --- /dev/null +++ b/docs/source/telegram.inlinequeryresultcachedvideo.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultcachedvideo module +================================= + +.. automodule:: telegram.inlinequeryresultcachedvideo + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultcachedvoice.rst b/docs/source/telegram.inlinequeryresultcachedvoice.rst new file mode 100644 index 000000000..5b9b362ed --- /dev/null +++ b/docs/source/telegram.inlinequeryresultcachedvoice.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultcachedvoice module +================================= + +.. automodule:: telegram.inlinequeryresultcachedvoice + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultcontact.rst b/docs/source/telegram.inlinequeryresultcontact.rst new file mode 100644 index 000000000..da5d91f08 --- /dev/null +++ b/docs/source/telegram.inlinequeryresultcontact.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultcontact module +================================= + +.. automodule:: telegram.inlinequeryresultcontact + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultdocument.rst b/docs/source/telegram.inlinequeryresultdocument.rst new file mode 100644 index 000000000..08a8a44be --- /dev/null +++ b/docs/source/telegram.inlinequeryresultdocument.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultdocument module +================================= + +.. automodule:: telegram.inlinequeryresultdocument + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultgif.rst b/docs/source/telegram.inlinequeryresultgif.rst new file mode 100644 index 000000000..0fd6f06f7 --- /dev/null +++ b/docs/source/telegram.inlinequeryresultgif.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultgif module +================================= + +.. automodule:: telegram.inlinequeryresultgif + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultlocation.rst b/docs/source/telegram.inlinequeryresultlocation.rst new file mode 100644 index 000000000..7c06a2df1 --- /dev/null +++ b/docs/source/telegram.inlinequeryresultlocation.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultlocation module +================================= + +.. automodule:: telegram.inlinequeryresultlocation + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultmpeg4gif.rst b/docs/source/telegram.inlinequeryresultmpeg4gif.rst new file mode 100644 index 000000000..9ed46db6c --- /dev/null +++ b/docs/source/telegram.inlinequeryresultmpeg4gif.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultmpeg4gif module +================================= + +.. automodule:: telegram.inlinequeryresultmpeg4gif + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultphoto.rst b/docs/source/telegram.inlinequeryresultphoto.rst new file mode 100644 index 000000000..1162bd203 --- /dev/null +++ b/docs/source/telegram.inlinequeryresultphoto.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultphoto module +================================= + +.. automodule:: telegram.inlinequeryresultphoto + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultvenue.rst b/docs/source/telegram.inlinequeryresultvenue.rst new file mode 100644 index 000000000..bcd8e2354 --- /dev/null +++ b/docs/source/telegram.inlinequeryresultvenue.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultvenue module +================================= + +.. automodule:: telegram.inlinequeryresultvenue + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultvideo.rst b/docs/source/telegram.inlinequeryresultvideo.rst new file mode 100644 index 000000000..3245bcdc5 --- /dev/null +++ b/docs/source/telegram.inlinequeryresultvideo.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultvideo module +================================= + +.. automodule:: telegram.inlinequeryresultvideo + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.inlinequeryresultvoice.rst b/docs/source/telegram.inlinequeryresultvoice.rst new file mode 100644 index 000000000..d33fc0e52 --- /dev/null +++ b/docs/source/telegram.inlinequeryresultvoice.rst @@ -0,0 +1,7 @@ +telegram.inlinequeryresultvoice module +================================= + +.. automodule:: telegram.inlinequeryresultvoice + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.jobqueue.rst b/docs/source/telegram.jobqueue.rst deleted file mode 100644 index b74f57d3b..000000000 --- a/docs/source/telegram.jobqueue.rst +++ /dev/null @@ -1,7 +0,0 @@ -telegram.jobqueue module -======================== - -.. automodule:: telegram.jobqueue - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/telegram.rst b/docs/source/telegram.rst index 8aea879c4..abd4e6803 100644 --- a/docs/source/telegram.rst +++ b/docs/source/telegram.rst @@ -9,11 +9,30 @@ Submodules telegram.audio telegram.base telegram.bot - telegram.ext.updater - telegram.ext.dispatcher - telegram.jobqueue + telegram.ext telegram.inlinequery telegram.inlinequeryresult + telegram.inlinekeyboardbutton + telegram.inlinekeyboardmarkup + telegram.inlinequeryresultarticle + telegram.inlinequeryresultaudio + telegram.inlinequeryresultcachedaudio + telegram.inlinequeryresultcacheddocument + telegram.inlinequeryresultcachedgif + telegram.inlinequeryresultcachedmpeg4gif + telegram.inlinequeryresultcachedphoto + telegram.inlinequeryresultcachedsticker + telegram.inlinequeryresultcachedvideo + telegram.inlinequeryresultcachedvoice + telegram.inlinequeryresultcontact + telegram.inlinequeryresultdocument + telegram.inlinequeryresultgif + telegram.inlinequeryresultlocation + telegram.inlinequeryresultmpeg4gif + telegram.inlinequeryresultphoto + telegram.inlinequeryresultvenue + telegram.inlinequeryresultvideo + telegram.inlinequeryresultvoice telegram.choseninlineresult telegram.chataction telegram.contact diff --git a/examples/clibot.py b/examples/clibot.py index 65c4b61a9..752f10d68 100644 --- a/examples/clibot.py +++ b/examples/clibot.py @@ -18,7 +18,8 @@ Reply to last chat from the command line by typing "/reply " Type 'stop' on the command line to stop the bot. """ -from telegram.ext import Updater +from telegram.ext import Updater, StringCommandHandler, StringRegexHandler, \ + MessageHandler, CommandHandler, RegexHandler, filters from telegram.ext.dispatcher import run_async from time import sleep import logging @@ -34,8 +35,9 @@ logger = logging.getLogger(__name__) last_chat_id = 0 -# Define a few (command) handlers. These usually take the two arguments bot and -# update. Error handlers also receive the raised TelegramError object in error. +# Define a few (command) handler callback functions. These usually take the +# two arguments bot and update. Error handlers also receive the raised +# TelegramError object in error. def start(bot, update): """ Answer in Telegram """ bot.sendMessage(update.message.chat_id, text='Hi!') @@ -59,13 +61,8 @@ def any_message(bot, update): update.message.text)) -def unknown_command(bot, update): - """ Answer in Telegram """ - bot.sendMessage(update.message.chat_id, text='Command not recognized!') - - @run_async -def message(bot, update, **kwargs): +def message(bot, update): """ Example for an asynchronous handler. It's not guaranteed that replies will be in order when using @run_async. Also, you have to include **kwargs in @@ -95,16 +92,12 @@ def cli_noncommand(bot, update, update_queue): appending it to the argument list. Be careful with this though. Here, we put the input string back into the queue, but as a command. - To learn more about those optional handler parameters, read: - http://python-telegram-bot.readthedocs.org/en/latest/telegram.dispatcher.html + To learn more about those optional handler parameters, read the + documentation of the Handler classes. """ update_queue.put('/%s' % update) -def unknown_cli_command(bot, update): - logger.warn("Command not found: %s" % update) - - def error(bot, update, error): """ Print error to console """ logger.warn('Update %s caused error %s' % (update, error)) @@ -119,42 +112,43 @@ def main(): dp = updater.dispatcher # This is how we add handlers for Telegram messages - dp.addTelegramCommandHandler("start", start) - dp.addTelegramCommandHandler("help", help) - dp.addUnknownTelegramCommandHandler(unknown_command) + dp.addHandler(CommandHandler("start", start)) + dp.addHandler(CommandHandler("help", help)) # Message handlers only receive updates that don't contain commands - dp.addTelegramMessageHandler(message) - # Regex handlers will receive all updates on which their regex matches - dp.addTelegramRegexHandler('.*', any_message) + dp.addHandler(MessageHandler([filters.TEXT], message)) + # Regex handlers will receive all updates on which their regex matches, + # but we have to add it in a separate group, since in one group, + # only one handler will be executed + dp.addHandler(RegexHandler('.*', any_message), group='log') - # String handlers work pretty much the same - dp.addStringCommandHandler('reply', cli_reply) - dp.addUnknownStringCommandHandler(unknown_cli_command) - dp.addStringRegexHandler('[^/].*', cli_noncommand) + # String handlers work pretty much the same. Note that we have to tell + # the handler to pass the args or update_queue parameter + dp.addHandler(StringCommandHandler('reply', cli_reply, pass_args=True)) + dp.addHandler(StringRegexHandler('[^/].*', cli_noncommand, + pass_update_queue=True)) # All TelegramErrors are caught for you and delivered to the error # handler(s). Other types of Errors are not caught. dp.addErrorHandler(error) # Start the Bot and store the update Queue, so we can insert updates - update_queue = updater.start_polling(poll_interval=0.1, timeout=10) + update_queue = updater.start_polling(timeout=10) ''' # Alternatively, run with webhook: - updater.bot.setWebhook(webhook_url='https://example.com/%s' % token, - certificate=open('cert.pem', 'rb')) update_queue = updater.start_webhook('0.0.0.0', 443, url_path=token, cert='cert.pem', - key='key.key') + key='key.key', + webhook_url='https://example.com/%s' + % token) # Or, if SSL is handled by a reverse proxy, the webhook URL is already set # and the reverse proxy is configured to deliver directly to port 6000: - update_queue = updater.start_webhook('0.0.0.0', - 6000) + update_queue = updater.start_webhook('0.0.0.0', 6000) ''' # Start CLI-Loop diff --git a/examples/echobot2.py b/examples/echobot2.py index 2a4f57fce..492fc0e38 100644 --- a/examples/echobot2.py +++ b/examples/echobot2.py @@ -17,7 +17,7 @@ Press Ctrl-C on the command line or send a signal to the process to stop the bot. """ -from telegram.ext import Updater +from telegram.ext import Updater, CommandHandler, MessageHandler, filters import logging # Enable logging @@ -54,11 +54,11 @@ def main(): dp = updater.dispatcher # on different commands - answer in Telegram - dp.addTelegramCommandHandler("start", start) - dp.addTelegramCommandHandler("help", help) + dp.addHandler(CommandHandler("start", start)) + dp.addHandler(CommandHandler("help", help)) # on noncommand i.e message - echo the message on Telegram - dp.addTelegramMessageHandler(echo) + dp.addHandler(MessageHandler([filters.TEXT], echo)) # log all errors dp.addErrorHandler(error) diff --git a/examples/inlinebot.py b/examples/inlinebot.py index 9df1bd3bc..f9243543d 100644 --- a/examples/inlinebot.py +++ b/examples/inlinebot.py @@ -16,12 +16,13 @@ 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 +from uuid import uuid4 import re -from telegram import InlineQueryResultArticle, ParseMode -from telegram.ext import Updater +from telegram import InlineQueryResultArticle, ParseMode, \ + InputTextMessageContent +from telegram.ext import Updater, InlineQueryHandler, CommandHandler import logging # Enable logging @@ -49,29 +50,29 @@ def escape_markdown(text): def inlinequery(bot, update): - if update.inline_query is not None and update.inline_query.query: - query = update.inline_query.query - results = list() + 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=uuid4(), + title="Caps", + input_message_content=InputTextMessageContent(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=uuid4(), + title="Bold", + input_message_content=InputTextMessageContent( + "*%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) + results.append(InlineQueryResultArticle( + id=uuid4(), + title="Italic", + input_message_content=InputTextMessageContent( + "_%s_" % escape_markdown(query), + parse_mode=ParseMode.MARKDOWN))) + bot.answerInlineQuery(update.inline_query.id, results=results) def error(bot, update, error): @@ -86,11 +87,11 @@ def main(): dp = updater.dispatcher # on different commands - answer in Telegram - dp.addTelegramCommandHandler("start", start) - dp.addTelegramCommandHandler("help", help) + dp.addHandler(CommandHandler("start", start)) + dp.addHandler(CommandHandler("help", help)) # on noncommand i.e message - echo the message on Telegram - dp.addTelegramInlineHandler(inlinequery) + dp.addHandler(InlineQueryHandler(inlinequery)) # log all errors dp.addErrorHandler(error) diff --git a/examples/inlinekeyboard_example.py b/examples/inlinekeyboard_example.py new file mode 100644 index 000000000..42ce67825 --- /dev/null +++ b/examples/inlinekeyboard_example.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Basic example for a bot that awaits an answer from the user. It's built upon +# the state_machine_bot.py example +# This program is dedicated to the public domain under the CC0 license. + +import logging +from telegram import Emoji, ForceReply, InlineKeyboardButton, \ + InlineKeyboardMarkup +from telegram.ext import Updater, CommandHandler, MessageHandler, \ + CallbackQueryHandler, filters + +logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - ' + '%(message)s', + level=logging.DEBUG) + +# Define the different states a chat can be in +MENU, AWAIT_CONFIRMATION, AWAIT_INPUT = range(3) + +# Python 2 and 3 unicode differences +try: + YES, NO = (Emoji.THUMBS_UP_SIGN.decode('utf-8'), + Emoji.THUMBS_DOWN_SIGN.decode('utf-8')) +except AttributeError: + YES, NO = (Emoji.THUMBS_UP_SIGN, Emoji.THUMBS_DOWN_SIGN) + +# States are saved in a dict that maps chat_id -> state +state = dict() +# Sometimes you need to save data temporarily +context = dict() +# This dict is used to store the settings value for the chat. +# Usually, you'd use persistence for this (e.g. sqlite). +values = dict() + + +# Example handler. Will be called on the /set command and on regular messages +def set_value(bot, update): + chat_id = update.message.chat_id + user_id = update.message.from_user.id + user_state = state.get(chat_id, MENU) + + if user_state == MENU: + state[user_id] = AWAIT_INPUT # set the state + bot.sendMessage(chat_id, + text="Please enter your settings value", + reply_markup=ForceReply()) + + +def entered_value(bot, update): + chat_id = update.message.chat_id + user_id = update.message.from_user.id + chat_state = state.get(user_id, MENU) + + # Check if we are waiting for input + if chat_state == AWAIT_INPUT: + state[user_id] = AWAIT_CONFIRMATION + + # Save the user id and the answer to context + context[user_id] = update.message.text + reply_markup = InlineKeyboardMarkup( + [[InlineKeyboardButton(YES, callback_data=YES), + InlineKeyboardButton(NO, callback_data=NO)]]) + bot.sendMessage(chat_id, text="Are you sure?", + reply_markup=reply_markup) + + +def confirm_value(bot, update): + query = update.callback_query + chat_id = query.message.chat_id + user_id = query.from_user.id + text = query.data + user_state = state.get(user_id, MENU) + user_context = context.get(user_id, None) + + # Check if we are waiting for confirmation and the right user answered + if user_state == AWAIT_CONFIRMATION: + del state[user_id] + del context[user_id] + bot.answerCallbackQuery(query.id, text="Ok!") + if text == YES: + values[user_id] = user_context + bot.editMessageText(text="Changed value to %s." % values[user_id], + chat_id=chat_id, + message_id= + query.message.message_id) + else: + bot.editMessageText(text="Alright, value is still %s." + % values.get(user_id, 'not set'), + chat_id=chat_id, + message_id= + query.message.message_id) + + +def help(bot, update): + bot.sendMessage(update.message.chat_id, text="Use /set to test this bot.") + + +def error(bot, update, error): + logging.warning('Update "%s" caused error "%s"' % (update, error)) + +# Create the Updater and pass it your bot's token. +updater = Updater("TOKEN") + +# The command +updater.dispatcher.addHandler(CommandHandler('set', set_value)) +# The answer +updater.dispatcher.addHandler(MessageHandler([filters.TEXT], entered_value)) +# The confirmation +updater.dispatcher.addHandler(CallbackQueryHandler(confirm_value)) +updater.dispatcher.addHandler(CommandHandler('start', help)) +updater.dispatcher.addHandler(CommandHandler('help', help)) +updater.dispatcher.addErrorHandler(error) + +# Start the Bot +updater.start_polling() + +# Run the bot until the user presses Ctrl-C or the process receives SIGINT, +# SIGTERM or SIGABRT +updater.idle() diff --git a/examples/state_machine_bot.py b/examples/state_machine_bot.py index 414541e84..0ffe6328c 100644 --- a/examples/state_machine_bot.py +++ b/examples/state_machine_bot.py @@ -5,8 +5,8 @@ # This program is dedicated to the public domain under the CC0 license. import logging -from telegram import Emoji, ForceReply, ReplyKeyboardMarkup -from telegram.ext import Updater +from telegram import Emoji, ForceReply, ReplyKeyboardMarkup, KeyboardButton +from telegram.ext import Updater, CommandHandler, MessageHandler, filters logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - ' '%(message)s', @@ -55,14 +55,16 @@ def set_value(bot, update): # Save the user id and the answer to context context[chat_id] = (user_id, update.message.text) - reply_markup = ReplyKeyboardMarkup([[YES, NO]], one_time_keyboard=True) + reply_markup = ReplyKeyboardMarkup( + [[KeyboardButton(YES), KeyboardButton(NO)]], + one_time_keyboard=True) bot.sendMessage(chat_id, text="Are you sure?", reply_markup=reply_markup) # If we are waiting for confirmation and the right user answered elif chat_state == AWAIT_CONFIRMATION and chat_context[0] == user_id: - state[chat_id] = MENU - context[chat_id] = None + del state[chat_id] + del context[chat_id] if text == YES: values[chat_id] = chat_context[1] bot.sendMessage(chat_id, @@ -77,8 +79,8 @@ def set_value(bot, update): # Sets the state back to MENU and clears the context def cancel(bot, update): chat_id = update.message.chat_id - state[chat_id] = MENU - context[chat_id] = None + del state[chat_id] + del context[chat_id] def help(bot, update): @@ -89,12 +91,12 @@ def help(bot, update): updater = Updater("TOKEN") # The command -updater.dispatcher.addTelegramCommandHandler('set', set_value) +updater.dispatcher.addHandler(CommandHandler('set', set_value)) # The answer and confirmation -updater.dispatcher.addTelegramMessageHandler(set_value) -updater.dispatcher.addTelegramCommandHandler('cancel', cancel) -updater.dispatcher.addTelegramCommandHandler('start', help) -updater.dispatcher.addTelegramCommandHandler('help', help) +updater.dispatcher.addHandler(MessageHandler([filters.TEXT], set_value)) +updater.dispatcher.addHandler(CommandHandler('cancel', cancel)) +updater.dispatcher.addHandler(CommandHandler('start', help)) +updater.dispatcher.addHandler(CommandHandler('help', help)) # Start the Bot updater.start_polling() diff --git a/examples/timerbot.py b/examples/timerbot.py index 978df57af..0774b7eeb 100644 --- a/examples/timerbot.py +++ b/examples/timerbot.py @@ -18,7 +18,7 @@ Press Ctrl-C on the command line or send a signal to the process to stop the bot. """ -from telegram.ext import Updater +from telegram.ext import Updater, CommandHandler import logging # Enable logging @@ -73,9 +73,9 @@ def main(): dp = updater.dispatcher # on different commands - answer in Telegram - dp.addTelegramCommandHandler("start", start) - dp.addTelegramCommandHandler("help", start) - dp.addTelegramCommandHandler("set", set) + dp.addHandler(CommandHandler("start", start)) + dp.addHandler(CommandHandler("help", start)) + dp.addHandler(CommandHandler("set", set)) # log all errors dp.addErrorHandler(error) diff --git a/setup.py b/setup.py index 6463c354d..e2955eda1 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ def requirements(): setup( name='python-telegram-bot', - version='3.4', + version='4.0rc1', author='Leandro Toledo', author_email='devs@python-telegram-bot.org', license='LGPLv3', diff --git a/telegram/__init__.py b/telegram/__init__.py index 199075234..8ccbd93fa 100644 --- a/telegram/__init__.py +++ b/telegram/__init__.py @@ -30,8 +30,10 @@ from .sticker import Sticker from .video import Video from .contact import Contact from .location import Location +from .venue import Venue from .chataction import ChatAction from .userprofilephotos import UserProfilePhotos +from .keyboardbutton import KeyboardButton from .replymarkup import ReplyMarkup from .replykeyboardmarkup import ReplyKeyboardMarkup from .replykeyboardhide import ReplyKeyboardHide @@ -42,55 +44,101 @@ from .file import File from .nullhandler import NullHandler from .emoji import Emoji from .parsemode import ParseMode +from .messageentity import MessageEntity from .message import Message -from .inlinequery import InlineQuery +from .inputmessagecontent import InputMessageContent +from .callbackquery import CallbackQuery from .choseninlineresult import ChosenInlineResult -from .inlinequeryresult import InlineQueryResultArticle, InlineQueryResultGif,\ - InlineQueryResultMpeg4Gif, InlineQueryResultPhoto, InlineQueryResultVideo +from .inlinekeyboardbutton import InlineKeyboardButton +from .inlinekeyboardmarkup import InlineKeyboardMarkup +from .inlinequery import InlineQuery +from .inlinequeryresult import InlineQueryResult +from .inlinequeryresultarticle import InlineQueryResultArticle +from .inlinequeryresultaudio import InlineQueryResultAudio +from .inlinequeryresultcachedaudio import InlineQueryResultCachedAudio +from .inlinequeryresultcacheddocument import InlineQueryResultCachedDocument +from .inlinequeryresultcachedgif import InlineQueryResultCachedGif +from .inlinequeryresultcachedmpeg4gif import InlineQueryResultCachedMpeg4Gif +from .inlinequeryresultcachedphoto import InlineQueryResultCachedPhoto +from .inlinequeryresultcachedsticker import InlineQueryResultCachedSticker +from .inlinequeryresultcachedvideo import InlineQueryResultCachedVideo +from .inlinequeryresultcachedvoice import InlineQueryResultCachedVoice +from .inlinequeryresultcontact import InlineQueryResultContact +from .inlinequeryresultdocument import InlineQueryResultDocument +from .inlinequeryresultgif import InlineQueryResultGif +from .inlinequeryresultlocation import InlineQueryResultLocation +from .inlinequeryresultmpeg4gif import InlineQueryResultMpeg4Gif +from .inlinequeryresultphoto import InlineQueryResultPhoto +from .inlinequeryresultvenue import InlineQueryResultVenue +from .inlinequeryresultvideo import InlineQueryResultVideo +from .inlinequeryresultvoice import InlineQueryResultVoice +from .inputtextmessagecontent import InputTextMessageContent +from .inputlocationmessagecontent import InputLocationMessageContent +from .inputvenuemessagecontent import InputVenueMessageContent +from .inputcontactmessagecontent import InputContactMessageContent from .update import Update from .bot import Bot -def Updater(*args, **kwargs): - """ - Load the updater module on invocation and return an Updater instance. - """ - import warnings - warnings.warn("telegram.Updater is being deprecated, please use " - "telegram.ext.Updater from now on.") - from .ext.updater import Updater as Up - return Up(*args, **kwargs) - - -def Dispatcher(*args, **kwargs): - """ - Load the dispatcher module on invocation and return an Dispatcher instance. - """ - import warnings - warnings.warn("telegram.Dispatcher is being deprecated, please use " - "telegram.ext.Dispatcher from now on.") - from .ext.dispatcher import Dispatcher as Dis - return Dis(*args, **kwargs) - - -def JobQueue(*args, **kwargs): - """ - Load the jobqueue module on invocation and return a JobQueue instance. - """ - import warnings - warnings.warn("telegram.JobQueue is being deprecated, please use " - "telegram.ext.JobQueue from now on.") - from .ext.jobqueue import JobQueue as JobQ - return JobQ(*args, **kwargs) - - __author__ = 'devs@python-telegram-bot.org' -__version__ = '3.4' -__all__ = ('Audio', 'Bot', 'Chat', 'Emoji', 'TelegramError', 'InputFile', - 'Contact', 'ForceReply', 'ReplyKeyboardHide', 'ReplyKeyboardMarkup', - 'UserProfilePhotos', 'ChatAction', 'Location', 'Video', 'Document', - 'Sticker', 'File', 'PhotoSize', 'Update', 'ParseMode', 'Message', - 'User', 'TelegramObject', 'NullHandler', 'Voice', 'InlineQuery', - 'ReplyMarkup', 'ChosenInlineResult', 'InlineQueryResultArticle', - 'InlineQueryResultGif', 'InlineQueryResultPhoto', - 'InlineQueryResultMpeg4Gif', 'InlineQueryResultVideo') +__version__ = '4.0rc1' +__all__ = ['Audio', + 'Bot', + 'Chat', + 'ChatAction', + 'ChosenInlineResult', + 'CallbackQuery', + 'Contact', + 'Document', + 'Emoji', + 'File', + 'ForceReply', + 'InlineKeyboardButton', + 'InlineKeyboardMarkup', + 'InlineQuery', + 'InlineQueryResult', + 'InlineQueryResult', + 'InlineQueryResultArticle', + 'InlineQueryResultAudio', + 'InlineQueryResultCachedAudio', + 'InlineQueryResultCachedDocument', + 'InlineQueryResultCachedGif', + 'InlineQueryResultCachedMpeg4Gif', + 'InlineQueryResultCachedPhoto', + 'InlineQueryResultCachedSticker', + 'InlineQueryResultCachedVideo', + 'InlineQueryResultCachedVoice', + 'InlineQueryResultContact', + 'InlineQueryResultDocument', + 'InlineQueryResultGif', + 'InlineQueryResultLocation', + 'InlineQueryResultMpeg4Gif', + 'InlineQueryResultPhoto', + 'InlineQueryResultVenue', + 'InlineQueryResultVideo', + 'InlineQueryResultVoice', + 'InputContactMessageContent', + 'InputFile', + 'InputLocationMessageContent', + 'InputMessageContent', + 'InputTextMessageContent', + 'InputVenueMessageContent', + 'KeyboardButton', + 'Location', + 'Message', + 'MessageEntity', + 'NullHandler', + 'ParseMode', + 'PhotoSize', + 'ReplyKeyboardHide', + 'ReplyKeyboardMarkup', + 'ReplyMarkup', + 'Sticker', + 'TelegramError', + 'TelegramObject', + 'Update', + 'User', + 'UserProfilePhotos', + 'Venue', + 'Video', + 'Voice'] diff --git a/telegram/base.py b/telegram/base.py index 960776c1e..f86f1433a 100644 --- a/telegram/base.py +++ b/telegram/base.py @@ -43,7 +43,12 @@ class TelegramObject(object): Returns: telegram.TelegramObject: """ - raise NotImplementedError + if not data: + return None + + data = data.copy() + + return data def to_json(self): """ @@ -60,7 +65,7 @@ class TelegramObject(object): data = dict() for key, value in self.__dict__.items(): - if value: + if value or value == '': if hasattr(value, 'to_dict'): data[key] = value.to_dict() else: diff --git a/telegram/bot.py b/telegram/bot.py index a252596f4..e6a4b35d2 100644 --- a/telegram/bot.py +++ b/telegram/bot.py @@ -20,174 +20,152 @@ """This module contains a object that represents a Telegram Bot.""" -import functools import logging +import functools -from telegram import (User, Message, Update, UserProfilePhotos, File, - TelegramError, ReplyMarkup, TelegramObject, NullHandler) -from telegram.error import InvalidToken +from telegram import User, Message, Update, UserProfilePhotos, File, \ + ReplyMarkup, TelegramObject, NullHandler from telegram.utils import request -from telegram.utils.validate import validate_string +from telegram.utils.validate import validate_token logging.getLogger(__name__).addHandler(NullHandler()) class Bot(TelegramObject): - """This object represents a Telegram Bot. Attributes: - id (int): - first_name (str): - last_name (str): - username (str): - name (str): + id (int): Unique identifier for this bot. + first_name (str): Bot's first name. + last_name (str): Bot's last name. + username (str): Bot's username. + name (str): Bot's @username. Args: - token (str): + token (str): Bot's unique authentication. **kwargs: Arbitrary keyword arguments. Keyword Args: - base_url (Optional[str]): + base_url (Optional[str]): Telegram Bot API service URL. + base_file_url (Optional[str]): Telegram Bot API file URL. """ def __init__(self, token, - base_url=None): - self.token = self._valid_token(token) + base_url=None, + base_file_url=None): + self.token = validate_token(token) - if base_url is None: - self.base_url = 'https://api.telegram.org/bot%s' % self.token + if not base_url: + self.base_url = 'https://api.telegram.org/bot{0}'.format( + self.token) else: self.base_url = base_url + self.token - self.base_file_url = 'https://api.telegram.org/file/bot%s' % self.token + if not base_file_url: + self.base_file_url = 'https://api.telegram.org/file/bot{0}'.format( + self.token) + else: + self.base_file_url = base_file_url + self.token self.bot = None self.logger = logging.getLogger(__name__) def info(func): - """ - Returns: - """ @functools.wraps(func) def decorator(self, *args, **kwargs): - """ - decorator - """ if not self.bot: self.getMe() result = func(self, *args, **kwargs) return result + return decorator @property @info def id(self): - """int: """ return self.bot.id @property @info def first_name(self): - """str: """ return self.bot.first_name @property @info def last_name(self): - """str: """ return self.bot.last_name @property @info def username(self): - """str: """ return self.bot.username @property def name(self): - """str: """ - return '@%s' % self.username + return '@{0}'.format(self.username) def log(func): - """ - Returns: - A telegram.Message instance representing the message posted. - """ logger = logging.getLogger(func.__module__) @functools.wraps(func) def decorator(self, *args, **kwargs): - """ - decorator - """ logger.debug('Entering: %s', func.__name__) result = func(self, *args, **kwargs) logger.debug(result) logger.debug('Exiting: %s', func.__name__) return result + return decorator def message(func): - """ - Returns: - A telegram.Message instance representing the message posted. - """ @functools.wraps(func) def decorator(self, *args, **kwargs): - """ - decorator - """ url, data = func(self, *args, **kwargs) - return Bot._post_message(url, data, kwargs) + + if kwargs.get('reply_to_message_id'): + data['reply_to_message_id'] = \ + kwargs.get('reply_to_message_id') + + if kwargs.get('disable_notification'): + data['disable_notification'] = \ + kwargs.get('disable_notification') + + 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=kwargs.get('timeout'), + network_delay=kwargs.get('network_delay')) + + if result is True: + return result + + return Message.de_json(result) + 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('disable_notification'): - disable_notification = kwargs.get('disable_notification') - data['disable_notification'] = disable_notification - - 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. Returns: - A telegram.User instance representing that bot if the - credentials are valid, None otherwise. + :class:`telegram.User`: A :class:`telegram.User` instance + representing that bot if the credentials are valid, `None` + otherwise. + + Raises: + :class:`telegram.TelegramError` + """ - url = '%s/getMe' % self.base_url + + url = '{0}/getMe'.format(self.base_url) result = request.get(url) @@ -206,32 +184,45 @@ class Bot(TelegramObject): """Use this method to send text messages. Args: - chat_id: - Unique identifier for the message recipient - telegram.Chat id. - parse_mode: - Send 'Markdown', if you want Telegram apps to show bold, italic and - inline URLs in your bot's message. [Optional] - text: - Text of the message to be sent. The current maximum length is 4096 - UTF8 characters. - disable_web_page_preview: - Disables link previews for links in this message. [Optional] - disable_notification: - Sends the message silently. iOS users will not receive - a notification, Android users will receive a notification - with no sound. Other apps coming soon. [Optional] - reply_to_message_id: - If the message is a reply, ID of the original message. [Optional] - reply_markup: - Additional interface options. A JSON-serialized object for a custom - reply keyboard, instructions to hide keyboard or to force a reply - from the user. [Optional] + chat_id (str): Unique identifier for the target chat or + username of the target channel (in the format + @channelusername). + text (str): Text of the message to be sent. The current maximum + length is 4096 UTF-8 characters. + parse_mode (Optional[str]): Send Markdown or HTML, if you want + Telegram apps to show bold, italic, fixed-width text or inline + URLs in your bot's message. + disable_web_page_preview (Optional[bool]): Disables link previews + for links in this message. + **kwargs (dict): Arbitrary keyword arguments. + + Keyword Args: + disable_notification (Optional[bool]): Sends the message silently. + iOS users will not receive a notification, Android users will + receive a notification with no sound. + reply_to_message_id (Optional[int]): If the message is a reply, + ID of the original message. + reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional + interface options. A JSON-serialized object for an inline + keyboard, custom reply keyboard, instructions to hide reply + keyboard or to force a reply from the user. + timeout (Optional[float]): If this value is specified, use it as + the definitive timeout (in seconds) for urlopen() operations. + network_delay (Optional[float]): If using the timeout (which is + a `timeout` for the Telegram servers operation), + then `network_delay` as an extra delay (in seconds) to + compensate for network latency. Defaults to 2. Returns: - A telegram.Message instance representing the message posted. + :class:`telegram.Message`: On success, the sent message is + returned. + + Raises: + :class:`telegram.TelegramError` + """ - url = '%s/sendMessage' % self.base_url + url = '{0}/sendMessage'.format(self.base_url) data = {'chat_id': chat_id, 'text': text} @@ -248,7 +239,8 @@ class Bot(TelegramObject): def forwardMessage(self, chat_id, from_chat_id, - message_id): + message_id, + **kwargs): """Use this method to forward messages of any kind. Args: @@ -259,18 +251,31 @@ class Bot(TelegramObject): - Chat id. message_id: Unique message identifier. - disable_notification: - Sends the message silently. iOS users will not receive - a notification, Android users will receive a notification - with no sound. Other apps coming soon. [Optional] + + Keyword Args: + disable_notification (Optional[bool]): Sends the message silently. + iOS users will not receive a notification, Android users will + receive a notification with no sound. + timeout (Optional[float]): If this value is specified, use it as + the definitive timeout (in seconds) for urlopen() operations. + network_delay (Optional[float]): If using the timeout (which is + a `timeout` for the Telegram servers operation), + then `network_delay` as an extra delay (in seconds) to + compensate for network latency. Defaults to 2. Returns: - A telegram.Message instance representing the message forwarded. + :class:`telegram.Message`: On success, instance representing the + message forwarded. + + Raises: + :class:`telegram.TelegramError` + """ - url = '%s/forwardMessage' % self.base_url + url = '{0}/forwardMessage'.format(self.base_url) data = {} + if chat_id: data['chat_id'] = chat_id if from_chat_id: @@ -299,22 +304,34 @@ class Bot(TelegramObject): caption: Photo caption (may also be used when resending photos by file_id). [Optional] - disable_notification: - Sends the message silently. iOS users will not receive - a notification, Android users will receive a notification - with no sound. Other apps coming soon. [Optional] - reply_to_message_id: - If the message is a reply, ID of the original message. [Optional] - reply_markup: - Additional interface options. A JSON-serialized object for a custom - reply keyboard, instructions to hide keyboard or to force a reply - from the user. [Optional] + + Keyword Args: + disable_notification (Optional[bool]): Sends the message silently. + iOS users will not receive a notification, Android users will + receive a notification with no sound. + reply_to_message_id (Optional[int]): If the message is a reply, + ID of the original message. + reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional + interface options. A JSON-serialized object for an inline + keyboard, custom reply keyboard, instructions to hide reply + keyboard or to force a reply from the user. + timeout (Optional[float]): If this value is specified, use it as + the definitive timeout (in seconds) for urlopen() operations. + network_delay (Optional[float]): If using the timeout (which is + a `timeout` for the Telegram servers operation), + then `network_delay` as an extra delay (in seconds) to + compensate for network latency. Defaults to 2. Returns: - A telegram.Message instance representing the message posted. + :class:`telegram.Message`: On success, instance representing the + message posted. + + Raises: + :class:`telegram.TelegramError` + """ - url = '%s/sendPhoto' % self.base_url + url = '{0}/sendPhoto'.format(self.base_url) data = {'chat_id': chat_id, 'photo': photo} @@ -357,22 +374,34 @@ class Bot(TelegramObject): Performer of sent audio. [Optional] title: Title of sent audio. [Optional] - disable_notification: - Sends the message silently. iOS users will not receive - a notification, Android users will receive a notification - with no sound. Other apps coming soon. [Optional] - reply_to_message_id: - If the message is a reply, ID of the original message. [Optional] - reply_markup: - Additional interface options. A JSON-serialized object for a - custom reply keyboard, instructions to hide keyboard or to force a - reply from the user. [Optional] + + Keyword Args: + disable_notification (Optional[bool]): Sends the message silently. + iOS users will not receive a notification, Android users will + receive a notification with no sound. + reply_to_message_id (Optional[int]): If the message is a reply, + ID of the original message. + reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional + interface options. A JSON-serialized object for an inline + keyboard, custom reply keyboard, instructions to hide reply + keyboard or to force a reply from the user. + timeout (Optional[float]): If this value is specified, use it as + the definitive timeout (in seconds) for urlopen() operations. + network_delay (Optional[float]): If using the timeout (which is + a `timeout` for the Telegram servers operation), + then `network_delay` as an extra delay (in seconds) to + compensate for network latency. Defaults to 2. Returns: - A telegram.Message instance representing the message posted. + :class:`telegram.Message`: On success, instance representing the + message posted. + + Raises: + :class:`telegram.TelegramError` + """ - url = '%s/sendAudio' % self.base_url + url = '{0}/sendAudio'.format(self.base_url) data = {'chat_id': chat_id, 'audio': audio} @@ -405,22 +434,34 @@ class Bot(TelegramObject): filename: File name that shows in telegram message (it is usefull when you send file generated by temp module, for example). [Optional] - disable_notification: - Sends the message silently. iOS users will not receive - a notification, Android users will receive a notification - with no sound. Other apps coming soon. [Optional] - reply_to_message_id: - If the message is a reply, ID of the original message. [Optional] - reply_markup: - Additional interface options. A JSON-serialized object for a - custom reply keyboard, instructions to hide keyboard or to force a - reply from the user. [Optional] + + Keyword Args: + disable_notification (Optional[bool]): Sends the message silently. + iOS users will not receive a notification, Android users will + receive a notification with no sound. + reply_to_message_id (Optional[int]): If the message is a reply, + ID of the original message. + reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional + interface options. A JSON-serialized object for an inline + keyboard, custom reply keyboard, instructions to hide reply + keyboard or to force a reply from the user. + timeout (Optional[float]): If this value is specified, use it as + the definitive timeout (in seconds) for urlopen() operations. + network_delay (Optional[float]): If using the timeout (which is + a `timeout` for the Telegram servers operation), + then `network_delay` as an extra delay (in seconds) to + compensate for network latency. Defaults to 2. Returns: - A telegram.Message instance representing the message posted. + :class:`telegram.Message`: On success, instance representing the + message posted. + + Raises: + :class:`telegram.TelegramError` + """ - url = '%s/sendDocument' % self.base_url + url = '{0}/sendDocument'.format(self.base_url) data = {'chat_id': chat_id, 'document': document} @@ -445,22 +486,34 @@ class Bot(TelegramObject): Sticker to send. You can either pass a file_id as String to resend a sticker that is already on the Telegram servers, or upload a new sticker using multipart/form-data. - disable_notification: - Sends the message silently. iOS users will not receive - a notification, Android users will receive a notification - with no sound. Other apps coming soon. [Optional] - reply_to_message_id: - If the message is a reply, ID of the original message. [Optional] - reply_markup: - Additional interface options. A JSON-serialized object for a - custom reply keyboard, instructions to hide keyboard or to force a - reply from the user. [Optional] + + Keyword Args: + disable_notification (Optional[bool]): Sends the message silently. + iOS users will not receive a notification, Android users will + receive a notification with no sound. + reply_to_message_id (Optional[int]): If the message is a reply, + ID of the original message. + reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional + interface options. A JSON-serialized object for an inline + keyboard, custom reply keyboard, instructions to hide reply + keyboard or to force a reply from the user. + timeout (Optional[float]): If this value is specified, use it as + the definitive timeout (in seconds) for urlopen() operations. + network_delay (Optional[float]): If using the timeout (which is + a `timeout` for the Telegram servers operation), + then `network_delay` as an extra delay (in seconds) to + compensate for network latency. Defaults to 2. Returns: - A telegram.Message instance representing the message posted. + :class:`telegram.Message`: On success, instance representing the + message posted. + + Raises: + :class:`telegram.TelegramError` + """ - url = '%s/sendSticker' % self.base_url + url = '{0}/sendSticker'.format(self.base_url) data = {'chat_id': chat_id, 'sticker': sticker} @@ -468,12 +521,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). @@ -490,25 +543,34 @@ 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] - disable_notification: - Sends the message silently. iOS users will not receive - a notification, Android users will receive a notification - with no sound. Other apps coming soon. [Optional] - reply_to_message_id: - If the message is a reply, ID of the original message. [Optional] - reply_markup: - Additional interface options. A JSON-serialized object for a - custom reply keyboard, instructions to hide keyboard or to force a - reply from the user. [Optional] + + Keyword Args: + disable_notification (Optional[bool]): Sends the message silently. + iOS users will not receive a notification, Android users will + receive a notification with no sound. + reply_to_message_id (Optional[int]): If the message is a reply, + ID of the original message. + reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional + interface options. A JSON-serialized object for an inline + keyboard, custom reply keyboard, instructions to hide reply + keyboard or to force a reply from the user. + timeout (Optional[float]): If this value is specified, use it as + the definitive timeout (in seconds) for urlopen() operations. + network_delay (Optional[float]): If using the timeout (which is + a `timeout` for the Telegram servers operation), + then `network_delay` as an extra delay (in seconds) to + compensate for network latency. Defaults to 2. Returns: - A telegram.Message instance representing the message posted. + :class:`telegram.Message`: On success, instance representing the + message posted. + + Raises: + :class:`telegram.TelegramError` + """ - url = '%s/sendVideo' % self.base_url + url = '{0}/sendVideo'.format(self.base_url) data = {'chat_id': chat_id, 'video': video} @@ -518,7 +580,7 @@ class Bot(TelegramObject): if caption: data['caption'] = caption - return self._post_message(url, data, kwargs, timeout=timeout) + return url, data @log @message @@ -543,22 +605,34 @@ class Bot(TelegramObject): a new audio file using multipart/form-data. duration: Duration of sent audio in seconds. [Optional] - disable_notification: - Sends the message silently. iOS users will not receive - a notification, Android users will receive a notification - with no sound. Other apps coming soon. [Optional] - reply_to_message_id: - If the message is a reply, ID of the original message. [Optional] - reply_markup: - Additional interface options. A JSON-serialized object for a - custom reply keyboard, instructions to hide keyboard or to force a - reply from the user. [Optional] + + Keyword Args: + disable_notification (Optional[bool]): Sends the message silently. + iOS users will not receive a notification, Android users will + receive a notification with no sound. + reply_to_message_id (Optional[int]): If the message is a reply, + ID of the original message. + reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional + interface options. A JSON-serialized object for an inline + keyboard, custom reply keyboard, instructions to hide reply + keyboard or to force a reply from the user. + timeout (Optional[float]): If this value is specified, use it as + the definitive timeout (in seconds) for urlopen() operations. + network_delay (Optional[float]): If using the timeout (which is + a `timeout` for the Telegram servers operation), + then `network_delay` as an extra delay (in seconds) to + compensate for network latency. Defaults to 2. Returns: - A telegram.Message instance representing the message posted. + :class:`telegram.Message`: On success, instance representing the + message posted. + + Raises: + :class:`telegram.TelegramError` + """ - url = '%s/sendVoice' % self.base_url + url = '{0}/sendVoice'.format(self.base_url) data = {'chat_id': chat_id, 'voice': voice} @@ -584,22 +658,34 @@ class Bot(TelegramObject): Latitude of location. longitude: Longitude of location. - disable_notification: - Sends the message silently. iOS users will not receive - a notification, Android users will receive a notification - with no sound. Other apps coming soon. [Optional] - reply_to_message_id: - If the message is a reply, ID of the original message. [Optional] - reply_markup: - Additional interface options. A JSON-serialized object for a - custom reply keyboard, instructions to hide keyboard or to force a - reply from the user. [Optional] + + Keyword Args: + disable_notification (Optional[bool]): Sends the message silently. + iOS users will not receive a notification, Android users will + receive a notification with no sound. + reply_to_message_id (Optional[int]): If the message is a reply, + ID of the original message. + reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional + interface options. A JSON-serialized object for an inline + keyboard, custom reply keyboard, instructions to hide reply + keyboard or to force a reply from the user. + timeout (Optional[float]): If this value is specified, use it as + the definitive timeout (in seconds) for urlopen() operations. + network_delay (Optional[float]): If using the timeout (which is + a `timeout` for the Telegram servers operation), + then `network_delay` as an extra delay (in seconds) to + compensate for network latency. Defaults to 2. Returns: - A telegram.Message instance representing the message posted. + :class:`telegram.Message`: On success, instance representing the + message posted. + + Raises: + :class:`telegram.TelegramError` + """ - url = '%s/sendLocation' % self.base_url + url = '{0}/sendLocation'.format(self.base_url) data = {'chat_id': chat_id, 'latitude': latitude, @@ -607,6 +693,132 @@ class Bot(TelegramObject): return url, data + @log + @message + def sendVenue(self, + chat_id, + latitude, + longitude, + title, + address, + foursquare_id=None, + **kwargs): + """ + Use this method to send information about a venue. + + Args: + chat_id: + Unique identifier for the target chat or username of the target + channel (in the format @channelusername). + latitude: + Latitude of the venue. + longitude: + Longitude of the venue. + title: + Name of the venue. + address: + Address of the venue. + foursquare_id: + Foursquare identifier of the venue. + + Keyword Args: + disable_notification (Optional[bool]): Sends the message silently. + iOS users will not receive a notification, Android users will + receive a notification with no sound. + reply_to_message_id (Optional[int]): If the message is a reply, + ID of the original message. + reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional + interface options. A JSON-serialized object for an inline + keyboard, custom reply keyboard, instructions to hide reply + keyboard or to force a reply from the user. + timeout (Optional[float]): If this value is specified, use it as + the definitive timeout (in seconds) for urlopen() operations. + network_delay (Optional[float]): If using the timeout (which is + a `timeout` for the Telegram servers operation), + then `network_delay` as an extra delay (in seconds) to + compensate for network latency. Defaults to 2. + + Returns: + :class:`telegram.Message`: On success, instance representing the + message posted. + + Raises: + :class:`telegram.TelegramError` + + """ + + url = '{0}/sendVenue'.format(self.base_url) + + data = {'chat_id': chat_id, + 'latitude': latitude, + 'longitude': longitude, + 'address': address, + 'title': title} + + if foursquare_id: + data['foursquare_id'] = foursquare_id + + return url, data + + @log + @message + def sendContact(self, + chat_id, + phone_number, + first_name, + last_name=None, + **kwargs): + """ + Use this method to send phone contacts. + + Args: + chat_id: + Unique identifier for the target chat or username of the target + channel (in the format @channelusername). + phone_number: + Contact's phone number. + first_name: + Contact's first name. + last_name: + Contact's last name. + + Keyword Args: + disable_notification (Optional[bool]): Sends the message silently. + iOS users will not receive a notification, Android users will + receive a notification with no sound. + reply_to_message_id (Optional[int]): If the message is a reply, + ID of the original message. + reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional + interface options. A JSON-serialized object for an inline + keyboard, custom reply keyboard, instructions to hide reply + keyboard or to force a reply from the user. + timeout (Optional[float]): If this value is specified, use it as + the definitive timeout (in seconds) for urlopen() operations. + network_delay (Optional[float]): If using the timeout (which is + a `timeout` for the Telegram servers operation), + then `network_delay` as an extra delay (in seconds) to + compensate for network latency. Defaults to 2. + + Returns: + :class:`telegram.Message`: On success, instance representing the + message posted. + + Raises: + :class:`telegram.TelegramError` + + """ + + url = '{0}/sendContact'.format(self.base_url) + + data = {'chat_id': chat_id, + 'phone_number': phone_number, + 'first_name': first_name} + + if last_name: + data['last_name'] = last_name + + return url, data + @log @message def sendChatAction(self, @@ -631,7 +843,7 @@ class Bot(TelegramObject): - ChatAction.FIND_LOCATION for location data. """ - url = '%s/sendChatAction' % self.base_url + url = '{0}/sendChatAction'.format(self.base_url) data = {'chat_id': chat_id, 'action': action} @@ -642,50 +854,61 @@ class Bot(TelegramObject): def answerInlineQuery(self, inline_query_id, results, - cache_time=None, + cache_time=300, is_personal=None, - next_offset=None): - """Use this method to reply to an inline query. + next_offset=None, + switch_pm_text=None, + switch_pm_parameter=None): + """Use this method to send answers to an inline query. No more than + 50 results per query are allowed. 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. + inline_query_id (str): Unique identifier for the answered query. + results (list[:class:`telegram.InlineQueryResult`]): A list of + results for the inline query. + 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. + switch_pm_text (Optional[str]): If passed, clients will display + a button with specified text that switches the user to a + private chat with the bot and sends the bot a start message + with the parameter switch_pm_parameter. + switch_pm_parameter (Optional[str]): Parameter for the start + message sent to the bot when user presses the switch button. Returns: - A boolean if answering was successful + bool: On success, `True` is returned. + + Raises: + :class:`telegram.TelegramError` + """ - validate_string(inline_query_id, 'inline_query_id') - validate_string(inline_query_id, 'next_offset') - - url = '%s/answerInlineQuery' % self.base_url + url = '{0}/answerInlineQuery'.format(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: + if cache_time or cache_time == 0: + data['cache_time'] = cache_time + if is_personal: + data['is_personal'] = is_personal + if next_offset or next_offset == '': data['next_offset'] = next_offset + if switch_pm_text: + data['switch_pm_text'] = switch_pm_text + if switch_pm_parameter: + data['switch_pm_parameter'] = switch_pm_parameter result = request.post(url, data) @@ -709,10 +932,15 @@ class Bot(TelegramObject): are accepted. Defaults to 100. [Optional] Returns: - Returns a telegram.UserProfilePhotos object. + list[:class:`telegram.UserProfilePhotos`]: A list of + :class:`telegram.UserProfilePhotos` objects are returned. + + Raises: + :class:`telegram.TelegramError` + """ - url = '%s/getUserProfilePhotos' % self.base_url + url = '{0}/getUserProfilePhotos'.format(self.base_url) data = {'user_id': user_id} @@ -737,10 +965,15 @@ class Bot(TelegramObject): File identifier to get info about. Returns: - Returns a telegram.File object + :class:`telegram.File`: On success, a :class:`telegram.File` + object is returned. + + Raises: + :class:`telegram.TelegramError` + """ - url = '%s/getFile' % self.base_url + url = '{0}/getFile'.format(self.base_url) data = {'file_id': file_id} @@ -752,6 +985,288 @@ class Bot(TelegramObject): return File.de_json(result) + @log + def kickChatMember(self, + chat_id, + user_id): + """Use this method to kick a user from a group or a supergroup. In the + case of supergroups, the user will not be able to return to the group + on their own using invite links, etc., unless unbanned first. The bot + must be an administrator in the group for this to work. + + Args: + chat_id: + Unique identifier for the target group or username of the target + supergroup (in the format @supergroupusername). + user_id: + Unique identifier of the target user. + + Returns: + bool: On success, `True` is returned. + + Raises: + :class:`telegram.TelegramError` + + """ + + url = '{0}/kickChatMember'.format(self.base_url) + + data = {'chat_id': chat_id, + 'user_id': user_id} + + result = request.post(url, data) + + return result + + @log + def unbanChatMember(self, + chat_id, + user_id): + """Use this method to unban a previously kicked user in a supergroup. + The user will not return to the group automatically, but will be able + to join via link, etc. The bot must be an administrator in the group + for this to work. + + Args: + chat_id: + Unique identifier for the target group or username of the target + supergroup (in the format @supergroupusername). + user_id: + Unique identifier of the target user. + + Returns: + bool: On success, `True` is returned. + + Raises: + :class:`telegram.TelegramError` + + """ + + url = '{0}/unbanChatMember'.format(self.base_url) + + data = {'chat_id': chat_id, + 'user_id': user_id} + + result = request.post(url, data) + + return result + + @log + def answerCallbackQuery(self, + callback_query_id, + text=None, + show_alert=False): + """Use this method to send answers to callback queries sent from + inline keyboards. The answer will be displayed to the user as a + notification at the top of the chat screen or as an alert. + + Args: + callback_query_id (str): Unique identifier for the query to be + answered. + text (Optional[str]): Text of the notification. If not + specified, nothing will be shown to the user. + show_alert (Optional[bool]): If `True`, an alert will be shown + by the client instead of a notification at the top of the chat + screen. Defaults to `False`. + + Returns: + bool: On success, `True` is returned. + + Raises: + :class:`telegram.TelegramError` + + """ + + url = '{0}/answerCallbackQuery'.format(self.base_url) + + data = {'callback_query_id': callback_query_id} + + if text: + data['text'] = text + if show_alert: + data['show_alert'] = show_alert + + result = request.post(url, data) + + return result + + @log + def editMessageText(self, + text, + chat_id=None, + message_id=None, + inline_message_id=None, + parse_mode=None, + disable_web_page_preview=None, + reply_markup=None): + """Use this method to edit text messages sent by the bot or via the bot + (for inline bots). + + Args: + text: + New text of the message. + chat_id: + Required if inline_message_id is not specified. Unique identifier + for the target chat or username of the target channel (in the + format @channelusername). + message_id: + Required if inline_message_id is not specified. Unique identifier + of the sent message. + inline_message_id: + Required if chat_id and message_id are not specified. Identifier of + the inline message. + parse_mode: + Send Markdown or HTML, if you want Telegram apps to show bold, + italic, fixed-width text or inline URLs in your bot's message. + disable_web_page_preview: + Disables link previews for links in this message. + reply_markup: + A JSON-serialized object for an inline keyboard. + + Returns: + :class:`telegram.Message`: On success, if edited message is sent by + the bot, the edited message is returned, otherwise `True` is + returned. + + Raises: + :class:`telegram.TelegramError` + + """ + + url = '{0}/editMessageText'.format(self.base_url) + + data = {'text': text} + + if chat_id: + data['chat_id'] = chat_id + if message_id: + data['message_id'] = message_id + if inline_message_id: + data['inline_message_id'] = inline_message_id + if parse_mode: + data['parse_mode'] = parse_mode + if disable_web_page_preview: + data['disable_web_page_preview'] = disable_web_page_preview + if 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) + + return Message.de_json(result) + + @log + @message + def editMessageCaption(self, + chat_id=None, + message_id=None, + inline_message_id=None, + caption=None, + **kwargs): + """Use this method to edit captions of messages sent by the bot or + via the bot (for inline bots). + + Args: + chat_id (Optional[str]): Required if inline_message_id is not + specified. Unique identifier for the target chat or username of + the target channel (in the format @channelusername). + message_id (Optional[str]): Required if inline_message_id is not + specified. Unique identifier of the sent message. + inline_message_id (Optional[str]): Required if chat_id and + message_id are not specified. Identifier of the inline message. + caption (Optional[str]): New caption of the message. + **kwargs (Optional[dict]): Arbitrary keyword arguments. + + Keyword Args: + reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): + A JSON-serialized object for an inline keyboard. + timeout (Optional[float]): If this value is specified, use it as + the definitive timeout (in seconds) for urlopen() operations. + network_delay (Optional[float]): If using the timeout (which is + a `timeout` for the Telegram servers operation), + then `network_delay` as an extra delay (in seconds) to + compensate for network latency. Defaults to 2. + + Returns: + :class:`telegram.Message`: On success, if edited message is sent by + the bot, the edited message is returned, otherwise `True` is + returned. + + Raises: + :class:`telegram.TelegramError` + + """ + + url = '{0}/editMessageCaption'.format(self.base_url) + + data = {} + + if caption: + data['caption'] = caption + if chat_id: + data['chat_id'] = chat_id + if message_id: + data['message_id'] = message_id + if inline_message_id: + data['inline_message_id'] = inline_message_id + + return url, data + + @log + @message + def editMessageReplyMarkup(self, + chat_id=None, + message_id=None, + inline_message_id=None, + **kwargs): + """Use this method to edit only the reply markup of messages sent by + the bot or via the bot (for inline bots). + + Args: + chat_id (Optional[str]): Required if inline_message_id is not + specified. Unique identifier for the target chat or username of + the target channel (in the format @channelusername). + message_id (Optional[str]): Required if inline_message_id is not + specified. Unique identifier of the sent message. + inline_message_id (Optional[str]): Required if chat_id and + message_id are not specified. Identifier of the inline message. + **kwargs (Optional[dict]): Arbitrary keyword arguments. + + Keyword Args: + reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): + A JSON-serialized object for an inline keyboard. + timeout (Optional[float]): If this value is specified, use it as + the definitive timeout (in seconds) for urlopen() operations. + network_delay (Optional[float]): If using the timeout (which is + a `timeout` for the Telegram servers operation), + then `network_delay` as an extra delay (in seconds) to + compensate for network latency. Defaults to 2. + + Returns: + :class:`telegram.Message`: On success, if edited message is sent by + the bot, the edited message is returned, otherwise `True` is + returned. + + Raises: + :class:`telegram.TelegramError` + + """ + + url = '{0}/editMessageReplyMarkup'.format(self.base_url) + + data = {} + + if chat_id: + data['chat_id'] = chat_id + if message_id: + data['message_id'] = message_id + if inline_message_id: + data['inline_message_id'] = inline_message_id + + return url, data + @log def getUpdates(self, offset=None, @@ -780,17 +1295,23 @@ class Bot(TelegramObject): long for data to be transmitted from and to the Telegram servers. Returns: - A list of telegram.Update objects are returned. + list[:class:`telegram.Message`]: A list of :class:`telegram.Update` + objects are returned. + + Raises: + :class:`telegram.TelegramError` + """ - url = '%s/getUpdates' % self.base_url + url = '{0}/getUpdates'.format(self.base_url) data = {} + if offset: data['offset'] = offset if limit: data['limit'] = limit - if timeout: + if timeout or timeout == 0: data['timeout'] = timeout result = request.post(url, data, network_delay=network_delay) @@ -819,12 +1340,18 @@ class Bot(TelegramObject): Use an empty string to remove webhook integration Returns: - True if successful else TelegramError was raised + bool: On success, `True` is returned. + + Raises: + :class:`telegram.TelegramError` + """ - url = '%s/setWebhook' % self.base_url + + url = '{0}/setWebhook'.format(self.base_url) data = {} - if webhook_url: + + if webhook_url or webhook_url == '': data['url'] = webhook_url if certificate: data['certificate'] = certificate @@ -835,28 +1362,21 @@ class Bot(TelegramObject): @staticmethod def de_json(data): - pass + data = super(Bot, Bot).de_json(data) + + return Bot(**data) def to_dict(self): - """ - Returns: - dict: - """ data = {'id': self.id, 'username': self.username, 'first_name': self.username} + if self.last_name: data['last_name'] = self.last_name + return data def __reduce__(self): return (self.__class__, (self.token, - self.base_url.replace(self.token, ''))) - - @staticmethod - def _valid_token(token): - """a very basic validation on token""" - left, sep, _right = token.partition(':') - if (not sep) or (not left.isdigit()) or (len(left) < 3): - raise InvalidToken() - return token + self.base_url.replace(self.token, ''), + self.base_file_url.replace(self.token, ''))) diff --git a/telegram/callbackquery.py b/telegram/callbackquery.py new file mode 100644 index 000000000..4920fde40 --- /dev/null +++ b/telegram/callbackquery.py @@ -0,0 +1,50 @@ +#!/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 a object that represents a Telegram +CallbackQuery""" + +from telegram import TelegramObject, Message, User + + +class CallbackQuery(TelegramObject): + """This object represents a Telegram CallbackQuery.""" + + def __init__(self, + id, + from_user, + data, + **kwargs): + # Required + self.id = id + self.from_user = from_user + self.data = data + # Optionals + self.message = kwargs.get('message') + self.inline_message_id = kwargs.get('inline_message_id', '') + + @staticmethod + def de_json(data): + if not data: + return None + + data['from_user'] = User.de_json(data.get('from')) + data['message'] = Message.de_json(data.get('message')) + + return CallbackQuery(**data) diff --git a/telegram/choseninlineresult.py b/telegram/choseninlineresult.py index 87071833e..2b897d327 100644 --- a/telegram/choseninlineresult.py +++ b/telegram/choseninlineresult.py @@ -21,8 +21,7 @@ This module contains a object that represents a Telegram ChosenInlineResult """ - -from telegram import TelegramObject, User +from telegram import TelegramObject, User, Location class ChosenInlineResult(TelegramObject): @@ -46,11 +45,16 @@ class ChosenInlineResult(TelegramObject): def __init__(self, result_id, from_user, - query): + query, + location=None, + inline_message_id=None): # Required self.result_id = result_id self.from_user = from_user self.query = query + # Optionals + self.location = location + self.inline_message_id = inline_message_id @staticmethod def de_json(data): @@ -63,8 +67,11 @@ class ChosenInlineResult(TelegramObject): """ if not data: return None - data = data.copy() + + # Required data['from_user'] = User.de_json(data.pop('from')) + # Optionals + data['location'] = Location.de_json(data.get('location')) return ChosenInlineResult(**data) diff --git a/telegram/emoji.py b/telegram/emoji.py index 74ac58370..d6db95e4c 100644 --- a/telegram/emoji.py +++ b/telegram/emoji.py @@ -163,25 +163,25 @@ class Emoji(object): SQUARED_SOS = n(b'\xF0\x9F\x86\x98') SQUARED_UP_WITH_EXCLAMATION_MARK = n(b'\xF0\x9F\x86\x99') SQUARED_VS = n(b'\xF0\x9F\x86\x9A') - REGIONAL_INDICATOR_SYMBOL_LETTER_D_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_E\ + REGIONAL_INDICATOR_SYMBOL_LETTER_D_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_E \ = n(b'\xF0\x9F\x87\xA9\xF0\x9F\x87\xAA') - REGIONAL_INDICATOR_SYMBOL_LETTER_G_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_B\ + REGIONAL_INDICATOR_SYMBOL_LETTER_G_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_B \ = n(b'\xF0\x9F\x87\xAC\xF0\x9F\x87\xA7') - REGIONAL_INDICATOR_SYMBOL_LETTER_C_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_N\ + REGIONAL_INDICATOR_SYMBOL_LETTER_C_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_N \ = n(b'\xF0\x9F\x87\xA8\xF0\x9F\x87\xB3') - REGIONAL_INDICATOR_SYMBOL_LETTER_J_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_P\ + REGIONAL_INDICATOR_SYMBOL_LETTER_J_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_P \ = n(b'\xF0\x9F\x87\xAF\xF0\x9F\x87\xB5') - REGIONAL_INDICATOR_SYMBOL_LETTER_K_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_R\ + REGIONAL_INDICATOR_SYMBOL_LETTER_K_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_R \ = n(b'\xF0\x9F\x87\xB0\xF0\x9F\x87\xB7') - REGIONAL_INDICATOR_SYMBOL_LETTER_F_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_R\ + REGIONAL_INDICATOR_SYMBOL_LETTER_F_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_R \ = n(b'\xF0\x9F\x87\xAB\xF0\x9F\x87\xB7') - REGIONAL_INDICATOR_SYMBOL_LETTER_E_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_S\ + REGIONAL_INDICATOR_SYMBOL_LETTER_E_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_S \ = n(b'\xF0\x9F\x87\xAA\xF0\x9F\x87\xB8') - REGIONAL_INDICATOR_SYMBOL_LETTER_I_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_T\ + REGIONAL_INDICATOR_SYMBOL_LETTER_I_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_T \ = n(b'\xF0\x9F\x87\xAE\xF0\x9F\x87\xB9') - REGIONAL_INDICATOR_SYMBOL_LETTER_U_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_S\ + REGIONAL_INDICATOR_SYMBOL_LETTER_U_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_S \ = n(b'\xF0\x9F\x87\xBA\xF0\x9F\x87\xB8') - REGIONAL_INDICATOR_SYMBOL_LETTER_R_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_U\ + REGIONAL_INDICATOR_SYMBOL_LETTER_R_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_U \ = n(b'\xF0\x9F\x87\xB7\xF0\x9F\x87\xBA') SQUARED_KATAKANA_KOKO = n(b'\xF0\x9F\x88\x81') SQUARED_KATAKANA_SA = n(b'\xF0\x9F\x88\x82') diff --git a/telegram/error.py b/telegram/error.py index 28c6c6ecf..6416aa63e 100644 --- a/telegram/error.py +++ b/telegram/error.py @@ -62,13 +62,11 @@ class TelegramError(Exception): class Unauthorized(TelegramError): - def __init__(self): super(Unauthorized, self).__init__('Unauthorized') class InvalidToken(TelegramError): - def __init__(self): super(InvalidToken, self).__init__('Invalid token') @@ -78,6 +76,5 @@ class NetworkError(TelegramError): class TimedOut(NetworkError): - def __init__(self): super(TimedOut, self).__init__('Timed out') diff --git a/telegram/ext/__init__.py b/telegram/ext/__init__.py index ec0c992e3..f2946ff99 100644 --- a/telegram/ext/__init__.py +++ b/telegram/ext/__init__.py @@ -22,5 +22,18 @@ from .dispatcher import Dispatcher from .jobqueue import JobQueue from .updater import Updater +from .callbackqueryhandler import CallbackQueryHandler +from .choseninlineresulthandler import ChosenInlineResultHandler +from .commandhandler import CommandHandler +from .handler import Handler +from .inlinequeryhandler import InlineQueryHandler +from .messagehandler import MessageHandler +from .regexhandler import RegexHandler +from .stringcommandhandler import StringCommandHandler +from .stringregexhandler import StringRegexHandler +from .typehandler import TypeHandler -__all__ = ('Dispatcher', 'JobQueue', 'Updater') +__all__ = ('Dispatcher', 'JobQueue', 'Updater', 'CallbackQueryHandler', + 'ChosenInlineResultHandler', 'CommandHandler', 'Handler', + 'InlineQueryHandler', 'MessageHandler', 'RegexHandler', + 'StringCommandHandler', 'StringRegexHandler', 'TypeHandler') diff --git a/telegram/ext/callbackqueryhandler.py b/telegram/ext/callbackqueryhandler.py new file mode 100644 index 000000000..7d0e6e87a --- /dev/null +++ b/telegram/ext/callbackqueryhandler.py @@ -0,0 +1,48 @@ +#!/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 the CallbackQueryHandler class """ + +from .handler import Handler +from telegram import Update + + +class CallbackQueryHandler(Handler): + """ + Handler class to handle Telegram callback queries. + + Args: + callback (function): A function that takes ``bot, update`` as + positional arguments. It will be called when the ``checkUpdate`` + has determined that an update should be processed by this handler. + pass_update_queue (optional[bool]): If the handler should be passed the + update queue as a keyword argument called ``update_queue``. It can + be used to insert updates. Default is ``False`` + """ + + def __init__(self, callback, pass_update_queue=False): + super(CallbackQueryHandler, self).__init__(callback, pass_update_queue) + + def checkUpdate(self, update): + return isinstance(update, Update) and update.callback_query + + def handleUpdate(self, update, dispatcher): + optional_args = self.collectOptionalArgs(dispatcher) + + self.callback(dispatcher.bot, update, **optional_args) diff --git a/telegram/ext/choseninlineresulthandler.py b/telegram/ext/choseninlineresulthandler.py new file mode 100644 index 000000000..bd7757ec0 --- /dev/null +++ b/telegram/ext/choseninlineresulthandler.py @@ -0,0 +1,50 @@ +#!/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 the ChosenInlineResultHandler class """ + +from .handler import Handler +from telegram import Update + + +class ChosenInlineResultHandler(Handler): + """ + Handler class to handle Telegram updates that contain a chosen inline + result. + + Args: + callback (function): A function that takes ``bot, update`` as + positional arguments. It will be called when the ``checkUpdate`` + has determined that an update should be processed by this handler. + pass_update_queue (optional[bool]): If the handler should be passed the + update queue as a keyword argument called ``update_queue``. It can + be used to insert updates. Default is ``False`` + """ + + def __init__(self, callback, pass_update_queue=False): + super(ChosenInlineResultHandler, self).__init__(callback, + pass_update_queue) + + def checkUpdate(self, update): + return isinstance(update, Update) and update.chosen_inline_result + + def handleUpdate(self, update, dispatcher): + optional_args = self.collectOptionalArgs(dispatcher) + + self.callback(dispatcher.bot, update, **optional_args) diff --git a/telegram/ext/commandhandler.py b/telegram/ext/commandhandler.py new file mode 100644 index 000000000..c140626aa --- /dev/null +++ b/telegram/ext/commandhandler.py @@ -0,0 +1,66 @@ +#!/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 the CommandHandler class """ + +from .handler import Handler +from telegram import Update + + +class CommandHandler(Handler): + """ + Handler class to handle Telegram commands. Commands are Telegram messages + that start with ``/``, optionally followed by an ``@`` and the bot's + name and/or some additional text. + + Args: + command (str): The name of the command this handler should listen for. + callback (function): A function that takes ``bot, update`` as + positional arguments. It will be called when the ``checkUpdate`` + has determined that an update should be processed by this handler. + pass_args (optional[bool]): If the handler should be passed the + arguments passed to the command as a keyword argument called ` + ``args``. It will contain a list of strings, which is the text + following the command split on spaces. Default is ``False`` + pass_update_queue (optional[bool]): If the handler should be passed the + update queue as a keyword argument called ``update_queue``. It can + be used to insert updates. Default is ``False`` + """ + + def __init__(self, command, callback, pass_args=False, + pass_update_queue=False): + super(CommandHandler, self).__init__(callback, pass_update_queue) + self.command = command + self.pass_args = pass_args + + def checkUpdate(self, update): + return (isinstance(update, Update) and + update.message and + update.message.text and + update.message.text.startswith('/') and + update.message.text[1:].split(' ')[0].split('@')[0] == + self.command) + + def handleUpdate(self, update, dispatcher): + optional_args = self.collectOptionalArgs(dispatcher) + + if self.pass_args: + optional_args['args'] = update.message.text.split(' ')[1:] + + self.callback(dispatcher.bot, update, **optional_args) diff --git a/telegram/ext/dispatcher.py b/telegram/ext/dispatcher.py index 6b92c3284..a55b8f04d 100644 --- a/telegram/ext/dispatcher.py +++ b/telegram/ext/dispatcher.py @@ -21,13 +21,17 @@ import logging from functools import wraps -from inspect import getargspec from threading import Thread, BoundedSemaphore, Lock, Event, current_thread -from re import match, split from time import sleep -from telegram import (TelegramError, Update, NullHandler) -from telegram.utils.updatequeue import Empty +# Adjust for differences in Python versions +try: + from queue import Empty # flake8: noqa +except ImportError: + from Queue import Empty # flake8: noqa + +from telegram import (TelegramError, NullHandler) +from telegram.ext.handler import Handler logging.getLogger(__name__).addHandler(NullHandler()) @@ -35,13 +39,12 @@ semaphore = None async_threads = set() """:type: set[Thread]""" async_lock = Lock() +DEFAULT_GROUP = 0 def run_async(func): """ - Function decorator that will run the function in a new thread. A function - decorated with this will have to include **kwargs in their parameter list, - which will contain all optional parameters. + Function decorator that will run the function in a new thread. Args: func (function): The function to run in the thread. @@ -82,72 +85,20 @@ def run_async(func): class Dispatcher(object): """ This class dispatches all kinds of updates to its registered handlers. - A handler is a function that usually takes the following parameters - - bot: - The telegram.Bot instance that received the message - update: - The update that should be handled by the handler - - Error handlers take an additional parameter - - error: - The TelegramError instance that was raised during processing the - update - - All handlers, except error handlers, can also request more information by - appending one or more of the following arguments in their argument list for - convenience - - update_queue: - The Queue instance which contains all new updates and is - processed by the Dispatcher. Be careful with this - you might - create an infinite loop. - args: - If the update is an instance str or telegram.Update, this will be - 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 - procedure just queue in update_queue.put(update, context=context) or - processUpdate(update,context=context). - - context: - Extra data for handling updates. - - For regex-based handlers, you can also request information about the match. - For all other handlers, these will be None - - groups: - A tuple that contains the result of - re.match(matcher, ...).groups() - groupdict: - A dictionary that contains the result of - re.match(matcher, ...).groupdict() Args: bot (telegram.Bot): The bot object that should be passed to the handlers - update_queue (telegram.UpdateQueue): The synchronized queue that will - contain the updates. + update_queue (Queue): The synchronized queue that will contain the + updates. """ def __init__(self, bot, update_queue, workers=4, exception_event=None): 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 = {} - self.string_command_handlers = {} - self.type_handlers = {} - self.unknown_telegram_command_handlers = [] - self.unknown_string_command_handlers = [] + + self.handlers = {} self.error_handlers = [] + self.logger = logging.getLogger(__name__) self.running = False self.__stop_event = Event() @@ -177,10 +128,10 @@ class Dispatcher(object): self.running = True self.logger.debug('Dispatcher started') - while 1: + while True: try: # Pop update from update queue. - update, context = self.update_queue.get(True, 1, True) + update = self.update_queue.get(True, 1) except Empty: if self.__stop_event.is_set(): self.logger.debug('orderly stopping') @@ -191,26 +142,8 @@ class Dispatcher(object): break continue - try: - self.processUpdate(update, context) - self.logger.debug('Processed Update: %s with context %s' - % (update, context)) - - # Dispatch any errors - except TelegramError as te: - self.logger.warn("Error was raised while processing Update.") - - try: - self.dispatchError(update, te) - # Log errors in error handlers - except: - self.logger.exception("An uncaught error was raised while " - "handling the error") - - # All other errors should not stop the thread, just print them - except: - self.logger.exception("An uncaught error was raised while " - "processing an update") + self.logger.debug('Processing Update: %s' % update) + self.processUpdate(update) self.running = False self.logger.debug('Dispatcher thread stopped') @@ -225,481 +158,112 @@ class Dispatcher(object): sleep(0.1) self.__stop_event.clear() - def processUpdate(self, update, context=None): + def processUpdate(self, update): """ Processes a single update. Args: - update (any): + update (object): """ - handled = False - - # Custom type handlers - for t in self.type_handlers: - if isinstance(update, t): - self.dispatchType(update, context) - handled = True - - # string update - if type(update) is str and update.startswith('/'): - self.dispatchStringCommand(update, context) - handled = True - elif type(update) is str: - self.dispatchRegex(update, context) - handled = True - # An error happened while polling if isinstance(update, TelegramError): self.dispatchError(None, update) - handled = True - # Telegram update (regex) - if isinstance(update, Update) and update.message is not None: - self.dispatchRegex(update, context) - handled = True + else: + for group in self.handlers.values(): + for handler in group: + try: + if handler.checkUpdate(update): + handler.handleUpdate(update, self) + break + # Dispatch any errors + except TelegramError as te: + self.logger.warn( + 'A TelegramError was raised while processing the ' + 'Update.') - # Telegram update (command) - if update.message.text.startswith('/'): - self.dispatchTelegramCommand(update, context) + try: + self.dispatchError(update, te) + except: + self.logger.exception( + 'An uncaught error was raised while ' + 'handling the error') + finally: + break - # 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( - "Received update of unknown type %s" % type(update))) + # Errors should not stop the thread + except: + self.logger.exception( + 'An uncaught error was raised while ' + 'processing the update') + break - # Add Handlers - def addTelegramMessageHandler(self, handler): + def addHandler(self, handler, group=DEFAULT_GROUP): """ - Registers a message handler in the Dispatcher. + Register a handler. A handler must be an instance of a subclass of + telegram.ext.Handler. All handlers are organized in groups, the default + group is int(0), but any object can identify a group. Every update will + be tested against each handler in each group from first-added to last- + added. If the update has been handled in one group, it will not be + tested against other handlers in that group. That means an update can + only be handled 0 or 1 times per group, but multiple times across all + groups. Args: - handler (function): A function that takes (Bot, Update, *args) as - arguments. + handler (Handler): A Handler instance + group (optional[object]): The group identifier. Default is 0 """ - self.telegram_message_handlers.append(handler) + if not isinstance(handler, Handler): + raise TypeError('Handler is no instance of telegram.ext.Handler') - def addTelegramInlineHandler(self, handler): + if group not in self.handlers: + self.handlers[group] = list() + + self.handlers[group].append(handler) + + def removeHandler(self, handler, group=DEFAULT_GROUP): """ - Registers an inline query handler in the Dispatcher. + Remove a handler from the specified group Args: - handler (function): A function that takes (Bot, Update, *args) as - arguments. + handler (Handler): A Handler instance + group (optional[object]): The group identifier. Default is 0 """ + if handler in self.handlers[group]: + self.handlers[group].remove(handler) - self.telegram_inline_handlers.append(handler) - - def addTelegramCommandHandler(self, command, handler): - """ - Registers a command handler in the Dispatcher. - - Args: - command (str): The command keyword that this handler should be - listening to. - handler (function): A function that takes (Bot, Update, *args) as - arguments. - """ - - if command not in self.telegram_command_handlers: - self.telegram_command_handlers[command] = [] - - self.telegram_command_handlers[command].append(handler) - - def addTelegramRegexHandler(self, matcher, handler): - """ - Registers a regex handler in the Dispatcher. If handlers will be - called if re.match(matcher, update.message.text) is True. - - Args: - matcher (str/__Regex): A regex string or compiled regex object that - matches on messages that handler should be listening to - handler (function): A function that takes (Bot, Update, *args) as - arguments. - """ - - if matcher not in self.telegram_regex_handlers: - self.telegram_regex_handlers[matcher] = [] - - self.telegram_regex_handlers[matcher].append(handler) - - def addStringCommandHandler(self, command, handler): - """ - Registers a string-command handler in the Dispatcher. - - Args: - command (str): The command keyword that this handler should be - listening to. - handler (function): A function that takes (Bot, str, *args) as - arguments. - """ - - if command not in self.string_command_handlers: - self.string_command_handlers[command] = [] - - self.string_command_handlers[command].append(handler) - - def addStringRegexHandler(self, matcher, handler): - """ - Registers a regex handler in the Dispatcher. If handlers will be - called if re.match(matcher, string) is True. - - Args: - matcher (str/__Regex): A regex string or compiled regex object that - matches on the string input that handler should be listening to - handler (function): A function that takes (Bot, Update, *args) as - arguments. - """ - - if matcher not in self.string_regex_handlers: - self.string_regex_handlers[matcher] = [] - - self.string_regex_handlers[matcher].append(handler) - - def addUnknownTelegramCommandHandler(self, handler): - """ - Registers a command handler in the Dispatcher, that will receive all - commands that have no associated handler. - - Args: - handler (function): A function that takes (Bot, Update, *args) as - arguments. - """ - - self.unknown_telegram_command_handlers.append(handler) - - def addUnknownStringCommandHandler(self, handler): - """ - Registers a string-command handler in the Dispatcher, that will - receive all commands that have no associated handler. - - Args: - handler (function): A function that takes (Bot, str, *args) as - arguments. - """ - - self.unknown_string_command_handlers.append(handler) - - def addErrorHandler(self, handler): + def addErrorHandler(self, callback): """ Registers an error handler in the Dispatcher. Args: - handler (function): A function that takes (Bot, TelegramError) as - arguments. + handler (function): A function that takes ``Bot, Update, + TelegramError`` as arguments. """ - self.error_handlers.append(handler) + self.error_handlers.append(callback) - def addTypeHandler(self, the_type, handler): - """ - Registers a type handler in the Dispatcher. This allows you to send - any type of object into the update queue. - - Args: - the_type (type): The type this handler should listen to - handler (function): A function that takes (Bot, type, *args) as - arguments. - """ - - if the_type not in self.type_handlers: - self.type_handlers[the_type] = [] - - self.type_handlers[the_type].append(handler) - - # Remove Handlers - def removeTelegramMessageHandler(self, handler): - """ - De-registers a message handler. - - Args: - handler (any): - """ - - 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. - - Args: - command (str): The command - handler (any): - """ - - if command in self.telegram_command_handlers \ - and handler in self.telegram_command_handlers[command]: - self.telegram_command_handlers[command].remove(handler) - - def removeTelegramRegexHandler(self, matcher, handler): - """ - De-registers a regex handler. - - Args: - matcher (str/__Regex): The regex matcher object or string - handler (any): - """ - - if matcher in self.telegram_regex_handlers \ - and handler in self.telegram_regex_handlers[matcher]: - self.telegram_regex_handlers[matcher].remove(handler) - - def removeStringCommandHandler(self, command, handler): - """ - De-registers a string-command handler. - - Args: - command (str): The command - handler (any): - """ - - if command in self.string_command_handlers \ - and handler in self.string_command_handlers[command]: - self.string_command_handlers[command].remove(handler) - - def removeStringRegexHandler(self, matcher, handler): - """ - De-registers a regex handler. - - Args: - matcher (str/__Regex): The regex matcher object or string - handler (any): - """ - - if matcher in self.string_regex_handlers \ - and handler in self.string_regex_handlers[matcher]: - self.string_regex_handlers[matcher].remove(handler) - - def removeUnknownTelegramCommandHandler(self, handler): - """ - De-registers an unknown-command handler. - - Args: - handler (any): - """ - - if handler in self.unknown_telegram_command_handlers: - self.unknown_telegram_command_handlers.remove(handler) - - def removeUnknownStringCommandHandler(self, handler): - """ - De-registers an unknown-command handler. - - Args: - handler (any): - """ - - if handler in self.unknown_string_command_handlers: - self.unknown_string_command_handlers.remove(handler) - - def removeErrorHandler(self, handler): + def removeErrorHandler(self, callback): """ De-registers an error handler. Args: - handler (any): + handler (function): """ - if handler in self.error_handlers: - self.error_handlers.remove(handler) - - def removeTypeHandler(self, the_type, handler): - """ - De-registers a type handler. - - Args: - handler (any): - """ - - if the_type in self.type_handlers \ - and handler in self.type_handlers[the_type]: - self.type_handlers[the_type].remove(handler) - - def dispatchTelegramCommand(self, update, context=None): - """ - Dispatches an update that contains a command. - - Args: - command (str): The command keyword - update (telegram.Update): The Telegram update that contains the - command - """ - - command = split('\W', update.message.text[1:])[0] - - if command in self.telegram_command_handlers: - self.dispatchTo(self.telegram_command_handlers[command], update, - context=context) - else: - self.dispatchTo(self.unknown_telegram_command_handlers, update, - context=context) - - def dispatchRegex(self, update, context=None): - """ - Dispatches an update to all string or telegram regex handlers that - match the string/message content. - - Args: - update (str, Update): The update that should be checked for matches - """ - - if isinstance(update, Update): - handlers = self.telegram_regex_handlers - to_match = update.message.text - elif isinstance(update, str): - handlers = self.string_regex_handlers - to_match = update - - for matcher in handlers: - m = match(matcher, to_match) - if m: - for handler in handlers[matcher]: - self.call_handler(handler, - update, - groups=m.groups(), - groupdict=m.groupdict(), - context=context) - - def dispatchStringCommand(self, update, context=None): - """ - Dispatches a string-update that contains a command. - - Args: - update (str): The string input - """ - - command = update.split(' ')[0][1:] - - if command in self.string_command_handlers: - self.dispatchTo(self.string_command_handlers[command], update, - context=context) - else: - self.dispatchTo(self.unknown_string_command_handlers, update, - context=context) - - def dispatchType(self, update, context=None): - """ - Dispatches an update of any type. - - Args: - update (any): The update - """ - - for t in self.type_handlers: - if isinstance(update, t): - self.dispatchTo(self.type_handlers[t], update, context=context) - - def dispatchTelegramMessage(self, update, context=None): - """ - Dispatches an update that contains a regular message. - - Args: - update (telegram.Update): The Telegram update that contains the - message. - """ - - 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) + if callback in self.error_handlers: + self.error_handlers.remove(callback) def dispatchError(self, update, error): """ Dispatches an error. Args: - update (any): The update that caused the error + update (object): The update that caused the error error (telegram.TelegramError): The Telegram error that was raised. """ - for handler in self.error_handlers: - handler(self.bot, update, error) - - def dispatchTo(self, handlers, update, **kwargs): - """ - Dispatches an update to a list of handlers. - - Args: - handlers (list): A list of handler-functions. - update (any): The update to be dispatched - """ - - for handler in handlers: - self.call_handler(handler, update, **kwargs) - - def call_handler(self, handler, update, **kwargs): - """ - Calls an update handler. Checks the handler for keyword arguments and - fills them, if possible. - - Args: - handler (function): An update handler function - update (any): An update - """ - - target_kwargs = {} - fargs = getargspec(handler).args - - ''' - async handlers will receive all optional arguments, since we can't - their argument list. - ''' - - is_async = 'pargs' == getargspec(handler).varargs - - if is_async or 'update_queue' in fargs: - target_kwargs['update_queue'] = self.update_queue - - if is_async or 'args' in fargs: - 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: - args = None - - target_kwargs['args'] = args - - if is_async or 'groups' in fargs: - target_kwargs['groups'] = kwargs.get('groups', None) - - if is_async or 'groupdict' in fargs: - target_kwargs['groupdict'] = kwargs.get('groupdict', None) - - if is_async or 'context' in fargs: - target_kwargs['context'] = kwargs.get('context', None) - - handler(self.bot, update, **target_kwargs) + for callback in self.error_handlers: + callback(self.bot, update, error) diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py new file mode 100644 index 000000000..8bebebe6e --- /dev/null +++ b/telegram/ext/filters.py @@ -0,0 +1,23 @@ +#!/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 the filters used by the MessageHandler class """ + +TEXT, AUDIO, DOCUMENT, PHOTO, STICKER, VIDEO, VOICE, CONTACT, LOCATION, \ + VENUE, STATUS_UPDATE = range(11) diff --git a/telegram/ext/handler.py b/telegram/ext/handler.py new file mode 100644 index 000000000..10646093b --- /dev/null +++ b/telegram/ext/handler.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 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 base class for handlers as used by the +Dispatcher """ + + +class Handler(object): + """ + The base class for all update handlers. You can create your own handlers + by inheriting from this class. + + Args: + callback (function): A function that takes ``bot, update`` as + positional arguments. It will be called when the ``checkUpdate`` + has determined that an update should be processed by this handler. + pass_update_queue (optional[bool]): If the callback should be passed + the update queue as a keyword argument called ``update_queue``. It + can be used to insert updates. Default is ``False`` + """ + + def __init__(self, callback, pass_update_queue=False): + self.callback = callback + self.pass_update_queue = pass_update_queue + + def checkUpdate(self, update): + """ + This method is called to determine if an update should be handled by + this handler instance. It should always be overridden. + + Args: + update (object): The update to be tested + """ + raise NotImplementedError + + def handleUpdate(self, update, dispatcher): + """ + This method is called if it was determined that an update should indeed + be handled by this instance. It should also be overridden, but in most + cases call self.callback(dispatcher.bot, update), possibly along with + optional arguments. + + Args: + update (object): The update to be handled + + """ + raise NotImplementedError + + def collectOptionalArgs(self, dispatcher): + """ + Prepares the optional arguments that are the same for all types of + handlers + + Args: + dispatcher (Dispatcher): + """ + optional_args = dict() + if self.pass_update_queue: + optional_args['update_queue'] = dispatcher.update_queue + + return optional_args diff --git a/telegram/ext/inlinequeryhandler.py b/telegram/ext/inlinequeryhandler.py new file mode 100644 index 000000000..56a7823d9 --- /dev/null +++ b/telegram/ext/inlinequeryhandler.py @@ -0,0 +1,48 @@ +#!/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 the InlineQueryHandler class """ + +from .handler import Handler +from telegram import Update + + +class InlineQueryHandler(Handler): + """ + Handler class to handle Telegram inline queries. + + Args: + callback (function): A function that takes ``bot, update`` as + positional arguments. It will be called when the ``checkUpdate`` + has determined that an update should be processed by this handler. + pass_update_queue (optional[bool]): If the handler should be passed the + update queue as a keyword argument called ``update_queue``. It can + be used to insert updates. Default is ``False`` + """ + + def __init__(self, callback, pass_update_queue=False): + super(InlineQueryHandler, self).__init__(callback, pass_update_queue) + + def checkUpdate(self, update): + return isinstance(update, Update) and update.inline_query + + def handleUpdate(self, update, dispatcher): + optional_args = self.collectOptionalArgs(dispatcher) + + self.callback(dispatcher.bot, update, **optional_args) diff --git a/telegram/ext/messagehandler.py b/telegram/ext/messagehandler.py new file mode 100644 index 000000000..1dddf56bd --- /dev/null +++ b/telegram/ext/messagehandler.py @@ -0,0 +1,86 @@ +#!/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 the MessageHandler class """ + +from .handler import Handler +from telegram import Update + +from .filters import * # flake8: noqa + + +class MessageHandler(Handler): + """ + Handler class to handle telegram messages. Messages are Telegram Updates + that do not contain a command. They might contain text, media or status + updates. + + Args: + filters (list): A list of filters defined in ``telegram.ext.filters``. + All messages that match at least one of those filters will be + accepted. If ``bool(filters)`` evaluates to ``False``, messages are + not filtered. + callback (function): A function that takes ``bot, update`` as + positional arguments. It will be called when the ``checkUpdate`` + has determined that an update should be processed by this handler. + pass_update_queue (optional[bool]): If the handler should be passed the + update queue as a keyword argument called ``update_queue``. It can + be used to insert updates. Default is ``False`` + """ + + def __init__(self, filters, callback, pass_update_queue=False): + super(MessageHandler, self).__init__(callback, pass_update_queue) + self.filters = filters + + def checkUpdate(self, update): + filters = self.filters + if isinstance(update, Update) and update.message: + message = update.message + return (not filters or # If filters is empty, accept all messages + TEXT in filters and message.text and + not message.text.startswith('/') or + AUDIO in filters and message.audio or + DOCUMENT in filters and message.document or + PHOTO in filters and message.photo or + STICKER in filters and message.sticker or + VIDEO in filters and message.video or + VOICE in filters and message.voice or + CONTACT in filters and message.contact or + LOCATION in filters and message.location or + VENUE in filters and message.venue or + STATUS_UPDATE in filters and ( + message.new_chat_member or + message.left_chat_member or + message.new_chat_title or + message.new_chat_photo or + message.delete_chat_photo or + message.group_chat_created or + message.supergroup_chat_created or + message.channel_chat_created or + message.migrate_to_chat_id or + message.migrate_from_chat_id or + message.pinned_message) + ) + else: + return False + + def handleUpdate(self, update, dispatcher): + optional_args = self.collectOptionalArgs(dispatcher) + + self.callback(dispatcher.bot, update, **optional_args) diff --git a/telegram/ext/regexhandler.py b/telegram/ext/regexhandler.py new file mode 100644 index 000000000..ec4da53f4 --- /dev/null +++ b/telegram/ext/regexhandler.py @@ -0,0 +1,80 @@ +#!/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 the RegexHandler class """ + +import re + +from .handler import Handler +from telegram import Update + + +class RegexHandler(Handler): + """ + Handler class to handle Telegram updates based on a regex. It uses a + regular expression to check text messages. Read the documentation of the + ``re`` module for more information. The ``re.match`` function is used to + determine if an update should be handled by this handler. + + Args: + pattern (str or Pattern): The regex pattern. + callback (function): A function that takes ``bot, update`` as + positional arguments. It will be called when the ``checkUpdate`` + has determined that an update should be processed by this handler. + pass_groups (optional[bool]): If the callback should be passed the + result of ``re.match(pattern, text).groups()`` as a keyword + argument called ``groups``. Default is ``False`` + pass_groupdict (optional[bool]): If the callback should be passed the + result of ``re.match(pattern, text).groupdict()`` as a keyword + argument called ``groupdict``. Default is ``False`` + pass_update_queue (optional[bool]): If the handler should be passed the + update queue as a keyword argument called ``update_queue``. It can + be used to insert updates. Default is ``False`` + """ + + def __init__(self, pattern, callback, pass_groups=False, + pass_groupdict=False, pass_update_queue=False): + super(RegexHandler, self).__init__(callback, pass_update_queue) + + if isinstance(pattern, str): + pattern = re.compile(pattern) + + self.pattern = pattern + self.pass_groups = pass_groups + self.pass_groupdict = pass_groupdict + + def checkUpdate(self, update): + if (isinstance(update, Update) and + update.message and + update.message.text): + match = re.match(self.pattern, update.message.text) + return bool(match) + else: + return False + + def handleUpdate(self, update, dispatcher): + optional_args = self.collectOptionalArgs(dispatcher) + match = re.match(self.pattern, update.message.text) + + if self.pass_groups: + optional_args['groups'] = match.groups() + if self.pass_groupdict: + optional_args['groupdict'] = match.groupdict() + + self.callback(dispatcher.bot, update, **optional_args) diff --git a/telegram/ext/stringcommandhandler.py b/telegram/ext/stringcommandhandler.py new file mode 100644 index 000000000..b9df4375e --- /dev/null +++ b/telegram/ext/stringcommandhandler.py @@ -0,0 +1,61 @@ +#!/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 the StringCommandHandler class """ + +from .handler import Handler + + +class StringCommandHandler(Handler): + """ + Handler class to handle string commands. Commands are string updates + that start with ``/``. + + Args: + command (str): The name of the command this handler should listen for. + callback (function): A function that takes ``bot, update`` as + positional arguments. It will be called when the ``checkUpdate`` + has determined that an update should be processed by this handler. + pass_args (optional[bool]): If the handler should be passed the + arguments passed to the command as a keyword argument called ` + ``args``. It will contain a list of strings, which is the text + following the command split on spaces. Default is ``False`` + pass_update_queue (optional[bool]): If the handler should be passed the + update queue as a keyword argument called ``update_queue``. It can + be used to insert updates. Default is ``False`` + """ + + def __init__(self, command, callback, pass_args=False, + pass_update_queue=False): + super(StringCommandHandler, self).__init__(callback, pass_update_queue) + self.command = command + self.pass_args = pass_args + + def checkUpdate(self, update): + return (isinstance(update, str) and + update.startswith('/') and + update[1:].split(' ')[0] == self.command) + + def handleUpdate(self, update, dispatcher): + optional_args = self.collectOptionalArgs(dispatcher) + + if self.pass_args: + optional_args['args'] = update.split(' ')[1:] + + self.callback(dispatcher.bot, update, **optional_args) diff --git a/telegram/ext/stringregexhandler.py b/telegram/ext/stringregexhandler.py new file mode 100644 index 000000000..c064ff8cb --- /dev/null +++ b/telegram/ext/stringregexhandler.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 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 StringRegexHandler class """ + +import re + +from .handler import Handler + + +class StringRegexHandler(Handler): + """ + Handler class to handle string updates based on a regex. It uses a + regular expression to check update content. Read the documentation of the + ``re`` module for more information. The ``re.match`` function is used to + determine if an update should be handled by this handler. + + Args: + pattern (str or Pattern): The regex pattern. + callback (function): A function that takes ``bot, update`` as + positional arguments. It will be called when the ``checkUpdate`` + has determined that an update should be processed by this handler. + pass_groups (optional[bool]): If the callback should be passed the + result of ``re.match(pattern, update).groups()`` as a keyword + argument called ``groups``. Default is ``False`` + pass_groupdict (optional[bool]): If the callback should be passed the + result of ``re.match(pattern, update).groupdict()`` as a keyword + argument called ``groupdict``. Default is ``False`` + pass_update_queue (optional[bool]): If the handler should be passed the + update queue as a keyword argument called ``update_queue``. It can + be used to insert updates. Default is ``False`` + """ + + def __init__(self, pattern, callback, pass_groups=False, + pass_groupdict=False, pass_update_queue=False): + super(StringRegexHandler, self).__init__(callback, pass_update_queue) + + if isinstance(pattern, str): + pattern = re.compile(pattern) + + self.pattern = pattern + self.pass_groups = pass_groups + self.pass_groupdict = pass_groupdict + + def checkUpdate(self, update): + if isinstance(update, str): + match = re.match(self.pattern, update) + return bool(match) + else: + return False + + def handleUpdate(self, update, dispatcher): + optional_args = self.collectOptionalArgs(dispatcher) + match = re.match(self.pattern, update) + + if self.pass_groups: + optional_args['groups'] = match.groups() + if self.pass_groupdict: + optional_args['groupdict'] = match.groupdict() + + self.callback(dispatcher.bot, update, **optional_args) diff --git a/telegram/ext/typehandler.py b/telegram/ext/typehandler.py new file mode 100644 index 000000000..b81a87834 --- /dev/null +++ b/telegram/ext/typehandler.py @@ -0,0 +1,56 @@ +#!/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 the TypeHandler class """ + +from .handler import Handler + + +class TypeHandler(Handler): + """ + Handler class to handle updates of custom types. + + Args: + type (type): The ``type`` of updates this handler should process, as + determined by ``isinstance`` + callback (function): A function that takes ``bot, update`` as + positional arguments. It will be called when the ``checkUpdate`` + has determined that an update should be processed by this handler. + strict (optional[bool]): Use ``type`` instead of ``isinstance``. + Default is ``False`` + pass_update_queue (optional[bool]): If the handler should be passed the + update queue as a keyword argument called ``update_queue``. It can + be used to insert updates. Default is ``False`` + """ + + def __init__(self, type, callback, strict=False, pass_update_queue=False): + super(TypeHandler, self).__init__(callback, pass_update_queue) + self.type = type + self.strict = strict + + def checkUpdate(self, update): + if not self.strict: + return isinstance(update, self.type) + else: + return type(update) is self.type + + def handleUpdate(self, update, dispatcher): + optional_args = self.collectOptionalArgs(dispatcher) + + self.callback(dispatcher.bot, update, **optional_args) diff --git a/telegram/ext/updater.py b/telegram/ext/updater.py index cdfc64e2f..d770f7fe6 100644 --- a/telegram/ext/updater.py +++ b/telegram/ext/updater.py @@ -28,10 +28,16 @@ from threading import Thread, Lock, current_thread, Event from time import sleep import subprocess from signal import signal, SIGINT, SIGTERM, SIGABRT + +# Adjust for differences in Python versions +try: + from queue import Queue # flake8: noqa +except ImportError: + from Queue import Queue # flake8: noqa + from telegram import Bot, TelegramError, NullHandler from telegram.ext import dispatcher, Dispatcher, JobQueue from telegram.error import Unauthorized, InvalidToken -from telegram.utils.updatequeue import UpdateQueue from telegram.utils.webhookhandler import (WebhookServer, WebhookHandler) logging.getLogger(__name__).addHandler(NullHandler()) @@ -81,7 +87,7 @@ class Updater(object): self.bot = bot else: self.bot = Bot(token, base_url) - self.update_queue = UpdateQueue() + self.update_queue = Queue() self.job_queue = JobQueue(self.bot, job_queue_tick_interval) self.__exception_event = Event() self.dispatcher = Dispatcher(self.bot, self.update_queue, workers, @@ -110,9 +116,10 @@ class Updater(object): False. bootstrap_retries (Optional[int[): Whether the bootstrapping phase of the `Updater` will retry on failures on the Telegram server. - < 0 - retry indefinitely - 0 - no retries (default) - > 0 - retry up to X times + + | < 0 - retry indefinitely + | 0 - no retries (default) + | > 0 - retry up to X times Returns: Queue: The update queue that can be filled from the main thread @@ -177,9 +184,10 @@ class Updater(object): is False. bootstrap_retries (Optional[int[): Whether the bootstrapping phase of the `Updater` will retry on failures on the Telegram server. - < 0 - retry indefinitely - 0 - no retries (default) - > 0 - retry up to X times + + | < 0 - retry indefinitely + | 0 - no retries (default) + | > 0 - retry up to X times webhook_url (Optional[str]): Explicitly specifiy the webhook url. Useful behind NAT, reverse proxy, etc. Default is derived from `listen`, `port` & `url_path`. @@ -293,11 +301,11 @@ class Updater(object): if use_ssl: self._check_ssl_cert(cert, key) - if not webhook_url: - webhook_url = self._gen_webhook_url(listen, port, url_path) + if not webhook_url: + webhook_url = self._gen_webhook_url(listen, port, url_path) - self._set_webhook(webhook_url, bootstrap_retries, - open(cert, 'rb')) + self._set_webhook(webhook_url, bootstrap_retries, + open(cert, 'rb') if use_ssl else None) self.httpd.serve_forever(poll_interval=1) diff --git a/telegram/file.py b/telegram/file.py index 9a29e00fc..125898554 100644 --- a/telegram/file.py +++ b/telegram/file.py @@ -26,7 +26,6 @@ from telegram.utils.request import download as _download class File(TelegramObject): - """This object represents a Telegram File. Attributes: diff --git a/telegram/inlinekeyboardbutton.py b/telegram/inlinekeyboardbutton.py new file mode 100644 index 000000000..455748dac --- /dev/null +++ b/telegram/inlinekeyboardbutton.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 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 +InlineKeyboardButton""" + +from telegram import TelegramObject + + +class InlineKeyboardButton(TelegramObject): + """This object represents a Telegram InlineKeyboardButton. + + Attributes: + text (str): + url (str): + callback_data (str): + switch_inline_query (str): + + Args: + text (str): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + url (Optional[str]): + callback_data (Optional[str]): + switch_inline_query (Optional[str]): + + """ + + def __init__(self, + text, + **kwargs): + # Required + self.text = text + + # Optionals + self.url = kwargs.get('url') + self.callback_data = kwargs.get('callback_data') + self.switch_inline_query = kwargs.get('switch_inline_query') + + @staticmethod + def de_json(data): + data = super(InlineKeyboardButton, InlineKeyboardButton).de_json(data) + + if not data: + return None + + return InlineKeyboardButton(**data) + + @staticmethod + def de_list(data): + if not data: + return [] + + inline_keyboards = list() + for inline_keyboard in data: + inline_keyboards.append(InlineKeyboardButton. + de_json(inline_keyboard)) + + return inline_keyboards diff --git a/telegram/inlinekeyboardmarkup.py b/telegram/inlinekeyboardmarkup.py new file mode 100644 index 000000000..744c8ca25 --- /dev/null +++ b/telegram/inlinekeyboardmarkup.py @@ -0,0 +1,61 @@ +#!/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 a object that represents a Telegram +InlineKeyboardMarkup""" + +from telegram import ReplyMarkup, InlineKeyboardButton + + +class InlineKeyboardMarkup(ReplyMarkup): + """This object represents a Telegram InlineKeyboardMarkup. + + Attributes: + inline_keyboard (List[List[:class:`telegram.InlineKeyboardMarkup`]]): + + Args: + inline_keyboard (List[List[:class:`telegram.InlineKeyboardMarkup`]]): + + """ + + def __init__(self, + inline_keyboard): + # Required + self.inline_keyboard = inline_keyboard + + @staticmethod + def de_json(data): + if not data: + return None + + data['inline_keyboard'] = \ + [InlineKeyboardButton.de_list(inline_keyboard) for inline_keyboard + in data['inline_keyboard']] + + return InlineKeyboardMarkup(**data) + + def to_dict(self): + data = super(InlineKeyboardMarkup, self).to_dict() + + data['inline_keyboard'] = [] + for inline_keyboard in self.inline_keyboard: + data['inline_keyboard'].append( + [x.to_dict() for x in inline_keyboard]) + + return data diff --git a/telegram/inlinequery.py b/telegram/inlinequery.py index f5165cff7..1bd9c44c8 100644 --- a/telegram/inlinequery.py +++ b/telegram/inlinequery.py @@ -39,14 +39,14 @@ class InlineQuery(TelegramObject): from_user (:class:`telegram.User`): query (str): offset (str): - """ def __init__(self, id, from_user, query, - offset): + offset, + **kwargs): # Required self.id = id self.from_user = from_user @@ -62,10 +62,12 @@ class InlineQuery(TelegramObject): Returns: telegram.InlineQuery: """ + data = super(InlineQuery, InlineQuery).de_json(data) + if not data: return None - data = data.copy() - data['from_user'] = User.de_json(data.pop('from')) + + data['from_user'] = User.de_json(data.get('from')) return InlineQuery(**data) diff --git a/telegram/inlinequeryresult.py b/telegram/inlinequeryresult.py index bc6385ce8..46daf7739 100644 --- a/telegram/inlinequeryresult.py +++ b/telegram/inlinequeryresult.py @@ -1,7 +1,8 @@ #!/usr/bin/env python # # A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015 Leandro Toledo de Souza +# 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 @@ -16,13 +17,10 @@ # 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 -""" +"""This module contains the classes that represent Telegram +InlineQueryResult""" from telegram import TelegramObject -from telegram.utils.validate import validate_string class InlineQueryResult(TelegramObject): @@ -47,453 +45,4 @@ class InlineQueryResult(TelegramObject): @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) + return super(InlineQueryResult, InlineQueryResult).de_json(data) diff --git a/telegram/inlinequeryresultarticle.py b/telegram/inlinequeryresultarticle.py new file mode 100644 index 000000000..936ff52c0 --- /dev/null +++ b/telegram/inlinequeryresultarticle.py @@ -0,0 +1,108 @@ +#!/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 the classes that represent Telegram +InlineQueryResultArticle""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultArticle(InlineQueryResult): + """This object represents a Telegram InlineQueryResultArticle. + + Attributes: + id (str): + title (str): + input_message_content (:class:`telegram.InputMessageContent`): + reply_markup (:class:`telegram.ReplyMarkup`): + url (str): + hide_url (bool): + description (str): + thumb_url (str): + thumb_width (int): + thumb_height (int): + + Deprecated: 4.0 + message_text (str): Use :class:`InputTextMessageContent` instead. + + parse_mode (str): Use :class:`InputTextMessageContent` instead. + + disable_web_page_preview (bool): Use :class:`InputTextMessageContent` + instead. + + Args: + id (str): Unique identifier for this result, 1-64 Bytes + title (str): + reply_markup (:class:`telegram.ReplyMarkup`): + + Keyword Args: + 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, + input_message_content, + reply_markup=None, + url=None, + hide_url=None, + description=None, + thumb_url=None, + thumb_width=None, + thumb_height=None, + **kwargs): + + # Required + super(InlineQueryResultArticle, self).__init__('article', id) + self.title = title + self.input_message_content = input_message_content + + # Optional + if reply_markup: + self.reply_markup = reply_markup + if url: + self.url = url + if hide_url: + self.hide_url = hide_url + if description: + self.description = description + if thumb_url: + self.thumb_url = thumb_url + if thumb_width: + self.thumb_width = thumb_width + if thumb_height: + self.thumb_height = thumb_height + + @staticmethod + def de_json(data): + data = super(InlineQueryResultArticle, + InlineQueryResultArticle).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultArticle(**data) diff --git a/telegram/inlinequeryresultaudio.py b/telegram/inlinequeryresultaudio.py new file mode 100644 index 000000000..2b2988ad2 --- /dev/null +++ b/telegram/inlinequeryresultaudio.py @@ -0,0 +1,97 @@ +#!/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 the classes that represent Telegram +InlineQueryResultAudio""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultAudio(InlineQueryResult): + """Represents a link to an mp3 audio file. By default, this audio file will + be sent by the user. Alternatively, you can use input_message_content to + send a message with the specified content instead of the audio. + + Attributes: + id (str): + audio_url (str): + title (str): + performer (Optional[str]): + audio_duration (Optional[str]): + reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): + input_message_content (Optional[ + :class:`telegram.input_message_content`]): + + Deprecated: 4.0 + message_text (str): Use :class:`InputTextMessageContent` instead. + + parse_mode (str): Use :class:`InputTextMessageContent` instead. + + disable_web_page_preview (bool): Use :class:`InputTextMessageContent` + instead. + + Args: + audio_url (str): + title (str): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + performer (Optional[str]): + audio_duration (Optional[str]): + reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): + input_message_content (Optional[ + :class:`telegram.input_message_content`]): + """ + def __init__(self, + id, + audio_url, + title, + performer=None, + audio_duration=None, + reply_markup=None, + input_message_content=None, + **kwargs): + + # Required + super(InlineQueryResultAudio, self).__init__('audio', id) + self.audio_url = audio_url + self.title = title + + # Optionals + if performer: + self.performer = performer + if audio_duration: + self.audio_duration = audio_duration + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + + @staticmethod + def de_json(data): + data = super(InlineQueryResultAudio, + InlineQueryResultAudio).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultAudio(**data) diff --git a/telegram/inlinequeryresultcachedaudio.py b/telegram/inlinequeryresultcachedaudio.py new file mode 100644 index 000000000..ffca243f5 --- /dev/null +++ b/telegram/inlinequeryresultcachedaudio.py @@ -0,0 +1,54 @@ +#!/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 the classes that represent Telegram +InlineQueryResultCachedAudio""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultCachedAudio(InlineQueryResult): + def __init__(self, + id, + audio_file_id, + reply_markup=None, + input_message_content=None, + **kwargs): + # Required + super(InlineQueryResultCachedAudio, self).__init__('audio', id) + self.audio_file_id = audio_file_id + + # Optionals + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + + @staticmethod + def de_json(data): + data = super(InlineQueryResultCachedAudio, + InlineQueryResultCachedAudio).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultCachedAudio(**data) diff --git a/telegram/inlinequeryresultcacheddocument.py b/telegram/inlinequeryresultcacheddocument.py new file mode 100644 index 000000000..d78dc2bd1 --- /dev/null +++ b/telegram/inlinequeryresultcacheddocument.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2016 +# Leandro Toledo de Souza +# +# 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 +InlineQueryResultCachedDocument""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultCachedDocument(InlineQueryResult): + def __init__(self, + id, + title, + document_file_id, + description=None, + caption=None, + reply_markup=None, + input_message_content=None, + **kwargs): + # Required + super(InlineQueryResultCachedDocument, self).__init__('document', id) + self.title = title + self.document_file_id = document_file_id + + # Optionals + if description: + self.description = description + if caption: + self.caption = caption + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + + @staticmethod + def de_json(data): + data = super(InlineQueryResultCachedDocument, + InlineQueryResultCachedDocument).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultCachedDocument(**data) diff --git a/telegram/inlinequeryresultcachedgif.py b/telegram/inlinequeryresultcachedgif.py new file mode 100644 index 000000000..6ad4268ac --- /dev/null +++ b/telegram/inlinequeryresultcachedgif.py @@ -0,0 +1,60 @@ +#!/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 the classes that represent Telegram +InlineQueryResultCachedGif""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultCachedGif(InlineQueryResult): + def __init__(self, + id, + gif_file_id, + title=None, + caption=None, + reply_markup=None, + input_message_content=None, + **kwargs): + # Required + super(InlineQueryResultCachedGif, self).__init__('gif', id) + self.gif_file_id = gif_file_id + + # Optionals + if title: + self.title = title + if caption: + self.caption = caption + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + + @staticmethod + def de_json(data): + data = super(InlineQueryResultCachedGif, + InlineQueryResultCachedGif).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultCachedGif(**data) diff --git a/telegram/inlinequeryresultcachedmpeg4gif.py b/telegram/inlinequeryresultcachedmpeg4gif.py new file mode 100644 index 000000000..7c5d675ae --- /dev/null +++ b/telegram/inlinequeryresultcachedmpeg4gif.py @@ -0,0 +1,60 @@ +#!/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 the classes that represent Telegram +InlineQueryResultMpeg4Gif""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): + def __init__(self, + id, + mpeg4_file_id, + title=None, + caption=None, + reply_markup=None, + input_message_content=None, + **kwargs): + # Required + super(InlineQueryResultCachedMpeg4Gif, self).__init__('mpeg4_gif', id) + self.mpeg4_file_id = mpeg4_file_id + + # Optionals + if title: + self.title = title + if caption: + self.caption = caption + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + + @staticmethod + def de_json(data): + data = super(InlineQueryResultCachedMpeg4Gif, + InlineQueryResultCachedMpeg4Gif).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultCachedMpeg4Gif(**data) diff --git a/telegram/inlinequeryresultcachedphoto.py b/telegram/inlinequeryresultcachedphoto.py new file mode 100644 index 000000000..abb76a941 --- /dev/null +++ b/telegram/inlinequeryresultcachedphoto.py @@ -0,0 +1,63 @@ +#!/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 the classes that represent Telegram +InlineQueryResultPhoto""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultCachedPhoto(InlineQueryResult): + def __init__(self, + id, + photo_file_id, + title=None, + description=None, + caption=None, + reply_markup=None, + input_message_content=None, + **kwargs): + # Required + super(InlineQueryResultCachedPhoto, self).__init__('photo', id) + self.photo_file_id = photo_file_id + + # Optionals + if title: + self.title = title + if description: + self.description = description + if caption: + self.caption = caption + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + + @staticmethod + def de_json(data): + data = super(InlineQueryResultCachedPhoto, + InlineQueryResultCachedPhoto).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultCachedPhoto(**data) diff --git a/telegram/inlinequeryresultcachedsticker.py b/telegram/inlinequeryresultcachedsticker.py new file mode 100644 index 000000000..1d01ff57f --- /dev/null +++ b/telegram/inlinequeryresultcachedsticker.py @@ -0,0 +1,54 @@ +#!/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 the classes that represent Telegram +InlineQueryResultCachedSticker""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultCachedSticker(InlineQueryResult): + def __init__(self, + id, + sticker_file_id, + reply_markup=None, + input_message_content=None, + **kwargs): + # Required + super(InlineQueryResultCachedSticker, self).__init__('sticker', id) + self.sticker_file_id = sticker_file_id + + # Optionals + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + + @staticmethod + def de_json(data): + data = super(InlineQueryResultCachedSticker, + InlineQueryResultCachedSticker).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultCachedSticker(**data) diff --git a/telegram/inlinequeryresultcachedvideo.py b/telegram/inlinequeryresultcachedvideo.py new file mode 100644 index 000000000..807ab4060 --- /dev/null +++ b/telegram/inlinequeryresultcachedvideo.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2016 +# Leandro Toledo de Souza +# +# 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 +InlineQueryResultCachedVideo""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultCachedVideo(InlineQueryResult): + def __init__(self, + id, + video_file_id, + title, + description=None, + caption=None, + reply_markup=None, + input_message_content=None, + **kwargs): + # Required + super(InlineQueryResultCachedVideo, self).__init__('video', id) + self.video_file_id = video_file_id + self.title = title + + # Optionals + if description: + self.description = description + if caption: + self.caption = caption + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + + @staticmethod + def de_json(data): + data = super(InlineQueryResultCachedVideo, + InlineQueryResultCachedVideo).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultCachedVideo(**data) diff --git a/telegram/inlinequeryresultcachedvoice.py b/telegram/inlinequeryresultcachedvoice.py new file mode 100644 index 000000000..61ba9cb4e --- /dev/null +++ b/telegram/inlinequeryresultcachedvoice.py @@ -0,0 +1,59 @@ +#!/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 the classes that represent Telegram +InlineQueryResultCachedVoice""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultCachedVoice(InlineQueryResult): + def __init__(self, + id, + voice_file_id, + title, + description=None, + reply_markup=None, + input_message_content=None, + **kwargs): + # Required + super(InlineQueryResultCachedVoice, self).__init__('voice', id) + self.voice_file_id = voice_file_id + self.title = title + + # Optionals + if description: + self.description = description + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + + @staticmethod + def de_json(data): + data = super(InlineQueryResultCachedVoice, + InlineQueryResultCachedVoice).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultCachedVoice(**data) diff --git a/telegram/inlinequeryresultcontact.py b/telegram/inlinequeryresultcontact.py new file mode 100644 index 000000000..ada569e30 --- /dev/null +++ b/telegram/inlinequeryresultcontact.py @@ -0,0 +1,68 @@ +#!/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 the classes that represent Telegram +InlineQueryResultContact""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultContact(InlineQueryResult): + def __init__(self, + id, + phone_number, + first_name, + last_name=None, + reply_markup=None, + input_message_content=None, + thumb_url=None, + thumb_width=None, + thumb_height=None, + **kwargs): + # Required + super(InlineQueryResultContact, self).__init__('contact', id) + self.phone_number = phone_number + self.first_name = first_name + + # Optionals + if last_name: + self.last_name = last_name + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + if thumb_url: + self.thumb_url = thumb_url + if thumb_width: + self.thumb_width = thumb_width + if thumb_height: + self.thumb_height = thumb_height + + @staticmethod + def de_json(data): + data = super(InlineQueryResultContact, + InlineQueryResultContact).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultContact(**data) diff --git a/telegram/inlinequeryresultdocument.py b/telegram/inlinequeryresultdocument.py new file mode 100644 index 000000000..0c0735cf9 --- /dev/null +++ b/telegram/inlinequeryresultdocument.py @@ -0,0 +1,73 @@ +#!/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 the classes that represent Telegram +InlineQueryResultDocument""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultDocument(InlineQueryResult): + def __init__(self, + id, + document_url, + title, + mime_type, + caption=None, + description=None, + reply_markup=None, + input_message_content=None, + thumb_url=None, + thumb_width=None, + thumb_height=None, + **kwargs): + # Required + super(InlineQueryResultDocument, self).__init__('document', id) + self.document_url = document_url + self.title = title + self.mime_type = mime_type + + # Optionals + if caption: + self.caption = caption + if description: + self.description = description + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + if thumb_url: + self.thumb_url = thumb_url + if thumb_width: + self.thumb_width = thumb_width + if thumb_height: + self.thumb_height = thumb_height + + @staticmethod + def de_json(data): + data = super(InlineQueryResultDocument, + InlineQueryResultDocument).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultDocument(**data) diff --git a/telegram/inlinequeryresultgif.py b/telegram/inlinequeryresultgif.py new file mode 100644 index 000000000..ae3589f6b --- /dev/null +++ b/telegram/inlinequeryresultgif.py @@ -0,0 +1,69 @@ +#!/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 the classes that represent Telegram +InlineQueryResultGif""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultGif(InlineQueryResult): + def __init__(self, + id, + gif_url, + thumb_url, + gif_width=None, + gif_height=None, + title=None, + caption=None, + reply_markup=None, + input_message_content=None, + **kwargs): + + # Required + super(InlineQueryResultGif, self).__init__('gif', id) + self.gif_url = gif_url + self.thumb_url = thumb_url + + # Optionals + if gif_width: + self.gif_width = gif_width + if gif_height: + self.gif_height = gif_height + if title: + self.title = title + if caption: + self.caption = caption + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + + @staticmethod + def de_json(data): + data = super(InlineQueryResultGif, + InlineQueryResultGif).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultGif(**data) diff --git a/telegram/inlinequeryresultlocation.py b/telegram/inlinequeryresultlocation.py new file mode 100644 index 000000000..a5d77179f --- /dev/null +++ b/telegram/inlinequeryresultlocation.py @@ -0,0 +1,67 @@ +#!/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 the classes that represent Telegram +InlineQueryResultLocation""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultLocation(InlineQueryResult): + def __init__(self, + id, + latitude, + longitude, + title, + reply_markup=None, + input_message_content=None, + thumb_url=None, + thumb_width=None, + thumb_height=None, + **kwargs): + # Required + super(InlineQueryResultLocation, self).__init__('location', id) + self.latitude = latitude + self.longitude = longitude + self.title = title + + # Optionals + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + if thumb_url: + self.thumb_url = thumb_url + if thumb_width: + self.thumb_width = thumb_width + if thumb_height: + self.thumb_height = thumb_height + + @staticmethod + def de_json(data): + data = super(InlineQueryResultLocation, + InlineQueryResultLocation).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultLocation(**data) diff --git a/telegram/inlinequeryresultmpeg4gif.py b/telegram/inlinequeryresultmpeg4gif.py new file mode 100644 index 000000000..9f2232eea --- /dev/null +++ b/telegram/inlinequeryresultmpeg4gif.py @@ -0,0 +1,69 @@ +#!/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 the classes that represent Telegram +InlineQueryResultMpeg4Gif""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultMpeg4Gif(InlineQueryResult): + def __init__(self, + id, + mpeg4_url, + thumb_url, + mpeg4_width=None, + mpeg4_height=None, + title=None, + caption=None, + reply_markup=None, + input_message_content=None, + **kwargs): + + # Required + super(InlineQueryResultMpeg4Gif, self).__init__('mpeg4_gif', id) + self.mpeg4_url = mpeg4_url + self.thumb_url = thumb_url + + # Optional + if mpeg4_width: + self.mpeg4_width = mpeg4_width + if mpeg4_height: + self.mpeg4_height = mpeg4_height + if title: + self.title = title + if caption: + self.caption = caption + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + + @staticmethod + def de_json(data): + data = super(InlineQueryResultMpeg4Gif, + InlineQueryResultMpeg4Gif).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultMpeg4Gif(**data) diff --git a/telegram/inlinequeryresultphoto.py b/telegram/inlinequeryresultphoto.py new file mode 100644 index 000000000..76afaf695 --- /dev/null +++ b/telegram/inlinequeryresultphoto.py @@ -0,0 +1,71 @@ +#!/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 the classes that represent Telegram +InlineQueryResultPhoto""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultPhoto(InlineQueryResult): + def __init__(self, + id, + photo_url, + thumb_url, + photo_width=None, + photo_height=None, + title=None, + description=None, + caption=None, + reply_markup=None, + input_message_content=None, + **kwargs): + # Required + super(InlineQueryResultPhoto, self).__init__('photo', id) + self.photo_url = photo_url + self.thumb_url = thumb_url + + # Optionals + if photo_width: + self.photo_width = int(photo_width) + if photo_height: + self.photo_height = int(photo_height) + if title: + self.title = title + if description: + self.description = description + if caption: + self.caption = caption + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + + @staticmethod + def de_json(data): + data = super(InlineQueryResultPhoto, + InlineQueryResultPhoto).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultPhoto(**data) diff --git a/telegram/inlinequeryresultvenue.py b/telegram/inlinequeryresultvenue.py new file mode 100644 index 000000000..bd1189c2d --- /dev/null +++ b/telegram/inlinequeryresultvenue.py @@ -0,0 +1,73 @@ +#!/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 the classes that represent Telegram +InlineQueryResultVenue""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultVenue(InlineQueryResult): + def __init__(self, + id, + latitude, + longitude, + title, + address, + foursquare_id=None, + reply_markup=None, + input_message_content=None, + thumb_url=None, + thumb_width=None, + thumb_height=None, + **kwargs): + + # Required + super(InlineQueryResultVenue, self).__init__('venue', id) + self.latitude = latitude + self.longitude = longitude + self.title = title + self.address = address + + # Optional + if foursquare_id: + self.foursquare_id = foursquare_id + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + if thumb_url: + self.thumb_url = thumb_url + if thumb_width: + self.thumb_width = thumb_width + if thumb_height: + self.thumb_height = thumb_height + + @staticmethod + def de_json(data): + data = super(InlineQueryResultVenue, + InlineQueryResultVenue).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultVenue(**data) diff --git a/telegram/inlinequeryresultvideo.py b/telegram/inlinequeryresultvideo.py new file mode 100644 index 000000000..6f1e49188 --- /dev/null +++ b/telegram/inlinequeryresultvideo.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 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 +InlineQueryResultVideo""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultVideo(InlineQueryResult): + def __init__(self, + id, + video_url, + mime_type, + thumb_url, + title, + caption=None, + video_width=None, + video_height=None, + video_duration=None, + description=None, + reply_markup=None, + input_message_content=None, + **kwargs): + + # 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 + + # Optional + if caption: + self.caption = caption + if video_width: + self.video_width = video_width + if video_height: + self.video_height = video_height + if video_duration: + self.video_duration = video_duration + if description: + self.description = description + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + + @staticmethod + def de_json(data): + data = super(InlineQueryResultVideo, + InlineQueryResultVideo).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultVideo(**data) diff --git a/telegram/inlinequeryresultvoice.py b/telegram/inlinequeryresultvoice.py new file mode 100644 index 000000000..a63156dc0 --- /dev/null +++ b/telegram/inlinequeryresultvoice.py @@ -0,0 +1,60 @@ +#!/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 the classes that represent Telegram +InlineQueryResultVoice""" + +from telegram import InlineQueryResult, InlineKeyboardMarkup, \ + InputMessageContent + + +class InlineQueryResultVoice(InlineQueryResult): + def __init__(self, + id, + voice_url, + title, + voice_duration=None, + reply_markup=None, + input_message_content=None, + **kwargs): + + # Required + super(InlineQueryResultVoice, self).__init__('voice', id) + self.voice_url = voice_url + self.title = title + + # Optional + if voice_duration: + self.voice_duration = voice_duration + if reply_markup: + self.reply_markup = reply_markup + if input_message_content: + self.input_message_content = input_message_content + + @staticmethod + def de_json(data): + data = super(InlineQueryResultVoice, + InlineQueryResultVoice).de_json(data) + + data['reply_markup'] = InlineKeyboardMarkup.de_json( + data.get('reply_markup')) + data['input_message_content'] = InputMessageContent.de_json( + data.get('input_message_content')) + + return InlineQueryResultVoice(**data) diff --git a/telegram/inputcontactmessagecontent.py b/telegram/inputcontactmessagecontent.py new file mode 100644 index 000000000..2fc052f1f --- /dev/null +++ b/telegram/inputcontactmessagecontent.py @@ -0,0 +1,27 @@ +#!/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 the classes that represent Telegram +InputContactMessageContent""" + +from telegram import InputMessageContent + + +class InputContactMessageContent(InputMessageContent): + pass diff --git a/telegram/inputfile.py b/telegram/inputfile.py index c2070b38b..1a6014093 100644 --- a/telegram/inputfile.py +++ b/telegram/inputfile.py @@ -85,7 +85,7 @@ class InputFile(object): hasattr(self.input_file, 'name'): self.filename = os.path.basename(self.input_file.name) elif from_url: - self.filename = os.path.basename(self.input_file.url)\ + self.filename = os.path.basename(self.input_file.url) \ .split('?')[0].split('&')[0] try: @@ -94,7 +94,7 @@ class InputFile(object): self.filename = self.mimetype.replace('/', '.') except TelegramError: self.mimetype = mimetypes.guess_type(self.filename)[0] or \ - DEFAULT_MIME_TYPE + DEFAULT_MIME_TYPE @property def headers(self): @@ -198,7 +198,7 @@ class InputFile(object): if file_type: file_content = data[file_type[0]] - return isinstance(file_content, file) or \ - str(file_content).startswith('http') + return isinstance(file_content, file) or str( + file_content).startswith('http') return False diff --git a/telegram/inputlocationmessagecontent.py b/telegram/inputlocationmessagecontent.py new file mode 100644 index 000000000..178649da9 --- /dev/null +++ b/telegram/inputlocationmessagecontent.py @@ -0,0 +1,41 @@ +#!/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 the classes that represent Telegram +InputLocationMessageContent""" + +from telegram import InputMessageContent + + +class InputLocationMessageContent(InputMessageContent): + """Base class for Telegram InputLocationMessageContent Objects""" + + def __init__(self, + latitude, + longitude): + # Required + self.latitude = latitude + self.longitude = longitude + + @staticmethod + def de_json(data): + data = super(InputLocationMessageContent, + InputLocationMessageContent).de_json(data) + + return InputLocationMessageContent(**data) diff --git a/telegram/inputmessagecontent.py b/telegram/inputmessagecontent.py new file mode 100644 index 000000000..8cad33f50 --- /dev/null +++ b/telegram/inputmessagecontent.py @@ -0,0 +1,31 @@ +#!/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 the classes that represent Telegram +InputMessageContent""" + +from telegram import TelegramObject + + +class InputMessageContent(TelegramObject): + """Base class for Telegram InputMessageContent Objects""" + + @staticmethod + def de_json(data): + pass diff --git a/telegram/inputtextmessagecontent.py b/telegram/inputtextmessagecontent.py new file mode 100644 index 000000000..fceaf3349 --- /dev/null +++ b/telegram/inputtextmessagecontent.py @@ -0,0 +1,44 @@ +#!/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 the classes that represent Telegram +InputTextMessageContent""" + +from telegram import InputMessageContent + + +class InputTextMessageContent(InputMessageContent): + """Base class for Telegram InputTextMessageContent Objects""" + + def __init__(self, + message_text, + parse_mode=None, + disable_web_page_preview=None): + # Required + self.message_text = message_text + # Optionals + self.parse_mode = parse_mode + self.disable_web_page_preview = disable_web_page_preview + + @staticmethod + def de_json(data): + data = super(InputTextMessageContent, + InputTextMessageContent).de_json(data) + + return InputTextMessageContent(**data) diff --git a/telegram/inputvenuemessagecontent.py b/telegram/inputvenuemessagecontent.py new file mode 100644 index 000000000..00abc7fc3 --- /dev/null +++ b/telegram/inputvenuemessagecontent.py @@ -0,0 +1,48 @@ +#!/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 the classes that represent Telegram +InputVenueMessageContent""" + +from telegram import InputMessageContent + + +class InputVenueMessageContent(InputMessageContent): + """Base class for Telegram InputVenueMessageContent Objects""" + + def __init__(self, + latitude, + longitude, + title, + address, + foursquare_id=None): + # Required + self.latitude = latitude + self.longitude = longitude + self.title = title + self.address = address + # Optionals + self.foursquare_id = foursquare_id + + @staticmethod + def de_json(data): + data = super(InputVenueMessageContent, + InputVenueMessageContent).de_json(data) + + return InputVenueMessageContent(**data) diff --git a/telegram/keyboardbutton.py b/telegram/keyboardbutton.py new file mode 100644 index 000000000..59422f8b9 --- /dev/null +++ b/telegram/keyboardbutton.py @@ -0,0 +1,66 @@ +#!/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 a object that represents a Telegram KeyboardButton.""" + +from telegram import TelegramObject + + +class KeyboardButton(TelegramObject): + """ + This object represents one button of the reply keyboard. For simple + text buttons String can be used instead of this object to specify text + of the button. + + Args: + text (str): + request_location (Optional[bool]): + request_contact (Optional[bool]): + """ + + def __init__(self, + text, + request_contact=None, + request_location=None): + # Required + self.text = text + # Optionals + if request_contact: + self.request_contact = request_contact + if request_location: + self.request_location = request_location + + @staticmethod + def de_json(data): + if not data: + return None + + return KeyboardButton(**data) + + @staticmethod + def de_list(data): + if not data: + return [] + + keyboards = list() + for keyboard in data: + keyboards.append(KeyboardButton. + de_json(keyboard)) + + return keyboards diff --git a/telegram/message.py b/telegram/message.py index 14541fcc8..5d43f091b 100644 --- a/telegram/message.py +++ b/telegram/message.py @@ -24,7 +24,8 @@ from datetime import datetime from time import mktime from telegram import (Audio, Contact, Document, Chat, Location, PhotoSize, - Sticker, TelegramObject, User, Video, Voice) + Sticker, TelegramObject, User, Video, Voice, Venue, + MessageEntity) class Message(TelegramObject): @@ -50,8 +51,8 @@ class Message(TelegramObject): caption (str): contact (:class:`telegram.Contact`): location (:class:`telegram.Location`): - new_chat_participant (:class:`telegram.User`): - left_chat_participant (:class:`telegram.User`): + new_chat_member (:class:`telegram.User`): + left_chat_member (:class:`telegram.User`): new_chat_title (str): new_chat_photo (List[:class:`telegram.PhotoSize`]): delete_chat_photo (bool): @@ -61,6 +62,13 @@ class Message(TelegramObject): migrate_from_chat_id (int): channel_chat_created (bool): + Deprecated: 4.0 + new_chat_participant (:class:`telegram.User`): Use `new_chat_member` + instead. + + left_chat_participant (:class:`telegram.User`): Use `left_chat_member` + instead. + Args: message_id (int): from_user (:class:`telegram.User`): @@ -82,8 +90,8 @@ class Message(TelegramObject): caption (Optional[str]): contact (Optional[:class:`telegram.Contact`]): location (Optional[:class:`telegram.Location`]): - new_chat_participant (Optional[:class:`telegram.User`]): - left_chat_participant (Optional[:class:`telegram.User`]): + new_chat_member (Optional[:class:`telegram.User`]): + left_chat_member (Optional[:class:`telegram.User`]): new_chat_title (Optional[str]): new_chat_photo (Optional[List[:class:`telegram.PhotoSize`]): delete_chat_photo (Optional[bool]): @@ -110,6 +118,7 @@ class Message(TelegramObject): self.forward_date = kwargs.get('forward_date') self.reply_to_message = kwargs.get('reply_to_message') self.text = kwargs.get('text', '') + self.entities = kwargs.get('entities', list()) self.audio = kwargs.get('audio') self.document = kwargs.get('document') self.photo = kwargs.get('photo') @@ -119,8 +128,9 @@ class Message(TelegramObject): self.caption = kwargs.get('caption', '') self.contact = kwargs.get('contact') self.location = kwargs.get('location') - self.new_chat_participant = kwargs.get('new_chat_participant') - self.left_chat_participant = kwargs.get('left_chat_participant') + self.venue = kwargs.get('venue') + self.new_chat_member = kwargs.get('new_chat_member') + self.left_chat_member = kwargs.get('left_chat_member') self.new_chat_title = kwargs.get('new_chat_title', '') self.new_chat_photo = kwargs.get('new_chat_photo') self.delete_chat_photo = bool(kwargs.get('delete_chat_photo', False)) @@ -131,6 +141,7 @@ class Message(TelegramObject): self.migrate_from_chat_id = int(kwargs.get('migrate_from_chat_id', 0)) self.channel_chat_created = bool(kwargs.get('channel_chat_created', False)) + self.pinned_message = kwargs.get('pinned_message') @property def chat_id(self): @@ -152,34 +163,24 @@ class Message(TelegramObject): data['from_user'] = User.de_json(data.get('from')) data['date'] = datetime.fromtimestamp(data['date']) data['chat'] = Chat.de_json(data.get('chat')) - data['forward_from'] = \ - User.de_json(data.get('forward_from')) - data['forward_date'] = \ - Message._fromtimestamp(data.get('forward_date')) + data['entities'] = MessageEntity.de_list(data.get('entities')) + data['forward_from'] = User.de_json(data.get('forward_from')) + data['forward_date'] = Message._fromtimestamp(data.get('forward_date')) data['reply_to_message'] = \ Message.de_json(data.get('reply_to_message')) - data['audio'] = \ - Audio.de_json(data.get('audio')) - data['document'] = \ - Document.de_json(data.get('document')) - data['photo'] = \ - PhotoSize.de_list(data.get('photo')) - data['sticker'] = \ - Sticker.de_json(data.get('sticker')) - data['video'] = \ - Video.de_json(data.get('video')) - data['voice'] = \ - Voice.de_json(data.get('voice')) - data['contact'] = \ - Contact.de_json(data.get('contact')) - data['location'] = \ - Location.de_json(data.get('location')) - data['new_chat_participant'] = \ - User.de_json(data.get('new_chat_participant')) - data['left_chat_participant'] = \ - User.de_json(data.get('left_chat_participant')) - data['new_chat_photo'] = \ - PhotoSize.de_list(data.get('new_chat_photo')) + data['audio'] = Audio.de_json(data.get('audio')) + data['document'] = Document.de_json(data.get('document')) + data['photo'] = PhotoSize.de_list(data.get('photo')) + data['sticker'] = Sticker.de_json(data.get('sticker')) + data['video'] = Video.de_json(data.get('video')) + data['voice'] = Voice.de_json(data.get('voice')) + data['contact'] = Contact.de_json(data.get('contact')) + data['location'] = Location.de_json(data.get('location')) + data['venue'] = Venue.de_json(data.get('venue')) + data['new_chat_member'] = User.de_json(data.get('new_chat_member')) + data['left_chat_member'] = User.de_json(data.get('left_chat_member')) + data['new_chat_photo'] = PhotoSize.de_list(data.get('new_chat_photo')) + data['pinned_message'] = Message.de_json(data.get('pinned_message')) return Message(**data) @@ -204,6 +205,8 @@ class Message(TelegramObject): data['forward_date'] = self._totimestamp(self.forward_date) if self.photo: data['photo'] = [p.to_dict() for p in self.photo] + if self.entities: + data['entities'] = [e.to_dict() for e in self.entities] if self.new_chat_photo: data['new_chat_photo'] = [p.to_dict() for p in self.new_chat_photo] diff --git a/telegram/messageentity.py b/telegram/messageentity.py new file mode 100644 index 000000000..179f74feb --- /dev/null +++ b/telegram/messageentity.py @@ -0,0 +1,71 @@ +#!/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 a object that represents a Telegram MessageEntity.""" + +from telegram import TelegramObject + + +class MessageEntity(TelegramObject): + """ + This object represents one special entity in a text message. For example, + hashtags, usernames, URLs, etc. + + Args: + type (str): + offset (int): + length (int): + url (Optional[str]): + """ + + def __init__(self, + type, + offset, + length, + url=None): + # Required + self.type = type + self.offset = offset + self.length = length + # Optionals + self.url = url + + @staticmethod + def de_json(data): + data = super(MessageEntity, MessageEntity).de_json(data) + + return MessageEntity(**data) + + @staticmethod + def de_list(data): + """ + Args: + data (list): + + Returns: + List: + """ + if not data: + return list() + + entities = list() + for entity in data: + entities.append(MessageEntity.de_json(entity)) + + return entities diff --git a/telegram/replykeyboardmarkup.py b/telegram/replykeyboardmarkup.py index 9179dbafd..dd81c5e68 100644 --- a/telegram/replykeyboardmarkup.py +++ b/telegram/replykeyboardmarkup.py @@ -20,14 +20,14 @@ """This module contains a object that represents a Telegram ReplyKeyboardMarkup.""" -from telegram import ReplyMarkup +from telegram import ReplyMarkup, KeyboardButton class ReplyKeyboardMarkup(ReplyMarkup): """This object represents a Telegram ReplyKeyboardMarkup. Attributes: - keyboard (List[List[str]]): + keyboard (List[List[:class:`telegram.KeyboardButton`]]): resize_keyboard (bool): one_time_keyboard (bool): selective (bool): @@ -64,4 +64,16 @@ class ReplyKeyboardMarkup(ReplyMarkup): if not data: return None + data['keyboard'] = [KeyboardButton.de_list(keyboard) for keyboard in + data['keyboard']] + return ReplyKeyboardMarkup(**data) + + def to_dict(self): + data = super(ReplyKeyboardMarkup, self).to_dict() + + data['keyboard'] = [] + for keyboard in self.keyboard: + data['keyboard'].append([x.to_dict() for x in keyboard]) + + return data diff --git a/telegram/update.py b/telegram/update.py index f3c51e825..9b60b0be2 100644 --- a/telegram/update.py +++ b/telegram/update.py @@ -19,7 +19,8 @@ """This module contains a object that represents a Telegram Update.""" -from telegram import Message, TelegramObject, InlineQuery, ChosenInlineResult +from telegram import (Message, TelegramObject, InlineQuery, + ChosenInlineResult, CallbackQuery) class Update(TelegramObject): @@ -30,6 +31,7 @@ class Update(TelegramObject): message (:class:`telegram.Message`): inline_query (:class:`telegram.InlineQuery`): chosen_inline_result (:class:`telegram.ChosenInlineResult`): + callback_query (:class:`telegram.CallbackQuery`): Args: update_id (int): @@ -39,7 +41,9 @@ class Update(TelegramObject): message (Optional[:class:`telegram.Message`]): inline_query (Optional[:class:`telegram.InlineQuery`]): chosen_inline_result (Optional[:class:`telegram.ChosenInlineResult`]) + callback_query (Optional[:class:`telegram.CallbackQuery`]): """ + def __init__(self, update_id, **kwargs): @@ -49,6 +53,7 @@ class Update(TelegramObject): self.message = kwargs.get('message') self.inline_query = kwargs.get('inline_query') self.chosen_inline_result = kwargs.get('chosen_inline_result') + self.callback_query = kwargs.get('callback_query') @staticmethod def de_json(data): @@ -66,5 +71,7 @@ class Update(TelegramObject): data['inline_query'] = InlineQuery.de_json(data.get('inline_query')) data['chosen_inline_result'] = \ ChosenInlineResult.de_json(data.get('chosen_inline_result')) + data['callback_query'] = \ + CallbackQuery.de_json(data.get('callback_query')) return Update(**data) diff --git a/telegram/utils/request.py b/telegram/utils/request.py index a9f14cc9d..55dfab019 100644 --- a/telegram/utils/request.py +++ b/telegram/utils/request.py @@ -186,7 +186,7 @@ def download(url, The web location we want to retrieve. filename: - The filename wihtin the path to download the file. + The filename within the path to download the file. """ urlretrieve(url, filename) diff --git a/telegram/utils/updatequeue.py b/telegram/utils/updatequeue.py deleted file mode 100644 index b41392343..000000000 --- a/telegram/utils/updatequeue.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/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 the class UpdateQueue to override standard -Queue.""" - - -# Adjust for differences in Python versions -try: - # loading Empty here so it can be imported by users of updatequeue - from queue import Queue, Empty # flake8: noqa -except ImportError: - from Queue import Queue, Empty # flake8: noqa - - -class UpdateQueue(Queue): - """ - This class overrides standard Queues. Allows you to de/queue context - data apart from the handled `update` - """ - - def put(self, item, block=True, timeout=None, context=None): - """ - Put an item into the queue with context data if provided as a - tuple (item, context). Overrides standard Queue.put method. - - Args: - update (any): handled by the dispatcher - context (any): extra data to use in handlers - """ - Queue.put(self, (item, context), block, timeout) - - def get(self, block=True, timeout=None, context=False): - """ - Remove and return an item from the queue. A tuple of - (update, context) if requested. Overrides standard Queue.get - method. - - Args: - context (boolean): set true to get (update, context) - """ - if not context: - return Queue.get(self, block, timeout)[0] - return Queue.get(self, block, timeout) diff --git a/telegram/utils/validate.py b/telegram/utils/validate.py index 84cadbfe6..834cf9b17 100644 --- a/telegram/utils/validate.py +++ b/telegram/utils/validate.py @@ -19,6 +19,8 @@ """This module contains functions to validate function arguments""" +from telegram.error import InvalidToken + try: type(basestring) except NameError: @@ -36,3 +38,11 @@ def validate_string(arg, name): """ if not isinstance(arg, basestring) and arg is not None: raise ValueError(name + ' is not a string') + + +def validate_token(token): + """a very basic validation on token""" + left, sep, _right = token.partition(':') + if (not sep) or (not left.isdigit()) or (len(left) < 3): + raise InvalidToken() + return token diff --git a/telegram/venue.py b/telegram/venue.py new file mode 100644 index 000000000..dcb1256f6 --- /dev/null +++ b/telegram/venue.py @@ -0,0 +1,57 @@ +#!/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 a object that represents a Telegram Venue.""" + +from telegram import TelegramObject, Location + + +class Venue(TelegramObject): + """ + This object represents a venue. + + Args: + location (:class:`telegram.Location`): + title (str): + address (str): + foursquare_id (Optional[str]): + """ + + def __init__(self, + location, + title, + address, + foursquare_id=None): + # Required + self.location = location + self.title = title + self.address = address + # Optionals + self.foursquare_id = foursquare_id + + @staticmethod + def de_json(data): + if not data: + return None + + data = super(Venue, Venue).de_json(data) + + data['location'] = Location.de_json(data.get('location')) + + return Venue(**data) diff --git a/tests/test_bot.py b/tests/test_bot.py index e47320c53..5e0314079 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -186,8 +186,9 @@ class BotTest(BaseTest, unittest.TestCase): def testInvalidSrvResp(self): 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}) - bot = bot_cls('12') + bot = telegram.Bot.__new__(telegram.Bot) + bot.base_url = 'https://api.telegram.org/bot{0}'.format('12') + bot.getMe() diff --git a/tests/test_inlineresult.py b/tests/test_inlineresult.py index 5e73189f4..4ce0181b3 100644 --- a/tests/test_inlineresult.py +++ b/tests/test_inlineresult.py @@ -40,9 +40,9 @@ class InlineQueryResultArticleTest(BaseTest, unittest.TestCase): 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.message_text = 'message text' + #self.parse_mode = 'HTML' + #self.disable_web_page_preview = True self.url = 'url' self.hide_url = True self.description = 'description' @@ -54,9 +54,9 @@ class InlineQueryResultArticleTest(BaseTest, unittest.TestCase): '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, + #'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, @@ -71,10 +71,10 @@ class InlineQueryResultArticleTest(BaseTest, unittest.TestCase): 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.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) @@ -102,29 +102,29 @@ class InlineQueryResultPhotoTest(BaseTest, unittest.TestCase): self.id = 'id' self.type = 'photo' self.photo_url = 'photo url' - self.mime_type = 'mime type' + #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.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, + #'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 + #'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): @@ -133,16 +133,16 @@ class InlineQueryResultPhotoTest(BaseTest, unittest.TestCase): 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.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) + #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) @@ -169,9 +169,9 @@ class InlineQueryResultGifTest(BaseTest, unittest.TestCase): 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.message_text = 'message text' + #self.parse_mode = 'parse mode' + #self.disable_web_page_preview = True self.json_dict = { 'type': self.type, @@ -182,9 +182,9 @@ class InlineQueryResultGifTest(BaseTest, unittest.TestCase): '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 + #'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): @@ -198,10 +198,10 @@ class InlineQueryResultGifTest(BaseTest, unittest.TestCase): 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) + #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) @@ -227,9 +227,9 @@ class InlineQueryResultMpeg4GifTest(BaseTest, unittest.TestCase): 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.message_text = 'message text' + #self.parse_mode = 'parse mode' + #self.disable_web_page_preview = True self.json_dict = { 'type': self.type, @@ -240,9 +240,9 @@ class InlineQueryResultMpeg4GifTest(BaseTest, unittest.TestCase): '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 + #'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): @@ -256,10 +256,10 @@ class InlineQueryResultMpeg4GifTest(BaseTest, unittest.TestCase): 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) + #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) @@ -289,9 +289,9 @@ class InlineQueryResultVideoTest(BaseTest, unittest.TestCase): 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.message_text = 'message text' + #self.parse_mode = 'parse mode' + #self.disable_web_page_preview = True self.json_dict = { 'type': self.type, @@ -305,9 +305,9 @@ class InlineQueryResultVideoTest(BaseTest, unittest.TestCase): '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 + #'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): @@ -324,10 +324,10 @@ class InlineQueryResultVideoTest(BaseTest, unittest.TestCase): 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) + #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) diff --git a/tests/test_jobqueue.py b/tests/test_jobqueue.py index d8239e8e5..84bcc9573 100644 --- a/tests/test_jobqueue.py +++ b/tests/test_jobqueue.py @@ -71,15 +71,6 @@ class JobQueueTest(BaseTest, unittest.TestCase): def job2(self, bot): raise Exception("Test Error") - def test_legacy_import(self): - from telegram import JobQueue as legacyJobQueue - - ljq = legacyJobQueue("Bot", tick_interval=0.005) - - self.assertIsInstance(ljq, JobQueue) - - ljq.stop() - def test_basic(self): self.jq.put(self.job1, 0.1) sleep(1.5) diff --git a/tests/test_reply_keyboard_markup.py b/tests/test_reply_keyboard_markup.py index 02fa1eba0..d4102d6dd 100644 --- a/tests/test_reply_keyboard_markup.py +++ b/tests/test_reply_keyboard_markup.py @@ -32,46 +32,52 @@ class ReplyKeyboardMarkupTest(BaseTest, unittest.TestCase): """This object represents Tests for Telegram ReplyKeyboardMarkup.""" def setUp(self): - self.keyboard = [['button1', 'button2']] + self.keyboard = [[telegram.KeyboardButton('button1'), + telegram.KeyboardButton('button2')]] self.resize_keyboard = True self.one_time_keyboard = True self.selective = True self.json_dict = { - 'keyboard': self.keyboard, + 'keyboard': [[self.keyboard[0][0].to_dict(), + self.keyboard[0][1].to_dict()]], 'resize_keyboard': self.resize_keyboard, 'one_time_keyboard': self.one_time_keyboard, 'selective': self.selective, } - + def test_send_message_with_reply_keyboard_markup(self): message = self._bot.sendMessage(self._chat_id, 'Моё судно на воздушной подушке полно угрей', reply_markup=telegram.ReplyKeyboardMarkup.de_json(self.json_dict)) - + self.assertTrue(self.is_json(message.to_json())) self.assertEqual(message.text, u'Моё судно на воздушной подушке полно угрей') def test_reply_keyboard_markup_de_json(self): reply_keyboard_markup = telegram.ReplyKeyboardMarkup.de_json(self.json_dict) - self.assertEqual(reply_keyboard_markup.keyboard, self.keyboard) + self.assertTrue(isinstance(reply_keyboard_markup.keyboard, list)) + self.assertTrue(isinstance(reply_keyboard_markup.keyboard[0][0], + telegram.KeyboardButton)) self.assertEqual(reply_keyboard_markup.resize_keyboard, self.resize_keyboard) self.assertEqual(reply_keyboard_markup.one_time_keyboard, self.one_time_keyboard) self.assertEqual(reply_keyboard_markup.selective, self.selective) - + def test_reply_keyboard_markup_to_json(self): 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): reply_keyboard_markup = telegram.ReplyKeyboardMarkup.de_json(self.json_dict) - self.assertEqual(reply_keyboard_markup['keyboard'], self.keyboard) + self.assertTrue(isinstance(reply_keyboard_markup.keyboard, list)) + self.assertTrue(isinstance(reply_keyboard_markup.keyboard[0][0], + telegram.KeyboardButton)) self.assertEqual(reply_keyboard_markup['resize_keyboard'], self.resize_keyboard) self.assertEqual(reply_keyboard_markup['one_time_keyboard'], self.one_time_keyboard) self.assertEqual(reply_keyboard_markup['selective'], self.selective) - + if __name__ == '__main__': unittest.main() diff --git a/tests/test_updater.py b/tests/test_updater.py index 078b9b322..b981aa52f 100644 --- a/tests/test_updater.py +++ b/tests/test_updater.py @@ -48,7 +48,7 @@ except ImportError: sys.path.append('.') from telegram import Update, Message, TelegramError, User, Chat, Bot -from telegram.ext import Updater +from telegram.ext import * from telegram.ext.dispatcher import run_async from telegram.error import Unauthorized, InvalidToken from tests.base import BaseTest @@ -60,7 +60,8 @@ root.setLevel(logging.INFO) ch = logging.StreamHandler(sys.stdout) ch.setLevel(logging.WARN) -formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s ' + '- %(message)s') ch.setFormatter(formatter) root.addHandler(ch) @@ -98,8 +99,12 @@ class UpdaterTest(BaseTest, unittest.TestCase): update.chosen_inline_result) self.message_count += 1 + def telegramCallbackHandlerTest(self, bot, update): + self.received_message = update.callback_query + self.message_count += 1 + @run_async - def asyncHandlerTest(self, bot, update, **kwargs): + def asyncHandlerTest(self, bot, update): sleep(1) with self.lock: self.received_message = update.message.text @@ -109,7 +114,7 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.received_message = update self.message_count += 1 - def regexGroupHandlerTest(self, bot, update, groups=None, groupdict=None): + def regexGroupHandlerTest(self, bot, update, groups, groupdict): self.received_message = (groups, groupdict) self.message_count += 1 @@ -120,15 +125,9 @@ class UpdaterTest(BaseTest, unittest.TestCase): update_queue.put('/test5 noresend') elif args[0] == 'noresend': pass - - def contextTest(self, bot, update, context): - self.received_message = update - self.message_count += 1 - self.context = context @run_async - def asyncAdditionalHandlerTest(self, bot, update, update_queue=None, - **kwargs): + def asyncAdditionalHandlerTest(self, bot, update, update_queue=None): sleep(1) with self.lock: if update_queue is not None: @@ -142,37 +141,18 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.received_message = error.message self.message_count += 1 - def test_importLegacyUpdater(self): - from telegram import Updater as legacyUpdater - - lu = legacyUpdater(workers=2, bot=Bot('123:abcd')) - - self.assertIsInstance(lu, Updater) - - lu.stop() - - def test_importLegacyDispatcher(self): - from telegram.ext import Dispatcher - from telegram.utils.updatequeue import UpdateQueue - from telegram import Dispatcher as legacyDispatcher - - lp = legacyDispatcher(bot=Bot('123:abcd'), update_queue=UpdateQueue()) - - self.assertIsInstance(lp, Dispatcher) - - lp.stop() - def test_addRemoveTelegramMessageHandler(self): self._setup_updater('Test') d = self.updater.dispatcher - d.addTelegramMessageHandler( - self.telegramHandlerTest) + from telegram.ext import filters + handler = MessageHandler([filters.TEXT], self.telegramHandlerTest) + d.addHandler(handler) self.updater.start_polling(0.01) sleep(.1) self.assertEqual(self.received_message, 'Test') # Remove handler - d.removeTelegramMessageHandler(self.telegramHandlerTest) + d.removeHandler(handler) self.reset() self.updater.bot.send_messages = 1 @@ -181,8 +161,8 @@ class UpdaterTest(BaseTest, unittest.TestCase): def test_addTelegramMessageHandlerMultipleMessages(self): self._setup_updater('Multiple', 100) - self.updater.dispatcher.addTelegramMessageHandler( - self.telegramHandlerTest) + self.updater.dispatcher.addHandler( + MessageHandler([], self.telegramHandlerTest)) self.updater.start_polling(0.0) sleep(2) self.assertEqual(self.received_message, 'Multiple') @@ -192,14 +172,14 @@ class UpdaterTest(BaseTest, unittest.TestCase): self._setup_updater('Test2') d = self.updater.dispatcher regobj = re.compile('Te.*') - self.updater.dispatcher.addTelegramRegexHandler(regobj, - self.telegramHandlerTest) + handler = RegexHandler(regobj, self.telegramHandlerTest) + self.updater.dispatcher.addHandler(handler) self.updater.start_polling(0.01) sleep(.1) self.assertEqual(self.received_message, 'Test2') # Remove handler - d.removeTelegramRegexHandler(regobj, self.telegramHandlerTest) + d.removeHandler(handler) self.reset() self.updater.bot.send_messages = 1 @@ -209,31 +189,14 @@ class UpdaterTest(BaseTest, unittest.TestCase): def test_addRemoveTelegramCommandHandler(self): self._setup_updater('/test') d = self.updater.dispatcher - self.updater.dispatcher.addTelegramCommandHandler( - 'test', self.telegramHandlerTest) + handler = CommandHandler('test', self.telegramHandlerTest) + self.updater.dispatcher.addHandler(handler) self.updater.start_polling(0.01) sleep(.1) self.assertEqual(self.received_message, '/test') # Remove handler - d.removeTelegramCommandHandler('test', self.telegramHandlerTest) - self.reset() - - self.updater.bot.send_messages = 1 - sleep(.1) - self.assertTrue(None is self.received_message) - - def test_addRemoveUnknownTelegramCommandHandler(self): - self._setup_updater('/test2') - d = self.updater.dispatcher - self.updater.dispatcher.addUnknownTelegramCommandHandler( - self.telegramHandlerTest) - self.updater.start_polling(0.01) - sleep(.1) - self.assertEqual(self.received_message, '/test2') - - # Remove handler - d.removeUnknownTelegramCommandHandler(self.telegramHandlerTest) + d.removeHandler(handler) self.reset() self.updater.bot.send_messages = 1 @@ -243,14 +206,15 @@ class UpdaterTest(BaseTest, unittest.TestCase): def test_addRemoveStringRegexHandler(self): self._setup_updater('', messages=0) d = self.updater.dispatcher - d.addStringRegexHandler('Te.*', self.stringHandlerTest) + handler = StringRegexHandler('Te.*', self.stringHandlerTest) + d.addHandler(handler) queue = self.updater.start_polling(0.01) queue.put('Test3') sleep(.1) self.assertEqual(self.received_message, 'Test3') # Remove handler - d.removeStringRegexHandler('Te.*', self.stringHandlerTest) + d.removeHandler(handler) self.reset() queue.put('Test3') @@ -260,8 +224,8 @@ class UpdaterTest(BaseTest, unittest.TestCase): def test_addRemoveStringCommandHandler(self): self._setup_updater('', messages=0) d = self.updater.dispatcher - d.addStringCommandHandler( - 'test3', self.stringHandlerTest) + handler = StringCommandHandler('test3', self.stringHandlerTest) + d.addHandler(handler) queue = self.updater.start_polling(0.01) queue.put('/test3') @@ -269,31 +233,13 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertEqual(self.received_message, '/test3') # Remove handler - d.removeStringCommandHandler('test3', self.stringHandlerTest) + d.removeHandler(handler) self.reset() queue.put('/test3') sleep(.1) self.assertTrue(None is self.received_message) - def test_addRemoveUnknownStringCommandHandler(self): - self._setup_updater('/test') - d = self.updater.dispatcher - d.addUnknownStringCommandHandler( - self.stringHandlerTest) - queue = self.updater.start_polling(0.01) - queue.put('/test4') - sleep(.1) - self.assertEqual(self.received_message, '/test4') - - # Remove handler - d.removeUnknownStringCommandHandler(self.stringHandlerTest) - self.reset() - - self.updater.bot.send_messages = 1 - sleep(.1) - self.assertTrue(None is self.received_message) - def test_addRemoveErrorHandler(self): self._setup_updater('', messages=0) d = self.updater.dispatcher @@ -315,7 +261,8 @@ class UpdaterTest(BaseTest, unittest.TestCase): def test_errorInHandler(self): self._setup_updater('', messages=0) d = self.updater.dispatcher - d.addStringRegexHandler('.*', self.errorRaisingHandlerTest) + handler = StringRegexHandler('.*', self.errorRaisingHandlerTest) + d.addHandler(handler) self.updater.dispatcher.addErrorHandler(self.errorHandlerTest) queue = self.updater.start_polling(0.01) @@ -326,7 +273,8 @@ class UpdaterTest(BaseTest, unittest.TestCase): def test_cleanBeforeStart(self): self._setup_updater('') d = self.updater.dispatcher - d.addTelegramMessageHandler(self.telegramHandlerTest) + handler = MessageHandler([], self.telegramHandlerTest) + d.addHandler(handler) self.updater.start_polling(0.01, clean=True) sleep(.1) self.assertEqual(self.message_count, 0) @@ -343,7 +291,8 @@ class UpdaterTest(BaseTest, unittest.TestCase): def test_addRemoveTypeHandler(self): self._setup_updater('', messages=0) d = self.updater.dispatcher - d.addTypeHandler(dict, self.stringHandlerTest) + handler = TypeHandler(dict, self.stringHandlerTest) + d.addHandler(handler) queue = self.updater.start_polling(0.01) payload = {"Test": 42} queue.put(payload) @@ -351,18 +300,20 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertEqual(self.received_message, payload) # Remove handler - d.removeTypeHandler(dict, self.stringHandlerTest) + d.removeHandler(handler) self.reset() queue.put(payload) sleep(.1) self.assertTrue(None is self.received_message) - def test_addRemoveInlineHandlerQuery(self): - print('Testing add/removeInlineHandler') + def test_addRemoveInlineQueryHandler(self): self._setup_updater('', messages=0) d = self.updater.dispatcher - d.addTelegramInlineHandler(self.telegramInlineHandlerTest) + handler = InlineQueryHandler(self.telegramInlineHandlerTest) + handler2 = ChosenInlineResultHandler(self.telegramInlineHandlerTest) + d.addHandler(handler) + d.addHandler(handler2) queue = self.updater.start_polling(0.01) update = Update(update_id=0, inline_query="testquery") update2 = Update(update_id=0, chosen_inline_result="testresult") @@ -375,7 +326,27 @@ class UpdaterTest(BaseTest, unittest.TestCase): self.assertEqual(self.received_message[1], "testresult") # Remove handler - d.removeTelegramInlineHandler(self.telegramInlineHandlerTest) + d.removeHandler(handler) + d.removeHandler(handler2) + self.reset() + + queue.put(update) + sleep(.1) + self.assertTrue(None is self.received_message) + + def test_addRemoveCallbackQueryHandler(self): + self._setup_updater('', messages=0) + d = self.updater.dispatcher + handler = CallbackQueryHandler(self.telegramCallbackHandlerTest) + d.addHandler(handler) + queue = self.updater.start_polling(0.01) + update = Update(update_id=0, callback_query="testcallback") + queue.put(update) + sleep(.1) + self.assertEqual(self.received_message, "testcallback") + + # Remove handler + d.removeHandler(handler) self.reset() queue.put(update) @@ -385,8 +356,8 @@ class UpdaterTest(BaseTest, unittest.TestCase): def test_runAsync(self): self._setup_updater('Test5', messages=2) d = self.updater.dispatcher - d.addTelegramMessageHandler( - self.asyncHandlerTest) + handler = MessageHandler([], self.asyncHandlerTest) + d.addHandler(handler) self.updater.start_polling(0.01) sleep(1.2) self.assertEqual(self.received_message, 'Test5') @@ -394,33 +365,23 @@ class UpdaterTest(BaseTest, unittest.TestCase): def test_additionalArgs(self): self._setup_updater('', messages=0) - self.updater.dispatcher.addStringCommandHandler( - 'test5', self.additionalArgsTest) + handler = StringCommandHandler('test5', self.additionalArgsTest, + pass_update_queue=True, pass_args=True) + self.updater.dispatcher.addHandler(handler) queue = self.updater.start_polling(0.01) queue.put('/test5 resend') sleep(.1) self.assertEqual(self.received_message, '/test5 noresend') self.assertEqual(self.message_count, 2) - - def test_context(self): - context = "context_data" - self._setup_updater('', messages=0) - self.updater.dispatcher.addStringCommandHandler( - 'test_context', self.contextTest) - - queue = self.updater.start_polling(0.01) - queue.put('/test_context', context=context) - sleep(.5) - self.assertEqual(self.received_message, '/test_context') - self.assertEqual(self.message_count, 1) - self.assertEqual(self.context, context) def test_regexGroupHandler(self): self._setup_updater('', messages=0) d = self.updater.dispatcher - d.addStringRegexHandler('^(This).*?(?Pregex group).*', - self.regexGroupHandlerTest) + handler = StringRegexHandler('^(This).*?(?Pregex group).*', + self.regexGroupHandlerTest, + pass_groupdict=True, pass_groups=True) + d.addHandler(handler) queue = self.updater.start_polling(0.01) queue.put('This is a test message for regex group matching.') sleep(.1) @@ -430,8 +391,9 @@ class UpdaterTest(BaseTest, unittest.TestCase): def test_runAsyncWithAdditionalArgs(self): self._setup_updater('Test6', messages=2) d = self.updater.dispatcher - d.addTelegramMessageHandler( - self.asyncAdditionalHandlerTest) + handler = MessageHandler([], self.asyncAdditionalHandlerTest, + pass_update_queue=True) + d.addHandler(handler) self.updater.start_polling(0.01) sleep(1.2) self.assertEqual(self.received_message, 'Test6') @@ -440,15 +402,16 @@ class UpdaterTest(BaseTest, unittest.TestCase): def test_webhook(self): self._setup_updater('', messages=0) d = self.updater.dispatcher - d.addTelegramMessageHandler( - self.telegramHandlerTest) + handler = MessageHandler([], self.telegramHandlerTest) + d.addHandler(handler) ip = '127.0.0.1' port = randrange(1024, 49152) # Select random port for travis self.updater.start_webhook(ip, port, url_path='TOKEN', cert='./tests/test_updater.py', - key='./tests/test_updater.py') + key='./tests/test_updater.py', + webhook_url=None) sleep(0.5) # SSL-Wrapping will fail, so we start the server without SSL Thread(target=self.updater.httpd.serve_forever).start() @@ -485,12 +448,12 @@ class UpdaterTest(BaseTest, unittest.TestCase): def test_webhook_no_ssl(self): self._setup_updater('', messages=0) d = self.updater.dispatcher - d.addTelegramMessageHandler( - self.telegramHandlerTest) + handler = MessageHandler([], self.telegramHandlerTest) + d.addHandler(handler) ip = '127.0.0.1' port = randrange(1024, 49152) # Select random port for travis - self.updater.start_webhook(ip, port) + self.updater.start_webhook(ip, port, webhook_url=None) sleep(0.5) # Now, we send an update to the server via urlopen @@ -505,6 +468,12 @@ class UpdaterTest(BaseTest, unittest.TestCase): sleep(1) self.assertEqual(self.received_message, 'Webhook Test 2') + def test_start_dispatcher_twice(self): + self._setup_updater('', messages=0) + d = self.updater.dispatcher + self.updater.start_polling(0.1) + d.start() + def test_bootstrap_retries_success(self): retries = 3 self._setup_updater('', messages=0, bootstrap_retries=retries)