mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-19 15:45:13 +00:00
Compare commits
442 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 834bf192b9 | |||
| d5486433e5 | |||
| c9ec436d68 | |||
| ad3eec2af8 | |||
| e3fe1d2632 | |||
| 52bd3daa39 | |||
| b4e8209f2c | |||
| f5f95ef8c9 | |||
| 04a871aff5 | |||
| 81a755a7d8 | |||
| 6016aca0ba | |||
| 7c908db901 | |||
| d192b385ea | |||
| f0b2028e3f | |||
| f443003408 | |||
| afc36a235b | |||
| b76337de87 | |||
| 6afee6e0bd | |||
| 27e57bbf58 | |||
| 4990d664bb | |||
| b3e42c3e20 | |||
| c2cce40299 | |||
| a2ed7b26f1 | |||
| 89a3dc8372 | |||
| 9fd298a393 | |||
| ecbc268781 | |||
| c7c21c94ea | |||
| 57efde5e0f | |||
| 31073101a3 | |||
| 1e0ebe89f3 | |||
| 35872d7a8b | |||
| f65b6911ea | |||
| 02af1ea803 | |||
| c4a8ee5175 | |||
| e0539d5992 | |||
| 738e3213a7 | |||
| b41f7e3e79 | |||
| caf72ca490 | |||
| 7635bc0eec | |||
| 703bece155 | |||
| 949f4a4fbd | |||
| 05522e4321 | |||
| 4f101a79bb | |||
| 5b91194cc7 | |||
| 494a7ec1e4 | |||
| fc05d3a626 | |||
| bc77c845ea | |||
| a814e9de6b | |||
| d37b6d6735 | |||
| e479c7f25e | |||
| a30411c9fa | |||
| 881d1d0e25 | |||
| cb6ddfded5 | |||
| bda0244ed8 | |||
| 9338f93d24 | |||
| e10fa66286 | |||
| deb9de0ba0 | |||
| 94fd6851ab | |||
| 897f9615f0 | |||
| 86676d59f1 | |||
| f0b91ecf46 | |||
| bbbc622517 | |||
| 1f5601dae2 | |||
| 3608c2bbe5 | |||
| c28763c5be | |||
| dd8b6219b9 | |||
| 78f9bdcac9 | |||
| da95341d5b | |||
| 98be6abc11 | |||
| b08d41d0ff | |||
| de2d732135 | |||
| 1ff348adbb | |||
| 6b457bada5 | |||
| 74283bd414 | |||
| 41f6591ac6 | |||
| 6d08e1bc7f | |||
| 073d7949dc | |||
| dd91ce1f39 | |||
| 57759d8e6d | |||
| 574fc8cddf | |||
| b040568b07 | |||
| 3076dfc086 | |||
| f8a9722573 | |||
| 527daaf93d | |||
| 986add59ab | |||
| bc62a1813a | |||
| 41432f5b02 | |||
| f737ab780a | |||
| e0e8f6b085 | |||
| 17abf0274e | |||
| 8fab7ad302 | |||
| f31bd91673 | |||
| 32ac617e2e | |||
| 7e7acdeb23 | |||
| 792ad62fe8 | |||
| 25bcfa9b35 | |||
| ff00e211d7 | |||
| d40f0a8309 | |||
| 783f9c375c | |||
| 406303d6bb | |||
| 2534e0df9b | |||
| b06983a94a | |||
| 2f408ab767 | |||
| c8497424b7 | |||
| 80fbe98b44 | |||
| 22e9326610 | |||
| a0bb5730c6 | |||
| 748cc3a35f | |||
| 9a13de4a96 | |||
| 561f1c3f02 | |||
| 3907e64966 | |||
| 1abbca3324 | |||
| e7f4a07b7a | |||
| bb165b6acf | |||
| 20067ff178 | |||
| 41daccce07 | |||
| 786216305c | |||
| 86571bc75d | |||
| 663fa0013d | |||
| 37c7af2e14 | |||
| e70625772c | |||
| 1e398821a0 | |||
| b3142d2974 | |||
| 8278779a23 | |||
| 3aedd78e29 | |||
| 6b90ac9f1c | |||
| 386b91f708 | |||
| 76b9a7d328 | |||
| d90b0f495d | |||
| c4d5eff9f3 | |||
| f45ecee820 | |||
| c340585f33 | |||
| 75490ac757 | |||
| 046e69b1c1 | |||
| 7e0be09c58 | |||
| 18f3f43026 | |||
| 0b2fd120d8 | |||
| 408959e91c | |||
| ab2f6e13c9 | |||
| 108e4264fc | |||
| 68b5562c49 | |||
| e50a3622e1 | |||
| d7e226ec0f | |||
| 7c84516d2b | |||
| 53a91be21f | |||
| 2471eaa778 | |||
| 6bfdff8892 | |||
| 2389b07382 | |||
| c7db9a96cd | |||
| af89cbecf3 | |||
| b987c8937c | |||
| 5a0696b181 | |||
| eb303903ef | |||
| a00f409992 | |||
| dc27ff41ef | |||
| 68ec73afb6 | |||
| 0ace0aa016 | |||
| f5847be8ca | |||
| ba26a8ba5d | |||
| d028d4edd0 | |||
| 4d770843cc | |||
| 53de38f6c9 | |||
| be105a2d4a | |||
| ac55ba007e | |||
| 6bdca1e4f8 | |||
| fd7b571e92 | |||
| 9243cd3507 | |||
| 1b09738191 | |||
| 836c50965a | |||
| 1d9d0fc764 | |||
| e57e6dd645 | |||
| 49122d6a99 | |||
| d445d35ceb | |||
| 703b8d1301 | |||
| 46993d5f2d | |||
| 880746baed | |||
| 8ad1f330ea | |||
| 56b1d4f5ce | |||
| df16846d80 | |||
| 3ac9a1cd71 | |||
| 4d8174edc3 | |||
| 235bb72702 | |||
| 9a5ccb1c2d | |||
| a18640a8d3 | |||
| fdc3ac0cc5 | |||
| d881fa6a5f | |||
| c551d71735 | |||
| bee3d881d1 | |||
| 6ec81dd552 | |||
| 6e9f30ca6e | |||
| 252cafb04c | |||
| d4f7b7165c | |||
| 0ed5e8e1a3 | |||
| 335813a11a | |||
| 10a98211f8 | |||
| b5570ddfa5 | |||
| 99c9544a27 | |||
| 0e0611767a | |||
| 4ba5fbf4f5 | |||
| 4b0be65a76 | |||
| 5971cb35f8 | |||
| 6e5302c089 | |||
| 5aab4525c2 | |||
| 62c651d167 | |||
| 41e457f5ed | |||
| 4ce0ab53d0 | |||
| d940afa718 | |||
| a327e9d6ff | |||
| 5e5510d42b | |||
| 73c60ee817 | |||
| 474d5f0c9f | |||
| 74a2baf03d | |||
| 0612385233 | |||
| c0489db17c | |||
| 592352c849 | |||
| 9d367e9f2c | |||
| d86ca30601 | |||
| ec15e866be | |||
| 998040da92 | |||
| 0c74b3cfb9 | |||
| 0ca3ef7a38 | |||
| e160355190 | |||
| d80e0b4b8c | |||
| 96d98084c7 | |||
| 865bba3f3e | |||
| 2745023854 | |||
| fd15e51e28 | |||
| d093506ef2 | |||
| 7f463131aa | |||
| a79f636345 | |||
| 4a4dbcdbf8 | |||
| 7b13adb34b | |||
| e905f310c4 | |||
| 631c825c1d | |||
| ed7ecddb8d | |||
| a3dae1f112 | |||
| 1d33c39a1b | |||
| 14d3f62e44 | |||
| 931b22f49e | |||
| f0aafda1b9 | |||
| ecb6ddd83d | |||
| cb46b0373f | |||
| 35f6de326b | |||
| a686db2c6f | |||
| 1f29093027 | |||
| 0e34c0395f | |||
| 49385493f4 | |||
| f107070db2 | |||
| 3f28633e79 | |||
| 8ff8d57998 | |||
| e9a782a3c8 | |||
| 1f83e7ae4e | |||
| 856f4460fc | |||
| c5ad34b074 | |||
| 78d1faa21e | |||
| 4ac8f86156 | |||
| 8a087dce69 | |||
| 82282ae125 | |||
| 02243f6fda | |||
| 05a90dc3bc | |||
| 1425533871 | |||
| 186db99565 | |||
| 835c4d04c1 | |||
| 5b867b2290 | |||
| ff66a9fa21 | |||
| 3189d4d339 | |||
| d66493b038 | |||
| eca5f66cd6 | |||
| f7aec236e7 | |||
| fc7c161018 | |||
| c0d63deae3 | |||
| f152195a68 | |||
| b2fd2ba697 | |||
| d21ee819b7 | |||
| d3cec77e78 | |||
| 94e35d5b1c | |||
| 39ba575a4d | |||
| 87ac962805 | |||
| 01cca924ca | |||
| 14cb13acfd | |||
| 4ecb4911db | |||
| d47787876a | |||
| 4b173bfd52 | |||
| bfbad1625e | |||
| 564baea8c3 | |||
| adc3104214 | |||
| 2f7cccfc9f | |||
| 5a7a62c3d8 | |||
| ca1fff25f7 | |||
| 0d6b3a5411 | |||
| 6cacea96f7 | |||
| 4775107af0 | |||
| fe06708d34 | |||
| 6390b7fd9b | |||
| 2160ce70a6 | |||
| 5e5125444c | |||
| f86e6f97db | |||
| 325d8131ee | |||
| f68b8c3a4a | |||
| 7c8a0e00c0 | |||
| 4c0737d7c1 | |||
| 5dd95f1968 | |||
| b30417681b | |||
| 37e48b4707 | |||
| 9e306dce16 | |||
| 9b1a55f9e9 | |||
| 252abb138d | |||
| 203364d939 | |||
| 1450478d27 | |||
| 5b36a85e63 | |||
| 7daf26198e | |||
| 8c0de59d46 | |||
| d46d9a711d | |||
| b444cd7bce | |||
| 93e15fa9f8 | |||
| d383c10d4e | |||
| 012bbe1307 | |||
| 4faa4774bd | |||
| cb79317354 | |||
| cfdfdeb4fc | |||
| 67a2d31edf | |||
| 0669c72fe3 | |||
| 185c40da79 | |||
| 5315e072cb | |||
| a8255e4f51 | |||
| e56c6dfab6 | |||
| 263310be36 | |||
| 720c4d22d8 | |||
| 3be8b9ecb9 | |||
| bb36c725af | |||
| ba7e1cada7 | |||
| 9bf5da5ed3 | |||
| c0dd9c6ffc | |||
| 10a96481f5 | |||
| 9a96ad8efd | |||
| fc277d7393 | |||
| 48eb623f15 | |||
| 9a340d2ea9 | |||
| 693df6dc2c | |||
| a98919a86e | |||
| 9790d6d8dc | |||
| 7913d09295 | |||
| 1db337fe2b | |||
| b40a59f509 | |||
| 0d9d5032ef | |||
| de5619f3ca | |||
| b6fceefc80 | |||
| 687a3b0ba1 | |||
| bf5ba9a369 | |||
| d879a0d018 | |||
| ad211655ea | |||
| 3a0eb588cb | |||
| c9bfa71ff9 | |||
| 5f19452dd7 | |||
| 5cccf2603b | |||
| 185b080daa | |||
| cb99341848 | |||
| 1544f612ae | |||
| 53d1d5f589 | |||
| 6992a7a369 | |||
| a846c8fd86 | |||
| 0e5129e59f | |||
| 90576de9e2 | |||
| 79ca07f839 | |||
| 63c793aad4 | |||
| 5e80efaa54 | |||
| a114f70249 | |||
| 31fba47829 | |||
| 360c3077ea | |||
| f2a92ccf46 | |||
| dd8f94885a | |||
| 06ec465c5f | |||
| 79228b0655 | |||
| b2045717d6 | |||
| 15cc410c10 | |||
| f6524b0207 | |||
| ecc86d4bcd | |||
| 56b17f2a17 | |||
| 8bf4a6fdda | |||
| 109af62425 | |||
| ec27edef58 | |||
| 7231eaa349 | |||
| 1876867ec7 | |||
| 17509fc24f | |||
| 2d2b269932 | |||
| 802a74c606 | |||
| c4074f740e | |||
| 1e0ee0694f | |||
| 6a7c0bb584 | |||
| fb2fc3842b | |||
| c794c3520b | |||
| 0fd013feec | |||
| 1834d6c754 | |||
| f51564f7cd | |||
| 624160e1db | |||
| d2f2b74bdb | |||
| 85f1b1af0c | |||
| 6cb177d2a7 | |||
| b5cbf17ef5 | |||
| 0d0ad1334c | |||
| b9305ca7ac | |||
| 0e21609382 | |||
| 95fde0c6c4 | |||
| 884bb41d4a | |||
| a9b305edd0 | |||
| 1e19084a0d | |||
| e179b27f57 | |||
| 8b95f9cbeb | |||
| 390184f605 | |||
| 086fa1251c | |||
| 60f9aede07 | |||
| c411ef7822 | |||
| e1de7220df | |||
| 429ea92254 | |||
| 751402a0d3 | |||
| 2af15cadd6 | |||
| 93e19dc2ae | |||
| c2f5309cbf | |||
| 1657e43904 | |||
| 23eba8a24e | |||
| ed170e1595 | |||
| 46ca28f01c | |||
| b99518e8b8 | |||
| 62045316fe | |||
| 736d62e20d | |||
| 68dca31f15 | |||
| 9a523dbc40 | |||
| 0342a5b30b | |||
| b7f83cf375 | |||
| d9d48f5c91 | |||
| 01b90e7ede | |||
| ba0ea4f268 | |||
| a0788788da | |||
| a264530540 | |||
| 33f047a3dc | |||
| 88eb332c99 | |||
| eb6bafc003 | |||
| f4603831b8 | |||
| b41e1ab127 | |||
| 5c71ebce52 | |||
| 6db377d56d | |||
| da193711b1 |
@@ -1,130 +1,201 @@
|
||||
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``
|
||||
|
||||
``$ pip install -r requirements-dev.txt``
|
||||
|
||||
5. In order to run tests you need to set the following environment variables:
|
||||
|
||||
``$ export CHAT_ID=your-chat-id``
|
||||
|
||||
``$ export TOKEN=your-bot-token``
|
||||
|
||||
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".
|
||||
|
||||
- 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.
|
||||
|
||||
- Please ensure that the code you write is well-tested.
|
||||
|
||||
- Don’t 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 and push it to your GitHub fork, run:
|
||||
|
||||
``$ git commit -a -m "your-commit-message-here"``
|
||||
|
||||
``$ 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 your 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
|
||||
.. _`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:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ git clone https://github.com/<your username>/python-telegram-bot
|
||||
$ cd python-telegram-bot
|
||||
|
||||
3. Add a track to the original repository:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ git remote add upstream https://github.com/python-telegram-bot/python-telegram-bot
|
||||
|
||||
4. Install dependencies:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo pip install -r requirements.txt -r requirements-dev.txt
|
||||
|
||||
|
||||
5. Install pre-commit hooks:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ 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:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ 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.
|
||||
|
||||
- Don’t 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:
|
||||
|
||||
.. code-block::
|
||||
|
||||
$ make test
|
||||
|
||||
- To actually make the commit (this will trigger tests for yapf, lint and pep8 automatically):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ git add your-file-changed.py
|
||||
|
||||
- yapf may change code formatting, make sure to re-add them to your commit.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ git commit -a -m "your-commit-message-here"
|
||||
|
||||
- Finally, push it to your GitHub fork, run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ 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:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ 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:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ 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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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: ../AUTHORS.rst
|
||||
@@ -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)
|
||||
@@ -0,0 +1,18 @@
|
||||
- repo: git://github.com/pre-commit/mirrors-yapf
|
||||
sha: 316b795b2f32cbe80047aff7e842b72368d5a2c1
|
||||
hooks:
|
||||
- id: yapf
|
||||
files: ^(telegram|tests)/.*\.py$
|
||||
- repo: git://github.com/pre-commit/pre-commit-hooks
|
||||
sha: 3fa02652357ff0dbb42b5bc78c673b7bc105fcf3
|
||||
hooks:
|
||||
- id: flake8
|
||||
files: ^telegram/.*\.py$
|
||||
- repo: git://github.com/pre-commit/mirrors-pylint
|
||||
sha: 4de6c8dfadef1a271a814561ce05b8bc1c446d22
|
||||
hooks:
|
||||
- id: pylint
|
||||
files: ^telegram/.*\.py$
|
||||
args:
|
||||
- --errors-only
|
||||
- --disable=no-name-in-module,import-error
|
||||
+1
-2
@@ -13,7 +13,6 @@ install:
|
||||
- pip install -r requirements-dev.txt
|
||||
script:
|
||||
- nosetests -v --with-flaky --no-flaky-report --with-coverage --cover-package=telegram/
|
||||
- flake8 telegram
|
||||
- 'if [[ $TRAVIS_PYTHON_VERSION != 2.6 ]]; then pylint -E telegram --disable=no-name-in-module,import-error; fi'
|
||||
- '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
|
||||
|
||||
@@ -25,6 +25,7 @@ The following wonderful people contributed directly or indirectly to this projec
|
||||
- `Rahiel Kasim <https://github.com/rahiel>`_
|
||||
- `Shelomentsev D <https://github.com/shelomentsevd>`_
|
||||
- `sooyhwang <https://github.com/sooyhwang>`_
|
||||
- `Valentijn <https://github.com/Faalentijn>`_
|
||||
- `wjt <https://github.com/wjt>`_
|
||||
|
||||
Please add yourself here alphabetically when you submit your first pull request.
|
||||
|
||||
+111
@@ -1,3 +1,114 @@
|
||||
=======
|
||||
Changes
|
||||
=======
|
||||
|
||||
**2016-07-15**
|
||||
|
||||
*Released 5.0*
|
||||
|
||||
- Rework ``JobQueue``
|
||||
- Introduce ``ConversationHandler``
|
||||
|
||||
**2016-07-12**
|
||||
|
||||
*Released 4.3.4*
|
||||
|
||||
- Fix proxy support with ``urllib3`` when proxy requires auth
|
||||
|
||||
**2016-07-08**
|
||||
|
||||
*Released 4.3.3*
|
||||
|
||||
- Fix proxy support with ``urllib3``
|
||||
|
||||
**2016-07-04**
|
||||
|
||||
*Released 4.3.2*
|
||||
|
||||
- Fix: Use ``timeout`` parameter in all API methods
|
||||
|
||||
**2016-06-29**
|
||||
|
||||
*Released 4.3.1*
|
||||
|
||||
- Update wrong requirement: ``urllib3>=1.10``
|
||||
|
||||
**2016-06-28**
|
||||
|
||||
*Released 4.3*
|
||||
|
||||
- Use ``urllib3.PoolManager`` for connection re-use
|
||||
- Rewrite ``run_async`` decorator to re-use threads
|
||||
- New requirements: ``urllib3`` and ``certifi``
|
||||
|
||||
**2016-06-10**
|
||||
|
||||
*Released 4.2.1*
|
||||
|
||||
- Fix ``CallbackQuery.to_dict()`` bug (thanks to @jlmadurga)
|
||||
- Fix ``editMessageText`` exception when receiving a ``CallbackQuery``
|
||||
|
||||
**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*
|
||||
|
||||
- Fix deprecation warning in ``Dispatcher``
|
||||
|
||||
**2016-05-15**
|
||||
|
||||
*Released 4.1*
|
||||
|
||||
- Implement API changes from May 6, 2016
|
||||
- Fix bug when ``start_polling`` with ``clean=True``
|
||||
- Methods now have snake_case equivalent, for example ``telegram.Bot.send_message`` is the same as ``telegram.Bot.sendMessage``
|
||||
|
||||
**2016-05-01**
|
||||
|
||||
*Released 4.0.3*
|
||||
|
||||
- Add missing attribute ``location`` to ``InlineQuery``
|
||||
|
||||
**2016-04-29**
|
||||
|
||||
*Released 4.0.2*
|
||||
|
||||
- Bugfixes
|
||||
- ``KeyboardReplyMarkup`` now accepts ``str`` again
|
||||
|
||||
**2016-04-27**
|
||||
|
||||
*Released 4.0.1*
|
||||
|
||||
- Implement Bot API 2.0
|
||||
- Almost complete recode of ``Dispatcher``
|
||||
- Please read the `Transition Guide to 4.0 <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Transition-guide-to-Version-4.0>`_
|
||||
- **Changes from 4.0rc1**
|
||||
- The syntax of filters for ``MessageHandler`` (upper/lower cases)
|
||||
- Handler groups are now identified by ``int`` only, and ordered
|
||||
- **Note:** v4.0 has been skipped due to a PyPI accident
|
||||
|
||||
**2016-04-22**
|
||||
|
||||
*Released 4.0rc1*
|
||||
|
||||
- Implement Bot API 2.0
|
||||
- Almost complete recode of ``Dispatcher``
|
||||
- Please read the `Transistion Guide to 4.0 <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Transistion-guide-to-Version-4.0>`_
|
||||
|
||||
**2016-03-22**
|
||||
|
||||
*Released 3.4*
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
.DEFAULT_GOAL := help
|
||||
.PHONY: clean pep8 lint test install
|
||||
.PHONY: clean pep257 pep8 yapf lint test install
|
||||
|
||||
PYLINT := pylint
|
||||
NOSETESTS := nosetests
|
||||
PEP257 := pep257
|
||||
PEP8 := flake8
|
||||
YAPF := yapf
|
||||
PIP := pip
|
||||
|
||||
clean:
|
||||
@@ -19,7 +20,10 @@ pep257:
|
||||
$(PEP257) telegram
|
||||
|
||||
pep8:
|
||||
$(PEP8) telegram
|
||||
$(PEP8) telegram
|
||||
|
||||
yapf:
|
||||
$(YAPF) -r telegram
|
||||
|
||||
lint:
|
||||
$(PYLINT) -E telegram --disable=no-name-in-module,import-error
|
||||
@@ -28,7 +32,7 @@ test:
|
||||
$(NOSETESTS) -v
|
||||
|
||||
install:
|
||||
$(PIP) install -r requirements.txt
|
||||
$(PIP) install -r requirements.txt -r requirements-dev.txt
|
||||
|
||||
help:
|
||||
@echo "Available targets:"
|
||||
@@ -36,6 +40,7 @@ help:
|
||||
@echo "- pep257 Check docstring style with pep257"
|
||||
@echo "- pep8 Check style with flake8"
|
||||
@echo "- lint Check style with pylint"
|
||||
@echo "- yapf Check style with yapf"
|
||||
@echo "- test Run tests"
|
||||
@echo
|
||||
@echo "Available variables:"
|
||||
@@ -43,4 +48,5 @@ help:
|
||||
@echo "- NOSETESTS default: $(NOSETESTS)"
|
||||
@echo "- PEP257 default: $(PEP257)"
|
||||
@echo "- PEP8 default: $(PEP8)"
|
||||
@echo "- YAPF default: $(YAPF)"
|
||||
@echo "- PIP default: $(PIP)"
|
||||
|
||||
+93
-337
@@ -3,24 +3,24 @@
|
||||
:target: https://github.com/python-telegram-bot/logos
|
||||
:alt: python-telegram-bot Logo
|
||||
|
||||
A Python wrapper around the Telegram Bot API.
|
||||
Not **just** a Python wrapper around the Telegram Bot API
|
||||
|
||||
*Stay tuned for library updates and new releases on our* `Telegram Channel <http://telegram.me/pythontelegrambotchannel>`_.
|
||||
*Stay tuned for library updates and new releases on our* `Telegram Channel <https://telegram.me/pythontelegrambotchannel>`_.
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/python-telegram-bot.svg
|
||||
:target: https://pypi.python.org/pypi/python-telegram-bot
|
||||
:alt: PyPi Package Version
|
||||
|
||||
.. image:: https://img.shields.io/pypi/dm/python-telegram-bot.svg
|
||||
.. image:: https://img.shields.io/pypi/pyversions/python-telegram-bot.svg
|
||||
:target: https://pypi.python.org/pypi/python-telegram-bot
|
||||
:alt: PyPi Package Monthly Download
|
||||
: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
|
||||
:target: http://www.gnu.org/licenses/lgpl-3.0.html
|
||||
:target: https://www.gnu.org/licenses/lgpl-3.0.html
|
||||
:alt: LGPLv3 License
|
||||
|
||||
.. image:: https://travis-ci.org/python-telegram-bot/python-telegram-bot.svg?branch=master
|
||||
@@ -34,6 +34,10 @@ 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
|
||||
@@ -45,129 +49,97 @@ Table of contents
|
||||
|
||||
- `Introduction`_
|
||||
|
||||
- `Status`_
|
||||
|
||||
1. `Telegram API support`_
|
||||
|
||||
2. `Python Version support`_
|
||||
- `Telegram API support`_
|
||||
|
||||
- `Installing`_
|
||||
|
||||
- `Getting the code`_
|
||||
|
||||
- `Getting started`_
|
||||
|
||||
1. `API`_
|
||||
#. `Learning by example`_
|
||||
|
||||
2. `Extensions`_
|
||||
#. `Logging`_
|
||||
|
||||
3. `JobQueue`_
|
||||
#. `Documentation`_
|
||||
|
||||
4. `Logging`_
|
||||
- `Getting help`_
|
||||
|
||||
5. `Examples`_
|
||||
|
||||
6. `Documentation`_
|
||||
- `Contributing`_
|
||||
|
||||
- `License`_
|
||||
|
||||
- `Contact`_
|
||||
============
|
||||
Introduction
|
||||
============
|
||||
|
||||
- `TODO`_
|
||||
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>`_.
|
||||
|
||||
===============
|
||||
_`Introduction`
|
||||
===============
|
||||
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.
|
||||
|
||||
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>`_.
|
||||
====================
|
||||
Telegram API support
|
||||
====================
|
||||
|
||||
=========
|
||||
_`Status`
|
||||
=========
|
||||
As of **28. May 2016**, all types and methods of the Telegram Bot API are supported.
|
||||
|
||||
-----------------------
|
||||
_`Telegram API support`
|
||||
-----------------------
|
||||
==========
|
||||
Installing
|
||||
==========
|
||||
|
||||
========================= ============
|
||||
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
|
||||
========================= ============
|
||||
You can install or upgrade python-telegram-bot with:
|
||||
|
||||
-------------------------
|
||||
_`Python Version support`
|
||||
-------------------------
|
||||
|
||||
============== ============
|
||||
Python Version *Supported?*
|
||||
============== ============
|
||||
2.6 Yes
|
||||
2.7 Yes
|
||||
3.3 Yes
|
||||
3.4 Yes
|
||||
3.5 Yes
|
||||
PyPy Yes
|
||||
PyPy3 Yes
|
||||
============== ============
|
||||
|
||||
=============
|
||||
_`Installing`
|
||||
=============
|
||||
|
||||
You can install python-telegram-bot using::
|
||||
|
||||
$ pip install python-telegram-bot
|
||||
|
||||
Or upgrade to the latest version::
|
||||
.. code:: shell
|
||||
|
||||
$ pip install python-telegram-bot --upgrade
|
||||
|
||||
===================
|
||||
_`Getting the code`
|
||||
===================
|
||||
===============
|
||||
Getting started
|
||||
===============
|
||||
|
||||
The code is hosted at https://github.com/python-telegram-bot/python-telegram-bot
|
||||
Our Wiki contains a lot of resources to get you started with ``python-telegram-bot``:
|
||||
|
||||
Check out the latest development version anonymously with::
|
||||
- `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>`_
|
||||
|
||||
$ git clone https://github.com/python-telegram-bot/python-telegram-bot
|
||||
$ cd python-telegram-bot
|
||||
Other references:
|
||||
|
||||
Install dependencies:
|
||||
- `Telegram API documentation <https://core.telegram.org/bots/api>`_
|
||||
- `python-telegram-bot documentation <https://pythonhosted.org/python-telegram-bot/>`_
|
||||
|
||||
$ pip install -r requirements.txt
|
||||
-------------------
|
||||
Learning by example
|
||||
-------------------
|
||||
|
||||
Run tests:
|
||||
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.
|
||||
|
||||
$ make test
|
||||
- `echobot2 <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot2.py>`_ replies back messages.
|
||||
|
||||
To see other options available, run:
|
||||
- `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>`_.
|
||||
|
||||
$ make help
|
||||
- `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.
|
||||
|
||||
==================
|
||||
_`Getting started`
|
||||
==================
|
||||
- `timerbot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/timerbot.py>`_ uses the ``JobQueue`` to send timed messages.
|
||||
|
||||
View the last release API documentation at: https://core.telegram.org/bots/api
|
||||
- `echobot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/legacy/echobot.py>`_ uses only the pure API to echo messages.
|
||||
|
||||
This library uses the `logging` module. To set up logging to standard output, put::
|
||||
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
|
||||
|
||||
import logging
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
@@ -175,265 +147,49 @@ 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.
|
||||
You can also use logs in your application by calling ``logging.getLogger()`` and setting the log level you want:
|
||||
|
||||
------
|
||||
_`API`
|
||||
------
|
||||
.. code:: python
|
||||
|
||||
Note: Using the ``Bot`` class directly is the 'old' method, but almost all of this is still important information, even if you're using the ``telegram.ext`` submodule!
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
The API is exposed via the ``telegram.Bot`` class.
|
||||
If you want DEBUG logs instead:
|
||||
|
||||
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#botfather>`_).
|
||||
.. code:: python
|
||||
|
||||
For full details see the `Bots: An introduction for developers <https://core.telegram.org/bots>`_.
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
To create an instance of the ``telegram.Bot``::
|
||||
|
||||
>>> import telegram
|
||||
>>> bot = telegram.Bot(token='token')
|
||||
=============
|
||||
Documentation
|
||||
=============
|
||||
|
||||
To see if your credentials are successful::
|
||||
``python-telegram-bot``'s documentation lives at `pythonhosted.org <https://pythonhosted.org/python-telegram-bot/>`_.
|
||||
|
||||
>>> print bot.getMe()
|
||||
{"first_name": "Toledo's Palace Bot", "username": "ToledosPalaceBot"}
|
||||
============
|
||||
Getting help
|
||||
============
|
||||
|
||||
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.
|
||||
You can get help in several ways:
|
||||
|
||||
To fetch text messages sent to your Bot::
|
||||
1. We have a vibrant community of developers helping each other in our `Telegram group <https://telegram.me/pythontelegrambotgroup>`_. Join us!
|
||||
|
||||
>>> updates = bot.getUpdates()
|
||||
>>> print [u.message.text for u in updates]
|
||||
2. Our `Wiki pages <https://github.com/python-telegram-bot/python-telegram-bot/wiki/>`_ offer a growing amount of resources.
|
||||
|
||||
To fetch images sent to your Bot::
|
||||
3. You can ask for help on Stack Overflow using the `python-telegram-bot tag <https://stackoverflow.com/questions/tagged/python-telegram-bot>`_.
|
||||
|
||||
>>> updates = bot.getUpdates()
|
||||
>>> print [u.message.photo for u in updates if u.message.photo]
|
||||
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>`_.
|
||||
|
||||
To reply messages you'll always need the chat_id::
|
||||
|
||||
>>> chat_id = bot.getUpdates()[-1].message.chat_id
|
||||
============
|
||||
Contributing
|
||||
============
|
||||
|
||||
To post a text message::
|
||||
|
||||
>>> 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::
|
||||
|
||||
>>> 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::
|
||||
|
||||
>>> 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>`_)::
|
||||
|
||||
>>> bot.sendMessage(chat_id=chat_id, text=telegram.Emoji.PILE_OF_POO)
|
||||
|
||||
To post an image file via URL::
|
||||
|
||||
>>> bot.sendPhoto(chat_id=chat_id, photo='https://telegram.org/img/t_logo.png')
|
||||
|
||||
To post an image file from disk::
|
||||
|
||||
>>> bot.sendPhoto(chat_id=chat_id, photo=open('tests/test.png', 'rb'))
|
||||
|
||||
To post a voice file from disk::
|
||||
|
||||
>>> bot.sendVoice(chat_id=chat_id, voice=open('tests/telegram.ogg', 'rb'))
|
||||
|
||||
To tell the user that something is happening on bot's side::
|
||||
|
||||
>>> bot.sendChatAction(chat_id=chat_id, action=telegram.ChatAction.TYPING)
|
||||
|
||||
To create `Custom Keyboards <https://core.telegram.org/bots#keyboards>`_::
|
||||
|
||||
>>> custom_keyboard = [[ telegram.Emoji.THUMBS_UP_SIGN, 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>`_::
|
||||
|
||||
>>> 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)::
|
||||
|
||||
>>> 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::
|
||||
|
||||
$ pydoc telegram.Bot
|
||||
|
||||
-------------
|
||||
_`Extensions`
|
||||
-------------
|
||||
|
||||
The ``telegram.ext`` submodule is built on top of the bare-metal API. It provides an easy-to-use interface to the ``telegram.Bot`` by caring about getting new updates with the ``Updater`` class from telegram and forwarding them to the ``Dispatcher`` class. We can register handler functions in the ``Dispatcher`` to make our bot react to Telegram commands, messages and even arbitrary updates.
|
||||
|
||||
We'll need an Access Token. **Note:** If you have done this in the previous step, you can use that one. To generate an Access Token, we have to talk to `BotFather <https://telegram.me/botfather>`_ and follow a few simple steps (described `here <https://core.telegram.org/bots#botfather>`_).
|
||||
|
||||
First, we create an ``Updater`` object::
|
||||
|
||||
>>> from telegram.ext import Updater
|
||||
>>> updater = Updater(token='token')
|
||||
|
||||
For quicker access to the ``Dispatcher`` used by our ``Updater``, we can introduce it locally::
|
||||
|
||||
>>> dispatcher = updater.dispatcher
|
||||
|
||||
Now, we need to define a function that should process a specific type of update::
|
||||
|
||||
>>> def start(bot, update):
|
||||
... bot.sendMessage(chat_id=update.message.chat_id, text="I'm a bot, please talk to me!")
|
||||
|
||||
We want this function to be called on a Telegram message that contains the ``/start`` command, so we need to register it in the dispatcher::
|
||||
|
||||
>>> dispatcher.addTelegramCommandHandler('start', start)
|
||||
|
||||
The last step is to tell the ``Updater`` to start working::
|
||||
|
||||
>>> updater.start_polling()
|
||||
|
||||
Our bot is now up and running (go ahead and try it)! It's not doing anything yet, besides answering to the ``/start`` command. Let's add another handler function and register it::
|
||||
|
||||
>>> def echo(bot, update):
|
||||
... bot.sendMessage(chat_id=update.message.chat_id, text=update.message.text)
|
||||
...
|
||||
>>> dispatcher.addTelegramMessageHandler(echo)
|
||||
|
||||
Our bot should now reply to all messages that are not a command with a message that has the same content.
|
||||
|
||||
People might try to send commands to the bot that it doesn't understand, so we should get that covered as well::
|
||||
|
||||
>>> def unknown(bot, update):
|
||||
... bot.sendMessage(chat_id=update.message.chat_id, text="Sorry, I didn't understand that command.")
|
||||
...
|
||||
>>> dispatcher.addUnknownTelegramCommandHandler(unknown)
|
||||
|
||||
Let's add some functionality to our bot. We want to add the ``/caps`` command, that will take some text as parameter and return it in all caps. We can get the arguments that were passed to the command in the handler function simply by adding it to the parameter list::
|
||||
|
||||
>>> def caps(bot, update, args):
|
||||
... text_caps = ' '.join(args).upper()
|
||||
... bot.sendMessage(chat_id=update.message.chat_id, text=text_caps)
|
||||
...
|
||||
>>> dispatcher.addTelegramCommandHandler('caps', caps)
|
||||
|
||||
To enable our bot to respond to inline queries, we can add the following (you will also have to talk to BotFather)::
|
||||
|
||||
>>> from telegram import InlineQueryResultArticle
|
||||
>>> def inline_caps(bot, update):
|
||||
... # If you activated inline feedback, updates might either contain
|
||||
... # inline_query or chosen_inline_result, the other one will be None
|
||||
... if update.inline_query:
|
||||
... query = bot.update.inline_query.query
|
||||
... results = list()
|
||||
... results.append(InlineQueryResultArticle(query.upper(), 'Caps', text_caps))
|
||||
... bot.answerInlineQuery(update.inline_query.id, results)
|
||||
...
|
||||
>>> dispatcher.addTelegramInlineHandler(inline_caps)
|
||||
|
||||
Now it's time to stop the bot::
|
||||
|
||||
>>> updater.stop()
|
||||
|
||||
Check out more examples in the `examples folder <https://github.com/python-telegram-bot/python-telegram-bot/tree/master/examples>`_!
|
||||
|
||||
-----------
|
||||
_`JobQueue`
|
||||
-----------
|
||||
|
||||
The ``JobQueue`` allows you to perform tasks with a delay or even periodically. The ``Updater`` will create one for you::
|
||||
|
||||
>>> from telegram.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``::
|
||||
|
||||
>>> 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::
|
||||
|
||||
>>> 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::
|
||||
|
||||
>>> u.stop()
|
||||
|
||||
We can also stop the job queue by itself::
|
||||
|
||||
>>> j.stop()
|
||||
|
||||
----------
|
||||
_`Logging`
|
||||
----------
|
||||
|
||||
You can get logs in your main application by calling `logging` and setting the log level you want::
|
||||
|
||||
>>> import logging
|
||||
>>> logger = logging.getLogger()
|
||||
>>> logger.setLevel(logging.INFO)
|
||||
|
||||
If you want DEBUG logs instead::
|
||||
|
||||
>>> logger.setLevel(logging.DEBUG)
|
||||
|
||||
-----------
|
||||
_`Examples`
|
||||
-----------
|
||||
|
||||
Here follows some examples to help you to get your own Bot up to speed:
|
||||
|
||||
- `echobot2 <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot2.py>`_ replies back messages.
|
||||
|
||||
- `clibot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/clibot.py>`_ has a command line interface.
|
||||
|
||||
- `timerbot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/timerbot.py>`_ uses the ``JobQueue`` to send timed messages.
|
||||
|
||||
- `Welcome Bot <https://github.com/jh0ker/welcomebot>`_ greets everyone who joins a group chat.
|
||||
|
||||
Legacy examples (pre-3.0):
|
||||
|
||||
- `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>`_.
|
||||
|
||||
- `Simple-Echo-Telegram-Bot <https://github.com/sooyhwang/Simple-Echo-Telegram-Bot>`_ simple Python Telegram bot that echoes your input with Flask microframework, setWebhook method, and Google App Engine (optional) - by @sooyhwang.
|
||||
|
||||
- `DevOps Reaction Bot <https://github.com/leandrotoledo/gae-devops-reaction-telegram-bot>`_ sends latest or random posts from `DevOps Reaction <http://devopsreactions.tumblr.com/>`_. Running on `Google App Engine <https://cloud.google.com/appengine>`_ (billing has to be enabled for fully Socket API support).
|
||||
|
||||
Other notable examples:
|
||||
|
||||
- `TwitterForwarderBot <https://github.com/franciscod/telegram-twitter-forwarder-bot>`_ forwards you tweets from people that you have subscribed to.
|
||||
|
||||
================
|
||||
_`Documentation`
|
||||
================
|
||||
|
||||
``python-telegram-bot``'s documentation lives at `Read the Docs <http://python-telegram-bot.readthedocs.org/en/latest/>`_.
|
||||
|
||||
==========
|
||||
_`License`
|
||||
==========
|
||||
|
||||
You may copy, distribute and modify the software provided that modifications are described and licensed for free under `LGPL-3 <http://www.gnu.org/licenses/lgpl-3.0.html>`_. Derivatives works (including modifications or anything statically linked to the library) can only be redistributed under `LGPL-3 <http://www.gnu.org/licenses/lgpl-3.0.html>`_, but applications that use the library don't have to be.
|
||||
|
||||
==========
|
||||
_`Contact`
|
||||
==========
|
||||
|
||||
Feel free to join to our `Telegram group <https://telegram.me/pythontelegrambotgroup>`_.
|
||||
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>`_.
|
||||
|
||||
=======
|
||||
_`TODO`
|
||||
License
|
||||
=======
|
||||
|
||||
Patches and bug reports are `welcome <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_, just please keep the style consistent with the original source.
|
||||
You may copy, distribute and modify the software provided that modifications are described and licensed for free under `LGPL-3 <https://www.gnu.org/licenses/lgpl-3.0.html>`_. Derivatives works (including modifications or anything statically linked to the library) can only be redistributed under LGPL-3, but applications that use the library don't have to be.
|
||||
|
||||
+1
-1
@@ -189,4 +189,4 @@ xml:
|
||||
pseudoxml:
|
||||
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
|
||||
@echo
|
||||
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
||||
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
||||
+4
-3
@@ -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 = '3.4'
|
||||
version = telegram.__version__[:3]
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '3.4.0'
|
||||
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,6 +7,7 @@ Welcome to Python Telegram Bot's documentation!
|
||||
===============================================
|
||||
|
||||
Contents:
|
||||
telegram
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
@@ -3,5 +3,4 @@ telegram.bot module
|
||||
|
||||
.. automodule:: telegram.bot
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.constants module
|
||||
=========================
|
||||
|
||||
.. automodule:: telegram.constants
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.contrib.botan module
|
||||
=============================
|
||||
|
||||
.. automodule:: telegram.contrib.botan
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.ext.callbackqueryhandler module
|
||||
========================================
|
||||
|
||||
.. automodule:: telegram.ext.callbackqueryhandler
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.ext.choseninlineresulthandler module
|
||||
=============================================
|
||||
|
||||
.. automodule:: telegram.ext.choseninlineresulthandler
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.ext.commandhandler module
|
||||
==================================
|
||||
|
||||
.. automodule:: telegram.ext.commandhandler
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.ext.conversationhandler module
|
||||
=======================================
|
||||
|
||||
.. automodule:: telegram.ext.conversationhandler
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.ext.handler module
|
||||
===========================
|
||||
|
||||
.. automodule:: telegram.ext.handler
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.ext.inlinequeryhandler module
|
||||
======================================
|
||||
|
||||
.. automodule:: telegram.ext.inlinequeryhandler
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.ext.jobqueue module
|
||||
============================
|
||||
|
||||
.. automodule:: telegram.ext.jobqueue
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.ext.messagehandler module
|
||||
==================================
|
||||
|
||||
.. automodule:: telegram.ext.messagehandler
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.ext.regexhandler module
|
||||
================================
|
||||
|
||||
.. automodule:: telegram.ext.regexhandler
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,29 @@
|
||||
telegram.ext package
|
||||
====================
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
|
||||
telegram.ext.updater
|
||||
telegram.ext.dispatcher
|
||||
telegram.ext.jobqueue
|
||||
telegram.ext.handler
|
||||
telegram.ext.choseninlineresulthandler
|
||||
telegram.ext.conversationhandler
|
||||
telegram.ext.commandhandler
|
||||
telegram.ext.inlinequeryhandler
|
||||
telegram.ext.messagehandler
|
||||
telegram.ext.regexhandler
|
||||
telegram.ext.stringcommandhandler
|
||||
telegram.ext.stringregexhandler
|
||||
telegram.ext.typehandler
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
.. automodule:: telegram.ext
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.ext.stringcommandhandler module
|
||||
========================================
|
||||
|
||||
.. automodule:: telegram.ext.stringcommandhandler
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.ext.stringregexhandler module
|
||||
======================================
|
||||
|
||||
.. automodule:: telegram.ext.stringregexhandler
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.ext.typehandler module
|
||||
===============================
|
||||
|
||||
.. automodule:: telegram.ext.typehandler
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinekeyboardbutton module
|
||||
===========================
|
||||
|
||||
.. automodule:: telegram.inlinekeyboardbutton
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinekeyboardmarkup module
|
||||
==========================
|
||||
|
||||
.. automodule:: telegram.inlinekeyboardmarkup
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultarticle module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultarticle
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultaudio module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultaudio
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultcachedaudio module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultcachedaudio
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultcacheddocument module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultcacheddocument
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultcachedgif module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultcachedgif
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultcachedmpeg4gif module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultcachedmpeg4gif
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultcachedphoto module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultcachedphoto
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultcachedsticker module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultcachedsticker
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultcachedvideo module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultcachedvideo
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultcachedvoice module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultcachedvoice
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultcontact module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultcontact
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultdocument module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultdocument
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultgif module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultgif
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultlocation module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultlocation
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultmpeg4gif module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultmpeg4gif
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultphoto module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultphoto
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultvenue module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultvenue
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultvideo module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultvideo
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.inlinequeryresultvoice module
|
||||
=================================
|
||||
|
||||
.. automodule:: telegram.inlinequeryresultvoice
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -1,7 +0,0 @@
|
||||
telegram.jobqueue module
|
||||
========================
|
||||
|
||||
.. automodule:: telegram.jobqueue
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -9,11 +9,30 @@ Submodules
|
||||
telegram.audio
|
||||
telegram.base
|
||||
telegram.bot
|
||||
telegram.ext.updater
|
||||
telegram.ext.dispatcher
|
||||
telegram.jobqueue
|
||||
telegram.ext
|
||||
telegram.inlinequery
|
||||
telegram.inlinequeryresult
|
||||
telegram.inlinekeyboardbutton
|
||||
telegram.inlinekeyboardmarkup
|
||||
telegram.inlinequeryresultarticle
|
||||
telegram.inlinequeryresultaudio
|
||||
telegram.inlinequeryresultcachedaudio
|
||||
telegram.inlinequeryresultcacheddocument
|
||||
telegram.inlinequeryresultcachedgif
|
||||
telegram.inlinequeryresultcachedmpeg4gif
|
||||
telegram.inlinequeryresultcachedphoto
|
||||
telegram.inlinequeryresultcachedsticker
|
||||
telegram.inlinequeryresultcachedvideo
|
||||
telegram.inlinequeryresultcachedvoice
|
||||
telegram.inlinequeryresultcontact
|
||||
telegram.inlinequeryresultdocument
|
||||
telegram.inlinequeryresultgif
|
||||
telegram.inlinequeryresultlocation
|
||||
telegram.inlinequeryresultmpeg4gif
|
||||
telegram.inlinequeryresultphoto
|
||||
telegram.inlinequeryresultvenue
|
||||
telegram.inlinequeryresultvideo
|
||||
telegram.inlinequeryresultvoice
|
||||
telegram.choseninlineresult
|
||||
telegram.chataction
|
||||
telegram.contact
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# 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.
|
||||
|
||||
First, a few handler functions are defined. Then, those functions are passed to
|
||||
the Dispatcher and registered at their respective places.
|
||||
Then, the bot is started and the CLI-Loop is entered, where all text inputs are
|
||||
inserted into the update queue for the bot to handle.
|
||||
|
||||
Usage:
|
||||
Repeats messages with a delay.
|
||||
Reply to last chat from the command line by typing "/reply <text>"
|
||||
Type 'stop' on the command line to stop the bot.
|
||||
"""
|
||||
|
||||
from telegram.ext import Updater
|
||||
from telegram.ext.dispatcher import run_async
|
||||
from time import sleep
|
||||
import logging
|
||||
|
||||
# Enable Logging
|
||||
logging.basicConfig(
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
level=logging.INFO)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# We use this var to save the last chat id, so we can reply to it
|
||||
last_chat_id = 0
|
||||
|
||||
|
||||
# Define a few (command) handlers. These usually take the two arguments bot and
|
||||
# update. Error handlers also receive the raised TelegramError object in error.
|
||||
def start(bot, update):
|
||||
""" Answer in Telegram """
|
||||
bot.sendMessage(update.message.chat_id, text='Hi!')
|
||||
|
||||
|
||||
def help(bot, update):
|
||||
""" Answer in Telegram """
|
||||
bot.sendMessage(update.message.chat_id, text='Help!')
|
||||
|
||||
|
||||
def any_message(bot, update):
|
||||
""" Print to console """
|
||||
|
||||
# Save last chat_id to use in reply handler
|
||||
global last_chat_id
|
||||
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))
|
||||
|
||||
|
||||
def unknown_command(bot, update):
|
||||
""" Answer in Telegram """
|
||||
bot.sendMessage(update.message.chat_id, text='Command not recognized!')
|
||||
|
||||
|
||||
@run_async
|
||||
def message(bot, update, **kwargs):
|
||||
"""
|
||||
Example for an asynchronous handler. It's not guaranteed that replies will
|
||||
be in order when using @run_async. Also, you have to include **kwargs in
|
||||
your parameter list. The kwargs contain all optional parameters that are
|
||||
"""
|
||||
|
||||
sleep(2) # IO-heavy operation here
|
||||
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
|
||||
# on the command line interface
|
||||
def cli_reply(bot, update, args):
|
||||
"""
|
||||
For any update of type telegram.Update or str that contains a command, you
|
||||
can get the argument list by appending args to the function parameters.
|
||||
Here, we reply to the last active chat with the text after the command.
|
||||
"""
|
||||
if last_chat_id is not 0:
|
||||
bot.sendMessage(chat_id=last_chat_id, text=' '.join(args))
|
||||
|
||||
|
||||
def cli_noncommand(bot, update, update_queue):
|
||||
"""
|
||||
You can also get the update queue as an argument in any handler by
|
||||
appending it to the argument list. Be careful with this though.
|
||||
Here, we put the input string back into the queue, but as a command.
|
||||
|
||||
To learn more about those optional handler parameters, read:
|
||||
http://python-telegram-bot.readthedocs.org/en/latest/telegram.dispatcher.html
|
||||
"""
|
||||
update_queue.put('/%s' % update)
|
||||
|
||||
|
||||
def unknown_cli_command(bot, update):
|
||||
logger.warn("Command not found: %s" % update)
|
||||
|
||||
|
||||
def error(bot, update, error):
|
||||
""" Print error to console """
|
||||
logger.warn('Update %s caused error %s' % (update, error))
|
||||
|
||||
|
||||
def main():
|
||||
# Create the EventHandler and pass it your bot's token.
|
||||
token = 'TOKEN'
|
||||
updater = Updater(token, workers=10)
|
||||
|
||||
# Get the dispatcher to register handlers
|
||||
dp = updater.dispatcher
|
||||
|
||||
# This is how we add handlers for Telegram messages
|
||||
dp.addTelegramCommandHandler("start", start)
|
||||
dp.addTelegramCommandHandler("help", help)
|
||||
dp.addUnknownTelegramCommandHandler(unknown_command)
|
||||
# Message handlers only receive updates that don't contain commands
|
||||
dp.addTelegramMessageHandler(message)
|
||||
# Regex handlers will receive all updates on which their regex matches
|
||||
dp.addTelegramRegexHandler('.*', any_message)
|
||||
|
||||
# String handlers work pretty much the same
|
||||
dp.addStringCommandHandler('reply', cli_reply)
|
||||
dp.addUnknownStringCommandHandler(unknown_cli_command)
|
||||
dp.addStringRegexHandler('[^/].*', cli_noncommand)
|
||||
|
||||
# All TelegramErrors are caught for you and delivered to the error
|
||||
# handler(s). Other types of Errors are not caught.
|
||||
dp.addErrorHandler(error)
|
||||
|
||||
# Start the Bot and store the update Queue, so we can insert updates
|
||||
update_queue = updater.start_polling(poll_interval=0.1, timeout=10)
|
||||
|
||||
'''
|
||||
# Alternatively, run with webhook:
|
||||
updater.bot.setWebhook(webhook_url='https://example.com/%s' % token,
|
||||
certificate=open('cert.pem', 'rb'))
|
||||
|
||||
update_queue = updater.start_webhook('0.0.0.0',
|
||||
443,
|
||||
url_path=token,
|
||||
cert='cert.pem',
|
||||
key='key.key')
|
||||
|
||||
# Or, if SSL is handled by a reverse proxy, the webhook URL is already set
|
||||
# and the reverse proxy is configured to deliver directly to port 6000:
|
||||
|
||||
update_queue = updater.start_webhook('0.0.0.0',
|
||||
6000)
|
||||
'''
|
||||
|
||||
# Start CLI-Loop
|
||||
while True:
|
||||
try:
|
||||
text = raw_input()
|
||||
except NameError:
|
||||
text = input()
|
||||
|
||||
# Gracefully stop the event handler
|
||||
if text == 'stop':
|
||||
updater.stop()
|
||||
break
|
||||
|
||||
# else, put the text into the update queue to be handled by our handlers
|
||||
elif len(text) > 0:
|
||||
update_queue.put(text)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Simple Bot to reply to Telegram messages
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
"""
|
||||
This Bot uses the Updater class to handle the bot.
|
||||
|
||||
First, a few callback functions are defined. Then, those functions are passed to
|
||||
the Dispatcher and registered at their respective places.
|
||||
Then, the bot is started and runs until we press Ctrl-C on the command line.
|
||||
|
||||
Usage:
|
||||
Example of a bot-user conversation using ConversationHandler.
|
||||
Send /start to initiate the conversation.
|
||||
Press Ctrl-C on the command line or send a signal to the process to stop the
|
||||
bot.
|
||||
"""
|
||||
|
||||
from telegram import (ReplyKeyboardMarkup)
|
||||
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, RegexHandler,
|
||||
ConversationHandler)
|
||||
|
||||
import logging
|
||||
|
||||
# Enable logging
|
||||
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
level=logging.INFO)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
GENDER, PHOTO, LOCATION, BIO = range(4)
|
||||
|
||||
|
||||
def start(bot, update):
|
||||
reply_keyboard = [['Boy', 'Girl', 'Other']]
|
||||
|
||||
bot.sendMessage(update.message.chat_id,
|
||||
text='Hi! My name is Professor Bot. I will hold a conversation with you. '
|
||||
'Send /cancel to stop talking to me.\n\n'
|
||||
'Are you a boy or a girl?',
|
||||
reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True))
|
||||
|
||||
return GENDER
|
||||
|
||||
|
||||
def gender(bot, update):
|
||||
user = update.message.from_user
|
||||
logger.info("Gender of %s: %s" % (user.first_name, update.message.text))
|
||||
bot.sendMessage(update.message.chat_id,
|
||||
text='I see! Please send me a photo of yourself, '
|
||||
'so I know what you look like, or send /skip if you don\'t want to.')
|
||||
|
||||
return PHOTO
|
||||
|
||||
|
||||
def photo(bot, update):
|
||||
user = update.message.from_user
|
||||
photo_file = bot.getFile(update.message.photo[-1].file_id)
|
||||
photo_file.download('user_photo.jpg')
|
||||
logger.info("Photo of %s: %s" % (user.first_name, 'user_photo.jpg'))
|
||||
bot.sendMessage(update.message.chat_id, text='Gorgeous! Now, send me your location please, '
|
||||
'or send /skip if you don\'t want to.')
|
||||
|
||||
return LOCATION
|
||||
|
||||
|
||||
def skip_photo(bot, update):
|
||||
user = update.message.from_user
|
||||
logger.info("User %s did not send a photo." % user.first_name)
|
||||
bot.sendMessage(update.message.chat_id, text='I bet you look great! Now, send me your '
|
||||
'location please, or send /skip.')
|
||||
|
||||
return LOCATION
|
||||
|
||||
|
||||
def location(bot, update):
|
||||
user = update.message.from_user
|
||||
user_location = update.message.location
|
||||
logger.info("Location of %s: %f / %f"
|
||||
% (user.first_name, user_location.latitude, user_location.longitude))
|
||||
bot.sendMessage(update.message.chat_id, text='Maybe I can visit you sometime! '
|
||||
'At last, tell me something about yourself.')
|
||||
|
||||
return BIO
|
||||
|
||||
|
||||
def skip_location(bot, update):
|
||||
user = update.message.from_user
|
||||
logger.info("User %s did not send a location." % user.first_name)
|
||||
bot.sendMessage(update.message.chat_id, text='You seem a bit paranoid! '
|
||||
'At last, tell me something about yourself.')
|
||||
|
||||
return BIO
|
||||
|
||||
|
||||
def bio(bot, update):
|
||||
user = update.message.from_user
|
||||
logger.info("Bio of %s: %s" % (user.first_name, update.message.text))
|
||||
bot.sendMessage(update.message.chat_id,
|
||||
text='Thank you! I hope we can talk again some day.')
|
||||
|
||||
return ConversationHandler.END
|
||||
|
||||
|
||||
def cancel(bot, update):
|
||||
user = update.message.from_user
|
||||
logger.info("User %s canceled the conversation." % user.first_name)
|
||||
bot.sendMessage(update.message.chat_id,
|
||||
text='Bye! I hope we can talk again some day.')
|
||||
|
||||
return ConversationHandler.END
|
||||
|
||||
|
||||
def error(bot, update, error):
|
||||
logger.warn('Update "%s" caused error "%s"' % (update, error))
|
||||
|
||||
|
||||
def main():
|
||||
# Create the EventHandler and pass it your bot's token.
|
||||
updater = Updater("TOKEN")
|
||||
|
||||
# Get the dispatcher to register handlers
|
||||
dp = updater.dispatcher
|
||||
|
||||
# Add conversation handler with the states GENDER, PHOTO, LOCATION and BIO
|
||||
conv_handler = ConversationHandler(
|
||||
entry_points=[CommandHandler('start', start)],
|
||||
|
||||
states={
|
||||
GENDER: [RegexHandler('^(Boy|Girl|Other)$', gender)],
|
||||
|
||||
PHOTO: [MessageHandler([Filters.photo], photo),
|
||||
CommandHandler('skip', skip_photo)],
|
||||
|
||||
LOCATION: [MessageHandler([Filters.location], location),
|
||||
CommandHandler('skip', skip_location)],
|
||||
|
||||
BIO: [MessageHandler([Filters.text], bio)]
|
||||
},
|
||||
|
||||
fallbacks=[CommandHandler('cancel', cancel)]
|
||||
)
|
||||
|
||||
dp.add_handler(conv_handler)
|
||||
|
||||
# log all errors
|
||||
dp.add_error_handler(error)
|
||||
|
||||
# Start the Bot
|
||||
updater.start_polling()
|
||||
|
||||
# Run the bot until the you presses Ctrl-C or the process receives SIGINT,
|
||||
# SIGTERM or SIGABRT. This should be used most of the time, since
|
||||
# start_polling() is non-blocking and will stop the bot gracefully.
|
||||
updater.idle()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,16 +1,19 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Simple Bot to reply to Telegram messages
|
||||
# Simple Bot to reply to Telegram messages. This is built on the API wrapper, see
|
||||
# echobot2.py to see the same example built on the telegram.ext bot framework.
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
import logging
|
||||
import telegram
|
||||
from telegram.error import NetworkError, Unauthorized
|
||||
from time import sleep
|
||||
|
||||
|
||||
update_id = None
|
||||
|
||||
def main():
|
||||
global update_id
|
||||
# Telegram Bot Authorization Token
|
||||
bot = telegram.Bot('TOKEN')
|
||||
|
||||
@@ -21,12 +24,11 @@ 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:
|
||||
update_id = echo(bot, update_id)
|
||||
echo(bot)
|
||||
except NetworkError:
|
||||
sleep(1)
|
||||
except Unauthorized:
|
||||
@@ -34,21 +36,17 @@ def main():
|
||||
update_id += 1
|
||||
|
||||
|
||||
def echo(bot, update_id):
|
||||
|
||||
def echo(bot):
|
||||
global update_id
|
||||
# Request updates after the last update_id
|
||||
for update in bot.getUpdates(offset=update_id, timeout=10):
|
||||
# chat_id is required to reply to any message
|
||||
chat_id = update.message.chat_id
|
||||
update_id = update.update_id + 1
|
||||
message = update.message.text
|
||||
|
||||
if message:
|
||||
if update.message: # your bot can receive updates without messages
|
||||
# Reply to the message
|
||||
bot.sendMessage(chat_id=chat_id,
|
||||
text=message)
|
||||
|
||||
return update_id
|
||||
bot.sendMessage(chat_id=chat_id, text=update.message.text)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@@ -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.
|
||||
|
||||
@@ -17,13 +16,12 @@ Press Ctrl-C on the command line or send a signal to the process to stop the
|
||||
bot.
|
||||
"""
|
||||
|
||||
from telegram.ext import Updater
|
||||
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
|
||||
import logging
|
||||
|
||||
# Enable logging
|
||||
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__)
|
||||
|
||||
@@ -54,14 +52,14 @@ def main():
|
||||
dp = updater.dispatcher
|
||||
|
||||
# on different commands - answer in Telegram
|
||||
dp.addTelegramCommandHandler("start", start)
|
||||
dp.addTelegramCommandHandler("help", help)
|
||||
dp.add_handler(CommandHandler("start", start))
|
||||
dp.add_handler(CommandHandler("help", help))
|
||||
|
||||
# on noncommand i.e message - echo the message on Telegram
|
||||
dp.addTelegramMessageHandler(echo)
|
||||
dp.add_handler(MessageHandler([Filters.text], echo))
|
||||
|
||||
# log all errors
|
||||
dp.addErrorHandler(error)
|
||||
dp.add_error_handler(error)
|
||||
|
||||
# Start the Bot
|
||||
updater.start_polling()
|
||||
@@ -71,5 +69,6 @@ def main():
|
||||
# start_polling() is non-blocking and will stop the bot gracefully.
|
||||
updater.idle()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
+28
-30
@@ -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.
|
||||
|
||||
@@ -16,18 +15,18 @@ Basic inline bot example. Applies different text transformations.
|
||||
Press Ctrl-C on the command line or send a signal to the process to stop the
|
||||
bot.
|
||||
"""
|
||||
from random import getrandbits
|
||||
from uuid import uuid4
|
||||
|
||||
import re
|
||||
|
||||
from telegram import InlineQueryResultArticle, ParseMode
|
||||
from telegram.ext import Updater
|
||||
from telegram import InlineQueryResultArticle, ParseMode, \
|
||||
InputTextMessageContent
|
||||
from telegram.ext import Updater, InlineQueryHandler, CommandHandler
|
||||
import logging
|
||||
|
||||
# Enable logging
|
||||
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__)
|
||||
|
||||
@@ -49,29 +48,27 @@ def escape_markdown(text):
|
||||
|
||||
|
||||
def inlinequery(bot, update):
|
||||
if update.inline_query is not None and update.inline_query.query:
|
||||
query = update.inline_query.query
|
||||
results = list()
|
||||
query = update.inline_query.query
|
||||
results = list()
|
||||
|
||||
results.append(InlineQueryResultArticle(
|
||||
id=hex(getrandbits(64))[2:],
|
||||
title="Caps",
|
||||
message_text=query.upper()))
|
||||
results.append(InlineQueryResultArticle(id=uuid4(),
|
||||
title="Caps",
|
||||
input_message_content=InputTextMessageContent(
|
||||
query.upper())))
|
||||
|
||||
results.append(InlineQueryResultArticle(
|
||||
id=hex(getrandbits(64))[2:],
|
||||
title="Bold",
|
||||
message_text="*%s*" % escape_markdown(query),
|
||||
parse_mode=ParseMode.MARKDOWN))
|
||||
results.append(InlineQueryResultArticle(id=uuid4(),
|
||||
title="Bold",
|
||||
input_message_content=InputTextMessageContent(
|
||||
"*%s*" % escape_markdown(query),
|
||||
parse_mode=ParseMode.MARKDOWN)))
|
||||
|
||||
results.append(InlineQueryResultArticle(
|
||||
id=hex(getrandbits(64))[2:],
|
||||
title="Italic",
|
||||
message_text="_%s_" % escape_markdown(query),
|
||||
parse_mode=ParseMode.MARKDOWN))
|
||||
|
||||
bot.answerInlineQuery(update.inline_query.id, results=results)
|
||||
results.append(InlineQueryResultArticle(id=uuid4(),
|
||||
title="Italic",
|
||||
input_message_content=InputTextMessageContent(
|
||||
"_%s_" % escape_markdown(query),
|
||||
parse_mode=ParseMode.MARKDOWN)))
|
||||
|
||||
bot.answerInlineQuery(update.inline_query.id, results=results)
|
||||
|
||||
|
||||
def error(bot, update, error):
|
||||
@@ -86,14 +83,14 @@ def main():
|
||||
dp = updater.dispatcher
|
||||
|
||||
# on different commands - answer in Telegram
|
||||
dp.addTelegramCommandHandler("start", start)
|
||||
dp.addTelegramCommandHandler("help", help)
|
||||
dp.add_handler(CommandHandler("start", start))
|
||||
dp.add_handler(CommandHandler("help", help))
|
||||
|
||||
# on noncommand i.e message - echo the message on Telegram
|
||||
dp.addTelegramInlineHandler(inlinequery)
|
||||
dp.add_handler(InlineQueryHandler(inlinequery))
|
||||
|
||||
# log all errors
|
||||
dp.addErrorHandler(error)
|
||||
dp.add_error_handler(error)
|
||||
|
||||
# Start the Bot
|
||||
updater.start_polling()
|
||||
@@ -103,5 +100,6 @@ def main():
|
||||
# start_polling() is non-blocking and will stop the bot gracefully.
|
||||
updater.idle()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Basic example for a bot that awaits an answer from the user. It's built upon
|
||||
# the state_machine_bot.py example
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
import logging
|
||||
from telegram import Emoji, ForceReply, InlineKeyboardButton, \
|
||||
InlineKeyboardMarkup
|
||||
from telegram.ext import Updater, CommandHandler, MessageHandler, \
|
||||
CallbackQueryHandler, Filters
|
||||
|
||||
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
level=logging.DEBUG)
|
||||
|
||||
# Define the different states a chat can be in
|
||||
MENU, AWAIT_CONFIRMATION, AWAIT_INPUT = range(3)
|
||||
|
||||
# Python 2 and 3 unicode differences
|
||||
try:
|
||||
YES, NO = (Emoji.THUMBS_UP_SIGN.decode('utf-8'), Emoji.THUMBS_DOWN_SIGN.decode('utf-8'))
|
||||
except AttributeError:
|
||||
YES, NO = (Emoji.THUMBS_UP_SIGN, Emoji.THUMBS_DOWN_SIGN)
|
||||
|
||||
# States are saved in a dict that maps chat_id -> state
|
||||
state = dict()
|
||||
# Sometimes you need to save data temporarily
|
||||
context = dict()
|
||||
# This dict is used to store the settings value for the chat.
|
||||
# Usually, you'd use persistence for this (e.g. sqlite).
|
||||
values = dict()
|
||||
|
||||
|
||||
# Example handler. Will be called on the /set command and on regular messages
|
||||
def set_value(bot, update):
|
||||
chat_id = update.message.chat_id
|
||||
user_id = update.message.from_user.id
|
||||
user_state = state.get(chat_id, MENU)
|
||||
|
||||
if user_state == MENU:
|
||||
state[user_id] = AWAIT_INPUT # set the state
|
||||
bot.sendMessage(chat_id,
|
||||
text="Please enter your settings value",
|
||||
reply_markup=ForceReply())
|
||||
|
||||
|
||||
def entered_value(bot, update):
|
||||
chat_id = update.message.chat_id
|
||||
user_id = update.message.from_user.id
|
||||
chat_state = state.get(user_id, MENU)
|
||||
|
||||
# Check if we are waiting for input
|
||||
if chat_state == AWAIT_INPUT:
|
||||
state[user_id] = AWAIT_CONFIRMATION
|
||||
|
||||
# Save the user id and the answer to context
|
||||
context[user_id] = update.message.text
|
||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton(YES, callback_data=YES),
|
||||
InlineKeyboardButton(NO, callback_data=NO)]])
|
||||
bot.sendMessage(chat_id, text="Are you sure?", reply_markup=reply_markup)
|
||||
|
||||
|
||||
def confirm_value(bot, update):
|
||||
query = update.callback_query
|
||||
chat_id = query.message.chat_id
|
||||
user_id = query.from_user.id
|
||||
text = query.data
|
||||
user_state = state.get(user_id, MENU)
|
||||
user_context = context.get(user_id, None)
|
||||
|
||||
# Check if we are waiting for confirmation and the right user answered
|
||||
if user_state == AWAIT_CONFIRMATION:
|
||||
del state[user_id]
|
||||
del context[user_id]
|
||||
bot.answerCallbackQuery(query.id, text="Ok!")
|
||||
if text == YES:
|
||||
values[user_id] = user_context
|
||||
bot.editMessageText(text="Changed value to %s." % values[user_id],
|
||||
chat_id=chat_id,
|
||||
message_id=query.message.message_id)
|
||||
else:
|
||||
bot.editMessageText(text="Alright, value is still %s." %
|
||||
values.get(user_id, 'not set'),
|
||||
chat_id=chat_id,
|
||||
message_id=query.message.message_id)
|
||||
|
||||
|
||||
def help(bot, update):
|
||||
bot.sendMessage(update.message.chat_id, text="Use /set to test this bot.")
|
||||
|
||||
|
||||
def error(bot, update, error):
|
||||
logging.warning('Update "%s" caused error "%s"' % (update, error))
|
||||
|
||||
# Create the Updater and pass it your bot's token.
|
||||
updater = Updater("TOKEN")
|
||||
|
||||
# The command
|
||||
updater.dispatcher.add_handler(CommandHandler('set', set_value))
|
||||
# The answer
|
||||
updater.dispatcher.add_handler(MessageHandler([Filters.text], entered_value))
|
||||
# The confirmation
|
||||
updater.dispatcher.add_handler(CallbackQueryHandler(confirm_value))
|
||||
updater.dispatcher.add_handler(CommandHandler('start', help))
|
||||
updater.dispatcher.add_handler(CommandHandler('help', help))
|
||||
updater.dispatcher.add_error_handler(error)
|
||||
|
||||
# Start the Bot
|
||||
updater.start_polling()
|
||||
|
||||
# Run the bot until the user presses Ctrl-C or the process receives SIGINT,
|
||||
# SIGTERM or SIGABRT
|
||||
updater.idle()
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Robô Ed Telegram Bot
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
import logging
|
||||
import telegram
|
||||
import urllib
|
||||
|
||||
|
||||
def main():
|
||||
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
|
||||
|
||||
while True:
|
||||
for update in bot.getUpdates(offset=LAST_UPDATE_ID, timeout=10):
|
||||
text = update.message.text
|
||||
chat_id = update.message.chat.id
|
||||
update_id = update.update_id
|
||||
|
||||
if text:
|
||||
roboed = ed(text) # Ask something to Robô Ed
|
||||
bot.sendMessage(chat_id=chat_id, text=roboed)
|
||||
LAST_UPDATE_ID = update_id + 1
|
||||
|
||||
|
||||
def ed(text):
|
||||
url = 'http://www.ed.conpet.gov.br/mod_perl/bot_gateway.cgi?server=0.0.0.0%3A8085&charset_post=utf-8&charset=utf-8&pure=1&js=0&tst=1&msg=' + text
|
||||
data = urllib.urlopen(url).read()
|
||||
|
||||
return data.strip()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,103 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Basic example for a bot that awaits an answer from the user
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
import logging
|
||||
from telegram import Updater, ReplyKeyboardMarkup, Emoji, ForceReply
|
||||
|
||||
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - '
|
||||
'%(message)s',
|
||||
level=logging.INFO)
|
||||
|
||||
# Define the different states a chat can be in
|
||||
MENU, AWAIT_CONFIRMATION, AWAIT_INPUT = range(3)
|
||||
|
||||
# Python 2 and 3 unicode differences
|
||||
try:
|
||||
YES, NO = (Emoji.THUMBS_UP_SIGN.decode('utf-8'),
|
||||
Emoji.THUMBS_DOWN_SIGN.decode('utf-8'))
|
||||
except AttributeError:
|
||||
YES, NO = (Emoji.THUMBS_UP_SIGN, Emoji.THUMBS_DOWN_SIGN)
|
||||
|
||||
# States are saved in a dict that maps chat_id -> state
|
||||
state = dict()
|
||||
# Sometimes you need to save data temporarily
|
||||
context = dict()
|
||||
# This dict is used to store the settings value for the chat.
|
||||
# Usually, you'd use persistence for this (e.g. sqlite).
|
||||
values = dict()
|
||||
|
||||
|
||||
# Example handler. Will be called on the /set command and on regular messages
|
||||
def set_value(bot, update):
|
||||
chat_id = update.message.chat_id
|
||||
user_id = update.message.from_user.id
|
||||
text = update.message.text
|
||||
chat_state = state.get(chat_id, MENU)
|
||||
chat_context = context.get(chat_id, None)
|
||||
|
||||
# Since the handler will also be called on messages, we need to check if
|
||||
# the message is actually a command
|
||||
if chat_state == MENU and text[0] == '/':
|
||||
state[chat_id] = AWAIT_INPUT # set the state
|
||||
context[chat_id] = user_id # save the user id to context
|
||||
bot.sendMessage(chat_id,
|
||||
text="Please enter your settings value or send "
|
||||
"/cancel to abort",
|
||||
reply_markup=ForceReply())
|
||||
|
||||
# If we are waiting for input and the right user answered
|
||||
elif chat_state == AWAIT_INPUT and chat_context == user_id:
|
||||
state[chat_id] = AWAIT_CONFIRMATION
|
||||
|
||||
# Save the user id and the answer to context
|
||||
context[chat_id] = (user_id, update.message.text)
|
||||
reply_markup = ReplyKeyboardMarkup([[YES, NO]], one_time_keyboard=True)
|
||||
bot.sendMessage(chat_id, text="Are you sure?",
|
||||
reply_markup=reply_markup)
|
||||
|
||||
# If we are waiting for confirmation and the right user answered
|
||||
elif chat_state == AWAIT_CONFIRMATION and chat_context[0] == user_id:
|
||||
state[chat_id] = MENU
|
||||
context[chat_id] = None
|
||||
if text == YES:
|
||||
values[chat_id] = chat_context[1]
|
||||
bot.sendMessage(chat_id,
|
||||
text="Changed value to %s." % values[chat_id])
|
||||
else:
|
||||
bot.sendMessage(chat_id,
|
||||
text="Value not changed: %s."
|
||||
% values.get(chat_id, '<not set>'))
|
||||
|
||||
|
||||
# Handler for the /cancel command.
|
||||
# Sets the state back to MENU and clears the context
|
||||
def cancel(bot, update):
|
||||
chat_id = update.message.chat_id
|
||||
state[chat_id] = MENU
|
||||
context[chat_id] = None
|
||||
|
||||
|
||||
def help(bot, update):
|
||||
bot.sendMessage(update.message.chat_id, text="Use /set to test this bot.")
|
||||
|
||||
|
||||
# Create the Updater and pass it your bot's token.
|
||||
updater = Updater("TOKEN")
|
||||
|
||||
# The command
|
||||
updater.dispatcher.addTelegramCommandHandler('set', set_value)
|
||||
# The answer and confirmation
|
||||
updater.dispatcher.addTelegramMessageHandler(set_value)
|
||||
updater.dispatcher.addTelegramCommandHandler('cancel', cancel)
|
||||
updater.dispatcher.addTelegramCommandHandler('start', help)
|
||||
updater.dispatcher.addTelegramCommandHandler('help', help)
|
||||
|
||||
# Start the Bot
|
||||
updater.start_polling()
|
||||
|
||||
# Run the bot until the user presses Ctrl-C or the process receives SIGINT,
|
||||
# SIGTERM or SIGABRT
|
||||
updater.idle()
|
||||
+39
-25
@@ -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.
|
||||
@@ -18,67 +17,81 @@ Press Ctrl-C on the command line or send a signal to the process to stop the
|
||||
bot.
|
||||
"""
|
||||
|
||||
from telegram.ext import Updater
|
||||
from telegram.ext import Updater, CommandHandler, Job
|
||||
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.DEBUG)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
job_queue = None
|
||||
timers = dict()
|
||||
|
||||
|
||||
# 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):
|
||||
""" Adds a job to the queue """
|
||||
def alarm(bot, job):
|
||||
"""Function to send the alarm message"""
|
||||
bot.sendMessage(job.context, text='Beep!')
|
||||
|
||||
|
||||
def set(bot, update, args, job_queue):
|
||||
"""Adds a job to the queue"""
|
||||
chat_id = update.message.chat_id
|
||||
try:
|
||||
# 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!')
|
||||
def alarm(bot):
|
||||
""" Inner function to send the alarm message """
|
||||
bot.sendMessage(chat_id, text='Beep!')
|
||||
bot.sendMessage(chat_id, text='Sorry we can not go back to future!')
|
||||
|
||||
# Add job to queue
|
||||
job_queue.put(alarm, due, repeat=False)
|
||||
job = Job(alarm, due, repeat=False, context=chat_id)
|
||||
timers[chat_id] = job
|
||||
job_queue.put(job)
|
||||
|
||||
bot.sendMessage(chat_id, text='Timer successfully set!')
|
||||
|
||||
except IndexError:
|
||||
bot.sendMessage(chat_id, text='Usage: /set <seconds>')
|
||||
except ValueError:
|
||||
except (IndexError, ValueError):
|
||||
bot.sendMessage(chat_id, text='Usage: /set <seconds>')
|
||||
|
||||
|
||||
def unset(bot, update):
|
||||
"""Removes the job if the user changed their mind"""
|
||||
chat_id = update.message.chat_id
|
||||
|
||||
if chat_id not in timers:
|
||||
bot.sendMessage(chat_id, text='You have no active timer')
|
||||
return
|
||||
|
||||
job = timers[chat_id]
|
||||
job.schedule_removal()
|
||||
del timers[chat_id]
|
||||
|
||||
bot.sendMessage(chat_id, text='Timer successfully unset!')
|
||||
|
||||
|
||||
def error(bot, update, error):
|
||||
logger.warn('Update "%s" caused error "%s"' % (update, error))
|
||||
|
||||
|
||||
def main():
|
||||
global job_queue
|
||||
|
||||
updater = Updater("TOKEN")
|
||||
job_queue = updater.job_queue
|
||||
|
||||
# Get the dispatcher to register handlers
|
||||
dp = updater.dispatcher
|
||||
|
||||
# on different commands - answer in Telegram
|
||||
dp.addTelegramCommandHandler("start", start)
|
||||
dp.addTelegramCommandHandler("help", start)
|
||||
dp.addTelegramCommandHandler("set", set)
|
||||
dp.add_handler(CommandHandler("start", start))
|
||||
dp.add_handler(CommandHandler("help", start))
|
||||
dp.add_handler(CommandHandler("set", set, pass_args=True, pass_job_queue=True))
|
||||
dp.add_handler(CommandHandler("unset", unset))
|
||||
|
||||
# log all errors
|
||||
dp.addErrorHandler(error)
|
||||
dp.add_error_handler(error)
|
||||
|
||||
# Start the Bot
|
||||
updater.start_polling()
|
||||
@@ -88,5 +101,6 @@ def main():
|
||||
# start_polling() is non-blocking and will stop the bot gracefully.
|
||||
updater.idle()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -3,4 +3,7 @@ nose
|
||||
pep257
|
||||
pylint
|
||||
unittest2
|
||||
flaky
|
||||
flaky
|
||||
yapf
|
||||
pre-commit
|
||||
pre-commit-hooks
|
||||
|
||||
+3
-1
@@ -1 +1,3 @@
|
||||
future
|
||||
future>=0.15.2
|
||||
urllib3>=1.10
|
||||
certifi
|
||||
|
||||
@@ -8,3 +8,12 @@ all_files = 1
|
||||
|
||||
[upload_sphinx]
|
||||
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
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
"""The setup and build script for the python-telegram-bot library."""
|
||||
|
||||
'''The setup and build script for the python-telegram-bot library.'''
|
||||
|
||||
import os
|
||||
|
||||
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 = []
|
||||
@@ -23,35 +16,33 @@ def requirements():
|
||||
|
||||
return requirements_list
|
||||
|
||||
|
||||
setup(
|
||||
name='python-telegram-bot',
|
||||
version='3.4',
|
||||
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',
|
||||
],)
|
||||
|
||||
+64
-47
@@ -16,12 +16,14 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""A library that provides a Python interface to the Telegram Bot API"""
|
||||
|
||||
from sys import version_info
|
||||
|
||||
from .base import TelegramObject
|
||||
from .user import User
|
||||
from .chat import Chat
|
||||
from .chatmember import ChatMember
|
||||
from .photosize import PhotoSize
|
||||
from .audio import Audio
|
||||
from .voice import Voice
|
||||
@@ -30,8 +32,10 @@ from .sticker import Sticker
|
||||
from .video import Video
|
||||
from .contact import Contact
|
||||
from .location import Location
|
||||
from .venue import Venue
|
||||
from .chataction import ChatAction
|
||||
from .userprofilephotos import UserProfilePhotos
|
||||
from .keyboardbutton import KeyboardButton
|
||||
from .replymarkup import ReplyMarkup
|
||||
from .replykeyboardmarkup import ReplyKeyboardMarkup
|
||||
from .replykeyboardhide import ReplyKeyboardHide
|
||||
@@ -42,55 +46,68 @@ from .file import File
|
||||
from .nullhandler import NullHandler
|
||||
from .emoji import Emoji
|
||||
from .parsemode import ParseMode
|
||||
from .messageentity import MessageEntity
|
||||
from .message import Message
|
||||
from .inlinequery import InlineQuery
|
||||
from .inputmessagecontent import InputMessageContent
|
||||
from .callbackquery import CallbackQuery
|
||||
from .choseninlineresult import ChosenInlineResult
|
||||
from .inlinequeryresult import InlineQueryResultArticle, InlineQueryResultGif,\
|
||||
InlineQueryResultMpeg4Gif, InlineQueryResultPhoto, InlineQueryResultVideo
|
||||
from .inlinekeyboardbutton import InlineKeyboardButton
|
||||
from .inlinekeyboardmarkup import InlineKeyboardMarkup
|
||||
from .inlinequery import InlineQuery
|
||||
from .inlinequeryresult import InlineQueryResult
|
||||
from .inlinequeryresultarticle import InlineQueryResultArticle
|
||||
from .inlinequeryresultaudio import InlineQueryResultAudio
|
||||
from .inlinequeryresultcachedaudio import InlineQueryResultCachedAudio
|
||||
from .inlinequeryresultcacheddocument import InlineQueryResultCachedDocument
|
||||
from .inlinequeryresultcachedgif import InlineQueryResultCachedGif
|
||||
from .inlinequeryresultcachedmpeg4gif import InlineQueryResultCachedMpeg4Gif
|
||||
from .inlinequeryresultcachedphoto import InlineQueryResultCachedPhoto
|
||||
from .inlinequeryresultcachedsticker import InlineQueryResultCachedSticker
|
||||
from .inlinequeryresultcachedvideo import InlineQueryResultCachedVideo
|
||||
from .inlinequeryresultcachedvoice import InlineQueryResultCachedVoice
|
||||
from .inlinequeryresultcontact import InlineQueryResultContact
|
||||
from .inlinequeryresultdocument import InlineQueryResultDocument
|
||||
from .inlinequeryresultgif import InlineQueryResultGif
|
||||
from .inlinequeryresultlocation import InlineQueryResultLocation
|
||||
from .inlinequeryresultmpeg4gif import InlineQueryResultMpeg4Gif
|
||||
from .inlinequeryresultphoto import InlineQueryResultPhoto
|
||||
from .inlinequeryresultvenue import InlineQueryResultVenue
|
||||
from .inlinequeryresultvideo import InlineQueryResultVideo
|
||||
from .inlinequeryresultvoice import InlineQueryResultVoice
|
||||
from .inputtextmessagecontent import InputTextMessageContent
|
||||
from .inputlocationmessagecontent import InputLocationMessageContent
|
||||
from .inputvenuemessagecontent import InputVenueMessageContent
|
||||
from .inputcontactmessagecontent import InputContactMessageContent
|
||||
from .update import Update
|
||||
from .bot import Bot
|
||||
|
||||
|
||||
def Updater(*args, **kwargs):
|
||||
"""
|
||||
Load the updater module on invocation and return an Updater instance.
|
||||
"""
|
||||
import warnings
|
||||
warnings.warn("telegram.Updater is being deprecated, please use "
|
||||
"telegram.ext.Updater from now on.")
|
||||
from .ext.updater import Updater as Up
|
||||
return Up(*args, **kwargs)
|
||||
|
||||
|
||||
def Dispatcher(*args, **kwargs):
|
||||
"""
|
||||
Load the dispatcher module on invocation and return an Dispatcher instance.
|
||||
"""
|
||||
import warnings
|
||||
warnings.warn("telegram.Dispatcher is being deprecated, please use "
|
||||
"telegram.ext.Dispatcher from now on.")
|
||||
from .ext.dispatcher import Dispatcher as Dis
|
||||
return Dis(*args, **kwargs)
|
||||
|
||||
|
||||
def JobQueue(*args, **kwargs):
|
||||
"""
|
||||
Load the jobqueue module on invocation and return a JobQueue instance.
|
||||
"""
|
||||
import warnings
|
||||
warnings.warn("telegram.JobQueue is being deprecated, please use "
|
||||
"telegram.ext.JobQueue from now on.")
|
||||
from .ext.jobqueue import JobQueue as JobQ
|
||||
return JobQ(*args, **kwargs)
|
||||
|
||||
from .constants import (MAX_MESSAGE_LENGTH, MAX_CAPTION_LENGTH, SUPPORTED_WEBHOOK_PORTS,
|
||||
MAX_FILESIZE_DOWNLOAD, MAX_FILESIZE_UPLOAD,
|
||||
MAX_MESSAGES_PER_SECOND_PER_CHAT, MAX_MESSAGES_PER_SECOND,
|
||||
MAX_MESSAGES_PER_MINUTE_PER_GROUP)
|
||||
|
||||
__author__ = 'devs@python-telegram-bot.org'
|
||||
__version__ = '3.4'
|
||||
__all__ = ('Audio', 'Bot', 'Chat', 'Emoji', 'TelegramError', 'InputFile',
|
||||
'Contact', 'ForceReply', 'ReplyKeyboardHide', 'ReplyKeyboardMarkup',
|
||||
'UserProfilePhotos', 'ChatAction', 'Location', 'Video', 'Document',
|
||||
'Sticker', 'File', 'PhotoSize', 'Update', 'ParseMode', 'Message',
|
||||
'User', 'TelegramObject', 'NullHandler', 'Voice', 'InlineQuery',
|
||||
'ReplyMarkup', 'ChosenInlineResult', 'InlineQueryResultArticle',
|
||||
'InlineQueryResultGif', 'InlineQueryResultPhoto',
|
||||
'InlineQueryResultMpeg4Gif', 'InlineQueryResultVideo')
|
||||
__version__ = '5.0.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', 'MAX_MESSAGE_LENGTH', 'MAX_CAPTION_LENGTH', 'SUPPORTED_WEBHOOK_PORTS',
|
||||
'MAX_FILESIZE_DOWNLOAD', 'MAX_FILESIZE_UPLOAD', 'MAX_MESSAGES_PER_SECOND_PER_CHAT',
|
||||
'MAX_MESSAGES_PER_SECOND', 'MAX_MESSAGES_PER_MINUTE_PER_GROUP']
|
||||
|
||||
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 version to at least Python 2.7!")
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import sys
|
||||
|
||||
import urllib3
|
||||
import certifi
|
||||
import future
|
||||
|
||||
from . import __version__ as telegram_ver
|
||||
|
||||
|
||||
def print_ver_info():
|
||||
print('python-telegram-bot {0}'.format(telegram_ver))
|
||||
print('urllib3 {0}'.format(urllib3.__version__))
|
||||
print('certifi {0}'.format(certifi.__version__))
|
||||
print('future {0}'.format(future.__version__))
|
||||
print('Python {0}'.format(sys.version.replace('\n', ' ')))
|
||||
|
||||
# main
|
||||
print_ver_info()
|
||||
+1
-5
@@ -16,7 +16,6 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""This module contains a object that represents a Telegram Audio."""
|
||||
|
||||
from telegram import TelegramObject
|
||||
@@ -45,10 +44,7 @@ class Audio(TelegramObject):
|
||||
file_size (Optional[int]):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
file_id,
|
||||
duration,
|
||||
**kwargs):
|
||||
def __init__(self, file_id, duration, **kwargs):
|
||||
# Required
|
||||
self.file_id = str(file_id)
|
||||
self.duration = int(duration)
|
||||
|
||||
+9
-4
@@ -16,7 +16,6 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""Base class for Telegram Objects."""
|
||||
|
||||
import json
|
||||
@@ -43,7 +42,12 @@ class TelegramObject(object):
|
||||
Returns:
|
||||
telegram.TelegramObject:
|
||||
"""
|
||||
raise NotImplementedError
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = data.copy()
|
||||
|
||||
return data
|
||||
|
||||
def to_json(self):
|
||||
"""
|
||||
@@ -59,8 +63,9 @@ class TelegramObject(object):
|
||||
"""
|
||||
data = dict()
|
||||
|
||||
for key, value in self.__dict__.items():
|
||||
if value:
|
||||
for key in iter(self.__dict__):
|
||||
value = self.__dict__[key]
|
||||
if value is not None:
|
||||
if hasattr(value, 'to_dict'):
|
||||
data[key] = value.to_dict()
|
||||
else:
|
||||
|
||||
+984
-372
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Leandro Toledo de Souza <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
|
||||
CallbackQuery"""
|
||||
|
||||
from telegram import TelegramObject, Message, User
|
||||
|
||||
|
||||
class CallbackQuery(TelegramObject):
|
||||
"""This object represents a Telegram CallbackQuery."""
|
||||
|
||||
def __init__(self, id, from_user, data, **kwargs):
|
||||
# Required
|
||||
self.id = id
|
||||
self.from_user = from_user
|
||||
self.data = data
|
||||
# Optionals
|
||||
self.message = kwargs.get('message')
|
||||
self.inline_message_id = kwargs.get('inline_message_id', '')
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['from_user'] = User.de_json(data.get('from'))
|
||||
data['message'] = Message.de_json(data.get('message'))
|
||||
|
||||
return CallbackQuery(**data)
|
||||
|
||||
def to_dict(self):
|
||||
"""
|
||||
Returns:
|
||||
dict:
|
||||
"""
|
||||
data = super(CallbackQuery, self).to_dict()
|
||||
|
||||
# Required
|
||||
data['from'] = data.pop('from_user', None)
|
||||
return data
|
||||
+6
-5
@@ -17,7 +17,6 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""This module contains a object that represents a Telegram Chat."""
|
||||
|
||||
from telegram import TelegramObject
|
||||
@@ -43,10 +42,12 @@ class Chat(TelegramObject):
|
||||
type (Optional[str]):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
id,
|
||||
type,
|
||||
**kwargs):
|
||||
PRIVATE = 'private'
|
||||
GROUP = 'group'
|
||||
SUPERGROUP = 'supergroup'
|
||||
CHANNEL = 'channel'
|
||||
|
||||
def __init__(self, id, type, **kwargs):
|
||||
# Required
|
||||
self.id = int(id)
|
||||
self.type = type
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""This module contains a object that represents a Telegram ChatAction."""
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -16,13 +16,11 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""
|
||||
This module contains a object that represents a Telegram ChosenInlineResult
|
||||
"""
|
||||
|
||||
|
||||
from telegram import TelegramObject, User
|
||||
from telegram import TelegramObject, User, Location
|
||||
|
||||
|
||||
class ChosenInlineResult(TelegramObject):
|
||||
@@ -46,11 +44,17 @@ class ChosenInlineResult(TelegramObject):
|
||||
def __init__(self,
|
||||
result_id,
|
||||
from_user,
|
||||
query):
|
||||
query,
|
||||
location=None,
|
||||
inline_message_id=None,
|
||||
**kwargs):
|
||||
# Required
|
||||
self.result_id = result_id
|
||||
self.from_user = from_user
|
||||
self.query = query
|
||||
# Optionals
|
||||
self.location = location
|
||||
self.inline_message_id = inline_message_id
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
@@ -63,8 +67,11 @@ class ChosenInlineResult(TelegramObject):
|
||||
"""
|
||||
if not data:
|
||||
return None
|
||||
data = data.copy()
|
||||
|
||||
# Required
|
||||
data['from_user'] = User.de_json(data.pop('from'))
|
||||
# Optionals
|
||||
data['location'] = Location.de_json(data.get('location'))
|
||||
|
||||
return ChosenInlineResult(**data)
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# python-telegram-bot - a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# by the python-telegram-bot contributors <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/].
|
||||
"""Constants in the Telegram network.
|
||||
|
||||
Attributes:
|
||||
MAX_MESSAGE_LENGTH (int): from
|
||||
https://core.telegram.org/method/messages.sendMessage#return-errors
|
||||
MAX_CAPTION_LENGTH (int): from https://core.telegram.org/bots/api#sendphoto
|
||||
|
||||
The following constants were extracted from the
|
||||
`Telegram Bots FAQ <https://core.telegram.org/bots/faq>`_.
|
||||
|
||||
Attributes:
|
||||
SUPPORTED_WEBHOOK_PORTS (List[int])
|
||||
MAX_FILESIZE_DOWNLOAD (int): In bytes.
|
||||
MAX_FILESIZE_UPLOAD (int): Official limit, the actual limit can be a bit higher.
|
||||
MAX_MESSAGES_PER_SECOND_PER_CHAT (int): Telegram may allow short bursts that go over this
|
||||
limit, but eventually you'll begin receiving 429 errors.
|
||||
MAX_MESSAGES_PER_SECOND (int)
|
||||
MAX_MESSAGES_PER_MINUTE_PER_GROUP (int)
|
||||
"""
|
||||
|
||||
MAX_MESSAGE_LENGTH = 4096
|
||||
MAX_CAPTION_LENGTH = 200
|
||||
|
||||
# constants above this line are tested
|
||||
|
||||
SUPPORTED_WEBHOOK_PORTS = [443, 80, 88, 8443]
|
||||
MAX_FILESIZE_DOWNLOAD = int(20E6) # (20MB)
|
||||
MAX_FILESIZE_UPLOAD = int(50E6) # (50MB)
|
||||
MAX_MESSAGES_PER_SECOND_PER_CHAT = 1
|
||||
MAX_MESSAGES_PER_SECOND = 30
|
||||
MAX_MESSAGES_PER_MINUTE_PER_GROUP = 20
|
||||
+1
-5
@@ -16,7 +16,6 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""This module contains a object that represents a Telegram Contact."""
|
||||
|
||||
from telegram import TelegramObject
|
||||
@@ -41,10 +40,7 @@ class Contact(TelegramObject):
|
||||
user_id (Optional[int]):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
phone_number,
|
||||
first_name,
|
||||
**kwargs):
|
||||
def __init__(self, phone_number, first_name, **kwargs):
|
||||
# Required
|
||||
self.phone_number = str(phone_number)
|
||||
self.first_name = first_name
|
||||
|
||||
@@ -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
|
||||
@@ -16,7 +16,6 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""This module contains a object that represents a Telegram Document."""
|
||||
|
||||
from telegram import PhotoSize, TelegramObject
|
||||
@@ -43,9 +42,7 @@ class Document(TelegramObject):
|
||||
file_size (Optional[int]):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
file_id,
|
||||
**kwargs):
|
||||
def __init__(self, file_id, **kwargs):
|
||||
# Required
|
||||
self.file_id = str(file_id)
|
||||
# Optionals
|
||||
|
||||
+23
-23
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# flake8: noqa
|
||||
# pylint: disable=C0103,C0301,R0903
|
||||
# pylint: disable=C0103,R0903
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
@@ -18,7 +18,6 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""This module contains a object that represents an Emoji."""
|
||||
|
||||
from future.utils import bytes_to_native_str as n
|
||||
@@ -163,26 +162,26 @@ class Emoji(object):
|
||||
SQUARED_SOS = n(b'\xF0\x9F\x86\x98')
|
||||
SQUARED_UP_WITH_EXCLAMATION_MARK = n(b'\xF0\x9F\x86\x99')
|
||||
SQUARED_VS = n(b'\xF0\x9F\x86\x9A')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_D_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_E\
|
||||
= n(b'\xF0\x9F\x87\xA9\xF0\x9F\x87\xAA')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_G_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_B\
|
||||
= n(b'\xF0\x9F\x87\xAC\xF0\x9F\x87\xA7')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_C_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_N\
|
||||
= n(b'\xF0\x9F\x87\xA8\xF0\x9F\x87\xB3')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_J_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_P\
|
||||
= n(b'\xF0\x9F\x87\xAF\xF0\x9F\x87\xB5')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_K_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_R\
|
||||
= n(b'\xF0\x9F\x87\xB0\xF0\x9F\x87\xB7')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_F_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_R\
|
||||
= n(b'\xF0\x9F\x87\xAB\xF0\x9F\x87\xB7')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_E_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_S\
|
||||
= n(b'\xF0\x9F\x87\xAA\xF0\x9F\x87\xB8')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_I_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_T\
|
||||
= n(b'\xF0\x9F\x87\xAE\xF0\x9F\x87\xB9')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_U_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_S\
|
||||
= n(b'\xF0\x9F\x87\xBA\xF0\x9F\x87\xB8')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_R_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_U\
|
||||
= n(b'\xF0\x9F\x87\xB7\xF0\x9F\x87\xBA')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_D_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_E = n(
|
||||
b'\xF0\x9F\x87\xA9\xF0\x9F\x87\xAA')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_G_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_B = n(
|
||||
b'\xF0\x9F\x87\xAC\xF0\x9F\x87\xA7')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_C_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_N = n(
|
||||
b'\xF0\x9F\x87\xA8\xF0\x9F\x87\xB3')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_J_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_P = n(
|
||||
b'\xF0\x9F\x87\xAF\xF0\x9F\x87\xB5')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_K_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_R = n(
|
||||
b'\xF0\x9F\x87\xB0\xF0\x9F\x87\xB7')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_F_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_R = n(
|
||||
b'\xF0\x9F\x87\xAB\xF0\x9F\x87\xB7')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_E_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_S = n(
|
||||
b'\xF0\x9F\x87\xAA\xF0\x9F\x87\xB8')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_I_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_T = n(
|
||||
b'\xF0\x9F\x87\xAE\xF0\x9F\x87\xB9')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_U_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_S = n(
|
||||
b'\xF0\x9F\x87\xBA\xF0\x9F\x87\xB8')
|
||||
REGIONAL_INDICATOR_SYMBOL_LETTER_R_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_U = n(
|
||||
b'\xF0\x9F\x87\xB7\xF0\x9F\x87\xBA')
|
||||
SQUARED_KATAKANA_KOKO = n(b'\xF0\x9F\x88\x81')
|
||||
SQUARED_KATAKANA_SA = n(b'\xF0\x9F\x88\x82')
|
||||
SQUARED_CJK_UNIFIED_IDEOGRAPH_7121 = n(b'\xF0\x9F\x88\x9A')
|
||||
@@ -858,7 +857,8 @@ class Emoji(object):
|
||||
NO_MOBILE_PHONES = n(b'\xF0\x9F\x93\xB5')
|
||||
TWISTED_RIGHTWARDS_ARROWS = n(b'\xF0\x9F\x94\x80')
|
||||
CLOCKWISE_RIGHTWARDS_AND_LEFTWARDS_OPEN_CIRCLE_ARROWS = n(b'\xF0\x9F\x94\x81')
|
||||
CLOCKWISE_RIGHTWARDS_AND_LEFTWARDS_OPEN_CIRCLE_ARROWS_WITH_CIRCLED_ONE_OVERLAY = n(b'\xF0\x9F\x94\x82')
|
||||
CLOCKWISE_RIGHTWARDS_AND_LEFTWARDS_OPEN_CIRCLE_ARROWS_WITH_CIRCLED_ONE_OVERLAY = n(
|
||||
b'\xF0\x9F\x94\x82')
|
||||
ANTICLOCKWISE_DOWNWARDS_AND_UPWARDS_OPEN_CIRCLE_ARROWS = n(b'\xF0\x9F\x94\x84')
|
||||
LOW_BRIGHTNESS_SYMBOL = n(b'\xF0\x9F\x94\x85')
|
||||
HIGH_BRIGHTNESS_SYMBOL = n(b'\xF0\x9F\x94\x86')
|
||||
|
||||
+5
-1
@@ -16,7 +16,6 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""This module contains a object that represents a Telegram Error."""
|
||||
|
||||
|
||||
@@ -52,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()
|
||||
@@ -77,6 +77,10 @@ class NetworkError(TelegramError):
|
||||
pass
|
||||
|
||||
|
||||
class BadRequest(NetworkError):
|
||||
pass
|
||||
|
||||
|
||||
class TimedOut(NetworkError):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
@@ -16,11 +16,24 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""Extensions over the Telegram Bot API to facilitate bot making"""
|
||||
|
||||
from .dispatcher import Dispatcher
|
||||
from .jobqueue import JobQueue
|
||||
from .jobqueue import JobQueue, Job
|
||||
from .updater import Updater
|
||||
from .callbackqueryhandler import CallbackQueryHandler
|
||||
from .choseninlineresulthandler import ChosenInlineResultHandler
|
||||
from .commandhandler import CommandHandler
|
||||
from .handler import Handler
|
||||
from .inlinequeryhandler import InlineQueryHandler
|
||||
from .messagehandler import MessageHandler, Filters
|
||||
from .regexhandler import RegexHandler
|
||||
from .stringcommandhandler import StringCommandHandler
|
||||
from .stringregexhandler import StringRegexHandler
|
||||
from .typehandler import TypeHandler
|
||||
from .conversationhandler import ConversationHandler
|
||||
|
||||
__all__ = ('Dispatcher', 'JobQueue', 'Updater')
|
||||
__all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler',
|
||||
'ChosenInlineResultHandler', 'CommandHandler', 'Handler', 'InlineQueryHandler',
|
||||
'MessageHandler', 'Filters', 'RegexHandler', 'StringCommandHandler',
|
||||
'StringRegexHandler', 'TypeHandler', 'ConversationHandler')
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Leandro Toledo de Souza <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 the CallbackQueryHandler class """
|
||||
|
||||
from .handler import Handler
|
||||
from telegram import Update
|
||||
from telegram.utils.deprecate import deprecate
|
||||
|
||||
|
||||
class CallbackQueryHandler(Handler):
|
||||
"""
|
||||
Handler class to handle Telegram callback queries.
|
||||
|
||||
Args:
|
||||
callback (function): A function that takes ``bot, update`` as
|
||||
positional arguments. It will be called when the ``check_update``
|
||||
has determined that an update should be processed by this handler.
|
||||
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``update_queue`` will be passed to the callback function. It will be the ``Queue``
|
||||
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
|
||||
be used to insert updates. Default is ``False``.
|
||||
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
|
||||
instance created by the ``Updater`` which can be used to schedule new jobs.
|
||||
Default is ``False``.
|
||||
"""
|
||||
|
||||
def __init__(self, callback, pass_update_queue=False, pass_job_queue=False):
|
||||
super(CallbackQueryHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
|
||||
def check_update(self, update):
|
||||
return isinstance(update, Update) and update.callback_query
|
||||
|
||||
def handle_update(self, update, dispatcher):
|
||||
optional_args = self.collect_optional_args(dispatcher)
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
# old non-PEP8 Handler methods
|
||||
m = "telegram.CallbackQueryHandler."
|
||||
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
|
||||
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
|
||||
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Leandro Toledo de Souza <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 the ChosenInlineResultHandler class """
|
||||
|
||||
from .handler import Handler
|
||||
from telegram import Update
|
||||
from telegram.utils.deprecate import deprecate
|
||||
|
||||
|
||||
class ChosenInlineResultHandler(Handler):
|
||||
"""
|
||||
Handler class to handle Telegram updates that contain a chosen inline
|
||||
result.
|
||||
|
||||
Args:
|
||||
callback (function): A function that takes ``bot, update`` as
|
||||
positional arguments. It will be called when the ``check_update``
|
||||
has determined that an update should be processed by this handler.
|
||||
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``update_queue`` will be passed to the callback function. It will be the ``Queue``
|
||||
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
|
||||
be used to insert updates. Default is ``False``.
|
||||
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
|
||||
instance created by the ``Updater`` which can be used to schedule new jobs.
|
||||
Default is ``False``.
|
||||
"""
|
||||
|
||||
def __init__(self, callback, pass_update_queue=False, pass_job_queue=False):
|
||||
super(ChosenInlineResultHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
|
||||
def check_update(self, update):
|
||||
return isinstance(update, Update) and update.chosen_inline_result
|
||||
|
||||
def handle_update(self, update, dispatcher):
|
||||
optional_args = self.collect_optional_args(dispatcher)
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
# old non-PEP8 Handler methods
|
||||
m = "telegram.ChosenInlineResultHandler."
|
||||
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
|
||||
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
|
||||
@@ -0,0 +1,91 @@
|
||||
#!/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 the CommandHandler class """
|
||||
|
||||
from .handler import Handler
|
||||
from telegram import Update
|
||||
from telegram.utils.deprecate import deprecate
|
||||
|
||||
|
||||
class CommandHandler(Handler):
|
||||
"""
|
||||
Handler class to handle Telegram commands. Commands are Telegram messages
|
||||
that start with ``/``, optionally followed by an ``@`` and the bot's
|
||||
name and/or some additional text.
|
||||
|
||||
Args:
|
||||
command (str): The name of the command this handler should listen for.
|
||||
callback (function): A function that takes ``bot, update`` as
|
||||
positional arguments. It will be called when the ``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
|
||||
following the command split on spaces. Default is ``False``
|
||||
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``update_queue`` will be passed to the callback function. It will be the ``Queue``
|
||||
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
|
||||
be used to insert updates. Default is ``False``.
|
||||
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
|
||||
instance created by the ``Updater`` which can be used to schedule new jobs.
|
||||
Default is ``False``.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
command,
|
||||
callback,
|
||||
allow_edited=False,
|
||||
pass_args=False,
|
||||
pass_update_queue=False,
|
||||
pass_job_queue=False):
|
||||
super(CommandHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
self.command = command
|
||||
self.allow_edited = allow_edited
|
||||
self.pass_args = pass_args
|
||||
|
||||
def check_update(self, update):
|
||||
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'] = message.text.split(' ')[1:]
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
# old non-PEP8 Handler methods
|
||||
m = "telegram.CommandHandler."
|
||||
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
|
||||
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
|
||||
@@ -0,0 +1,222 @@
|
||||
#!/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 the ConversationHandler """
|
||||
|
||||
import logging
|
||||
|
||||
from telegram import Update
|
||||
from telegram.ext import Handler
|
||||
from telegram.utils.promise import Promise
|
||||
|
||||
|
||||
class ConversationHandler(Handler):
|
||||
"""
|
||||
A handler to hold a conversation with a user by managing four collections of other handlers.
|
||||
|
||||
The first collection, a ``list`` named ``entry_points``, is used to initiate the conversation,
|
||||
for example with a ``CommandHandler`` or ``RegexHandler``.
|
||||
|
||||
The second collection, a ``dict`` named ``states``, contains the different conversation steps
|
||||
and one or more associated handlers that should be used if the user sends a message when the
|
||||
conversation with them is currently in that state. You will probably use mostly
|
||||
``MessageHandler`` and ``RegexHandler`` here.
|
||||
|
||||
The third collection, a ``list`` named ``fallbacks``, is used if the user is currently in a
|
||||
conversation but the state has either no associated handler or the handler that is associated
|
||||
to the state is inappropriate for the update, for example if the update contains a command, but
|
||||
a regular text message is expected. You could use this for a ``/cancel`` command or to let the
|
||||
user know their message was not recognized.
|
||||
|
||||
The fourth, optional collection of handlers, a ``list`` named ``timed_out_behavior`` is used if
|
||||
the wait for ``run_async`` takes longer than defined in ``run_async_timeout``. For example,
|
||||
you can let the user know that they should wait for a bit before they can continue.
|
||||
|
||||
To change the state of conversation, the callback function of a handler must return the new
|
||||
state after responding to the user. If it does not return anything (returning ``None`` by
|
||||
default), the state will not change. To end the conversation, the callback function must
|
||||
return ``CallbackHandler.END`` or ``-1``.
|
||||
|
||||
Args:
|
||||
entry_points (list): A list of ``Handler`` objects that can trigger the start of the
|
||||
conversation. The first handler which ``check_update`` method returns ``True`` will be
|
||||
used. If all return ``False``, the update is not handled.
|
||||
states (dict): A ``dict[object: list[Handler]]`` that defines the different states of
|
||||
conversation a user can be in and one or more associated ``Handler`` objects that
|
||||
should be used in that state. The first handler which ``check_update`` method returns
|
||||
``True`` will be used.
|
||||
fallbacks (list): A list of handlers that might be used if the user is in a conversation,
|
||||
but every handler for their current state returned ``False`` on ``check_update``.
|
||||
The first handler which ``check_update`` method returns ``True`` will be used. If all
|
||||
return ``False``, the update is not handled.
|
||||
allow_reentry (Optional[bool]): If set to ``True``, a user that is currently in a
|
||||
conversation can restart the conversation by triggering one of the entry points.
|
||||
run_async_timeout (Optional[float]): If the previous handler for this user was running
|
||||
asynchronously using the ``run_async`` decorator, it might not be finished when the
|
||||
next message arrives. This timeout defines how long the conversation handler should
|
||||
wait for the next state to be computed. The default is ``None`` which means it will
|
||||
wait indefinitely.
|
||||
timed_out_behavior (Optional[list]): A list of handlers that might be used if
|
||||
the wait for ``run_async`` timed out. The first handler which ``check_update`` method
|
||||
returns ``True`` will be used. If all return ``False``, the update is not handled.
|
||||
|
||||
"""
|
||||
|
||||
END = -1
|
||||
|
||||
def __init__(self,
|
||||
entry_points,
|
||||
states,
|
||||
fallbacks,
|
||||
allow_reentry=False,
|
||||
run_async_timeout=None,
|
||||
timed_out_behavior=None):
|
||||
|
||||
self.entry_points = entry_points
|
||||
""":type: list[telegram.ext.Handler]"""
|
||||
|
||||
self.states = states
|
||||
""":type: dict[str: telegram.ext.Handler]"""
|
||||
|
||||
self.fallbacks = fallbacks
|
||||
""":type: list[telegram.ext.Handler]"""
|
||||
|
||||
self.allow_reentry = allow_reentry
|
||||
self.run_async_timeout = run_async_timeout
|
||||
|
||||
self.timed_out_behavior = timed_out_behavior
|
||||
""":type: list[telegram.ext.Handler]"""
|
||||
|
||||
self.conversations = dict()
|
||||
""":type: dict[(int, int): str]"""
|
||||
|
||||
self.current_conversation = None
|
||||
self.current_handler = None
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
def check_update(self, update):
|
||||
|
||||
if not isinstance(update, Update):
|
||||
return False
|
||||
|
||||
user = None
|
||||
chat = None
|
||||
|
||||
if update.message:
|
||||
user = update.message.from_user
|
||||
chat = update.message.chat
|
||||
|
||||
elif update.edited_message:
|
||||
user = update.edited_message.from_user
|
||||
chat = update.edited_message.chat
|
||||
|
||||
elif update.inline_query:
|
||||
user = update.inline_query.from_user
|
||||
|
||||
elif update.chosen_inline_result:
|
||||
user = update.chosen_inline_result.from_user
|
||||
|
||||
elif update.callback_query:
|
||||
user = update.callback_query.from_user
|
||||
chat = update.callback_query.message.chat if update.callback_query.message else None
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
key = (chat.id, user.id) if chat else (None, user.id)
|
||||
state = self.conversations.get(key)
|
||||
|
||||
# Resolve promises
|
||||
if isinstance(state, tuple) and len(state) is 2 and isinstance(state[1], Promise):
|
||||
self.logger.debug('waiting for promise...')
|
||||
|
||||
old_state, new_state = state
|
||||
new_state.result(timeout=self.run_async_timeout)
|
||||
|
||||
if new_state.done.is_set():
|
||||
self.update_state(new_state.result(), key)
|
||||
state = self.conversations.get(key)
|
||||
|
||||
else:
|
||||
for candidate in (self.timed_out_behavior or []):
|
||||
if candidate.check_update(update):
|
||||
# Save the current user and the selected handler for handle_update
|
||||
self.current_conversation = key
|
||||
self.current_handler = candidate
|
||||
|
||||
return True
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
self.logger.debug('selecting conversation %s with state %s' % (str(key), str(state)))
|
||||
|
||||
handler = None
|
||||
|
||||
# Search entry points for a match
|
||||
if state is None or self.allow_reentry:
|
||||
for entry_point in self.entry_points:
|
||||
if entry_point.check_update(update):
|
||||
handler = entry_point
|
||||
break
|
||||
|
||||
else:
|
||||
if state is None:
|
||||
return False
|
||||
|
||||
# Get the handler list for current state, if we didn't find one yet and we're still here
|
||||
if state is not None and not handler:
|
||||
handlers = self.states.get(state)
|
||||
|
||||
for candidate in (handlers or []):
|
||||
if candidate.check_update(update):
|
||||
handler = candidate
|
||||
break
|
||||
|
||||
# Find a fallback handler if all other handlers fail
|
||||
else:
|
||||
for fallback in self.fallbacks:
|
||||
if fallback.check_update(update):
|
||||
handler = fallback
|
||||
break
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
# Save the current user and the selected handler for handle_update
|
||||
self.current_conversation = key
|
||||
self.current_handler = handler
|
||||
|
||||
return True
|
||||
|
||||
def handle_update(self, update, dispatcher):
|
||||
|
||||
new_state = self.current_handler.handle_update(update, dispatcher)
|
||||
|
||||
self.update_state(new_state, self.current_conversation)
|
||||
|
||||
def update_state(self, new_state, key):
|
||||
if new_state == self.END:
|
||||
del self.conversations[key]
|
||||
|
||||
elif isinstance(new_state, Promise):
|
||||
self.conversations[key] = (self.conversations[key], new_state)
|
||||
|
||||
elif new_state is not None:
|
||||
self.conversations[key] = new_state
|
||||
+161
-551
@@ -16,32 +16,54 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""This module contains the Dispatcher class."""
|
||||
|
||||
import logging
|
||||
from functools import wraps
|
||||
from inspect import getargspec
|
||||
from threading import Thread, BoundedSemaphore, Lock, Event, current_thread
|
||||
from re import match, split
|
||||
from threading import Thread, Lock, Event, current_thread
|
||||
from time import sleep
|
||||
from queue import Queue, Empty
|
||||
|
||||
from telegram import (TelegramError, Update, NullHandler)
|
||||
from telegram.utils.updatequeue import Empty
|
||||
from future.builtins import range
|
||||
|
||||
from telegram import (TelegramError, NullHandler)
|
||||
from telegram.utils import request
|
||||
from telegram.ext.handler import Handler
|
||||
from telegram.utils.deprecate import deprecate
|
||||
from telegram.utils.promise import Promise
|
||||
|
||||
logging.getLogger(__name__).addHandler(NullHandler())
|
||||
|
||||
semaphore = None
|
||||
async_threads = set()
|
||||
ASYNC_QUEUE = Queue()
|
||||
ASYNC_THREADS = set()
|
||||
""":type: set[Thread]"""
|
||||
async_lock = Lock()
|
||||
ASYNC_LOCK = Lock() # guards ASYNC_THREADS
|
||||
DEFAULT_GROUP = 0
|
||||
|
||||
|
||||
def _pooled():
|
||||
"""
|
||||
A wrapper to run a thread in a thread pool
|
||||
"""
|
||||
while 1:
|
||||
promise = ASYNC_QUEUE.get()
|
||||
|
||||
# If unpacking fails, the thread pool is being closed from Updater._join_async_threads
|
||||
if not isinstance(promise, Promise):
|
||||
logging.getLogger(__name__).debug("Closing run_async thread %s/%d" %
|
||||
(current_thread().getName(), len(ASYNC_THREADS)))
|
||||
break
|
||||
|
||||
try:
|
||||
promise.run()
|
||||
|
||||
except:
|
||||
logging.getLogger(__name__).exception("run_async function raised exception")
|
||||
|
||||
|
||||
def run_async(func):
|
||||
"""
|
||||
Function decorator that will run the function in a new thread. A function
|
||||
decorated with this will have to include **kwargs in their parameter list,
|
||||
which will contain all optional parameters.
|
||||
Function decorator that will run the function in a new thread.
|
||||
|
||||
Args:
|
||||
func (function): The function to run in the thread.
|
||||
@@ -54,110 +76,65 @@ def run_async(func):
|
||||
# set a threading.Event to notify caller thread
|
||||
|
||||
@wraps(func)
|
||||
def pooled(*pargs, **kwargs):
|
||||
"""
|
||||
A wrapper to run a thread in a thread pool
|
||||
"""
|
||||
result = func(*pargs, **kwargs)
|
||||
semaphore.release()
|
||||
with async_lock:
|
||||
async_threads.remove(current_thread())
|
||||
return result
|
||||
|
||||
@wraps(func)
|
||||
def async_func(*pargs, **kwargs):
|
||||
def async_func(*args, **kwargs):
|
||||
"""
|
||||
A wrapper to run a function in a thread
|
||||
"""
|
||||
thread = Thread(target=pooled, args=pargs, kwargs=kwargs)
|
||||
semaphore.acquire()
|
||||
with async_lock:
|
||||
async_threads.add(thread)
|
||||
thread.start()
|
||||
return thread
|
||||
promise = Promise(func, args, kwargs)
|
||||
ASYNC_QUEUE.put(promise)
|
||||
return promise
|
||||
|
||||
return async_func
|
||||
|
||||
|
||||
class Dispatcher:
|
||||
class Dispatcher(object):
|
||||
"""
|
||||
This class dispatches all kinds of updates to its registered handlers.
|
||||
A handler is a function that usually takes the following parameters
|
||||
|
||||
bot:
|
||||
The telegram.Bot instance that received the message
|
||||
update:
|
||||
The update that should be handled by the handler
|
||||
|
||||
Error handlers take an additional parameter
|
||||
|
||||
error:
|
||||
The TelegramError instance that was raised during processing the
|
||||
update
|
||||
|
||||
All handlers, except error handlers, can also request more information by
|
||||
appending one or more of the following arguments in their argument list for
|
||||
convenience
|
||||
|
||||
update_queue:
|
||||
The Queue instance which contains all new updates and is
|
||||
processed by the Dispatcher. Be careful with this - you might
|
||||
create an infinite loop.
|
||||
args:
|
||||
If the update is an instance str or telegram.Update, this will be
|
||||
a list that contains the content of the message split on spaces,
|
||||
except the first word (usually the command).
|
||||
Example: '/add item1 item2 item3' -> ['item1', 'item2', 'item3']
|
||||
For updates that contain inline queries, they will contain the
|
||||
whole query split on spaces.
|
||||
For other updates, args will be None
|
||||
|
||||
In some cases handlers may need some context data to process the update. To
|
||||
procedure just queue in update_queue.put(update, context=context) or
|
||||
processUpdate(update,context=context).
|
||||
|
||||
context:
|
||||
Extra data for handling updates.
|
||||
|
||||
For regex-based handlers, you can also request information about the match.
|
||||
For all other handlers, these will be None
|
||||
|
||||
groups:
|
||||
A tuple that contains the result of
|
||||
re.match(matcher, ...).groups()
|
||||
groupdict:
|
||||
A dictionary that contains the result of
|
||||
re.match(matcher, ...).groupdict()
|
||||
|
||||
Args:
|
||||
bot (telegram.Bot): The bot object that should be passed to the
|
||||
handlers
|
||||
update_queue (telegram.UpdateQueue): The synchronized queue that will
|
||||
contain the updates.
|
||||
update_queue (Queue): The synchronized queue that will contain the
|
||||
updates.
|
||||
job_queue (Optional[telegram.ext.JobQueue]): The ``JobQueue`` instance to pass onto handler
|
||||
callbacks
|
||||
workers (Optional[int]): Number of maximum concurrent worker threads for the ``@run_async``
|
||||
decorator
|
||||
"""
|
||||
def __init__(self, bot, update_queue, workers=4, exception_event=None):
|
||||
|
||||
def __init__(self, bot, update_queue, workers=4, exception_event=None, job_queue=None):
|
||||
self.bot = bot
|
||||
self.update_queue = update_queue
|
||||
self.telegram_message_handlers = []
|
||||
self.telegram_inline_handlers = []
|
||||
self.telegram_command_handlers = {}
|
||||
self.telegram_regex_handlers = {}
|
||||
self.string_regex_handlers = {}
|
||||
self.string_command_handlers = {}
|
||||
self.type_handlers = {}
|
||||
self.unknown_telegram_command_handlers = []
|
||||
self.unknown_string_command_handlers = []
|
||||
self.job_queue = job_queue
|
||||
|
||||
self.handlers = {}
|
||||
""":type: dict[int, list[Handler]"""
|
||||
self.groups = []
|
||||
""":type: list[int]"""
|
||||
self.error_handlers = []
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.running = False
|
||||
self.__stop_event = Event()
|
||||
self.__exception_event = exception_event or Event()
|
||||
|
||||
global semaphore
|
||||
if not semaphore:
|
||||
semaphore = BoundedSemaphore(value=workers)
|
||||
else:
|
||||
self.logger.debug('Semaphore already initialized, skipping.')
|
||||
with ASYNC_LOCK:
|
||||
if not ASYNC_THREADS:
|
||||
if request.is_con_pool_initialized():
|
||||
raise RuntimeError('Connection Pool already initialized')
|
||||
|
||||
# we need a connection pool the size of:
|
||||
# * for each of the workers
|
||||
# * 1 for Dispatcher
|
||||
# * 1 for polling Updater (even if updater is webhook, we can spare a connection)
|
||||
# * 1 for JobQueue
|
||||
request.CON_POOL_SIZE = workers + 3
|
||||
for i in range(workers):
|
||||
thread = Thread(target=_pooled, name=str(i))
|
||||
ASYNC_THREADS.add(thread)
|
||||
thread.start()
|
||||
else:
|
||||
self.logger.debug('Thread pool already initialized, skipping.')
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
@@ -180,37 +157,18 @@ class Dispatcher:
|
||||
while 1:
|
||||
try:
|
||||
# Pop update from update queue.
|
||||
update, context = self.update_queue.get(True, 1, True)
|
||||
update = self.update_queue.get(True, 1)
|
||||
except Empty:
|
||||
if self.__stop_event.is_set():
|
||||
self.logger.debug('orderly stopping')
|
||||
break
|
||||
elif self.__stop_event.is_set():
|
||||
self.logger.critical(
|
||||
'stopping due to exception in another thread')
|
||||
elif self.__exception_event.is_set():
|
||||
self.logger.critical('stopping due to exception in another thread')
|
||||
break
|
||||
continue
|
||||
|
||||
try:
|
||||
self.processUpdate(update, context)
|
||||
self.logger.debug('Processed Update: %s with context %s'
|
||||
% (update, context))
|
||||
|
||||
# Dispatch any errors
|
||||
except TelegramError as te:
|
||||
self.logger.warn("Error was raised while processing Update.")
|
||||
|
||||
try:
|
||||
self.dispatchError(update, te)
|
||||
# Log errors in error handlers
|
||||
except:
|
||||
self.logger.exception("An uncaught error was raised while "
|
||||
"handling the error")
|
||||
|
||||
# All other errors should not stop the thread, just print them
|
||||
except:
|
||||
self.logger.exception("An uncaught error was raised while "
|
||||
"processing an update")
|
||||
self.logger.debug('Processing Update: %s' % update)
|
||||
self.process_update(update)
|
||||
|
||||
self.running = False
|
||||
self.logger.debug('Dispatcher thread stopped')
|
||||
@@ -225,481 +183,133 @@ class Dispatcher:
|
||||
sleep(0.1)
|
||||
self.__stop_event.clear()
|
||||
|
||||
def processUpdate(self, update, context=None):
|
||||
def process_update(self, update):
|
||||
"""
|
||||
Processes a single update.
|
||||
|
||||
Args:
|
||||
update (any):
|
||||
update (object):
|
||||
"""
|
||||
|
||||
handled = False
|
||||
|
||||
# Custom type handlers
|
||||
for t in self.type_handlers:
|
||||
if isinstance(update, t):
|
||||
self.dispatchType(update, context)
|
||||
handled = True
|
||||
|
||||
# string update
|
||||
if type(update) is str and update.startswith('/'):
|
||||
self.dispatchStringCommand(update, context)
|
||||
handled = True
|
||||
elif type(update) is str:
|
||||
self.dispatchRegex(update, context)
|
||||
handled = True
|
||||
|
||||
# An error happened while polling
|
||||
if isinstance(update, TelegramError):
|
||||
self.dispatchError(None, update)
|
||||
handled = True
|
||||
self.dispatch_error(None, update)
|
||||
|
||||
# Telegram update (regex)
|
||||
if isinstance(update, Update) and update.message is not None:
|
||||
self.dispatchRegex(update, context)
|
||||
handled = True
|
||||
else:
|
||||
for group in self.groups:
|
||||
for handler in self.handlers[group]:
|
||||
try:
|
||||
if handler.check_update(update):
|
||||
handler.handle_update(update, self)
|
||||
break
|
||||
# Dispatch any errors
|
||||
except TelegramError as te:
|
||||
self.logger.warn('A TelegramError was raised while processing the '
|
||||
'Update.')
|
||||
|
||||
# Telegram update (command)
|
||||
if update.message.text.startswith('/'):
|
||||
self.dispatchTelegramCommand(update, context)
|
||||
try:
|
||||
self.dispatch_error(update, te)
|
||||
except Exception:
|
||||
self.logger.exception('An uncaught error was raised while '
|
||||
'handling the error')
|
||||
finally:
|
||||
break
|
||||
|
||||
# Telegram update (message)
|
||||
else:
|
||||
self.dispatchTelegramMessage(update, context)
|
||||
handled = True
|
||||
elif isinstance(update, Update) and \
|
||||
(update.inline_query is not None or
|
||||
update.chosen_inline_result is not None):
|
||||
self.dispatchTelegramInline(update, context)
|
||||
handled = True
|
||||
# Update not recognized
|
||||
if not handled:
|
||||
self.dispatchError(update, TelegramError(
|
||||
"Received update of unknown type %s" % type(update)))
|
||||
# Errors should not stop the thread
|
||||
except Exception:
|
||||
self.logger.exception('An uncaught error was raised while '
|
||||
'processing the update')
|
||||
break
|
||||
|
||||
# Add Handlers
|
||||
def addTelegramMessageHandler(self, handler):
|
||||
def add_handler(self, handler, group=DEFAULT_GROUP):
|
||||
"""
|
||||
Registers a message handler in the Dispatcher.
|
||||
Register a handler.
|
||||
|
||||
TL;DR: Order and priority counts. 0 or 1 handlers per group will be
|
||||
used.
|
||||
|
||||
A handler must be an instance of a subclass of
|
||||
telegram.ext.Handler. All handlers are organized in groups with a
|
||||
numeric value. The default group is 0. All groups will be evaluated for
|
||||
handling an update, but only 0 or 1 handler per group will be used.
|
||||
|
||||
The priority/order of handlers is determined as follows:
|
||||
|
||||
* Priority of the group (lower group number == higher priority)
|
||||
|
||||
* The first handler in a group which should handle an update will be
|
||||
used. Other handlers from the group will not be used. The order in
|
||||
which handlers were added to the group defines the priority.
|
||||
|
||||
Args:
|
||||
handler (function): A function that takes (Bot, Update, *args) as
|
||||
arguments.
|
||||
handler (Handler): A Handler instance
|
||||
group (Optional[int]): The group identifier. Default is 0
|
||||
"""
|
||||
|
||||
self.telegram_message_handlers.append(handler)
|
||||
if not isinstance(handler, Handler):
|
||||
raise TypeError('handler is not an instance of {0}'.format(Handler.__name__))
|
||||
if not isinstance(group, int):
|
||||
raise TypeError('group is not int')
|
||||
|
||||
def addTelegramInlineHandler(self, handler):
|
||||
if group not in self.handlers:
|
||||
self.handlers[group] = list()
|
||||
self.groups.append(group)
|
||||
self.groups = sorted(self.groups)
|
||||
|
||||
self.handlers[group].append(handler)
|
||||
|
||||
def remove_handler(self, handler, group=DEFAULT_GROUP):
|
||||
"""
|
||||
Registers an inline query handler in the Dispatcher.
|
||||
Remove a handler from the specified group
|
||||
|
||||
Args:
|
||||
handler (function): A function that takes (Bot, Update, *args) as
|
||||
arguments.
|
||||
handler (Handler): A Handler instance
|
||||
group (optional[object]): The group identifier. Default is 0
|
||||
"""
|
||||
if handler in self.handlers[group]:
|
||||
self.handlers[group].remove(handler)
|
||||
if not self.handlers[group]:
|
||||
del self.handlers[group]
|
||||
self.groups.remove(group)
|
||||
|
||||
self.telegram_inline_handlers.append(handler)
|
||||
|
||||
def addTelegramCommandHandler(self, command, handler):
|
||||
"""
|
||||
Registers a command handler in the Dispatcher.
|
||||
|
||||
Args:
|
||||
command (str): The command keyword that this handler should be
|
||||
listening to.
|
||||
handler (function): A function that takes (Bot, Update, *args) as
|
||||
arguments.
|
||||
"""
|
||||
|
||||
if command not in self.telegram_command_handlers:
|
||||
self.telegram_command_handlers[command] = []
|
||||
|
||||
self.telegram_command_handlers[command].append(handler)
|
||||
|
||||
def addTelegramRegexHandler(self, matcher, handler):
|
||||
"""
|
||||
Registers a regex handler in the Dispatcher. If handlers will be
|
||||
called if re.match(matcher, update.message.text) is True.
|
||||
|
||||
Args:
|
||||
matcher (str/__Regex): A regex string or compiled regex object that
|
||||
matches on messages that handler should be listening to
|
||||
handler (function): A function that takes (Bot, Update, *args) as
|
||||
arguments.
|
||||
"""
|
||||
|
||||
if matcher not in self.telegram_regex_handlers:
|
||||
self.telegram_regex_handlers[matcher] = []
|
||||
|
||||
self.telegram_regex_handlers[matcher].append(handler)
|
||||
|
||||
def addStringCommandHandler(self, command, handler):
|
||||
"""
|
||||
Registers a string-command handler in the Dispatcher.
|
||||
|
||||
Args:
|
||||
command (str): The command keyword that this handler should be
|
||||
listening to.
|
||||
handler (function): A function that takes (Bot, str, *args) as
|
||||
arguments.
|
||||
"""
|
||||
|
||||
if command not in self.string_command_handlers:
|
||||
self.string_command_handlers[command] = []
|
||||
|
||||
self.string_command_handlers[command].append(handler)
|
||||
|
||||
def addStringRegexHandler(self, matcher, handler):
|
||||
"""
|
||||
Registers a regex handler in the Dispatcher. If handlers will be
|
||||
called if re.match(matcher, string) is True.
|
||||
|
||||
Args:
|
||||
matcher (str/__Regex): A regex string or compiled regex object that
|
||||
matches on the string input that handler should be listening to
|
||||
handler (function): A function that takes (Bot, Update, *args) as
|
||||
arguments.
|
||||
"""
|
||||
|
||||
if matcher not in self.string_regex_handlers:
|
||||
self.string_regex_handlers[matcher] = []
|
||||
|
||||
self.string_regex_handlers[matcher].append(handler)
|
||||
|
||||
def addUnknownTelegramCommandHandler(self, handler):
|
||||
"""
|
||||
Registers a command handler in the Dispatcher, that will receive all
|
||||
commands that have no associated handler.
|
||||
|
||||
Args:
|
||||
handler (function): A function that takes (Bot, Update, *args) as
|
||||
arguments.
|
||||
"""
|
||||
|
||||
self.unknown_telegram_command_handlers.append(handler)
|
||||
|
||||
def addUnknownStringCommandHandler(self, handler):
|
||||
"""
|
||||
Registers a string-command handler in the Dispatcher, that will
|
||||
receive all commands that have no associated handler.
|
||||
|
||||
Args:
|
||||
handler (function): A function that takes (Bot, str, *args) as
|
||||
arguments.
|
||||
"""
|
||||
|
||||
self.unknown_string_command_handlers.append(handler)
|
||||
|
||||
def addErrorHandler(self, handler):
|
||||
def add_error_handler(self, callback):
|
||||
"""
|
||||
Registers an error handler in the Dispatcher.
|
||||
|
||||
Args:
|
||||
handler (function): A function that takes (Bot, TelegramError) as
|
||||
arguments.
|
||||
handler (function): A function that takes ``Bot, Update,
|
||||
TelegramError`` as arguments.
|
||||
"""
|
||||
|
||||
self.error_handlers.append(handler)
|
||||
self.error_handlers.append(callback)
|
||||
|
||||
def addTypeHandler(self, the_type, handler):
|
||||
"""
|
||||
Registers a type handler in the Dispatcher. This allows you to send
|
||||
any type of object into the update queue.
|
||||
|
||||
Args:
|
||||
the_type (type): The type this handler should listen to
|
||||
handler (function): A function that takes (Bot, type, *args) as
|
||||
arguments.
|
||||
"""
|
||||
|
||||
if the_type not in self.type_handlers:
|
||||
self.type_handlers[the_type] = []
|
||||
|
||||
self.type_handlers[the_type].append(handler)
|
||||
|
||||
# Remove Handlers
|
||||
def removeTelegramMessageHandler(self, handler):
|
||||
"""
|
||||
De-registers a message handler.
|
||||
|
||||
Args:
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if handler in self.telegram_message_handlers:
|
||||
self.telegram_message_handlers.remove(handler)
|
||||
|
||||
def removeTelegramInlineHandler(self, handler):
|
||||
"""
|
||||
De-registers an inline query handler.
|
||||
|
||||
Args:
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if handler in self.telegram_inline_handlers:
|
||||
self.telegram_inline_handlers.remove(handler)
|
||||
|
||||
def removeTelegramCommandHandler(self, command, handler):
|
||||
"""
|
||||
De-registers a command handler.
|
||||
|
||||
Args:
|
||||
command (str): The command
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if command in self.telegram_command_handlers \
|
||||
and handler in self.telegram_command_handlers[command]:
|
||||
self.telegram_command_handlers[command].remove(handler)
|
||||
|
||||
def removeTelegramRegexHandler(self, matcher, handler):
|
||||
"""
|
||||
De-registers a regex handler.
|
||||
|
||||
Args:
|
||||
matcher (str/__Regex): The regex matcher object or string
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if matcher in self.telegram_regex_handlers \
|
||||
and handler in self.telegram_regex_handlers[matcher]:
|
||||
self.telegram_regex_handlers[matcher].remove(handler)
|
||||
|
||||
def removeStringCommandHandler(self, command, handler):
|
||||
"""
|
||||
De-registers a string-command handler.
|
||||
|
||||
Args:
|
||||
command (str): The command
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if command in self.string_command_handlers \
|
||||
and handler in self.string_command_handlers[command]:
|
||||
self.string_command_handlers[command].remove(handler)
|
||||
|
||||
def removeStringRegexHandler(self, matcher, handler):
|
||||
"""
|
||||
De-registers a regex handler.
|
||||
|
||||
Args:
|
||||
matcher (str/__Regex): The regex matcher object or string
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if matcher in self.string_regex_handlers \
|
||||
and handler in self.string_regex_handlers[matcher]:
|
||||
self.string_regex_handlers[matcher].remove(handler)
|
||||
|
||||
def removeUnknownTelegramCommandHandler(self, handler):
|
||||
"""
|
||||
De-registers an unknown-command handler.
|
||||
|
||||
Args:
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if handler in self.unknown_telegram_command_handlers:
|
||||
self.unknown_telegram_command_handlers.remove(handler)
|
||||
|
||||
def removeUnknownStringCommandHandler(self, handler):
|
||||
"""
|
||||
De-registers an unknown-command handler.
|
||||
|
||||
Args:
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if handler in self.unknown_string_command_handlers:
|
||||
self.unknown_string_command_handlers.remove(handler)
|
||||
|
||||
def removeErrorHandler(self, handler):
|
||||
def remove_error_handler(self, callback):
|
||||
"""
|
||||
De-registers an error handler.
|
||||
|
||||
Args:
|
||||
handler (any):
|
||||
handler (function):
|
||||
"""
|
||||
|
||||
if handler in self.error_handlers:
|
||||
self.error_handlers.remove(handler)
|
||||
if callback in self.error_handlers:
|
||||
self.error_handlers.remove(callback)
|
||||
|
||||
def removeTypeHandler(self, the_type, handler):
|
||||
"""
|
||||
De-registers a type handler.
|
||||
|
||||
Args:
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if the_type in self.type_handlers \
|
||||
and handler in self.type_handlers[the_type]:
|
||||
self.type_handlers[the_type].remove(handler)
|
||||
|
||||
def dispatchTelegramCommand(self, update, context=None):
|
||||
"""
|
||||
Dispatches an update that contains a command.
|
||||
|
||||
Args:
|
||||
command (str): The command keyword
|
||||
update (telegram.Update): The Telegram update that contains the
|
||||
command
|
||||
"""
|
||||
|
||||
command = split('\W', update.message.text[1:])[0]
|
||||
|
||||
if command in self.telegram_command_handlers:
|
||||
self.dispatchTo(self.telegram_command_handlers[command], update,
|
||||
context=context)
|
||||
else:
|
||||
self.dispatchTo(self.unknown_telegram_command_handlers, update,
|
||||
context=context)
|
||||
|
||||
def dispatchRegex(self, update, context=None):
|
||||
"""
|
||||
Dispatches an update to all string or telegram regex handlers that
|
||||
match the string/message content.
|
||||
|
||||
Args:
|
||||
update (str, Update): The update that should be checked for matches
|
||||
"""
|
||||
|
||||
if isinstance(update, Update):
|
||||
handlers = self.telegram_regex_handlers
|
||||
to_match = update.message.text
|
||||
elif isinstance(update, str):
|
||||
handlers = self.string_regex_handlers
|
||||
to_match = update
|
||||
|
||||
for matcher in handlers:
|
||||
m = match(matcher, to_match)
|
||||
if m:
|
||||
for handler in handlers[matcher]:
|
||||
self.call_handler(handler,
|
||||
update,
|
||||
groups=m.groups(),
|
||||
groupdict=m.groupdict(),
|
||||
context=context)
|
||||
|
||||
def dispatchStringCommand(self, update, context=None):
|
||||
"""
|
||||
Dispatches a string-update that contains a command.
|
||||
|
||||
Args:
|
||||
update (str): The string input
|
||||
"""
|
||||
|
||||
command = update.split(' ')[0][1:]
|
||||
|
||||
if command in self.string_command_handlers:
|
||||
self.dispatchTo(self.string_command_handlers[command], update,
|
||||
context=context)
|
||||
else:
|
||||
self.dispatchTo(self.unknown_string_command_handlers, update,
|
||||
context=context)
|
||||
|
||||
def dispatchType(self, update, context=None):
|
||||
"""
|
||||
Dispatches an update of any type.
|
||||
|
||||
Args:
|
||||
update (any): The update
|
||||
"""
|
||||
|
||||
for t in self.type_handlers:
|
||||
if isinstance(update, t):
|
||||
self.dispatchTo(self.type_handlers[t], update, context=context)
|
||||
|
||||
def dispatchTelegramMessage(self, update, context=None):
|
||||
"""
|
||||
Dispatches an update that contains a regular message.
|
||||
|
||||
Args:
|
||||
update (telegram.Update): The Telegram update that contains the
|
||||
message.
|
||||
"""
|
||||
|
||||
self.dispatchTo(self.telegram_message_handlers, update,
|
||||
context=context)
|
||||
|
||||
def dispatchTelegramInline(self, update, context=None):
|
||||
"""
|
||||
Dispatches an update that contains an inline update.
|
||||
|
||||
Args:
|
||||
update (telegram.Update): The Telegram update that contains the
|
||||
message.
|
||||
"""
|
||||
|
||||
self.dispatchTo(self.telegram_inline_handlers, update, context=None)
|
||||
|
||||
def dispatchError(self, update, error):
|
||||
def dispatch_error(self, update, error):
|
||||
"""
|
||||
Dispatches an error.
|
||||
|
||||
Args:
|
||||
update (any): The pdate that caused the error
|
||||
update (object): The update that caused the error
|
||||
error (telegram.TelegramError): The Telegram error that was raised.
|
||||
"""
|
||||
|
||||
for handler in self.error_handlers:
|
||||
handler(self.bot, update, error)
|
||||
for callback in self.error_handlers:
|
||||
callback(self.bot, update, error)
|
||||
|
||||
def dispatchTo(self, handlers, update, **kwargs):
|
||||
"""
|
||||
Dispatches an update to a list of handlers.
|
||||
|
||||
Args:
|
||||
handlers (list): A list of handler-functions.
|
||||
update (any): The update to be dispatched
|
||||
"""
|
||||
|
||||
for handler in handlers:
|
||||
self.call_handler(handler, update, **kwargs)
|
||||
|
||||
def call_handler(self, handler, update, **kwargs):
|
||||
"""
|
||||
Calls an update handler. Checks the handler for keyword arguments and
|
||||
fills them, if possible.
|
||||
|
||||
Args:
|
||||
handler (function): An update handler function
|
||||
update (any): An update
|
||||
"""
|
||||
|
||||
target_kwargs = {}
|
||||
fargs = getargspec(handler).args
|
||||
|
||||
'''
|
||||
async handlers will receive all optional arguments, since we can't
|
||||
their argument list.
|
||||
'''
|
||||
|
||||
is_async = 'pargs' == getargspec(handler).varargs
|
||||
|
||||
if is_async or 'update_queue' in fargs:
|
||||
target_kwargs['update_queue'] = self.update_queue
|
||||
|
||||
if is_async or 'args' in fargs:
|
||||
if isinstance(update, Update) and update.message:
|
||||
args = update.message.text.split(' ')[1:]
|
||||
elif isinstance(update, Update) and update.inline_query:
|
||||
args = update.inline_query.query.split(' ')
|
||||
elif isinstance(update, str):
|
||||
args = update.split(' ')[1:]
|
||||
else:
|
||||
args = None
|
||||
|
||||
target_kwargs['args'] = args
|
||||
|
||||
if is_async or 'groups' in fargs:
|
||||
target_kwargs['groups'] = kwargs.get('groups', None)
|
||||
|
||||
if is_async or 'groupdict' in fargs:
|
||||
target_kwargs['groupdict'] = kwargs.get('groupdict', None)
|
||||
|
||||
if is_async or 'context' in fargs:
|
||||
target_kwargs['context'] = kwargs.get('context', None)
|
||||
|
||||
handler(self.bot, update, **target_kwargs)
|
||||
# old non-PEP8 Dispatcher methods
|
||||
m = "telegram.dispatcher."
|
||||
addHandler = deprecate(add_handler, m + "AddHandler", m + "add_handler")
|
||||
removeHandler = deprecate(remove_handler, m + "removeHandler", m + "remove_handler")
|
||||
addErrorHandler = deprecate(add_error_handler, m + "addErrorHandler", m + "add_error_handler")
|
||||
removeErrorHandler = deprecate(remove_error_handler, m + "removeErrorHandler",
|
||||
m + "remove_error_handler")
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
#!/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 the base class for handlers as used by the
|
||||
Dispatcher """
|
||||
|
||||
from telegram.utils.deprecate import deprecate
|
||||
|
||||
|
||||
class Handler(object):
|
||||
"""
|
||||
The base class for all update handlers. You can create your own handlers
|
||||
by inheriting from this class.
|
||||
|
||||
Args:
|
||||
callback (function): A function that takes ``bot, update`` as
|
||||
positional arguments. It will be called when the ``check_update``
|
||||
has determined that an update should be processed by this handler.
|
||||
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``update_queue`` will be passed to the callback function. It will be the ``Queue``
|
||||
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
|
||||
be used to insert updates. Default is ``False``.
|
||||
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
|
||||
instance created by the ``Updater`` which can be used to schedule new jobs.
|
||||
Default is ``False``.
|
||||
"""
|
||||
|
||||
def __init__(self, callback, pass_update_queue=False, pass_job_queue=False):
|
||||
self.callback = callback
|
||||
self.pass_update_queue = pass_update_queue
|
||||
self.pass_job_queue = pass_job_queue
|
||||
|
||||
def check_update(self, update):
|
||||
"""
|
||||
This method is called to determine if an update should be handled by
|
||||
this handler instance. It should always be overridden.
|
||||
|
||||
Args:
|
||||
update (object): The update to be tested
|
||||
|
||||
Returns:
|
||||
bool
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def handle_update(self, update, dispatcher):
|
||||
"""
|
||||
This method is called if it was determined that an update should indeed
|
||||
be handled by this instance. It should also be overridden, but in most
|
||||
cases call ``self.callback(dispatcher.bot, update)``, possibly along with
|
||||
optional arguments. To work with the ``ConversationHandler``, this method should return the
|
||||
value returned from ``self.callback``
|
||||
|
||||
Args:
|
||||
update (object): The update to be handled
|
||||
dispatcher (Dispatcher): The dispatcher to collect optional args
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def collect_optional_args(self, dispatcher):
|
||||
"""
|
||||
Prepares the optional arguments that are the same for all types of
|
||||
handlers
|
||||
|
||||
Args:
|
||||
dispatcher (Dispatcher):
|
||||
"""
|
||||
optional_args = dict()
|
||||
if self.pass_update_queue:
|
||||
optional_args['update_queue'] = dispatcher.update_queue
|
||||
if self.pass_job_queue:
|
||||
optional_args['job_queue'] = dispatcher.job_queue
|
||||
|
||||
return optional_args
|
||||
|
||||
# old non-PEP8 Handler methods
|
||||
m = "telegram.Handler."
|
||||
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
|
||||
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
|
||||
collectOptionalArgs = deprecate(collect_optional_args, m + "collectOptionalArgs",
|
||||
m + "collect_optional_args")
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Leandro Toledo de Souza <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 the InlineQueryHandler class """
|
||||
|
||||
from .handler import Handler
|
||||
from telegram import Update
|
||||
from telegram.utils.deprecate import deprecate
|
||||
|
||||
|
||||
class InlineQueryHandler(Handler):
|
||||
"""
|
||||
Handler class to handle Telegram inline queries.
|
||||
|
||||
Args:
|
||||
callback (function): A function that takes ``bot, update`` as
|
||||
positional arguments. It will be called when the ``check_update``
|
||||
has determined that an update should be processed by this handler.
|
||||
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``update_queue`` will be passed to the callback function. It will be the ``Queue``
|
||||
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
|
||||
be used to insert updates. Default is ``False``.
|
||||
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
|
||||
instance created by the ``Updater`` which can be used to schedule new jobs.
|
||||
Default is ``False``.
|
||||
"""
|
||||
|
||||
def __init__(self, callback, pass_update_queue=False, pass_job_queue=False):
|
||||
super(InlineQueryHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
|
||||
def check_update(self, update):
|
||||
return isinstance(update, Update) and update.inline_query
|
||||
|
||||
def handle_update(self, update, dispatcher):
|
||||
optional_args = self.collect_optional_args(dispatcher)
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
# old non-PEP8 Handler methods
|
||||
m = "telegram.InlineQueryHandler."
|
||||
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
|
||||
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
|
||||
+181
-91
@@ -16,150 +16,240 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""This module contains the class JobQueue."""
|
||||
"""This module contains the classes JobQueue and Job."""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from threading import Thread, Lock
|
||||
|
||||
try:
|
||||
from queue import PriorityQueue
|
||||
except ImportError:
|
||||
from Queue import PriorityQueue
|
||||
from threading import Thread, Lock, Event
|
||||
from queue import PriorityQueue, Empty
|
||||
|
||||
|
||||
class JobQueue(object):
|
||||
"""
|
||||
This class allows you to periodically perform tasks with the bot.
|
||||
"""This class allows you to periodically perform tasks with the bot.
|
||||
|
||||
Attributes:
|
||||
tick_interval (float):
|
||||
queue (PriorityQueue):
|
||||
bot (Bot):
|
||||
running (bool):
|
||||
prevent_autostart (Optional[bool]): If ``True``, the job queue will not be started
|
||||
automatically. Defaults to ``False``
|
||||
|
||||
Args:
|
||||
bot (Bot): The bot instance that should be passed to the jobs
|
||||
tick_interval (Optional[float]): The interval this queue should check
|
||||
the newest task in seconds. Defaults to 1.0
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, bot, tick_interval=1.0):
|
||||
self.tick_interval = tick_interval
|
||||
def __init__(self, bot, prevent_autostart=False):
|
||||
self.queue = PriorityQueue()
|
||||
self.bot = bot
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.__lock = Lock()
|
||||
self.running = False
|
||||
self.logger = logging.getLogger(self.__class__.__name__)
|
||||
self.__start_lock = Lock()
|
||||
self.__next_peek_lock = Lock() # to protect self._next_peek & self.__tick
|
||||
self.__tick = Event()
|
||||
self.__thread = None
|
||||
""":type: Thread"""
|
||||
self._next_peek = None
|
||||
""":type: float"""
|
||||
self._running = False
|
||||
|
||||
def put(self,
|
||||
run,
|
||||
interval,
|
||||
repeat=True,
|
||||
next_t=None,
|
||||
prevent_autostart=False):
|
||||
"""
|
||||
Queue a new job. If the JobQueue is not running, it will be started.
|
||||
if not prevent_autostart:
|
||||
self.logger.debug('Auto-starting %s', self.__class__.__name__)
|
||||
self.start()
|
||||
|
||||
def put(self, job, next_t=None):
|
||||
"""Queue a new job. If the JobQueue is not running, it will be started.
|
||||
|
||||
Args:
|
||||
run (function): A function that takes the parameter `bot`
|
||||
interval (float): The interval in seconds in which `run` should be
|
||||
executed
|
||||
repeat (Optional[bool]): If `False`, job will only be executed once
|
||||
next_t (Optional[float]): Time in seconds in which run should be
|
||||
executed first. Defaults to `interval`
|
||||
prevent_autostart (Optional[bool]): If `True`, the job queue will
|
||||
not be started automatically if it is not running.
|
||||
"""
|
||||
name = run.__name__
|
||||
job (Job): The ``Job`` instance representing the new job
|
||||
next_t (Optional[float]): Time in seconds in which the job should be executed first.
|
||||
Defaults to ``job.interval``
|
||||
|
||||
job = JobQueue.Job()
|
||||
job.run = run
|
||||
job.interval = interval
|
||||
job.name = name
|
||||
job.repeat = repeat
|
||||
"""
|
||||
job.job_queue = self
|
||||
|
||||
if next_t is None:
|
||||
next_t = interval
|
||||
next_t = job.interval
|
||||
|
||||
next_t += time.time()
|
||||
now = time.time()
|
||||
next_t += now
|
||||
|
||||
self.logger.debug('Putting a %s with t=%f' % (job.name, next_t))
|
||||
self.logger.debug('Putting job %s with t=%f', job.name, next_t)
|
||||
self.queue.put((next_t, job))
|
||||
|
||||
if not self.running and not prevent_autostart:
|
||||
self.logger.debug('Auto-starting JobQueue')
|
||||
self.start()
|
||||
# Wake up the loop if this job should be executed next
|
||||
self._set_next_peek(next_t)
|
||||
|
||||
def _set_next_peek(self, t):
|
||||
"""
|
||||
Set next peek if not defined or `t` is before next peek.
|
||||
In case the next peek was set, also trigger the `self.__tick` event.
|
||||
|
||||
"""
|
||||
with self.__next_peek_lock:
|
||||
if not self._next_peek or self._next_peek > t:
|
||||
self._next_peek = t
|
||||
self.__tick.set()
|
||||
|
||||
def tick(self):
|
||||
"""
|
||||
Run all jobs that are due and re-enqueue them with their interval
|
||||
Run all jobs that are due and re-enqueue them with their interval.
|
||||
|
||||
"""
|
||||
now = time.time()
|
||||
|
||||
self.logger.debug('Ticking jobs with t=%f' % now)
|
||||
while not self.queue.empty():
|
||||
t, j = self.queue.queue[0]
|
||||
self.logger.debug('Peeked at %s with t=%f' % (j.name, t))
|
||||
self.logger.debug('Ticking jobs with t=%f', now)
|
||||
|
||||
if t < now:
|
||||
self.queue.get()
|
||||
self.logger.debug('Running job %s' % j.name)
|
||||
try:
|
||||
j.run(self.bot)
|
||||
except:
|
||||
self.logger.exception('An uncaught error was raised while '
|
||||
'executing job %s' % j.name)
|
||||
if j.repeat:
|
||||
self.put(j.run, j.interval)
|
||||
while True:
|
||||
try:
|
||||
t, job = self.queue.get(False)
|
||||
except Empty:
|
||||
break
|
||||
|
||||
self.logger.debug('Peeked at %s with t=%f', job.name, t)
|
||||
|
||||
if t > now:
|
||||
# we can get here in two conditions:
|
||||
# 1. At the second or later pass of the while loop, after we've already processed
|
||||
# the job(s) we were supposed to at this time.
|
||||
# 2. At the first iteration of the loop only if `self.put()` had triggered
|
||||
# `self.__tick` because `self._next_peek` wasn't set
|
||||
self.logger.debug("Next task isn't due yet. Finished!")
|
||||
self.queue.put((t, job))
|
||||
self._set_next_peek(t)
|
||||
break
|
||||
|
||||
if job._remove.is_set():
|
||||
self.logger.debug('Removing job %s', job.name)
|
||||
continue
|
||||
|
||||
self.logger.debug('Next task isn\'t due yet. Finished!')
|
||||
break
|
||||
if job.enabled:
|
||||
self.logger.debug('Running job %s', job.name)
|
||||
|
||||
try:
|
||||
job.run(self.bot)
|
||||
|
||||
except:
|
||||
self.logger.exception('An uncaught error was raised while executing job %s',
|
||||
job.name)
|
||||
|
||||
else:
|
||||
self.logger.debug('Skipping disabled job %s', job.name)
|
||||
|
||||
if job.repeat:
|
||||
self.put(job)
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Starts the job_queue thread.
|
||||
|
||||
"""
|
||||
self.__lock.acquire()
|
||||
if not self.running:
|
||||
self.running = True
|
||||
self.__lock.release()
|
||||
job_queue_thread = Thread(target=self._start,
|
||||
name="job_queue")
|
||||
job_queue_thread.start()
|
||||
self.logger.debug('Job Queue thread started')
|
||||
self.__start_lock.acquire()
|
||||
|
||||
if not self._running:
|
||||
self._running = True
|
||||
self.__start_lock.release()
|
||||
self.__thread = Thread(target=self._main_loop, name="job_queue")
|
||||
self.__thread.start()
|
||||
self.logger.debug('%s thread started', self.__class__.__name__)
|
||||
|
||||
else:
|
||||
self.__lock.release()
|
||||
self.__start_lock.release()
|
||||
|
||||
def _start(self):
|
||||
def _main_loop(self):
|
||||
"""
|
||||
Thread target of thread 'job_queue'. Runs in background and performs
|
||||
ticks on the job queue.
|
||||
Thread target of thread ``job_queue``. Runs in background and performs ticks on the job
|
||||
queue.
|
||||
|
||||
"""
|
||||
while self.running:
|
||||
while self._running:
|
||||
# self._next_peek may be (re)scheduled during self.tick() or self.put()
|
||||
with self.__next_peek_lock:
|
||||
tmout = self._next_peek and self._next_peek - time.time()
|
||||
self._next_peek = None
|
||||
self.__tick.clear()
|
||||
|
||||
self.__tick.wait(tmout)
|
||||
|
||||
# If we were woken up by self.stop(), just bail out
|
||||
if not self._running:
|
||||
break
|
||||
|
||||
self.tick()
|
||||
time.sleep(self.tick_interval)
|
||||
|
||||
self.logger.debug('Job Queue thread stopped')
|
||||
self.logger.debug('%s thread stopped', self.__class__.__name__)
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
Stops the thread
|
||||
"""
|
||||
with self.__lock:
|
||||
self.running = False
|
||||
with self.__start_lock:
|
||||
self._running = False
|
||||
|
||||
class Job(object):
|
||||
""" Inner class that represents a job """
|
||||
interval = None
|
||||
name = None
|
||||
repeat = None
|
||||
self.__tick.set()
|
||||
if self.__thread is not None:
|
||||
self.__thread.join()
|
||||
|
||||
def run(self):
|
||||
pass
|
||||
def jobs(self):
|
||||
"""Returns a tuple of all jobs that are currently in the ``JobQueue``"""
|
||||
return tuple(job[1] for job in self.queue.queue if job)
|
||||
|
||||
def __lt__(self, other):
|
||||
return False
|
||||
|
||||
class Job(object):
|
||||
"""This class encapsulates a Job
|
||||
|
||||
Attributes:
|
||||
callback (function):
|
||||
interval (float):
|
||||
repeat (bool):
|
||||
name (str):
|
||||
enabled (bool): Boolean property that decides if this job is currently active
|
||||
|
||||
Args:
|
||||
callback (function): The callback function that should be executed by the Job. It should
|
||||
take two parameters ``bot`` and ``job``, where ``job`` is the ``Job`` instance. It
|
||||
can be used to terminate the job or modify its interval.
|
||||
interval (float): The interval in which this job should execute its callback function in
|
||||
seconds.
|
||||
repeat (Optional[bool]): If this job should be periodically execute its callback function
|
||||
(``True``) or only once (``False``). Defaults to ``True``
|
||||
context (Optional[object]): Additional data needed for the callback function. Can be
|
||||
accessed through ``job.context`` in the callback. Defaults to ``None``
|
||||
|
||||
"""
|
||||
job_queue = None
|
||||
|
||||
def __init__(self, callback, interval, repeat=True, context=None):
|
||||
self.callback = callback
|
||||
self.interval = interval
|
||||
self.repeat = repeat
|
||||
self.context = context
|
||||
|
||||
self.name = callback.__name__
|
||||
self._remove = Event()
|
||||
self._enabled = Event()
|
||||
self._enabled.set()
|
||||
|
||||
def run(self, bot):
|
||||
"""Executes the callback function"""
|
||||
self.callback(bot, self)
|
||||
|
||||
def schedule_removal(self):
|
||||
"""
|
||||
Schedules this job for removal from the ``JobQueue``. It will be removed without executing
|
||||
its callback function again.
|
||||
|
||||
"""
|
||||
self._remove.set()
|
||||
|
||||
def is_enabled(self):
|
||||
return self._enabled.is_set()
|
||||
|
||||
def set_enabled(self, status):
|
||||
if status:
|
||||
self._enabled.set()
|
||||
else:
|
||||
self._enabled.clear()
|
||||
|
||||
enabled = property(is_enabled, set_enabled)
|
||||
|
||||
def __lt__(self, other):
|
||||
return False
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
#!/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 the MessageHandler class """
|
||||
|
||||
from .handler import Handler
|
||||
from telegram import Update
|
||||
from telegram.utils.deprecate import deprecate
|
||||
|
||||
|
||||
class Filters(object):
|
||||
"""
|
||||
Convenient namespace (class) & methods for the filter funcs of the
|
||||
MessageHandler class.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def text(message):
|
||||
return message.text and not message.text.startswith('/')
|
||||
|
||||
@staticmethod
|
||||
def command(message):
|
||||
return message.text and message.text.startswith('/')
|
||||
|
||||
@staticmethod
|
||||
def audio(message):
|
||||
return bool(message.audio)
|
||||
|
||||
@staticmethod
|
||||
def document(message):
|
||||
return bool(message.document)
|
||||
|
||||
@staticmethod
|
||||
def photo(message):
|
||||
return bool(message.photo)
|
||||
|
||||
@staticmethod
|
||||
def sticker(message):
|
||||
return bool(message.sticker)
|
||||
|
||||
@staticmethod
|
||||
def video(message):
|
||||
return bool(message.video)
|
||||
|
||||
@staticmethod
|
||||
def voice(message):
|
||||
return bool(message.voice)
|
||||
|
||||
@staticmethod
|
||||
def contact(message):
|
||||
return bool(message.contact)
|
||||
|
||||
@staticmethod
|
||||
def location(message):
|
||||
return bool(message.location)
|
||||
|
||||
@staticmethod
|
||||
def venue(message):
|
||||
return bool(message.venue)
|
||||
|
||||
@staticmethod
|
||||
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):
|
||||
"""
|
||||
Handler class to handle telegram messages. Messages are Telegram Updates
|
||||
that do not contain a command. They might contain text, media or status
|
||||
updates.
|
||||
|
||||
Args:
|
||||
filters (list[function]): A list of filter functions. Standard filters
|
||||
can be found in the Filters class above.
|
||||
| Each `function` takes ``Update`` as arg and returns ``bool``.
|
||||
| All messages that match at least one of those filters will be
|
||||
accepted. If ``bool(filters)`` evaluates to ``False``, messages are
|
||||
not filtered.
|
||||
callback (function): A function that takes ``bot, update`` as
|
||||
positional arguments. It will be called when the ``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,
|
||||
allow_edited=False,
|
||||
pass_update_queue=False,
|
||||
pass_job_queue=False):
|
||||
super(MessageHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
self.filters = filters
|
||||
self.allow_edited = allow_edited
|
||||
|
||||
def check_update(self, update):
|
||||
if (isinstance(update, Update)
|
||||
and (update.message or update.edited_message and self.allow_edited)):
|
||||
|
||||
if not self.filters:
|
||||
res = True
|
||||
|
||||
else:
|
||||
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):
|
||||
optional_args = self.collect_optional_args(dispatcher)
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
# old non-PEP8 Handler methods
|
||||
m = "telegram.MessageHandler."
|
||||
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
|
||||
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
|
||||
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Leandro Toledo de Souza <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 the RegexHandler class """
|
||||
|
||||
import re
|
||||
|
||||
from future.utils import string_types
|
||||
|
||||
from .handler import Handler
|
||||
from telegram import Update
|
||||
from telegram.utils.deprecate import deprecate
|
||||
|
||||
|
||||
class RegexHandler(Handler):
|
||||
"""
|
||||
Handler class to handle Telegram updates based on a regex. It uses a
|
||||
regular expression to check text messages. Read the documentation of the
|
||||
``re`` module for more information. The ``re.match`` function is used to
|
||||
determine if an update should be handled by this handler.
|
||||
|
||||
Args:
|
||||
pattern (str or Pattern): The regex pattern.
|
||||
callback (function): A function that takes ``bot, update`` as
|
||||
positional arguments. It will be called when the ``check_update``
|
||||
has determined that an update should be processed by this handler.
|
||||
pass_groups (optional[bool]): If the callback should be passed the
|
||||
result of ``re.match(pattern, text).groups()`` as a keyword
|
||||
argument called ``groups``. Default is ``False``
|
||||
pass_groupdict (optional[bool]): If the callback should be passed the
|
||||
result of ``re.match(pattern, text).groupdict()`` as a keyword
|
||||
argument called ``groupdict``. Default is ``False``
|
||||
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``update_queue`` will be passed to the callback function. It will be the ``Queue``
|
||||
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
|
||||
be used to insert updates. Default is ``False``.
|
||||
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
|
||||
instance created by the ``Updater`` which can be used to schedule new jobs.
|
||||
Default is ``False``.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
pattern,
|
||||
callback,
|
||||
pass_groups=False,
|
||||
pass_groupdict=False,
|
||||
pass_update_queue=False,
|
||||
pass_job_queue=False):
|
||||
super(RegexHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
|
||||
if isinstance(pattern, string_types):
|
||||
pattern = re.compile(pattern)
|
||||
|
||||
self.pattern = pattern
|
||||
self.pass_groups = pass_groups
|
||||
self.pass_groupdict = pass_groupdict
|
||||
|
||||
def check_update(self, update):
|
||||
if (isinstance(update, Update) and update.message and update.message.text):
|
||||
match = re.match(self.pattern, update.message.text)
|
||||
return bool(match)
|
||||
else:
|
||||
return False
|
||||
|
||||
def handle_update(self, update, dispatcher):
|
||||
optional_args = self.collect_optional_args(dispatcher)
|
||||
match = re.match(self.pattern, update.message.text)
|
||||
|
||||
if self.pass_groups:
|
||||
optional_args['groups'] = match.groups()
|
||||
if self.pass_groupdict:
|
||||
optional_args['groupdict'] = match.groupdict()
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
# old non-PEP8 Handler methods
|
||||
m = "telegram.RegexHandler."
|
||||
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
|
||||
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
|
||||
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Leandro Toledo de Souza <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 the StringCommandHandler class """
|
||||
|
||||
from .handler import Handler
|
||||
from telegram.utils.deprecate import deprecate
|
||||
|
||||
|
||||
class StringCommandHandler(Handler):
|
||||
"""
|
||||
Handler class to handle string commands. Commands are string updates
|
||||
that start with ``/``.
|
||||
|
||||
Args:
|
||||
command (str): The name of the command this handler should listen for.
|
||||
callback (function): A function that takes ``bot, update`` as
|
||||
positional arguments. It will be called when the ``check_update``
|
||||
has determined that an update should be processed by this handler.
|
||||
pass_args (optional[bool]): If the handler should be passed the
|
||||
arguments passed to the command as a keyword argument called `
|
||||
``args``. It will contain a list of strings, which is the text
|
||||
following the command split on spaces. Default is ``False``
|
||||
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``update_queue`` will be passed to the callback function. It will be the ``Queue``
|
||||
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
|
||||
be used to insert updates. Default is ``False``.
|
||||
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
|
||||
instance created by the ``Updater`` which can be used to schedule new jobs.
|
||||
Default is ``False``.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
command,
|
||||
callback,
|
||||
pass_args=False,
|
||||
pass_update_queue=False,
|
||||
pass_job_queue=False):
|
||||
super(StringCommandHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
self.command = command
|
||||
self.pass_args = pass_args
|
||||
|
||||
def check_update(self, update):
|
||||
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)
|
||||
|
||||
if self.pass_args:
|
||||
optional_args['args'] = update.split(' ')[1:]
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
# old non-PEP8 Handler methods
|
||||
m = "telegram.StringCommandHandler."
|
||||
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
|
||||
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
|
||||
@@ -0,0 +1,92 @@
|
||||
#!/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 the StringRegexHandler class """
|
||||
|
||||
import re
|
||||
|
||||
from future.utils import string_types
|
||||
|
||||
from .handler import Handler
|
||||
from telegram.utils.deprecate import deprecate
|
||||
|
||||
|
||||
class StringRegexHandler(Handler):
|
||||
"""
|
||||
Handler class to handle string updates based on a regex. It uses a
|
||||
regular expression to check update content. Read the documentation of the
|
||||
``re`` module for more information. The ``re.match`` function is used to
|
||||
determine if an update should be handled by this handler.
|
||||
|
||||
Args:
|
||||
pattern (str or Pattern): The regex pattern.
|
||||
callback (function): A function that takes ``bot, update`` as
|
||||
positional arguments. It will be called when the ``check_update``
|
||||
has determined that an update should be processed by this handler.
|
||||
pass_groups (optional[bool]): If the callback should be passed the
|
||||
result of ``re.match(pattern, update).groups()`` as a keyword
|
||||
argument called ``groups``. Default is ``False``
|
||||
pass_groupdict (optional[bool]): If the callback should be passed the
|
||||
result of ``re.match(pattern, update).groupdict()`` as a keyword
|
||||
argument called ``groupdict``. Default is ``False``
|
||||
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``update_queue`` will be passed to the callback function. It will be the ``Queue``
|
||||
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
|
||||
be used to insert updates. Default is ``False``.
|
||||
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
|
||||
instance created by the ``Updater`` which can be used to schedule new jobs.
|
||||
Default is ``False``.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
pattern,
|
||||
callback,
|
||||
pass_groups=False,
|
||||
pass_groupdict=False,
|
||||
pass_update_queue=False,
|
||||
pass_job_queue=False):
|
||||
super(StringRegexHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
|
||||
if isinstance(pattern, string_types):
|
||||
pattern = re.compile(pattern)
|
||||
|
||||
self.pattern = pattern
|
||||
self.pass_groups = pass_groups
|
||||
self.pass_groupdict = pass_groupdict
|
||||
|
||||
def check_update(self, update):
|
||||
return isinstance(update, string_types) and bool(re.match(self.pattern, update))
|
||||
|
||||
def handle_update(self, update, dispatcher):
|
||||
optional_args = self.collect_optional_args(dispatcher)
|
||||
match = re.match(self.pattern, update)
|
||||
|
||||
if self.pass_groups:
|
||||
optional_args['groups'] = match.groups()
|
||||
if self.pass_groupdict:
|
||||
optional_args['groupdict'] = match.groupdict()
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
# old non-PEP8 Handler methods
|
||||
m = "telegram.StringRegexHandler."
|
||||
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
|
||||
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Leandro Toledo de Souza <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 the TypeHandler class """
|
||||
|
||||
from .handler import Handler
|
||||
from telegram.utils.deprecate import deprecate
|
||||
|
||||
|
||||
class TypeHandler(Handler):
|
||||
"""
|
||||
Handler class to handle updates of custom types.
|
||||
|
||||
Args:
|
||||
type (type): The ``type`` of updates this handler should process, as
|
||||
determined by ``isinstance``
|
||||
callback (function): A function that takes ``bot, update`` as
|
||||
positional arguments. It will be called when the ``check_update``
|
||||
has determined that an update should be processed by this handler.
|
||||
strict (optional[bool]): Use ``type`` instead of ``isinstance``.
|
||||
Default is ``False``
|
||||
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``update_queue`` will be passed to the callback function. It will be the ``Queue``
|
||||
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
|
||||
be used to insert updates. Default is ``False``.
|
||||
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
|
||||
instance created by the ``Updater`` which can be used to schedule new jobs.
|
||||
Default is ``False``.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
type,
|
||||
callback,
|
||||
strict=False,
|
||||
pass_update_queue=False,
|
||||
pass_job_queue=False):
|
||||
super(TypeHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
self.type = type
|
||||
self.strict = strict
|
||||
|
||||
def check_update(self, update):
|
||||
if not self.strict:
|
||||
return isinstance(update, self.type)
|
||||
else:
|
||||
return type(update) is self.type
|
||||
|
||||
def handle_update(self, update, dispatcher):
|
||||
optional_args = self.collect_optional_args(dispatcher)
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
# old non-PEP8 Handler methods
|
||||
m = "telegram.TypeHandler."
|
||||
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
|
||||
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
|
||||
+158
-143
@@ -16,8 +16,6 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
|
||||
"""This module contains the class Updater, which tries to make creating
|
||||
Telegram bots intuitive."""
|
||||
|
||||
@@ -28,19 +26,20 @@ from threading import Thread, Lock, current_thread, Event
|
||||
from time import sleep
|
||||
import subprocess
|
||||
from signal import signal, SIGINT, SIGTERM, SIGABRT
|
||||
from queue import Queue
|
||||
|
||||
from telegram import Bot, TelegramError, NullHandler
|
||||
from telegram.ext import dispatcher, Dispatcher, JobQueue
|
||||
from telegram.error import Unauthorized, InvalidToken
|
||||
from telegram.utils.updatequeue import UpdateQueue
|
||||
from telegram.utils.webhookhandler import (WebhookServer, WebhookHandler)
|
||||
|
||||
logging.getLogger(__name__).addHandler(NullHandler())
|
||||
|
||||
|
||||
class Updater:
|
||||
class Updater(object):
|
||||
"""
|
||||
This class, which employs the Dispatcher class, provides a frontend to
|
||||
telegram.Bot to the programmer, so they can focus on coding the bot. It's
|
||||
telegram.Bot to the programmer, so they can focus on coding the bot. Its
|
||||
purpose is to receive the updates from Telegram and to deliver them to said
|
||||
dispatcher. It also runs in a separate thread, so the user can interact
|
||||
with the bot, for example on the command line. The dispatcher supports
|
||||
@@ -66,12 +65,7 @@ class Updater:
|
||||
ValueError: If both `token` and `bot` are passed or none of them.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
token=None,
|
||||
base_url=None,
|
||||
workers=4,
|
||||
bot=None,
|
||||
job_queue_tick_interval=1.0):
|
||||
def __init__(self, token=None, base_url=None, workers=4, bot=None):
|
||||
if (token is None) and (bot is None):
|
||||
raise ValueError('`token` or `bot` must be passed')
|
||||
if (token is not None) and (bot is not None):
|
||||
@@ -81,11 +75,14 @@ class Updater:
|
||||
self.bot = bot
|
||||
else:
|
||||
self.bot = Bot(token, base_url)
|
||||
self.update_queue = UpdateQueue()
|
||||
self.job_queue = JobQueue(self.bot, job_queue_tick_interval)
|
||||
self.update_queue = Queue()
|
||||
self.job_queue = JobQueue(self.bot)
|
||||
self.__exception_event = Event()
|
||||
self.dispatcher = Dispatcher(self.bot, self.update_queue, workers,
|
||||
self.__exception_event)
|
||||
self.dispatcher = Dispatcher(self.bot,
|
||||
self.update_queue,
|
||||
job_queue=self.job_queue,
|
||||
workers=workers,
|
||||
exception_event=self.__exception_event)
|
||||
self.last_update_id = 0
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.running = False
|
||||
@@ -95,47 +92,8 @@ class Updater:
|
||||
self.__threads = []
|
||||
""":type: list[Thread]"""
|
||||
|
||||
def start_polling(self, poll_interval=0.0, timeout=10, network_delay=2,
|
||||
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
|
||||
timeout (Optional[float]): Passed to Bot.getUpdates
|
||||
network_delay (Optional[float]): Passed to Bot.getUpdates
|
||||
clean (Optional[bool]): Whether to clean any pending updates on
|
||||
Telegram servers before actually starting to poll. Default is
|
||||
False.
|
||||
bootstrap_retries (Optional[int[): Whether the bootstrapping phase
|
||||
of the `Updater` will retry on failures on the Telegram server.
|
||||
< 0 - retry indefinitely
|
||||
0 - no retries (default)
|
||||
> 0 - retry up to X times
|
||||
|
||||
Returns:
|
||||
Queue: The update queue that can be filled from the main thread
|
||||
|
||||
"""
|
||||
with self.__lock:
|
||||
if not self.running:
|
||||
self.running = True
|
||||
if clean:
|
||||
self._clean_updates()
|
||||
|
||||
# Create & start threads
|
||||
self._init_thread(self.dispatcher.start, "dispatcher")
|
||||
self._init_thread(self._start_polling, "updater",
|
||||
poll_interval, timeout, network_delay,
|
||||
bootstrap_retries)
|
||||
|
||||
# Return the update queue so the main thread can insert updates
|
||||
return self.update_queue
|
||||
|
||||
def _init_thread(self, target, name, *args, **kwargs):
|
||||
thr = Thread(target=self._thread_wrapper, name=name,
|
||||
args=(target,) + args, kwargs=kwargs)
|
||||
thr = Thread(target=self._thread_wrapper, name=name, args=(target,) + args, kwargs=kwargs)
|
||||
thr.start()
|
||||
self.__threads.append(thr)
|
||||
|
||||
@@ -150,6 +108,49 @@ class Updater:
|
||||
raise
|
||||
self.logger.debug('{0} - ended'.format(thr_name))
|
||||
|
||||
def start_polling(self,
|
||||
poll_interval=0.0,
|
||||
timeout=10,
|
||||
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
|
||||
|
||||
timeout (Optional[float]): Passed to Bot.getUpdates
|
||||
|
||||
network_delay (Optional[float]): Passed to Bot.getUpdates
|
||||
|
||||
clean (Optional[bool]): Whether to clean any pending updates on Telegram servers before
|
||||
actually starting to poll. Default is False.
|
||||
|
||||
bootstrap_retries (Optional[int]): Whether the bootstrapping phase of the `Updater`
|
||||
will retry on failures on the Telegram server.
|
||||
|
||||
| < 0 - retry indefinitely
|
||||
| 0 - no retries (default)
|
||||
| > 0 - retry up to X times
|
||||
|
||||
Returns:
|
||||
Queue: The update queue that can be filled from the main thread
|
||||
|
||||
"""
|
||||
with self.__lock:
|
||||
if not self.running:
|
||||
self.running = True
|
||||
|
||||
# Create & start threads
|
||||
self._init_thread(self.dispatcher.start, "dispatcher")
|
||||
self._init_thread(self._start_polling, "updater", poll_interval, timeout,
|
||||
network_delay, bootstrap_retries, clean)
|
||||
|
||||
# Return the update queue so the main thread can insert updates
|
||||
return self.update_queue
|
||||
|
||||
def start_webhook(self,
|
||||
listen='127.0.0.1',
|
||||
port=80,
|
||||
@@ -177,9 +178,10 @@ class Updater:
|
||||
is False.
|
||||
bootstrap_retries (Optional[int[): Whether the bootstrapping phase
|
||||
of the `Updater` will retry on failures on the Telegram server.
|
||||
< 0 - retry indefinitely
|
||||
0 - no retries (default)
|
||||
> 0 - retry up to X times
|
||||
|
||||
| < 0 - retry indefinitely
|
||||
| 0 - no retries (default)
|
||||
| > 0 - retry up to X times
|
||||
webhook_url (Optional[str]): Explicitly specifiy the webhook url.
|
||||
Useful behind NAT, reverse proxy, etc. Default is derived from
|
||||
`listen`, `port` & `url_path`.
|
||||
@@ -191,20 +193,16 @@ class Updater:
|
||||
with self.__lock:
|
||||
if not self.running:
|
||||
self.running = True
|
||||
if clean:
|
||||
self._clean_updates()
|
||||
|
||||
# Create & start threads
|
||||
self._init_thread(self.dispatcher.start, "dispatcher"),
|
||||
self._init_thread(self._start_webhook, "updater", listen,
|
||||
port, url_path, cert, key, bootstrap_retries,
|
||||
webhook_url)
|
||||
self._init_thread(self._start_webhook, "updater", listen, port, url_path, cert,
|
||||
key, bootstrap_retries, clean, webhook_url)
|
||||
|
||||
# Return the update queue so the main thread can insert updates
|
||||
return self.update_queue
|
||||
|
||||
def _start_polling(self, poll_interval, timeout, network_delay,
|
||||
bootstrap_retries):
|
||||
def _start_polling(self, poll_interval, timeout, network_delay, bootstrap_retries, clean):
|
||||
"""
|
||||
Thread target of thread 'updater'. Runs in background, pulls
|
||||
updates from Telegram and inserts them in the update queue of the
|
||||
@@ -214,7 +212,7 @@ class Updater:
|
||||
cur_interval = poll_interval
|
||||
self.logger.debug('Updater thread started')
|
||||
|
||||
self._set_webhook(None, bootstrap_retries)
|
||||
self._bootstrap(bootstrap_retries, clean=clean, webhook_url='')
|
||||
|
||||
while self.running:
|
||||
try:
|
||||
@@ -222,8 +220,7 @@ class Updater:
|
||||
timeout=timeout,
|
||||
network_delay=network_delay)
|
||||
except TelegramError as te:
|
||||
self.logger.error(
|
||||
"Error while getting Updates: {0}".format(te))
|
||||
self.logger.error("Error while getting Updates: {0}".format(te))
|
||||
|
||||
# Put the error into the update queue and let the Dispatcher
|
||||
# broadcast it
|
||||
@@ -246,27 +243,6 @@ class Updater:
|
||||
|
||||
sleep(cur_interval)
|
||||
|
||||
def _set_webhook(self, webhook_url, max_retries):
|
||||
retries = 0
|
||||
while 1:
|
||||
try:
|
||||
# Remove webhook
|
||||
self.bot.setWebhook(webhook_url=webhook_url)
|
||||
except (Unauthorized, InvalidToken):
|
||||
raise
|
||||
except TelegramError:
|
||||
msg = 'failed to set webhook; try={0} max_retries={1}'.format(
|
||||
retries, max_retries)
|
||||
if max_retries < 0 or retries < max_retries:
|
||||
self.logger.info(msg)
|
||||
retries += 1
|
||||
else:
|
||||
self.logger.exception(msg)
|
||||
raise
|
||||
else:
|
||||
break
|
||||
sleep(1)
|
||||
|
||||
@staticmethod
|
||||
def _increase_poll_interval(current_interval):
|
||||
# increase waiting times on subsequent errors up to 30secs
|
||||
@@ -278,50 +254,83 @@ class Updater:
|
||||
current_interval = 30
|
||||
return current_interval
|
||||
|
||||
def _start_webhook(self, listen, port, url_path, cert, key,
|
||||
bootstrap_retries, webhook_url):
|
||||
def _start_webhook(self, listen, port, url_path, cert, key, bootstrap_retries, clean,
|
||||
webhook_url):
|
||||
self.logger.debug('Updater thread started')
|
||||
use_ssl = cert is not None and key is not None
|
||||
url_path = "/%s" % url_path
|
||||
if not url_path.startswith('/'):
|
||||
url_path = '/{0}'.format(url_path)
|
||||
|
||||
# Create and start server
|
||||
self.httpd = WebhookServer((listen, port), WebhookHandler,
|
||||
self.update_queue, url_path)
|
||||
if not webhook_url:
|
||||
webhook_url = self._gen_webhook_url(listen, port, url_path,
|
||||
use_ssl)
|
||||
self._set_webhook(webhook_url, bootstrap_retries)
|
||||
self.httpd = WebhookServer((listen, port), WebhookHandler, self.update_queue, url_path)
|
||||
|
||||
if use_ssl:
|
||||
# Check SSL-Certificate with openssl, if possible
|
||||
try:
|
||||
exit_code = subprocess.call(["openssl", "x509", "-text",
|
||||
"-noout", "-in", cert],
|
||||
stdout=open(os.devnull, 'wb'),
|
||||
stderr=subprocess.STDOUT)
|
||||
except OSError:
|
||||
exit_code = 0
|
||||
self._check_ssl_cert(cert, key)
|
||||
|
||||
if exit_code is 0:
|
||||
try:
|
||||
self.httpd.socket = ssl.wrap_socket(self.httpd.socket,
|
||||
certfile=cert,
|
||||
keyfile=key,
|
||||
server_side=True)
|
||||
except ssl.SSLError as error:
|
||||
self.logger.exception('failed to init SSL socket')
|
||||
raise TelegramError(str(error))
|
||||
else:
|
||||
raise TelegramError('SSL Certificate invalid')
|
||||
# DO NOT CHANGE: Only set webhook if SSL is handled by library
|
||||
if not webhook_url:
|
||||
webhook_url = self._gen_webhook_url(listen, port, url_path)
|
||||
|
||||
self._bootstrap(max_retries=bootstrap_retries,
|
||||
clean=clean,
|
||||
webhook_url=webhook_url,
|
||||
cert=open(cert, 'rb'))
|
||||
elif clean:
|
||||
self.logger.warning("cleaning updates is not supported if "
|
||||
"SSL-termination happens elsewhere; skipping")
|
||||
|
||||
self.httpd.serve_forever(poll_interval=1)
|
||||
|
||||
def _gen_webhook_url(self, listen, port, url_path, use_ssl):
|
||||
return '{proto}://{listen}:{port}{path}'.format(
|
||||
proto='https' if use_ssl else 'http',
|
||||
listen=listen,
|
||||
port=port,
|
||||
path=url_path)
|
||||
def _check_ssl_cert(self, cert, key):
|
||||
# Check SSL-Certificate with openssl, if possible
|
||||
try:
|
||||
exit_code = subprocess.call(
|
||||
["openssl", "x509", "-text", "-noout", "-in", cert],
|
||||
stdout=open(os.devnull, 'wb'),
|
||||
stderr=subprocess.STDOUT)
|
||||
except OSError:
|
||||
exit_code = 0
|
||||
if exit_code is 0:
|
||||
try:
|
||||
self.httpd.socket = ssl.wrap_socket(self.httpd.socket,
|
||||
certfile=cert,
|
||||
keyfile=key,
|
||||
server_side=True)
|
||||
except ssl.SSLError as error:
|
||||
self.logger.exception('Failed to init SSL socket')
|
||||
raise TelegramError(str(error))
|
||||
else:
|
||||
raise TelegramError('SSL Certificate invalid')
|
||||
|
||||
@staticmethod
|
||||
def _gen_webhook_url(listen, port, url_path):
|
||||
return 'https://{listen}:{port}{path}'.format(listen=listen, port=port, path=url_path)
|
||||
|
||||
def _bootstrap(self, max_retries, clean, webhook_url, cert=None):
|
||||
retries = 0
|
||||
while 1:
|
||||
|
||||
try:
|
||||
if clean:
|
||||
# Disable webhook for cleaning
|
||||
self.bot.setWebhook(webhook_url='')
|
||||
self._clean_updates()
|
||||
|
||||
self.bot.setWebhook(webhook_url=webhook_url, certificate=cert)
|
||||
except (Unauthorized, InvalidToken):
|
||||
raise
|
||||
except TelegramError:
|
||||
msg = 'error in bootstrap phase; try={0} max_retries={1}'.format(retries,
|
||||
max_retries)
|
||||
if max_retries < 0 or retries < max_retries:
|
||||
self.logger.warning(msg)
|
||||
retries += 1
|
||||
else:
|
||||
self.logger.exception(msg)
|
||||
raise
|
||||
else:
|
||||
break
|
||||
sleep(1)
|
||||
|
||||
def _clean_updates(self):
|
||||
self.logger.debug('Cleaning updates from Telegram server')
|
||||
@@ -336,7 +345,7 @@ class Updater:
|
||||
|
||||
self.job_queue.stop()
|
||||
with self.__lock:
|
||||
if self.running:
|
||||
if self.running or dispatcher.ASYNC_THREADS:
|
||||
self.logger.debug('Stopping Updater and Dispatcher...')
|
||||
|
||||
self.running = False
|
||||
@@ -344,17 +353,15 @@ class Updater:
|
||||
self._stop_httpd()
|
||||
self._stop_dispatcher()
|
||||
self._join_threads()
|
||||
# async threads must be join()ed only after the dispatcher
|
||||
# thread was joined, otherwise we can still have new async
|
||||
# threads dispatched
|
||||
# async threads must be join()ed only after the dispatcher thread was joined,
|
||||
# otherwise we can still have new async threads dispatched
|
||||
self._join_async_threads()
|
||||
|
||||
def _stop_httpd(self):
|
||||
if self.httpd:
|
||||
self.logger.debug(
|
||||
'Waiting for current webhook connection to be '
|
||||
'closed... Send a Telegram message to the bot to exit '
|
||||
'immediately.')
|
||||
self.logger.debug('Waiting for current webhook connection to be '
|
||||
'closed... Send a Telegram message to the bot to exit '
|
||||
'immediately.')
|
||||
self.httpd.shutdown()
|
||||
self.httpd = None
|
||||
|
||||
@@ -363,27 +370,35 @@ class Updater:
|
||||
self.dispatcher.stop()
|
||||
|
||||
def _join_async_threads(self):
|
||||
with dispatcher.async_lock:
|
||||
threads = list(dispatcher.async_threads)
|
||||
total = len(threads)
|
||||
for i, thr in enumerate(threads):
|
||||
self.logger.debug(
|
||||
'Waiting for async thread {0}/{1} to end'.format(i, total))
|
||||
thr.join()
|
||||
self.logger.debug(
|
||||
'async thread {0}/{1} has ended'.format(i, total))
|
||||
with dispatcher.ASYNC_LOCK:
|
||||
threads = list(dispatcher.ASYNC_THREADS)
|
||||
total = len(threads)
|
||||
|
||||
# Stop all threads in the thread pool by put()ting one non-tuple per thread
|
||||
for i in range(total):
|
||||
dispatcher.ASYNC_QUEUE.put(None)
|
||||
|
||||
for i, thr in enumerate(threads):
|
||||
self.logger.debug('Waiting for async thread {0}/{1} to end'.format(i + 1, total))
|
||||
thr.join()
|
||||
dispatcher.ASYNC_THREADS.remove(thr)
|
||||
self.logger.debug('async thread {0}/{1} has ended'.format(i + 1, total))
|
||||
|
||||
def _join_threads(self):
|
||||
for thr in self.__threads:
|
||||
self.logger.debug(
|
||||
'Waiting for {0} thread to end'.format(thr.name))
|
||||
self.logger.debug('Waiting for {0} thread to end'.format(thr.name))
|
||||
thr.join()
|
||||
self.logger.debug('{0} thread has ended'.format(thr.name))
|
||||
self.__threads = []
|
||||
|
||||
def signal_handler(self, signum, frame):
|
||||
self.is_idle = False
|
||||
self.stop()
|
||||
if self.running:
|
||||
self.stop()
|
||||
else:
|
||||
self.logger.warning('Exiting immediately!')
|
||||
import os
|
||||
os._exit(1)
|
||||
|
||||
def idle(self, stop_signals=(SIGINT, SIGTERM, SIGABRT)):
|
||||
"""
|
||||
|
||||
+2
-7
@@ -16,7 +16,6 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""This module contains a object that represents a Telegram File."""
|
||||
|
||||
from os.path import basename
|
||||
@@ -26,7 +25,6 @@ from telegram.utils.request import download as _download
|
||||
|
||||
|
||||
class File(TelegramObject):
|
||||
|
||||
"""This object represents a Telegram File.
|
||||
|
||||
Attributes:
|
||||
@@ -43,9 +41,7 @@ class File(TelegramObject):
|
||||
file_path (Optional[str]):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
file_id,
|
||||
**kwargs):
|
||||
def __init__(self, file_id, **kwargs):
|
||||
# Required
|
||||
self.file_id = str(file_id)
|
||||
# Optionals
|
||||
@@ -66,8 +62,7 @@ class File(TelegramObject):
|
||||
|
||||
return File(**data)
|
||||
|
||||
def download(self,
|
||||
custom_path=None):
|
||||
def download(self, custom_path=None):
|
||||
"""
|
||||
Args:
|
||||
custom_path (str):
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""This module contains a object that represents a Telegram ForceReply."""
|
||||
|
||||
from telegram import ReplyMarkup
|
||||
@@ -37,9 +36,7 @@ class ForceReply(ReplyMarkup):
|
||||
selective (Optional[bool]):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
force_reply=True,
|
||||
**kwargs):
|
||||
def __init__(self, force_reply=True, **kwargs):
|
||||
# Required
|
||||
self.force_reply = bool(force_reply)
|
||||
# Optionals
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#!/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
|
||||
InlineKeyboardButton"""
|
||||
|
||||
from telegram import TelegramObject
|
||||
|
||||
|
||||
class InlineKeyboardButton(TelegramObject):
|
||||
"""This object represents a Telegram InlineKeyboardButton.
|
||||
|
||||
Attributes:
|
||||
text (str):
|
||||
url (str):
|
||||
callback_data (str):
|
||||
switch_inline_query (str):
|
||||
|
||||
Args:
|
||||
text (str):
|
||||
**kwargs: Arbitrary keyword arguments.
|
||||
|
||||
Keyword Args:
|
||||
url (Optional[str]):
|
||||
callback_data (Optional[str]):
|
||||
switch_inline_query (Optional[str]):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, text, **kwargs):
|
||||
# Required
|
||||
self.text = text
|
||||
|
||||
# Optionals
|
||||
self.url = kwargs.get('url')
|
||||
self.callback_data = kwargs.get('callback_data')
|
||||
self.switch_inline_query = kwargs.get('switch_inline_query')
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineKeyboardButton, InlineKeyboardButton).de_json(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
return InlineKeyboardButton(**data)
|
||||
|
||||
@staticmethod
|
||||
def de_list(data):
|
||||
if not data:
|
||||
return []
|
||||
|
||||
inline_keyboards = list()
|
||||
for inline_keyboard in data:
|
||||
inline_keyboards.append(InlineKeyboardButton.de_json(inline_keyboard))
|
||||
|
||||
return inline_keyboards
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Leandro Toledo de Souza <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
|
||||
InlineKeyboardMarkup"""
|
||||
|
||||
from telegram import ReplyMarkup, InlineKeyboardButton
|
||||
|
||||
|
||||
class InlineKeyboardMarkup(ReplyMarkup):
|
||||
"""This object represents a Telegram InlineKeyboardMarkup.
|
||||
|
||||
Attributes:
|
||||
inline_keyboard (List[List[:class:`telegram.InlineKeyboardButton`]]):
|
||||
|
||||
Args:
|
||||
inline_keyboard (List[List[:class:`telegram.InlineKeyboardButton`]]):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, inline_keyboard, **kwargs):
|
||||
# Required
|
||||
self.inline_keyboard = inline_keyboard
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineKeyboardMarkup, InlineKeyboardMarkup).de_json(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['inline_keyboard'] = [InlineKeyboardButton.de_list(inline_keyboard)
|
||||
for inline_keyboard in data['inline_keyboard']]
|
||||
|
||||
return InlineKeyboardMarkup(**data)
|
||||
|
||||
def to_dict(self):
|
||||
data = super(InlineKeyboardMarkup, self).to_dict()
|
||||
|
||||
data['inline_keyboard'] = []
|
||||
for inline_keyboard in self.inline_keyboard:
|
||||
data['inline_keyboard'].append([x.to_dict() for x in inline_keyboard])
|
||||
|
||||
return data
|
||||
+13
-9
@@ -16,10 +16,9 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""This module contains a object that represents a Telegram InlineQuery"""
|
||||
|
||||
from telegram import TelegramObject, User
|
||||
from telegram import TelegramObject, User, Location
|
||||
|
||||
|
||||
class InlineQuery(TelegramObject):
|
||||
@@ -39,20 +38,22 @@ class InlineQuery(TelegramObject):
|
||||
from_user (:class:`telegram.User`):
|
||||
query (str):
|
||||
offset (str):
|
||||
**kwargs: Arbitrary keyword arguments.
|
||||
|
||||
Keyword Args:
|
||||
location (optional[:class:`telegram.Location`]):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
id,
|
||||
from_user,
|
||||
query,
|
||||
offset):
|
||||
def __init__(self, id, from_user, query, offset, **kwargs):
|
||||
# Required
|
||||
self.id = id
|
||||
self.from_user = from_user
|
||||
self.query = query
|
||||
self.offset = offset
|
||||
|
||||
# Optional
|
||||
self.location = kwargs.get('location')
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
"""
|
||||
@@ -62,10 +63,13 @@ class InlineQuery(TelegramObject):
|
||||
Returns:
|
||||
telegram.InlineQuery:
|
||||
"""
|
||||
data = super(InlineQuery, InlineQuery).de_json(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
data = data.copy()
|
||||
data['from_user'] = User.de_json(data.pop('from'))
|
||||
|
||||
data['from_user'] = User.de_json(data.get('from'))
|
||||
data['location'] = Location.de_json(data.get('location'))
|
||||
|
||||
return InlineQuery(**data)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user