Compare commits

...

60 Commits

Author SHA1 Message Date
Jannes Höke 41432f5b02 bump version to v4.2.0 2016-05-28 22:49:15 +02:00
Jannes Höke f737ab780a Merge pull request #314 from python-telegram-bot/docs-restructuring
Move large parts from README to Wiki
2016-05-28 22:44:17 +02:00
Rahiel Kasim e0e8f6b085 README: update docs badge link, https some links 2016-05-28 22:40:18 +02:00
Jannes Höke 17abf0274e Fix formatting 2016-05-28 21:52:03 +02:00
Jannes Höke 8fab7ad302 move stuff to wiki, other stuff 2016-05-28 21:44:10 +02:00
Rahiel Kasim f31bd91673 Merge pull request #305 from python-telegram-bot/move-botan
move botan from utils to contrib
2016-05-28 21:32:07 +02:00
Leandro Toledo 32ac617e2e Merge pull request #312 from python-telegram-bot/timeout_take2
set default network_delay to 5 seconds
2016-05-28 15:14:54 -03:00
Noam Meltzer 7e7acdeb23 set default network_delay to 5 seconds
fixes #309
2016-05-28 19:34:16 +03:00
Leandro Toledo 792ad62fe8 Merge pull request #308 from python-telegram-bot/bot2.1
New methods for Bot 2.1 API
2016-05-28 12:14:01 -03:00
Jannes Höke 25bcfa9b35 add constants for Chat.type and ChatMember.status 2016-05-28 16:51:44 +02:00
Jannes Höke ff00e211d7 include in warning that Py2.7 will still be supported 2016-05-28 16:44:39 +02:00
Rahiel Kasim b06983a94a let python 2 find the contrib module 2016-05-28 09:27:17 +02:00
Rahiel Kasim 2f408ab767 generate docs for botan 2016-05-28 09:18:36 +02:00
Rahiel Kasim c8497424b7 move botan to contrib 2016-05-28 09:12:10 +02:00
Leandro Toledo 80fbe98b44 Reflecting tests for Filters change 2016-05-27 21:14:55 -03:00
Leandro Toledo 22e9326610 Merge remote-tracking branch 'origin/master' into bot2.1 2016-05-27 21:10:24 -03:00
Jannes Höke a0bb5730c6 add allow_edited parameter to MessageHandler and CommandHandler 2016-05-27 11:07:06 +02:00
Jannes Höke 748cc3a35f Add isitmaintained.com issue resolving time badge 2016-05-26 23:30:29 +02:00
Leandro Toledo 9a13de4a96 Merge remote-tracking branch 'origin/master' into bot2.1
Conflicts:
	telegram/bot.py
	tests/test_bot.py
2016-05-26 16:15:50 -03:00
Noam Meltzer 561f1c3f02 bot: validate token does not contain white spaces (#306)
in addition move validation code from validate.py into bot.py and delete
the former file
2016-05-26 22:09:14 +03:00
Leandro Toledo 3907e64966 Adds telegram.utils.botan back using deprecate 2016-05-26 14:13:27 -03:00
Rahiel Kasim 1abbca3324 bot.py: fix snake_case alias 2016-05-26 15:32:02 +02:00
Leandro Toledo 86571bc75d addHandler to add_handler 2016-05-26 01:55:51 -03:00
Leandro Toledo 663fa0013d merge master 2016-05-25 22:09:18 -03:00
Leandro Toledo 37c7af2e14 Add docstrings #302 2016-05-25 21:41:12 -03:00
Leandro Toledo e70625772c Keeps backwards compatibility to BadRequest #302 2016-05-25 21:24:29 -03:00
Leandro Toledo 1e398821a0 Introducing telegram.error.BadRequest and testLeaveChat testcase #302 2016-05-25 21:15:17 -03:00
Rahiel Kasim 386b91f708 refactor testing if user is bot 2016-05-25 09:41:48 +02:00
Rahiel Kasim 76b9a7d328 add tests for Bot.getChat* 2016-05-25 01:31:10 +02:00
Leandro Toledo d90b0f495d End the madness when bumping versions [ci skip] 2016-05-24 19:43:24 -03:00
Rahiel Kasim c4d5eff9f3 move botan from utils to ext 2016-05-24 23:40:09 +02:00
Rahiel Kasim f45ecee820 Merge pull request #304 from TiagoDanin/FixReadme
README Update url for contribution guidelines
2016-05-24 23:09:13 +02:00
Tiago Danin c340585f33 README Update url for contribution guidelines 2016-05-24 15:49:43 -05:00
Leandro Toledo 75490ac757 Fix travis 2016-05-23 22:13:38 -03:00
Leandro Toledo 046e69b1c1 Commenting test for token with newline 2016-05-23 22:02:15 -03:00
Leandro Toledo 7e0be09c58 Merge remote-tracking branch 'origin/master' into bot2.1 2016-05-23 21:56:01 -03:00
Leandro Toledo 18f3f43026 Add issue template #298 2016-05-23 21:54:36 -03:00
Leandro Toledo 0b2fd120d8 Due kwargs I had to change the factory class in favor of InputLocationMessageContent #302 2016-05-23 21:09:07 -03:00
Leandro Toledo 408959e91c Removes examples from tests 2016-05-23 20:44:47 -03:00
Leandro Toledo ab2f6e13c9 Add kwargs to API calls #302 2016-05-23 20:43:17 -03:00
Leandro Toledo 108e4264fc Add user to MessageEntity #302 2016-05-23 20:31:36 -03:00
Leandro Toledo 68b5562c49 Add edit_date to Message #302 2016-05-23 20:28:36 -03:00
Leandro Toledo e50a3622e1 Add edited_message to Update #302 2016-05-23 20:24:43 -03:00
Leandro Toledo d7e226ec0f Add new Bot methods and ChatMember class #302 2016-05-23 20:22:31 -03:00
Leandro Toledo 7c84516d2b Merge branch 'master' of github.com:python-telegram-bot/python-telegram-bot 2016-05-23 19:44:34 -03:00
Leandro Toledo 53a91be21f Update CONTRIBUTING.rst 2016-05-23 19:05:56 -03:00
Leandro Toledo 2471eaa778 Adding some style commandments 2016-05-23 19:05:04 -03:00
leandrotoledo 6bfdff8892 Update examples to column width to 99 [ci skip] 2016-05-23 17:45:01 -03:00
leandrotoledo 2389b07382 Updates pre-commit hooks to check examples folder [ci skip] 2016-05-23 17:23:57 -03:00
leandrotoledo c7db9a96cd Set split_before_logical_operator to True 2016-05-23 17:19:35 -03:00
Leandro Toledo af89cbecf3 Add test for unstripped tokens [ci skip] 2016-05-22 18:22:22 -03:00
leandrotoledo b987c8937c Hooks for travis 2016-05-22 13:26:57 -03:00
leandrotoledo 5a0696b181 Replace individual checks to pre-commit run --all-files in Travis 2016-05-22 13:12:05 -03:00
leandrotoledo eb303903ef Fix travis 2016-05-22 12:55:48 -03:00
leandrotoledo a00f409992 Updates pre-commit hooks 2016-05-22 12:31:03 -03:00
Jannes Höke dc27ff41ef bump version to 4.1.2 2016-05-22 13:01:14 +02:00
Jannes Höke 68ec73afb6 use kwargs on messageentity 2016-05-22 12:58:19 +02:00
Rahiel Kasim 0ace0aa016 README: remove "Getting the Code" section, confuses users like #297 2016-05-19 17:06:28 +02:00
Jannes Höke f5847be8ca update file size from 684 to 685 2016-05-17 10:19:00 +02:00
Jannes Höke ba26a8ba5d use command filter instead of regexhandler #292 2016-05-17 07:36:04 +02:00
53 changed files with 1040 additions and 904 deletions
+186 -140
View File
@@ -1,140 +1,186 @@
How To Contribute
=================
Every open source project lives from the generous help by contributors that sacrifice their time and ``python-telegram-bot`` is no different. To make participation as pleasant as possible, this project adheres to the `Code of Conduct`_ by the Python Software Foundation.
Setting things up
-----------------
1. Fork the ``python-telegram-bot`` repository to your GitHub account.
2. Clone your forked repository of ``python-telegram-bot`` to your computer:
``$ git clone https://github.com/<your username>/python-telegram-bot``
``$ cd python-telegram-bot``
3. Add a track to the original repository:
``$ git remote add upstream https://github.com/python-telegram-bot/python-telegram-bot``
4. Install dependencies:
``$ pip install -r requirements.txt -r requirements-dev.txt``
5. Install pre-commit hooks:
``$ pre-commit install``
Finding something to do
-----------------------
If you already know what you'd like to work on, you can skip this section.
If you have an idea for something to do, first check if it's already been filed on the `issue tracker`_. If so, add a comment to the issue saying you'd like to work on it, and we'll help you get started! Otherwise, please file a new issue and assign yourself to it.
Another great way to start contributing is by writing tests. Tests are really important because they help prevent developers from accidentally breaking existing code, allowing them to build cool things faster. If you're interested in helping out, let the development team know by posting to the `developers' mailing list`_, and we'll help you get started.
Instructions for making a code change
-------------------------------------
The central development branch is ``master``, which should be clean and ready for release at any time. In general, all changes should be done as feature branches based off of ``master``.
Here's how to make a one-off code change.
1. **Choose a descriptive branch name.** It should be lowercase, hyphen-separated, and a noun describing the change (so, ``fuzzy-rules``, but not ``implement-fuzzy-rules``). Also, it shouldn't start with ``hotfix`` or ``release``.
2. **Create a new branch with this name, starting from** ``master``. In other words, run:
``$ git fetch upstream``
``$ git checkout master``
``$ git merge upstream/master``
``$ git checkout -b your-branch-name``
3. **Make a commit to your feature branch**. Each commit should be self-contained and have a descriptive commit message that helps other developers understand why the changes were made.
- 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:
- Documenting types of global variables and complex types of class members can be done using the Sphinx docstring convention.
- Please ensure that the code you write is well-tested.
- Dont break backward compatibility.
- Add yourself to the AUTHORS.rst_ file in an alphabetical fashion.
- Before making a commit ensure that all automated tests still pass:
``$ make test``
- To actually make the commit (this will trigger tests for yapf, lint and pep8 automatically):
``$ git add your-file-changed.py``
- 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.**
- Go to your fork on GitHub, select your branch from the dropdown menu, and click "New pull request".
- Add a descriptive comment explaining the purpose of the branch (e.g. "Add the new API feature to create inline bot queries."). This will tell the reviewer what the purpose of the branch is.
- Click "Create pull request". An admin will assign a reviewer to your commit.
5. **Address review comments until all reviewers give LGTM ('looks good to me').**
- When your reviewer has reviewed the code, you'll get an email. You'll need to respond in two ways:
- Make a new commit addressing the comments you agree with, and push it to the same branch. Ideally, the commit message would explain what the commit does (e.g. "Fix lint error"), but if there are lots of disparate review comments, it's fine to refer to the original commit message and add something like "(address review comments)".
- In addition, please reply to each comment. Each reply should be either "Done" or a response explaining why the corresponding suggestion wasn't implemented. All comments must be resolved before LGTM can be given.
- Resolve any merge conflicts that arise. To resolve conflicts between 'your-branch-name' (in your fork) and 'master' (in the ``python-telegram-bot`` repository), run:
``$ git checkout your-branch-name``
``$ git fetch upstream``
``$ git merge upstream/master``
``$ ...[fix the conflicts]...``
``$ ...[make sure the tests pass before committing]...``
``$ git commit -a``
``$ git push origin your-branch-name``
- At the end, the reviewer will merge the pull request.
6. **Tidy up!** Delete the feature branch from both your local clone and the GitHub repository:
``$ git branch -D your-branch-name``
``$ git push origin --delete your-branch-name``
7. **Celebrate.** Congratulations, you have contributed to ``python-telegram-bot``!
.. _`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
How To Contribute
=================
Every open source project lives from the generous help by contributors that sacrifice their time and ``python-telegram-bot`` is no different. To make participation as pleasant as possible, this project adheres to the `Code of Conduct`_ by the Python Software Foundation.
Setting things up
-----------------
1. Fork the ``python-telegram-bot`` repository to your GitHub account.
2. Clone your forked repository of ``python-telegram-bot`` to your computer:
``$ git clone https://github.com/<your username>/python-telegram-bot``
``$ cd python-telegram-bot``
3. Add a track to the original repository:
``$ git remote add upstream https://github.com/python-telegram-bot/python-telegram-bot``
4. Install dependencies:
``$ pip install -r requirements.txt -r requirements-dev.txt``
5. Install pre-commit hooks:
``$ pre-commit install``
Finding something to do
-----------------------
If you already know what you'd like to work on, you can skip this section.
If you have an idea for something to do, first check if it's already been filed on the `issue tracker`_. If so, add a comment to the issue saying you'd like to work on it, and we'll help you get started! Otherwise, please file a new issue and assign yourself to it.
Another great way to start contributing is by writing tests. Tests are really important because they help prevent developers from accidentally breaking existing code, allowing them to build cool things faster. If you're interested in helping out, let the development team know by posting to the `developers' mailing list`_, and we'll help you get started.
Instructions for making a code change
-------------------------------------
The central development branch is ``master``, which should be clean and ready for release at any time. In general, all changes should be done as feature branches based off of ``master``.
Here's how to make a one-off code change.
1. **Choose a descriptive branch name.** It should be lowercase, hyphen-separated, and a noun describing the change (so, ``fuzzy-rules``, but not ``implement-fuzzy-rules``). Also, it shouldn't start with ``hotfix`` or ``release``.
2. **Create a new branch with this name, starting from** ``master``. In other words, run:
``$ git fetch upstream``
``$ git checkout master``
``$ git merge upstream/master``
``$ git checkout -b your-branch-name``
3. **Make a commit to your feature branch**. Each commit should be self-contained and have a descriptive commit message that helps other developers understand why the changes were made.
- 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:
- Documenting types of global variables and complex types of class members can be done using the Sphinx docstring convention.
- Please ensure that the code you write is well-tested.
- Dont break backward compatibility.
- Add yourself to the AUTHORS.rst_ file in an alphabetical fashion.
- Before making a commit ensure that all automated tests still pass:
``$ make test``
- To actually make the commit (this will trigger tests for yapf, lint and pep8 automatically):
``$ git add your-file-changed.py``
- 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.**
- Go to your fork on GitHub, select your branch from the dropdown menu, and click "New pull request".
- Add a descriptive comment explaining the purpose of the branch (e.g. "Add the new API feature to create inline bot queries."). This will tell the reviewer what the purpose of the branch is.
- Click "Create pull request". An admin will assign a reviewer to your commit.
5. **Address review comments until all reviewers give LGTM ('looks good to me').**
- When your reviewer has reviewed the code, you'll get an email. You'll need to respond in two ways:
- Make a new commit addressing the comments you agree with, and push it to the same branch. Ideally, the commit message would explain what the commit does (e.g. "Fix lint error"), but if there are lots of disparate review comments, it's fine to refer to the original commit message and add something like "(address review comments)".
- In addition, please reply to each comment. Each reply should be either "Done" or a response explaining why the corresponding suggestion wasn't implemented. All comments must be resolved before LGTM can be given.
- Resolve any merge conflicts that arise. To resolve conflicts between 'your-branch-name' (in your fork) and 'master' (in the ``python-telegram-bot`` repository), run:
``$ git checkout your-branch-name``
``$ git fetch upstream``
``$ git merge upstream/master``
``$ ...[fix the conflicts]...``
``$ ...[make sure the tests pass before committing]...``
``$ git commit -a``
``$ git push origin your-branch-name``
- At the end, the reviewer will merge the pull request.
6. **Tidy up!** Delete the feature branch from both your local clone and the GitHub repository:
``$ git branch -D your-branch-name``
``$ git push origin --delete your-branch-name``
7. **Celebrate.** Congratulations, you have contributed to ``python-telegram-bot``!
Style commandments
==================
Specific commandments
---------------------
- Avoid using "double quotes" where you can reasonably use 'single quotes'.
AssertEqual argument order
--------------------------
assertEqual method's arguments should be in ('actual', 'expected') order.
Properly calling callables
--------------------------
Methods, functions and classes can specify optional parameters (with default
values) using Python's keyword arg syntax. When providing a value to such a
callable we prefer that the call also uses keyword arg syntax. For example::
# GOOD
f(0, optional=True)
# BAD
f(0, True)
This gives us the flexibility to re-order arguments and more importantly
to add new required arguments. It's also more explicit and easier to read.
Properly defining optional arguments
------------------------------------
It's always good to not initialize optional arguments at class creation,
instead use ``**kwargs`` to get them. It's well known Telegram API can
change without notice, in that case if a new argument is added it won't
break the API classes. For example::
# GOOD
def __init__(self, id, name, **kwargs):
self.last_name = kwargs.get('last_name', '')
# BAD
def __init__(self, id, name, last_name=''):
self.last_name = last_name
.. _`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
+31
View File
@@ -0,0 +1,31 @@
<!--
Thanks for reporting issues of python-telegram-bot!
To make it easier for us to help you please enter detailed information below.
-->
### Steps to reproduce
1.
2.
3.
### Expected behaviour
Tell us what should happen
### Actual behaviour
Tell us what happens instead
### Configuration
**Operating System:**
**Version of Python:**
``$ python -V``
**Version of python-telegram-bot:**
``$ python -c 'import telegram; print(telegram.__version__)'``
### Logs
Insert logs here (if necessary)
+9 -8
View File
@@ -1,17 +1,18 @@
- repo: git://github.com/pre-commit/mirrors-yapf
sha: 'v0.7.1'
sha: 316b795b2f32cbe80047aff7e842b72368d5a2c1
hooks:
- id: yapf
args: ['-i']
files: ^(telegram|tests)/.*\.py$
- repo: git://github.com/pre-commit/pre-commit-hooks
sha: 'v0.5.0'
sha: adbb569fe9a64ad9bce3b53a77f1bc39ef31f682
hooks:
- id: flake8
args: ['telegram']
files: ^telegram/.*\.py$
- repo: git://github.com/pre-commit/mirrors-pylint
sha: 'v1.5.5'
sha: 4de6c8dfadef1a271a814561ce05b8bc1c446d22
hooks:
- id: pylint
args: ['--errors-only', '--disable=no-name-in-module,import-error', 'telegram']
files: ^telegram/.*\.py$
args:
- --errors-only
- --disable=no-name-in-module,import-error
+1 -3
View File
@@ -13,8 +13,6 @@ 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 ] && [ $TRAVIS_PYTHON_VERSION != 3.3 ] && [ $TRAVIS_PYTHON_VERSION != pypy3 ]; then pre-commit run --all-files; fi'
after_success:
coveralls
+14
View File
@@ -1,3 +1,17 @@
**2016-05-28**
*Released 4.2*
- Implement Bot API 2.1
- Move ``botan`` module to ``telegram.contrib``
- New exception type: ``BadRequest``
**2016-05-22**
*Released 4.1.2*
- Fix ``MessageEntity`` decoding with Bot API 2.1 changes
**2016-05-16**
*Released 4.1.1*
+70 -377
View File
@@ -15,8 +15,8 @@ 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://readthedocs.org/projects/python-telegram-bot/badge/?version=latest
:target: https://readthedocs.org/projects/python-telegram-bot/?badge=latest
.. image:: https://img.shields.io/badge/docs-latest-af1a97.svg
:target: https://pythonhosted.org/python-telegram-bot/
:alt: Documentation Status
.. image:: https://img.shields.io/pypi/l/python-telegram-bot.svg
@@ -34,6 +34,10 @@ Not **just** a Python wrapper around the Telegram Bot API
.. image:: https://coveralls.io/repos/python-telegram-bot/python-telegram-bot/badge.svg?branch=master&service=github
:target: https://coveralls.io/github/python-telegram-bot/python-telegram-bot?branch=master
:alt: Coveralls
.. image:: http://isitmaintained.com/badge/resolution/python-telegram-bot/python-telegram-bot.svg
:target: http://isitmaintained.com/project/python-telegram-bot/python-telegram-bot
:alt: Average time to resolve an issue
.. image:: https://img.shields.io/badge/Telegram-Group-blue.svg
:target: https://telegram.me/pythontelegrambotgroup
@@ -49,18 +53,10 @@ Table of contents
- `Installing`_
- `Getting the code`_
- `Getting started`_
#. `Learning by example`_
#. `API`_
#. `Extensions`_
#. `JobQueue`_
#. `Logging`_
#. `Documentation`_
@@ -75,38 +71,21 @@ Table of contents
_`Introduction`
===============
This library provides a pure Python interface for the `Telegram Bot API <https://core.telegram.org/bots/api>`_. It works with Python versions from 2.6+. It also works with `Google App Engine <https://cloud.google.com/appengine>`_.
This library provides a pure Python interface for the
`Telegram Bot API <https://core.telegram.org/bots/api>`_.
It works with Python versions from 2.6+ (**Note:** Support for 2.6 will be dropped at some point
this year. 2.7 will still be supported).
It also works with `Google App Engine <https://cloud.google.com/appengine>`_.
In addition to the pure API implementation, this library features a number of high-level classes to
make the development of bots easy and straightforward. These classes are contained in the
``telegram.ext`` submodule.
=======================
_`Telegram API support`
=======================
========================= ============
Telegram Bot API Method *Supported?*
========================= ============
getMe Yes
sendMessage Yes
forwardMessage Yes
sendPhoto Yes
sendAudio Yes
sendDocument Yes
sendSticker Yes
sendVideo Yes
sendVoice Yes
sendLocation Yes
sendChatAction Yes
getUpdates Yes
getUserProfilePhotos Yes
getFile Yes
setWebhook Yes
answerInlineQuery Yes
kickChatMember Yes
unbanChatMember Yes
answerCallbackQuery Yes
editMessageText Yes
editMessageCaption Yes
editMessageReplyMarkup Yes
========================= ============
As of **28. May 2016**, all types and methods of the Telegram Bot API are supported.
=============
_`Installing`
@@ -118,44 +97,53 @@ You can install or upgrade python-telegram-bot with:
$ pip install python-telegram-bot --upgrade
===================
_`Getting the code`
===================
The code is hosted at https://github.com/python-telegram-bot/python-telegram-bot
Check out the latest development version anonymously with:
.. code:: shell
$ git clone https://github.com/python-telegram-bot/python-telegram-bot
$ cd python-telegram-bot
Install dependencies:
.. code:: shell
$ pip install -r requirements.txt -r requirements-dev.txt
Run tests:
.. code:: shell
$ make test
To see other available options, run:
.. code:: shell
$ make help
==================
_`Getting started`
==================
View the last release API documentation at: https://core.telegram.org/bots/api
Our Wiki contains a lot of resources to get you started with ``python-telegram-bot``:
This library uses the `logging` module. To set up logging to standard output, put:
- `Introduction to the API <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Introduction-to-the-API>`_
- Tutorial: `Your first Bot <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-Your-first-Bot>`_
Other references:
- `Telegram API documentation <https://core.telegram.org/bots/api>`_
- `python-telegram-bot documentation <https://pythonhosted.org/python-telegram-bot/>`_
----------------------
_`Learning by example`
----------------------
We believe that the best way to learn and understand this simple package is by example. So here
are some examples for you to review. Even if it's not your approach for learning, please take a
look at ``echobot2`` (below), it is de facto the base for most of the bots out there. Best of all,
the code for these examples are released to the public domain, so you can start by grabbing the
code and building on top of it.
- `echobot2 <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot2.py>`_ replies back messages.
- `inlinebot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinebot.py>`_ basic example of an `inline bot <https://core.telegram.org/bots/inline>`_.
- `state machine bot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/state_machine_bot.py>`_ keeps the state for individual users, useful for multipart conversations.
- `timerbot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/timerbot.py>`_ uses the ``JobQueue`` to send timed messages.
- `clibot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/clibot.py>`_ has a command line interface.
Examples using only the pure API:
- `echobot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/legacy/echobot.py>`_ replies back messages.
- `roboed <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/legacy/roboed.py>`_ talks to `Robô Ed <http://www.ed.conpet.gov.br/br/converse.php>`_.
Look at the examples on the `wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Examples>`_ to see other bots the community has built.
----------
_`Logging`
----------
This library uses the ``logging`` module. To set up logging to standard output, put:
.. code:: python
@@ -165,322 +153,25 @@ This library uses the `logging` module. To set up logging to standard output, pu
at the beginning of your script.
**Note:** The ``telegram.ext`` module will catch errors that would cause the bot to crash. All these are logged to the ``logging`` module, so it's recommended to use this if you are looking for error causes.
----------------------
_`Learning by example`
----------------------
We believe that the best way to learn and understand this simple package is by example. So here are some examples for you to review. Even if it's not your approach for learning, please take a look at ``echobot2`` (below), it is de facto the base for most of the bots out there. Best of all, the code for these examples are released to the public domain, so you can start by grabbing the code and building on top of it.
- `clibot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/clibot.py>`_ has a command line interface.
- `echobot2 <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot2.py>`_ replies back messages.
- `inlinebot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinebot.py>`_ basic example of an `inline bot <https://core.telegram.org/bots/inline>`_
- `state machine bot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/state_machine_bot.py>`_ keeps the state for individual users, useful for multipart conversations
- `timerbot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/timerbot.py>`_ uses the ``JobQueue`` to send timed messages.
Examples using only the API:
- `echobot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/legacy/echobot.py>`_ replies back messages.
- `roboed <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/legacy/roboed.py>`_ talks to `Robô Ed <http://www.ed.conpet.gov.br/br/converse.php>`_.
Look at the examples on the `wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Examples>`_ to see other bots the community has built.
------
_`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 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>`_).
For full details see the `Bots: An introduction for developers <https://core.telegram.org/bots>`_.
To create an instance of the ``telegram.Bot``:
You can also use logs in your application by calling ``logging.getLogger()`` and setting the log level you want:
.. code:: python
>>> import telegram
>>> bot = telegram.Bot(token='token')
To see if your credentials are successful:
.. code:: python
>>> print(bot.getMe())
{"first_name": "Toledo's Palace Bot", "username": "ToledosPalaceBot"}
Bots can't initiate conversations with users. A user must either add them to a group or send them a message first. People can use ``telegram.me/<bot_username>`` links or username search to find your bot.
To fetch text messages sent to your Bot:
.. code:: python
>>> updates = bot.getUpdates()
>>> print([u.message.text for u in updates])
To fetch images sent to your Bot:
.. code:: python
>>> updates = bot.getUpdates()
>>> print([u.message.photo for u in updates if u.message.photo])
To reply messages you'll always need the ``chat_id``:
.. code:: python
>>> chat_id = bot.getUpdates()[-1].message.chat_id
To post a text message:
.. code:: python
>>> bot.sendMessage(chat_id=chat_id, text="I'm sorry Dave I'm afraid I can't do that.")
To post a text message with markdown:
.. code:: python
>>> bot.sendMessage(chat_id=chat_id, text="*bold* _italic_ [link](http://google.com).", parse_mode=telegram.ParseMode.MARKDOWN)
To post a text message with Html style:
.. code:: python
>>> bot.sendMessage(chat_id=chat_id, text='<b>bold</b> <i>italic</i> <a href="http://google.com">link</a>.', parse_mode=telegram.ParseMode.HTML)
To post an Emoji (special thanks to `Tim Whitlock <http://apps.timwhitlock.info/emoji/tables/unicode>`_):
.. code:: python
>>> bot.sendMessage(chat_id=chat_id, text=telegram.Emoji.PILE_OF_POO)
To post an image file via URL:
.. code:: python
>>> bot.sendPhoto(chat_id=chat_id, photo='https://telegram.org/img/t_logo.png')
To post an image file from disk:
.. code:: python
>>> bot.sendPhoto(chat_id=chat_id, photo=open('tests/test.png', 'rb'))
To post a voice file from disk:
.. code:: python
>>> bot.sendVoice(chat_id=chat_id, voice=open('tests/telegram.ogg', 'rb'))
To tell the user that something is happening on bot's side:
.. code:: python
>>> bot.sendChatAction(chat_id=chat_id, action=telegram.ChatAction.TYPING)
To create `Custom Keyboards <https://core.telegram.org/bots#keyboards>`_:
.. code:: python
>>> custom_keyboard = [[ telegram.KeyboardButton(telegram.Emoji.THUMBS_UP_SIGN),
... telegram.KeyboardButton(telegram.Emoji.THUMBS_DOWN_SIGN) ]]
>>> reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard)
>>> bot.sendMessage(chat_id=chat_id, text="Stay here, I'll be back.", reply_markup=reply_markup)
To hide `Custom Keyboards <https://core.telegram.org/bots#keyboards>`_:
.. code:: python
>>> reply_markup = telegram.ReplyKeyboardHide()
>>> bot.sendMessage(chat_id=chat_id, text="I'm back.", reply_markup=reply_markup)
To download a file (you will need its ``file_id``):
.. code:: python
>>> file_id = message.voice.file_id
>>> newFile = bot.getFile(file_id)
>>> newFile.download('voice.ogg')
There are many more API methods, to read the full API documentation:
.. code:: shell
$ pydoc telegram.Bot
-------------
_`Extensions`
-------------
The ``telegram.ext`` submodule is built on top of the bare-metal API. It provides an easy-to-use interface to the ``telegram.Bot`` by caring about getting new updates with the ``Updater`` class from telegram and forwarding them to the ``Dispatcher`` class. We can register handler functions in the ``Dispatcher`` to make our bot react to Telegram commands, messages and even arbitrary updates.
We'll need an Access Token. **Note:** If you have done this in the previous step, you can use that one. To generate an Access Token, we have to talk to `BotFather <https://telegram.me/botfather>`_ and follow a few simple steps (described `here <https://core.telegram.org/bots#botfather>`_).
First, we create an ``Updater`` object:
.. code:: python
>>> from telegram.ext import Updater
>>> updater = Updater(token='token')
For quicker access to the ``Dispatcher`` used by our ``Updater``, we can introduce it locally:
.. code:: python
>>> dispatcher = updater.dispatcher
Now, we need to define a function that should process a specific type of update:
.. code:: python
>>> def start(bot, update):
... bot.sendMessage(chat_id=update.message.chat_id, text="I'm a bot, please talk to me!")
We want this function to be called on a Telegram message that contains the ``/start`` command. To do that, we have to use a ``CommandHandler`` object and register it in the dispatcher:
.. code:: python
>>> from telegram.ext import CommandHandler
>>> start_handler = CommandHandler('start', start)
>>> dispatcher.add_handler(start_handler)
The last step is to tell the ``Updater`` to start working:
.. code:: python
>>> updater.start_polling()
Our bot is now up and running (go ahead and try it)! It's not doing anything yet, besides answering to the ``/start`` command. Let's add another handler that listens for regular messages. We're using the `MessageHandler` here to echo to all text messages:
.. code:: python
>>> def echo(bot, update):
... bot.sendMessage(chat_id=update.message.chat_id, text=update.message.text)
...
>>> from telegram.ext import MessageHandler, Filters
>>> echo_handler = MessageHandler([Filters.text], echo)
>>> 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.
Let's add some functionality to our bot. We want to add the ``/caps`` command, that will take some text as parameter and return it in all caps. We can get the arguments that were passed to a command in the handler function:
.. code:: python
>>> def caps(bot, update, args):
... text_caps = ' '.join(args).upper()
... bot.sendMessage(chat_id=update.message.chat_id, text=text_caps)
...
>>> caps_handler = CommandHandler('caps', caps, pass_args=True)
>>> 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):
.. code:: python
>>> from telegram import InlineQueryResultArticle
>>> def inline_caps(bot, update):
... query = bot.update.inline_query.query
... results = list()
... results.append(InlineQueryResultArticle(query.upper(), 'Caps', query.upper()))
... bot.answerInlineQuery(update.inline_query.id, results)
...
>>> from telegram.ext import InlineQueryHandler
>>> inline_caps_handler = InlineQueryHandler(inline_caps)
>>> 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:
.. code:: python
>>> def unknown(bot, update):
... bot.sendMessage(chat_id=update.message.chat_id, text="Sorry, I didn't understand that command.")
...
>>> from telegram.ext import RegexHandler
>>> unknown_handler = RegexHandler(r'/.*', unknown)
>>> dispatcher.add_handler(unknown_handler)
If you're done playing around, stop the bot with this:
.. code:: python
>>> updater.stop()
Check out more examples in the `examples folder <https://github.com/python-telegram-bot/python-telegram-bot/tree/master/examples>`_!
-----------
_`JobQueue`
-----------
The ``JobQueue`` allows you to perform tasks with a delay or even periodically. The ``Updater`` will create one for you:
.. code:: python
>>> from telegram.ext import Updater
>>> u = Updater('TOKEN')
>>> j = u.job_queue
The job queue uses functions for tasks, so we define one and add it to the queue. Usually, when the first job is added to the queue, it wil start automatically. We can prevent this by setting ``prevent_autostart=True``:
.. code:: python
>>> def job1(bot):
... bot.sendMessage(chat_id='@examplechannel', text='One message every minute')
>>> j.put(job1, 60, next_t=0, prevent_autostart=True)
You can also have a job that will not be executed repeatedly:
.. code:: python
>>> def job2(bot):
... bot.sendMessage(chat_id='@examplechannel', text='A single message with 30s delay')
>>> j.put(job2, 30, repeat=False)
Now, because we didn't prevent the auto start this time, the queue will start ticking. It runs in a seperate thread, so it is non-blocking. When we stop the Updater, the related queue will be stopped as well:
.. code:: python
>>> u.stop()
We can also stop the job queue by itself:
.. code:: python
>>> j.stop()
----------
_`Logging`
----------
You can get logs in your main application by calling `logging` and setting the log level you want:
.. code:: python
>>> import logging
>>> logger = logging.getLogger()
>>> logger.setLevel(logging.INFO)
logger = logging.getLogger()
logger.setLevel(logging.INFO)
If you want DEBUG logs instead:
.. code:: python
>>> logger.setLevel(logging.DEBUG)
logger.setLevel(logging.DEBUG)
================
_`Documentation`
================
``python-telegram-bot``'s documentation lives at `Read the Docs <https://python-telegram-bot.readthedocs.org/en/latest/>`_.
``python-telegram-bot``'s documentation lives at `pythonhosted.org <https://pythonhosted.org/python-telegram-bot/>`_.
===============
_`Getting help`
@@ -489,17 +180,19 @@ _`Getting help`
You can get help in several ways:
1. We have a vibrant community of developers helping each other in our `Telegram group <https://telegram.me/pythontelegrambotgroup>`_. Join us!
2. You can ask for help on Stack Overflow using the `python-telegram-bot tag <https://stackoverflow.com/questions/tagged/python-telegram-bot>`_.
3. As last resort, the developers are ready to help you with `serious issues <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_.
2. Our `Wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki/>`_ offers a growing amount of resources.
3. You can ask for help on Stack Overflow using the `python-telegram-bot tag <https://stackoverflow.com/questions/tagged/python-telegram-bot>`_.
4. As last resort, the developers are ready to help you with `serious issues <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_.
===============
_`Contributing`
===============
Contributions of all sizes are welcome. Please review our `contribution guidelines <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/CONTRIBUTING.rst>`_ to get started. You can also help by `reporting bugs <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_.
Contributions of all sizes are welcome. Please review our `contribution guidelines <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/.github/CONTRIBUTING.rst>`_ to get started. You can also help by `reporting bugs <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_.
==========
_`License`
+4 -3
View File
@@ -15,6 +15,7 @@
import sys
import os
import shlex
import telegram
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
@@ -58,9 +59,9 @@ author = u'Leandro Toledo'
# built documents.
#
# The short X.Y version.
version = '4.1'
version = telegram.__version__[:3]
# The full version, including alpha/beta/rc tags.
release = '4.1.1'
release = telegram.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -270,7 +271,7 @@ man_pages = [
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'PythonTelegramBot', u'Python Telegram Bot Documentation',
author, 'PythonTelegramBot', 'One line description of project.',
author, 'PythonTelegramBot', 'Not just a Python wrapper around the Telegram Bot API',
'Miscellaneous'),
]
+7
View File
@@ -0,0 +1,7 @@
telegram.contrib.botan module
=============================
.. automodule:: telegram.contrib.botan
:members:
:undoc-members:
:show-inheritance:
+6 -12
View File
@@ -3,7 +3,6 @@
#
# Example Bot to show some of the functionality of the library
# This program is dedicated to the public domain under the CC0 license.
"""
This Bot uses the Updater class to handle the bot.
@@ -27,9 +26,8 @@ import logging
from future.builtins import input
# Enable Logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
@@ -58,9 +56,7 @@ def any_message(bot, update):
last_chat_id = update.message.chat_id
logger.info("New message\nFrom: %s\nchat_id: %d\nText: %s" %
(update.message.from_user,
update.message.chat_id,
update.message.text))
(update.message.from_user, update.message.chat_id, update.message.text))
@run_async
@@ -72,8 +68,7 @@ def message(bot, update):
"""
sleep(2) # IO-heavy operation here
bot.sendMessage(update.message.chat_id, text='Echo: %s' %
update.message.text)
bot.sendMessage(update.message.chat_id, text='Echo: %s' % update.message.text)
# These handlers are for updates of type str. We use them to react to inputs
@@ -126,8 +121,7 @@ def main():
# String handlers work pretty much the same. Note that we have to tell
# the handler to pass the args or update_queue parameter
dp.add_handler(StringCommandHandler('reply', cli_reply, pass_args=True))
dp.add_handler(StringRegexHandler('[^/].*', cli_noncommand,
pass_update_queue=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.
@@ -135,7 +129,6 @@ def main():
# Start the Bot and store the update Queue, so we can insert updates
update_queue = updater.start_polling(timeout=10)
'''
# Alternatively, run with webhook:
@@ -166,5 +159,6 @@ def main():
elif len(text) > 0:
update_queue.put(text)
if __name__ == '__main__':
main()
+3 -4
View File
@@ -3,7 +3,6 @@
#
# Simple Bot to reply to Telegram messages
# This program is dedicated to the public domain under the CC0 license.
"""
This Bot uses the Updater class to handle the bot.
@@ -21,9 +20,8 @@ from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
import logging
# Enable logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
@@ -71,5 +69,6 @@ def main():
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
+17 -20
View File
@@ -3,7 +3,6 @@
#
# Simple Bot to reply to Telegram messages
# This program is dedicated to the public domain under the CC0 license.
"""
This Bot uses the Updater class to handle the bot.
@@ -26,9 +25,8 @@ from telegram.ext import Updater, InlineQueryHandler, CommandHandler
import logging
# Enable logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
@@ -53,24 +51,22 @@ def inlinequery(bot, update):
query = update.inline_query.query
results = list()
results.append(InlineQueryResultArticle(
id=uuid4(),
title="Caps",
input_message_content=InputTextMessageContent(query.upper())))
results.append(InlineQueryResultArticle(id=uuid4(),
title="Caps",
input_message_content=InputTextMessageContent(
query.upper())))
results.append(InlineQueryResultArticle(
id=uuid4(),
title="Bold",
input_message_content=InputTextMessageContent(
"*%s*" % escape_markdown(query),
parse_mode=ParseMode.MARKDOWN)))
results.append(InlineQueryResultArticle(id=uuid4(),
title="Bold",
input_message_content=InputTextMessageContent(
"*%s*" % escape_markdown(query),
parse_mode=ParseMode.MARKDOWN)))
results.append(InlineQueryResultArticle(
id=uuid4(),
title="Italic",
input_message_content=InputTextMessageContent(
"_%s_" % escape_markdown(query),
parse_mode=ParseMode.MARKDOWN)))
results.append(InlineQueryResultArticle(id=uuid4(),
title="Italic",
input_message_content=InputTextMessageContent(
"_%s_" % escape_markdown(query),
parse_mode=ParseMode.MARKDOWN)))
bot.answerInlineQuery(update.inline_query.id, results=results)
@@ -104,5 +100,6 @@ def main():
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
+10 -16
View File
@@ -11,8 +11,7 @@ from telegram import Emoji, ForceReply, InlineKeyboardButton, \
from telegram.ext import Updater, CommandHandler, MessageHandler, \
CallbackQueryHandler, Filters
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - '
'%(message)s',
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.DEBUG)
# Define the different states a chat can be in
@@ -20,8 +19,7 @@ MENU, AWAIT_CONFIRMATION, AWAIT_INPUT = range(3)
# Python 2 and 3 unicode differences
try:
YES, NO = (Emoji.THUMBS_UP_SIGN.decode('utf-8'),
Emoji.THUMBS_DOWN_SIGN.decode('utf-8'))
YES, NO = (Emoji.THUMBS_UP_SIGN.decode('utf-8'), Emoji.THUMBS_DOWN_SIGN.decode('utf-8'))
except AttributeError:
YES, NO = (Emoji.THUMBS_UP_SIGN, Emoji.THUMBS_DOWN_SIGN)
@@ -58,11 +56,9 @@ def entered_value(bot, update):
# Save the user id and the answer to context
context[user_id] = update.message.text
reply_markup = InlineKeyboardMarkup(
[[InlineKeyboardButton(YES, callback_data=YES),
InlineKeyboardButton(NO, callback_data=NO)]])
bot.sendMessage(chat_id, text="Are you sure?",
reply_markup=reply_markup)
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton(YES, callback_data=YES),
InlineKeyboardButton(NO, callback_data=NO)]])
bot.sendMessage(chat_id, text="Are you sure?", reply_markup=reply_markup)
def confirm_value(bot, update):
@@ -82,14 +78,12 @@ def confirm_value(bot, update):
values[user_id] = user_context
bot.editMessageText(text="Changed value to %s." % values[user_id],
chat_id=chat_id,
message_id=
query.message.message_id)
message_id=query.message.message_id)
else:
bot.editMessageText(text="Alright, value is still %s."
% values.get(user_id, 'not set'),
bot.editMessageText(text="Alright, value is still %s." %
values.get(user_id, 'not set'),
chat_id=chat_id,
message_id=
query.message.message_id)
message_id=query.message.message_id)
def help(bot, update):
@@ -103,7 +97,7 @@ def error(bot, update, error):
updater = Updater("TOKEN")
# The command
updater.dispatcher.addHandler(CommandHandler('set', set_value))
updater.dispatcher.add_handler(CommandHandler('set', set_value))
# The answer
updater.dispatcher.add_handler(MessageHandler([Filters.text], entered_value))
# The confirmation
+2 -4
View File
@@ -21,8 +21,7 @@ def main():
except IndexError:
update_id = None
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
while True:
try:
@@ -45,8 +44,7 @@ def echo(bot, update_id):
if message:
# Reply to the message
bot.sendMessage(chat_id=chat_id,
text=message)
bot.sendMessage(chat_id=chat_id, text=message)
return update_id
+2 -2
View File
@@ -10,8 +10,7 @@ import urllib
def main():
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
bot = telegram.Bot('TOKEN') # Telegram Bot Authorization Token
LAST_UPDATE_ID = bot.getUpdates()[-1].update_id # Get lastest update
@@ -34,5 +33,6 @@ def ed(text):
return data.strip()
if __name__ == '__main__':
main()
+6 -12
View File
@@ -8,8 +8,7 @@ import logging
from telegram import Emoji, ForceReply, ReplyKeyboardMarkup, KeyboardButton
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - '
'%(message)s',
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
# Define the different states a chat can be in
@@ -17,8 +16,7 @@ MENU, AWAIT_CONFIRMATION, AWAIT_INPUT = range(3)
# Python 2 and 3 unicode differences
try:
YES, NO = (Emoji.THUMBS_UP_SIGN.decode('utf-8'),
Emoji.THUMBS_DOWN_SIGN.decode('utf-8'))
YES, NO = (Emoji.THUMBS_UP_SIGN.decode('utf-8'), Emoji.THUMBS_DOWN_SIGN.decode('utf-8'))
except AttributeError:
YES, NO = (Emoji.THUMBS_UP_SIGN, Emoji.THUMBS_DOWN_SIGN)
@@ -46,7 +44,7 @@ def set_value(bot, update):
context[chat_id] = user_id # save the user id to context
bot.sendMessage(chat_id,
text="Please enter your settings value or send "
"/cancel to abort",
"/cancel to abort",
reply_markup=ForceReply())
# If we are waiting for input and the right user answered
@@ -58,8 +56,7 @@ def set_value(bot, update):
reply_markup = ReplyKeyboardMarkup(
[[KeyboardButton(YES), KeyboardButton(NO)]],
one_time_keyboard=True)
bot.sendMessage(chat_id, text="Are you sure?",
reply_markup=reply_markup)
bot.sendMessage(chat_id, text="Are you sure?", reply_markup=reply_markup)
# If we are waiting for confirmation and the right user answered
elif chat_state == AWAIT_CONFIRMATION and chat_context[0] == user_id:
@@ -67,12 +64,10 @@ def set_value(bot, update):
del context[chat_id]
if text == YES:
values[chat_id] = chat_context[1]
bot.sendMessage(chat_id,
text="Changed value to %s." % values[chat_id])
bot.sendMessage(chat_id, text="Changed value to %s." % values[chat_id])
else:
bot.sendMessage(chat_id,
text="Value not changed: %s."
% values.get(chat_id, '<not set>'))
text="Value not changed: %s." % values.get(chat_id, '<not set>'))
# Handler for the /cancel command.
@@ -86,7 +81,6 @@ def cancel(bot, update):
def help(bot, update):
bot.sendMessage(update.message.chat_id, text="Use /set to test this bot.")
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN")
+6 -7
View File
@@ -3,7 +3,6 @@
#
# Simple Bot to send timed Telegram messages
# This program is dedicated to the public domain under the CC0 license.
"""
This Bot uses the Updater class to handle the bot and the JobQueue to send
timed messages.
@@ -22,9 +21,8 @@ from telegram.ext import Updater, CommandHandler
import logging
# Enable logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
job_queue = None
@@ -33,8 +31,7 @@ job_queue = None
# Define a few command handlers. These usually take the two arguments bot and
# update. Error handlers also receive the raised TelegramError object in error.
def start(bot, update):
bot.sendMessage(update.message.chat_id, text='Hi! Use /set <seconds> to '
'set a timer')
bot.sendMessage(update.message.chat_id, text='Hi! Use /set <seconds> to ' 'set a timer')
def set(bot, update, args):
@@ -44,7 +41,8 @@ def set(bot, update, args):
# args[0] should contain the time for the timer in seconds
due = int(args[0])
if due < 0:
bot.sendMessage(chat_id, text='Sorry we can not go back to future!')
bot.sendMessage(chat_id, text='Sorry we can not go back to future!')
def alarm(bot):
""" Inner function to send the alarm message """
bot.sendMessage(chat_id, text='Beep!')
@@ -88,5 +86,6 @@ def main():
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
+2
View File
@@ -11,7 +11,9 @@ upload-dir = docs/build/html
[flake8]
max-line-length = 99
ignore = W503
[yapf]
based_on_style = google
split_before_logical_operator = True
column_limit = 99
+33 -39
View File
@@ -1,17 +1,11 @@
#!/usr/bin/env python
'''The setup and build script for the python-telegram-bot library.'''
import os
"""The setup and build script for the python-telegram-bot library."""
import codecs
import telegram
from setuptools import setup, find_packages
def read(*paths):
"""Build a file path from *paths* and return the contents."""
with open(os.path.join(*paths), 'r') as f:
return f.read()
def requirements():
"""Build the requirements list for this project"""
requirements_list = []
@@ -22,33 +16,33 @@ def requirements():
return requirements_list
setup(name='python-telegram-bot',
version='4.1.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',
],)
with codecs.open('README.rst', 'r', 'utf-8') as fd:
setup(name='python-telegram-bot',
version=telegram.__version__,
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='Not just a Python wrapper around the Telegram Bot API',
long_description=fd.read(),
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',
],)
+20 -18
View File
@@ -23,6 +23,7 @@ from sys import version_info
from .base import TelegramObject
from .user import User
from .chat import Chat
from .chatmember import ChatMember
from .photosize import PhotoSize
from .audio import Audio
from .voice import Voice
@@ -81,25 +82,26 @@ from .update import Update
from .bot import Bot
__author__ = 'devs@python-telegram-bot.org'
__version__ = '4.1.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.2.0'
__all__ = ['Audio', 'Bot', 'Chat', 'ChatMember', 'ChatAction', 'ChosenInlineResult',
'CallbackQuery', 'Contact', 'Document', 'Emoji', 'File', 'ForceReply',
'InlineKeyboardButton', 'InlineKeyboardMarkup', 'InlineQuery', 'InlineQueryResult',
'InlineQueryResult', 'InlineQueryResultArticle', 'InlineQueryResultAudio',
'InlineQueryResultCachedAudio', 'InlineQueryResultCachedDocument',
'InlineQueryResultCachedGif', 'InlineQueryResultCachedMpeg4Gif',
'InlineQueryResultCachedPhoto', 'InlineQueryResultCachedSticker',
'InlineQueryResultCachedVideo', 'InlineQueryResultCachedVoice',
'InlineQueryResultContact', 'InlineQueryResultDocument', 'InlineQueryResultGif',
'InlineQueryResultLocation', 'InlineQueryResultMpeg4Gif', 'InlineQueryResultPhoto',
'InlineQueryResultVenue', 'InlineQueryResultVideo', 'InlineQueryResultVoice',
'InputContactMessageContent', 'InputFile', 'InputLocationMessageContent',
'InputMessageContent', 'InputTextMessageContent', 'InputVenueMessageContent',
'KeyboardButton', 'Location', 'Message', 'MessageEntity', 'NullHandler', 'ParseMode',
'PhotoSize', 'ReplyKeyboardHide', 'ReplyKeyboardMarkup', 'ReplyMarkup', 'Sticker',
'TelegramError', 'TelegramObject', 'Update', 'User', 'UserProfilePhotos', 'Venue',
'Video', 'Voice']
if version_info < (2, 7):
from warnings import warn
warn("python-telegram-bot will stop supporting Python 2.6 in a future release. "
"Please upgrade your Python!")
"Please upgrade your Python version to at least Python 2.7!")
+199 -26
View File
@@ -19,13 +19,13 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Bot."""
import logging
import functools
import logging
from telegram import (User, Message, Update, UserProfilePhotos, File, ReplyMarkup, TelegramObject,
NullHandler)
from telegram import (User, Message, Update, Chat, ChatMember, UserProfilePhotos, File,
ReplyMarkup, TelegramObject, NullHandler)
from telegram.error import InvalidToken
from telegram.utils import request
from telegram.utils.validate import validate_token
logging.getLogger(__name__).addHandler(NullHandler())
@@ -48,7 +48,7 @@ class Bot(TelegramObject):
"""
def __init__(self, token, base_url=None, base_file_url=None):
self.token = validate_token(token)
self.token = self._validate_token(token)
if not base_url:
self.base_url = 'https://api.telegram.org/bot{0}'.format(self.token)
@@ -64,6 +64,18 @@ class Bot(TelegramObject):
self.logger = logging.getLogger(__name__)
@staticmethod
def _validate_token(token):
"""a very basic validation on token"""
if any(x.isspace() for x in token):
raise InvalidToken()
left, sep, _right = token.partition(':')
if (not sep) or (not left.isdigit()) or (len(left) < 3):
raise InvalidToken()
return token
def info(func):
@functools.wraps(func)
@@ -142,7 +154,7 @@ class Bot(TelegramObject):
return decorator
@log
def getMe(self):
def getMe(self, **kwargs):
"""A simple method for testing your bot's auth token.
Returns:
@@ -1170,31 +1182,27 @@ class Bot(TelegramObject):
return url, data
@log
def getUpdates(self, offset=None, limit=100, timeout=0, network_delay=.2):
def getUpdates(self, offset=None, limit=100, timeout=0, network_delay=5., **kwargs):
"""Use this method to receive incoming updates using long polling.
Args:
offset:
Identifier of the first update to be returned. Must be greater by
one than the highest among the identifiers of previously received
updates. By default, updates starting with the earliest unconfirmed
update are returned. An update is considered confirmed as soon as
getUpdates is called with an offset higher than its update_id.
limit:
Limits the number of updates to be retrieved. Values between 1-100
are accepted. Defaults to 100.
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.
offset (Optional[int]):
Identifier of the first update to be returned. Must be greater by one than the highest
among the identifiers of previously received updates. By default, updates starting with
the earliest unconfirmed update are returned. An update is considered confirmed as soon
as getUpdates is called with an offset higher than its update_id.
limit (Optional[int]):
Limits the number of updates to be retrieved. Values between 1-100 are accepted.
Defaults to 100.
timeout (Optional[int]):
Timeout in seconds for long polling. Defaults to 0, i.e. usual short polling.
network_delay (Optional[float]):
Additional timeout in seconds to allow the response from Telegram servers. This should
cover network latency around the globe, SSL handshake and slowness of the Telegram
servers (which unfortunately happens a lot recently - 2016-05-28). Defaults to 5.
Returns:
list[:class:`telegram.Update`]: A list of :class:`telegram.Update`
objects are returned.
list[:class:`telegram.Update`]
Raises:
:class:`telegram.TelegramError`
@@ -1259,6 +1267,166 @@ class Bot(TelegramObject):
return result
@log
def leaveChat(self, chat_id, **kwargs):
"""Use this method for your bot to leave a group, supergroup or
channel.
Args:
chat_id:
Unique identifier for the target chat or username of the target
channel (in the format @channelusername).
Keyword Args:
timeout (Optional[float]): If this value is specified, use it as
the definitive timeout (in seconds) for urlopen() operations.
Returns:
bool: On success, `True` is returned.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/leaveChat'.format(self.base_url)
data = {'chat_id': chat_id}
result = request.post(url, data, timeout=kwargs.get('timeout'))
return result
@log
def getChat(self, chat_id, **kwargs):
"""Use this method to get up to date information about the chat
(current name of the user for one-on-one conversations, current
username of a user, group or channel, etc.).
Args:
chat_id:
Unique identifier for the target chat or username of the target
channel (in the format @channelusername).
Keyword Args:
timeout (Optional[float]): If this value is specified, use it as
the definitive timeout (in seconds) for urlopen() operations.
Returns:
:class:`telegram.Chat`: On success, :class:`telegram.Chat` is
returned.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/getChat'.format(self.base_url)
data = {'chat_id': chat_id}
result = request.post(url, data, timeout=kwargs.get('timeout'))
return Chat.de_json(result)
@log
def getChatAdministrators(self, chat_id, **kwargs):
"""Use this method to get a list of administrators in a chat. On
success, returns an Array of ChatMember objects that contains
information about all chat administrators except other bots. If the
chat is a group or a supergroup and no administrators were appointed,
only the creator will be returned.
Args:
chat_id:
Unique identifier for the target chat or username of the target
channel (in the format @channelusername).
Keyword Args:
timeout (Optional[float]): If this value is specified, use it as
the definitive timeout (in seconds) for urlopen() operations.
Returns:
list[:class:`telegram.ChatMember`]: On success, a list of
:class:`telegram.ChatMember` objects are returned.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/getChatAdministrators'.format(self.base_url)
data = {'chat_id': chat_id}
result = request.post(url, data, timeout=kwargs.get('timeout'))
return [ChatMember.de_json(x) for x in result]
@log
def getChatMembersCount(self, chat_id, **kwargs):
"""Use this method to get the number of members in a chat.
Args:
chat_id:
Unique identifier for the target chat or username of the target
channel (in the format @channelusername).
Keyword Args:
timeout (Optional[float]): If this value is specified, use it as
the definitive timeout (in seconds) for urlopen() operations.
Returns:
int: On success, an `int` is returned.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/getChatMembersCount'.format(self.base_url)
data = {'chat_id': chat_id}
result = request.post(url, data, timeout=kwargs.get('timeout'))
return result
@log
def getChatMember(self, chat_id, user_id, **kwargs):
"""Use this method to get information about a member of a chat.
Args:
chat_id:
Unique identifier for the target chat or username of the target
channel (in the format @channelusername).
user_id:
Unique identifier of the target user.
Keyword Args:
timeout (Optional[float]): If this value is specified, use it as
the definitive timeout (in seconds) for urlopen() operations.
Returns:
:class:`telegram.ChatMember`: On success,
:class:`telegram.ChatMember` is returned.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/getChatMember'.format(self.base_url)
data = {'chat_id': chat_id, 'user_id': user_id}
result = request.post(url, data, timeout=kwargs.get('timeout'))
return ChatMember.de_json(result)
@staticmethod
def de_json(data):
data = super(Bot, Bot).de_json(data)
@@ -1302,3 +1470,8 @@ class Bot(TelegramObject):
edit_message_reply_markup = editMessageReplyMarkup
get_updates = getUpdates
set_webhook = setWebhook
leave_chat = leaveChat
get_chat = getChat
get_chat_administrators = getChatAdministrators
get_chat_member = getChatMember
get_chat_members_count = getChatMembersCount
+5
View File
@@ -42,6 +42,11 @@ class Chat(TelegramObject):
type (Optional[str]):
"""
PRIVATE = 'private'
GROUP = 'group'
SUPERGROUP = 'supergroup'
CHANNEL = 'channel'
def __init__(self, id, type, **kwargs):
# Required
self.id = int(id)
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram ChatMember."""
from telegram import User, TelegramObject
class ChatMember(TelegramObject):
"""This object represents a Telegram ChatMember.
Attributes:
user (:class:`telegram.User`): Information about the user.
status (str): The member's status in the chat. Can be 'creator', 'administrator', 'member',
'left' or 'kicked'.
Args:
user (:class:`telegram.User`):
status (str):
"""
CREATOR = 'creator'
ADMINISTRATOR = 'administrator'
MEMBER = 'member'
LEFT = 'left'
KICKED = 'kicked'
def __init__(self, user, status, **kwargs):
# Required
self.user = user
self.status = status
@staticmethod
def de_json(data):
"""
Args:
data (dict):
Returns:
telegram.ChatMember:
"""
if not data:
return None
data['user'] = User.de_json(data.get('user'))
return ChatMember(**data)
+7 -1
View File
@@ -41,7 +41,13 @@ class ChosenInlineResult(TelegramObject):
"""
def __init__(self, result_id, from_user, query, location=None, inline_message_id=None):
def __init__(self,
result_id,
from_user,
query,
location=None,
inline_message_id=None,
**kwargs):
# Required
self.result_id = result_id
self.from_user = from_user
View File
+46
View File
@@ -0,0 +1,46 @@
import logging
from telegram import NullHandler
from future.moves.urllib.parse import quote
from future.moves.urllib.error import HTTPError, URLError
from future.moves.urllib.request import urlopen, Request
logging.getLogger(__name__).addHandler(NullHandler())
class Botan(object):
"""This class helps to send incoming events to your botan analytics account.
See more: https://github.com/botanio/sdk#botan-sdk
"""
token = ''
url_template = 'https://api.botan.io/track?token={token}' \
'&uid={uid}&name={name}&src=python-telegram-bot'
def __init__(self, token):
self.token = token
self.logger = logging.getLogger(__name__)
def track(self, message, event_name='event'):
try:
uid = message.chat_id
except AttributeError:
self.logger.warn('No chat_id in message')
return False
data = message.to_json()
try:
url = self.url_template.format(token=str(self.token),
uid=str(uid),
name=quote(event_name))
request = Request(url,
data=data.encode(),
headers={'Content-Type': 'application/json'})
urlopen(request)
return True
except HTTPError as error:
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))
return False
+5
View File
@@ -51,6 +51,7 @@ class TelegramError(Exception):
msg = _lstrip_str(message, 'Error: ')
msg = _lstrip_str(msg, '[Error]: ')
msg = _lstrip_str(msg, 'Bad Request: ')
if msg != message:
# api_error - capitalize the msg...
msg = msg.capitalize()
@@ -76,6 +77,10 @@ class NetworkError(TelegramError):
pass
class BadRequest(NetworkError):
pass
class TimedOut(NetworkError):
def __init__(self):
+21 -5
View File
@@ -34,6 +34,8 @@ class CommandHandler(Handler):
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
allow_edited (Optional[bool]): If the handler should also accept edited messages.
Default is ``False``
pass_args (optional[bool]): If the handler should be passed the
arguments passed to the command as a keyword argument called `
``args``. It will contain a list of strings, which is the text
@@ -43,21 +45,35 @@ class CommandHandler(Handler):
be used to insert updates. Default is ``False``
"""
def __init__(self, command, callback, pass_args=False, pass_update_queue=False):
def __init__(self,
command,
callback,
allow_edited=False,
pass_args=False,
pass_update_queue=False):
super(CommandHandler, self).__init__(callback, pass_update_queue)
self.command = command
self.allow_edited = allow_edited
self.pass_args = pass_args
def check_update(self, update):
return (isinstance(update, Update) and update.message and update.message.text and
update.message.text.startswith('/') and
update.message.text[1:].split(' ')[0].split('@')[0] == self.command)
if (isinstance(update, Update)
and (update.message or update.edited_message and self.allow_edited)):
message = update.message or update.edited_message
return (message.text and message.text.startswith('/')
and message.text[1:].split(' ')[0].split('@')[0] == self.command)
else:
return False
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
message = update.message or update.edited_message
if self.pass_args:
optional_args['args'] = update.message.text.split(' ')[1:]
optional_args['args'] = message.text.split(' ')[1:]
self.callback(dispatcher.bot, update, **optional_args)
+40 -35
View File
@@ -30,60 +30,56 @@ class Filters(object):
"""
@staticmethod
def text(update):
return update.message.text and not update.message.text.startswith('/')
def text(message):
return message.text and not message.text.startswith('/')
@staticmethod
def command(update):
return update.message.text and update.message.text.startswith('/')
def command(message):
return message.text and message.text.startswith('/')
@staticmethod
def audio(update):
return bool(update.message.audio)
def audio(message):
return bool(message.audio)
@staticmethod
def document(update):
return bool(update.message.document)
def document(message):
return bool(message.document)
@staticmethod
def photo(update):
return bool(update.message.photo)
def photo(message):
return bool(message.photo)
@staticmethod
def sticker(update):
return bool(update.message.sticker)
def sticker(message):
return bool(message.sticker)
@staticmethod
def video(update):
return bool(update.message.video)
def video(message):
return bool(message.video)
@staticmethod
def voice(update):
return bool(update.message.voice)
def voice(message):
return bool(message.voice)
@staticmethod
def contact(update):
return bool(update.message.contact)
def contact(message):
return bool(message.contact)
@staticmethod
def location(update):
return bool(update.message.location)
def location(message):
return bool(message.location)
@staticmethod
def venue(update):
return bool(update.message.venue)
def venue(message):
return bool(message.venue)
@staticmethod
def status_update(update):
# 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
def status_update(message):
return bool(message.new_chat_member or message.left_chat_member or message.new_chat_title
or message.new_chat_photo or message.delete_chat_photo
or message.group_chat_created or message.supergroup_chat_created
or message.channel_chat_created or message.migrate_to_chat_id
or message.migrate_from_chat_id or message.pinned_message)
class MessageHandler(Handler):
@@ -102,23 +98,32 @@ class MessageHandler(Handler):
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
allow_edited (Optional[bool]): If the handler should also accept edited messages.
Default is ``False``
pass_update_queue (optional[bool]): If the handler should be passed the
update queue as a keyword argument called ``update_queue``. It can
be used to insert updates. Default is ``False``
"""
def __init__(self, filters, callback, pass_update_queue=False):
def __init__(self, filters, callback, allow_edited=False, pass_update_queue=False):
super(MessageHandler, self).__init__(callback, pass_update_queue)
self.filters = filters
self.allow_edited = allow_edited
def check_update(self, update):
if isinstance(update, Update) and update.message:
if (isinstance(update, Update)
and (update.message or update.edited_message and self.allow_edited)):
if not self.filters:
res = True
else:
res = any(func(update) for func in self.filters)
message = update.message or update.edited_message
res = any(func(message) for func in self.filters)
else:
res = False
return res
def handle_update(self, update, dispatcher):
+2 -2
View File
@@ -47,8 +47,8 @@ class StringCommandHandler(Handler):
self.pass_args = pass_args
def check_update(self, update):
return (isinstance(update, str) and update.startswith('/') and
update[1:].split(' ')[0] == self.command)
return (isinstance(update, str) and update.startswith('/')
and update[1:].split(' ')[0] == self.command)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
+13 -10
View File
@@ -112,22 +112,25 @@ class Updater(object):
def start_polling(self,
poll_interval=0.0,
timeout=10,
network_delay=2,
network_delay=5.,
clean=False,
bootstrap_retries=0):
"""
Starts polling updates from Telegram.
Args:
poll_interval (Optional[float]): Time to wait between polling
updates from Telegram in seconds. Default is 0.0
poll_interval (Optional[float]): Time to wait between polling updates from Telegram in
seconds. Default is 0.0
timeout (Optional[float]): Passed to Bot.getUpdates
network_delay (Optional[float]): Passed to Bot.getUpdates
clean (Optional[bool]): Whether to clean any pending updates on
Telegram servers before actually starting to poll. Default is
False.
bootstrap_retries (Optional[int[): Whether the bootstrapping phase
of the `Updater` will retry on failures on the Telegram server.
clean (Optional[bool]): Whether to clean any pending updates on Telegram servers before
actually starting to poll. Default is False.
bootstrap_retries (Optional[int]): Whether the bootstrapping phase of the `Updater`
will retry on failures on the Telegram server.
| < 0 - retry indefinitely
| 0 - no retries (default)
@@ -318,8 +321,8 @@ class Updater(object):
except (Unauthorized, InvalidToken):
raise
except TelegramError:
msg = 'error in bootstrap phase; try={0} max_retries={1}'\
.format(retries, max_retries)
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
+1 -1
View File
@@ -33,7 +33,7 @@ class InlineKeyboardMarkup(ReplyMarkup):
"""
def __init__(self, inline_keyboard):
def __init__(self, inline_keyboard, **kwargs):
# Required
self.inline_keyboard = inline_keyboard
+1 -1
View File
@@ -25,7 +25,7 @@ from telegram import InputMessageContent
class InputContactMessageContent(InputMessageContent):
"""Base class for Telegram InputContactMessageContent Objects"""
def __init__(self, phone_number, first_name, last_name=None):
def __init__(self, phone_number, first_name, last_name=None, **kwargs):
# Required
self.phone_number = phone_number
self.first_name = first_name
+1 -1
View File
@@ -25,7 +25,7 @@ from telegram import InputMessageContent
class InputLocationMessageContent(InputMessageContent):
"""Base class for Telegram InputLocationMessageContent Objects"""
def __init__(self, latitude, longitude):
def __init__(self, latitude, longitude, **kwargs):
# Required
self.latitude = latitude
self.longitude = longitude
+4 -4
View File
@@ -39,14 +39,14 @@ class InputMessageContent(TelegramObject):
pass
try:
from telegram import InputLocationMessageContent
return InputLocationMessageContent.de_json(data)
from telegram import InputVenueMessageContent
return InputVenueMessageContent.de_json(data)
except TypeError:
pass
try:
from telegram import InputVenueMessageContent
return InputVenueMessageContent.de_json(data)
from telegram import InputLocationMessageContent
return InputLocationMessageContent.de_json(data)
except TypeError:
pass
+1 -1
View File
@@ -25,7 +25,7 @@ from telegram import InputMessageContent
class InputTextMessageContent(InputMessageContent):
"""Base class for Telegram InputTextMessageContent Objects"""
def __init__(self, message_text, parse_mode=None, disable_web_page_preview=None):
def __init__(self, message_text, parse_mode=None, disable_web_page_preview=None, **kwargs):
# Required
self.message_text = message_text
# Optionals
+1 -1
View File
@@ -25,7 +25,7 @@ from telegram import InputMessageContent
class InputVenueMessageContent(InputMessageContent):
"""Base class for Telegram InputVenueMessageContent Objects"""
def __init__(self, latitude, longitude, title, address, foursquare_id=None):
def __init__(self, latitude, longitude, title, address, foursquare_id=None, **kwargs):
# Required
self.latitude = latitude
self.longitude = longitude
+1 -1
View File
@@ -33,7 +33,7 @@ class KeyboardButton(TelegramObject):
request_contact (Optional[bool]):
"""
def __init__(self, text, request_contact=None, request_location=None):
def __init__(self, text, request_contact=None, request_location=None, **kwargs):
# Required
self.text = text
# Optionals
+1 -1
View File
@@ -33,7 +33,7 @@ class Location(TelegramObject):
latitude (float):
"""
def __init__(self, longitude, latitude):
def __init__(self, longitude, latitude, **kwargs):
# Required
self.longitude = float(longitude)
self.latitude = float(latitude)
+6
View File
@@ -40,6 +40,7 @@ class Message(TelegramObject):
forward_from_chat (:class:`telegram.Chat`):
forward_date (:class:`datetime.datetime`):
reply_to_message (:class:`telegram.Message`):
edit_date (:class:`datetime.datetime`):
text (str):
audio (:class:`telegram.Audio`):
document (:class:`telegram.Document`):
@@ -80,6 +81,7 @@ class Message(TelegramObject):
forward_from_chat (:class:`telegram.Chat`):
forward_date (Optional[:class:`datetime.datetime`]):
reply_to_message (Optional[:class:`telegram.Message`]):
edit_date (Optional[:class:`datetime.datetime`]):
text (Optional[str]):
audio (Optional[:class:`telegram.Audio`]):
document (Optional[:class:`telegram.Document`]):
@@ -113,6 +115,7 @@ class Message(TelegramObject):
self.forward_from_chat = kwargs.get('forward_from_chat')
self.forward_date = kwargs.get('forward_date')
self.reply_to_message = kwargs.get('reply_to_message')
self.edit_date = kwargs.get('edit_date')
self.text = kwargs.get('text', '')
self.entities = kwargs.get('entities', list())
self.audio = kwargs.get('audio')
@@ -162,6 +165,7 @@ class Message(TelegramObject):
data['forward_from_chat'] = Chat.de_json(data.get('forward_from_chat'))
data['forward_date'] = Message._fromtimestamp(data.get('forward_date'))
data['reply_to_message'] = Message.de_json(data.get('reply_to_message'))
data['edit_date'] = Message._fromtimestamp(data.get('edit_date'))
data['audio'] = Audio.de_json(data.get('audio'))
data['document'] = Document.de_json(data.get('document'))
data['photo'] = PhotoSize.de_list(data.get('photo'))
@@ -197,6 +201,8 @@ class Message(TelegramObject):
# Optionals
if self.forward_date:
data['forward_date'] = self._totimestamp(self.forward_date)
if self.edit_date:
data['edit_date'] = self._totimestamp(self.edit_date)
if self.photo:
data['photo'] = [p.to_dict() for p in self.photo]
if self.entities:
+7 -3
View File
@@ -18,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram MessageEntity."""
from telegram import TelegramObject
from telegram import User, TelegramObject
class MessageEntity(TelegramObject):
@@ -31,20 +31,24 @@ class MessageEntity(TelegramObject):
offset (int):
length (int):
url (Optional[str]):
user (Optional[:class:`telegram.User`]):
"""
def __init__(self, type, offset, length, url=None):
def __init__(self, type, offset, length, **kwargs):
# Required
self.type = type
self.offset = offset
self.length = length
# Optionals
self.url = url
self.url = kwargs.get('url')
self.user = kwargs.get('user')
@staticmethod
def de_json(data):
data = super(MessageEntity, MessageEntity).de_json(data)
data['user'] = User.de_json(data.get('user'))
return MessageEntity(**data)
@staticmethod
+2 -2
View File
@@ -51,8 +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):
+4
View File
@@ -27,6 +27,7 @@ class Update(TelegramObject):
Attributes:
update_id (int):
message (:class:`telegram.Message`):
edited_message (:class:`telegram.Message`):
inline_query (:class:`telegram.InlineQuery`):
chosen_inline_result (:class:`telegram.ChosenInlineResult`):
callback_query (:class:`telegram.CallbackQuery`):
@@ -37,6 +38,7 @@ class Update(TelegramObject):
Keyword Args:
message (Optional[:class:`telegram.Message`]):
edited_message (Optional[:class:`telegram.Message`]):
inline_query (Optional[:class:`telegram.InlineQuery`]):
chosen_inline_result (Optional[:class:`telegram.ChosenInlineResult`])
callback_query (Optional[:class:`telegram.CallbackQuery`]):
@@ -47,6 +49,7 @@ class Update(TelegramObject):
self.update_id = int(update_id)
# Optionals
self.message = kwargs.get('message')
self.edited_message = kwargs.get('edited_message')
self.inline_query = kwargs.get('inline_query')
self.chosen_inline_result = kwargs.get('chosen_inline_result')
self.callback_query = kwargs.get('callback_query')
@@ -64,6 +67,7 @@ class Update(TelegramObject):
return None
data['message'] = Message.de_json(data.get('message'))
data['edited_message'] = Message.de_json(data.get('edited_message'))
data['inline_query'] = InlineQuery.de_json(data.get('inline_query'))
data['chosen_inline_result'] = ChosenInlineResult.de_json(data.get('chosen_inline_result'))
data['callback_query'] = CallbackQuery.de_json(data.get('callback_query'))
+1 -1
View File
@@ -34,7 +34,7 @@ class UserProfilePhotos(TelegramObject):
photos (List[List[:class:`telegram.PhotoSize`]]):
"""
def __init__(self, total_count, photos):
def __init__(self, total_count, photos, **kwargs):
# Required
self.total_count = int(total_count)
self.photos = photos
+3 -51
View File
@@ -1,52 +1,4 @@
#!/usr/bin/env python
from .deprecate import deprecate
from telegram.contrib.botan import Botan as Bo
import logging
from telegram import NullHandler
try:
from urllib.request import urlopen, Request
from urllib.parse import quote
from urllib.error import URLError, HTTPError
except ImportError:
from urllib2 import urlopen, Request
from urllib import quote
from urllib2 import URLError, HTTPError
logging.getLogger(__name__).addHandler(NullHandler())
class Botan(object):
"""This class helps to send incoming events in your botan analytics account.
See more: https://github.com/botanio/sdk#botan-sdk"""
token = ''
url_template = 'https://api.botan.io/track?token={token}' \
'&uid={uid}&name={name}&src=python-telegram-bot'
def __init__(self, token):
self.token = token
self.logger = logging.getLogger(__name__)
def track(self, message, event_name='event'):
try:
uid = message.chat_id
except AttributeError:
self.logger.warn('No chat_id in message')
return False
data = message.to_json()
try:
url = self.url_template.format(token=str(self.token),
uid=str(uid),
name=quote(event_name))
request = Request(url,
data=data.encode(),
headers={'Content-Type': 'application/json'})
urlopen(request)
return True
except HTTPError as error:
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))
return False
Botan = deprecate(Bo, 'telegram.utils.botan', 'telegram.contrib.botan')
+8 -6
View File
@@ -28,7 +28,7 @@ from future.moves.urllib.error import HTTPError, URLError
from future.moves.urllib.request import urlopen, urlretrieve, Request
from telegram import (InputFile, TelegramError)
from telegram.error import Unauthorized, NetworkError, TimedOut
from telegram.error import Unauthorized, NetworkError, TimedOut, BadRequest
def _parse(json_data):
@@ -67,13 +67,15 @@ def _try_except_req(func):
# come first.
errcode = error.getcode()
if errcode in (401, 403):
raise Unauthorized()
elif errcode == 502:
raise NetworkError('Bad Gateway')
try:
message = _parse(error.read())
if errcode in (401, 403):
raise Unauthorized()
elif errcode == 400:
raise BadRequest(message)
elif errcode == 502:
raise NetworkError('Bad Gateway')
except ValueError:
message = 'Unknown HTTPError {0}'.format(error.getcode())
-29
View File
@@ -1,29 +0,0 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains functions to validate function arguments"""
from telegram.error import InvalidToken
def validate_token(token):
"""a very basic validation on token"""
left, sep, _right = token.partition(':')
if (not sep) or (not left.isdigit()) or (len(left) < 3):
raise InvalidToken()
return token
+1 -1
View File
@@ -32,7 +32,7 @@ class Venue(TelegramObject):
foursquare_id (Optional[str]):
"""
def __init__(self, location, title, address, foursquare_id=None):
def __init__(self, location, title, address, foursquare_id=None, **kwargs):
# Required
self.location = location
self.title = title
+2
View File
@@ -40,6 +40,8 @@ class BaseTest(object):
'133505823:AAHZFMHno3mzVLErU5b5jJvaeG--qUyLyG0'))
chat_id = os.environ.get('CHAT_ID', '12173560')
self._group_id = os.environ.get('GROUP_ID', '-49740850')
self._channel_id = os.environ.get('CHANNEL_ID', '@pythontelegrambottests')
self._bot = bot
self._chat_id = chat_id
+70 -10
View File
@@ -45,11 +45,7 @@ class BotTest(BaseTest, unittest.TestCase):
bot = self._bot.getMe()
self.assertTrue(self.is_json(bot.to_json()))
self.assertEqual(bot.id, 133505823)
self.assertEqual(bot.first_name, 'PythonTelegramBot')
self.assertEqual(bot.last_name, '')
self.assertEqual(bot.username, 'PythonTelegramBot')
self.assertEqual(bot.name, '@PythonTelegramBot')
self._testUserEqualsBot(bot)
@flaky(3, 1)
@timeout(10)
@@ -75,7 +71,7 @@ class BotTest(BaseTest, unittest.TestCase):
@flaky(3, 1)
@timeout(10)
def testGetUpdates(self):
updates = self._bot.getUpdates()
updates = self._bot.getUpdates(timeout=1)
if updates:
self.assertTrue(self.is_json(updates[0].to_json()))
@@ -120,12 +116,12 @@ class BotTest(BaseTest, unittest.TestCase):
@timeout(10)
def testResendPhoto(self):
message = self._bot.sendPhoto(
photo='AgADAQADyKcxGx8j9Qdp6d-gpUsw4Gja1i8ABEVJsVqQk8LfJ3wAAgI',
photo='AgADAQAD1y0yGx8j9Qf8f_m3CKeS6Iy95y8ABI1ggfVJ4-UvwJcAAgI',
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.photo[0].file_id,
'AgADAQADyKcxGx8j9Qdp6d-gpUsw4Gja1i8ABEVJsVqQk8LfJ3wAAgI')
'AgADAQAD1y0yGx8j9Qf8f_m3CKeS6Iy95y8ABI1ggfVJ4-UvwJcAAgI')
@flaky(3, 1)
@timeout(10)
@@ -145,7 +141,7 @@ class BotTest(BaseTest, unittest.TestCase):
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.photo[0].file_size, 684)
self.assertEqual(message.photo[0].file_size, 685)
@flaky(3, 1)
@timeout(10)
@@ -155,7 +151,7 @@ class BotTest(BaseTest, unittest.TestCase):
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.photo[0].file_size, 684)
self.assertEqual(message.photo[0].file_size, 685)
@flaky(3, 1)
@timeout(10)
@@ -192,6 +188,13 @@ class BotTest(BaseTest, unittest.TestCase):
def testInvalidToken3(self):
self._test_invalid_token('12:')
def testInvalidToken4(self):
# white spaces are invalid
self._test_invalid_token('1234:abcd1234\n')
self._test_invalid_token(' 1234:abcd1234')
self._test_invalid_token(' 1234:abcd1234\r')
self._test_invalid_token('1234:abcd 1234')
def testUnauthToken(self):
with self.assertRaisesRegexp(telegram.error.Unauthorized, 'Unauthorized'):
bot = telegram.Bot('1234:abcd1234')
@@ -205,6 +208,63 @@ class BotTest(BaseTest, unittest.TestCase):
bot.getMe()
@flaky(3, 1)
@timeout(10)
def testLeaveChat(self):
with self.assertRaisesRegexp(telegram.error.BadRequest, 'Chat not found'):
chat = self._bot.leaveChat(-123456)
with self.assertRaisesRegexp(telegram.error.NetworkError, 'Chat not found'):
chat = self._bot.leaveChat(-123456)
@flaky(3, 1)
@timeout(10)
def testGetChat(self):
chat = self._bot.getChat(self._group_id)
self.assertTrue(self.is_json(chat.to_json()))
self.assertEqual(chat.type, "group")
self.assertEqual(chat.title, ">>> telegram.Bot() - Developers")
self.assertEqual(chat.id, int(self._group_id))
@flaky(3, 1)
@timeout(10)
def testGetChatAdministrators(self):
admins = self._bot.getChatAdministrators(self._channel_id)
self.assertTrue(isinstance(admins, list))
self.assertTrue(self.is_json(admins[0].to_json()))
for a in admins:
self.assertTrue(a.status in ("administrator", "creator"))
bot = [a.user for a in admins if a.user.id == 133505823][0]
self._testUserEqualsBot(bot)
@flaky(3, 1)
@timeout(10)
def testGetChatMembersCount(self):
count = self._bot.getChatMembersCount(self._channel_id)
self.assertTrue(isinstance(count, int))
self.assertTrue(count > 3)
@flaky(3, 1)
@timeout(10)
def testGetChatMember(self):
chat_member = self._bot.getChatMember(self._channel_id, 133505823)
bot = chat_member.user
self.assertTrue(self.is_json(chat_member.to_json()))
self.assertEqual(chat_member.status, "administrator")
self._testUserEqualsBot(bot)
def _testUserEqualsBot(self, user):
"""Tests if user is our trusty @PythonTelegramBot."""
self.assertEqual(user.id, 133505823)
self.assertEqual(user.first_name, 'PythonTelegramBot')
self.assertEqual(user.last_name, '')
self.assertEqual(user.username, 'PythonTelegramBot')
self.assertEqual(user.name, '@PythonTelegramBot')
if __name__ == '__main__':
unittest.main()
+2 -2
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python
"""This module contains a object that represents Tests for Botan analytics integration"""
"""This module contains an object that represents Tests for Botan analytics integration"""
import sys
import unittest
@@ -9,7 +9,7 @@ from flaky import flaky
sys.path.append('.')
from telegram.utils.botan import Botan
from telegram.contrib.botan import Botan
from tests.base import BaseTest
+35 -36
View File
@@ -26,7 +26,7 @@ from datetime import datetime
sys.path.append('.')
from telegram import Update, Message, User, Chat
from telegram import Message, User, Chat
from telegram.ext import Filters
from tests.base import BaseTest
@@ -36,119 +36,118 @@ class FiltersTest(BaseTest, unittest.TestCase):
def setUp(self):
self.message = Message(0, User(0, "Testuser"), datetime.now(), Chat(0, 'private'))
self.update = Update(0, message=self.message)
def test_filters_text(self):
self.message.text = 'test'
self.assertTrue(Filters.text(self.update))
self.assertTrue(Filters.text(self.message))
self.message.text = '/test'
self.assertFalse(Filters.text(self.update))
self.assertFalse(Filters.text(self.message))
def test_filters_command(self):
self.message.text = 'test'
self.assertFalse(Filters.command(self.update))
self.assertFalse(Filters.command(self.message))
self.message.text = '/test'
self.assertTrue(Filters.command(self.update))
self.assertTrue(Filters.command(self.message))
def test_filters_audio(self):
self.message.audio = 'test'
self.assertTrue(Filters.audio(self.update))
self.assertTrue(Filters.audio(self.message))
self.message.audio = None
self.assertFalse(Filters.audio(self.update))
self.assertFalse(Filters.audio(self.message))
def test_filters_document(self):
self.message.document = 'test'
self.assertTrue(Filters.document(self.update))
self.assertTrue(Filters.document(self.message))
self.message.document = None
self.assertFalse(Filters.document(self.update))
self.assertFalse(Filters.document(self.message))
def test_filters_photo(self):
self.message.photo = 'test'
self.assertTrue(Filters.photo(self.update))
self.assertTrue(Filters.photo(self.message))
self.message.photo = None
self.assertFalse(Filters.photo(self.update))
self.assertFalse(Filters.photo(self.message))
def test_filters_sticker(self):
self.message.sticker = 'test'
self.assertTrue(Filters.sticker(self.update))
self.assertTrue(Filters.sticker(self.message))
self.message.sticker = None
self.assertFalse(Filters.sticker(self.update))
self.assertFalse(Filters.sticker(self.message))
def test_filters_video(self):
self.message.video = 'test'
self.assertTrue(Filters.video(self.update))
self.assertTrue(Filters.video(self.message))
self.message.video = None
self.assertFalse(Filters.video(self.update))
self.assertFalse(Filters.video(self.message))
def test_filters_voice(self):
self.message.voice = 'test'
self.assertTrue(Filters.voice(self.update))
self.assertTrue(Filters.voice(self.message))
self.message.voice = None
self.assertFalse(Filters.voice(self.update))
self.assertFalse(Filters.voice(self.message))
def test_filters_contact(self):
self.message.contact = 'test'
self.assertTrue(Filters.contact(self.update))
self.assertTrue(Filters.contact(self.message))
self.message.contact = None
self.assertFalse(Filters.contact(self.update))
self.assertFalse(Filters.contact(self.message))
def test_filters_location(self):
self.message.location = 'test'
self.assertTrue(Filters.location(self.update))
self.assertTrue(Filters.location(self.message))
self.message.location = None
self.assertFalse(Filters.location(self.update))
self.assertFalse(Filters.location(self.message))
def test_filters_venue(self):
self.message.venue = 'test'
self.assertTrue(Filters.venue(self.update))
self.assertTrue(Filters.venue(self.message))
self.message.venue = None
self.assertFalse(Filters.venue(self.update))
self.assertFalse(Filters.venue(self.message))
def test_filters_status_update(self):
self.assertFalse(Filters.status_update(self.update))
self.assertFalse(Filters.status_update(self.message))
self.message.new_chat_member = 'test'
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.new_chat_member = None
self.message.left_chat_member = 'test'
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.left_chat_member = None
self.message.new_chat_title = 'test'
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.new_chat_title = ''
self.message.new_chat_photo = 'test'
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.new_chat_photo = None
self.message.delete_chat_photo = True
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.delete_chat_photo = False
self.message.group_chat_created = True
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.group_chat_created = False
self.message.supergroup_chat_created = True
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.supergroup_chat_created = False
self.message.migrate_to_chat_id = 100
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.migrate_to_chat_id = 0
self.message.migrate_from_chat_id = 100
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.migrate_from_chat_id = 0
self.message.channel_chat_created = True
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.channel_chat_created = False
self.message.pinned_message = 'test'
self.assertTrue(Filters.status_update(self.update))
self.assertTrue(Filters.status_update(self.message))
self.message.pinned_message = None
+4 -4
View File
@@ -60,8 +60,8 @@ class InlineKeyboardMarkupTest(BaseTest, unittest.TestCase):
inline_keyboard_markup = telegram.InlineKeyboardMarkup.de_json(self.json_dict)
self.assertTrue(isinstance(inline_keyboard_markup.inline_keyboard, list))
self.assertTrue(isinstance(inline_keyboard_markup.inline_keyboard[0][
0], telegram.InlineKeyboardButton))
self.assertTrue(isinstance(inline_keyboard_markup.inline_keyboard[0][0],
telegram.InlineKeyboardButton))
def test_inline_keyboard_markup_to_json(self):
inline_keyboard_markup = telegram.InlineKeyboardMarkup.de_json(self.json_dict)
@@ -72,8 +72,8 @@ class InlineKeyboardMarkupTest(BaseTest, unittest.TestCase):
inline_keyboard_markup = telegram.InlineKeyboardMarkup.de_json(self.json_dict)
self.assertTrue(isinstance(inline_keyboard_markup.inline_keyboard, list))
self.assertTrue(isinstance(inline_keyboard_markup.inline_keyboard[0][
0], telegram.InlineKeyboardButton))
self.assertTrue(isinstance(inline_keyboard_markup.inline_keyboard[0][0],
telegram.InlineKeyboardButton))
if __name__ == '__main__':
+55 -4
View File
@@ -93,6 +93,10 @@ class UpdaterTest(BaseTest, unittest.TestCase):
self.received_message = update.message.text
self.message_count += 1
def telegramHandlerEditedTest(self, bot, update):
self.received_message = update.edited_message.text
self.message_count += 1
def telegramInlineHandlerTest(self, bot, update):
self.received_message = (update.inline_query, update.chosen_inline_result)
self.message_count += 1
@@ -157,6 +161,28 @@ class UpdaterTest(BaseTest, unittest.TestCase):
sleep(.1)
self.assertTrue(None is self.received_message)
def test_editedMessageHandler(self):
self._setup_updater('Test', edited=True)
d = self.updater.dispatcher
from telegram.ext import Filters
handler = MessageHandler([Filters.text], self.telegramHandlerEditedTest, allow_edited=True)
d.addHandler(handler)
self.updater.start_polling(0.01)
sleep(.1)
self.assertEqual(self.received_message, 'Test')
# Remove handler
d.removeHandler(handler)
handler = MessageHandler([Filters.text],
self.telegramHandlerEditedTest,
allow_edited=False)
d.addHandler(handler)
self.reset()
self.updater.bot.send_messages = 1
sleep(.1)
self.assertTrue(None is self.received_message)
def test_addTelegramMessageHandlerMultipleMessages(self):
self._setup_updater('Multiple', 100)
self.updater.dispatcher.addHandler(MessageHandler([], self.telegramHandlerTest))
@@ -200,6 +226,25 @@ class UpdaterTest(BaseTest, unittest.TestCase):
sleep(.1)
self.assertTrue(None is self.received_message)
def test_editedCommandHandler(self):
self._setup_updater('/test', edited=True)
d = self.updater.dispatcher
handler = CommandHandler('test', self.telegramHandlerEditedTest, allow_edited=True)
d.addHandler(handler)
self.updater.start_polling(0.01)
sleep(.1)
self.assertEqual(self.received_message, '/test')
# Remove handler
d.removeHandler(handler)
handler = CommandHandler('test', self.telegramHandlerEditedTest, allow_edited=False)
d.addHandler(handler)
self.reset()
self.updater.bot.send_messages = 1
sleep(.1)
self.assertTrue(None is self.received_message)
def test_addRemoveStringRegexHandler(self):
self._setup_updater('', messages=0)
d = self.updater.dispatcher
@@ -612,7 +657,8 @@ class MockBot(object):
messages=1,
raise_error=False,
bootstrap_retries=None,
bootstrap_err=TelegramError('test')):
bootstrap_err=TelegramError('test'),
edited=False):
self.text = text
self.send_messages = messages
self.raise_error = raise_error
@@ -620,13 +666,18 @@ class MockBot(object):
self.bootstrap_retries = bootstrap_retries
self.bootstrap_attempts = 0
self.bootstrap_err = bootstrap_err
self.edited = edited
@staticmethod
def mockUpdate(text):
def mockUpdate(self, text):
message = Message(0, None, None, None)
message.text = text
update = Update(0)
update.message = message
if self.edited:
update.edited_message = message
else:
update.message = message
return update
def setWebhook(self, webhook_url=None, certificate=None):