Compare commits

..

49 Commits

Author SHA1 Message Date
Leandro Toledo 4f26bdd18f Merge pull request #213 from python-telegram-bot/prep34
Prepare Release of v3.4
2016-03-21 23:15:38 -03:00
Jannes Höke 808945b623 prepare release of v3.4 2016-03-22 03:02:13 +01:00
Jannes Höke 63a83d4cc2 fix imports of updatequeue 2016-03-22 02:48:56 +01:00
Jannes Höke c43b348117 lazily load all of telegram.ext 2016-03-22 02:42:40 +01:00
Jannes Höke 45a47d54bd move updatequeue to telegram.utils 2016-03-22 02:42:19 +01:00
Jannes Höke 5e7f2688be add encoding to fsm example bot 2016-03-22 02:33:43 +01:00
Jannes Höke 7199d2894f add state machine example 2016-03-21 20:08:32 +01:00
Jannes Höke d9a58fd904 Merge pull request #211 from python-telegram-bot/ext-docs
Adjust sphinx docs to telegram.ext submodule
2016-03-21 14:23:33 +01:00
Jannes Höke cf9897e74b Merge pull request #208 from rahiel/master
declare support for python 3.5, drop old py 3.2 reference
2016-03-21 14:23:23 +01:00
Jannes Höke 30308bdcc6 Fix formatting, table of contents link, add note on errors 2016-03-20 00:37:55 +01:00
Jannes Höke af62c5be8e update examples to telegram.ext 2016-03-19 16:39:35 +01:00
Jannes Höke cd7bc8dfac Update README to telegram.ext, move API section up 2016-03-19 16:35:02 +01:00
Jannes Höke 150914cf25 Adjust sphinx docs to telegram.ext submodule 2016-03-19 13:26:38 +01:00
Rahiel Kasim 19390b9659 declare support for python 3.5, drop old py 3.2 reference 2016-03-17 22:12:36 +01:00
Jannes Höke fac4eb6438 Merge pull request #202 from tsnoam/master
Updater webhook bootstrapping improvements
2016-03-17 13:05:04 +01:00
Noam Meltzer 2939885c5b test_updater: import Updater from telegram.ext 2016-03-15 22:08:06 +02:00
Noam Meltzer f07f33e160 updater: unitests for _set_webhook 2016-03-15 22:02:34 +02:00
Noam Meltzer 0ddcb16889 start_webhook(): call bot.setWebhook() as a bootstrap step 2016-03-15 22:02:34 +02:00
Noam Meltzer 594b81e463 start_polling(): new argument - bootstrap_retries
refs #196
2016-03-15 22:02:16 +02:00
Leandro Toledo 41d0d45e2f Merge pull request #204 from python-telegram-bot/debug-level-logs
Changing INFO logs to DEBUG and minor fixes
2016-03-15 11:35:05 -03:00
Leandro Toledo 1e4ae6546f Changing INFO logs to DEBUG and minor fixes 2016-03-14 22:56:20 -03:00
Leandro Toledo fd170773e2 Merge pull request #198 from rahiel/master
lazily load Updater & move extended classes to submodule
2016-03-14 17:00:25 -03:00
Rahiel Kasim d1516f66ac deprecation warning for telegram.Updater 2016-03-14 19:43:38 +01:00
Rahiel Kasim 739e218eb7 PEP8 2016-03-14 15:26:22 +01:00
Rahiel Kasim dcea2c8015 Merge branch 'master' of https://github.com/leandrotoledo/python-telegram-bot 2016-03-14 15:07:16 +01:00
Rahiel Kasim 98112d3987 move Updater and friends to ext submodule 2016-03-14 14:50:12 +01:00
Jannes Höke 45a4689fd0 update docstrings of Message regarding supergroup conversion 2016-03-14 09:54:33 +01:00
Jannes Höke 82030c4109 Merge pull request #199 from python-telegram-bot/send-inputfile-from-bytesio
Fix InputFile attribute check when from a BufferedReader object
2016-03-14 01:17:27 +01:00
Leandro Toledo 25595e6d9e Update README.rst
Update group link
2016-03-13 19:23:48 -03:00
Leandro Toledo 9637a8748c Merge pull request #201 from shelomentsevd/master
Dispather command processing fix(#200).
2016-03-13 12:26:04 -03:00
Dmitriy 00e2b4815a Processing commands without casting to UTF-8 2016-03-13 12:32:27 +03:00
Shelomentsev Dmitriy fb34f81533 ShelomentsevD added to AUTHORS.rst 2016-03-13 02:27:10 +03:00
Shelomentsev Dmitriy 3d89f6b284 dispatchTelegramCommand no-break space fix. 2016-03-13 02:15:48 +03:00
Leandro Toledo 7a8e84b46f Update README.rst 2016-03-12 20:04:45 -03:00
Leandro Toledo 8ad34fc3c0 Fix InputFile attribute check when from a BufferedReader object 2016-03-12 19:40:56 -03:00
Leandro Toledo 00f4328bab Remove files from download()' tests on make clean 2016-03-12 18:23:14 -03:00
Rahiel Kasim 8b196ce71f load Updater class only when used 2016-03-12 15:29:54 +01:00
Jannes Höke 196d1fcc3d Update invite link to supergroup 2016-03-11 23:43:41 +01:00
Leandro Toledo 7ff7397d0f Update CONTRIBUTING.rst 2016-03-11 17:45:06 -03:00
Leandro Toledo 0c676b1a75 Merge pull request #195 from aidarbiktimirov/master
Added disable_notification parameter for silent messages
2016-03-11 17:12:00 -03:00
Aydar Biktimirov dc9b77e02c Merge remote-tracking branch 'upstream/master' 2016-03-11 23:03:13 +03:00
Leandro Toledo e7b339d527 Merge pull request #197 from rahiel/master
bot.sendMessage: update documentation
2016-03-11 16:42:16 -03:00
Rahiel Kasim 5958da0031 bot.sendMessage: update documentation 2016-03-11 20:32:10 +01:00
Aydar Biktimirov 0600b2634e added tests for disable_notification parameter 2016-03-11 09:37:43 +03:00
Leandro Toledo f8f16f0fe9 Update README.rst
Link to group updated
2016-03-10 10:12:39 -03:00
Aydar Biktimirov 433110abe9 Added disable_notification parameter for silent messages 2016-03-09 18:47:33 +03:00
Noam Meltzer 793437fec7 Merge pull request #191 from tsnoam/master
updater: allow cleaning updates from Telegram servers before start (+ docstring fix)
2016-03-01 23:32:20 +02:00
Noam Meltzer a0a040a9c2 updater: allow cleaning updates from Telegram servers before start 2016-03-01 22:12:12 +02:00
Noam Meltzer f0e7a3316c jobqueue: fix docstring 2016-03-01 20:20:51 +02:00
34 changed files with 559 additions and 195 deletions
+1
View File
@@ -4,6 +4,7 @@ python:
- "2.7"
- "3.3"
- "3.4"
- "3.5"
- "pypy"
- "pypy3"
install:
+1
View File
@@ -23,6 +23,7 @@ The following wonderful people contributed directly or indirectly to this projec
- `Noam Meltzer <https://github.com/tsnoam>`_
- `Oleg Shlyazhko <https://github.com/ollmer>`_
- `Rahiel Kasim <https://github.com/rahiel>`_
- `Shelomentsev D <https://github.com/shelomentsevd>`_
- `sooyhwang <https://github.com/sooyhwang>`_
- `wjt <https://github.com/wjt>`_
+10
View File
@@ -1,3 +1,13 @@
**2016-03-22**
*Released 3.4*
- Move ``Updater``, ``Dispatcher`` and ``JobQueue`` to new ``telegram.ext`` submodule (thanks to @rahiel)
- Add ``disable_notification`` parameter (thanks to @aidarbiktimirov)
- Fix bug where commands sent by Telegram Web would not be recognized (thanks to @shelomentsevd)
- Add option to skip old updates on bot startup
- Send files from ``BufferedReader``
**2016-02-28**
*Released 3.3*
+1 -1
View File
@@ -54,7 +54,7 @@ Here's how to make a one-off code change.
``$ git checkout master``
``$ git checkout merge upstream/master``
``$ git merge upstream/master``
``$ git checkout -b your-branch-name``
+2
View File
@@ -1,3 +1,4 @@
.DEFAULT_GOAL := help
.PHONY: clean pep8 lint test install
PYLINT := pylint
@@ -12,6 +13,7 @@ clean:
find . -name '*.pyc' -exec rm -f {} \;
find . -name '*.pyo' -exec rm -f {} \;
find . -name '*~' -exec rm -f {} \;
find . -regex "./telegram.\(mp3\|mp4\|ogg\|png\|webp\)" -exec rm {} \;
pep257:
$(PEP257) telegram
+83 -79
View File
@@ -36,7 +36,7 @@ A Python wrapper around the Telegram Bot API.
:alt: Coveralls
.. image:: https://img.shields.io/badge/Telegram-Group-blue.svg
:target: https://telegram.me/joinchat/ALnA-AJQm5TcNEiy2G_4cQ
:target: https://telegram.me/pythontelegrambotgroup
:alt: Telegram Group
=================
@@ -57,9 +57,9 @@ Table of contents
- `Getting started`_
1. `The Updater class`_
1. `API`_
2. `API`_
2. `Extensions`_
3. `JobQueue`_
@@ -107,6 +107,7 @@ getUpdates Yes
getUserProfilePhotos Yes
getFile Yes
setWebhook Yes
answerInlineQuery Yes
========================= ============
-------------------------
@@ -120,6 +121,7 @@ Python Version *Supported?*
2.7 Yes
3.3 Yes
3.4 Yes
3.5 Yes
PyPy Yes
PyPy3 Yes
============== ============
@@ -173,85 +175,13 @@ This library uses the `logging` module. To set up logging to standard output, pu
at the beginning of your script.
--------------------
_`The Updater class`
--------------------
The ``Updater`` class is the new way to create bots with ``python-telegram-bot``. It provides an easy-to-use interface to the ``telegram.Bot`` by caring about getting new updates from telegram and forwarding them to the ``Dispatcher`` class. We can register handler functions in the ``Dispatcher`` to make our bot react to Telegram commands, messages and even arbitrary updates.
As with the old method, we'll need an Access Token. To generate an Access Token, we have to talk to `BotFather <https://telegram.me/botfather>`_ and follow a few simple steps (described `here <https://core.telegram.org/bots#botfather>`_).
First, we create an ``Updater`` object::
>>> from telegram import Updater
>>> updater = Updater(token='token')
For quicker access to the ``Dispatcher`` used by our ``Updater``, we can introduce it locally::
>>> dispatcher = updater.dispatcher
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::
>>> dispatcher.addTelegramCommandHandler('start', start)
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::
>>> def echo(bot, update):
... bot.sendMessage(chat_id=update.message.chat_id, text=update.message.text)
...
>>> dispatcher.addTelegramMessageHandler(echo)
Our bot should now reply to all 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::
>>> 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)
To enable our bot to respond to inline queries, we can add the following (you will also have to talk to BotFather)::
>>> from telegram import InlineQueryResultArticle
>>> def inline_caps(bot, update):
... # If you activated inline feedback, updates might either contain
... # inline_query or chosen_inline_result, the other one will be None
... if update.inline_query:
... query = bot.update.inline_query.query
... results = list()
... results.append(InlineQueryResultArticle(query.upper(), 'Caps', text_caps))
... bot.answerInlineQuery(update.inline_query.id, results)
...
>>> dispatcher.addTelegramInlineHandler(inline_caps)
Now it's time to stop the bot::
>>> updater.stop()
Check out more examples in the `examples folder <https://github.com/python-telegram-bot/python-telegram-bot/tree/master/examples>`_!
**Note:** The ``telegram.ext`` module will catch errors that would cause the bot to crash. All these are logged to the ``logging`` module, so it's recommended to use this if you are looking for error causes.
------
_`API`
------
Note: Using the ``Bot`` class directly is the 'old' method, but some of this is still important information, even if you're using the ``Updater`` class!
Note: Using the ``Bot`` class directly is the 'old' method, but almost all of this is still important information, even if you're using the ``telegram.ext`` submodule!
The API is exposed via the ``telegram.Bot`` class.
@@ -338,13 +268,87 @@ There are many more API methods, to read the full API documentation::
$ pydoc telegram.Bot
-------------
_`Extensions`
-------------
The ``telegram.ext`` submodule is built on top of the bare-metal API. It provides an easy-to-use interface to the ``telegram.Bot`` by caring about getting new updates with the ``Updater`` class from telegram and forwarding them to the ``Dispatcher`` class. We can register handler functions in the ``Dispatcher`` to make our bot react to Telegram commands, messages and even arbitrary updates.
We'll need an Access Token. **Note:** If you have done this in the previous step, you can use that one. To generate an Access Token, we have to talk to `BotFather <https://telegram.me/botfather>`_ and follow a few simple steps (described `here <https://core.telegram.org/bots#botfather>`_).
First, we create an ``Updater`` object::
>>> from telegram.ext import Updater
>>> updater = Updater(token='token')
For quicker access to the ``Dispatcher`` used by our ``Updater``, we can introduce it locally::
>>> dispatcher = updater.dispatcher
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::
>>> dispatcher.addTelegramCommandHandler('start', start)
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::
>>> def echo(bot, update):
... bot.sendMessage(chat_id=update.message.chat_id, text=update.message.text)
...
>>> dispatcher.addTelegramMessageHandler(echo)
Our bot should now reply to all 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::
>>> 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)
To enable our bot to respond to inline queries, we can add the following (you will also have to talk to BotFather)::
>>> from telegram import InlineQueryResultArticle
>>> def inline_caps(bot, update):
... # If you activated inline feedback, updates might either contain
... # inline_query or chosen_inline_result, the other one will be None
... if update.inline_query:
... query = bot.update.inline_query.query
... results = list()
... results.append(InlineQueryResultArticle(query.upper(), 'Caps', text_caps))
... bot.answerInlineQuery(update.inline_query.id, results)
...
>>> dispatcher.addTelegramInlineHandler(inline_caps)
Now it's time to stop the bot::
>>> updater.stop()
Check out more examples in the `examples folder <https://github.com/python-telegram-bot/python-telegram-bot/tree/master/examples>`_!
-----------
_`JobQueue`
-----------
The ``JobQueue`` allows you to perform tasks with a delay or even periodically. The ``Updater`` will create one for you::
>>> from telegram import Updater
>>> from telegram.ext import Updater
>>> u = Updater('TOKEN')
>>> j = u.job_queue
@@ -426,7 +430,7 @@ You may copy, distribute and modify the software provided that modifications are
_`Contact`
==========
Feel free to join to our `Telegram group <https://telegram.me/joinchat/ALnA-AJQm5TcNEiy2G_4cQ>`_.
Feel free to join to our `Telegram group <https://telegram.me/pythontelegrambotgroup>`_.
=======
_`TODO`
+2 -2
View File
@@ -58,9 +58,9 @@ author = u'Leandro Toledo'
# built documents.
#
# The short X.Y version.
version = '3.3'
version = '3.4'
# The full version, including alpha/beta/rc tags.
release = '3.3.0'
release = '3.4.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
-7
View File
@@ -1,7 +0,0 @@
telegram.dispatcher module
=========================
.. automodule:: telegram.dispatcher
:members:
:undoc-members:
:show-inheritance:
+7
View File
@@ -0,0 +1,7 @@
telegram.ext.dispatcher module
==============================
.. automodule:: telegram.ext.dispatcher
:members:
:undoc-members:
:show-inheritance:
+7
View File
@@ -0,0 +1,7 @@
telegram.ext.updater module
===========================
.. automodule:: telegram.ext.updater
:members:
:undoc-members:
:show-inheritance:
+2 -2
View File
@@ -9,8 +9,8 @@ Submodules
telegram.audio
telegram.base
telegram.bot
telegram.updater
telegram.dispatcher
telegram.ext.updater
telegram.ext.dispatcher
telegram.jobqueue
telegram.inlinequery
telegram.inlinequeryresult
-7
View File
@@ -1,7 +0,0 @@
telegram.updater module
=========================
.. automodule:: telegram.updater
:members:
:undoc-members:
:show-inheritance:
+2 -2
View File
@@ -18,8 +18,8 @@ Reply to last chat from the command line by typing "/reply <text>"
Type 'stop' on the command line to stop the bot.
"""
from telegram import Updater
from telegram.dispatcher import run_async
from telegram.ext import Updater
from telegram.ext.dispatcher import run_async
from time import sleep
import logging
+1 -1
View File
@@ -17,7 +17,7 @@ Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
from telegram import Updater
from telegram.ext import Updater
import logging
# Enable logging
+2 -1
View File
@@ -20,7 +20,8 @@ from random import getrandbits
import re
from telegram import Updater, Update, InlineQueryResultArticle, ParseMode
from telegram import InlineQueryResultArticle, ParseMode
from telegram.ext import Updater
import logging
# Enable logging
+103
View File
@@ -0,0 +1,103 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Basic example for a bot that awaits an answer from the user
# This program is dedicated to the public domain under the CC0 license.
import logging
from telegram import Updater, ReplyKeyboardMarkup, Emoji, ForceReply
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - '
'%(message)s',
level=logging.INFO)
# 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
text = update.message.text
chat_state = state.get(chat_id, MENU)
chat_context = context.get(chat_id, None)
# Since the handler will also be called on messages, we need to check if
# the message is actually a command
if chat_state == MENU and text[0] == '/':
state[chat_id] = AWAIT_INPUT # set the state
context[chat_id] = user_id # save the user id to context
bot.sendMessage(chat_id,
text="Please enter your settings value or send "
"/cancel to abort",
reply_markup=ForceReply())
# If we are waiting for input and the right user answered
elif chat_state == AWAIT_INPUT and chat_context == user_id:
state[chat_id] = AWAIT_CONFIRMATION
# 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)
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
if text == YES:
values[chat_id] = chat_context[1]
bot.sendMessage(chat_id,
text="Changed value to %s." % values[chat_id])
else:
bot.sendMessage(chat_id,
text="Value not changed: %s."
% values.get(chat_id, '<not set>'))
# Handler for the /cancel command.
# 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
def help(bot, update):
bot.sendMessage(update.message.chat_id, text="Use /set to test this bot.")
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN")
# The command
updater.dispatcher.addTelegramCommandHandler('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)
# 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()
+1 -1
View File
@@ -18,7 +18,7 @@ Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
from telegram import Updater
from telegram.ext import Updater
import logging
# Enable logging
+2 -2
View File
@@ -26,7 +26,7 @@ def requirements():
setup(
name='python-telegram-bot',
version='3.3',
version='3.4',
author='Leandro Toledo',
author_email='devs@python-telegram-bot.org',
license='LGPLv3',
@@ -50,8 +50,8 @@ setup(
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
],
)
+41 -14
View File
@@ -49,21 +49,48 @@ from .inlinequeryresult import InlineQueryResultArticle, InlineQueryResultGif,\
InlineQueryResultMpeg4Gif, InlineQueryResultPhoto, InlineQueryResultVideo
from .update import Update
from .bot import Bot
from .dispatcher import Dispatcher
from .jobqueue import JobQueue
from .updatequeue import UpdateQueue
from .updater import Updater
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.3'
__all__ = ('Bot', 'Updater', 'Dispatcher', 'Emoji', 'TelegramError',
'InputFile', 'ReplyMarkup', 'ForceReply', 'ReplyKeyboardHide',
'ReplyKeyboardMarkup', 'UserProfilePhotos', 'ChatAction',
'Location', 'Contact', 'Video', 'Sticker', 'Document', 'File',
'Audio', 'PhotoSize', 'Chat', 'Update', 'ParseMode', 'Message',
'User', 'TelegramObject', 'NullHandler', 'Voice', 'JobQueue',
'InlineQuery', 'ChosenInlineResult', 'InlineQueryResultArticle',
__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',
'UpdateQueue')
'InlineQueryResultMpeg4Gif', 'InlineQueryResultVideo')
+46 -7
View File
@@ -29,8 +29,7 @@ from telegram.error import InvalidToken
from telegram.utils import request
from telegram.utils.validate import validate_string
H = NullHandler()
logging.getLogger(__name__).addHandler(H)
logging.getLogger(__name__).addHandler(NullHandler())
class Bot(TelegramObject):
@@ -161,6 +160,10 @@ class Bot(TelegramObject):
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):
@@ -206,13 +209,17 @@ class Bot(TelegramObject):
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. For the moment, only Telegram
for Android supports this. [Optional]
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.
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:
@@ -252,6 +259,10 @@ 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]
Returns:
A telegram.Message instance representing the message forwarded.
@@ -288,6 +299,10 @@ 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:
@@ -342,6 +357,10 @@ 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:
@@ -386,6 +405,10 @@ 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:
@@ -422,6 +445,10 @@ 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:
@@ -466,6 +493,10 @@ class Bot(TelegramObject):
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:
@@ -512,6 +543,10 @@ 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:
@@ -549,6 +584,10 @@ 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:
@@ -757,7 +796,7 @@ class Bot(TelegramObject):
result = request.post(url, data, network_delay=network_delay)
if result:
self.logger.info(
self.logger.debug(
'Getting updates: %s', [u['update_id'] for u in result])
else:
self.logger.debug('No new updates found.')
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""Extensions over the Telegram Bot API to facilitate bot making"""
from .dispatcher import Dispatcher
from .jobqueue import JobQueue
from .updater import Updater
__all__ = ('Dispatcher', 'JobQueue', 'Updater')
@@ -23,14 +23,13 @@ import logging
from functools import wraps
from inspect import getargspec
from threading import Thread, BoundedSemaphore, Lock, Event, current_thread
from re import match
from re import match, split
from time import sleep
from telegram import (TelegramError, Update, NullHandler)
from telegram.updatequeue import Empty
from telegram.utils.updatequeue import Empty
H = NullHandler()
logging.getLogger(__name__).addHandler(H)
logging.getLogger(__name__).addHandler(NullHandler())
semaphore = None
async_threads = set()
@@ -158,7 +157,7 @@ class Dispatcher:
if not semaphore:
semaphore = BoundedSemaphore(value=workers)
else:
self.logger.info("Semaphore already initialized, skipping.")
self.logger.debug('Semaphore already initialized, skipping.')
def start(self):
"""
@@ -176,7 +175,7 @@ class Dispatcher:
raise TelegramError(msg)
self.running = True
self.logger.info('Dispatcher started')
self.logger.debug('Dispatcher started')
while 1:
try:
@@ -184,7 +183,7 @@ class Dispatcher:
update, context = self.update_queue.get(True, 1, True)
except Empty:
if self.__stop_event.is_set():
self.logger.info('orderly stopping')
self.logger.debug('orderly stopping')
break
elif self.__stop_event.is_set():
self.logger.critical(
@@ -214,7 +213,7 @@ class Dispatcher:
"processing an update")
self.running = False
self.logger.info('Dispatcher thread stopped')
self.logger.debug('Dispatcher thread stopped')
def stop(self):
"""
@@ -548,7 +547,7 @@ class Dispatcher:
command
"""
command = update.message.text.split(' ')[0][1:].split('@')[0]
command = split('\W', update.message.text[1:])[0]
if command in self.telegram_command_handlers:
self.dispatchTo(self.telegram_command_handlers[command], update,
@@ -41,10 +41,9 @@ class JobQueue(object):
Args:
bot (Bot): The bot instance that should be passed to the jobs
Keyword Args:
tick_interval (Optional[float]): The interval this queue should check
the newest task in seconds. Defaults to 1.0
"""
def __init__(self, bot, tick_interval=1.0):
@@ -87,11 +86,11 @@ class JobQueue(object):
next_t += time.time()
self.logger.debug("Putting a %s with t=%f" % (job.name, next_t))
self.logger.debug('Putting a %s with t=%f' % (job.name, next_t))
self.queue.put((next_t, job))
if not self.running and not prevent_autostart:
self.logger.info("Auto-starting JobQueue")
self.logger.debug('Auto-starting JobQueue')
self.start()
def tick(self):
@@ -100,24 +99,24 @@ class JobQueue(object):
"""
now = time.time()
self.logger.debug("Ticking jobs with t=%f" % now)
self.logger.debug('Ticking jobs with t=%f' % now)
while not self.queue.empty():
t, j = self.queue.queue[0]
self.logger.debug("Peeked at %s with t=%f" % (j.name, t))
self.logger.debug('Peeked at %s with t=%f' % (j.name, t))
if t < now:
self.queue.get()
self.logger.info("Running job %s" % j.name)
self.logger.debug('Running job %s' % j.name)
try:
j.run(self.bot)
except:
self.logger.exception("An uncaught error was raised while "
"executing job %s" % j.name)
self.logger.exception('An uncaught error was raised while '
'executing job %s' % j.name)
if j.repeat:
self.put(j.run, j.interval)
continue
self.logger.debug("Next task isn't due yet. Finished!")
self.logger.debug('Next task isn\'t due yet. Finished!')
break
def start(self):
@@ -131,7 +130,7 @@ class JobQueue(object):
job_queue_thread = Thread(target=self._start,
name="job_queue")
job_queue_thread.start()
self.logger.info('Job Queue thread started')
self.logger.debug('Job Queue thread started')
else:
self.__lock.release()
@@ -144,7 +143,7 @@ class JobQueue(object):
self.tick()
time.sleep(self.tick_interval)
self.logger.info('Job Queue thread stopped')
self.logger.debug('Job Queue thread stopped')
def stop(self):
"""
+95 -24
View File
@@ -28,12 +28,13 @@ from threading import Thread, Lock, current_thread, Event
from time import sleep
import subprocess
from signal import signal, SIGINT, SIGTERM, SIGABRT
from telegram import (Bot, TelegramError, dispatcher, Dispatcher,
NullHandler, JobQueue, UpdateQueue)
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)
H = NullHandler()
logging.getLogger(__name__).addHandler(H)
logging.getLogger(__name__).addHandler(NullHandler())
class Updater:
@@ -58,6 +59,8 @@ class Updater:
workers (Optional[int]): Amount of threads in the thread pool for
functions decorated with @run_async
bot (Optional[Bot]):
job_queue_tick_interval(Optional[float]): The interval the queue should
be checked for new tasks. Defaults to 1.0
Raises:
ValueError: If both `token` and `bot` are passed or none of them.
@@ -92,7 +95,8 @@ class Updater:
self.__threads = []
""":type: list[Thread]"""
def start_polling(self, poll_interval=0.0, timeout=10, network_delay=2):
def start_polling(self, poll_interval=0.0, timeout=10, network_delay=2,
clean=False, bootstrap_retries=0):
"""
Starts polling updates from Telegram.
@@ -101,19 +105,30 @@ class Updater:
updates from Telegram in seconds. Default is 0.0
timeout (Optional[float]): Passed to Bot.getUpdates
network_delay (Optional[float]): Passed to Bot.getUpdates
clean (Optional[bool]): Whether to clean any pending updates on
Telegram servers before actually starting to poll. Default 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
Returns:
Queue: The update queue that can be filled from the main thread
"""
"""
with self.__lock:
if not self.running:
self.running = True
if clean:
self._clean_updates()
# Create & start threads
self._init_thread(self.dispatcher.start, "dispatcher")
self._init_thread(self._start_polling, "updater",
poll_interval, timeout, network_delay)
poll_interval, timeout, network_delay,
bootstrap_retries)
# Return the update queue so the main thread can insert updates
return self.update_queue
@@ -140,7 +155,10 @@ class Updater:
port=80,
url_path='',
cert=None,
key=None):
key=None,
clean=False,
bootstrap_retries=0,
webhook_url=None):
"""
Starts a small http server to listen for updates via webhook. If cert
and key are not provided, the webhook will be started directly on
@@ -154,35 +172,49 @@ class Updater:
url_path (Optional[str]): Path inside url
cert (Optional[str]): Path to the SSL certificate file
key (Optional[str]): Path to the SSL key file
clean (Optional[bool]): Whether to clean any pending updates on
Telegram servers before actually starting the webhook. Default
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
webhook_url (Optional[str]): Explicitly specifiy the webhook url.
Useful behind NAT, reverse proxy, etc. Default is derived from
`listen`, `port` & `url_path`.
Returns:
Queue: The update queue that can be filled from the main thread
"""
"""
with self.__lock:
if not self.running:
self.running = True
if clean:
self._clean_updates()
# Create & start threads
self._init_thread(self.dispatcher.start, "dispatcher"),
self._init_thread(self._start_webhook, "updater", listen,
port, url_path, cert, key)
port, url_path, cert, key, bootstrap_retries,
webhook_url)
# Return the update queue so the main thread can insert updates
return self.update_queue
def _start_polling(self, poll_interval, timeout, network_delay):
def _start_polling(self, poll_interval, timeout, network_delay,
bootstrap_retries):
"""
Thread target of thread 'updater'. Runs in background, pulls
updates from Telegram and inserts them in the update queue of the
Dispatcher.
"""
cur_interval = poll_interval
self.logger.info('Updater thread started')
self.logger.debug('Updater thread started')
# Remove webhook
self.bot.setWebhook(webhook_url=None)
self._set_webhook(None, bootstrap_retries)
while self.running:
try:
@@ -201,8 +233,8 @@ class Updater:
else:
if not self.running:
if len(updates) > 0:
self.logger.info('Updates ignored and will be pulled '
'again on restart.')
self.logger.debug('Updates ignored and will be pulled '
'again on restart.')
break
if updates:
@@ -214,6 +246,27 @@ class Updater:
sleep(cur_interval)
def _set_webhook(self, webhook_url, max_retries):
retries = 0
while 1:
try:
# Remove webhook
self.bot.setWebhook(webhook_url=webhook_url)
except (Unauthorized, InvalidToken):
raise
except TelegramError:
msg = 'failed to set webhook; try={0} max_retries={1}'.format(
retries, max_retries)
if max_retries < 0 or retries < max_retries:
self.logger.info(msg)
retries += 1
else:
self.logger.exception(msg)
raise
else:
break
sleep(1)
@staticmethod
def _increase_poll_interval(current_interval):
# increase waiting times on subsequent errors up to 30secs
@@ -225,14 +278,19 @@ class Updater:
current_interval = 30
return current_interval
def _start_webhook(self, listen, port, url_path, cert, key):
self.logger.info('Updater thread started')
def _start_webhook(self, listen, port, url_path, cert, key,
bootstrap_retries, webhook_url):
self.logger.debug('Updater thread started')
use_ssl = cert is not None and key is not None
url_path = "/%s" % url_path
# Create and start server
self.httpd = WebhookServer((listen, port), WebhookHandler,
self.update_queue, url_path)
if not webhook_url:
webhook_url = self._gen_webhook_url(listen, port, url_path,
use_ssl)
self._set_webhook(webhook_url, bootstrap_retries)
if use_ssl:
# Check SSL-Certificate with openssl, if possible
@@ -258,6 +316,19 @@ class Updater:
self.httpd.serve_forever(poll_interval=1)
def _gen_webhook_url(self, listen, port, url_path, use_ssl):
return '{proto}://{listen}:{port}{path}'.format(
proto='https' if use_ssl else 'http',
listen=listen,
port=port,
path=url_path)
def _clean_updates(self):
self.logger.debug('Cleaning updates from Telegram server')
updates = self.bot.getUpdates()
while updates:
updates = self.bot.getUpdates(updates[-1].update_id + 1)
def stop(self):
"""
Stops the polling/webhook thread, the dispatcher and the job queue
@@ -266,7 +337,7 @@ class Updater:
self.job_queue.stop()
with self.__lock:
if self.running:
self.logger.info('Stopping Updater and Dispatcher...')
self.logger.debug('Stopping Updater and Dispatcher...')
self.running = False
@@ -280,7 +351,7 @@ class Updater:
def _stop_httpd(self):
if self.httpd:
self.logger.info(
self.logger.debug(
'Waiting for current webhook connection to be '
'closed... Send a Telegram message to the bot to exit '
'immediately.')
@@ -288,7 +359,7 @@ class Updater:
self.httpd = None
def _stop_dispatcher(self):
self.logger.debug("Requesting Dispatcher to stop...")
self.logger.debug('Requesting Dispatcher to stop...')
self.dispatcher.stop()
def _join_async_threads(self):
@@ -296,7 +367,7 @@ class Updater:
threads = list(dispatcher.async_threads)
total = len(threads)
for i, thr in enumerate(threads):
self.logger.info(
self.logger.debug(
'Waiting for async thread {0}/{1} to end'.format(i, total))
thr.join()
self.logger.debug(
@@ -304,7 +375,7 @@ class Updater:
def _join_threads(self):
for thr in self.__threads:
self.logger.info(
self.logger.debug(
'Waiting for {0} thread to end'.format(thr.name))
thr.join()
self.logger.debug('{0} thread has ended'.format(thr.name))
+3 -2
View File
@@ -81,7 +81,8 @@ class InputFile(object):
self.input_file_content = self.input_file.read()
if 'filename' in data:
self.filename = self.data.pop('filename')
elif isinstance(self.input_file, file):
elif isinstance(self.input_file, file) and \
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)\
@@ -134,7 +135,7 @@ class InputFile(object):
form_boundary,
'Content-Disposition: form-data; name="%s"; filename="%s"' % (
self.input_name, self.filename
),
),
'Content-Type: %s' % self.mimetype,
'',
self.input_file_content
+8
View File
@@ -56,6 +56,10 @@ class Message(TelegramObject):
new_chat_photo (List[:class:`telegram.PhotoSize`]):
delete_chat_photo (bool):
group_chat_created (bool):
supergroup_chat_created (bool):
migrate_to_chat_id (int):
migrate_from_chat_id (int):
channel_chat_created (bool):
Args:
message_id (int):
@@ -84,6 +88,10 @@ class Message(TelegramObject):
new_chat_photo (Optional[List[:class:`telegram.PhotoSize`]):
delete_chat_photo (Optional[bool]):
group_chat_created (Optional[bool]):
supergroup_chat_created (Optional[bool]):
migrate_to_chat_id (Optional[int]):
migrate_from_chat_id (Optional[int]):
channel_chat_created (Optional[bool]):
"""
def __init__(self,
+1 -2
View File
@@ -12,8 +12,7 @@ except ImportError:
from urllib import quote
from urllib2 import URLError, HTTPError
H = NullHandler()
logging.getLogger(__name__).addHandler(H)
logging.getLogger(__name__).addHandler(NullHandler())
class Botan(object):
+1 -1
View File
@@ -98,7 +98,7 @@ def _try_except_req(func):
except (SSLError, socket.timeout) as error:
err_s = str(error)
if "operation timed out" in err_s:
if 'operation timed out' in err_s:
raise TimedOut()
raise NetworkError(err_s)
+1 -1
View File
@@ -35,4 +35,4 @@ def validate_string(arg, name):
name (str): The name of the argument, for the error message
"""
if not isinstance(arg, basestring) and arg is not None:
raise ValueError(name + " is not a string")
raise ValueError(name + ' is not a string')
+9 -10
View File
@@ -10,8 +10,7 @@ except ImportError:
import http.server as BaseHTTPServer
H = NullHandler()
logging.getLogger(__name__).addHandler(H)
logging.getLogger(__name__).addHandler(NullHandler())
class _InvalidPost(Exception):
@@ -36,14 +35,14 @@ class WebhookServer(BaseHTTPServer.HTTPServer, object):
def serve_forever(self, poll_interval=0.5):
with self.server_lock:
self.is_running = True
self.logger.info("Webhook Server started.")
self.logger.debug('Webhook Server started.')
super(WebhookServer, self).serve_forever(poll_interval)
self.logger.info("Webhook Server stopped.")
self.logger.debug('Webhook Server stopped.')
def shutdown(self):
with self.shutdown_lock:
if not self.is_running:
self.logger.warn("Webhook Server already stopped.")
self.logger.warn('Webhook Server already stopped.')
return
else:
super(WebhookServer, self).shutdown()
@@ -54,7 +53,7 @@ class WebhookServer(BaseHTTPServer.HTTPServer, object):
# Based on: https://github.com/eternnoir/pyTelegramBotAPI/blob/master/
# examples/webhook_examples/webhook_cpython_echo_bot.py
class WebhookHandler(BaseHTTPServer.BaseHTTPRequestHandler, object):
server_version = "WebhookHandler/1.0"
server_version = 'WebhookHandler/1.0'
def __init__(self, request, client_address, server):
self.logger = logging.getLogger(__name__)
@@ -69,7 +68,7 @@ class WebhookHandler(BaseHTTPServer.BaseHTTPRequestHandler, object):
self.end_headers()
def do_POST(self):
self.logger.debug("Webhook triggered")
self.logger.debug('Webhook triggered')
try:
self._validate_post()
clen = self._get_content_len()
@@ -83,11 +82,11 @@ class WebhookHandler(BaseHTTPServer.BaseHTTPRequestHandler, object):
self.send_response(200)
self.end_headers()
self.logger.debug("Webhook received data: " + json_string)
self.logger.debug('Webhook received data: ' + json_string)
update = Update.de_json(json.loads(json_string))
self.logger.info("Received Update with ID %d on Webhook" %
update.update_id)
self.logger.debug('Received Update with ID %d on Webhook' %
update.update_id)
self.server.update_queue.put(update)
def _validate_post(self):
+23
View File
@@ -61,6 +61,17 @@ class BotTest(BaseTest, unittest.TestCase):
self.assertEqual(message.text, u'Моё судно на воздушной подушке полно угрей')
self.assertTrue(isinstance(message.date, datetime))
@flaky(3, 1)
@timeout(10)
def testSilentSendMessage(self):
message = self._bot.sendMessage(chat_id=self._chat_id,
text='Моё судно на воздушной подушке полно угрей',
disable_notification=True)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.text, u'Моё судно на воздушной подушке полно угрей')
self.assertTrue(isinstance(message.date, datetime))
@flaky(3, 1)
@timeout(10)
def testGetUpdates(self):
@@ -93,6 +104,18 @@ class BotTest(BaseTest, unittest.TestCase):
self.assertEqual(message.photo[0].file_size, 1451)
self.assertEqual(message.caption, 'testSendPhoto')
@flaky(3, 1)
@timeout(10)
def testSilentSendPhoto(self):
message = self._bot.sendPhoto(photo=open('tests/data/telegram.png', 'rb'),
caption='testSendPhoto',
chat_id=self._chat_id,
disable_notification=True)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.photo[0].file_size, 1451)
self.assertEqual(message.caption, 'testSendPhoto')
@flaky(3, 1)
@timeout(10)
def testResendPhoto(self):
+1 -1
View File
@@ -37,7 +37,7 @@ except ImportError:
sys.path.append('.')
from telegram import JobQueue, Updater
from telegram.ext import JobQueue, Updater
from tests.base import BaseTest
# Enable logging
+58 -7
View File
@@ -47,8 +47,10 @@ except ImportError:
sys.path.append('.')
from telegram import Update, Message, TelegramError, User, Chat, Updater, Bot
from telegram.dispatcher import run_async
from telegram import Update, Message, TelegramError, User, Chat, Bot
from telegram.ext import Updater
from telegram.ext.dispatcher import run_async
from telegram.error import Unauthorized, InvalidToken
from tests.base import BaseTest
from threading import Lock, Thread
@@ -301,6 +303,15 @@ class UpdaterTest(BaseTest, unittest.TestCase):
sleep(.1)
self.assertEqual(self.received_message, 'Test Error 1')
def test_cleanBeforeStart(self):
self._setup_updater('')
d = self.updater.dispatcher
d.addTelegramMessageHandler(self.telegramHandlerTest)
self.updater.start_polling(0.01, clean=True)
sleep(.1)
self.assertEqual(self.message_count, 0)
self.assertIsNone(self.received_message)
def test_errorOnGetUpdates(self):
self._setup_updater('', raise_error=True)
d = self.updater.dispatcher
@@ -396,7 +407,6 @@ class UpdaterTest(BaseTest, unittest.TestCase):
self.assertEqual(self.received_message, (('This', 'regex group'),
{'testgroup': 'regex group'}))
def test_runAsyncWithAdditionalArgs(self):
self._setup_updater('Test6', messages=2)
d = self.updater.dispatcher
@@ -475,13 +485,46 @@ class UpdaterTest(BaseTest, unittest.TestCase):
sleep(1)
self.assertEqual(self.received_message, 'Webhook Test 2')
def test_bootstrap_retries_success(self):
retries = 3
self._setup_updater('', messages=0, bootstrap_retries=retries)
self.updater._set_webhook('path', retries)
self.assertEqual(self.updater.bot.bootstrap_attempts, retries)
def test_bootstrap_retries_unauth(self):
retries = 3
self._setup_updater('', messages=0, bootstrap_retries=retries,
bootstrap_err=Unauthorized())
self.assertRaises(Unauthorized, self.updater._set_webhook, 'path',
retries)
self.assertEqual(self.updater.bot.bootstrap_attempts, 1)
def test_bootstrap_retries_invalid_token(self):
retries = 3
self._setup_updater('', messages=0, bootstrap_retries=retries,
bootstrap_err=InvalidToken())
self.assertRaises(InvalidToken, self.updater._set_webhook, 'path',
retries)
self.assertEqual(self.updater.bot.bootstrap_attempts, 1)
def test_bootstrap_retries_fail(self):
retries = 1
self._setup_updater('', messages=0, bootstrap_retries=retries)
self.assertRaisesRegexp(TelegramError, 'test',
self.updater._set_webhook, 'path', retries - 1)
self.assertEqual(self.updater.bot.bootstrap_attempts, 1)
def test_webhook_invalid_posts(self):
self._setup_updater('', messages=0)
ip = '127.0.0.1'
port = randrange(1024, 49152) # select random port for travis
thr = Thread(target=self.updater._start_webhook,
args=(ip, port, '', None, None))
args=(ip, port, '', None, None, 0, None))
thr.start()
sleep(0.5)
@@ -570,12 +613,15 @@ class UpdaterTest(BaseTest, unittest.TestCase):
class MockBot:
def __init__(self, text, messages=1, raise_error=False):
def __init__(self, text, messages=1, raise_error=False,
bootstrap_retries=None, bootstrap_err=TelegramError('test')):
self.text = text
self.send_messages = messages
self.raise_error = raise_error
self.token = "TOKEN"
pass
self.bootstrap_retries = bootstrap_retries
self.bootstrap_attempts = 0
self.bootstrap_err = bootstrap_err
@staticmethod
def mockUpdate(text):
@@ -586,7 +632,12 @@ class MockBot:
return update
def setWebhook(self, webhook_url=None, certificate=None):
pass
if self.bootstrap_retries is None:
return
if self.bootstrap_attempts < self.bootstrap_retries:
self.bootstrap_attempts += 1
raise self.bootstrap_err
def getUpdates(self,
offset=None,