Compare commits

...

62 Commits

Author SHA1 Message Date
leandrotoledo ac55ba007e Bumping version to v4.1 [ci skip] 2016-05-15 20:05:38 -03:00
Leandro Toledo 6bdca1e4f8 Merge pull request #294 from python-telegram-bot/yapf
yapf formatter, pre-commit hooks and new travis tests
2016-05-15 19:31:54 -03:00
Leandro Toledo fd7b571e92 Disables yapf for travis tests for pypy3 (ugh!) #259 2016-05-15 00:19:59 -03:00
Leandro Toledo 9243cd3507 Minor typo fix #259 [ci skip] 2016-05-15 00:11:14 -03:00
Leandro Toledo 1b09738191 Disables yapf for travis tests for pypy3.3 #259 2016-05-15 00:07:59 -03:00
Leandro Toledo 836c50965a Disables yapf for travis tests for py2.4 and 3.3 #259 2016-05-14 23:59:45 -03:00
Leandro Toledo 1d9d0fc764 Disables yapf for travis tests for py2.4 and 3.3 #259 2016-05-14 23:53:02 -03:00
Leandro Toledo e57e6dd645 Add yapf to travis and updates CONTRIBUTING doc #259 2016-05-14 23:46:21 -03:00
Leandro Toledo 49122d6a99 Remove \ from base code #259 2016-05-14 23:26:56 -03:00
Leandro Toledo d445d35ceb Running yapf for codebase #259 2016-05-14 22:46:40 -03:00
Leandro Toledo 703b8d1301 Adding more pre-commit hooks and testing InputFile #259 2016-05-14 22:24:35 -03:00
Leandro Toledo 46993d5f2d Adding pre-commit hooks #259 2016-05-14 21:59:08 -03:00
Leandro Toledo 880746baed Column width to 90 #259 2016-05-14 21:52:35 -03:00
Leandro Toledo 8ad1f330ea First run of yapf for tests/* #259 2016-05-14 21:39:11 -03:00
Leandro Toledo 56b1d4f5ce Merge pull request #265 from python-telegram-bot/snakes
rename methods to snake_case
2016-05-14 10:56:24 -03:00
Leandro Toledo df16846d80 Update README.rst
#293
2016-05-14 10:28:16 -03:00
Jannes Höke 3ac9a1cd71 Merge pull request #291 from python-telegram-bot/api-2016-05-06
Api additions 2016-05-06
2016-05-13 21:31:35 +02:00
Jannes Höke 4d8174edc3 check for py2 and decode 2016-05-12 08:31:47 +02:00
Jannes Höke 235bb72702 use future.utils.text_to_native_str 2016-05-12 08:17:05 +02:00
Rahiel Kasim 9a5ccb1c2d fix sticker emoji test
We always get utf-8 text from Telegram, so we compare to that.
2016-05-12 01:10:04 +02:00
Jannes Höke a18640a8d3 remove str conversion for emoji 2016-05-12 00:16:29 +02:00
Jannes Höke fdc3ac0cc5 copy paste mishap 2016-05-11 23:37:46 +02:00
Jannes Höke d881fa6a5f add forward_from_chat field to message 2016-05-11 23:22:05 +02:00
Jannes Höke c551d71735 add emoji field to sticker 2016-05-11 23:19:35 +02:00
Jannes Höke bee3d881d1 location docstring fix 2016-05-11 23:07:32 +02:00
Jannes Höke 6ec81dd552 move cleaning updates to bootstrapping phase (#282) 2016-05-11 00:58:55 +03:00
Rahiel Kasim 6e9f30ca6e fix merge conflict snakes 2016-05-02 16:37:45 +02:00
Jannes Höke 252cafb04c fix docs for inlinekeyboardmarkup #277 2016-05-02 14:19:06 +02:00
Jannes Höke d4f7b7165c flake8: set max line length to 99 2016-05-02 10:35:20 +02:00
Leandro Toledo 0ed5e8e1a3 Merge pull request #274 from kiote/master
Update README.rst
2016-05-01 18:29:55 -03:00
Ekaterina Krivich 335813a11a Update README.rst
answerCallbackQuery mentioned twice
2016-05-02 00:28:09 +03:00
Jannes Höke 10a98211f8 bump version to 4.0.3 2016-05-01 22:13:27 +02:00
Jannes Höke b5570ddfa5 update CHANGES.rst 2016-05-01 22:13:24 +02:00
Rahiel Kasim 99c9544a27 Merge pull request #273 from python-telegram-bot/inlinequery_location
Add location attribute #272
2016-05-01 21:27:19 +02:00
Jannes Höke 0e0611767a add location attribute #272 2016-05-01 20:08:34 +02:00
Rahiel Kasim 4ba5fbf4f5 Merge pull request #271 from Djaler/master
Fix Bot.getUpdates() docstring
2016-05-01 14:31:23 +02:00
Djaler 4b0be65a76 Fix Bot.getUpdates() docstring 2016-05-01 15:25:07 +03:00
Rahiel Kasim 5971cb35f8 fix merge conflict snakes with master 2016-04-30 14:56:48 +02:00
Jannes Höke 6e5302c089 pass args in timerbot example #270 2016-04-30 10:19:41 +02:00
Jannes Höke 5aab4525c2 add keyboard changes 2016-04-29 19:31:45 +02:00
Jannes Höke 62c651d167 bump version to 4.0.2 2016-04-29 19:31:45 +02:00
Jannes Höke 41e457f5ed Merge pull request #269 from python-telegram-bot/buttons
support str and KeyboardButton for reply_markup
2016-04-29 19:29:12 +02:00
Jannes Höke 4ce0ab53d0 Merge branch 'tsnoam-cr4.0_api2' 2016-04-29 18:57:23 +02:00
Jannes Höke d940afa718 merge master into cr4.0 2016-04-29 18:37:41 +02:00
Rahiel Kasim a327e9d6ff support str and KeyboardButton for reply_markup 2016-04-29 17:42:45 +02:00
Rahiel Kasim 5e5510d42b add deprecation warning for Python 2.6 users 2016-04-28 17:56:03 +02:00
Rahiel Kasim 73c60ee817 simpler py2/py3 input in clibot example 2016-04-28 17:32:10 +02:00
Rahiel Kasim 474d5f0c9f update README and examples to new snake_case methods 2016-04-28 17:29:34 +02:00
Rahiel Kasim 74a2baf03d update CONTRIBUTING guide 2016-04-28 15:41:44 +02:00
Rahiel Kasim 0612385233 set project max line length to 99 (flake8) 2016-04-28 15:03:43 +02:00
Rahiel Kasim c0489db17c fix import of deprecate function 2016-04-28 14:47:56 +02:00
Rahiel Kasim 592352c849 dispatcher/*handler methods to snake_case + deprecation warnings 2016-04-28 14:29:27 +02:00
Rahiel Kasim 9d367e9f2c telegram.utils.deprecate: helper module to facilitate deprecation 2016-04-28 14:24:12 +02:00
Rahiel Kasim d86ca30601 README: document snake_case alias methods for telegram.Bot 2016-04-28 12:44:35 +02:00
Rahiel Kasim ec15e866be add snake_case aliases for CamelCase methods 2016-04-28 12:20:42 +02:00
Leandro Toledo 998040da92 Update clibot.py
Changes group=str to int
2016-04-27 22:10:31 -03:00
Noam Meltzer 0c74b3cfb9 bot.py + request.py: network_delay is unique for getUpdates 2016-04-28 01:41:32 +03:00
Noam Meltzer 0ca3ef7a38 utils.request: clean imports using feature.moves 2016-04-28 01:41:32 +03:00
Noam Meltzer e160355190 remove unused imports, use future for urllib imports 2016-04-28 01:41:32 +03:00
Leandro Toledo d80e0b4b8c Add more test cases 2016-04-26 22:48:02 -03:00
Leandro Toledo 96d98084c7 Update README.rst
Drops 4.0rc version argument
2016-04-26 20:39:34 -03:00
Noam Meltzer a686db2c6f bot.Bot: fix class docstring 2016-04-26 23:31:29 +03:00
145 changed files with 1120 additions and 1493 deletions
+17
View File
@@ -0,0 +1,17 @@
- repo: git://github.com/pre-commit/mirrors-yapf
sha: 'v0.7.1'
hooks:
- id: yapf
args: ['-i']
- repo: git://github.com/pre-commit/pre-commit-hooks
sha: 'v0.5.0'
hooks:
- id: flake8
args: ['telegram']
- repo: git://github.com/pre-commit/mirrors-pylint
sha: 'v1.5.5'
hooks:
- id: pylint
args: ['--errors-only', '--disable=no-name-in-module,import-error', 'telegram']
+2 -1
View File
@@ -13,7 +13,8 @@ install:
- pip install -r requirements-dev.txt
script:
- nosetests -v --with-flaky --no-flaky-report --with-coverage --cover-package=telegram/
- 'if [ $TRAVIS_PYTHON_VERSION != 2.6 ] && [ $TRAVIS_PYTHON_VERSION != 3.3 ] && [ $TRAVIS_PYTHON_VERSION != pypy3 ]; then yapf -r telegram; fi'
- flake8 telegram
- 'if [[ $TRAVIS_PYTHON_VERSION != 2.6 ]]; then pylint -E telegram --disable=no-name-in-module,import-error; fi'
- 'if [ $TRAVIS_PYTHON_VERSION != 2.6 ]; then pylint -E telegram --disable=no-name-in-module,import-error; fi'
after_success:
coveralls
+13
View File
@@ -1,3 +1,16 @@
**2016-05-01**
*Released 4.0.3*
- Add missing attribute ``location`` to ``InlineQuery``
**2016-04-29**
*Released 4.0.2*
- Bugfixes
- ``KeyboardReplyMarkup`` now accepts ``str`` again
**2016-04-27**
*Released 4.0.1*
+15 -7
View File
@@ -20,9 +20,12 @@ Setting things up
4. Install dependencies:
``$ pip install -r requirements.txt``
``$ pip install -r requirements.txt -r requirements-dev.txt``
``$ pip install -r requirements-dev.txt``
5. Install pre-commit hooks:
``$ pre-commit install``
Finding something to do
-----------------------
@@ -56,6 +59,8 @@ Here's how to make a one-off code change.
- You can refer to relevant issues in the commit message by writing, e.g., "#105".
- Your code should adhere to the `PEP 8 Style Guide`_, with the exception that we have a maximum line length of 99.
- For consistency, please conform to `Google Python Style Guide`_ and `Google Python Style Docstrings`_. In addition, code should be formatted consistently with other code around it.
- The following exceptions to the above (Google's) style guides applies:
@@ -68,18 +73,20 @@ Here's how to make a one-off code change.
- Add yourself to the AUTHORS.rst_ file in an alphabetical fashion.
- Before making a commit ensure that all automated tests, pep8 & lint validations still pass:
- Before making a commit ensure that all automated tests still pass:
``$ make test``
``$ make pep8``
- To actually make the commit (this will trigger tests for yapf, lint and pep8 automatically):
``$ make lint``
``$ git add your-file-changed.py``
- To actually make the commit and push it to your GitHub fork, run:
- yapf may change code formatting, make sure to re-add them to your commit.
``$ git commit -a -m "your-commit-message-here"``
- Finally, push it to your GitHub fork, run:
``$ git push origin your-branch-name``
4. **When your feature is ready to merge, create a pull request.**
@@ -116,7 +123,7 @@ Here's how to make a one-off code change.
- At the end, the reviewer will merge the pull request.
6. **Tidy up!** Delete the feature branch from your both your local clone and the GitHub repository:
6. **Tidy up!** Delete the feature branch from both your local clone and the GitHub repository:
``$ git branch -D your-branch-name``
@@ -127,6 +134,7 @@ Here's how to make a one-off code change.
.. _`Code of Conduct`: https://www.python.org/psf/codeofconduct/
.. _`issue tracker`: https://github.com/python-telegram-bot/python-telegram-bot/issues
.. _`developers' mailing list`: mailto:devs@python-telegram-bot.org
.. _`PEP 8 Style Guide`: https://www.python.org/dev/peps/pep-0008/
.. _`Google Python Style Guide`: https://google-styleguide.googlecode.com/svn/trunk/pyguide.html
.. _`Google Python Style Docstrings`: http://sphinx-doc.org/latest/ext/example_google.html
.. _AUTHORS.rst: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/AUTHORS.rst
+9 -3
View File
@@ -1,10 +1,11 @@
.DEFAULT_GOAL := help
.PHONY: clean pep8 lint test install
.PHONY: clean pep257 pep8 yapf lint test install
PYLINT := pylint
NOSETESTS := nosetests
PEP257 := pep257
PEP8 := flake8
YAPF := yapf
PIP := pip
clean:
@@ -19,7 +20,10 @@ pep257:
$(PEP257) telegram
pep8:
$(PEP8) telegram
$(PEP8) telegram
yapf:
$(YAPF) -r telegram
lint:
$(PYLINT) -E telegram --disable=no-name-in-module,import-error
@@ -28,7 +32,7 @@ test:
$(NOSETESTS) -v
install:
$(PIP) install -r requirements.txt
$(PIP) install -r requirements.txt -r requirements-dev.txt
help:
@echo "Available targets:"
@@ -36,6 +40,7 @@ help:
@echo "- pep257 Check docstring style with pep257"
@echo "- pep8 Check style with flake8"
@echo "- lint Check style with pylint"
@echo "- yapf Check style with yapf"
@echo "- test Run tests"
@echo
@echo "Available variables:"
@@ -43,4 +48,5 @@ help:
@echo "- NOSETESTS default: $(NOSETESTS)"
@echo "- PEP257 default: $(PEP257)"
@echo "- PEP8 default: $(PEP8)"
@echo "- YAPF default: $(YAPF)"
@echo "- PIP default: $(PIP)"
+7 -12
View File
@@ -15,10 +15,6 @@ Not **just** a Python wrapper around the Telegram Bot API
:target: https://pypi.python.org/pypi/python-telegram-bot
:alt: Supported python versions
.. image:: https://img.shields.io/pypi/dm/python-telegram-bot.svg
:target: https://pypi.python.org/pypi/python-telegram-bot
:alt: PyPi Package Monthly Download
.. image:: https://readthedocs.org/projects/python-telegram-bot/badge/?version=latest
:target: https://readthedocs.org/projects/python-telegram-bot/?badge=latest
:alt: Documentation Status
@@ -110,7 +106,6 @@ answerCallbackQuery Yes
editMessageText Yes
editMessageCaption Yes
editMessageReplyMarkup Yes
answerCallbackQuery Yes
========================= ============
=============
@@ -121,7 +116,7 @@ You can install or upgrade python-telegram-bot with:
.. code:: shell
$ pip install python-telegram-bot==4.0rc1 --upgrade
$ pip install python-telegram-bot --upgrade
===================
_`Getting the code`
@@ -202,7 +197,7 @@ _`API`
Note: Using the ``Bot`` class directly is the 'old' method, we have an easier way to make bots described in the next section. All of this is however still important information, even if you're using the ``telegram.ext`` submodule!
The API is exposed via the ``telegram.Bot`` class.
The API is exposed via the ``telegram.Bot`` class. The methods have names as described in the official `Telegram Bot API <https://core.telegram.org/bots/api>`_, but equivalent snake_case methods are available for `PEP8 <https://www.python.org/dev/peps/pep-0008/>`_ enthusiasts. So for example `telegram.Bot.send_message` is the same as `telegram.Bot.sendMessage`.
To generate an Access Token you have to talk to `BotFather <https://telegram.me/botfather>`_ and follow a few simple steps (described `here <https://core.telegram.org/bots#6-botfather>`_).
@@ -356,7 +351,7 @@ We want this function to be called on a Telegram message that contains the ``/st
>>> from telegram.ext import CommandHandler
>>> start_handler = CommandHandler('start', start)
>>> dispatcher.addHandler(start_handler)
>>> dispatcher.add_handler(start_handler)
The last step is to tell the ``Updater`` to start working:
@@ -373,7 +368,7 @@ Our bot is now up and running (go ahead and try it)! It's not doing anything yet
...
>>> from telegram.ext import MessageHandler, Filters
>>> echo_handler = MessageHandler([Filters.text], echo)
>>> dispatcher.addHandler(echo_handler)
>>> dispatcher.add_handler(echo_handler)
Our bot should now reply to all text messages that are not a command with a message that has the same content.
@@ -386,7 +381,7 @@ Let's add some functionality to our bot. We want to add the ``/caps`` command, t
... bot.sendMessage(chat_id=update.message.chat_id, text=text_caps)
...
>>> caps_handler = CommandHandler('caps', caps, pass_args=True)
>>> dispatcher.addHandler(caps_handler)
>>> dispatcher.add_handler(caps_handler)
To enable our bot to respond to inline queries, we can add the following (you will also have to talk to BotFather):
@@ -401,7 +396,7 @@ To enable our bot to respond to inline queries, we can add the following (you wi
...
>>> from telegram.ext import InlineQueryHandler
>>> inline_caps_handler = InlineQueryHandler(inline_caps)
>>> dispatcher.addHandler(inline_caps_handler)
>>> dispatcher.add_handler(inline_caps_handler)
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:
@@ -412,7 +407,7 @@ People might try to send commands to the bot that it doesn't understand, so we c
...
>>> from telegram.ext import RegexHandler
>>> unknown_handler = RegexHandler(r'/.*', unknown)
>>> dispatcher.addHandler(unknown_handler)
>>> dispatcher.add_handler(unknown_handler)
If you're done playing around, stop the bot with this:
+2 -2
View File
@@ -58,9 +58,9 @@ author = u'Leandro Toledo'
# built documents.
#
# The short X.Y version.
version = '4.0'
version = '4.1'
# The full version, including alpha/beta/rc tags.
release = '4.0.1'
release = '4.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
+11 -12
View File
@@ -24,6 +24,8 @@ from telegram.ext.dispatcher import run_async
from time import sleep
import logging
from future.builtins import input
# Enable Logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
@@ -112,24 +114,24 @@ def main():
dp = updater.dispatcher
# This is how we add handlers for Telegram messages
dp.addHandler(CommandHandler("start", start))
dp.addHandler(CommandHandler("help", help))
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", help))
# Message handlers only receive updates that don't contain commands
dp.addHandler(MessageHandler([Filters.text], message))
dp.add_handler(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')
dp.add_handler(RegexHandler('.*', any_message), group=1)
# 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))
dp.add_handler(StringCommandHandler('reply', cli_reply, pass_args=True))
dp.add_handler(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)
dp.add_error_handler(error)
# Start the Bot and store the update Queue, so we can insert updates
update_queue = updater.start_polling(timeout=10)
@@ -153,10 +155,7 @@ def main():
# Start CLI-Loop
while True:
try:
text = raw_input()
except NameError:
text = input()
text = input()
# Gracefully stop the event handler
if text == 'stop':
+4 -4
View File
@@ -54,14 +54,14 @@ def main():
dp = updater.dispatcher
# on different commands - answer in Telegram
dp.addHandler(CommandHandler("start", start))
dp.addHandler(CommandHandler("help", help))
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", help))
# on noncommand i.e message - echo the message on Telegram
dp.addHandler(MessageHandler([Filters.text], echo))
dp.add_handler(MessageHandler([Filters.text], echo))
# log all errors
dp.addErrorHandler(error)
dp.add_error_handler(error)
# Start the Bot
updater.start_polling()
+4 -4
View File
@@ -87,14 +87,14 @@ def main():
dp = updater.dispatcher
# on different commands - answer in Telegram
dp.addHandler(CommandHandler("start", start))
dp.addHandler(CommandHandler("help", help))
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", help))
# on noncommand i.e message - echo the message on Telegram
dp.addHandler(InlineQueryHandler(inlinequery))
dp.add_handler(InlineQueryHandler(inlinequery))
# log all errors
dp.addErrorHandler(error)
dp.add_error_handler(error)
# Start the Bot
updater.start_polling()
+5 -5
View File
@@ -105,12 +105,12 @@ updater = Updater("TOKEN")
# The command
updater.dispatcher.addHandler(CommandHandler('set', set_value))
# The answer
updater.dispatcher.addHandler(MessageHandler([Filters.text], entered_value))
updater.dispatcher.add_handler(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)
updater.dispatcher.add_handler(CallbackQueryHandler(confirm_value))
updater.dispatcher.add_handler(CommandHandler('start', help))
updater.dispatcher.add_handler(CommandHandler('help', help))
updater.dispatcher.add_error_handler(error)
# Start the Bot
updater.start_polling()
+5 -5
View File
@@ -91,12 +91,12 @@ def help(bot, update):
updater = Updater("TOKEN")
# The command
updater.dispatcher.addHandler(CommandHandler('set', set_value))
updater.dispatcher.add_handler(CommandHandler('set', set_value))
# The answer and confirmation
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))
updater.dispatcher.add_handler(MessageHandler([Filters.text], set_value))
updater.dispatcher.add_handler(CommandHandler('cancel', cancel))
updater.dispatcher.add_handler(CommandHandler('start', help))
updater.dispatcher.add_handler(CommandHandler('help', help))
# Start the Bot
updater.start_polling()
+4 -4
View File
@@ -73,12 +73,12 @@ def main():
dp = updater.dispatcher
# on different commands - answer in Telegram
dp.addHandler(CommandHandler("start", start))
dp.addHandler(CommandHandler("help", start))
dp.addHandler(CommandHandler("set", set))
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", start))
dp.add_handler(CommandHandler("set", set, pass_args=True))
# log all errors
dp.addErrorHandler(error)
dp.add_error_handler(error)
# Start the Bot
updater.start_polling()
+4 -1
View File
@@ -3,4 +3,7 @@ nose
pep257
pylint
unittest2
flaky
flaky
yapf
pre-commit
pre-commit-hooks
+7
View File
@@ -8,3 +8,10 @@ all_files = 1
[upload_sphinx]
upload-dir = docs/build/html
[flake8]
max-line-length = 99
[yapf]
based_on_style = google
column_limit = 99
+29 -32
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env python
'''The setup and build script for the python-telegram-bot library.'''
import os
@@ -24,34 +23,32 @@ def requirements():
return requirements_list
setup(
name='python-telegram-bot',
version='4.0.1',
author='Leandro Toledo',
author_email='devs@python-telegram-bot.org',
license='LGPLv3',
url='https://github.com/python-telegram-bot/python-telegram-bot',
keywords='python telegram bot api wrapper',
description='A Python wrapper around the Telegram Bot API',
long_description=(read('README.rst')),
packages=find_packages(exclude=['tests*']),
install_requires=requirements(),
include_package_data=True,
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)',
'Operating System :: OS Independent',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Communications :: Chat',
'Topic :: Internet',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
],
)
setup(name='python-telegram-bot',
version='4.1',
author='Leandro Toledo',
author_email='devs@python-telegram-bot.org',
license='LGPLv3',
url='https://github.com/python-telegram-bot/python-telegram-bot',
keywords='python telegram bot api wrapper',
description='A Python wrapper around the Telegram Bot API',
long_description=(read('README.rst')),
packages=find_packages(exclude=['tests*']),
install_requires=requirements(),
include_package_data=True,
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)',
'Operating System :: OS Independent',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Communications :: Chat',
'Topic :: Internet',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
],)
+24 -63
View File
@@ -16,9 +16,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/].
"""A library that provides a Python interface to the Telegram Bot API"""
from sys import version_info
from .base import TelegramObject
from .user import User
from .chat import Chat
@@ -79,66 +80,26 @@ from .inputcontactmessagecontent import InputContactMessageContent
from .update import Update
from .bot import Bot
__author__ = 'devs@python-telegram-bot.org'
__version__ = '4.0.1'
__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']
__version__ = '4.1'
__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']
if version_info < (2, 7):
from warnings import warn
warn("python-telegram-bot will stop supporting Python 2.6 in a future release. "
"Please upgrade your Python!")
+1 -5
View File
@@ -16,7 +16,6 @@
#
# 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 Audio."""
from telegram import TelegramObject
@@ -45,10 +44,7 @@ class Audio(TelegramObject):
file_size (Optional[int]):
"""
def __init__(self,
file_id,
duration,
**kwargs):
def __init__(self, file_id, duration, **kwargs):
# Required
self.file_id = str(file_id)
self.duration = int(duration)
-1
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""Base class for Telegram Objects."""
import json
+114 -277
View File
@@ -17,14 +17,13 @@
#
# 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 Bot."""
import logging
import functools
from telegram import User, Message, Update, UserProfilePhotos, File, \
ReplyMarkup, TelegramObject, NullHandler
from telegram import (User, Message, Update, UserProfilePhotos, File, ReplyMarkup, TelegramObject,
NullHandler)
from telegram.utils import request
from telegram.utils.validate import validate_token
@@ -43,28 +42,21 @@ class Bot(TelegramObject):
Args:
token (str): Bot's unique authentication.
**kwargs: Arbitrary keyword arguments.
Keyword Args:
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,
base_file_url=None):
def __init__(self, token, base_url=None, base_file_url=None):
self.token = validate_token(token)
if not base_url:
self.base_url = 'https://api.telegram.org/bot{0}'.format(
self.token)
self.base_url = 'https://api.telegram.org/bot{0}'.format(self.token)
else:
self.base_url = base_url + self.token
if not base_file_url:
self.base_file_url = 'https://api.telegram.org/file/bot{0}'.format(
self.token)
self.base_file_url = 'https://api.telegram.org/file/bot{0}'.format(self.token)
else:
self.base_file_url = base_file_url + self.token
@@ -73,6 +65,7 @@ class Bot(TelegramObject):
self.logger = logging.getLogger(__name__)
def info(func):
@functools.wraps(func)
def decorator(self, *args, **kwargs):
if not self.bot:
@@ -121,17 +114,16 @@ class Bot(TelegramObject):
return decorator
def message(func):
@functools.wraps(func)
def decorator(self, *args, **kwargs):
url, data = func(self, *args, **kwargs)
if kwargs.get('reply_to_message_id'):
data['reply_to_message_id'] = \
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')
data['disable_notification'] = kwargs.get('disable_notification')
if kwargs.get('reply_markup'):
reply_markup = kwargs.get('reply_markup')
@@ -140,9 +132,7 @@ class Bot(TelegramObject):
else:
data['reply_markup'] = reply_markup
result = request.post(url, data,
timeout=kwargs.get('timeout'),
network_delay=kwargs.get('network_delay'))
result = request.post(url, data, timeout=kwargs.get('timeout'))
if result is True:
return result
@@ -175,12 +165,7 @@ class Bot(TelegramObject):
@log
@message
def sendMessage(self,
chat_id,
text,
parse_mode=None,
disable_web_page_preview=None,
**kwargs):
def sendMessage(self, chat_id, text, parse_mode=None, disable_web_page_preview=None, **kwargs):
"""Use this method to send text messages.
Args:
@@ -208,10 +193,6 @@ class Bot(TelegramObject):
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, the sent message is
@@ -224,8 +205,7 @@ class Bot(TelegramObject):
url = '{0}/sendMessage'.format(self.base_url)
data = {'chat_id': chat_id,
'text': text}
data = {'chat_id': chat_id, 'text': text}
if parse_mode:
data['parse_mode'] = parse_mode
@@ -236,11 +216,7 @@ class Bot(TelegramObject):
@log
@message
def forwardMessage(self,
chat_id,
from_chat_id,
message_id,
**kwargs):
def forwardMessage(self, chat_id, from_chat_id, message_id, **kwargs):
"""Use this method to forward messages of any kind.
Args:
@@ -258,10 +234,6 @@ class Bot(TelegramObject):
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:
:class:`telegram.Message`: On success, instance representing the
@@ -287,11 +259,7 @@ class Bot(TelegramObject):
@log
@message
def sendPhoto(self,
chat_id,
photo,
caption=None,
**kwargs):
def sendPhoto(self, chat_id, photo, caption=None, **kwargs):
"""Use this method to send photos.
Args:
@@ -317,10 +285,6 @@ class Bot(TelegramObject):
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
@@ -333,8 +297,7 @@ class Bot(TelegramObject):
url = '{0}/sendPhoto'.format(self.base_url)
data = {'chat_id': chat_id,
'photo': photo}
data = {'chat_id': chat_id, 'photo': photo}
if caption:
data['caption'] = caption
@@ -343,13 +306,7 @@ class Bot(TelegramObject):
@log
@message
def sendAudio(self,
chat_id,
audio,
duration=None,
performer=None,
title=None,
**kwargs):
def sendAudio(self, chat_id, audio, duration=None, performer=None, title=None, **kwargs):
"""Use this method to send audio files, if you want Telegram clients to
display them in the music player. Your audio must be in an .mp3 format.
On success, the sent Message is returned. Bots can currently send audio
@@ -387,10 +344,6 @@ class Bot(TelegramObject):
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
@@ -403,8 +356,7 @@ class Bot(TelegramObject):
url = '{0}/sendAudio'.format(self.base_url)
data = {'chat_id': chat_id,
'audio': audio}
data = {'chat_id': chat_id, 'audio': audio}
if duration:
data['duration'] = duration
@@ -417,12 +369,7 @@ class Bot(TelegramObject):
@log
@message
def sendDocument(self,
chat_id,
document,
filename=None,
caption=None,
**kwargs):
def sendDocument(self, chat_id, document, filename=None, caption=None, **kwargs):
"""Use this method to send general files.
Args:
@@ -451,10 +398,6 @@ class Bot(TelegramObject):
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
@@ -467,8 +410,7 @@ class Bot(TelegramObject):
url = '{0}/sendDocument'.format(self.base_url)
data = {'chat_id': chat_id,
'document': document}
data = {'chat_id': chat_id, 'document': document}
if filename:
data['filename'] = filename
@@ -479,10 +421,7 @@ class Bot(TelegramObject):
@log
@message
def sendSticker(self,
chat_id,
sticker,
**kwargs):
def sendSticker(self, chat_id, sticker, **kwargs):
"""Use this method to send .webp stickers.
Args:
@@ -505,10 +444,6 @@ class Bot(TelegramObject):
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
@@ -521,19 +456,13 @@ class Bot(TelegramObject):
url = '{0}/sendSticker'.format(self.base_url)
data = {'chat_id': chat_id,
'sticker': sticker}
data = {'chat_id': chat_id, 'sticker': sticker}
return url, data
@log
@message
def sendVideo(self,
chat_id,
video,
duration=None,
caption=None,
**kwargs):
def sendVideo(self, chat_id, video, duration=None, caption=None, **kwargs):
"""Use this method to send video files, Telegram clients support mp4
videos (other formats may be sent as telegram.Document).
@@ -562,10 +491,6 @@ class Bot(TelegramObject):
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
@@ -578,8 +503,7 @@ class Bot(TelegramObject):
url = '{0}/sendVideo'.format(self.base_url)
data = {'chat_id': chat_id,
'video': video}
data = {'chat_id': chat_id, 'video': video}
if duration:
data['duration'] = duration
@@ -590,11 +514,7 @@ class Bot(TelegramObject):
@log
@message
def sendVoice(self,
chat_id,
voice,
duration=None,
**kwargs):
def sendVoice(self, chat_id, voice, duration=None, **kwargs):
"""Use this method to send audio files, if you want Telegram clients to
display the file as a playable voice message. For this to work, your
audio must be in an .ogg file encoded with OPUS (other formats may be
@@ -624,10 +544,6 @@ class Bot(TelegramObject):
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
@@ -640,8 +556,7 @@ class Bot(TelegramObject):
url = '{0}/sendVoice'.format(self.base_url)
data = {'chat_id': chat_id,
'voice': voice}
data = {'chat_id': chat_id, 'voice': voice}
if duration:
data['duration'] = duration
@@ -650,11 +565,7 @@ class Bot(TelegramObject):
@log
@message
def sendLocation(self,
chat_id,
latitude,
longitude,
**kwargs):
def sendLocation(self, chat_id, latitude, longitude, **kwargs):
"""Use this method to send point on the map.
Args:
@@ -677,10 +588,6 @@ class Bot(TelegramObject):
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
@@ -693,22 +600,19 @@ class Bot(TelegramObject):
url = '{0}/sendLocation'.format(self.base_url)
data = {'chat_id': chat_id,
'latitude': latitude,
'longitude': longitude}
data = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude}
return url, data
@log
@message
def sendVenue(self,
chat_id,
latitude,
longitude,
title,
address,
foursquare_id=None,
**kwargs):
def sendVenue(
self, chat_id,
latitude,
longitude,
title, address,
foursquare_id=None,
**kwargs):
"""
Use this method to send information about a venue.
@@ -739,10 +643,6 @@ class Bot(TelegramObject):
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
@@ -768,12 +668,7 @@ class Bot(TelegramObject):
@log
@message
def sendContact(self,
chat_id,
phone_number,
first_name,
last_name=None,
**kwargs):
def sendContact(self, chat_id, phone_number, first_name, last_name=None, **kwargs):
"""
Use this method to send phone contacts.
@@ -800,10 +695,6 @@ class Bot(TelegramObject):
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
@@ -816,9 +707,7 @@ class Bot(TelegramObject):
url = '{0}/sendContact'.format(self.base_url)
data = {'chat_id': chat_id,
'phone_number': phone_number,
'first_name': first_name}
data = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name}
if last_name:
data['last_name'] = last_name
@@ -827,10 +716,7 @@ class Bot(TelegramObject):
@log
@message
def sendChatAction(self,
chat_id,
action,
**kwargs):
def sendChatAction(self, chat_id, action, **kwargs):
"""Use this method when you need to tell the user that something is
happening on the bot's side. The status is set for 5 seconds or less
(when a message arrives from your bot, Telegram clients clear its
@@ -852,8 +738,7 @@ class Bot(TelegramObject):
url = '{0}/sendChatAction'.format(self.base_url)
data = {'chat_id': chat_id,
'action': action}
data = {'chat_id': chat_id, 'action': action}
return url, data
@@ -895,10 +780,6 @@ class Bot(TelegramObject):
Keyword Args:
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:
bool: On success, `True` is returned.
@@ -912,8 +793,7 @@ class Bot(TelegramObject):
results = [res.to_dict() for res in results]
data = {'inline_query_id': inline_query_id,
'results': results}
data = {'inline_query_id': inline_query_id, 'results': results}
if cache_time or cache_time == 0:
data['cache_time'] = cache_time
@@ -926,18 +806,12 @@ class Bot(TelegramObject):
if switch_pm_parameter:
data['switch_pm_parameter'] = switch_pm_parameter
result = request.post(url, data,
timeout=kwargs.get('timeout'),
network_delay=kwargs.get('network_delay'))
result = request.post(url, data, timeout=kwargs.get('timeout'))
return result
@log
def getUserProfilePhotos(self,
user_id,
offset=None,
limit=100,
**kwargs):
def getUserProfilePhotos(self, user_id, offset=None, limit=100, **kwargs):
"""Use this method to get a list of profile pictures for a user.
Args:
@@ -953,10 +827,6 @@ class Bot(TelegramObject):
Keyword Args:
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:
list[:class:`telegram.UserProfilePhotos`]: A list of
@@ -976,16 +846,12 @@ class Bot(TelegramObject):
if limit:
data['limit'] = limit
result = request.post(url, data,
timeout=kwargs.get('timeout'),
network_delay=kwargs.get('network_delay'))
result = request.post(url, data, timeout=kwargs.get('timeout'))
return UserProfilePhotos.de_json(result)
@log
def getFile(self,
file_id,
**kwargs):
def getFile(self, file_id, **kwargs):
"""Use this method to get basic info about a file and prepare it for
downloading. For the moment, bots can download files of up to 20MB in
size.
@@ -997,10 +863,6 @@ class Bot(TelegramObject):
Keyword Args:
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.File`: On success, a :class:`telegram.File`
@@ -1015,21 +877,15 @@ class Bot(TelegramObject):
data = {'file_id': file_id}
result = request.post(url, data,
timeout=kwargs.get('timeout'),
network_delay=kwargs.get('network_delay'))
result = request.post(url, data, timeout=kwargs.get('timeout'))
if result.get('file_path'):
result['file_path'] = '%s/%s' % (self.base_file_url,
result['file_path'])
result['file_path'] = '%s/%s' % (self.base_file_url, result['file_path'])
return File.de_json(result)
@log
def kickChatMember(self,
chat_id,
user_id,
**kwargs):
def kickChatMember(self, chat_id, user_id, **kwargs):
"""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
@@ -1045,10 +901,6 @@ class Bot(TelegramObject):
Keyword Args:
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:
bool: On success, `True` is returned.
@@ -1060,20 +912,14 @@ class Bot(TelegramObject):
url = '{0}/kickChatMember'.format(self.base_url)
data = {'chat_id': chat_id,
'user_id': user_id}
data = {'chat_id': chat_id, 'user_id': user_id}
result = request.post(url, data,
timeout=kwargs.get('timeout'),
network_delay=kwargs.get('network_delay'))
result = request.post(url, data, timeout=kwargs.get('timeout'))
return result
@log
def unbanChatMember(self,
chat_id,
user_id,
**kwargs):
def unbanChatMember(self, chat_id, user_id, **kwargs):
"""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
@@ -1089,10 +935,6 @@ class Bot(TelegramObject):
Keyword Args:
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:
bool: On success, `True` is returned.
@@ -1104,21 +946,14 @@ class Bot(TelegramObject):
url = '{0}/unbanChatMember'.format(self.base_url)
data = {'chat_id': chat_id,
'user_id': user_id}
data = {'chat_id': chat_id, 'user_id': user_id}
result = request.post(url, data,
timeout=kwargs.get('timeout'),
network_delay=kwargs.get('network_delay'))
result = request.post(url, data, timeout=kwargs.get('timeout'))
return result
@log
def answerCallbackQuery(self,
callback_query_id,
text=None,
show_alert=False,
**kwargs):
def answerCallbackQuery(self, callback_query_id, text=None, show_alert=False, **kwargs):
"""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.
@@ -1157,14 +992,11 @@ class Bot(TelegramObject):
if show_alert:
data['show_alert'] = show_alert
result = request.post(url, data,
timeout=kwargs.get('timeout'),
network_delay=kwargs.get('network_delay'))
result = request.post(url, data, timeout=kwargs.get('timeout'))
return result
@log
@message
def editMessageText(self,
text,
chat_id=None,
@@ -1172,6 +1004,7 @@ class Bot(TelegramObject):
inline_message_id=None,
parse_mode=None,
disable_web_page_preview=None,
reply_markup=None,
**kwargs):
"""Use this method to edit text messages sent by the bot or via the bot
(for inline bots).
@@ -1194,16 +1027,12 @@ class Bot(TelegramObject):
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.
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
@@ -1229,8 +1058,15 @@ class Bot(TelegramObject):
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
return url, data
result = request.post(url, data, timeout=kwargs.get('timeout'))
return Message.de_json(result)
@log
@message
@@ -1259,10 +1095,6 @@ class Bot(TelegramObject):
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
@@ -1291,11 +1123,10 @@ class Bot(TelegramObject):
@log
@message
def editMessageReplyMarkup(self,
chat_id=None,
message_id=None,
inline_message_id=None,
**kwargs):
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).
@@ -1314,10 +1145,6 @@ class Bot(TelegramObject):
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
@@ -1343,10 +1170,7 @@ class Bot(TelegramObject):
return url, data
@log
def getUpdates(self,
offset=None,
limit=100,
**kwargs):
def getUpdates(self, offset=None, limit=100, timeout=0, network_delay=.2):
"""Use this method to receive incoming updates using long polling.
Args:
@@ -1359,17 +1183,17 @@ class Bot(TelegramObject):
limit:
Limits the number of updates to be retrieved. Values between 1-100
are accepted. Defaults to 100.
Keyword Args:
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.
timeout:
Timeout in seconds for long polling. Defaults to 0, i.e. usual
short polling.
network_delay:
Additional timeout in seconds to allow the response from Telegram
to take some time when using long polling. Defaults to 2, which
should be enough for most connections. Increase it if it takes very
long for data to be transmitted from and to the Telegram servers.
Returns:
list[:class:`telegram.Message`]: A list of :class:`telegram.Update`
list[:class:`telegram.Update`]: A list of :class:`telegram.Update`
objects are returned.
Raises:
@@ -1379,30 +1203,26 @@ class Bot(TelegramObject):
url = '{0}/getUpdates'.format(self.base_url)
data = {}
data = {'timeout': timeout}
if offset:
data['offset'] = offset
if limit:
data['limit'] = limit
result = request.post(url, data,
timeout=kwargs.get('timeout'),
network_delay=kwargs.get('network_delay'))
urlopen_timeout = timeout + network_delay
result = request.post(url, data, timeout=urlopen_timeout)
if result:
self.logger.debug(
'Getting updates: %s', [u['update_id'] for u in result])
self.logger.debug('Getting updates: %s', [u['update_id'] for u in result])
else:
self.logger.debug('No new updates found.')
return [Update.de_json(x) for x in result]
@log
def setWebhook(self,
webhook_url=None,
certificate=None,
**kwargs):
def setWebhook(self, webhook_url=None, certificate=None, **kwargs):
"""Use this method to specify a url and receive incoming updates via an
outgoing webhook. Whenever there is an update for the bot, we will send
an HTTPS POST request to the specified url, containing a
@@ -1417,10 +1237,6 @@ class Bot(TelegramObject):
Keyword Args:
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:
bool: On success, `True` is returned.
@@ -1439,9 +1255,7 @@ class Bot(TelegramObject):
if certificate:
data['certificate'] = certificate
result = request.post(url, data,
timeout=kwargs.get('timeout'),
network_delay=kwargs.get('network_delay'))
result = request.post(url, data, timeout=kwargs.get('timeout'))
return result
@@ -1452,9 +1266,7 @@ class Bot(TelegramObject):
return Bot(**data)
def to_dict(self):
data = {'id': self.id,
'username': self.username,
'first_name': self.username}
data = {'id': self.id, 'username': self.username, 'first_name': self.username}
if self.last_name:
data['last_name'] = self.last_name
@@ -1462,6 +1274,31 @@ class Bot(TelegramObject):
return data
def __reduce__(self):
return (self.__class__, (self.token,
self.base_url.replace(self.token, ''),
return (self.__class__, (self.token, self.base_url.replace(self.token, ''),
self.base_file_url.replace(self.token, '')))
# snake_case (PEP8) aliases
get_me = getMe
send_message = sendMessage
forward_message = forwardMessage
send_photo = sendPhoto
send_audio = sendAudio
send_document = sendDocument
send_sticker = sendSticker
send_video = sendVideo
send_voice = sendVoice
send_location = sendLocation
send_venue = sendVenue
send_contact = sendContact
send_chat_action = sendChatAction
answer_inline_query = answerInlineQuery
get_user_profile_photos = getUserProfilePhotos
get_file = getFile
kick_chat_member = kickChatMember
unban_chat_member = unbanChatMember
answer_callback_query = answerCallbackQuery
edit_message_text = editMessageText
edit_message_caption = editMessageCaption
edit_message_reply_markup = editMessageReplyMarkup
get_updates = getUpdates
set_webhook = setWebhook
+1 -6
View File
@@ -16,7 +16,6 @@
#
# 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"""
@@ -26,11 +25,7 @@ from telegram import TelegramObject, Message, User
class CallbackQuery(TelegramObject):
"""This object represents a Telegram CallbackQuery."""
def __init__(self,
id,
from_user,
data,
**kwargs):
def __init__(self, id, from_user, data, **kwargs):
# Required
self.id = id
self.from_user = from_user
+1 -5
View File
@@ -17,7 +17,6 @@
#
# 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 Chat."""
from telegram import TelegramObject
@@ -43,10 +42,7 @@ class Chat(TelegramObject):
type (Optional[str]):
"""
def __init__(self,
id,
type,
**kwargs):
def __init__(self, id, type, **kwargs):
# Required
self.id = int(id)
self.type = type
-1
View File
@@ -17,7 +17,6 @@
#
# 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 ChatAction."""
+1 -7
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""
This module contains a object that represents a Telegram ChosenInlineResult
"""
@@ -42,12 +41,7 @@ class ChosenInlineResult(TelegramObject):
"""
def __init__(self,
result_id,
from_user,
query,
location=None,
inline_message_id=None):
def __init__(self, result_id, from_user, query, location=None, inline_message_id=None):
# Required
self.result_id = result_id
self.from_user = from_user
+1 -5
View File
@@ -16,7 +16,6 @@
#
# 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 Contact."""
from telegram import TelegramObject
@@ -41,10 +40,7 @@ class Contact(TelegramObject):
user_id (Optional[int]):
"""
def __init__(self,
phone_number,
first_name,
**kwargs):
def __init__(self, phone_number, first_name, **kwargs):
# Required
self.phone_number = str(phone_number)
self.first_name = first_name
+1 -4
View File
@@ -16,7 +16,6 @@
#
# 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 Document."""
from telegram import PhotoSize, TelegramObject
@@ -43,9 +42,7 @@ class Document(TelegramObject):
file_size (Optional[int]):
"""
def __init__(self,
file_id,
**kwargs):
def __init__(self, file_id, **kwargs):
# Required
self.file_id = str(file_id)
# Optionals
+23 -23
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python
# flake8: noqa
# pylint: disable=C0103,C0301,R0903
# pylint: disable=C0103,R0903
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
@@ -18,7 +18,6 @@
#
# 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 an Emoji."""
from future.utils import bytes_to_native_str as n
@@ -163,26 +162,26 @@ 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 \
= n(b'\xF0\x9F\x87\xA9\xF0\x9F\x87\xAA')
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 \
= n(b'\xF0\x9F\x87\xA8\xF0\x9F\x87\xB3')
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 \
= n(b'\xF0\x9F\x87\xB0\xF0\x9F\x87\xB7')
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 \
= n(b'\xF0\x9F\x87\xAA\xF0\x9F\x87\xB8')
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 \
= n(b'\xF0\x9F\x87\xBA\xF0\x9F\x87\xB8')
REGIONAL_INDICATOR_SYMBOL_LETTER_R_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_U \
= n(b'\xF0\x9F\x87\xB7\xF0\x9F\x87\xBA')
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 = n(
b'\xF0\x9F\x87\xAC\xF0\x9F\x87\xA7')
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 = n(
b'\xF0\x9F\x87\xAF\xF0\x9F\x87\xB5')
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 = n(
b'\xF0\x9F\x87\xAB\xF0\x9F\x87\xB7')
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 = n(
b'\xF0\x9F\x87\xAE\xF0\x9F\x87\xB9')
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 = 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')
SQUARED_CJK_UNIFIED_IDEOGRAPH_7121 = n(b'\xF0\x9F\x88\x9A')
@@ -858,7 +857,8 @@ class Emoji(object):
NO_MOBILE_PHONES = n(b'\xF0\x9F\x93\xB5')
TWISTED_RIGHTWARDS_ARROWS = n(b'\xF0\x9F\x94\x80')
CLOCKWISE_RIGHTWARDS_AND_LEFTWARDS_OPEN_CIRCLE_ARROWS = n(b'\xF0\x9F\x94\x81')
CLOCKWISE_RIGHTWARDS_AND_LEFTWARDS_OPEN_CIRCLE_ARROWS_WITH_CIRCLED_ONE_OVERLAY = n(b'\xF0\x9F\x94\x82')
CLOCKWISE_RIGHTWARDS_AND_LEFTWARDS_OPEN_CIRCLE_ARROWS_WITH_CIRCLED_ONE_OVERLAY = n(
b'\xF0\x9F\x94\x82')
ANTICLOCKWISE_DOWNWARDS_AND_UPWARDS_OPEN_CIRCLE_ARROWS = n(b'\xF0\x9F\x94\x84')
LOW_BRIGHTNESS_SYMBOL = n(b'\xF0\x9F\x94\x85')
HIGH_BRIGHTNESS_SYMBOL = n(b'\xF0\x9F\x94\x86')
+3 -1
View File
@@ -16,7 +16,6 @@
#
# 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 Error."""
@@ -62,11 +61,13 @@ 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')
@@ -76,5 +77,6 @@ class NetworkError(TelegramError):
class TimedOut(NetworkError):
def __init__(self):
super(TimedOut, self).__init__('Timed out')
+3 -4
View File
@@ -16,7 +16,6 @@
#
# 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
@@ -34,6 +33,6 @@ from .stringregexhandler import StringRegexHandler
from .typehandler import TypeHandler
__all__ = ('Dispatcher', 'JobQueue', 'Updater', 'CallbackQueryHandler',
'ChosenInlineResultHandler', 'CommandHandler', 'Handler',
'InlineQueryHandler', 'MessageHandler', 'Filters', 'RegexHandler',
'StringCommandHandler', 'StringRegexHandler', 'TypeHandler')
'ChosenInlineResultHandler', 'CommandHandler', 'Handler', 'InlineQueryHandler',
'MessageHandler', 'Filters', 'RegexHandler', 'StringCommandHandler',
'StringRegexHandler', 'TypeHandler')
+10 -5
View File
@@ -16,11 +16,11 @@
#
# 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
from telegram.utils.deprecate import deprecate
class CallbackQueryHandler(Handler):
@@ -29,7 +29,7 @@ class CallbackQueryHandler(Handler):
Args:
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``checkUpdate``
positional arguments. It will be called when the ``check_update``
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
@@ -39,10 +39,15 @@ class CallbackQueryHandler(Handler):
def __init__(self, callback, pass_update_queue=False):
super(CallbackQueryHandler, self).__init__(callback, pass_update_queue)
def checkUpdate(self, update):
def check_update(self, update):
return isinstance(update, Update) and update.callback_query
def handleUpdate(self, update, dispatcher):
optional_args = self.collectOptionalArgs(dispatcher)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.CallbackQueryHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+11 -7
View File
@@ -16,11 +16,11 @@
#
# 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
from telegram.utils.deprecate import deprecate
class ChosenInlineResultHandler(Handler):
@@ -30,7 +30,7 @@ class ChosenInlineResultHandler(Handler):
Args:
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``checkUpdate``
positional arguments. It will be called when the ``check_update``
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
@@ -38,13 +38,17 @@ class ChosenInlineResultHandler(Handler):
"""
def __init__(self, callback, pass_update_queue=False):
super(ChosenInlineResultHandler, self).__init__(callback,
pass_update_queue)
super(ChosenInlineResultHandler, self).__init__(callback, pass_update_queue)
def checkUpdate(self, update):
def check_update(self, update):
return isinstance(update, Update) and update.chosen_inline_result
def handleUpdate(self, update, dispatcher):
optional_args = self.collectOptionalArgs(dispatcher)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.ChosenInlineResultHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+13 -12
View File
@@ -16,11 +16,11 @@
#
# 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
from telegram.utils.deprecate import deprecate
class CommandHandler(Handler):
@@ -32,7 +32,7 @@ class CommandHandler(Handler):
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``
positional arguments. It will be called when the ``check_update``
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 `
@@ -43,24 +43,25 @@ class CommandHandler(Handler):
be used to insert updates. Default is ``False``
"""
def __init__(self, command, callback, pass_args=False,
pass_update_queue=False):
def __init__(self, command, callback, 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
def check_update(self, update):
return (isinstance(update, Update) and update.message and update.message.text and
update.message.text.startswith('/') and
update.message.text[1:].split(' ')[0].split('@')[0] ==
self.command)
update.message.text[1:].split(' ')[0].split('@')[0] == self.command)
def handleUpdate(self, update, dispatcher):
optional_args = self.collectOptionalArgs(dispatcher)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
if self.pass_args:
optional_args['args'] = update.message.text.split(' ')[1:]
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.CommandHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+22 -18
View File
@@ -16,7 +16,6 @@
#
# 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 Dispatcher class."""
import logging
@@ -28,6 +27,7 @@ from queue import Empty
from telegram import (TelegramError, NullHandler)
from telegram.ext.handler import Handler
from telegram.utils.deprecate import deprecate
logging.getLogger(__name__).addHandler(NullHandler())
@@ -91,6 +91,7 @@ class Dispatcher(object):
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
@@ -139,8 +140,7 @@ class Dispatcher(object):
self.logger.debug('orderly stopping')
break
elif self.__exception_event.is_set():
self.logger.critical(
'stopping due to exception in another thread')
self.logger.critical('stopping due to exception in another thread')
break
continue
@@ -181,27 +181,24 @@ class Dispatcher(object):
break
# Dispatch any errors
except TelegramError as te:
self.logger.warn(
'A TelegramError was raised while processing the '
'Update.')
self.logger.warn('A TelegramError was raised while processing the '
'Update.')
try:
self.dispatchError(update, te)
except Exception:
self.logger.exception(
'An uncaught error was raised while '
'handling the error')
self.logger.exception('An uncaught error was raised while '
'handling the error')
finally:
break
# Errors should not stop the thread
except Exception:
self.logger.exception(
'An uncaught error was raised while '
'processing the update')
self.logger.exception('An uncaught error was raised while '
'processing the update')
break
def addHandler(self, handler, group=DEFAULT_GROUP):
def add_handler(self, handler, group=DEFAULT_GROUP):
"""
Register a handler.
@@ -227,8 +224,7 @@ class Dispatcher(object):
"""
if not isinstance(handler, Handler):
raise TypeError(
'handler is not an instance of {0}'.format(Handler.__name__))
raise TypeError('handler is not an instance of {0}'.format(Handler.__name__))
if not isinstance(group, int):
raise TypeError('group is not int')
@@ -239,7 +235,7 @@ class Dispatcher(object):
self.handlers[group].append(handler)
def removeHandler(self, handler, group=DEFAULT_GROUP):
def remove_handler(self, handler, group=DEFAULT_GROUP):
"""
Remove a handler from the specified group
@@ -253,7 +249,7 @@ class Dispatcher(object):
del self.handlers[group]
self.groups.remove(group)
def addErrorHandler(self, callback):
def add_error_handler(self, callback):
"""
Registers an error handler in the Dispatcher.
@@ -264,7 +260,7 @@ class Dispatcher(object):
self.error_handlers.append(callback)
def removeErrorHandler(self, callback):
def remove_error_handler(self, callback):
"""
De-registers an error handler.
@@ -286,3 +282,11 @@ class Dispatcher(object):
for callback in self.error_handlers:
callback(self.bot, update, error)
# old non-PEP8 Dispatcher methods
m = "telegram.dispatcher."
addHandler = deprecate(add_handler, m + "AddHandler", m + "add_handler")
removeHandler = deprecate(remove_handler, m + "removeHandler", m + "remove_handler")
addErrorHandler = deprecate(add_error_handler, m + "addErrorHandler", m + "add_error_handler")
removeErrorHandler = deprecate(remove_error_handler, m + "removeErrorHandler",
m + "remove_error_handler")
+13 -5
View File
@@ -16,10 +16,11 @@
#
# 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 """
from telegram.utils.deprecate import deprecate
class Handler(object):
"""
@@ -28,7 +29,7 @@ class Handler(object):
Args:
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``checkUpdate``
positional arguments. It will be called when the ``check_update``
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
@@ -39,7 +40,7 @@ class Handler(object):
self.callback = callback
self.pass_update_queue = pass_update_queue
def checkUpdate(self, update):
def check_update(self, update):
"""
This method is called to determine if an update should be handled by
this handler instance. It should always be overridden.
@@ -52,7 +53,7 @@ class Handler(object):
"""
raise NotImplementedError
def handleUpdate(self, update, dispatcher):
def handle_update(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
@@ -65,7 +66,7 @@ class Handler(object):
"""
raise NotImplementedError
def collectOptionalArgs(self, dispatcher):
def collect_optional_args(self, dispatcher):
"""
Prepares the optional arguments that are the same for all types of
handlers
@@ -78,3 +79,10 @@ class Handler(object):
optional_args['update_queue'] = dispatcher.update_queue
return optional_args
# old non-PEP8 Handler methods
m = "telegram.Handler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
collectOptionalArgs = deprecate(collect_optional_args, m + "collectOptionalArgs",
m + "collect_optional_args")
+10 -5
View File
@@ -16,11 +16,11 @@
#
# 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
from telegram.utils.deprecate import deprecate
class InlineQueryHandler(Handler):
@@ -29,7 +29,7 @@ class InlineQueryHandler(Handler):
Args:
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``checkUpdate``
positional arguments. It will be called when the ``check_update``
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
@@ -39,10 +39,15 @@ class InlineQueryHandler(Handler):
def __init__(self, callback, pass_update_queue=False):
super(InlineQueryHandler, self).__init__(callback, pass_update_queue)
def checkUpdate(self, update):
def check_update(self, update):
return isinstance(update, Update) and update.inline_query
def handleUpdate(self, update, dispatcher):
optional_args = self.collectOptionalArgs(dispatcher)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.InlineQueryHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+2 -9
View File
@@ -16,7 +16,6 @@
#
# 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 JobQueue."""
import logging
@@ -50,12 +49,7 @@ class JobQueue(object):
self.__lock = Lock()
self.running = False
def put(self,
run,
interval,
repeat=True,
next_t=None,
prevent_autostart=False):
def put(self, run, interval, repeat=True, next_t=None, prevent_autostart=False):
"""
Queue a new job. If the JobQueue is not running, it will be started.
@@ -123,8 +117,7 @@ class JobQueue(object):
if not self.running:
self.running = True
self.__lock.release()
job_queue_thread = Thread(target=self._start,
name="job_queue")
job_queue_thread = Thread(target=self._start, name="job_queue")
job_queue_thread.start()
self.logger.debug('Job Queue thread started')
else:
+19 -17
View File
@@ -16,11 +16,11 @@
#
# 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 telegram.utils.deprecate import deprecate
class Filters(object):
@@ -75,18 +75,15 @@ class Filters(object):
@staticmethod
def status_update(update):
return bool(
update.message.new_chat_member or
update.message.left_chat_member or
update.message.new_chat_title or
update.message.new_chat_photo or
update.message.delete_chat_photo or
update.message.group_chat_created or
update.message.supergroup_chat_created or
update.message.channel_chat_created or
update.message.migrate_to_chat_id or
update.message.migrate_from_chat_id or
update.message.pinned_message)
# yapf: disable
# https://github.com/google/yapf/issues/252
return bool(update.message.new_chat_member or update.message.left_chat_member or
update.message.new_chat_title or update.message.new_chat_photo or
update.message.delete_chat_photo or update.message.group_chat_created or
update.message.supergroup_chat_created or
update.message.channel_chat_created or update.message.migrate_to_chat_id or
update.message.migrate_from_chat_id or update.message.pinned_message)
# yapf: enable
class MessageHandler(Handler):
@@ -103,7 +100,7 @@ class MessageHandler(Handler):
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``
positional arguments. It will be called when the ``check_update``
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
@@ -114,7 +111,7 @@ class MessageHandler(Handler):
super(MessageHandler, self).__init__(callback, pass_update_queue)
self.filters = filters
def checkUpdate(self, update):
def check_update(self, update):
if isinstance(update, Update) and update.message:
if not self.filters:
res = True
@@ -124,7 +121,12 @@ class MessageHandler(Handler):
res = False
return res
def handleUpdate(self, update, dispatcher):
optional_args = self.collectOptionalArgs(dispatcher)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.MessageHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+17 -10
View File
@@ -16,7 +16,6 @@
#
# 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
@@ -25,6 +24,7 @@ from future.utils import string_types
from .handler import Handler
from telegram import Update
from telegram.utils.deprecate import deprecate
class RegexHandler(Handler):
@@ -37,7 +37,7 @@ class RegexHandler(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``
positional arguments. It will be called when the ``check_update``
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
@@ -50,8 +50,12 @@ class RegexHandler(Handler):
be used to insert updates. Default is ``False``
"""
def __init__(self, pattern, callback, pass_groups=False,
pass_groupdict=False, pass_update_queue=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, string_types):
@@ -61,17 +65,15 @@ class RegexHandler(Handler):
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):
def check_update(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)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
match = re.match(self.pattern, update.message.text)
if self.pass_groups:
@@ -80,3 +82,8 @@ class RegexHandler(Handler):
optional_args['groupdict'] = match.groupdict()
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.RegexHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+12 -9
View File
@@ -16,10 +16,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 StringCommandHandler class """
from .handler import Handler
from telegram.utils.deprecate import deprecate
class StringCommandHandler(Handler):
@@ -30,7 +30,7 @@ class StringCommandHandler(Handler):
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``
positional arguments. It will be called when the ``check_update``
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 `
@@ -41,21 +41,24 @@ class StringCommandHandler(Handler):
be used to insert updates. Default is ``False``
"""
def __init__(self, command, callback, pass_args=False,
pass_update_queue=False):
def __init__(self, command, callback, 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
def check_update(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)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
if self.pass_args:
optional_args['args'] = update.split(' ')[1:]
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.StringCommandHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+17 -9
View File
@@ -16,7 +16,6 @@
#
# 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
@@ -24,6 +23,7 @@ import re
from future.utils import string_types
from .handler import Handler
from telegram.utils.deprecate import deprecate
class StringRegexHandler(Handler):
@@ -36,7 +36,7 @@ class StringRegexHandler(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``
positional arguments. It will be called when the ``check_update``
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
@@ -49,8 +49,12 @@ class StringRegexHandler(Handler):
be used to insert updates. Default is ``False``
"""
def __init__(self, pattern, callback, pass_groups=False,
pass_groupdict=False, pass_update_queue=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, string_types):
@@ -60,12 +64,11 @@ class StringRegexHandler(Handler):
self.pass_groups = pass_groups
self.pass_groupdict = pass_groupdict
def checkUpdate(self, update):
return isinstance(update, string_types) and bool(
re.match(self.pattern, update))
def check_update(self, update):
return isinstance(update, string_types) and bool(re.match(self.pattern, update))
def handleUpdate(self, update, dispatcher):
optional_args = self.collectOptionalArgs(dispatcher)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
match = re.match(self.pattern, update)
if self.pass_groups:
@@ -74,3 +77,8 @@ class StringRegexHandler(Handler):
optional_args['groupdict'] = match.groupdict()
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.StringRegexHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+10 -5
View File
@@ -16,10 +16,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 TypeHandler class """
from .handler import Handler
from telegram.utils.deprecate import deprecate
class TypeHandler(Handler):
@@ -30,7 +30,7 @@ class TypeHandler(Handler):
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``
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
strict (optional[bool]): Use ``type`` instead of ``isinstance``.
Default is ``False``
@@ -44,13 +44,18 @@ class TypeHandler(Handler):
self.type = type
self.strict = strict
def checkUpdate(self, update):
def check_update(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)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.TypeHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+79 -85
View File
@@ -16,8 +16,6 @@
#
# 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 Updater, which tries to make creating
Telegram bots intuitive."""
@@ -85,8 +83,7 @@ class Updater(object):
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,
self.__exception_event)
self.dispatcher = Dispatcher(self.bot, self.update_queue, workers, self.__exception_event)
self.last_update_id = 0
self.logger = logging.getLogger(__name__)
self.running = False
@@ -96,8 +93,28 @@ class Updater(object):
self.__threads = []
""":type: list[Thread]"""
def start_polling(self, poll_interval=0.0, timeout=10, network_delay=2,
clean=False, bootstrap_retries=0):
def _init_thread(self, target, name, *args, **kwargs):
thr = Thread(target=self._thread_wrapper, name=name, args=(target,) + args, kwargs=kwargs)
thr.start()
self.__threads.append(thr)
def _thread_wrapper(self, target, *args, **kwargs):
thr_name = current_thread().name
self.logger.debug('{0} - started'.format(thr_name))
try:
target(*args, **kwargs)
except Exception:
self.__exception_event.set()
self.logger.exception('unhandled exception')
raise
self.logger.debug('{0} - ended'.format(thr_name))
def start_polling(self,
poll_interval=0.0,
timeout=10,
network_delay=2,
clean=False,
bootstrap_retries=0):
"""
Starts polling updates from Telegram.
@@ -123,35 +140,15 @@ class Updater(object):
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,
bootstrap_retries)
self._init_thread(self._start_polling, "updater", poll_interval, timeout,
network_delay, bootstrap_retries, clean)
# Return the update queue so the main thread can insert updates
return self.update_queue
def _init_thread(self, target, name, *args, **kwargs):
thr = Thread(target=self._thread_wrapper, name=name,
args=(target,) + args, kwargs=kwargs)
thr.start()
self.__threads.append(thr)
def _thread_wrapper(self, target, *args, **kwargs):
thr_name = current_thread().name
self.logger.debug('{0} - started'.format(thr_name))
try:
target(*args, **kwargs)
except Exception:
self.__exception_event.set()
self.logger.exception('unhandled exception')
raise
self.logger.debug('{0} - ended'.format(thr_name))
def start_webhook(self,
listen='127.0.0.1',
port=80,
@@ -194,20 +191,16 @@ class Updater(object):
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, bootstrap_retries,
webhook_url)
self._init_thread(self._start_webhook, "updater", listen, port, url_path, cert,
key, bootstrap_retries, clean, 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,
bootstrap_retries):
def _start_polling(self, poll_interval, timeout, network_delay, bootstrap_retries, clean):
"""
Thread target of thread 'updater'. Runs in background, pulls
updates from Telegram and inserts them in the update queue of the
@@ -217,7 +210,7 @@ class Updater(object):
cur_interval = poll_interval
self.logger.debug('Updater thread started')
self._set_webhook(None, bootstrap_retries, None)
self._bootstrap(bootstrap_retries, clean=clean, webhook_url='')
while self.running:
try:
@@ -225,8 +218,7 @@ class Updater(object):
timeout=timeout,
network_delay=network_delay)
except TelegramError as te:
self.logger.error(
"Error while getting Updates: {0}".format(te))
self.logger.error("Error while getting Updates: {0}".format(te))
# Put the error into the update queue and let the Dispatcher
# broadcast it
@@ -249,28 +241,6 @@ class Updater(object):
sleep(cur_interval)
def _set_webhook(self, webhook_url, max_retries, cert):
retries = 0
while 1:
try:
# Remove webhook
self.bot.setWebhook(webhook_url=webhook_url,
certificate=cert)
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
@@ -282,16 +252,15 @@ class Updater(object):
current_interval = 30
return current_interval
def _start_webhook(self, listen, port, url_path, cert, key,
bootstrap_retries, webhook_url):
def _start_webhook(self, listen, port, url_path, cert, key, bootstrap_retries, clean,
webhook_url):
self.logger.debug('Updater thread started')
use_ssl = cert is not None and key is not None
if not url_path.startswith('/'):
url_path = '/{0}'.format(url_path)
# Create and start server
self.httpd = WebhookServer((listen, port), WebhookHandler,
self.update_queue, url_path)
self.httpd = WebhookServer((listen, port), WebhookHandler, self.update_queue, url_path)
if use_ssl:
self._check_ssl_cert(cert, key)
@@ -300,18 +269,23 @@ class Updater(object):
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._bootstrap(max_retries=bootstrap_retries,
clean=clean,
webhook_url=webhook_url,
cert=open(cert, 'rb'))
elif clean:
self.logger.warning("cleaning updates is not supported if "
"SSL-termination happens elsewhere; skipping")
self.httpd.serve_forever(poll_interval=1)
def _check_ssl_cert(self, cert, key):
# Check SSL-Certificate with openssl, if possible
try:
exit_code = subprocess.call(["openssl", "x509", "-text",
"-noout", "-in", cert],
stdout=open(os.devnull, 'wb'),
stderr=subprocess.STDOUT)
exit_code = subprocess.call(
["openssl", "x509", "-text", "-noout", "-in", cert],
stdout=open(os.devnull, 'wb'),
stderr=subprocess.STDOUT)
except OSError:
exit_code = 0
if exit_code is 0:
@@ -326,11 +300,35 @@ class Updater(object):
else:
raise TelegramError('SSL Certificate invalid')
def _gen_webhook_url(self, listen, port, url_path):
return 'https://{listen}:{port}{path}'.format(
listen=listen,
port=port,
path=url_path)
@staticmethod
def _gen_webhook_url(listen, port, url_path):
return 'https://{listen}:{port}{path}'.format(listen=listen, port=port, path=url_path)
def _bootstrap(self, max_retries, clean, webhook_url, cert=None):
retries = 0
while True:
try:
if clean:
# Disable webhook for cleaning
self.bot.setWebhook(webhook_url='')
self._clean_updates()
self.bot.setWebhook(webhook_url=webhook_url, certificate=cert)
except (Unauthorized, InvalidToken):
raise
except TelegramError:
msg = 'error in bootstrap phase; try={0} max_retries={1}'\
.format(retries, max_retries)
if max_retries < 0 or retries < max_retries:
self.logger.warning(msg)
retries += 1
else:
self.logger.exception(msg)
raise
else:
break
sleep(1)
def _clean_updates(self):
self.logger.debug('Cleaning updates from Telegram server')
@@ -360,10 +358,9 @@ class Updater(object):
def _stop_httpd(self):
if self.httpd:
self.logger.debug(
'Waiting for current webhook connection to be '
'closed... Send a Telegram message to the bot to exit '
'immediately.')
self.logger.debug('Waiting for current webhook connection to be '
'closed... Send a Telegram message to the bot to exit '
'immediately.')
self.httpd.shutdown()
self.httpd = None
@@ -376,16 +373,13 @@ class Updater(object):
threads = list(dispatcher.async_threads)
total = len(threads)
for i, thr in enumerate(threads):
self.logger.debug(
'Waiting for async thread {0}/{1} to end'.format(i, total))
self.logger.debug('Waiting for async thread {0}/{1} to end'.format(i, total))
thr.join()
self.logger.debug(
'async thread {0}/{1} has ended'.format(i, total))
self.logger.debug('async thread {0}/{1} has ended'.format(i, total))
def _join_threads(self):
for thr in self.__threads:
self.logger.debug(
'Waiting for {0} thread to end'.format(thr.name))
self.logger.debug('Waiting for {0} thread to end'.format(thr.name))
thr.join()
self.logger.debug('{0} thread has ended'.format(thr.name))
self.__threads = []
+2 -6
View File
@@ -16,7 +16,6 @@
#
# 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 File."""
from os.path import basename
@@ -42,9 +41,7 @@ class File(TelegramObject):
file_path (Optional[str]):
"""
def __init__(self,
file_id,
**kwargs):
def __init__(self, file_id, **kwargs):
# Required
self.file_id = str(file_id)
# Optionals
@@ -65,8 +62,7 @@ class File(TelegramObject):
return File(**data)
def download(self,
custom_path=None):
def download(self, custom_path=None):
"""
Args:
custom_path (str):
+1 -4
View File
@@ -16,7 +16,6 @@
#
# 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 ForceReply."""
from telegram import ReplyMarkup
@@ -37,9 +36,7 @@ class ForceReply(ReplyMarkup):
selective (Optional[bool]):
"""
def __init__(self,
force_reply=True,
**kwargs):
def __init__(self, force_reply=True, **kwargs):
# Required
self.force_reply = bool(force_reply)
# Optionals
+2 -6
View File
@@ -16,7 +16,6 @@
#
# 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"""
@@ -43,9 +42,7 @@ class InlineKeyboardButton(TelegramObject):
"""
def __init__(self,
text,
**kwargs):
def __init__(self, text, **kwargs):
# Required
self.text = text
@@ -70,7 +67,6 @@ class InlineKeyboardButton(TelegramObject):
inline_keyboards = list()
for inline_keyboard in data:
inline_keyboards.append(InlineKeyboardButton.
de_json(inline_keyboard))
inline_keyboards.append(InlineKeyboardButton.de_json(inline_keyboard))
return inline_keyboards
+6 -10
View File
@@ -16,7 +16,6 @@
#
# 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"""
@@ -27,15 +26,14 @@ class InlineKeyboardMarkup(ReplyMarkup):
"""This object represents a Telegram InlineKeyboardMarkup.
Attributes:
inline_keyboard (List[List[:class:`telegram.InlineKeyboardMarkup`]]):
inline_keyboard (List[List[:class:`telegram.InlineKeyboardButton`]]):
Args:
inline_keyboard (List[List[:class:`telegram.InlineKeyboardMarkup`]]):
inline_keyboard (List[List[:class:`telegram.InlineKeyboardButton`]]):
"""
def __init__(self,
inline_keyboard):
def __init__(self, inline_keyboard):
# Required
self.inline_keyboard = inline_keyboard
@@ -46,9 +44,8 @@ class InlineKeyboardMarkup(ReplyMarkup):
if not data:
return None
data['inline_keyboard'] = \
[InlineKeyboardButton.de_list(inline_keyboard) for inline_keyboard
in data['inline_keyboard']]
data['inline_keyboard'] = [InlineKeyboardButton.de_list(inline_keyboard)
for inline_keyboard in data['inline_keyboard']]
return InlineKeyboardMarkup(**data)
@@ -57,7 +54,6 @@ class InlineKeyboardMarkup(ReplyMarkup):
data['inline_keyboard'] = []
for inline_keyboard in self.inline_keyboard:
data['inline_keyboard'].append(
[x.to_dict() for x in inline_keyboard])
data['inline_keyboard'].append([x.to_dict() for x in inline_keyboard])
return data
+10 -8
View File
@@ -16,10 +16,9 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram InlineQuery"""
from telegram import TelegramObject, User
from telegram import TelegramObject, User, Location
class InlineQuery(TelegramObject):
@@ -39,20 +38,22 @@ class InlineQuery(TelegramObject):
from_user (:class:`telegram.User`):
query (str):
offset (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
location (optional[:class:`telegram.Location`]):
"""
def __init__(self,
id,
from_user,
query,
offset,
**kwargs):
def __init__(self, id, from_user, query, offset, **kwargs):
# Required
self.id = id
self.from_user = from_user
self.query = query
self.offset = offset
# Optional
self.location = kwargs.get('location')
@staticmethod
def de_json(data):
"""
@@ -68,6 +69,7 @@ class InlineQuery(TelegramObject):
return None
data['from_user'] = User.de_json(data.get('from'))
data['location'] = Location.de_json(data.get('location'))
return InlineQuery(**data)
+1 -4
View File
@@ -16,7 +16,6 @@
#
# 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
InlineQueryResult"""
@@ -36,9 +35,7 @@ class InlineQueryResult(TelegramObject):
"""
def __init__(self,
type,
id):
def __init__(self, type, id):
# Required
self.type = str(type)
self.id = str(id)
+5 -9
View File
@@ -16,12 +16,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
InlineQueryResultArticle"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultArticle(InlineQueryResult):
@@ -97,12 +95,10 @@ class InlineQueryResultArticle(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultArticle,
InlineQueryResultArticle).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'))
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)
+6 -9
View File
@@ -16,12 +16,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
InlineQueryResultAudio"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultAudio(InlineQueryResult):
@@ -59,6 +57,7 @@ class InlineQueryResultAudio(InlineQueryResult):
input_message_content (Optional[
:class:`telegram.input_message_content`]):
"""
def __init__(self,
id,
audio_url,
@@ -86,12 +85,10 @@ class InlineQueryResultAudio(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultAudio,
InlineQueryResultAudio).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'))
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)
+7 -15
View File
@@ -16,12 +16,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
InlineQueryResultCachedAudio"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedAudio(InlineQueryResult):
@@ -54,12 +52,8 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
input_message_content (Optional[
:class:`telegram.input_message_content`]):
"""
def __init__(self,
id,
audio_file_id,
reply_markup=None,
input_message_content=None,
**kwargs):
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
@@ -72,12 +66,10 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultCachedAudio,
InlineQueryResultCachedAudio).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'))
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)
+5 -7
View File
@@ -16,15 +16,14 @@
#
# 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
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedDocument(InlineQueryResult):
def __init__(self,
id,
title,
@@ -54,9 +53,8 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
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'))
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)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# 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
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedGif(InlineQueryResult):
def __init__(self,
id,
gif_file_id,
@@ -49,12 +48,10 @@ class InlineQueryResultCachedGif(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultCachedGif,
InlineQueryResultCachedGif).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'))
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)
+5 -7
View File
@@ -16,15 +16,14 @@
#
# 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
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
def __init__(self,
id,
mpeg4_file_id,
@@ -52,9 +51,8 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
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'))
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)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# 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
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedPhoto(InlineQueryResult):
def __init__(self,
id,
photo_file_id,
@@ -52,12 +51,10 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultCachedPhoto,
InlineQueryResultCachedPhoto).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'))
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)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# 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
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedSticker(InlineQueryResult):
def __init__(self,
id,
sticker_file_id,
@@ -43,12 +42,10 @@ class InlineQueryResultCachedSticker(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultCachedSticker,
InlineQueryResultCachedSticker).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'))
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)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# 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
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedVideo(InlineQueryResult):
def __init__(self,
id,
video_file_id,
@@ -51,12 +50,10 @@ class InlineQueryResultCachedVideo(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultCachedVideo,
InlineQueryResultCachedVideo).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'))
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)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# 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
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedVoice(InlineQueryResult):
def __init__(self,
id,
voice_file_id,
@@ -48,12 +47,10 @@ class InlineQueryResultCachedVoice(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultCachedVoice,
InlineQueryResultCachedVoice).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'))
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)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# 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
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultContact(InlineQueryResult):
def __init__(self,
id,
phone_number,
@@ -57,12 +56,10 @@ class InlineQueryResultContact(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultContact,
InlineQueryResultContact).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'))
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)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# 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
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultDocument(InlineQueryResult):
def __init__(self,
id,
document_url,
@@ -62,12 +61,10 @@ class InlineQueryResultDocument(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultDocument,
InlineQueryResultDocument).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'))
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)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# 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
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultGif(InlineQueryResult):
def __init__(self,
id,
gif_url,
@@ -58,12 +57,10 @@ class InlineQueryResultGif(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultGif,
InlineQueryResultGif).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'))
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)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# 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
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultLocation(InlineQueryResult):
def __init__(self,
id,
latitude,
@@ -56,12 +55,10 @@ class InlineQueryResultLocation(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultLocation,
InlineQueryResultLocation).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'))
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)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# 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
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultMpeg4Gif(InlineQueryResult):
def __init__(self,
id,
mpeg4_url,
@@ -58,12 +57,10 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultMpeg4Gif,
InlineQueryResultMpeg4Gif).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'))
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)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# 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
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultPhoto(InlineQueryResult):
def __init__(self,
id,
photo_url,
@@ -60,12 +59,10 @@ class InlineQueryResultPhoto(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultPhoto,
InlineQueryResultPhoto).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'))
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)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# 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
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultVenue(InlineQueryResult):
def __init__(self,
id,
latitude,
@@ -62,12 +61,10 @@ class InlineQueryResultVenue(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultVenue,
InlineQueryResultVenue).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'))
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)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# 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
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultVideo(InlineQueryResult):
def __init__(self,
id,
video_url,
@@ -65,12 +64,10 @@ class InlineQueryResultVideo(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultVideo,
InlineQueryResultVideo).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'))
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)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# 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
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultVoice(InlineQueryResult):
def __init__(self,
id,
voice_url,
@@ -49,12 +48,10 @@ class InlineQueryResultVoice(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultVoice,
InlineQueryResultVoice).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'))
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)
+1 -5
View File
@@ -16,7 +16,6 @@
#
# 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"""
@@ -26,10 +25,7 @@ from telegram import InputMessageContent
class InputContactMessageContent(InputMessageContent):
"""Base class for Telegram InputContactMessageContent Objects"""
def __init__(self,
phone_number,
first_name,
last_name=None):
def __init__(self, phone_number, first_name, last_name=None):
# Required
self.phone_number = phone_number
self.first_name = first_name
+21 -32
View File
@@ -17,33 +17,32 @@
#
# 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 InputFile."""
try:
# python 3
from email.generator import _make_boundary as choose_boundary
except ImportError:
# python 2
from mimetools import choose_boundary
import imghdr
import mimetypes
import os
import sys
import imghdr
try:
from email.generator import _make_boundary as choose_boundary
from urllib.request import urlopen
except ImportError:
from mimetools import choose_boundary
from urllib2 import urlopen
from future.moves.urllib.request import urlopen
from telegram import TelegramError
DEFAULT_MIME_TYPE = 'application/octet-stream'
USER_AGENT = 'Python Telegram Bot' \
' (https://github.com/python-telegram-bot/python-telegram-bot)'
USER_AGENT = 'Python Telegram Bot (https://github.com/python-telegram-bot/python-telegram-bot)'
class InputFile(object):
"""This object represents a Telegram InputFile."""
def __init__(self,
data):
def __init__(self, data):
self.data = data
self.boundary = choose_boundary()
@@ -81,18 +80,18 @@ class InputFile(object):
if 'filename' in data:
self.filename = self.data.pop('filename')
elif hasattr(self.input_file, 'name'):
# on py2.7, pylint fails to understand this properly
# pylint: disable=E1101
self.filename = os.path.basename(self.input_file.name)
elif from_url:
self.filename = os.path.basename(self.input_file.url) \
.split('?')[0].split('&')[0]
self.filename = os.path.basename(self.input_file.url).split('?')[0].split('&')[0]
try:
self.mimetype = InputFile.is_image(self.input_file_content)
if not self.filename or '.' not in self.filename:
self.filename = self.mimetype.replace('/', '.')
except TelegramError:
self.mimetype = mimetypes.guess_type(self.filename)[0] or \
DEFAULT_MIME_TYPE
self.mimetype = mimetypes.guess_type(self.filename)[0] or DEFAULT_MIME_TYPE
@property
def headers(self):
@@ -100,8 +99,7 @@ class InputFile(object):
Returns:
str:
"""
return {'User-agent': USER_AGENT,
'Content-type': self.content_type}
return {'User-agent': USER_AGENT, 'Content-type': self.content_type}
@property
def content_type(self):
@@ -122,21 +120,14 @@ class InputFile(object):
# Add data fields
for name, value in self.data.items():
form.extend([
form_boundary,
'Content-Disposition: form-data; name="%s"' % name,
'',
str(value)
form_boundary, 'Content-Disposition: form-data; name="%s"' % name, '', str(value)
])
# Add input_file to upload
form.extend([
form_boundary,
'Content-Disposition: form-data; name="%s"; filename="%s"' % (
form_boundary, 'Content-Disposition: form-data; name="%s"; filename="%s"' % (
self.input_name, self.filename
),
'Content-Type: %s' % self.mimetype,
'',
self.input_file_content
), 'Content-Type: %s' % self.mimetype, '', self.input_file_content
])
form.append('--' + self.boundary + '--')
@@ -189,14 +180,12 @@ class InputFile(object):
bool
"""
if data:
file_types = ['audio', 'document', 'photo', 'sticker', 'video',
'voice', 'certificate']
file_types = ['audio', 'document', 'photo', 'sticker', 'video', 'voice', 'certificate']
file_type = [i for i in list(data.keys()) if i in file_types]
if file_type:
file_content = data[file_type[0]]
return hasattr(file_content, 'read') or str(
file_content).startswith('http')
return hasattr(file_content, 'read') or str(file_content).startswith('http')
return False
+1 -4
View File
@@ -16,7 +16,6 @@
#
# 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"""
@@ -26,9 +25,7 @@ from telegram import InputMessageContent
class InputLocationMessageContent(InputMessageContent):
"""Base class for Telegram InputLocationMessageContent Objects"""
def __init__(self,
latitude,
longitude):
def __init__(self, latitude, longitude):
# Required
self.latitude = latitude
self.longitude = longitude
-1
View File
@@ -16,7 +16,6 @@
#
# 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"""
+1 -5
View File
@@ -16,7 +16,6 @@
#
# 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"""
@@ -26,10 +25,7 @@ from telegram import InputMessageContent
class InputTextMessageContent(InputMessageContent):
"""Base class for Telegram InputTextMessageContent Objects"""
def __init__(self,
message_text,
parse_mode=None,
disable_web_page_preview=None):
def __init__(self, message_text, parse_mode=None, disable_web_page_preview=None):
# Required
self.message_text = message_text
# Optionals
+1 -7
View File
@@ -16,7 +16,6 @@
#
# 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"""
@@ -26,12 +25,7 @@ from telegram import InputMessageContent
class InputVenueMessageContent(InputMessageContent):
"""Base class for Telegram InputVenueMessageContent Objects"""
def __init__(self,
latitude,
longitude,
title,
address,
foursquare_id=None):
def __init__(self, latitude, longitude, title, address, foursquare_id=None):
# Required
self.latitude = latitude
self.longitude = longitude
+2 -7
View File
@@ -16,7 +16,6 @@
#
# 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
@@ -34,10 +33,7 @@ class KeyboardButton(TelegramObject):
request_contact (Optional[bool]):
"""
def __init__(self,
text,
request_contact=None,
request_location=None):
def __init__(self, text, request_contact=None, request_location=None):
# Required
self.text = text
# Optionals
@@ -60,7 +56,6 @@ class KeyboardButton(TelegramObject):
keyboards = list()
for keyboard in data:
keyboards.append(KeyboardButton.
de_json(keyboard))
keyboards.append(KeyboardButton.de_json(keyboard))
return keyboards
+2 -5
View File
@@ -16,14 +16,13 @@
#
# 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 Location."""
from telegram import TelegramObject
class Location(TelegramObject):
"""This object represents a Telegram Sticker.
"""This object represents a Telegram Location.
Attributes:
longitude (float):
@@ -34,9 +33,7 @@ class Location(TelegramObject):
latitude (float):
"""
def __init__(self,
longitude,
latitude):
def __init__(self, longitude, latitude):
# Required
self.longitude = float(longitude)
self.latitude = float(latitude)
+10 -16
View File
@@ -17,15 +17,13 @@
#
# 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 Message."""
from datetime import datetime
from time import mktime
from telegram import (Audio, Contact, Document, Chat, Location, PhotoSize,
Sticker, TelegramObject, User, Video, Voice, Venue,
MessageEntity)
from telegram import (Audio, Contact, Document, Chat, Location, PhotoSize, Sticker, TelegramObject,
User, Video, Voice, Venue, MessageEntity)
class Message(TelegramObject):
@@ -39,6 +37,7 @@ class Message(TelegramObject):
from_user (:class:`telegram.User`):
date (:class:`datetime.datetime`):
forward_from (:class:`telegram.User`):
forward_from_chat (:class:`telegram.Chat`):
forward_date (:class:`datetime.datetime`):
reply_to_message (:class:`telegram.Message`):
text (str):
@@ -78,6 +77,7 @@ class Message(TelegramObject):
Keyword Args:
forward_from (Optional[:class:`telegram.User`]):
forward_from_chat (:class:`telegram.Chat`):
forward_date (Optional[:class:`datetime.datetime`]):
reply_to_message (Optional[:class:`telegram.Message`]):
text (Optional[str]):
@@ -102,12 +102,7 @@ class Message(TelegramObject):
channel_chat_created (Optional[bool]):
"""
def __init__(self,
message_id,
from_user,
date,
chat,
**kwargs):
def __init__(self, message_id, from_user, date, chat, **kwargs):
# Required
self.message_id = int(message_id)
self.from_user = from_user
@@ -115,6 +110,7 @@ class Message(TelegramObject):
self.chat = chat
# Optionals
self.forward_from = kwargs.get('forward_from')
self.forward_from_chat = kwargs.get('forward_from_chat')
self.forward_date = kwargs.get('forward_date')
self.reply_to_message = kwargs.get('reply_to_message')
self.text = kwargs.get('text', '')
@@ -135,12 +131,10 @@ class Message(TelegramObject):
self.new_chat_photo = kwargs.get('new_chat_photo')
self.delete_chat_photo = bool(kwargs.get('delete_chat_photo', False))
self.group_chat_created = bool(kwargs.get('group_chat_created', False))
self.supergroup_chat_created = bool(kwargs.get(
'supergroup_chat_created', False))
self.supergroup_chat_created = bool(kwargs.get('supergroup_chat_created', False))
self.migrate_to_chat_id = int(kwargs.get('migrate_to_chat_id', 0))
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.channel_chat_created = bool(kwargs.get('channel_chat_created', False))
self.pinned_message = kwargs.get('pinned_message')
@property
@@ -165,9 +159,9 @@ class Message(TelegramObject):
data['chat'] = Chat.de_json(data.get('chat'))
data['entities'] = MessageEntity.de_list(data.get('entities'))
data['forward_from'] = User.de_json(data.get('forward_from'))
data['forward_from_chat'] = Chat.de_json(data.get('forward_from_chat'))
data['forward_date'] = Message._fromtimestamp(data.get('forward_date'))
data['reply_to_message'] = \
Message.de_json(data.get('reply_to_message'))
data['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'))
+1 -6
View File
@@ -16,7 +16,6 @@
#
# 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
@@ -34,11 +33,7 @@ class MessageEntity(TelegramObject):
url (Optional[str]):
"""
def __init__(self,
type,
offset,
length,
url=None):
def __init__(self, type, offset, length, url=None):
# Required
self.type = type
self.offset = offset
-1
View File
@@ -16,7 +16,6 @@
#
# 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 logging NullHandler."""
import logging
-1
View File
@@ -17,7 +17,6 @@
#
# 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
Message Parse Modes."""
+3 -10
View File
@@ -16,7 +16,6 @@
#
# 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 PhotoSize."""
from telegram import TelegramObject
@@ -41,11 +40,7 @@ class PhotoSize(TelegramObject):
file_size (Optional[int]):
"""
def __init__(self,
file_id,
width,
height,
**kwargs):
def __init__(self, file_id, width, height, **kwargs):
# Required
self.file_id = str(file_id)
self.width = int(width)
@@ -56,10 +51,8 @@ class PhotoSize(TelegramObject):
def __eq__(self, other):
if not isinstance(other, self.__class__):
return False
return (self.file_id == other.file_id and
self.width == other.width and
self.height == other.height and
self.file_size == other.file_size)
return (self.file_id == other.file_id and self.width == other.width and
self.height == other.height and self.file_size == other.file_size)
@staticmethod
def de_json(data):
+1 -4
View File
@@ -16,7 +16,6 @@
#
# 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
ReplyKeyboardHide."""
@@ -38,9 +37,7 @@ class ReplyKeyboardHide(ReplyMarkup):
selective (Optional[bool]):
"""
def __init__(self,
hide_keyboard=True,
**kwargs):
def __init__(self, hide_keyboard=True, **kwargs):
# Required
self.hide_keyboard = bool(hide_keyboard)
# Optionals
+10 -9
View File
@@ -16,7 +16,6 @@
#
# 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
ReplyKeyboardMarkup."""
@@ -42,9 +41,7 @@ class ReplyKeyboardMarkup(ReplyMarkup):
selective (Optional[bool]):
"""
def __init__(self,
keyboard,
**kwargs):
def __init__(self, keyboard, **kwargs):
# Required
self.keyboard = keyboard
# Optionals
@@ -64,8 +61,7 @@ class ReplyKeyboardMarkup(ReplyMarkup):
if not data:
return None
data['keyboard'] = [KeyboardButton.de_list(keyboard) for keyboard in
data['keyboard']]
data['keyboard'] = [KeyboardButton.de_list(keyboard) for keyboard in data['keyboard']]
return ReplyKeyboardMarkup(**data)
@@ -73,7 +69,12 @@ class ReplyKeyboardMarkup(ReplyMarkup):
data = super(ReplyKeyboardMarkup, self).to_dict()
data['keyboard'] = []
for keyboard in self.keyboard:
data['keyboard'].append([x.to_dict() for x in keyboard])
for row in self.keyboard:
r = []
for button in row:
if hasattr(button, 'to_dict'):
r.append(button.to_dict()) # telegram.KeyboardButton
else:
r.append(button) # str
data['keyboard'].append(r)
return data
-1
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""Base class for Telegram ReplyMarkup Objects."""
from telegram import TelegramObject
+4 -6
View File
@@ -16,7 +16,6 @@
#
# 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 Sticker."""
from telegram import PhotoSize, TelegramObject
@@ -30,6 +29,7 @@ class Sticker(TelegramObject):
width (int):
height (int):
thumb (:class:`telegram.PhotoSize`):
emoji (str):
file_size (int):
Args:
@@ -40,20 +40,18 @@ class Sticker(TelegramObject):
Keyword Args:
thumb (Optional[:class:`telegram.PhotoSize`]):
emoji (Optional[str]):
file_size (Optional[int]):
"""
def __init__(self,
file_id,
width,
height,
**kwargs):
def __init__(self, file_id, width, height, **kwargs):
# Required
self.file_id = str(file_id)
self.width = int(width)
self.height = int(height)
# Optionals
self.thumb = kwargs.get('thumb')
self.emoji = kwargs.get('emoji', '')
self.file_size = int(kwargs.get('file_size', 0))
@staticmethod
+4 -10
View File
@@ -16,11 +16,9 @@
#
# 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 Update."""
from telegram import (Message, TelegramObject, InlineQuery,
ChosenInlineResult, CallbackQuery)
from telegram import (Message, TelegramObject, InlineQuery, ChosenInlineResult, CallbackQuery)
class Update(TelegramObject):
@@ -44,9 +42,7 @@ class Update(TelegramObject):
callback_query (Optional[:class:`telegram.CallbackQuery`]):
"""
def __init__(self,
update_id,
**kwargs):
def __init__(self, update_id, **kwargs):
# Required
self.update_id = int(update_id)
# Optionals
@@ -69,9 +65,7 @@ class Update(TelegramObject):
data['message'] = Message.de_json(data.get('message'))
data['inline_query'] = InlineQuery.de_json(data.get('inline_query'))
data['chosen_inline_result'] = \
ChosenInlineResult.de_json(data.get('chosen_inline_result'))
data['callback_query'] = \
CallbackQuery.de_json(data.get('callback_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)
+1 -5
View File
@@ -17,7 +17,6 @@
#
# 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 User."""
from telegram import TelegramObject
@@ -44,10 +43,7 @@ class User(TelegramObject):
username (Optional[str]):
"""
def __init__(self,
id,
first_name,
**kwargs):
def __init__(self, id, first_name, **kwargs):
# Required
self.id = int(id)
self.first_name = first_name
+1 -4
View File
@@ -16,7 +16,6 @@
#
# 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
UserProfilePhotos."""
@@ -35,9 +34,7 @@ class UserProfilePhotos(TelegramObject):
photos (List[List[:class:`telegram.PhotoSize`]]):
"""
def __init__(self,
total_count,
photos):
def __init__(self, total_count, photos):
# Required
self.total_count = int(total_count)
self.photos = photos
+2 -3
View File
@@ -44,9 +44,8 @@ class Botan(object):
urlopen(request)
return True
except HTTPError as error:
self.logger.warn('Botan track error ' +
str(error.code) +
':' + error.read().decode('utf-8'))
self.logger.warn('Botan track error ' + str(error.code) + ':' + error.read().decode(
'utf-8'))
return False
except URLError as error:
self.logger.warn('Botan track error ' + str(error.reason))
+31
View File
@@ -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 <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module facilitates the deprecation of functions"""
import warnings
def deprecate(func, old, new):
"""Warn users invoking old to switch to the new function."""
def f(*args, **kwargs):
warnings.warn("{0} is being deprecated, please use {1} from now on".format(old, new))
return func(*args, **kwargs)
return f
+8 -37
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env python
# pylint: disable=no-name-in-module,unused-import
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
@@ -17,7 +16,6 @@
#
# 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 methods to make POST and GET requests"""
import functools
@@ -25,22 +23,9 @@ import json
import socket
from ssl import SSLError
try:
# python2
from httplib import HTTPException
except ImportError:
# python3
from http.client import HTTPException
try:
# python3
from urllib.request import urlopen, urlretrieve, Request
from urllib.error import HTTPError, URLError
except ImportError:
# python2
from urllib import urlretrieve
from urllib2 import urlopen, Request, URLError
from urllib2 import HTTPError
from future.moves.http.client import HTTPException
from future.moves.urllib.error import HTTPError, URLError
from future.moves.urllib.request import urlopen, urlretrieve, Request
from telegram import (InputFile, TelegramError)
from telegram.error import Unauthorized, NetworkError, TimedOut
@@ -71,6 +56,7 @@ def _parse(json_data):
def _try_except_req(func):
"""Decorator for requests to handle known exceptions"""
@functools.wraps(func)
def decorator(*args, **kwargs):
try:
@@ -128,10 +114,7 @@ def get(url):
@_try_except_req
def post(url,
data,
timeout=None,
network_delay=2.):
def post(url, data, timeout=None):
"""Request an URL.
Args:
url:
@@ -141,11 +124,6 @@ def post(url,
timeout:
float. If this value is specified, use it as the definitive timeout (in
seconds) for urlopen() operations. [Optional]
network_delay:
float. If using the timeout specified in `data` (which is a timeout for
the Telegram servers operation), then `network_delay` as an extra delay
(in seconds) to compensate for network latency.
default: 2 [Optional]
Notes:
If neither `timeout` nor `data['timeout']` is specified. The underlying
@@ -159,27 +137,20 @@ def post(url,
if timeout is not None:
urlopen_kwargs['timeout'] = timeout
elif 'timeout' in data:
urlopen_kwargs['timeout'] = data['timeout'] + network_delay
if InputFile.is_inputfile(data):
data = InputFile(data)
request = Request(url,
data=data.to_form(),
headers=data.headers)
request = Request(url, data=data.to_form(), headers=data.headers)
else:
data = json.dumps(data)
request = Request(url,
data=data.encode(),
headers={'Content-Type': 'application/json'})
request = Request(url, data=data.encode(), headers={'Content-Type': 'application/json'})
result = urlopen(request, **urlopen_kwargs).read()
return _parse(result)
@_try_except_req
def download(url,
filename):
def download(url, filename):
"""Download a file by its URL.
Args:
url:
-1
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains functions to validate function arguments"""
from telegram.error import InvalidToken
+5 -9
View File
@@ -9,7 +9,6 @@ try:
except ImportError:
import http.server as BaseHTTPServer
logging.getLogger(__name__).addHandler(NullHandler())
@@ -21,10 +20,9 @@ class _InvalidPost(Exception):
class WebhookServer(BaseHTTPServer.HTTPServer, object):
def __init__(self, server_address, RequestHandlerClass, update_queue,
webhook_path):
super(WebhookServer, self).__init__(server_address,
RequestHandlerClass)
def __init__(self, server_address, RequestHandlerClass, update_queue, webhook_path):
super(WebhookServer, self).__init__(server_address, RequestHandlerClass)
self.logger = logging.getLogger(__name__)
self.update_queue = update_queue
self.webhook_path = webhook_path
@@ -85,13 +83,11 @@ class WebhookHandler(BaseHTTPServer.BaseHTTPRequestHandler, object):
self.logger.debug('Webhook received data: ' + json_string)
update = Update.de_json(json.loads(json_string))
self.logger.debug('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):
if not (self.path == self.server.webhook_path and
'content-type' in self.headers and
if not (self.path == self.server.webhook_path and 'content-type' in self.headers and
self.headers['content-type'] == 'application/json'):
raise _InvalidPost(403)
+1 -6
View File
@@ -16,7 +16,6 @@
#
# 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
@@ -33,11 +32,7 @@ class Venue(TelegramObject):
foursquare_id (Optional[str]):
"""
def __init__(self,
location,
title,
address,
foursquare_id=None):
def __init__(self, location, title, address, foursquare_id=None):
# Required
self.location = location
self.title = title
+1 -7
View File
@@ -16,7 +16,6 @@
#
# 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 Video."""
from telegram import PhotoSize, TelegramObject
@@ -47,12 +46,7 @@ class Video(TelegramObject):
file_size (Optional[int]):
"""
def __init__(self,
file_id,
width,
height,
duration,
**kwargs):
def __init__(self, file_id, width, height, duration, **kwargs):
# Required
self.file_id = str(file_id)
self.width = int(width)
+1 -4
View File
@@ -16,7 +16,6 @@
#
# 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 Voice."""
from telegram import TelegramObject
@@ -41,9 +40,7 @@ class Voice(TelegramObject):
file_size (Optional[int]):
"""
def __init__(self,
file_id,
**kwargs):
def __init__(self, file_id, **kwargs):
# Required
self.file_id = str(file_id)
# Optionals
+8 -5
View File
@@ -18,10 +18,10 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Base class for tests"""
import signal
import sys
import os
import sys
import signal
from nose.tools import make_decorator
sys.path.append('.')
@@ -36,8 +36,8 @@ class BaseTest(object):
def __init__(self, *args, **kwargs):
super(BaseTest, self).__init__(*args, **kwargs)
bot = telegram.Bot(os.environ.get(
'TOKEN', '133505823:AAHZFMHno3mzVLErU5b5jJvaeG--qUyLyG0'))
bot = telegram.Bot(os.environ.get('TOKEN',
'133505823:AAHZFMHno3mzVLErU5b5jJvaeG--qUyLyG0'))
chat_id = os.environ.get('CHAT_ID', '12173560')
self._bot = bot
@@ -61,6 +61,7 @@ class BaseTest(object):
class TestTimedOut(AssertionError):
def __init__(self, time_limit, frame):
super(TestTimedOut, self).__init__('time_limit={0}'.format(time_limit))
self.time_limit = time_limit
@@ -68,7 +69,9 @@ class TestTimedOut(AssertionError):
def timeout(time_limit):
def decorator(func):
def timed_out(_signum, frame):
raise TestTimedOut(time_limit, frame)
+9 -9
View File
@@ -197,9 +197,9 @@ class AudioTest(BaseTest, unittest.TestCase):
del (json_dict['file_id'])
json_dict['audio'] = open(os.devnull, 'rb')
self.assertRaises(telegram.TelegramError,
lambda: self._bot.sendAudio(chat_id=self._chat_id,
**json_dict))
self.assertRaises(
telegram.TelegramError,
lambda: self._bot.sendAudio(chat_id=self._chat_id, **json_dict))
@flaky(3, 1)
@timeout(10)
@@ -209,9 +209,9 @@ class AudioTest(BaseTest, unittest.TestCase):
del (json_dict['file_id'])
json_dict['audio'] = ''
self.assertRaises(telegram.TelegramError,
lambda: self._bot.sendAudio(chat_id=self._chat_id,
**json_dict))
self.assertRaises(
telegram.TelegramError,
lambda: self._bot.sendAudio(chat_id=self._chat_id, **json_dict))
@flaky(3, 1)
@timeout(10)
@@ -221,9 +221,9 @@ class AudioTest(BaseTest, unittest.TestCase):
del (json_dict['file_id'])
del (json_dict['duration'])
self.assertRaises(TypeError,
lambda: self._bot.sendAudio(chat_id=self._chat_id,
**json_dict))
self.assertRaises(
TypeError,
lambda: self._bot.sendAudio(chat_id=self._chat_id, **json_dict))
if __name__ == '__main__':
+22 -33
View File
@@ -19,9 +19,9 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents Tests for Telegram Bot"""
import sys
from datetime import datetime
import io
from datetime import datetime
import sys
from flaky import flaky
@@ -54,26 +54,22 @@ class BotTest(BaseTest, unittest.TestCase):
@flaky(3, 1)
@timeout(10)
def testSendMessage(self):
message = self._bot.sendMessage(
chat_id=self._chat_id,
text='Моё судно на воздушной подушке полно угрей')
message = self._bot.sendMessage(chat_id=self._chat_id,
text='Моё судно на воздушной подушке полно угрей')
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.text,
u'Моё судно на воздушной подушке полно угрей')
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)
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.assertEqual(message.text, u'Моё судно на воздушной подушке полно угрей')
self.assertTrue(isinstance(message.date, datetime))
@flaky(3, 1)
@@ -100,10 +96,9 @@ class BotTest(BaseTest, unittest.TestCase):
@flaky(3, 1)
@timeout(10)
def testSendPhoto(self):
message = self._bot.sendPhoto(
photo=open('tests/data/telegram.png', 'rb'),
caption='testSendPhoto',
chat_id=self._chat_id)
message = self._bot.sendPhoto(photo=open('tests/data/telegram.png', 'rb'),
caption='testSendPhoto',
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.photo[0].file_size, 1451)
@@ -112,11 +107,10 @@ class BotTest(BaseTest, unittest.TestCase):
@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)
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)
@@ -130,9 +124,8 @@ class BotTest(BaseTest, unittest.TestCase):
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(
message.photo[0].file_id,
'AgADAQADyKcxGx8j9Qdp6d-gpUsw4Gja1i8ABEVJsVqQk8LfJ3wAAgI')
self.assertEqual(message.photo[0].file_id,
'AgADAQADyKcxGx8j9Qdp6d-gpUsw4Gja1i8ABEVJsVqQk8LfJ3wAAgI')
@flaky(3, 1)
@timeout(10)
@@ -177,8 +170,7 @@ class BotTest(BaseTest, unittest.TestCase):
@flaky(3, 1)
@timeout(10)
def testSendChatAction(self):
self._bot.sendChatAction(action=telegram.ChatAction.TYPING,
chat_id=self._chat_id)
self._bot.sendChatAction(action=telegram.ChatAction.TYPING, chat_id=self._chat_id)
@flaky(3, 1)
@timeout(10)
@@ -189,8 +181,7 @@ class BotTest(BaseTest, unittest.TestCase):
self.assertEqual(upf.photos[0][0].file_size, 12421)
def _test_invalid_token(self, token):
self.assertRaisesRegexp(telegram.error.InvalidToken, 'Invalid token',
telegram.Bot, token)
self.assertRaisesRegexp(telegram.error.InvalidToken, 'Invalid token', telegram.Bot, token)
def testInvalidToken1(self):
self._test_invalid_token('123')
@@ -202,14 +193,12 @@ class BotTest(BaseTest, unittest.TestCase):
self._test_invalid_token('12:')
def testUnauthToken(self):
with self.assertRaisesRegexp(telegram.error.Unauthorized,
'Unauthorized'):
with self.assertRaisesRegexp(telegram.error.Unauthorized, 'Unauthorized'):
bot = telegram.Bot('1234:abcd1234')
bot.getMe()
def testInvalidSrvResp(self):
with self.assertRaisesRegexp(telegram.TelegramError,
'Invalid server response'):
with self.assertRaisesRegexp(telegram.TelegramError, 'Invalid server response'):
# bypass the valid token check
bot = telegram.Bot.__new__(telegram.Bot)
bot.base_url = 'https://api.telegram.org/bot{0}'.format('12')
+3 -8
View File
@@ -1,4 +1,4 @@
# !/usr/bin/env python
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
@@ -18,9 +18,8 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents Tests for Telegram Chat"""
import sys
import unittest
import sys
sys.path.append('.')
import telegram
@@ -35,11 +34,7 @@ class ChatTest(BaseTest, unittest.TestCase):
self.title = 'ToledosPalaceBot - Group'
self.type = 'group'
self.json_dict = {
'id': self.id,
'title': self.title,
'type': self.type
}
self.json_dict = {'id': self.id, 'title': self.title, 'type': self.type}
def test_group_chat_de_json_empty_json(self):
group_chat = telegram.Chat.de_json({})
+1 -2
View File
@@ -52,8 +52,7 @@ class ChosenInlineResultTest(BaseTest, unittest.TestCase):
result = telegram.ChosenInlineResult.de_json(self.json_dict)
self.assertEqual(result.result_id, self.result_id)
self.assertDictEqual(result.from_user.to_dict(),
self.from_user.to_dict())
self.assertDictEqual(result.from_user.to_dict(), self.from_user.to_dict())
self.assertEqual(result.query, self.query)
def test_choseninlineresult_to_json(self):

Some files were not shown because too many files have changed in this diff Show More