Compare commits

..

156 Commits

Author SHA1 Message Date
Jannes Höke 57efde5e0f Bump version to v4.3 2016-06-28 13:35:42 +02:00
Noam Meltzer e0539d5992 Merge pull request #327 from python-telegram-bot/urllib3
Urllib3
2016-06-20 06:30:25 +03:00
leandrotoledo b41f7e3e79 Code style with latest yapf 2016-06-19 17:50:02 -04:00
Jannes Höke caf72ca490 Merge branch 'urllib3' of github.com:python-telegram-bot/python-telegram-bot into urllib3 2016-06-19 23:46:53 +02:00
Jannes Höke 7635bc0eec comments, lock thread pool, while 1 and snake_case everywhere 2016-06-19 23:46:34 +02:00
Jannes Höke 703bece155 set loglevel of urllib3 to WARNING by default 2016-06-19 23:40:34 +02:00
Jannes Höke 949f4a4fbd update requirements: minimum versions of urllib3 and future 2016-06-19 23:39:00 +02:00
leandrotoledo 05522e4321 Merge remote-tracking branch 'origin/master' into urllib3 2016-06-19 17:38:19 -04:00
leandrotoledo 4f101a79bb Update travis yapf [ci skip] 2016-06-19 17:08:12 -04:00
Noam Meltzer 5b91194cc7 new yapf version, new cosmetic fixes 2016-06-18 20:05:10 +03:00
Noam Meltzer 494a7ec1e4 ypaf fixes 2016-06-18 19:57:11 +03:00
Noam Meltzer fc05d3a626 switch back to PoolManager
telegram servers might send a reponse with HTTP 302 (redirect) to
another hostname. in such case HTTPSConnectionPool will fail to do the
job
2016-06-18 19:50:18 +03:00
Noam Meltzer bc77c845ea test_updater: make sure that conpool is stopped before setting updater
even for the first unitest, it might come after another unitests from
another file which had already init the conpool.
2016-06-18 09:53:08 +03:00
Noam Meltzer a814e9de6b make sure to stop conpool between sensitive unitests 2016-06-18 00:50:44 +03:00
Noam Meltzer d37b6d6735 make sure to stop Updater after the test_createBot is over 2016-06-18 00:01:36 +03:00
Noam Meltzer e479c7f25e type hinting (cosmetic fix) 2016-06-17 23:59:32 +03:00
Noam Meltzer a30411c9fa make sure to remove the stopped dispatcher threads from ASYNC_THREADS 2016-06-17 23:58:22 +03:00
Noam Meltzer 881d1d0e25 fix/hack Updater.stop() not working on extreme cases
during test_bootstrap_retries_fail() there is an exception raised (by
design): TelegramError('test')

For a reason I haven't managed to pinpoint the above exception in its
precise timing caused the Updater to be left in a state which is
'self.running == False', but the dispatcher threads already initialized.
This patch identifies this extreme case and makes sure to go over the
stop procedure.
2016-06-17 23:53:18 +03:00
Noam Meltzer cb6ddfded5 Merge remote-tracking branch 'origin/master' into urllib3 2016-06-17 17:54:04 +03:00
Noam Meltzer bda0244ed8 updater: fix print in log 2016-06-17 16:52:25 +03:00
Rahiel Kasim 9338f93d24 Merge pull request #325 from python-telegram-bot/examples
more robust echobot, let roboed go
2016-06-12 17:08:15 +02:00
Rahiel Kasim e10fa66286 echobot: simplify handling messageless updates 2016-06-12 17:06:03 +02:00
Rahiel Kasim deb9de0ba0 README: remove roboed, rename example 2016-06-12 16:58:18 +02:00
Rahiel Kasim 94fd6851ab more robust echobot, let roboed go 2016-06-12 15:30:56 +02:00
leandrotoledo 897f9615f0 Bump version to v4.2.1 2016-06-10 09:44:17 -04:00
Leandro Toledo 86676d59f1 Merge pull request #321 from python-telegram-bot/editMessageText-decorator
Adds @message decorator to editMessageText #320
2016-06-04 05:19:27 -04:00
leandrotoledo f0b91ecf46 Fix travis 2016-06-03 13:44:24 -04:00
leandrotoledo bbbc622517 Adds @message decorator to editMessageText #320 2016-06-03 13:28:29 -04:00
Noam Meltzer 1f5601dae2 fix SyntaxWarning 2016-06-01 22:38:08 +03:00
Noam Meltzer 3608c2bbe5 dispatcher: if connection pool is already initialized raise exception
this will better protect the user from wrong usage
2016-06-01 22:30:34 +03:00
Noam Meltzer c28763c5be dispatcher: cosmetic fix 2016-06-01 22:30:33 +03:00
Noam Meltzer dd8b6219b9 dispatcher: a little performance improvment 2016-06-01 22:30:33 +03:00
Noam Meltzer 78f9bdcac9 dispatcher: pep8 style fix
globals are supposed to be upper case
2016-06-01 22:30:09 +03:00
Jannes Höke da95341d5b Update README.rst 2016-06-01 00:05:09 +02:00
Jannes Höke 98be6abc11 Remove clibot.py example 2016-05-31 21:07:47 +02:00
Jannes Höke 1ff348adbb issue warning if connection pool was initialized before Dispatcher 2016-05-31 13:47:43 +02:00
Jannes Höke 6b457bada5 use keepalive for connection pool 2016-05-31 13:45:43 +02:00
Jannes Höke 74283bd414 use HTTPSConnectionPool instead of PoolManager 2016-05-30 17:12:50 +02:00
Jannes Höke 41f6591ac6 more sensible logging 2016-05-30 17:12:27 +02:00
Leandro Toledo 6d08e1bc7f Merge pull request #317 from jlmadurga/issue/316
Fix callbackquery to_dict
2016-05-30 11:35:44 -03:00
Juan Madurga 073d7949dc fix callbackquery to_dict 2016-05-30 15:59:45 +02:00
Jannes Höke dd91ce1f39 use single queue for thread pool, initialize connection pool with n+3 2016-05-30 13:09:23 +02:00
Jannes Höke 57759d8e6d [drunk] use actual thread pool and queue new functions into the pool instead of starting new threads every time 2016-05-30 03:16:33 +02:00
Noam Meltzer 574fc8cddf urllib3: validate https certificate 2016-05-30 01:05:19 +03:00
Noam Meltzer b040568b07 test_bot: fix for urllib3 compatibility 2016-05-30 01:05:19 +03:00
Noam Meltzer 3076dfc086 use urllib3 instead of urllib(2) 2016-05-30 01:05:19 +03:00
Jannes Höke f8a9722573 remove duplicate target names 2016-05-29 12:35:52 +02:00
Leandro Toledo 527daaf93d Merge pull request #315 from jlmadurga/typo/changes
Fix typo in Transition to 4.0 link
2016-05-29 06:50:57 -03:00
Juan Madurga 986add59ab fix typo in Transition to 4.0 link 2016-05-29 11:37:35 +02:00
Jannes Höke bc62a1813a fix rst according to collective.checkdocs 2016-05-28 23:36:51 +02:00
Jannes Höke 41432f5b02 bump version to v4.2.0 2016-05-28 22:49:15 +02:00
Jannes Höke f737ab780a Merge pull request #314 from python-telegram-bot/docs-restructuring
Move large parts from README to Wiki
2016-05-28 22:44:17 +02:00
Rahiel Kasim e0e8f6b085 README: update docs badge link, https some links 2016-05-28 22:40:18 +02:00
Jannes Höke 17abf0274e Fix formatting 2016-05-28 21:52:03 +02:00
Jannes Höke 8fab7ad302 move stuff to wiki, other stuff 2016-05-28 21:44:10 +02:00
Rahiel Kasim f31bd91673 Merge pull request #305 from python-telegram-bot/move-botan
move botan from utils to contrib
2016-05-28 21:32:07 +02:00
Leandro Toledo 32ac617e2e Merge pull request #312 from python-telegram-bot/timeout_take2
set default network_delay to 5 seconds
2016-05-28 15:14:54 -03:00
Noam Meltzer 7e7acdeb23 set default network_delay to 5 seconds
fixes #309
2016-05-28 19:34:16 +03:00
Leandro Toledo 792ad62fe8 Merge pull request #308 from python-telegram-bot/bot2.1
New methods for Bot 2.1 API
2016-05-28 12:14:01 -03:00
Jannes Höke 25bcfa9b35 add constants for Chat.type and ChatMember.status 2016-05-28 16:51:44 +02:00
Jannes Höke ff00e211d7 include in warning that Py2.7 will still be supported 2016-05-28 16:44:39 +02:00
Rahiel Kasim b06983a94a let python 2 find the contrib module 2016-05-28 09:27:17 +02:00
Rahiel Kasim 2f408ab767 generate docs for botan 2016-05-28 09:18:36 +02:00
Rahiel Kasim c8497424b7 move botan to contrib 2016-05-28 09:12:10 +02:00
Leandro Toledo 80fbe98b44 Reflecting tests for Filters change 2016-05-27 21:14:55 -03:00
Leandro Toledo 22e9326610 Merge remote-tracking branch 'origin/master' into bot2.1 2016-05-27 21:10:24 -03:00
Jannes Höke a0bb5730c6 add allow_edited parameter to MessageHandler and CommandHandler 2016-05-27 11:07:06 +02:00
Jannes Höke 748cc3a35f Add isitmaintained.com issue resolving time badge 2016-05-26 23:30:29 +02:00
Leandro Toledo 9a13de4a96 Merge remote-tracking branch 'origin/master' into bot2.1
Conflicts:
	telegram/bot.py
	tests/test_bot.py
2016-05-26 16:15:50 -03:00
Noam Meltzer 561f1c3f02 bot: validate token does not contain white spaces (#306)
in addition move validation code from validate.py into bot.py and delete
the former file
2016-05-26 22:09:14 +03:00
Leandro Toledo 3907e64966 Adds telegram.utils.botan back using deprecate 2016-05-26 14:13:27 -03:00
Rahiel Kasim 1abbca3324 bot.py: fix snake_case alias 2016-05-26 15:32:02 +02:00
Leandro Toledo 86571bc75d addHandler to add_handler 2016-05-26 01:55:51 -03:00
Leandro Toledo 663fa0013d merge master 2016-05-25 22:09:18 -03:00
Leandro Toledo 37c7af2e14 Add docstrings #302 2016-05-25 21:41:12 -03:00
Leandro Toledo e70625772c Keeps backwards compatibility to BadRequest #302 2016-05-25 21:24:29 -03:00
Leandro Toledo 1e398821a0 Introducing telegram.error.BadRequest and testLeaveChat testcase #302 2016-05-25 21:15:17 -03:00
Rahiel Kasim 386b91f708 refactor testing if user is bot 2016-05-25 09:41:48 +02:00
Rahiel Kasim 76b9a7d328 add tests for Bot.getChat* 2016-05-25 01:31:10 +02:00
Leandro Toledo d90b0f495d End the madness when bumping versions [ci skip] 2016-05-24 19:43:24 -03:00
Rahiel Kasim c4d5eff9f3 move botan from utils to ext 2016-05-24 23:40:09 +02:00
Rahiel Kasim f45ecee820 Merge pull request #304 from TiagoDanin/FixReadme
README Update url for contribution guidelines
2016-05-24 23:09:13 +02:00
Tiago Danin c340585f33 README Update url for contribution guidelines 2016-05-24 15:49:43 -05:00
Leandro Toledo 75490ac757 Fix travis 2016-05-23 22:13:38 -03:00
Leandro Toledo 046e69b1c1 Commenting test for token with newline 2016-05-23 22:02:15 -03:00
Leandro Toledo 7e0be09c58 Merge remote-tracking branch 'origin/master' into bot2.1 2016-05-23 21:56:01 -03:00
Leandro Toledo 18f3f43026 Add issue template #298 2016-05-23 21:54:36 -03:00
Leandro Toledo 0b2fd120d8 Due kwargs I had to change the factory class in favor of InputLocationMessageContent #302 2016-05-23 21:09:07 -03:00
Leandro Toledo 408959e91c Removes examples from tests 2016-05-23 20:44:47 -03:00
Leandro Toledo ab2f6e13c9 Add kwargs to API calls #302 2016-05-23 20:43:17 -03:00
Leandro Toledo 108e4264fc Add user to MessageEntity #302 2016-05-23 20:31:36 -03:00
Leandro Toledo 68b5562c49 Add edit_date to Message #302 2016-05-23 20:28:36 -03:00
Leandro Toledo e50a3622e1 Add edited_message to Update #302 2016-05-23 20:24:43 -03:00
Leandro Toledo d7e226ec0f Add new Bot methods and ChatMember class #302 2016-05-23 20:22:31 -03:00
Leandro Toledo 7c84516d2b Merge branch 'master' of github.com:python-telegram-bot/python-telegram-bot 2016-05-23 19:44:34 -03:00
Leandro Toledo 53a91be21f Update CONTRIBUTING.rst 2016-05-23 19:05:56 -03:00
Leandro Toledo 2471eaa778 Adding some style commandments 2016-05-23 19:05:04 -03:00
leandrotoledo 6bfdff8892 Update examples to column width to 99 [ci skip] 2016-05-23 17:45:01 -03:00
leandrotoledo 2389b07382 Updates pre-commit hooks to check examples folder [ci skip] 2016-05-23 17:23:57 -03:00
leandrotoledo c7db9a96cd Set split_before_logical_operator to True 2016-05-23 17:19:35 -03:00
Leandro Toledo af89cbecf3 Add test for unstripped tokens [ci skip] 2016-05-22 18:22:22 -03:00
leandrotoledo b987c8937c Hooks for travis 2016-05-22 13:26:57 -03:00
leandrotoledo 5a0696b181 Replace individual checks to pre-commit run --all-files in Travis 2016-05-22 13:12:05 -03:00
leandrotoledo eb303903ef Fix travis 2016-05-22 12:55:48 -03:00
leandrotoledo a00f409992 Updates pre-commit hooks 2016-05-22 12:31:03 -03:00
Jannes Höke dc27ff41ef bump version to 4.1.2 2016-05-22 13:01:14 +02:00
Jannes Höke 68ec73afb6 use kwargs on messageentity 2016-05-22 12:58:19 +02:00
Rahiel Kasim 0ace0aa016 README: remove "Getting the Code" section, confuses users like #297 2016-05-19 17:06:28 +02:00
Jannes Höke f5847be8ca update file size from 684 to 685 2016-05-17 10:19:00 +02:00
Jannes Höke ba26a8ba5d use command filter instead of regexhandler #292 2016-05-17 07:36:04 +02:00
Jannes Höke d028d4edd0 bump to 4.1.1 2016-05-16 16:05:02 +02:00
Jannes Höke 4d770843cc use non-deprecated methods 2016-05-16 15:02:51 +02:00
Rahiel Kasim 53de38f6c9 README: fix formatting inline code 2016-05-16 01:46:42 +02:00
Leandro Toledo be105a2d4a Update CHANGES.rst 2016-05-15 20:24:09 -03:00
leandrotoledo ac55ba007e Bumping version to v4.1 [ci skip] 2016-05-15 20:05:38 -03:00
Leandro Toledo 6bdca1e4f8 Merge pull request #294 from python-telegram-bot/yapf
yapf formatter, pre-commit hooks and new travis tests
2016-05-15 19:31:54 -03:00
Leandro Toledo fd7b571e92 Disables yapf for travis tests for pypy3 (ugh!) #259 2016-05-15 00:19:59 -03:00
Leandro Toledo 9243cd3507 Minor typo fix #259 [ci skip] 2016-05-15 00:11:14 -03:00
Leandro Toledo 1b09738191 Disables yapf for travis tests for pypy3.3 #259 2016-05-15 00:07:59 -03:00
Leandro Toledo 836c50965a Disables yapf for travis tests for py2.4 and 3.3 #259 2016-05-14 23:59:45 -03:00
Leandro Toledo 1d9d0fc764 Disables yapf for travis tests for py2.4 and 3.3 #259 2016-05-14 23:53:02 -03:00
Leandro Toledo e57e6dd645 Add yapf to travis and updates CONTRIBUTING doc #259 2016-05-14 23:46:21 -03:00
Leandro Toledo 49122d6a99 Remove \ from base code #259 2016-05-14 23:26:56 -03:00
Leandro Toledo d445d35ceb Running yapf for codebase #259 2016-05-14 22:46:40 -03:00
Leandro Toledo 703b8d1301 Adding more pre-commit hooks and testing InputFile #259 2016-05-14 22:24:35 -03:00
Leandro Toledo 46993d5f2d Adding pre-commit hooks #259 2016-05-14 21:59:08 -03:00
Leandro Toledo 880746baed Column width to 90 #259 2016-05-14 21:52:35 -03:00
Leandro Toledo 8ad1f330ea First run of yapf for tests/* #259 2016-05-14 21:39:11 -03:00
Leandro Toledo 56b1d4f5ce Merge pull request #265 from python-telegram-bot/snakes
rename methods to snake_case
2016-05-14 10:56:24 -03:00
Leandro Toledo df16846d80 Update README.rst
#293
2016-05-14 10:28:16 -03:00
Jannes Höke 3ac9a1cd71 Merge pull request #291 from python-telegram-bot/api-2016-05-06
Api additions 2016-05-06
2016-05-13 21:31:35 +02:00
Jannes Höke 4d8174edc3 check for py2 and decode 2016-05-12 08:31:47 +02:00
Jannes Höke 235bb72702 use future.utils.text_to_native_str 2016-05-12 08:17:05 +02:00
Rahiel Kasim 9a5ccb1c2d fix sticker emoji test
We always get utf-8 text from Telegram, so we compare to that.
2016-05-12 01:10:04 +02:00
Jannes Höke a18640a8d3 remove str conversion for emoji 2016-05-12 00:16:29 +02:00
Jannes Höke fdc3ac0cc5 copy paste mishap 2016-05-11 23:37:46 +02:00
Jannes Höke d881fa6a5f add forward_from_chat field to message 2016-05-11 23:22:05 +02:00
Jannes Höke c551d71735 add emoji field to sticker 2016-05-11 23:19:35 +02:00
Jannes Höke bee3d881d1 location docstring fix 2016-05-11 23:07:32 +02:00
Jannes Höke 6ec81dd552 move cleaning updates to bootstrapping phase (#282) 2016-05-11 00:58:55 +03:00
Rahiel Kasim 6e9f30ca6e fix merge conflict snakes 2016-05-02 16:37:45 +02:00
Jannes Höke 252cafb04c fix docs for inlinekeyboardmarkup #277 2016-05-02 14:19:06 +02:00
Jannes Höke d4f7b7165c flake8: set max line length to 99 2016-05-02 10:35:20 +02:00
Leandro Toledo 0ed5e8e1a3 Merge pull request #274 from kiote/master
Update README.rst
2016-05-01 18:29:55 -03:00
Ekaterina Krivich 335813a11a Update README.rst
answerCallbackQuery mentioned twice
2016-05-02 00:28:09 +03:00
Rahiel Kasim 5971cb35f8 fix merge conflict snakes with master 2016-04-30 14:56:48 +02:00
Rahiel Kasim 5e5510d42b add deprecation warning for Python 2.6 users 2016-04-28 17:56:03 +02:00
Rahiel Kasim 73c60ee817 simpler py2/py3 input in clibot example 2016-04-28 17:32:10 +02:00
Rahiel Kasim 474d5f0c9f update README and examples to new snake_case methods 2016-04-28 17:29:34 +02:00
Rahiel Kasim 74a2baf03d update CONTRIBUTING guide 2016-04-28 15:41:44 +02:00
Rahiel Kasim 0612385233 set project max line length to 99 (flake8) 2016-04-28 15:03:43 +02:00
Rahiel Kasim c0489db17c fix import of deprecate function 2016-04-28 14:47:56 +02:00
Rahiel Kasim 592352c849 dispatcher/*handler methods to snake_case + deprecation warnings 2016-04-28 14:29:27 +02:00
Rahiel Kasim 9d367e9f2c telegram.utils.deprecate: helper module to facilitate deprecation 2016-04-28 14:24:12 +02:00
Rahiel Kasim d86ca30601 README: document snake_case alias methods for telegram.Bot 2016-04-28 12:44:35 +02:00
Rahiel Kasim ec15e866be add snake_case aliases for CamelCase methods 2016-04-28 12:20:42 +02:00
151 changed files with 2047 additions and 2410 deletions
+186 -132
View File
@@ -1,132 +1,186 @@
How To Contribute
=================
Every open source project lives from the generous help by contributors that sacrifice their time and ``python-telegram-bot`` is no different. To make participation as pleasant as possible, this project adheres to the `Code of Conduct`_ by the Python Software Foundation.
Setting things up
-----------------
1. Fork the ``python-telegram-bot`` repository to your GitHub account.
2. Clone your forked repository of ``python-telegram-bot`` to your computer:
``$ git clone https://github.com/<your username>/python-telegram-bot``
``$ cd python-telegram-bot``
3. Add a track to the original repository:
``$ git remote add upstream https://github.com/python-telegram-bot/python-telegram-bot``
4. Install dependencies:
``$ pip install -r requirements.txt``
``$ pip install -r requirements-dev.txt``
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.
- The following exceptions to the above (Google's) style guides applies:
- Documenting types of global variables and complex types of class members can be done using the Sphinx docstring convention.
- Please ensure that the code you write is well-tested.
- Dont break backward compatibility.
- Add yourself to the AUTHORS.rst_ file in an alphabetical fashion.
- Before making a commit ensure that all automated tests, pep8 & lint validations still pass:
``$ make test``
``$ make pep8``
``$ make lint``
- 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:
``$ git clone https://github.com/<your username>/python-telegram-bot``
``$ cd python-telegram-bot``
3. Add a track to the original repository:
``$ git remote add upstream https://github.com/python-telegram-bot/python-telegram-bot``
4. Install dependencies:
``$ pip install -r requirements.txt -r requirements-dev.txt``
5. Install pre-commit hooks:
``$ pre-commit install``
Finding something to do
-----------------------
If you already know what you'd like to work on, you can skip this section.
If you have an idea for something to do, first check if it's already been filed on the `issue tracker`_. If so, add a comment to the issue saying you'd like to work on it, and we'll help you get started! Otherwise, please file a new issue and assign yourself to it.
Another great way to start contributing is by writing tests. Tests are really important because they help prevent developers from accidentally breaking existing code, allowing them to build cool things faster. If you're interested in helping out, let the development team know by posting to the `developers' mailing list`_, and we'll help you get started.
Instructions for making a code change
-------------------------------------
The central development branch is ``master``, which should be clean and ready for release at any time. In general, all changes should be done as feature branches based off of ``master``.
Here's how to make a one-off code change.
1. **Choose a descriptive branch name.** It should be lowercase, hyphen-separated, and a noun describing the change (so, ``fuzzy-rules``, but not ``implement-fuzzy-rules``). Also, it shouldn't start with ``hotfix`` or ``release``.
2. **Create a new branch with this name, starting from** ``master``. In other words, run:
``$ git fetch upstream``
``$ git checkout master``
``$ git merge upstream/master``
``$ git checkout -b your-branch-name``
3. **Make a commit to your feature branch**. Each commit should be self-contained and have a descriptive commit message that helps other developers understand why the changes were made.
- You can refer to relevant issues in the commit message by writing, e.g., "#105".
- Your code should adhere to the `PEP 8 Style Guide`_, with the exception that we have a maximum line length of 99.
- For consistency, please conform to `Google Python Style Guide`_ and `Google Python Style Docstrings`_. In addition, code should be formatted consistently with other code around it.
- The following exceptions to the above (Google's) style guides applies:
- Documenting types of global variables and complex types of class members can be done using the Sphinx docstring convention.
- Please ensure that the code you write is well-tested.
- Dont break backward compatibility.
- Add yourself to the AUTHORS.rst_ file in an alphabetical fashion.
- Before making a commit ensure that all automated tests still pass:
``$ make test``
- To actually make the commit (this will trigger tests for yapf, lint and pep8 automatically):
``$ git add your-file-changed.py``
- yapf may change code formatting, make sure to re-add them to your commit.
``$ git commit -a -m "your-commit-message-here"``
- Finally, push it to your GitHub fork, run:
``$ git push origin your-branch-name``
4. **When your feature is ready to merge, create a pull request.**
- Go to your fork on GitHub, select your branch from the dropdown menu, and click "New pull request".
- Add a descriptive comment explaining the purpose of the branch (e.g. "Add the new API feature to create inline bot queries."). This will tell the reviewer what the purpose of the branch is.
- Click "Create pull request". An admin will assign a reviewer to your commit.
5. **Address review comments until all reviewers give LGTM ('looks good to me').**
- When your reviewer has reviewed the code, you'll get an email. You'll need to respond in two ways:
- Make a new commit addressing the comments you agree with, and push it to the same branch. Ideally, the commit message would explain what the commit does (e.g. "Fix lint error"), but if there are lots of disparate review comments, it's fine to refer to the original commit message and add something like "(address review comments)".
- In addition, please reply to each comment. Each reply should be either "Done" or a response explaining why the corresponding suggestion wasn't implemented. All comments must be resolved before LGTM can be given.
- Resolve any merge conflicts that arise. To resolve conflicts between 'your-branch-name' (in your fork) and 'master' (in the ``python-telegram-bot`` repository), run:
``$ git checkout your-branch-name``
``$ git fetch upstream``
``$ git merge upstream/master``
``$ ...[fix the conflicts]...``
``$ ...[make sure the tests pass before committing]...``
``$ git commit -a``
``$ git push origin your-branch-name``
- At the end, the reviewer will merge the pull request.
6. **Tidy up!** Delete the feature branch from both your local clone and the GitHub repository:
``$ git branch -D your-branch-name``
``$ git push origin --delete your-branch-name``
7. **Celebrate.** Congratulations, you have contributed to ``python-telegram-bot``!
Style commandments
==================
Specific commandments
---------------------
- Avoid using "double quotes" where you can reasonably use 'single quotes'.
AssertEqual argument order
--------------------------
assertEqual method's arguments should be in ('actual', 'expected') order.
Properly calling callables
--------------------------
Methods, functions and classes can specify optional parameters (with default
values) using Python's keyword arg syntax. When providing a value to such a
callable we prefer that the call also uses keyword arg syntax. For example::
# GOOD
f(0, optional=True)
# BAD
f(0, True)
This gives us the flexibility to re-order arguments and more importantly
to add new required arguments. It's also more explicit and easier to read.
Properly defining optional arguments
------------------------------------
It's always good to not initialize optional arguments at class creation,
instead use ``**kwargs`` to get them. It's well known Telegram API can
change without notice, in that case if a new argument is added it won't
break the API classes. For example::
# GOOD
def __init__(self, id, name, **kwargs):
self.last_name = kwargs.get('last_name', '')
# BAD
def __init__(self, id, name, last_name=''):
self.last_name = last_name
.. _`Code of Conduct`: https://www.python.org/psf/codeofconduct/
.. _`issue tracker`: https://github.com/python-telegram-bot/python-telegram-bot/issues
.. _`developers' mailing list`: mailto:devs@python-telegram-bot.org
.. _`PEP 8 Style Guide`: https://www.python.org/dev/peps/pep-0008/
.. _`Google Python Style Guide`: https://google-styleguide.googlecode.com/svn/trunk/pyguide.html
.. _`Google Python Style Docstrings`: http://sphinx-doc.org/latest/ext/example_google.html
.. _AUTHORS.rst: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/AUTHORS.rst
+31
View File
@@ -0,0 +1,31 @@
<!--
Thanks for reporting issues of python-telegram-bot!
To make it easier for us to help you please enter detailed information below.
-->
### Steps to reproduce
1.
2.
3.
### Expected behaviour
Tell us what should happen
### Actual behaviour
Tell us what happens instead
### Configuration
**Operating System:**
**Version of Python:**
``$ python -V``
**Version of python-telegram-bot:**
``$ python -c 'import telegram; print(telegram.__version__)'``
### Logs
Insert logs here (if necessary)
+18
View File
@@ -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: 6dfcb89af3c9b4d172cc2e5a8a2fa0f54615a338
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
View File
@@ -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
+44 -1
View File
@@ -1,3 +1,46 @@
**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*
@@ -17,7 +60,7 @@
- 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>`_
- 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
+9 -3
View File
@@ -1,10 +1,11 @@
.DEFAULT_GOAL := help
.PHONY: clean pep8 lint test install
.PHONY: clean pep257 pep8 yapf lint test install
PYLINT := pylint
NOSETESTS := nosetests
PEP257 := pep257
PEP8 := flake8
YAPF := yapf
PIP := pip
clean:
@@ -19,7 +20,10 @@ pep257:
$(PEP257) telegram
pep8:
$(PEP8) telegram
$(PEP8) telegram
yapf:
$(YAPF) -r telegram
lint:
$(PYLINT) -E telegram --disable=no-name-in-module,import-error
@@ -28,7 +32,7 @@ test:
$(NOSETESTS) -v
install:
$(PIP) install -r requirements.txt
$(PIP) install -r requirements.txt -r requirements-dev.txt
help:
@echo "Available targets:"
@@ -36,6 +40,7 @@ help:
@echo "- pep257 Check docstring style with pep257"
@echo "- pep8 Check style with flake8"
@echo "- lint Check style with pylint"
@echo "- yapf Check style with yapf"
@echo "- test Run tests"
@echo
@echo "Available variables:"
@@ -43,4 +48,5 @@ help:
@echo "- NOSETESTS default: $(NOSETESTS)"
@echo "- PEP257 default: $(PEP257)"
@echo "- PEP8 default: $(PEP8)"
@echo "- YAPF default: $(YAPF)"
@echo "- PIP default: $(PIP)"
+77 -395
View File
@@ -15,12 +15,8 @@ Not **just** a Python wrapper around the Telegram Bot API
:target: https://pypi.python.org/pypi/python-telegram-bot
:alt: Supported python versions
.. image:: https://img.shields.io/pypi/dm/python-telegram-bot.svg
:target: https://pypi.python.org/pypi/python-telegram-bot
:alt: PyPi Package Monthly Download
.. image:: https://readthedocs.org/projects/python-telegram-bot/badge/?version=latest
:target: https://readthedocs.org/projects/python-telegram-bot/?badge=latest
.. 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
@@ -38,6 +34,10 @@ Not **just** a Python wrapper around the Telegram Bot API
.. image:: https://coveralls.io/repos/python-telegram-bot/python-telegram-bot/badge.svg?branch=master&service=github
:target: https://coveralls.io/github/python-telegram-bot/python-telegram-bot?branch=master
:alt: Coveralls
.. image:: http://isitmaintained.com/badge/resolution/python-telegram-bot/python-telegram-bot.svg
:target: http://isitmaintained.com/project/python-telegram-bot/python-telegram-bot
:alt: Average time to resolve an issue
.. image:: https://img.shields.io/badge/Telegram-Group-blue.svg
:target: https://telegram.me/pythontelegrambotgroup
@@ -53,18 +53,10 @@ Table of contents
- `Installing`_
- `Getting the code`_
- `Getting started`_
#. `Learning by example`_
#. `API`_
#. `Extensions`_
#. `JobQueue`_
#. `Logging`_
#. `Documentation`_
@@ -75,47 +67,29 @@ Table of contents
- `License`_
===============
_`Introduction`
===============
============
Introduction
============
This library provides a pure Python interface for the `Telegram Bot API <https://core.telegram.org/bots/api>`_. It works with Python versions from 2.6+. It also works with `Google App Engine <https://cloud.google.com/appengine>`_.
This library provides a pure Python interface for the
`Telegram Bot API <https://core.telegram.org/bots/api>`_.
It works with Python versions from 2.6+ (**Note:** Support for 2.6 will be dropped at some point
this year. 2.7 will still be supported).
It also works with `Google App Engine <https://cloud.google.com/appengine>`_.
=======================
_`Telegram API support`
=======================
In addition to the pure API implementation, this library features a number of high-level classes to
make the development of bots easy and straightforward. These classes are contained in the
``telegram.ext`` submodule.
========================= ============
Telegram Bot API Method *Supported?*
========================= ============
getMe Yes
sendMessage Yes
forwardMessage Yes
sendPhoto Yes
sendAudio Yes
sendDocument Yes
sendSticker Yes
sendVideo Yes
sendVoice Yes
sendLocation Yes
sendChatAction Yes
getUpdates Yes
getUserProfilePhotos Yes
getFile Yes
setWebhook Yes
answerInlineQuery Yes
kickChatMember Yes
unbanChatMember Yes
answerCallbackQuery Yes
editMessageText Yes
editMessageCaption Yes
editMessageReplyMarkup Yes
answerCallbackQuery Yes
========================= ============
====================
Telegram API support
====================
=============
_`Installing`
=============
As of **28. May 2016**, all types and methods of the Telegram Bot API are supported.
==========
Installing
==========
You can install or upgrade python-telegram-bot with:
@@ -123,44 +97,47 @@ You can install or upgrade python-telegram-bot with:
$ 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>`_
.. code:: shell
Other references:
$ git clone https://github.com/python-telegram-bot/python-telegram-bot
$ cd python-telegram-bot
- `Telegram API documentation <https://core.telegram.org/bots/api>`_
- `python-telegram-bot documentation <https://pythonhosted.org/python-telegram-bot/>`_
Install dependencies:
-------------------
Learning by example
-------------------
.. code:: shell
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.
$ pip install -r requirements.txt -r requirements-dev.txt
- `echobot2 <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot2.py>`_ replies back messages.
Run tests:
- `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>`_.
.. code:: shell
- `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.
$ make test
- `timerbot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/timerbot.py>`_ uses the ``JobQueue`` to send timed messages.
To see other available options, run:
- `echobot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/legacy/echobot.py>`_ uses only the pure API to echo messages.
.. code:: shell
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.
$ make help
-------
Logging
-------
==================
_`Getting started`
==================
View the last release API documentation at: https://core.telegram.org/bots/api
This library uses the `logging` module. To set up logging to standard output, put:
This library uses the ``logging`` module. To set up logging to standard output, put:
.. code:: python
@@ -170,344 +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.
----------------------
_`Learning by example`
----------------------
We believe that the best way to learn and understand this simple package is by example. So here are some examples for you to review. Even if it's not your approach for learning, please take a look at ``echobot2`` (below), it is de facto the base for most of the bots out there. Best of all, the code for these examples are released to the public domain, so you can start by grabbing the code and building on top of it.
- `clibot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/clibot.py>`_ has a command line interface.
- `echobot2 <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot2.py>`_ replies back messages.
- `inlinebot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinebot.py>`_ basic example of an `inline bot <https://core.telegram.org/bots/inline>`_
- `state machine bot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/state_machine_bot.py>`_ keeps the state for individual users, useful for multipart conversations
- `timerbot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/timerbot.py>`_ uses the ``JobQueue`` to send timed messages.
Examples using only the API:
- `echobot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/legacy/echobot.py>`_ replies back messages.
- `roboed <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/legacy/roboed.py>`_ talks to `Robô Ed <http://www.ed.conpet.gov.br/br/converse.php>`_.
Look at the examples on the `wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Examples>`_ to see other bots the community has built.
------
_`API`
------
Note: Using the ``Bot`` class directly is the 'old' method, we have an easier way to make bots described in the next section. All of this is however still important information, even if you're using the ``telegram.ext`` submodule!
The API is exposed via the ``telegram.Bot`` class.
To generate an Access Token you have to talk to `BotFather <https://telegram.me/botfather>`_ and follow a few simple steps (described `here <https://core.telegram.org/bots#6-botfather>`_).
For full details see the `Bots: An introduction for developers <https://core.telegram.org/bots>`_.
To create an instance of the ``telegram.Bot``:
You can also use logs in your application by calling ``logging.getLogger()`` and setting the log level you want:
.. code:: python
>>> import telegram
>>> bot = telegram.Bot(token='token')
To see if your credentials are successful:
.. code:: python
>>> print(bot.getMe())
{"first_name": "Toledo's Palace Bot", "username": "ToledosPalaceBot"}
Bots can't initiate conversations with users. A user must either add them to a group or send them a message first. People can use ``telegram.me/<bot_username>`` links or username search to find your bot.
To fetch text messages sent to your Bot:
.. code:: python
>>> updates = bot.getUpdates()
>>> print([u.message.text for u in updates])
To fetch images sent to your Bot:
.. code:: python
>>> updates = bot.getUpdates()
>>> print([u.message.photo for u in updates if u.message.photo])
To reply messages you'll always need the ``chat_id``:
.. code:: python
>>> chat_id = bot.getUpdates()[-1].message.chat_id
To post a text message:
.. code:: python
>>> bot.sendMessage(chat_id=chat_id, text="I'm sorry Dave I'm afraid I can't do that.")
To post a text message with markdown:
.. code:: python
>>> bot.sendMessage(chat_id=chat_id, text="*bold* _italic_ [link](http://google.com).", parse_mode=telegram.ParseMode.MARKDOWN)
To post a text message with Html style:
.. code:: python
>>> bot.sendMessage(chat_id=chat_id, text='<b>bold</b> <i>italic</i> <a href="http://google.com">link</a>.', parse_mode=telegram.ParseMode.HTML)
To post an Emoji (special thanks to `Tim Whitlock <http://apps.timwhitlock.info/emoji/tables/unicode>`_):
.. code:: python
>>> bot.sendMessage(chat_id=chat_id, text=telegram.Emoji.PILE_OF_POO)
To post an image file via URL:
.. code:: python
>>> bot.sendPhoto(chat_id=chat_id, photo='https://telegram.org/img/t_logo.png')
To post an image file from disk:
.. code:: python
>>> bot.sendPhoto(chat_id=chat_id, photo=open('tests/test.png', 'rb'))
To post a voice file from disk:
.. code:: python
>>> bot.sendVoice(chat_id=chat_id, voice=open('tests/telegram.ogg', 'rb'))
To tell the user that something is happening on bot's side:
.. code:: python
>>> bot.sendChatAction(chat_id=chat_id, action=telegram.ChatAction.TYPING)
To create `Custom Keyboards <https://core.telegram.org/bots#keyboards>`_:
.. code:: python
>>> custom_keyboard = [[ telegram.KeyboardButton(telegram.Emoji.THUMBS_UP_SIGN),
... telegram.KeyboardButton(telegram.Emoji.THUMBS_DOWN_SIGN) ]]
>>> reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard)
>>> bot.sendMessage(chat_id=chat_id, text="Stay here, I'll be back.", reply_markup=reply_markup)
To hide `Custom Keyboards <https://core.telegram.org/bots#keyboards>`_:
.. code:: python
>>> reply_markup = telegram.ReplyKeyboardHide()
>>> bot.sendMessage(chat_id=chat_id, text="I'm back.", reply_markup=reply_markup)
To download a file (you will need its ``file_id``):
.. code:: python
>>> file_id = message.voice.file_id
>>> newFile = bot.getFile(file_id)
>>> newFile.download('voice.ogg')
There are many more API methods, to read the full API documentation:
.. code:: shell
$ pydoc telegram.Bot
-------------
_`Extensions`
-------------
The ``telegram.ext`` submodule is built on top of the bare-metal API. It provides an easy-to-use interface to the ``telegram.Bot`` by caring about getting new updates with the ``Updater`` class from telegram and forwarding them to the ``Dispatcher`` class. We can register handler functions in the ``Dispatcher`` to make our bot react to Telegram commands, messages and even arbitrary updates.
We'll need an Access Token. **Note:** If you have done this in the previous step, you can use that one. To generate an Access Token, we have to talk to `BotFather <https://telegram.me/botfather>`_ and follow a few simple steps (described `here <https://core.telegram.org/bots#botfather>`_).
First, we create an ``Updater`` object:
.. code:: python
>>> from telegram.ext import Updater
>>> updater = Updater(token='token')
For quicker access to the ``Dispatcher`` used by our ``Updater``, we can introduce it locally:
.. code:: python
>>> dispatcher = updater.dispatcher
Now, we need to define a function that should process a specific type of update:
.. code:: python
>>> def start(bot, update):
... bot.sendMessage(chat_id=update.message.chat_id, text="I'm a bot, please talk to me!")
We want this function to be called on a Telegram message that contains the ``/start`` command. To do that, we have to use a ``CommandHandler`` object and register it in the dispatcher:
.. code:: python
>>> from telegram.ext import CommandHandler
>>> start_handler = CommandHandler('start', start)
>>> dispatcher.addHandler(start_handler)
The last step is to tell the ``Updater`` to start working:
.. code:: python
>>> updater.start_polling()
Our bot is now up and running (go ahead and try it)! It's not doing anything yet, besides answering to the ``/start`` command. Let's add another handler that listens for regular messages. We're using the `MessageHandler` here to echo to all text messages:
.. code:: python
>>> def echo(bot, update):
... bot.sendMessage(chat_id=update.message.chat_id, text=update.message.text)
...
>>> from telegram.ext import MessageHandler, Filters
>>> echo_handler = MessageHandler([Filters.text], echo)
>>> dispatcher.addHandler(echo_handler)
Our bot should now reply to all text messages that are not a command with a message that has the same content.
Let's add some functionality to our bot. We want to add the ``/caps`` command, that will take some text as parameter and return it in all caps. We can get the arguments that were passed to a command in the handler function:
.. code:: python
>>> def caps(bot, update, args):
... text_caps = ' '.join(args).upper()
... bot.sendMessage(chat_id=update.message.chat_id, text=text_caps)
...
>>> caps_handler = CommandHandler('caps', caps, pass_args=True)
>>> dispatcher.addHandler(caps_handler)
To enable our bot to respond to inline queries, we can add the following (you will also have to talk to BotFather):
.. code:: python
>>> from telegram import InlineQueryResultArticle
>>> def inline_caps(bot, update):
... query = bot.update.inline_query.query
... results = list()
... results.append(InlineQueryResultArticle(query.upper(), 'Caps', query.upper()))
... bot.answerInlineQuery(update.inline_query.id, results)
...
>>> from telegram.ext import InlineQueryHandler
>>> inline_caps_handler = InlineQueryHandler(inline_caps)
>>> dispatcher.addHandler(inline_caps_handler)
People might try to send commands to the bot that it doesn't understand, so we can use a ``RegexHandler`` to recognize all commands that were not recognized by the previous handlers. **Note:** This handler has to be added last, else it will be triggered before the ``CommandHandlers`` had a chance to look at the update:
.. code:: python
>>> def unknown(bot, update):
... bot.sendMessage(chat_id=update.message.chat_id, text="Sorry, I didn't understand that command.")
...
>>> from telegram.ext import RegexHandler
>>> unknown_handler = RegexHandler(r'/.*', unknown)
>>> dispatcher.addHandler(unknown_handler)
If you're done playing around, stop the bot with this:
.. code:: python
>>> updater.stop()
Check out more examples in the `examples folder <https://github.com/python-telegram-bot/python-telegram-bot/tree/master/examples>`_!
-----------
_`JobQueue`
-----------
The ``JobQueue`` allows you to perform tasks with a delay or even periodically. The ``Updater`` will create one for you:
.. code:: python
>>> from telegram.ext import Updater
>>> u = Updater('TOKEN')
>>> j = u.job_queue
The job queue uses functions for tasks, so we define one and add it to the queue. Usually, when the first job is added to the queue, it wil start automatically. We can prevent this by setting ``prevent_autostart=True``:
.. code:: python
>>> def job1(bot):
... bot.sendMessage(chat_id='@examplechannel', text='One message every minute')
>>> j.put(job1, 60, next_t=0, prevent_autostart=True)
You can also have a job that will not be executed repeatedly:
.. code:: python
>>> def job2(bot):
... bot.sendMessage(chat_id='@examplechannel', text='A single message with 30s delay')
>>> j.put(job2, 30, repeat=False)
Now, because we didn't prevent the auto start this time, the queue will start ticking. It runs in a seperate thread, so it is non-blocking. When we stop the Updater, the related queue will be stopped as well:
.. code:: python
>>> u.stop()
We can also stop the job queue by itself:
.. code:: python
>>> j.stop()
----------
_`Logging`
----------
You can get logs in your main application by calling `logging` and setting the log level you want:
.. code:: python
>>> import logging
>>> logger = logging.getLogger()
>>> logger.setLevel(logging.INFO)
logger = logging.getLogger()
logger.setLevel(logging.INFO)
If you want DEBUG logs instead:
.. code:: python
>>> logger.setLevel(logging.DEBUG)
logger.setLevel(logging.DEBUG)
================
_`Documentation`
================
=============
Documentation
=============
``python-telegram-bot``'s documentation lives at `Read the Docs <https://python-telegram-bot.readthedocs.org/en/latest/>`_.
``python-telegram-bot``'s documentation lives at `pythonhosted.org <https://pythonhosted.org/python-telegram-bot/>`_.
===============
_`Getting help`
===============
============
Getting help
============
You can get help in several ways:
1. We have a vibrant community of developers helping each other in our `Telegram group <https://telegram.me/pythontelegrambotgroup>`_. Join us!
2. You can ask for help on Stack Overflow using the `python-telegram-bot tag <https://stackoverflow.com/questions/tagged/python-telegram-bot>`_.
3. As last resort, the developers are ready to help you with `serious issues <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_.
2. Our `Wiki pages <https://github.com/python-telegram-bot/python-telegram-bot/wiki/>`_ offer a growing amount of resources.
3. You can ask for help on Stack Overflow using the `python-telegram-bot tag <https://stackoverflow.com/questions/tagged/python-telegram-bot>`_.
4. As last resort, the developers are ready to help you with `serious issues <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_.
===============
_`Contributing`
===============
============
Contributing
============
Contributions of all sizes are welcome. Please review our `contribution guidelines <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/CONTRIBUTING.rst>`_ to get started. You can also help by `reporting bugs <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_.
Contributions of all sizes are welcome. Please review our `contribution guidelines <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/.github/CONTRIBUTING.rst>`_ to get started. You can also help by `reporting bugs <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_.
==========
_`License`
==========
=======
License
=======
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 <https://www.gnu.org/licenses/lgpl-3.0.html>`_, but applications that use the library don't have to be.
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.
+4 -3
View File
@@ -15,6 +15,7 @@
import sys
import os
import shlex
import telegram
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
@@ -58,9 +59,9 @@ author = u'Leandro Toledo'
# built documents.
#
# The short X.Y version.
version = '4.0'
version = telegram.__version__[:3]
# The full version, including alpha/beta/rc tags.
release = '4.0.3'
release = telegram.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -270,7 +271,7 @@ man_pages = [
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'PythonTelegramBot', u'Python Telegram Bot Documentation',
author, 'PythonTelegramBot', 'One line description of project.',
author, 'PythonTelegramBot', 'Not just a Python wrapper around the Telegram Bot API',
'Miscellaneous'),
]
+7
View File
@@ -0,0 +1,7 @@
telegram.contrib.botan module
=============================
.. automodule:: telegram.contrib.botan
:members:
:undoc-members:
:show-inheritance:
-171
View File
@@ -1,171 +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, StringCommandHandler, StringRegexHandler, \
MessageHandler, CommandHandler, RegexHandler, Filters
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) handler callback functions. These usually take the
# two arguments bot and update. Error handlers also receive the raised
# TelegramError object in error.
def start(bot, update):
""" Answer in Telegram """
bot.sendMessage(update.message.chat_id, text='Hi!')
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))
@run_async
def message(bot, update):
"""
Example for an asynchronous handler. It's not guaranteed that replies will
be in order when using @run_async. Also, you have to include **kwargs in
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 the
documentation of the Handler classes.
"""
update_queue.put('/%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.addHandler(CommandHandler("start", start))
dp.addHandler(CommandHandler("help", help))
# Message handlers only receive updates that don't contain commands
dp.addHandler(MessageHandler([Filters.text], message))
# Regex handlers will receive all updates on which their regex matches,
# but we have to add it in a separate group, since in one group,
# only one handler will be executed
dp.addHandler(RegexHandler('.*', any_message), group=1)
# String handlers work pretty much the same. Note that we have to tell
# the handler to pass the args or update_queue parameter
dp.addHandler(StringCommandHandler('reply', cli_reply, pass_args=True))
dp.addHandler(StringRegexHandler('[^/].*', cli_noncommand,
pass_update_queue=True))
# 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(timeout=10)
'''
# Alternatively, run with webhook:
update_queue = updater.start_webhook('0.0.0.0',
443,
url_path=token,
cert='cert.pem',
key='key.key',
webhook_url='https://example.com/%s'
% token)
# Or, if SSL is handled by a reverse proxy, the webhook URL is already set
# and the reverse proxy is configured to deliver directly to port 6000:
update_queue = updater.start_webhook('0.0.0.0', 6000)
'''
# 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()
@@ -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__':
+7 -8
View File
@@ -3,7 +3,6 @@
#
# Simple Bot to reply to Telegram messages
# This program is dedicated to the public domain under the CC0 license.
"""
This Bot uses the Updater class to handle the bot.
@@ -21,9 +20,8 @@ from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
import logging
# Enable logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
@@ -54,14 +52,14 @@ def main():
dp = updater.dispatcher
# on different commands - answer in Telegram
dp.addHandler(CommandHandler("start", start))
dp.addHandler(CommandHandler("help", help))
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", help))
# on noncommand i.e message - echo the message on Telegram
dp.addHandler(MessageHandler([Filters.text], echo))
dp.add_handler(MessageHandler([Filters.text], echo))
# log all errors
dp.addErrorHandler(error)
dp.add_error_handler(error)
# Start the Bot
updater.start_polling()
@@ -71,5 +69,6 @@ def main():
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
+21 -24
View File
@@ -3,7 +3,6 @@
#
# Simple Bot to reply to Telegram messages
# This program is dedicated to the public domain under the CC0 license.
"""
This Bot uses the Updater class to handle the bot.
@@ -26,9 +25,8 @@ from telegram.ext import Updater, InlineQueryHandler, CommandHandler
import logging
# Enable logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
@@ -53,24 +51,22 @@ def inlinequery(bot, update):
query = update.inline_query.query
results = list()
results.append(InlineQueryResultArticle(
id=uuid4(),
title="Caps",
input_message_content=InputTextMessageContent(query.upper())))
results.append(InlineQueryResultArticle(id=uuid4(),
title="Caps",
input_message_content=InputTextMessageContent(
query.upper())))
results.append(InlineQueryResultArticle(
id=uuid4(),
title="Bold",
input_message_content=InputTextMessageContent(
"*%s*" % escape_markdown(query),
parse_mode=ParseMode.MARKDOWN)))
results.append(InlineQueryResultArticle(id=uuid4(),
title="Bold",
input_message_content=InputTextMessageContent(
"*%s*" % escape_markdown(query),
parse_mode=ParseMode.MARKDOWN)))
results.append(InlineQueryResultArticle(
id=uuid4(),
title="Italic",
input_message_content=InputTextMessageContent(
"_%s_" % escape_markdown(query),
parse_mode=ParseMode.MARKDOWN)))
results.append(InlineQueryResultArticle(id=uuid4(),
title="Italic",
input_message_content=InputTextMessageContent(
"_%s_" % escape_markdown(query),
parse_mode=ParseMode.MARKDOWN)))
bot.answerInlineQuery(update.inline_query.id, results=results)
@@ -87,14 +83,14 @@ def main():
dp = updater.dispatcher
# on different commands - answer in Telegram
dp.addHandler(CommandHandler("start", start))
dp.addHandler(CommandHandler("help", help))
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", help))
# on noncommand i.e message - echo the message on Telegram
dp.addHandler(InlineQueryHandler(inlinequery))
dp.add_handler(InlineQueryHandler(inlinequery))
# log all errors
dp.addErrorHandler(error)
dp.add_error_handler(error)
# Start the Bot
updater.start_polling()
@@ -104,5 +100,6 @@ def main():
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
@@ -11,8 +11,7 @@ from telegram import Emoji, ForceReply, InlineKeyboardButton, \
from telegram.ext import Updater, CommandHandler, MessageHandler, \
CallbackQueryHandler, Filters
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - '
'%(message)s',
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.DEBUG)
# Define the different states a chat can be in
@@ -20,8 +19,7 @@ MENU, AWAIT_CONFIRMATION, AWAIT_INPUT = range(3)
# Python 2 and 3 unicode differences
try:
YES, NO = (Emoji.THUMBS_UP_SIGN.decode('utf-8'),
Emoji.THUMBS_DOWN_SIGN.decode('utf-8'))
YES, NO = (Emoji.THUMBS_UP_SIGN.decode('utf-8'), Emoji.THUMBS_DOWN_SIGN.decode('utf-8'))
except AttributeError:
YES, NO = (Emoji.THUMBS_UP_SIGN, Emoji.THUMBS_DOWN_SIGN)
@@ -58,11 +56,9 @@ def entered_value(bot, update):
# Save the user id and the answer to context
context[user_id] = update.message.text
reply_markup = InlineKeyboardMarkup(
[[InlineKeyboardButton(YES, callback_data=YES),
InlineKeyboardButton(NO, callback_data=NO)]])
bot.sendMessage(chat_id, text="Are you sure?",
reply_markup=reply_markup)
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton(YES, callback_data=YES),
InlineKeyboardButton(NO, callback_data=NO)]])
bot.sendMessage(chat_id, text="Are you sure?", reply_markup=reply_markup)
def confirm_value(bot, update):
@@ -82,14 +78,12 @@ def confirm_value(bot, update):
values[user_id] = user_context
bot.editMessageText(text="Changed value to %s." % values[user_id],
chat_id=chat_id,
message_id=
query.message.message_id)
message_id=query.message.message_id)
else:
bot.editMessageText(text="Alright, value is still %s."
% values.get(user_id, 'not set'),
bot.editMessageText(text="Alright, value is still %s." %
values.get(user_id, 'not set'),
chat_id=chat_id,
message_id=
query.message.message_id)
message_id=query.message.message_id)
def help(bot, update):
@@ -103,14 +97,14 @@ def error(bot, update, error):
updater = Updater("TOKEN")
# The command
updater.dispatcher.addHandler(CommandHandler('set', set_value))
updater.dispatcher.add_handler(CommandHandler('set', set_value))
# The answer
updater.dispatcher.addHandler(MessageHandler([Filters.text], entered_value))
updater.dispatcher.add_handler(MessageHandler([Filters.text], entered_value))
# The confirmation
updater.dispatcher.addHandler(CallbackQueryHandler(confirm_value))
updater.dispatcher.addHandler(CommandHandler('start', help))
updater.dispatcher.addHandler(CommandHandler('help', help))
updater.dispatcher.addErrorHandler(error)
updater.dispatcher.add_handler(CallbackQueryHandler(confirm_value))
updater.dispatcher.add_handler(CommandHandler('start', help))
updater.dispatcher.add_handler(CommandHandler('help', help))
updater.dispatcher.add_error_handler(error)
# Start the Bot
updater.start_polling()
-38
View File
@@ -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()
+11 -17
View File
@@ -8,8 +8,7 @@ import logging
from telegram import Emoji, ForceReply, ReplyKeyboardMarkup, KeyboardButton
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - '
'%(message)s',
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
# Define the different states a chat can be in
@@ -17,8 +16,7 @@ MENU, AWAIT_CONFIRMATION, AWAIT_INPUT = range(3)
# Python 2 and 3 unicode differences
try:
YES, NO = (Emoji.THUMBS_UP_SIGN.decode('utf-8'),
Emoji.THUMBS_DOWN_SIGN.decode('utf-8'))
YES, NO = (Emoji.THUMBS_UP_SIGN.decode('utf-8'), Emoji.THUMBS_DOWN_SIGN.decode('utf-8'))
except AttributeError:
YES, NO = (Emoji.THUMBS_UP_SIGN, Emoji.THUMBS_DOWN_SIGN)
@@ -46,7 +44,7 @@ def set_value(bot, update):
context[chat_id] = user_id # save the user id to context
bot.sendMessage(chat_id,
text="Please enter your settings value or send "
"/cancel to abort",
"/cancel to abort",
reply_markup=ForceReply())
# If we are waiting for input and the right user answered
@@ -58,8 +56,7 @@ def set_value(bot, update):
reply_markup = ReplyKeyboardMarkup(
[[KeyboardButton(YES), KeyboardButton(NO)]],
one_time_keyboard=True)
bot.sendMessage(chat_id, text="Are you sure?",
reply_markup=reply_markup)
bot.sendMessage(chat_id, text="Are you sure?", reply_markup=reply_markup)
# If we are waiting for confirmation and the right user answered
elif chat_state == AWAIT_CONFIRMATION and chat_context[0] == user_id:
@@ -67,12 +64,10 @@ def set_value(bot, update):
del context[chat_id]
if text == YES:
values[chat_id] = chat_context[1]
bot.sendMessage(chat_id,
text="Changed value to %s." % values[chat_id])
bot.sendMessage(chat_id, text="Changed value to %s." % values[chat_id])
else:
bot.sendMessage(chat_id,
text="Value not changed: %s."
% values.get(chat_id, '<not set>'))
text="Value not changed: %s." % values.get(chat_id, '<not set>'))
# Handler for the /cancel command.
@@ -86,17 +81,16 @@ def cancel(bot, update):
def help(bot, update):
bot.sendMessage(update.message.chat_id, text="Use /set to test this bot.")
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN")
# The command
updater.dispatcher.addHandler(CommandHandler('set', set_value))
updater.dispatcher.add_handler(CommandHandler('set', set_value))
# The answer and confirmation
updater.dispatcher.addHandler(MessageHandler([Filters.text], set_value))
updater.dispatcher.addHandler(CommandHandler('cancel', cancel))
updater.dispatcher.addHandler(CommandHandler('start', help))
updater.dispatcher.addHandler(CommandHandler('help', help))
updater.dispatcher.add_handler(MessageHandler([Filters.text], set_value))
updater.dispatcher.add_handler(CommandHandler('cancel', cancel))
updater.dispatcher.add_handler(CommandHandler('start', help))
updater.dispatcher.add_handler(CommandHandler('help', help))
# Start the Bot
updater.start_polling()
+10 -11
View File
@@ -3,7 +3,6 @@
#
# Simple Bot to send timed Telegram messages
# This program is dedicated to the public domain under the CC0 license.
"""
This Bot uses the Updater class to handle the bot and the JobQueue to send
timed messages.
@@ -22,9 +21,8 @@ from telegram.ext import Updater, CommandHandler
import logging
# Enable logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
job_queue = None
@@ -33,8 +31,7 @@ job_queue = None
# Define a few command handlers. These usually take the two arguments bot and
# update. Error handlers also receive the raised TelegramError object in error.
def start(bot, update):
bot.sendMessage(update.message.chat_id, text='Hi! Use /set <seconds> to '
'set a timer')
bot.sendMessage(update.message.chat_id, text='Hi! Use /set <seconds> to ' 'set a timer')
def set(bot, update, args):
@@ -44,7 +41,8 @@ def set(bot, update, args):
# args[0] should contain the time for the timer in seconds
due = int(args[0])
if due < 0:
bot.sendMessage(chat_id, text='Sorry we can not go back to future!')
bot.sendMessage(chat_id, text='Sorry we can not go back to future!')
def alarm(bot):
""" Inner function to send the alarm message """
bot.sendMessage(chat_id, text='Beep!')
@@ -73,12 +71,12 @@ def main():
dp = updater.dispatcher
# on different commands - answer in Telegram
dp.addHandler(CommandHandler("start", start))
dp.addHandler(CommandHandler("help", start))
dp.addHandler(CommandHandler("set", set, pass_args=True))
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", start))
dp.add_handler(CommandHandler("set", set, pass_args=True))
# log all errors
dp.addErrorHandler(error)
dp.add_error_handler(error)
# Start the Bot
updater.start_polling()
@@ -88,5 +86,6 @@ def main():
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
+4 -1
View File
@@ -3,4 +3,7 @@ nose
pep257
pylint
unittest2
flaky
flaky
yapf
pre-commit
pre-commit-hooks
+3 -1
View File
@@ -1 +1,3 @@
future
future>=0.15.2
urllib3>=1.8.3
certifi
+9
View File
@@ -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
+33 -42
View File
@@ -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='4.0.3',
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',
],)
+26 -63
View File
@@ -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
@@ -79,66 +81,27 @@ from .inputcontactmessagecontent import InputContactMessageContent
from .update import Update
from .bot import Bot
__author__ = 'devs@python-telegram-bot.org'
__version__ = '4.0.3'
__all__ = ['Audio',
'Bot',
'Chat',
'ChatAction',
'ChosenInlineResult',
'CallbackQuery',
'Contact',
'Document',
'Emoji',
'File',
'ForceReply',
'InlineKeyboardButton',
'InlineKeyboardMarkup',
'InlineQuery',
'InlineQueryResult',
'InlineQueryResult',
'InlineQueryResultArticle',
'InlineQueryResultAudio',
'InlineQueryResultCachedAudio',
'InlineQueryResultCachedDocument',
'InlineQueryResultCachedGif',
'InlineQueryResultCachedMpeg4Gif',
'InlineQueryResultCachedPhoto',
'InlineQueryResultCachedSticker',
'InlineQueryResultCachedVideo',
'InlineQueryResultCachedVoice',
'InlineQueryResultContact',
'InlineQueryResultDocument',
'InlineQueryResultGif',
'InlineQueryResultLocation',
'InlineQueryResultMpeg4Gif',
'InlineQueryResultPhoto',
'InlineQueryResultVenue',
'InlineQueryResultVideo',
'InlineQueryResultVoice',
'InputContactMessageContent',
'InputFile',
'InputLocationMessageContent',
'InputMessageContent',
'InputTextMessageContent',
'InputVenueMessageContent',
'KeyboardButton',
'Location',
'Message',
'MessageEntity',
'NullHandler',
'ParseMode',
'PhotoSize',
'ReplyKeyboardHide',
'ReplyKeyboardMarkup',
'ReplyMarkup',
'Sticker',
'TelegramError',
'TelegramObject',
'Update',
'User',
'UserProfilePhotos',
'Venue',
'Video',
'Voice']
__version__ = '4.3'
__all__ = ['Audio', 'Bot', 'Chat', 'ChatMember', 'ChatAction', 'ChosenInlineResult',
'CallbackQuery', 'Contact', 'Document', 'Emoji', 'File', 'ForceReply',
'InlineKeyboardButton', 'InlineKeyboardMarkup', 'InlineQuery', 'InlineQueryResult',
'InlineQueryResult', 'InlineQueryResultArticle', 'InlineQueryResultAudio',
'InlineQueryResultCachedAudio', 'InlineQueryResultCachedDocument',
'InlineQueryResultCachedGif', 'InlineQueryResultCachedMpeg4Gif',
'InlineQueryResultCachedPhoto', 'InlineQueryResultCachedSticker',
'InlineQueryResultCachedVideo', 'InlineQueryResultCachedVoice',
'InlineQueryResultContact', 'InlineQueryResultDocument', 'InlineQueryResultGif',
'InlineQueryResultLocation', 'InlineQueryResultMpeg4Gif', 'InlineQueryResultPhoto',
'InlineQueryResultVenue', 'InlineQueryResultVideo', 'InlineQueryResultVoice',
'InputContactMessageContent', 'InputFile', 'InputLocationMessageContent',
'InputMessageContent', 'InputTextMessageContent', 'InputVenueMessageContent',
'KeyboardButton', 'Location', 'Message', 'MessageEntity', 'NullHandler', 'ParseMode',
'PhotoSize', 'ReplyKeyboardHide', 'ReplyKeyboardMarkup', 'ReplyMarkup', 'Sticker',
'TelegramError', 'TelegramObject', 'Update', 'User', 'UserProfilePhotos', 'Venue',
'Video', 'Voice']
if version_info < (2, 7):
from warnings import warn
warn("python-telegram-bot will stop supporting Python 2.6 in a future release. "
"Please upgrade your Python version to at least Python 2.7!")
+1 -5
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Audio."""
from telegram import TelegramObject
@@ -45,10 +44,7 @@ class Audio(TelegramObject):
file_size (Optional[int]):
"""
def __init__(self,
file_id,
duration,
**kwargs):
def __init__(self, file_id, duration, **kwargs):
# Required
self.file_id = str(file_id)
self.duration = int(duration)
-1
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""Base class for Telegram Objects."""
import json
+291 -203
View File
@@ -17,16 +17,15 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Bot."""
import logging
import functools
import logging
from telegram import User, Message, Update, UserProfilePhotos, File, \
ReplyMarkup, TelegramObject, NullHandler
from telegram import (User, Message, Update, Chat, ChatMember, UserProfilePhotos, File,
ReplyMarkup, TelegramObject, NullHandler)
from telegram.error import InvalidToken
from telegram.utils import request
from telegram.utils.validate import validate_token
logging.getLogger(__name__).addHandler(NullHandler())
@@ -48,21 +47,16 @@ class Bot(TelegramObject):
"""
def __init__(self,
token,
base_url=None,
base_file_url=None):
self.token = validate_token(token)
def __init__(self, token, base_url=None, base_file_url=None):
self.token = self._validate_token(token)
if not base_url:
self.base_url = 'https://api.telegram.org/bot{0}'.format(
self.token)
self.base_url = 'https://api.telegram.org/bot{0}'.format(self.token)
else:
self.base_url = base_url + self.token
if not base_file_url:
self.base_file_url = 'https://api.telegram.org/file/bot{0}'.format(
self.token)
self.base_file_url = 'https://api.telegram.org/file/bot{0}'.format(self.token)
else:
self.base_file_url = base_file_url + self.token
@@ -70,7 +64,20 @@ class Bot(TelegramObject):
self.logger = logging.getLogger(__name__)
@staticmethod
def _validate_token(token):
"""a very basic validation on token"""
if any(x.isspace() for x in token):
raise InvalidToken()
left, sep, _right = token.partition(':')
if (not sep) or (not left.isdigit()) or (len(left) < 3):
raise InvalidToken()
return token
def info(func):
@functools.wraps(func)
def decorator(self, *args, **kwargs):
if not self.bot:
@@ -119,17 +126,16 @@ class Bot(TelegramObject):
return decorator
def message(func):
@functools.wraps(func)
def decorator(self, *args, **kwargs):
url, data = func(self, *args, **kwargs)
if kwargs.get('reply_to_message_id'):
data['reply_to_message_id'] = \
kwargs.get('reply_to_message_id')
data['reply_to_message_id'] = kwargs.get('reply_to_message_id')
if kwargs.get('disable_notification'):
data['disable_notification'] = \
kwargs.get('disable_notification')
data['disable_notification'] = kwargs.get('disable_notification')
if kwargs.get('reply_markup'):
reply_markup = kwargs.get('reply_markup')
@@ -138,8 +144,7 @@ class Bot(TelegramObject):
else:
data['reply_markup'] = reply_markup
result = request.post(url, data,
timeout=kwargs.get('timeout'))
result = request.post(url, data, timeout=kwargs.get('timeout'))
if result is True:
return result
@@ -149,7 +154,7 @@ class Bot(TelegramObject):
return decorator
@log
def getMe(self):
def getMe(self, **kwargs):
"""A simple method for testing your bot's auth token.
Returns:
@@ -172,12 +177,7 @@ class Bot(TelegramObject):
@log
@message
def sendMessage(self,
chat_id,
text,
parse_mode=None,
disable_web_page_preview=None,
**kwargs):
def sendMessage(self, chat_id, text, parse_mode=None, disable_web_page_preview=None, **kwargs):
"""Use this method to send text messages.
Args:
@@ -217,8 +217,7 @@ class Bot(TelegramObject):
url = '{0}/sendMessage'.format(self.base_url)
data = {'chat_id': chat_id,
'text': text}
data = {'chat_id': chat_id, 'text': text}
if parse_mode:
data['parse_mode'] = parse_mode
@@ -229,11 +228,7 @@ class Bot(TelegramObject):
@log
@message
def forwardMessage(self,
chat_id,
from_chat_id,
message_id,
**kwargs):
def forwardMessage(self, chat_id, from_chat_id, message_id, **kwargs):
"""Use this method to forward messages of any kind.
Args:
@@ -276,11 +271,7 @@ class Bot(TelegramObject):
@log
@message
def sendPhoto(self,
chat_id,
photo,
caption=None,
**kwargs):
def sendPhoto(self, chat_id, photo, caption=None, **kwargs):
"""Use this method to send photos.
Args:
@@ -318,8 +309,7 @@ class Bot(TelegramObject):
url = '{0}/sendPhoto'.format(self.base_url)
data = {'chat_id': chat_id,
'photo': photo}
data = {'chat_id': chat_id, 'photo': photo}
if caption:
data['caption'] = caption
@@ -328,13 +318,7 @@ class Bot(TelegramObject):
@log
@message
def sendAudio(self,
chat_id,
audio,
duration=None,
performer=None,
title=None,
**kwargs):
def sendAudio(self, chat_id, audio, duration=None, performer=None, title=None, **kwargs):
"""Use this method to send audio files, if you want Telegram clients to
display them in the music player. Your audio must be in an .mp3 format.
On success, the sent Message is returned. Bots can currently send audio
@@ -384,8 +368,7 @@ class Bot(TelegramObject):
url = '{0}/sendAudio'.format(self.base_url)
data = {'chat_id': chat_id,
'audio': audio}
data = {'chat_id': chat_id, 'audio': audio}
if duration:
data['duration'] = duration
@@ -398,12 +381,7 @@ class Bot(TelegramObject):
@log
@message
def sendDocument(self,
chat_id,
document,
filename=None,
caption=None,
**kwargs):
def sendDocument(self, chat_id, document, filename=None, caption=None, **kwargs):
"""Use this method to send general files.
Args:
@@ -444,8 +422,7 @@ class Bot(TelegramObject):
url = '{0}/sendDocument'.format(self.base_url)
data = {'chat_id': chat_id,
'document': document}
data = {'chat_id': chat_id, 'document': document}
if filename:
data['filename'] = filename
@@ -456,10 +433,7 @@ class Bot(TelegramObject):
@log
@message
def sendSticker(self,
chat_id,
sticker,
**kwargs):
def sendSticker(self, chat_id, sticker, **kwargs):
"""Use this method to send .webp stickers.
Args:
@@ -494,19 +468,13 @@ class Bot(TelegramObject):
url = '{0}/sendSticker'.format(self.base_url)
data = {'chat_id': chat_id,
'sticker': sticker}
data = {'chat_id': chat_id, 'sticker': sticker}
return url, data
@log
@message
def sendVideo(self,
chat_id,
video,
duration=None,
caption=None,
**kwargs):
def sendVideo(self, chat_id, video, duration=None, caption=None, **kwargs):
"""Use this method to send video files, Telegram clients support mp4
videos (other formats may be sent as telegram.Document).
@@ -547,8 +515,7 @@ class Bot(TelegramObject):
url = '{0}/sendVideo'.format(self.base_url)
data = {'chat_id': chat_id,
'video': video}
data = {'chat_id': chat_id, 'video': video}
if duration:
data['duration'] = duration
@@ -559,11 +526,7 @@ class Bot(TelegramObject):
@log
@message
def sendVoice(self,
chat_id,
voice,
duration=None,
**kwargs):
def sendVoice(self, chat_id, voice, duration=None, **kwargs):
"""Use this method to send audio files, if you want Telegram clients to
display the file as a playable voice message. For this to work, your
audio must be in an .ogg file encoded with OPUS (other formats may be
@@ -605,8 +568,7 @@ class Bot(TelegramObject):
url = '{0}/sendVoice'.format(self.base_url)
data = {'chat_id': chat_id,
'voice': voice}
data = {'chat_id': chat_id, 'voice': voice}
if duration:
data['duration'] = duration
@@ -615,11 +577,7 @@ class Bot(TelegramObject):
@log
@message
def sendLocation(self,
chat_id,
latitude,
longitude,
**kwargs):
def sendLocation(self, chat_id, latitude, longitude, **kwargs):
"""Use this method to send point on the map.
Args:
@@ -654,22 +612,19 @@ class Bot(TelegramObject):
url = '{0}/sendLocation'.format(self.base_url)
data = {'chat_id': chat_id,
'latitude': latitude,
'longitude': longitude}
data = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude}
return url, data
@log
@message
def sendVenue(self,
chat_id,
latitude,
longitude,
title,
address,
foursquare_id=None,
**kwargs):
def sendVenue(
self, chat_id,
latitude,
longitude,
title, address,
foursquare_id=None,
**kwargs):
"""
Use this method to send information about a venue.
@@ -725,12 +680,7 @@ class Bot(TelegramObject):
@log
@message
def sendContact(self,
chat_id,
phone_number,
first_name,
last_name=None,
**kwargs):
def sendContact(self, chat_id, phone_number, first_name, last_name=None, **kwargs):
"""
Use this method to send phone contacts.
@@ -769,9 +719,7 @@ class Bot(TelegramObject):
url = '{0}/sendContact'.format(self.base_url)
data = {'chat_id': chat_id,
'phone_number': phone_number,
'first_name': first_name}
data = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name}
if last_name:
data['last_name'] = last_name
@@ -780,10 +728,7 @@ class Bot(TelegramObject):
@log
@message
def sendChatAction(self,
chat_id,
action,
**kwargs):
def sendChatAction(self, chat_id, action, **kwargs):
"""Use this method when you need to tell the user that something is
happening on the bot's side. The status is set for 5 seconds or less
(when a message arrives from your bot, Telegram clients clear its
@@ -805,8 +750,7 @@ class Bot(TelegramObject):
url = '{0}/sendChatAction'.format(self.base_url)
data = {'chat_id': chat_id,
'action': action}
data = {'chat_id': chat_id, 'action': action}
return url, data
@@ -861,8 +805,7 @@ class Bot(TelegramObject):
results = [res.to_dict() for res in results]
data = {'inline_query_id': inline_query_id,
'results': results}
data = {'inline_query_id': inline_query_id, 'results': results}
if cache_time or cache_time == 0:
data['cache_time'] = cache_time
@@ -875,17 +818,12 @@ class Bot(TelegramObject):
if switch_pm_parameter:
data['switch_pm_parameter'] = switch_pm_parameter
result = request.post(url, data,
timeout=kwargs.get('timeout'))
result = request.post(url, data, timeout=kwargs.get('timeout'))
return result
@log
def getUserProfilePhotos(self,
user_id,
offset=None,
limit=100,
**kwargs):
def getUserProfilePhotos(self, user_id, offset=None, limit=100, **kwargs):
"""Use this method to get a list of profile pictures for a user.
Args:
@@ -920,15 +858,12 @@ class Bot(TelegramObject):
if limit:
data['limit'] = limit
result = request.post(url, data,
timeout=kwargs.get('timeout'))
result = request.post(url, data, timeout=kwargs.get('timeout'))
return UserProfilePhotos.de_json(result)
@log
def getFile(self,
file_id,
**kwargs):
def getFile(self, file_id, **kwargs):
"""Use this method to get basic info about a file and prepare it for
downloading. For the moment, bots can download files of up to 20MB in
size.
@@ -954,20 +889,15 @@ class Bot(TelegramObject):
data = {'file_id': file_id}
result = request.post(url, data,
timeout=kwargs.get('timeout'))
result = request.post(url, data, timeout=kwargs.get('timeout'))
if result.get('file_path'):
result['file_path'] = '%s/%s' % (self.base_file_url,
result['file_path'])
result['file_path'] = '%s/%s' % (self.base_file_url, result['file_path'])
return File.de_json(result)
@log
def kickChatMember(self,
chat_id,
user_id,
**kwargs):
def kickChatMember(self, chat_id, user_id, **kwargs):
"""Use this method to kick a user from a group or a supergroup. In the
case of supergroups, the user will not be able to return to the group
on their own using invite links, etc., unless unbanned first. The bot
@@ -994,19 +924,14 @@ class Bot(TelegramObject):
url = '{0}/kickChatMember'.format(self.base_url)
data = {'chat_id': chat_id,
'user_id': user_id}
data = {'chat_id': chat_id, 'user_id': user_id}
result = request.post(url, data,
timeout=kwargs.get('timeout'))
result = request.post(url, data, timeout=kwargs.get('timeout'))
return result
@log
def unbanChatMember(self,
chat_id,
user_id,
**kwargs):
def unbanChatMember(self, chat_id, user_id, **kwargs):
"""Use this method to unban a previously kicked user in a supergroup.
The user will not return to the group automatically, but will be able
to join via link, etc. The bot must be an administrator in the group
@@ -1033,20 +958,14 @@ class Bot(TelegramObject):
url = '{0}/unbanChatMember'.format(self.base_url)
data = {'chat_id': chat_id,
'user_id': user_id}
data = {'chat_id': chat_id, 'user_id': user_id}
result = request.post(url, data,
timeout=kwargs.get('timeout'))
result = request.post(url, data, timeout=kwargs.get('timeout'))
return result
@log
def answerCallbackQuery(self,
callback_query_id,
text=None,
show_alert=False,
**kwargs):
def answerCallbackQuery(self, callback_query_id, text=None, show_alert=False, **kwargs):
"""Use this method to send answers to callback queries sent from
inline keyboards. The answer will be displayed to the user as a
notification at the top of the chat screen or as an alert.
@@ -1085,12 +1004,12 @@ class Bot(TelegramObject):
if show_alert:
data['show_alert'] = show_alert
result = request.post(url, data,
timeout=kwargs.get('timeout'))
result = request.post(url, data, timeout=kwargs.get('timeout'))
return result
@log
@message
def editMessageText(self,
text,
chat_id=None,
@@ -1098,7 +1017,6 @@ class Bot(TelegramObject):
inline_message_id=None,
parse_mode=None,
disable_web_page_preview=None,
reply_markup=None,
**kwargs):
"""Use this method to edit text messages sent by the bot or via the bot
(for inline bots).
@@ -1125,6 +1043,10 @@ class Bot(TelegramObject):
A JSON-serialized object for an inline keyboard.
Keyword Args:
reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional
interface options. A JSON-serialized object for an inline
keyboard, custom reply keyboard, instructions to hide reply
keyboard or to force a reply from the user.
timeout (Optional[float]): If this value is specified, use it as
the definitive timeout (in seconds) for urlopen() operations.
@@ -1152,16 +1074,8 @@ class Bot(TelegramObject):
data['parse_mode'] = parse_mode
if disable_web_page_preview:
data['disable_web_page_preview'] = disable_web_page_preview
if reply_markup:
if isinstance(reply_markup, ReplyMarkup):
data['reply_markup'] = reply_markup.to_json()
else:
data['reply_markup'] = reply_markup
result = request.post(url, data,
timeout=kwargs.get('timeout'))
return Message.de_json(result)
return url, data
@log
@message
@@ -1218,11 +1132,10 @@ class Bot(TelegramObject):
@log
@message
def editMessageReplyMarkup(self,
chat_id=None,
message_id=None,
inline_message_id=None,
**kwargs):
def editMessageReplyMarkup(
self, chat_id=None,
message_id=None, inline_message_id=None,
**kwargs):
"""Use this method to edit only the reply markup of messages sent by
the bot or via the bot (for inline bots).
@@ -1266,35 +1179,27 @@ class Bot(TelegramObject):
return url, data
@log
def getUpdates(self,
offset=None,
limit=100,
timeout=0,
network_delay=.2):
def getUpdates(self, offset=None, limit=100, timeout=0, network_delay=5., **kwargs):
"""Use this method to receive incoming updates using long polling.
Args:
offset:
Identifier of the first update to be returned. Must be greater by
one than the highest among the identifiers of previously received
updates. By default, updates starting with the earliest unconfirmed
update are returned. An update is considered confirmed as soon as
getUpdates is called with an offset higher than its update_id.
limit:
Limits the number of updates to be retrieved. Values between 1-100
are accepted. Defaults to 100.
timeout:
Timeout in seconds for long polling. Defaults to 0, i.e. usual
short polling.
network_delay:
Additional timeout in seconds to allow the response from Telegram
to take some time when using long polling. Defaults to 2, which
should be enough for most connections. Increase it if it takes very
long for data to be transmitted from and to the Telegram servers.
offset (Optional[int]):
Identifier of the first update to be returned. Must be greater by one than the highest
among the identifiers of previously received updates. By default, updates starting with
the earliest unconfirmed update are returned. An update is considered confirmed as soon
as getUpdates is called with an offset higher than its update_id.
limit (Optional[int]):
Limits the number of updates to be retrieved. Values between 1-100 are accepted.
Defaults to 100.
timeout (Optional[int]):
Timeout in seconds for long polling. Defaults to 0, i.e. usual short polling.
network_delay (Optional[float]):
Additional timeout in seconds to allow the response from Telegram servers. This should
cover network latency around the globe, SSL handshake and slowness of the Telegram
servers (which unfortunately happens a lot recently - 2016-05-28). Defaults to 5.
Returns:
list[:class:`telegram.Update`]: A list of :class:`telegram.Update`
objects are returned.
list[:class:`telegram.Update`]
Raises:
:class:`telegram.TelegramError`
@@ -1315,18 +1220,14 @@ class Bot(TelegramObject):
result = request.post(url, data, timeout=urlopen_timeout)
if result:
self.logger.debug(
'Getting updates: %s', [u['update_id'] for u in result])
self.logger.debug('Getting updates: %s', [u['update_id'] for u in result])
else:
self.logger.debug('No new updates found.')
return [Update.de_json(x) for x in result]
@log
def setWebhook(self,
webhook_url=None,
certificate=None,
**kwargs):
def setWebhook(self, webhook_url=None, certificate=None, **kwargs):
"""Use this method to specify a url and receive incoming updates via an
outgoing webhook. Whenever there is an update for the bot, we will send
an HTTPS POST request to the specified url, containing a
@@ -1359,11 +1260,170 @@ class Bot(TelegramObject):
if certificate:
data['certificate'] = certificate
result = request.post(url, data,
timeout=kwargs.get('timeout'))
result = request.post(url, data, timeout=kwargs.get('timeout'))
return result
@log
def leaveChat(self, chat_id, **kwargs):
"""Use this method for your bot to leave a group, supergroup or
channel.
Args:
chat_id:
Unique identifier for the target chat or username of the target
channel (in the format @channelusername).
Keyword Args:
timeout (Optional[float]): If this value is specified, use it as
the definitive timeout (in seconds) for urlopen() operations.
Returns:
bool: On success, `True` is returned.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/leaveChat'.format(self.base_url)
data = {'chat_id': chat_id}
result = request.post(url, data, timeout=kwargs.get('timeout'))
return result
@log
def getChat(self, chat_id, **kwargs):
"""Use this method to get up to date information about the chat
(current name of the user for one-on-one conversations, current
username of a user, group or channel, etc.).
Args:
chat_id:
Unique identifier for the target chat or username of the target
channel (in the format @channelusername).
Keyword Args:
timeout (Optional[float]): If this value is specified, use it as
the definitive timeout (in seconds) for urlopen() operations.
Returns:
:class:`telegram.Chat`: On success, :class:`telegram.Chat` is
returned.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/getChat'.format(self.base_url)
data = {'chat_id': chat_id}
result = request.post(url, data, timeout=kwargs.get('timeout'))
return Chat.de_json(result)
@log
def getChatAdministrators(self, chat_id, **kwargs):
"""Use this method to get a list of administrators in a chat. On
success, returns an Array of ChatMember objects that contains
information about all chat administrators except other bots. If the
chat is a group or a supergroup and no administrators were appointed,
only the creator will be returned.
Args:
chat_id:
Unique identifier for the target chat or username of the target
channel (in the format @channelusername).
Keyword Args:
timeout (Optional[float]): If this value is specified, use it as
the definitive timeout (in seconds) for urlopen() operations.
Returns:
list[:class:`telegram.ChatMember`]: On success, a list of
:class:`telegram.ChatMember` objects are returned.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/getChatAdministrators'.format(self.base_url)
data = {'chat_id': chat_id}
result = request.post(url, data, timeout=kwargs.get('timeout'))
return [ChatMember.de_json(x) for x in result]
@log
def getChatMembersCount(self, chat_id, **kwargs):
"""Use this method to get the number of members in a chat.
Args:
chat_id:
Unique identifier for the target chat or username of the target
channel (in the format @channelusername).
Keyword Args:
timeout (Optional[float]): If this value is specified, use it as
the definitive timeout (in seconds) for urlopen() operations.
Returns:
int: On success, an `int` is returned.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/getChatMembersCount'.format(self.base_url)
data = {'chat_id': chat_id}
result = request.post(url, data, timeout=kwargs.get('timeout'))
return result
@log
def getChatMember(self, chat_id, user_id, **kwargs):
"""Use this method to get information about a member of a chat.
Args:
chat_id:
Unique identifier for the target chat or username of the target
channel (in the format @channelusername).
user_id:
Unique identifier of the target user.
Keyword Args:
timeout (Optional[float]): If this value is specified, use it as
the definitive timeout (in seconds) for urlopen() operations.
Returns:
:class:`telegram.ChatMember`: On success,
:class:`telegram.ChatMember` is returned.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/getChatMember'.format(self.base_url)
data = {'chat_id': chat_id, 'user_id': user_id}
result = request.post(url, data, timeout=kwargs.get('timeout'))
return ChatMember.de_json(result)
@staticmethod
def de_json(data):
data = super(Bot, Bot).de_json(data)
@@ -1371,9 +1431,7 @@ class Bot(TelegramObject):
return Bot(**data)
def to_dict(self):
data = {'id': self.id,
'username': self.username,
'first_name': self.username}
data = {'id': self.id, 'username': self.username, 'first_name': self.username}
if self.last_name:
data['last_name'] = self.last_name
@@ -1381,6 +1439,36 @@ class Bot(TelegramObject):
return data
def __reduce__(self):
return (self.__class__, (self.token,
self.base_url.replace(self.token, ''),
return (self.__class__, (self.token, self.base_url.replace(self.token, ''),
self.base_file_url.replace(self.token, '')))
# snake_case (PEP8) aliases
get_me = getMe
send_message = sendMessage
forward_message = forwardMessage
send_photo = sendPhoto
send_audio = sendAudio
send_document = sendDocument
send_sticker = sendSticker
send_video = sendVideo
send_voice = sendVoice
send_location = sendLocation
send_venue = sendVenue
send_contact = sendContact
send_chat_action = sendChatAction
answer_inline_query = answerInlineQuery
get_user_profile_photos = getUserProfilePhotos
get_file = getFile
kick_chat_member = kickChatMember
unban_chat_member = unbanChatMember
answer_callback_query = answerCallbackQuery
edit_message_text = editMessageText
edit_message_caption = editMessageCaption
edit_message_reply_markup = editMessageReplyMarkup
get_updates = getUpdates
set_webhook = setWebhook
leave_chat = leaveChat
get_chat = getChat
get_chat_administrators = getChatAdministrators
get_chat_member = getChatMember
get_chat_members_count = getChatMembersCount
+12 -6
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
CallbackQuery"""
@@ -26,11 +25,7 @@ from telegram import TelegramObject, Message, User
class CallbackQuery(TelegramObject):
"""This object represents a Telegram CallbackQuery."""
def __init__(self,
id,
from_user,
data,
**kwargs):
def __init__(self, id, from_user, data, **kwargs):
# Required
self.id = id
self.from_user = from_user
@@ -48,3 +43,14 @@ class CallbackQuery(TelegramObject):
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
View File
@@ -17,7 +17,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Chat."""
from telegram import TelegramObject
@@ -43,10 +42,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
-1
View File
@@ -17,7 +17,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram ChatAction."""
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram ChatMember."""
from telegram import User, TelegramObject
class ChatMember(TelegramObject):
"""This object represents a Telegram ChatMember.
Attributes:
user (:class:`telegram.User`): Information about the user.
status (str): The member's status in the chat. Can be 'creator', 'administrator', 'member',
'left' or 'kicked'.
Args:
user (:class:`telegram.User`):
status (str):
"""
CREATOR = 'creator'
ADMINISTRATOR = 'administrator'
MEMBER = 'member'
LEFT = 'left'
KICKED = 'kicked'
def __init__(self, user, status, **kwargs):
# Required
self.user = user
self.status = status
@staticmethod
def de_json(data):
"""
Args:
data (dict):
Returns:
telegram.ChatMember:
"""
if not data:
return None
data['user'] = User.de_json(data.get('user'))
return ChatMember(**data)
+2 -2
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""
This module contains a object that represents a Telegram ChosenInlineResult
"""
@@ -47,7 +46,8 @@ class ChosenInlineResult(TelegramObject):
from_user,
query,
location=None,
inline_message_id=None):
inline_message_id=None,
**kwargs):
# Required
self.result_id = result_id
self.from_user = from_user
+1 -5
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Contact."""
from telegram import TelegramObject
@@ -41,10 +40,7 @@ class Contact(TelegramObject):
user_id (Optional[int]):
"""
def __init__(self,
phone_number,
first_name,
**kwargs):
def __init__(self, phone_number, first_name, **kwargs):
# Required
self.phone_number = str(phone_number)
self.first_name = first_name
View File
+46
View File
@@ -0,0 +1,46 @@
import logging
from telegram import NullHandler
from future.moves.urllib.parse import quote
from future.moves.urllib.error import HTTPError, URLError
from future.moves.urllib.request import urlopen, Request
logging.getLogger(__name__).addHandler(NullHandler())
class Botan(object):
"""This class helps to send incoming events to your botan analytics account.
See more: https://github.com/botanio/sdk#botan-sdk
"""
token = ''
url_template = 'https://api.botan.io/track?token={token}' \
'&uid={uid}&name={name}&src=python-telegram-bot'
def __init__(self, token):
self.token = token
self.logger = logging.getLogger(__name__)
def track(self, message, event_name='event'):
try:
uid = message.chat_id
except AttributeError:
self.logger.warn('No chat_id in message')
return False
data = message.to_json()
try:
url = self.url_template.format(token=str(self.token),
uid=str(uid),
name=quote(event_name))
request = Request(url,
data=data.encode(),
headers={'Content-Type': 'application/json'})
urlopen(request)
return True
except HTTPError as error:
self.logger.warn('Botan track error ' + str(error.code) + ':' + error.read().decode(
'utf-8'))
return False
except URLError as error:
self.logger.warn('Botan track error ' + str(error.reason))
return False
+1 -4
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Document."""
from telegram import PhotoSize, TelegramObject
@@ -43,9 +42,7 @@ class Document(TelegramObject):
file_size (Optional[int]):
"""
def __init__(self,
file_id,
**kwargs):
def __init__(self, file_id, **kwargs):
# Required
self.file_id = str(file_id)
# Optionals
+23 -23
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python
# flake8: noqa
# pylint: disable=C0103,C0301,R0903
# pylint: disable=C0103,R0903
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
@@ -18,7 +18,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents an Emoji."""
from future.utils import bytes_to_native_str as n
@@ -163,26 +162,26 @@ class Emoji(object):
SQUARED_SOS = n(b'\xF0\x9F\x86\x98')
SQUARED_UP_WITH_EXCLAMATION_MARK = n(b'\xF0\x9F\x86\x99')
SQUARED_VS = n(b'\xF0\x9F\x86\x9A')
REGIONAL_INDICATOR_SYMBOL_LETTER_D_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_E \
= n(b'\xF0\x9F\x87\xA9\xF0\x9F\x87\xAA')
REGIONAL_INDICATOR_SYMBOL_LETTER_G_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_B \
= n(b'\xF0\x9F\x87\xAC\xF0\x9F\x87\xA7')
REGIONAL_INDICATOR_SYMBOL_LETTER_C_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_N \
= n(b'\xF0\x9F\x87\xA8\xF0\x9F\x87\xB3')
REGIONAL_INDICATOR_SYMBOL_LETTER_J_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_P \
= n(b'\xF0\x9F\x87\xAF\xF0\x9F\x87\xB5')
REGIONAL_INDICATOR_SYMBOL_LETTER_K_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_R \
= n(b'\xF0\x9F\x87\xB0\xF0\x9F\x87\xB7')
REGIONAL_INDICATOR_SYMBOL_LETTER_F_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_R \
= n(b'\xF0\x9F\x87\xAB\xF0\x9F\x87\xB7')
REGIONAL_INDICATOR_SYMBOL_LETTER_E_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_S \
= n(b'\xF0\x9F\x87\xAA\xF0\x9F\x87\xB8')
REGIONAL_INDICATOR_SYMBOL_LETTER_I_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_T \
= n(b'\xF0\x9F\x87\xAE\xF0\x9F\x87\xB9')
REGIONAL_INDICATOR_SYMBOL_LETTER_U_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_S \
= n(b'\xF0\x9F\x87\xBA\xF0\x9F\x87\xB8')
REGIONAL_INDICATOR_SYMBOL_LETTER_R_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_U \
= n(b'\xF0\x9F\x87\xB7\xF0\x9F\x87\xBA')
REGIONAL_INDICATOR_SYMBOL_LETTER_D_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_E = n(
b'\xF0\x9F\x87\xA9\xF0\x9F\x87\xAA')
REGIONAL_INDICATOR_SYMBOL_LETTER_G_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_B = n(
b'\xF0\x9F\x87\xAC\xF0\x9F\x87\xA7')
REGIONAL_INDICATOR_SYMBOL_LETTER_C_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_N = n(
b'\xF0\x9F\x87\xA8\xF0\x9F\x87\xB3')
REGIONAL_INDICATOR_SYMBOL_LETTER_J_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_P = n(
b'\xF0\x9F\x87\xAF\xF0\x9F\x87\xB5')
REGIONAL_INDICATOR_SYMBOL_LETTER_K_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_R = n(
b'\xF0\x9F\x87\xB0\xF0\x9F\x87\xB7')
REGIONAL_INDICATOR_SYMBOL_LETTER_F_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_R = n(
b'\xF0\x9F\x87\xAB\xF0\x9F\x87\xB7')
REGIONAL_INDICATOR_SYMBOL_LETTER_E_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_S = n(
b'\xF0\x9F\x87\xAA\xF0\x9F\x87\xB8')
REGIONAL_INDICATOR_SYMBOL_LETTER_I_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_T = n(
b'\xF0\x9F\x87\xAE\xF0\x9F\x87\xB9')
REGIONAL_INDICATOR_SYMBOL_LETTER_U_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_S = n(
b'\xF0\x9F\x87\xBA\xF0\x9F\x87\xB8')
REGIONAL_INDICATOR_SYMBOL_LETTER_R_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_U = n(
b'\xF0\x9F\x87\xB7\xF0\x9F\x87\xBA')
SQUARED_KATAKANA_KOKO = n(b'\xF0\x9F\x88\x81')
SQUARED_KATAKANA_SA = n(b'\xF0\x9F\x88\x82')
SQUARED_CJK_UNIFIED_IDEOGRAPH_7121 = n(b'\xF0\x9F\x88\x9A')
@@ -858,7 +857,8 @@ class Emoji(object):
NO_MOBILE_PHONES = n(b'\xF0\x9F\x93\xB5')
TWISTED_RIGHTWARDS_ARROWS = n(b'\xF0\x9F\x94\x80')
CLOCKWISE_RIGHTWARDS_AND_LEFTWARDS_OPEN_CIRCLE_ARROWS = n(b'\xF0\x9F\x94\x81')
CLOCKWISE_RIGHTWARDS_AND_LEFTWARDS_OPEN_CIRCLE_ARROWS_WITH_CIRCLED_ONE_OVERLAY = n(b'\xF0\x9F\x94\x82')
CLOCKWISE_RIGHTWARDS_AND_LEFTWARDS_OPEN_CIRCLE_ARROWS_WITH_CIRCLED_ONE_OVERLAY = n(
b'\xF0\x9F\x94\x82')
ANTICLOCKWISE_DOWNWARDS_AND_UPWARDS_OPEN_CIRCLE_ARROWS = n(b'\xF0\x9F\x94\x84')
LOW_BRIGHTNESS_SYMBOL = n(b'\xF0\x9F\x94\x85')
HIGH_BRIGHTNESS_SYMBOL = n(b'\xF0\x9F\x94\x86')
+8 -1
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Error."""
@@ -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()
@@ -62,11 +62,13 @@ class TelegramError(Exception):
class Unauthorized(TelegramError):
def __init__(self):
super(Unauthorized, self).__init__('Unauthorized')
class InvalidToken(TelegramError):
def __init__(self):
super(InvalidToken, self).__init__('Invalid token')
@@ -75,6 +77,11 @@ class NetworkError(TelegramError):
pass
class BadRequest(NetworkError):
pass
class TimedOut(NetworkError):
def __init__(self):
super(TimedOut, self).__init__('Timed out')
+3 -4
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""Extensions over the Telegram Bot API to facilitate bot making"""
from .dispatcher import Dispatcher
@@ -34,6 +33,6 @@ from .stringregexhandler import StringRegexHandler
from .typehandler import TypeHandler
__all__ = ('Dispatcher', 'JobQueue', 'Updater', 'CallbackQueryHandler',
'ChosenInlineResultHandler', 'CommandHandler', 'Handler',
'InlineQueryHandler', 'MessageHandler', 'Filters', 'RegexHandler',
'StringCommandHandler', 'StringRegexHandler', 'TypeHandler')
'ChosenInlineResultHandler', 'CommandHandler', 'Handler', 'InlineQueryHandler',
'MessageHandler', 'Filters', 'RegexHandler', 'StringCommandHandler',
'StringRegexHandler', 'TypeHandler')
+10 -5
View File
@@ -16,11 +16,11 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the CallbackQueryHandler class """
from .handler import Handler
from telegram import Update
from telegram.utils.deprecate import deprecate
class CallbackQueryHandler(Handler):
@@ -29,7 +29,7 @@ class CallbackQueryHandler(Handler):
Args:
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``checkUpdate``
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
pass_update_queue (optional[bool]): If the handler should be passed the
update queue as a keyword argument called ``update_queue``. It can
@@ -39,10 +39,15 @@ class CallbackQueryHandler(Handler):
def __init__(self, callback, pass_update_queue=False):
super(CallbackQueryHandler, self).__init__(callback, pass_update_queue)
def checkUpdate(self, update):
def check_update(self, update):
return isinstance(update, Update) and update.callback_query
def handleUpdate(self, update, dispatcher):
optional_args = self.collectOptionalArgs(dispatcher)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.CallbackQueryHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+11 -7
View File
@@ -16,11 +16,11 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the ChosenInlineResultHandler class """
from .handler import Handler
from telegram import Update
from telegram.utils.deprecate import deprecate
class ChosenInlineResultHandler(Handler):
@@ -30,7 +30,7 @@ class ChosenInlineResultHandler(Handler):
Args:
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``checkUpdate``
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
pass_update_queue (optional[bool]): If the handler should be passed the
update queue as a keyword argument called ``update_queue``. It can
@@ -38,13 +38,17 @@ class ChosenInlineResultHandler(Handler):
"""
def __init__(self, callback, pass_update_queue=False):
super(ChosenInlineResultHandler, self).__init__(callback,
pass_update_queue)
super(ChosenInlineResultHandler, self).__init__(callback, pass_update_queue)
def checkUpdate(self, update):
def check_update(self, update):
return isinstance(update, Update) and update.chosen_inline_result
def handleUpdate(self, update, dispatcher):
optional_args = self.collectOptionalArgs(dispatcher)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.ChosenInlineResultHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+30 -13
View File
@@ -16,11 +16,11 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the CommandHandler class """
from .handler import Handler
from telegram import Update
from telegram.utils.deprecate import deprecate
class CommandHandler(Handler):
@@ -32,8 +32,10 @@ class CommandHandler(Handler):
Args:
command (str): The name of the command this handler should listen for.
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``checkUpdate``
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
allow_edited (Optional[bool]): If the handler should also accept edited messages.
Default is ``False``
pass_args (optional[bool]): If the handler should be passed the
arguments passed to the command as a keyword argument called `
``args``. It will contain a list of strings, which is the text
@@ -43,24 +45,39 @@ class CommandHandler(Handler):
be used to insert updates. Default is ``False``
"""
def __init__(self, command, callback, pass_args=False,
def __init__(self,
command,
callback,
allow_edited=False,
pass_args=False,
pass_update_queue=False):
super(CommandHandler, self).__init__(callback, pass_update_queue)
self.command = command
self.allow_edited = allow_edited
self.pass_args = pass_args
def checkUpdate(self, update):
return (isinstance(update, Update) and
update.message and
update.message.text and
update.message.text.startswith('/') and
update.message.text[1:].split(' ')[0].split('@')[0] ==
self.command)
def 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
def handleUpdate(self, update, dispatcher):
optional_args = self.collectOptionalArgs(dispatcher)
return (message.text and message.text.startswith('/')
and message.text[1:].split(' ')[0].split('@')[0] == self.command)
else:
return False
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
message = update.message or update.edited_message
if self.pass_args:
optional_args['args'] = update.message.text.split(' ')[1:]
optional_args['args'] = message.text.split(' ')[1:]
self.callback(dispatcher.bot, update, **optional_args)
# 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")
+72 -57
View File
@@ -16,28 +16,51 @@
#
# 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 threading import Thread, BoundedSemaphore, Lock, Event, current_thread
from threading import Thread, Lock, Event, current_thread
from time import sleep
from queue import Queue, Empty
from queue 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
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:
try:
func, args, kwargs = ASYNC_QUEUE.get()
# If unpacking fails, the thread pool is being closed from Updater._join_async_threads
except TypeError:
logging.getLogger(__name__).debug("Closing run_async thread %s/%d" %
(current_thread().getName(), len(ASYNC_THREADS)))
break
try:
func(*args, **kwargs)
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.
@@ -53,30 +76,11 @@ 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
"""
try:
result = func(*pargs, **kwargs)
finally:
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
ASYNC_QUEUE.put((func, args, kwargs))
return async_func
@@ -91,6 +95,7 @@ class Dispatcher(object):
update_queue (Queue): The synchronized queue that will contain the
updates.
"""
def __init__(self, bot, update_queue, workers=4, exception_event=None):
self.bot = bot
self.update_queue = update_queue
@@ -106,11 +111,18 @@ class Dispatcher(object):
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')
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):
"""
@@ -130,7 +142,7 @@ class Dispatcher(object):
self.running = True
self.logger.debug('Dispatcher started')
while True:
while 1:
try:
# Pop update from update queue.
update = self.update_queue.get(True, 1)
@@ -139,13 +151,12 @@ class Dispatcher(object):
self.logger.debug('orderly stopping')
break
elif self.__exception_event.is_set():
self.logger.critical(
'stopping due to exception in another thread')
self.logger.critical('stopping due to exception in another thread')
break
continue
self.logger.debug('Processing Update: %s' % update)
self.processUpdate(update)
self.process_update(update)
self.running = False
self.logger.debug('Dispatcher thread stopped')
@@ -160,7 +171,7 @@ class Dispatcher(object):
sleep(0.1)
self.__stop_event.clear()
def processUpdate(self, update):
def process_update(self, update):
"""
Processes a single update.
@@ -170,38 +181,35 @@ class Dispatcher(object):
# An error happened while polling
if isinstance(update, TelegramError):
self.dispatchError(None, update)
self.dispatch_error(None, update)
else:
for group in self.groups:
for handler in self.handlers[group]:
try:
if handler.checkUpdate(update):
handler.handleUpdate(update, self)
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.')
self.logger.warn('A TelegramError was raised while processing the '
'Update.')
try:
self.dispatchError(update, te)
self.dispatch_error(update, te)
except Exception:
self.logger.exception(
'An uncaught error was raised while '
'handling the error')
self.logger.exception('An uncaught error was raised while '
'handling the error')
finally:
break
# Errors should not stop the thread
except Exception:
self.logger.exception(
'An uncaught error was raised while '
'processing the update')
self.logger.exception('An uncaught error was raised while '
'processing the update')
break
def addHandler(self, handler, group=DEFAULT_GROUP):
def add_handler(self, handler, group=DEFAULT_GROUP):
"""
Register a handler.
@@ -227,8 +235,7 @@ class Dispatcher(object):
"""
if not isinstance(handler, Handler):
raise TypeError(
'handler is not an instance of {0}'.format(Handler.__name__))
raise TypeError('handler is not an instance of {0}'.format(Handler.__name__))
if not isinstance(group, int):
raise TypeError('group is not int')
@@ -239,7 +246,7 @@ class Dispatcher(object):
self.handlers[group].append(handler)
def removeHandler(self, handler, group=DEFAULT_GROUP):
def remove_handler(self, handler, group=DEFAULT_GROUP):
"""
Remove a handler from the specified group
@@ -253,7 +260,7 @@ class Dispatcher(object):
del self.handlers[group]
self.groups.remove(group)
def addErrorHandler(self, callback):
def add_error_handler(self, callback):
"""
Registers an error handler in the Dispatcher.
@@ -264,7 +271,7 @@ class Dispatcher(object):
self.error_handlers.append(callback)
def removeErrorHandler(self, callback):
def remove_error_handler(self, callback):
"""
De-registers an error handler.
@@ -275,7 +282,7 @@ class Dispatcher(object):
if callback in self.error_handlers:
self.error_handlers.remove(callback)
def dispatchError(self, update, error):
def dispatch_error(self, update, error):
"""
Dispatches an error.
@@ -286,3 +293,11 @@ class Dispatcher(object):
for callback in self.error_handlers:
callback(self.bot, update, error)
# old non-PEP8 Dispatcher methods
m = "telegram.dispatcher."
addHandler = deprecate(add_handler, m + "AddHandler", m + "add_handler")
removeHandler = deprecate(remove_handler, m + "removeHandler", m + "remove_handler")
addErrorHandler = deprecate(add_error_handler, m + "addErrorHandler", m + "add_error_handler")
removeErrorHandler = deprecate(remove_error_handler, m + "removeErrorHandler",
m + "remove_error_handler")
+13 -5
View File
@@ -16,10 +16,11 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the base class for handlers as used by the
Dispatcher """
from telegram.utils.deprecate import deprecate
class Handler(object):
"""
@@ -28,7 +29,7 @@ class Handler(object):
Args:
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``checkUpdate``
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
pass_update_queue (optional[bool]): If the callback should be passed
the update queue as a keyword argument called ``update_queue``. It
@@ -39,7 +40,7 @@ class Handler(object):
self.callback = callback
self.pass_update_queue = pass_update_queue
def checkUpdate(self, update):
def check_update(self, update):
"""
This method is called to determine if an update should be handled by
this handler instance. It should always be overridden.
@@ -52,7 +53,7 @@ class Handler(object):
"""
raise NotImplementedError
def handleUpdate(self, update, dispatcher):
def handle_update(self, update, dispatcher):
"""
This method is called if it was determined that an update should indeed
be handled by this instance. It should also be overridden, but in most
@@ -65,7 +66,7 @@ class Handler(object):
"""
raise NotImplementedError
def collectOptionalArgs(self, dispatcher):
def collect_optional_args(self, dispatcher):
"""
Prepares the optional arguments that are the same for all types of
handlers
@@ -78,3 +79,10 @@ class Handler(object):
optional_args['update_queue'] = dispatcher.update_queue
return optional_args
# old non-PEP8 Handler methods
m = "telegram.Handler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
collectOptionalArgs = deprecate(collect_optional_args, m + "collectOptionalArgs",
m + "collect_optional_args")
+10 -5
View File
@@ -16,11 +16,11 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the InlineQueryHandler class """
from .handler import Handler
from telegram import Update
from telegram.utils.deprecate import deprecate
class InlineQueryHandler(Handler):
@@ -29,7 +29,7 @@ class InlineQueryHandler(Handler):
Args:
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``checkUpdate``
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
pass_update_queue (optional[bool]): If the handler should be passed the
update queue as a keyword argument called ``update_queue``. It can
@@ -39,10 +39,15 @@ class InlineQueryHandler(Handler):
def __init__(self, callback, pass_update_queue=False):
super(InlineQueryHandler, self).__init__(callback, pass_update_queue)
def checkUpdate(self, update):
def check_update(self, update):
return isinstance(update, Update) and update.inline_query
def handleUpdate(self, update, dispatcher):
optional_args = self.collectOptionalArgs(dispatcher)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.InlineQueryHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+2 -9
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the class JobQueue."""
import logging
@@ -50,12 +49,7 @@ class JobQueue(object):
self.__lock = Lock()
self.running = False
def put(self,
run,
interval,
repeat=True,
next_t=None,
prevent_autostart=False):
def put(self, run, interval, repeat=True, next_t=None, prevent_autostart=False):
"""
Queue a new job. If the JobQueue is not running, it will be started.
@@ -123,8 +117,7 @@ class JobQueue(object):
if not self.running:
self.running = True
self.__lock.release()
job_queue_thread = Thread(target=self._start,
name="job_queue")
job_queue_thread = Thread(target=self._start, name="job_queue")
job_queue_thread.start()
self.logger.debug('Job Queue thread started')
else:
+50 -43
View File
@@ -16,11 +16,11 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the MessageHandler class """
from .handler import Handler
from telegram import Update
from telegram.utils.deprecate import deprecate
class Filters(object):
@@ -30,63 +30,56 @@ class Filters(object):
"""
@staticmethod
def text(update):
return update.message.text and not update.message.text.startswith('/')
def text(message):
return message.text and not message.text.startswith('/')
@staticmethod
def command(update):
return update.message.text and update.message.text.startswith('/')
def command(message):
return message.text and message.text.startswith('/')
@staticmethod
def audio(update):
return bool(update.message.audio)
def audio(message):
return bool(message.audio)
@staticmethod
def document(update):
return bool(update.message.document)
def document(message):
return bool(message.document)
@staticmethod
def photo(update):
return bool(update.message.photo)
def photo(message):
return bool(message.photo)
@staticmethod
def sticker(update):
return bool(update.message.sticker)
def sticker(message):
return bool(message.sticker)
@staticmethod
def video(update):
return bool(update.message.video)
def video(message):
return bool(message.video)
@staticmethod
def voice(update):
return bool(update.message.voice)
def voice(message):
return bool(message.voice)
@staticmethod
def contact(update):
return bool(update.message.contact)
def contact(message):
return bool(message.contact)
@staticmethod
def location(update):
return bool(update.message.location)
def location(message):
return bool(message.location)
@staticmethod
def venue(update):
return bool(update.message.venue)
def venue(message):
return bool(message.venue)
@staticmethod
def status_update(update):
return bool(
update.message.new_chat_member or
update.message.left_chat_member or
update.message.new_chat_title or
update.message.new_chat_photo or
update.message.delete_chat_photo or
update.message.group_chat_created or
update.message.supergroup_chat_created or
update.message.channel_chat_created or
update.message.migrate_to_chat_id or
update.message.migrate_from_chat_id or
update.message.pinned_message)
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):
@@ -103,28 +96,42 @@ class MessageHandler(Handler):
accepted. If ``bool(filters)`` evaluates to ``False``, messages are
not filtered.
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``checkUpdate``
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
allow_edited (Optional[bool]): If the handler should also accept edited messages.
Default is ``False``
pass_update_queue (optional[bool]): If the handler should be passed the
update queue as a keyword argument called ``update_queue``. It can
be used to insert updates. Default is ``False``
"""
def __init__(self, filters, callback, pass_update_queue=False):
def __init__(self, filters, callback, allow_edited=False, pass_update_queue=False):
super(MessageHandler, self).__init__(callback, pass_update_queue)
self.filters = filters
self.allow_edited = allow_edited
def check_update(self, update):
if (isinstance(update, Update)
and (update.message or update.edited_message and self.allow_edited)):
def checkUpdate(self, update):
if isinstance(update, Update) and update.message:
if not self.filters:
res = True
else:
res = any(func(update) for func in self.filters)
message = update.message or update.edited_message
res = any(func(message) for func in self.filters)
else:
res = False
return res
def handleUpdate(self, update, dispatcher):
optional_args = self.collectOptionalArgs(dispatcher)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.MessageHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+17 -10
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the RegexHandler class """
import re
@@ -25,6 +24,7 @@ from future.utils import string_types
from .handler import Handler
from telegram import Update
from telegram.utils.deprecate import deprecate
class RegexHandler(Handler):
@@ -37,7 +37,7 @@ class RegexHandler(Handler):
Args:
pattern (str or Pattern): The regex pattern.
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``checkUpdate``
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
pass_groups (optional[bool]): If the callback should be passed the
result of ``re.match(pattern, text).groups()`` as a keyword
@@ -50,8 +50,12 @@ class RegexHandler(Handler):
be used to insert updates. Default is ``False``
"""
def __init__(self, pattern, callback, pass_groups=False,
pass_groupdict=False, pass_update_queue=False):
def __init__(self,
pattern,
callback,
pass_groups=False,
pass_groupdict=False,
pass_update_queue=False):
super(RegexHandler, self).__init__(callback, pass_update_queue)
if isinstance(pattern, string_types):
@@ -61,17 +65,15 @@ class RegexHandler(Handler):
self.pass_groups = pass_groups
self.pass_groupdict = pass_groupdict
def checkUpdate(self, update):
if (isinstance(update, Update) and
update.message and
update.message.text):
def check_update(self, update):
if (isinstance(update, Update) and update.message and update.message.text):
match = re.match(self.pattern, update.message.text)
return bool(match)
else:
return False
def handleUpdate(self, update, dispatcher):
optional_args = self.collectOptionalArgs(dispatcher)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
match = re.match(self.pattern, update.message.text)
if self.pass_groups:
@@ -80,3 +82,8 @@ class RegexHandler(Handler):
optional_args['groupdict'] = match.groupdict()
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.RegexHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+13 -10
View File
@@ -16,10 +16,10 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the StringCommandHandler class """
from .handler import Handler
from telegram.utils.deprecate import deprecate
class StringCommandHandler(Handler):
@@ -30,7 +30,7 @@ class StringCommandHandler(Handler):
Args:
command (str): The name of the command this handler should listen for.
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``checkUpdate``
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
pass_args (optional[bool]): If the handler should be passed the
arguments passed to the command as a keyword argument called `
@@ -41,21 +41,24 @@ class StringCommandHandler(Handler):
be used to insert updates. Default is ``False``
"""
def __init__(self, command, callback, pass_args=False,
pass_update_queue=False):
def __init__(self, command, callback, pass_args=False, pass_update_queue=False):
super(StringCommandHandler, self).__init__(callback, pass_update_queue)
self.command = command
self.pass_args = pass_args
def checkUpdate(self, update):
return (isinstance(update, str) and
update.startswith('/') and
update[1:].split(' ')[0] == self.command)
def check_update(self, update):
return (isinstance(update, str) and update.startswith('/')
and update[1:].split(' ')[0] == self.command)
def handleUpdate(self, update, dispatcher):
optional_args = self.collectOptionalArgs(dispatcher)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
if self.pass_args:
optional_args['args'] = update.split(' ')[1:]
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.StringCommandHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+17 -9
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the StringRegexHandler class """
import re
@@ -24,6 +23,7 @@ import re
from future.utils import string_types
from .handler import Handler
from telegram.utils.deprecate import deprecate
class StringRegexHandler(Handler):
@@ -36,7 +36,7 @@ class StringRegexHandler(Handler):
Args:
pattern (str or Pattern): The regex pattern.
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``checkUpdate``
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
pass_groups (optional[bool]): If the callback should be passed the
result of ``re.match(pattern, update).groups()`` as a keyword
@@ -49,8 +49,12 @@ class StringRegexHandler(Handler):
be used to insert updates. Default is ``False``
"""
def __init__(self, pattern, callback, pass_groups=False,
pass_groupdict=False, pass_update_queue=False):
def __init__(self,
pattern,
callback,
pass_groups=False,
pass_groupdict=False,
pass_update_queue=False):
super(StringRegexHandler, self).__init__(callback, pass_update_queue)
if isinstance(pattern, string_types):
@@ -60,12 +64,11 @@ class StringRegexHandler(Handler):
self.pass_groups = pass_groups
self.pass_groupdict = pass_groupdict
def checkUpdate(self, update):
return isinstance(update, string_types) and bool(
re.match(self.pattern, update))
def check_update(self, update):
return isinstance(update, string_types) and bool(re.match(self.pattern, update))
def handleUpdate(self, update, dispatcher):
optional_args = self.collectOptionalArgs(dispatcher)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
match = re.match(self.pattern, update)
if self.pass_groups:
@@ -74,3 +77,8 @@ class StringRegexHandler(Handler):
optional_args['groupdict'] = match.groupdict()
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.StringRegexHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+10 -5
View File
@@ -16,10 +16,10 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the TypeHandler class """
from .handler import Handler
from telegram.utils.deprecate import deprecate
class TypeHandler(Handler):
@@ -30,7 +30,7 @@ class TypeHandler(Handler):
type (type): The ``type`` of updates this handler should process, as
determined by ``isinstance``
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``checkUpdate``
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
strict (optional[bool]): Use ``type`` instead of ``isinstance``.
Default is ``False``
@@ -44,13 +44,18 @@ class TypeHandler(Handler):
self.type = type
self.strict = strict
def checkUpdate(self, update):
def check_update(self, update):
if not self.strict:
return isinstance(update, self.type)
else:
return type(update) is self.type
def handleUpdate(self, update, dispatcher):
optional_args = self.collectOptionalArgs(dispatcher)
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.TypeHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+113 -111
View File
@@ -16,8 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the class Updater, which tries to make creating
Telegram bots intuitive."""
@@ -85,8 +83,7 @@ class Updater(object):
self.update_queue = Queue()
self.job_queue = JobQueue(self.bot, job_queue_tick_interval)
self.__exception_event = Event()
self.dispatcher = Dispatcher(self.bot, self.update_queue, workers,
self.__exception_event)
self.dispatcher = Dispatcher(self.bot, self.update_queue, workers, self.__exception_event)
self.last_update_id = 0
self.logger = logging.getLogger(__name__)
self.running = False
@@ -96,48 +93,8 @@ class Updater(object):
self.__threads = []
""":type: list[Thread]"""
def start_polling(self, poll_interval=0.0, timeout=10, network_delay=2,
clean=False, bootstrap_retries=0):
"""
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)
@@ -152,6 +109,49 @@ class Updater(object):
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,
@@ -194,20 +194,16 @@ class Updater(object):
with self.__lock:
if not self.running:
self.running = True
if clean:
self._clean_updates()
# Create & start threads
self._init_thread(self.dispatcher.start, "dispatcher"),
self._init_thread(self._start_webhook, "updater", listen,
port, url_path, cert, key, bootstrap_retries,
webhook_url)
self._init_thread(self._start_webhook, "updater", listen, port, url_path, cert,
key, bootstrap_retries, clean, webhook_url)
# Return the update queue so the main thread can insert updates
return self.update_queue
def _start_polling(self, poll_interval, timeout, network_delay,
bootstrap_retries):
def _start_polling(self, poll_interval, timeout, network_delay, bootstrap_retries, clean):
"""
Thread target of thread 'updater'. Runs in background, pulls
updates from Telegram and inserts them in the update queue of the
@@ -217,7 +213,7 @@ class Updater(object):
cur_interval = poll_interval
self.logger.debug('Updater thread started')
self._set_webhook(None, bootstrap_retries, None)
self._bootstrap(bootstrap_retries, clean=clean, webhook_url='')
while self.running:
try:
@@ -225,8 +221,7 @@ class Updater(object):
timeout=timeout,
network_delay=network_delay)
except TelegramError as te:
self.logger.error(
"Error while getting Updates: {0}".format(te))
self.logger.error("Error while getting Updates: {0}".format(te))
# Put the error into the update queue and let the Dispatcher
# broadcast it
@@ -249,28 +244,6 @@ class Updater(object):
sleep(cur_interval)
def _set_webhook(self, webhook_url, max_retries, cert):
retries = 0
while 1:
try:
# Remove webhook
self.bot.setWebhook(webhook_url=webhook_url,
certificate=cert)
except (Unauthorized, InvalidToken):
raise
except TelegramError:
msg = 'failed to set webhook; try={0} max_retries={1}'.format(
retries, max_retries)
if max_retries < 0 or retries < max_retries:
self.logger.info(msg)
retries += 1
else:
self.logger.exception(msg)
raise
else:
break
sleep(1)
@staticmethod
def _increase_poll_interval(current_interval):
# increase waiting times on subsequent errors up to 30secs
@@ -282,16 +255,15 @@ class Updater(object):
current_interval = 30
return current_interval
def _start_webhook(self, listen, port, url_path, cert, key,
bootstrap_retries, webhook_url):
def _start_webhook(self, listen, port, url_path, cert, key, bootstrap_retries, clean,
webhook_url):
self.logger.debug('Updater thread started')
use_ssl = cert is not None and key is not None
if not url_path.startswith('/'):
url_path = '/{0}'.format(url_path)
# Create and start server
self.httpd = WebhookServer((listen, port), WebhookHandler,
self.update_queue, url_path)
self.httpd = WebhookServer((listen, port), WebhookHandler, self.update_queue, url_path)
if use_ssl:
self._check_ssl_cert(cert, key)
@@ -300,18 +272,23 @@ class Updater(object):
if not webhook_url:
webhook_url = self._gen_webhook_url(listen, port, url_path)
self._set_webhook(webhook_url, bootstrap_retries,
open(cert, 'rb'))
self._bootstrap(max_retries=bootstrap_retries,
clean=clean,
webhook_url=webhook_url,
cert=open(cert, 'rb'))
elif clean:
self.logger.warning("cleaning updates is not supported if "
"SSL-termination happens elsewhere; skipping")
self.httpd.serve_forever(poll_interval=1)
def _check_ssl_cert(self, cert, key):
# Check SSL-Certificate with openssl, if possible
try:
exit_code = subprocess.call(["openssl", "x509", "-text",
"-noout", "-in", cert],
stdout=open(os.devnull, 'wb'),
stderr=subprocess.STDOUT)
exit_code = subprocess.call(
["openssl", "x509", "-text", "-noout", "-in", cert],
stdout=open(os.devnull, 'wb'),
stderr=subprocess.STDOUT)
except OSError:
exit_code = 0
if exit_code is 0:
@@ -326,11 +303,35 @@ class Updater(object):
else:
raise TelegramError('SSL Certificate invalid')
def _gen_webhook_url(self, listen, port, url_path):
return 'https://{listen}:{port}{path}'.format(
listen=listen,
port=port,
path=url_path)
@staticmethod
def _gen_webhook_url(listen, port, url_path):
return 'https://{listen}:{port}{path}'.format(listen=listen, port=port, path=url_path)
def _bootstrap(self, max_retries, clean, webhook_url, cert=None):
retries = 0
while 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')
@@ -345,7 +346,7 @@ class Updater(object):
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
@@ -353,17 +354,15 @@ class Updater(object):
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
@@ -372,20 +371,23 @@ class Updater(object):
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 = []
+2 -6
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram File."""
from os.path import basename
@@ -42,9 +41,7 @@ class File(TelegramObject):
file_path (Optional[str]):
"""
def __init__(self,
file_id,
**kwargs):
def __init__(self, file_id, **kwargs):
# Required
self.file_id = str(file_id)
# Optionals
@@ -65,8 +62,7 @@ class File(TelegramObject):
return File(**data)
def download(self,
custom_path=None):
def download(self, custom_path=None):
"""
Args:
custom_path (str):
+1 -4
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram ForceReply."""
from telegram import ReplyMarkup
@@ -37,9 +36,7 @@ class ForceReply(ReplyMarkup):
selective (Optional[bool]):
"""
def __init__(self,
force_reply=True,
**kwargs):
def __init__(self, force_reply=True, **kwargs):
# Required
self.force_reply = bool(force_reply)
# Optionals
+2 -6
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
InlineKeyboardButton"""
@@ -43,9 +42,7 @@ class InlineKeyboardButton(TelegramObject):
"""
def __init__(self,
text,
**kwargs):
def __init__(self, text, **kwargs):
# Required
self.text = text
@@ -70,7 +67,6 @@ class InlineKeyboardButton(TelegramObject):
inline_keyboards = list()
for inline_keyboard in data:
inline_keyboards.append(InlineKeyboardButton.
de_json(inline_keyboard))
inline_keyboards.append(InlineKeyboardButton.de_json(inline_keyboard))
return inline_keyboards
+6 -10
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
InlineKeyboardMarkup"""
@@ -27,15 +26,14 @@ class InlineKeyboardMarkup(ReplyMarkup):
"""This object represents a Telegram InlineKeyboardMarkup.
Attributes:
inline_keyboard (List[List[:class:`telegram.InlineKeyboardMarkup`]]):
inline_keyboard (List[List[:class:`telegram.InlineKeyboardButton`]]):
Args:
inline_keyboard (List[List[:class:`telegram.InlineKeyboardMarkup`]]):
inline_keyboard (List[List[:class:`telegram.InlineKeyboardButton`]]):
"""
def __init__(self,
inline_keyboard):
def __init__(self, inline_keyboard, **kwargs):
# Required
self.inline_keyboard = inline_keyboard
@@ -46,9 +44,8 @@ class InlineKeyboardMarkup(ReplyMarkup):
if not data:
return None
data['inline_keyboard'] = \
[InlineKeyboardButton.de_list(inline_keyboard) for inline_keyboard
in data['inline_keyboard']]
data['inline_keyboard'] = [InlineKeyboardButton.de_list(inline_keyboard)
for inline_keyboard in data['inline_keyboard']]
return InlineKeyboardMarkup(**data)
@@ -57,7 +54,6 @@ class InlineKeyboardMarkup(ReplyMarkup):
data['inline_keyboard'] = []
for inline_keyboard in self.inline_keyboard:
data['inline_keyboard'].append(
[x.to_dict() for x in inline_keyboard])
data['inline_keyboard'].append([x.to_dict() for x in inline_keyboard])
return data
+1 -7
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram InlineQuery"""
from telegram import TelegramObject, User, Location
@@ -45,12 +44,7 @@ class InlineQuery(TelegramObject):
location (optional[:class:`telegram.Location`]):
"""
def __init__(self,
id,
from_user,
query,
offset,
**kwargs):
def __init__(self, id, from_user, query, offset, **kwargs):
# Required
self.id = id
self.from_user = from_user
+1 -4
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResult"""
@@ -36,9 +35,7 @@ class InlineQueryResult(TelegramObject):
"""
def __init__(self,
type,
id):
def __init__(self, type, id):
# Required
self.type = str(type)
self.id = str(id)
+5 -9
View File
@@ -16,12 +16,10 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultArticle"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultArticle(InlineQueryResult):
@@ -97,12 +95,10 @@ class InlineQueryResultArticle(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultArticle,
InlineQueryResultArticle).de_json(data)
data = super(InlineQueryResultArticle, InlineQueryResultArticle).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultArticle(**data)
+6 -9
View File
@@ -16,12 +16,10 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultAudio"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultAudio(InlineQueryResult):
@@ -59,6 +57,7 @@ class InlineQueryResultAudio(InlineQueryResult):
input_message_content (Optional[
:class:`telegram.input_message_content`]):
"""
def __init__(self,
id,
audio_url,
@@ -86,12 +85,10 @@ class InlineQueryResultAudio(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultAudio,
InlineQueryResultAudio).de_json(data)
data = super(InlineQueryResultAudio, InlineQueryResultAudio).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultAudio(**data)
+7 -15
View File
@@ -16,12 +16,10 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultCachedAudio"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedAudio(InlineQueryResult):
@@ -54,12 +52,8 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
input_message_content (Optional[
:class:`telegram.input_message_content`]):
"""
def __init__(self,
id,
audio_file_id,
reply_markup=None,
input_message_content=None,
**kwargs):
def __init__(self, id, audio_file_id, reply_markup=None, input_message_content=None, **kwargs):
# Required
super(InlineQueryResultCachedAudio, self).__init__('audio', id)
self.audio_file_id = audio_file_id
@@ -72,12 +66,10 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultCachedAudio,
InlineQueryResultCachedAudio).de_json(data)
data = super(InlineQueryResultCachedAudio, InlineQueryResultCachedAudio).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultCachedAudio(**data)
+5 -7
View File
@@ -16,15 +16,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultCachedDocument"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedDocument(InlineQueryResult):
def __init__(self,
id,
title,
@@ -54,9 +53,8 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
data = super(InlineQueryResultCachedDocument,
InlineQueryResultCachedDocument).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultCachedDocument(**data)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultCachedGif"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedGif(InlineQueryResult):
def __init__(self,
id,
gif_file_id,
@@ -49,12 +48,10 @@ class InlineQueryResultCachedGif(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultCachedGif,
InlineQueryResultCachedGif).de_json(data)
data = super(InlineQueryResultCachedGif, InlineQueryResultCachedGif).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultCachedGif(**data)
+5 -7
View File
@@ -16,15 +16,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultMpeg4Gif"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
def __init__(self,
id,
mpeg4_file_id,
@@ -52,9 +51,8 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
data = super(InlineQueryResultCachedMpeg4Gif,
InlineQueryResultCachedMpeg4Gif).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultCachedMpeg4Gif(**data)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultPhoto"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedPhoto(InlineQueryResult):
def __init__(self,
id,
photo_file_id,
@@ -52,12 +51,10 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultCachedPhoto,
InlineQueryResultCachedPhoto).de_json(data)
data = super(InlineQueryResultCachedPhoto, InlineQueryResultCachedPhoto).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultCachedPhoto(**data)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultCachedSticker"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedSticker(InlineQueryResult):
def __init__(self,
id,
sticker_file_id,
@@ -43,12 +42,10 @@ class InlineQueryResultCachedSticker(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultCachedSticker,
InlineQueryResultCachedSticker).de_json(data)
data = super(InlineQueryResultCachedSticker, InlineQueryResultCachedSticker).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultCachedSticker(**data)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultCachedVideo"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedVideo(InlineQueryResult):
def __init__(self,
id,
video_file_id,
@@ -51,12 +50,10 @@ class InlineQueryResultCachedVideo(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultCachedVideo,
InlineQueryResultCachedVideo).de_json(data)
data = super(InlineQueryResultCachedVideo, InlineQueryResultCachedVideo).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultCachedVideo(**data)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultCachedVoice"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedVoice(InlineQueryResult):
def __init__(self,
id,
voice_file_id,
@@ -48,12 +47,10 @@ class InlineQueryResultCachedVoice(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultCachedVoice,
InlineQueryResultCachedVoice).de_json(data)
data = super(InlineQueryResultCachedVoice, InlineQueryResultCachedVoice).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultCachedVoice(**data)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultContact"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultContact(InlineQueryResult):
def __init__(self,
id,
phone_number,
@@ -57,12 +56,10 @@ class InlineQueryResultContact(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultContact,
InlineQueryResultContact).de_json(data)
data = super(InlineQueryResultContact, InlineQueryResultContact).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultContact(**data)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultDocument"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultDocument(InlineQueryResult):
def __init__(self,
id,
document_url,
@@ -62,12 +61,10 @@ class InlineQueryResultDocument(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultDocument,
InlineQueryResultDocument).de_json(data)
data = super(InlineQueryResultDocument, InlineQueryResultDocument).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultDocument(**data)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultGif"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultGif(InlineQueryResult):
def __init__(self,
id,
gif_url,
@@ -58,12 +57,10 @@ class InlineQueryResultGif(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultGif,
InlineQueryResultGif).de_json(data)
data = super(InlineQueryResultGif, InlineQueryResultGif).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultGif(**data)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultLocation"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultLocation(InlineQueryResult):
def __init__(self,
id,
latitude,
@@ -56,12 +55,10 @@ class InlineQueryResultLocation(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultLocation,
InlineQueryResultLocation).de_json(data)
data = super(InlineQueryResultLocation, InlineQueryResultLocation).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultLocation(**data)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultMpeg4Gif"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultMpeg4Gif(InlineQueryResult):
def __init__(self,
id,
mpeg4_url,
@@ -58,12 +57,10 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultMpeg4Gif,
InlineQueryResultMpeg4Gif).de_json(data)
data = super(InlineQueryResultMpeg4Gif, InlineQueryResultMpeg4Gif).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultMpeg4Gif(**data)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultPhoto"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultPhoto(InlineQueryResult):
def __init__(self,
id,
photo_url,
@@ -60,12 +59,10 @@ class InlineQueryResultPhoto(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultPhoto,
InlineQueryResultPhoto).de_json(data)
data = super(InlineQueryResultPhoto, InlineQueryResultPhoto).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultPhoto(**data)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultVenue"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultVenue(InlineQueryResult):
def __init__(self,
id,
latitude,
@@ -62,12 +61,10 @@ class InlineQueryResultVenue(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultVenue,
InlineQueryResultVenue).de_json(data)
data = super(InlineQueryResultVenue, InlineQueryResultVenue).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultVenue(**data)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultVideo"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultVideo(InlineQueryResult):
def __init__(self,
id,
video_url,
@@ -65,12 +64,10 @@ class InlineQueryResultVideo(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultVideo,
InlineQueryResultVideo).de_json(data)
data = super(InlineQueryResultVideo, InlineQueryResultVideo).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultVideo(**data)
+6 -9
View File
@@ -16,15 +16,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultVoice"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, \
InputMessageContent
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultVoice(InlineQueryResult):
def __init__(self,
id,
voice_url,
@@ -49,12 +48,10 @@ class InlineQueryResultVoice(InlineQueryResult):
@staticmethod
def de_json(data):
data = super(InlineQueryResultVoice,
InlineQueryResultVoice).de_json(data)
data = super(InlineQueryResultVoice, InlineQueryResultVoice).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(
data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'))
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultVoice(**data)
+1 -5
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InputContactMessageContent"""
@@ -26,10 +25,7 @@ from telegram import InputMessageContent
class InputContactMessageContent(InputMessageContent):
"""Base class for Telegram InputContactMessageContent Objects"""
def __init__(self,
phone_number,
first_name,
last_name=None):
def __init__(self, phone_number, first_name, last_name=None, **kwargs):
# Required
self.phone_number = phone_number
self.first_name = first_name
+10 -25
View File
@@ -17,7 +17,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram InputFile."""
try:
@@ -37,15 +36,13 @@ from future.moves.urllib.request import urlopen
from telegram import TelegramError
DEFAULT_MIME_TYPE = 'application/octet-stream'
USER_AGENT = 'Python Telegram Bot' \
' (https://github.com/python-telegram-bot/python-telegram-bot)'
USER_AGENT = 'Python Telegram Bot (https://github.com/python-telegram-bot/python-telegram-bot)'
class InputFile(object):
"""This object represents a Telegram InputFile."""
def __init__(self,
data):
def __init__(self, data):
self.data = data
self.boundary = choose_boundary()
@@ -87,16 +84,14 @@ class InputFile(object):
# pylint: disable=E1101
self.filename = os.path.basename(self.input_file.name)
elif from_url:
self.filename = os.path.basename(self.input_file.url) \
.split('?')[0].split('&')[0]
self.filename = os.path.basename(self.input_file.url).split('?')[0].split('&')[0]
try:
self.mimetype = InputFile.is_image(self.input_file_content)
if not self.filename or '.' not in self.filename:
self.filename = self.mimetype.replace('/', '.')
except TelegramError:
self.mimetype = mimetypes.guess_type(self.filename)[0] or \
DEFAULT_MIME_TYPE
self.mimetype = mimetypes.guess_type(self.filename)[0] or DEFAULT_MIME_TYPE
@property
def headers(self):
@@ -104,8 +99,7 @@ class InputFile(object):
Returns:
str:
"""
return {'User-agent': USER_AGENT,
'Content-type': self.content_type}
return {'User-agent': USER_AGENT, 'Content-type': self.content_type}
@property
def content_type(self):
@@ -126,21 +120,14 @@ class InputFile(object):
# Add data fields
for name, value in self.data.items():
form.extend([
form_boundary,
'Content-Disposition: form-data; name="%s"' % name,
'',
str(value)
form_boundary, 'Content-Disposition: form-data; name="%s"' % name, '', str(value)
])
# Add input_file to upload
form.extend([
form_boundary,
'Content-Disposition: form-data; name="%s"; filename="%s"' % (
form_boundary, 'Content-Disposition: form-data; name="%s"; filename="%s"' % (
self.input_name, self.filename
),
'Content-Type: %s' % self.mimetype,
'',
self.input_file_content
), 'Content-Type: %s' % self.mimetype, '', self.input_file_content
])
form.append('--' + self.boundary + '--')
@@ -193,14 +180,12 @@ class InputFile(object):
bool
"""
if data:
file_types = ['audio', 'document', 'photo', 'sticker', 'video',
'voice', 'certificate']
file_types = ['audio', 'document', 'photo', 'sticker', 'video', 'voice', 'certificate']
file_type = [i for i in list(data.keys()) if i in file_types]
if file_type:
file_content = data[file_type[0]]
return hasattr(file_content, 'read') or str(
file_content).startswith('http')
return hasattr(file_content, 'read') or str(file_content).startswith('http')
return False
+1 -4
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InputLocationMessageContent"""
@@ -26,9 +25,7 @@ from telegram import InputMessageContent
class InputLocationMessageContent(InputMessageContent):
"""Base class for Telegram InputLocationMessageContent Objects"""
def __init__(self,
latitude,
longitude):
def __init__(self, latitude, longitude, **kwargs):
# Required
self.latitude = latitude
self.longitude = longitude
+4 -5
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InputMessageContent"""
@@ -40,14 +39,14 @@ class InputMessageContent(TelegramObject):
pass
try:
from telegram import InputLocationMessageContent
return InputLocationMessageContent.de_json(data)
from telegram import InputVenueMessageContent
return InputVenueMessageContent.de_json(data)
except TypeError:
pass
try:
from telegram import InputVenueMessageContent
return InputVenueMessageContent.de_json(data)
from telegram import InputLocationMessageContent
return InputLocationMessageContent.de_json(data)
except TypeError:
pass
+1 -5
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InputTextMessageContent"""
@@ -26,10 +25,7 @@ from telegram import InputMessageContent
class InputTextMessageContent(InputMessageContent):
"""Base class for Telegram InputTextMessageContent Objects"""
def __init__(self,
message_text,
parse_mode=None,
disable_web_page_preview=None):
def __init__(self, message_text, parse_mode=None, disable_web_page_preview=None, **kwargs):
# Required
self.message_text = message_text
# Optionals
+1 -7
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InputVenueMessageContent"""
@@ -26,12 +25,7 @@ from telegram import InputMessageContent
class InputVenueMessageContent(InputMessageContent):
"""Base class for Telegram InputVenueMessageContent Objects"""
def __init__(self,
latitude,
longitude,
title,
address,
foursquare_id=None):
def __init__(self, latitude, longitude, title, address, foursquare_id=None, **kwargs):
# Required
self.latitude = latitude
self.longitude = longitude
+2 -7
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram KeyboardButton."""
from telegram import TelegramObject
@@ -34,10 +33,7 @@ class KeyboardButton(TelegramObject):
request_contact (Optional[bool]):
"""
def __init__(self,
text,
request_contact=None,
request_location=None):
def __init__(self, text, request_contact=None, request_location=None, **kwargs):
# Required
self.text = text
# Optionals
@@ -60,7 +56,6 @@ class KeyboardButton(TelegramObject):
keyboards = list()
for keyboard in data:
keyboards.append(KeyboardButton.
de_json(keyboard))
keyboards.append(KeyboardButton.de_json(keyboard))
return keyboards
+2 -5
View File
@@ -16,14 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Location."""
from telegram import TelegramObject
class Location(TelegramObject):
"""This object represents a Telegram Sticker.
"""This object represents a Telegram Location.
Attributes:
longitude (float):
@@ -34,9 +33,7 @@ class Location(TelegramObject):
latitude (float):
"""
def __init__(self,
longitude,
latitude):
def __init__(self, longitude, latitude, **kwargs):
# Required
self.longitude = float(longitude)
self.latitude = float(latitude)
+16 -16
View File
@@ -17,15 +17,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Message."""
from datetime import datetime
from time import mktime
from telegram import (Audio, Contact, Document, Chat, Location, PhotoSize,
Sticker, TelegramObject, User, Video, Voice, Venue,
MessageEntity)
from telegram import (Audio, Contact, Document, Chat, Location, PhotoSize, Sticker, TelegramObject,
User, Video, Voice, Venue, MessageEntity)
class Message(TelegramObject):
@@ -39,8 +37,10 @@ class Message(TelegramObject):
from_user (:class:`telegram.User`):
date (:class:`datetime.datetime`):
forward_from (:class:`telegram.User`):
forward_from_chat (:class:`telegram.Chat`):
forward_date (:class:`datetime.datetime`):
reply_to_message (:class:`telegram.Message`):
edit_date (:class:`datetime.datetime`):
text (str):
audio (:class:`telegram.Audio`):
document (:class:`telegram.Document`):
@@ -78,8 +78,10 @@ class Message(TelegramObject):
Keyword Args:
forward_from (Optional[:class:`telegram.User`]):
forward_from_chat (:class:`telegram.Chat`):
forward_date (Optional[:class:`datetime.datetime`]):
reply_to_message (Optional[:class:`telegram.Message`]):
edit_date (Optional[:class:`datetime.datetime`]):
text (Optional[str]):
audio (Optional[:class:`telegram.Audio`]):
document (Optional[:class:`telegram.Document`]):
@@ -102,12 +104,7 @@ class Message(TelegramObject):
channel_chat_created (Optional[bool]):
"""
def __init__(self,
message_id,
from_user,
date,
chat,
**kwargs):
def __init__(self, message_id, from_user, date, chat, **kwargs):
# Required
self.message_id = int(message_id)
self.from_user = from_user
@@ -115,8 +112,10 @@ class Message(TelegramObject):
self.chat = chat
# Optionals
self.forward_from = kwargs.get('forward_from')
self.forward_from_chat = kwargs.get('forward_from_chat')
self.forward_date = kwargs.get('forward_date')
self.reply_to_message = kwargs.get('reply_to_message')
self.edit_date = kwargs.get('edit_date')
self.text = kwargs.get('text', '')
self.entities = kwargs.get('entities', list())
self.audio = kwargs.get('audio')
@@ -135,12 +134,10 @@ class Message(TelegramObject):
self.new_chat_photo = kwargs.get('new_chat_photo')
self.delete_chat_photo = bool(kwargs.get('delete_chat_photo', False))
self.group_chat_created = bool(kwargs.get('group_chat_created', False))
self.supergroup_chat_created = bool(kwargs.get(
'supergroup_chat_created', False))
self.supergroup_chat_created = bool(kwargs.get('supergroup_chat_created', False))
self.migrate_to_chat_id = int(kwargs.get('migrate_to_chat_id', 0))
self.migrate_from_chat_id = int(kwargs.get('migrate_from_chat_id', 0))
self.channel_chat_created = bool(kwargs.get('channel_chat_created',
False))
self.channel_chat_created = bool(kwargs.get('channel_chat_created', False))
self.pinned_message = kwargs.get('pinned_message')
@property
@@ -165,9 +162,10 @@ class Message(TelegramObject):
data['chat'] = Chat.de_json(data.get('chat'))
data['entities'] = MessageEntity.de_list(data.get('entities'))
data['forward_from'] = User.de_json(data.get('forward_from'))
data['forward_from_chat'] = Chat.de_json(data.get('forward_from_chat'))
data['forward_date'] = Message._fromtimestamp(data.get('forward_date'))
data['reply_to_message'] = \
Message.de_json(data.get('reply_to_message'))
data['reply_to_message'] = Message.de_json(data.get('reply_to_message'))
data['edit_date'] = Message._fromtimestamp(data.get('edit_date'))
data['audio'] = Audio.de_json(data.get('audio'))
data['document'] = Document.de_json(data.get('document'))
data['photo'] = PhotoSize.de_list(data.get('photo'))
@@ -203,6 +201,8 @@ class Message(TelegramObject):
# Optionals
if self.forward_date:
data['forward_date'] = self._totimestamp(self.forward_date)
if self.edit_date:
data['edit_date'] = self._totimestamp(self.edit_date)
if self.photo:
data['photo'] = [p.to_dict() for p in self.photo]
if self.entities:
+7 -8
View File
@@ -16,10 +16,9 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram MessageEntity."""
from telegram import TelegramObject
from telegram import User, TelegramObject
class MessageEntity(TelegramObject):
@@ -32,24 +31,24 @@ class MessageEntity(TelegramObject):
offset (int):
length (int):
url (Optional[str]):
user (Optional[:class:`telegram.User`]):
"""
def __init__(self,
type,
offset,
length,
url=None):
def __init__(self, type, offset, length, **kwargs):
# Required
self.type = type
self.offset = offset
self.length = length
# Optionals
self.url = url
self.url = kwargs.get('url')
self.user = kwargs.get('user')
@staticmethod
def de_json(data):
data = super(MessageEntity, MessageEntity).de_json(data)
data['user'] = User.de_json(data.get('user'))
return MessageEntity(**data)
@staticmethod
-1
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a logging NullHandler."""
import logging
-1
View File
@@ -17,7 +17,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
Message Parse Modes."""
+3 -10
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram PhotoSize."""
from telegram import TelegramObject
@@ -41,11 +40,7 @@ class PhotoSize(TelegramObject):
file_size (Optional[int]):
"""
def __init__(self,
file_id,
width,
height,
**kwargs):
def __init__(self, file_id, width, height, **kwargs):
# Required
self.file_id = str(file_id)
self.width = int(width)
@@ -56,10 +51,8 @@ class PhotoSize(TelegramObject):
def __eq__(self, other):
if not isinstance(other, self.__class__):
return False
return (self.file_id == other.file_id and
self.width == other.width and
self.height == other.height and
self.file_size == other.file_size)
return (self.file_id == other.file_id and self.width == other.width
and self.height == other.height and self.file_size == other.file_size)
@staticmethod
def de_json(data):
+1 -4
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
ReplyKeyboardHide."""
@@ -38,9 +37,7 @@ class ReplyKeyboardHide(ReplyMarkup):
selective (Optional[bool]):
"""
def __init__(self,
hide_keyboard=True,
**kwargs):
def __init__(self, hide_keyboard=True, **kwargs):
# Required
self.hide_keyboard = bool(hide_keyboard)
# Optionals
+3 -7
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
ReplyKeyboardMarkup."""
@@ -42,9 +41,7 @@ class ReplyKeyboardMarkup(ReplyMarkup):
selective (Optional[bool]):
"""
def __init__(self,
keyboard,
**kwargs):
def __init__(self, keyboard, **kwargs):
# Required
self.keyboard = keyboard
# Optionals
@@ -64,8 +61,7 @@ class ReplyKeyboardMarkup(ReplyMarkup):
if not data:
return None
data['keyboard'] = [KeyboardButton.de_list(keyboard) for keyboard in
data['keyboard']]
data['keyboard'] = [KeyboardButton.de_list(keyboard) for keyboard in data['keyboard']]
return ReplyKeyboardMarkup(**data)
@@ -79,6 +75,6 @@ class ReplyKeyboardMarkup(ReplyMarkup):
if hasattr(button, 'to_dict'):
r.append(button.to_dict()) # telegram.KeyboardButton
else:
r.append(button) # str
r.append(button) # str
data['keyboard'].append(r)
return data
-1
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""Base class for Telegram ReplyMarkup Objects."""
from telegram import TelegramObject
+4 -6
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Sticker."""
from telegram import PhotoSize, TelegramObject
@@ -30,6 +29,7 @@ class Sticker(TelegramObject):
width (int):
height (int):
thumb (:class:`telegram.PhotoSize`):
emoji (str):
file_size (int):
Args:
@@ -40,20 +40,18 @@ class Sticker(TelegramObject):
Keyword Args:
thumb (Optional[:class:`telegram.PhotoSize`]):
emoji (Optional[str]):
file_size (Optional[int]):
"""
def __init__(self,
file_id,
width,
height,
**kwargs):
def __init__(self, file_id, width, height, **kwargs):
# Required
self.file_id = str(file_id)
self.width = int(width)
self.height = int(height)
# Optionals
self.thumb = kwargs.get('thumb')
self.emoji = kwargs.get('emoji', '')
self.file_size = int(kwargs.get('file_size', 0))
@staticmethod
+8 -10
View File
@@ -16,11 +16,9 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Update."""
from telegram import (Message, TelegramObject, InlineQuery,
ChosenInlineResult, CallbackQuery)
from telegram import (Message, TelegramObject, InlineQuery, ChosenInlineResult, CallbackQuery)
class Update(TelegramObject):
@@ -29,6 +27,7 @@ class Update(TelegramObject):
Attributes:
update_id (int):
message (:class:`telegram.Message`):
edited_message (:class:`telegram.Message`):
inline_query (:class:`telegram.InlineQuery`):
chosen_inline_result (:class:`telegram.ChosenInlineResult`):
callback_query (:class:`telegram.CallbackQuery`):
@@ -39,18 +38,18 @@ class Update(TelegramObject):
Keyword Args:
message (Optional[:class:`telegram.Message`]):
edited_message (Optional[:class:`telegram.Message`]):
inline_query (Optional[:class:`telegram.InlineQuery`]):
chosen_inline_result (Optional[:class:`telegram.ChosenInlineResult`])
callback_query (Optional[:class:`telegram.CallbackQuery`]):
"""
def __init__(self,
update_id,
**kwargs):
def __init__(self, update_id, **kwargs):
# Required
self.update_id = int(update_id)
# Optionals
self.message = kwargs.get('message')
self.edited_message = kwargs.get('edited_message')
self.inline_query = kwargs.get('inline_query')
self.chosen_inline_result = kwargs.get('chosen_inline_result')
self.callback_query = kwargs.get('callback_query')
@@ -68,10 +67,9 @@ class Update(TelegramObject):
return None
data['message'] = Message.de_json(data.get('message'))
data['edited_message'] = Message.de_json(data.get('edited_message'))
data['inline_query'] = InlineQuery.de_json(data.get('inline_query'))
data['chosen_inline_result'] = \
ChosenInlineResult.de_json(data.get('chosen_inline_result'))
data['callback_query'] = \
CallbackQuery.de_json(data.get('callback_query'))
data['chosen_inline_result'] = ChosenInlineResult.de_json(data.get('chosen_inline_result'))
data['callback_query'] = CallbackQuery.de_json(data.get('callback_query'))
return Update(**data)
+1 -5
View File
@@ -17,7 +17,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram User."""
from telegram import TelegramObject
@@ -44,10 +43,7 @@ class User(TelegramObject):
username (Optional[str]):
"""
def __init__(self,
id,
first_name,
**kwargs):
def __init__(self, id, first_name, **kwargs):
# Required
self.id = int(id)
self.first_name = first_name
+1 -4
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
UserProfilePhotos."""
@@ -35,9 +34,7 @@ class UserProfilePhotos(TelegramObject):
photos (List[List[:class:`telegram.PhotoSize`]]):
"""
def __init__(self,
total_count,
photos):
def __init__(self, total_count, photos, **kwargs):
# Required
self.total_count = int(total_count)
self.photos = photos
+3 -52
View File
@@ -1,53 +1,4 @@
#!/usr/bin/env python
from .deprecate import deprecate
from telegram.contrib.botan import Botan as Bo
import logging
from telegram import NullHandler
try:
from urllib.request import urlopen, Request
from urllib.parse import quote
from urllib.error import URLError, HTTPError
except ImportError:
from urllib2 import urlopen, Request
from urllib import quote
from urllib2 import URLError, HTTPError
logging.getLogger(__name__).addHandler(NullHandler())
class Botan(object):
"""This class helps to send incoming events in your botan analytics account.
See more: https://github.com/botanio/sdk#botan-sdk"""
token = ''
url_template = 'https://api.botan.io/track?token={token}' \
'&uid={uid}&name={name}&src=python-telegram-bot'
def __init__(self, token):
self.token = token
self.logger = logging.getLogger(__name__)
def track(self, message, event_name='event'):
try:
uid = message.chat_id
except AttributeError:
self.logger.warn('No chat_id in message')
return False
data = message.to_json()
try:
url = self.url_template.format(token=str(self.token),
uid=str(uid),
name=quote(event_name))
request = Request(url,
data=data.encode(),
headers={'Content-Type': 'application/json'})
urlopen(request)
return True
except HTTPError as error:
self.logger.warn('Botan track error ' +
str(error.code) +
':' + error.read().decode('utf-8'))
return False
except URLError as error:
self.logger.warn('Botan track error ' + str(error.reason))
return False
Botan = deprecate(Bo, 'telegram.utils.botan', 'telegram.contrib.botan')
@@ -16,15 +16,16 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module facilitates the deprecation of functions"""
"""This module contains functions to validate function arguments"""
from telegram.error import InvalidToken
import warnings
def validate_token(token):
"""a very basic validation on token"""
left, sep, _right = token.partition(':')
if (not sep) or (not left.isdigit()) or (len(left) < 3):
raise InvalidToken()
return token
def deprecate(func, old, new):
"""Warn users invoking old to switch to the new function."""
def f(*args, **kwargs):
warnings.warn("{0} is being deprecated, please use {1} from now on".format(old, new))
return func(*args, **kwargs)
return f
+86 -65
View File
@@ -16,32 +16,58 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains methods to make POST and GET requests"""
import functools
import json
import socket
from ssl import SSLError
import logging
from future.moves.http.client import HTTPException
from future.moves.urllib.error import HTTPError, URLError
from future.moves.urllib.request import urlopen, urlretrieve, Request
import certifi
import urllib3
from urllib3.connection import HTTPConnection
from telegram import (InputFile, TelegramError)
from telegram.error import Unauthorized, NetworkError, TimedOut
from telegram.error import Unauthorized, NetworkError, TimedOut, BadRequest
_CON_POOL = None
""":type: urllib3.PoolManager"""
CON_POOL_SIZE = 1
logging.getLogger('urllib3').setLevel(logging.WARNING)
def _get_con_pool():
global _CON_POOL
if _CON_POOL is not None:
return _CON_POOL
_CON_POOL = urllib3.PoolManager(maxsize=CON_POOL_SIZE,
cert_reqs='CERT_REQUIRED',
ca_certs=certifi.where(),
socket_options=HTTPConnection.default_socket_options + [
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
])
return _CON_POOL
def is_con_pool_initialized():
return _CON_POOL is not None
def stop_con_pool():
global _CON_POOL
if _CON_POOL is not None:
_CON_POOL.clear()
_CON_POOL = None
def _parse(json_data):
"""Try and parse the JSON returned from Telegram and return an empty
dictionary if there is any error.
Args:
url:
urllib.urlopen object
"""Try and parse the JSON returned from Telegram.
Returns:
A JSON parsed as Python dict with results.
dict: A JSON parsed as Python dict with results - on error this dict will be empty.
"""
decoded_s = json_data.decode('utf-8')
try:
@@ -55,50 +81,49 @@ def _parse(json_data):
return data['result']
def _try_except_req(func):
"""Decorator for requests to handle known exceptions"""
@functools.wraps(func)
def decorator(*args, **kwargs):
try:
return func(*args, **kwargs)
def _request_wrapper(*args, **kwargs):
"""Wraps urllib3 request for handling known exceptions.
except HTTPError as error:
# `HTTPError` inherits from `URLError` so `HTTPError` handling must
# come first.
errcode = error.getcode()
Args:
args: unnamed arguments, passed to urllib3 request.
kwargs: keyword arguments, passed tp urllib3 request.
if errcode in (401, 403):
raise Unauthorized()
elif errcode == 502:
raise NetworkError('Bad Gateway')
Returns:
str: A non-parsed JSON text.
try:
message = _parse(error.read())
except ValueError:
message = 'Unknown HTTPError {0}'.format(error.getcode())
Raises:
TelegramError
raise NetworkError('{0} ({1})'.format(message, errcode))
"""
except URLError as error:
raise NetworkError('URLError: {0}'.format(error.reason))
try:
resp = _get_con_pool().request(*args, **kwargs)
except urllib3.exceptions.TimeoutError as error:
raise TimedOut()
except urllib3.exceptions.HTTPError as error:
# HTTPError must come last as its the base urllib3 exception class
# TODO: do something smart here; for now just raise NetworkError
raise NetworkError('urllib3 HTTPError {0}'.format(error))
except (SSLError, socket.timeout) as error:
err_s = str(error)
if 'operation timed out' in err_s:
raise TimedOut()
if 200 <= resp.status <= 299:
# 200-299 range are HTTP success statuses
return resp.data
raise NetworkError(err_s)
try:
message = _parse(resp.data)
except ValueError:
raise NetworkError('Unknown HTTPError {0}'.format(resp.status))
except HTTPException as error:
raise NetworkError('HTTPException: {0!r}'.format(error))
except socket.error as error:
raise NetworkError('socket.error: {0!r}'.format(error))
return decorator
if resp.status in (401, 403):
raise Unauthorized()
elif resp.status == 400:
raise BadRequest(repr(message))
elif resp.status == 502:
raise NetworkError('Bad Gateway')
else:
raise NetworkError('{0} ({1})'.format(message, resp.status))
@_try_except_req
def get(url):
"""Request an URL.
Args:
@@ -107,16 +132,14 @@ def get(url):
Returns:
A JSON object.
"""
result = urlopen(url).read()
result = _request_wrapper('GET', url)
return _parse(result)
@_try_except_req
def post(url,
data,
timeout=None):
def post(url, data, timeout=None):
"""Request an URL.
Args:
url:
@@ -142,22 +165,18 @@ def post(url,
if InputFile.is_inputfile(data):
data = InputFile(data)
request = Request(url,
data=data.to_form(),
headers=data.headers)
result = _request_wrapper('POST', url, body=data.to_form(), headers=data.headers)
else:
data = json.dumps(data)
request = Request(url,
data=data.encode(),
headers={'Content-Type': 'application/json'})
result = _request_wrapper('POST',
url,
body=data.encode(),
headers={'Content-Type': 'application/json'})
result = urlopen(request, **urlopen_kwargs).read()
return _parse(result)
@_try_except_req
def download(url,
filename):
def download(url, filename):
"""Download a file by its URL.
Args:
url:
@@ -165,6 +184,8 @@ def download(url,
filename:
The filename within the path to download the file.
"""
urlretrieve(url, filename)
"""
buf = _request_wrapper('GET', url)
with open(filename, 'wb') as fobj:
fobj.write(buf)
+5 -9
View File
@@ -9,7 +9,6 @@ try:
except ImportError:
import http.server as BaseHTTPServer
logging.getLogger(__name__).addHandler(NullHandler())
@@ -21,10 +20,9 @@ class _InvalidPost(Exception):
class WebhookServer(BaseHTTPServer.HTTPServer, object):
def __init__(self, server_address, RequestHandlerClass, update_queue,
webhook_path):
super(WebhookServer, self).__init__(server_address,
RequestHandlerClass)
def __init__(self, server_address, RequestHandlerClass, update_queue, webhook_path):
super(WebhookServer, self).__init__(server_address, RequestHandlerClass)
self.logger = logging.getLogger(__name__)
self.update_queue = update_queue
self.webhook_path = webhook_path
@@ -85,13 +83,11 @@ class WebhookHandler(BaseHTTPServer.BaseHTTPRequestHandler, object):
self.logger.debug('Webhook received data: ' + json_string)
update = Update.de_json(json.loads(json_string))
self.logger.debug('Received Update with ID %d on Webhook' %
update.update_id)
self.logger.debug('Received Update with ID %d on Webhook' % update.update_id)
self.server.update_queue.put(update)
def _validate_post(self):
if not (self.path == self.server.webhook_path and
'content-type' in self.headers and
if not (self.path == self.server.webhook_path and 'content-type' in self.headers and
self.headers['content-type'] == 'application/json'):
raise _InvalidPost(403)
+1 -6
View File
@@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Venue."""
from telegram import TelegramObject, Location
@@ -33,11 +32,7 @@ class Venue(TelegramObject):
foursquare_id (Optional[str]):
"""
def __init__(self,
location,
title,
address,
foursquare_id=None):
def __init__(self, location, title, address, foursquare_id=None, **kwargs):
# Required
self.location = location
self.title = title

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