Compare commits

...

107 Commits

Author SHA1 Message Date
Pieter Schutz d9f77d6ee1 Bump to v10.0.1 2018-03-05 12:46:19 +01:00
Eldinnie 698a91427a Fix conversationhandler timeout (#1032)
* Fix conversationhandler

As found by @nmlorg and described in #1031

closes #1031

* adding another test and definitely finish conversationhandler

It seems another problem was when the job executed the timeout, it wasn;t removed from `self.conversation_timeouts` which made it still fail because job would be present in the handler dict, although it was already disabled.
This should fix it properly.
2018-03-05 12:18:40 +01:00
Jannes Höke 5956aae235 Add missing docs utils (#912)
* add documentation for telegram.utils.promise and .request

* improve documentation for telegram.utils.promise and .request

* add missing 's' to new_chat_member(s) in all docstrings

* fix docs for `set_chat_photo`

[CI skip]
2018-03-05 12:17:56 +01:00
Noam Meltzer 0811f566a2 Bump version to v10.0.0
[ci skip]
2018-03-03 01:23:08 +02:00
Noam Meltzer 69acd2b3bd Fix documentation build to include CHANGES.rst
Use forward slashes instead of the Windows backward slashes.
2018-03-03 01:23:08 +02:00
Noam Meltzer a6bf456645 Updater improvements (#1018)
- Refactor bootstrap phase to be resilient for network errors
 - Retry bootstrap phase indefinitely (by default) on network errors
 - Improved logs
 - Improved unitests for polling updater

fixes #605
2018-03-02 23:11:16 +02:00
Evgen 811369d1a0 Added conversation timeout in ConversationHandler (#895) 2018-03-01 11:34:47 +02:00
Paul Larsen b67ea7a691 CommandHandler - ignore strings in entities and "/" followed by whitespace (#1020) 2018-03-01 10:11:16 +02:00
Noam Meltzer f6332d45a8 Improved File.download (#1019)
- File.download_as_bytearray - new method to get a d/led file as bytearray
   This is much more convenient and straight forward than using a file
   object.

 - File.download(): Now returns a meaningful return value

 - File.download*(): New and/or better unitests
2018-03-01 10:10:04 +02:00
Eldinnie b275031a16 Add Message caption html/markdown methods (#1013)
Closes #1010
2018-02-22 17:38:54 +02:00
Eldinnie a9a503b9c3 Increase timeouts on creation of fixtures in tests (#1015) 2018-02-22 15:13:57 +02:00
Noam Meltzer 3762792cca Remove deprecated code (#1012)
* JobQueue: Remove deprecated prevent_autostart & put()

* Bot, Updater: Remove deprecated network_delay

* Remove deprecated Message.new_chat_member

closes #795
2018-02-22 14:17:06 +02:00
Eldinnie 36a93e0636 remove codeclimate (#1014)
* remove codeclimate

* Remove codecov PR comments
2018-02-19 21:30:28 +01:00
Noam Meltzer baec1bb554 flake8 revamp
- Use pre-commit v1.2.0 flake8
 - Make sure we're running flake8 on examples and tests
 - Fix what had to be fixed
2018-02-19 12:41:38 +02:00
Noam Meltzer 746ae0caf3 Get jobs by name (#1011) 2018-02-19 10:36:40 +02:00
Eldinnie 0bed087542 remove unnecessary to_dict() (#834)
We have some objects that have exactly the same to_dict() method, only specifying that `from_user` should be `from` in the `data`-dict. I refractored this logic to `TelegramObject` and removed those to_dicts() from the code.
2018-02-19 10:31:38 +02:00
Unknown efea0361c5 Fix attribute order error 2018-02-18 18:12:08 +01:00
Noam Meltzer 91ae35e26c updater.py: Better handling of timeouts during getUpdates (#1007)
TimedOut exception is an expected an normal event. To reduce noise and
make things more "fluent" we now:
 - Make sure that we don't sleep after the timeout but rather retry
immediately.
 - Log debug instead of error level.

Fixes #802
2018-02-18 17:50:38 +02:00
Jacob Bom ebcc40ae92 More instance methods (#963)
* Bot.get_file now allows passing a file in addition to file_id

* Add .get_file() to Audio, Document, PhotoSize, Sticker, Video, VideoNote and Voice

* Add .send_*() methods to User and Chat
2018-02-18 17:49:52 +02:00
Noam Meltzer dcb510e62c travis.yml: Removed pypy3.5
Travis keeps failing on weird INTERNAL ERROR messages and it just fails
the build. I don't see any other choice but to disable pypy3.5 tests.
2018-02-18 17:41:45 +02:00
Jannik c152d6583e Support v3.6 API (#1006)
* Added support for new field `telegram.Message.connected_message`

* Added support for new field `telegram.Message.connected_message`

* Added support for parse_mode in captions

* Added parse_mode parameter for captions in InlineQueryResult*

* Added supports_streaming parameter in telegram.Bot.send_video and telegram.InputMediaVideo
Fixed Docstrings for parse_mode in captions

* pypy3.5 unitests are now running with a new version due internal errors on travis.

closes #1005
2018-02-18 17:11:04 +02:00
Joscha Götzer 9338dc4697 Added utils.helpers.effective_message_type (#826) 2018-02-15 11:21:19 +02:00
Daniel Reed 063704c0b9 Explicitly check update.effective_chat in ConversationHandler.check_update (#959)
Fixes #927
2018-02-12 16:09:51 +02:00
Oleg 62e76f1fba bot.py: Add shortcut method reply_media_group (#994)
fixes #936
2018-02-12 15:51:18 +02:00
Joscha Götzer f0dfdfb203 bot.py: Add shortcut methods reply_{markdown,html} (#827) 2018-02-12 15:39:46 +02:00
Willem c19e464324 README.md: Fixed link to paymentbot example (#983)
[ci skip]
2018-02-12 15:32:35 +02:00
Eldinnie d5c9212f2e Add two arguments to send_invoice (#986)
Telegram silently added `send_phone_number_to_provider` and `send_email_to_provider` to the send_invoice method.
2018-02-10 17:54:09 +02:00
Noam Meltzer 8690ba256e webhookhandler: Fix exception thrown during error handling (#985)
BaseServer.handle_error() default behaviour is to print to stdout or
stderr (depends on the python version). In case that the file descriptor
is closed an additional exception will be raised, causing the webhook
thread to quit.

Fixes #970
2018-01-25 10:42:48 +02:00
Noam Meltzer d6b47da593 Update code of conduct (#980)
* Update code of conduct

* fix typo [ci skip]

* remove ending periods [ci skip]
2018-01-20 18:17:06 +01:00
Noam Meltzer ddf3a1fcad jobqueue: Thread safety fixes (#977)
- Fix JobQueue.jobs to obtain a lock on the internal queue object prior
   to iterating over it.

 - Rename JobQueue.queue to JobQueue._queue. This shouldn't be
   accessible by the user directly, but rather only with sanitized
   thread safe methods.

 - JobQueue.interval_seconds - access self.interval only once to avoid
   race conditions.

Fixes #968
2018-01-20 15:27:01 +02:00
Eldinnie 820f4e1d59 Fix user profile photo unitests (#967)
Telegram changed something again.
2018-01-09 17:56:43 +02:00
Mischa Krüger eb67c039f1 Updater: Issue INFO log upon received signal (#951)
Fixes #946
2018-01-09 17:54:07 +02:00
Jacob Bom 2ca7ff82ef Update headers to 2018 (#962)
Also add two missing headers.
[ci skip]
2018-01-04 17:16:06 +02:00
Noam Meltzer a2c04313d3 Fix documentation & coding style of User.name & User.full_name (#956)
- Use string `format` instead of dict comprehension.
 - Better documentation to signify the semantics difference between
   `name` and `full_name`.

 - Use string `format` instead of dict comprehension.
 - Better documentation to signify the semantics difference between
   `name` and `full_name`.

* Removed the NOTE and mentinoed the "@" prefix.
2017-12-30 22:58:32 +02:00
Eldinnie 0faa19726a wrong link to paymentbot (#942)
[CI skip]
2017-12-30 15:17:36 +02:00
graynk d347c0d45e User.full_name convinience property (#949)
Fixes #943
2017-12-30 15:13:06 +02:00
Daniel Reed 5c8470e552 Store bot in PreCheckoutQuery (#953)
Fixes #937
2017-12-30 14:48:38 +02:00
Eldin 3d4e0500bf Bump to 9.0.0 + changelog 2017-12-08 23:08:42 +01:00
Eldinnie 042d4bb2a4 add support for 3.5 api (#920)
* add support for 3.5 api

* removed "unused" import by accident

* Hardcoded values

Appearantly TG decided to change the size of a send image (again)

* test_official

* Improve coverage

* Finishing up

* spelling error

* pytest fixed tot < than 3.3 for python 3.3 support

* flake8

* rollback requirements

* as per CR

* object for provider_data

Make it possible to send an object that will be json-serialized for send_invoice + tests

* shorten error message

* using string_types
2017-12-08 22:38:59 +01:00
Eldinnie 1e22d570a3 Drop 3.3 from support (#930)
* Remove 3.3 from testsuite and setup.py

* add changelog to the docs

[CI skip]
2017-12-08 22:38:23 +01:00
Jacob Bom 2719c54899 Small changes to the examples readme (#893)
- Add paymentbot.py
- More docs links
2017-12-06 13:26:51 +01:00
Erjan Kalybek a5c8e98ee7 Fix sendPhoto method description (#919) 2017-12-06 13:24:34 +01:00
Eldinnie 40113bb3ad make codecov less aggressive (#929)
[CI skip]
2017-12-06 13:23:49 +01:00
Eldinnie e8fac19de3 codecov tweaking (#897)
Reduces the size and amount of info from the codecov bot on PR
2017-11-07 14:33:24 +01:00
Jacob Bom 820ae3b7d6 Create CODE_OF_CONDUCT.md (#892) 2017-10-26 19:57:38 +02:00
Noam Meltzer 4b3315db6f Fix race condition in dispatcher start/stop (#887)
fixes #881
2017-10-21 14:40:24 +03:00
Jannes Höke 3ed05991ad log error trace if there is no error handler registered (#694) 2017-10-21 12:35:11 +03:00
Simon Schürrle 38637ecf62 Update examples with consistent string formatting (#870)
* Use the modern string.format().
* Wherever logging is involved, let logging take care of the expansion.
2017-10-20 21:24:00 +03:00
Simon Schürrle bb5357a815 Fix typo in the docstring (#878)
Changed `Filters.status_update.new_chat_member` to `Filters.status_update.new_chat_members`
Thanks to @ajbvn for pointing it out.

[ci skip]
2017-10-18 22:00:17 +03:00
Rahiel Kasim f55018abd9 CHANGES.rst: fix some typo's (#874) 2017-10-15 16:59:41 +02:00
Jacob Bom 8df35fd53b Fix for crashes on 8.1 (#873)
* Make Commandhandler not crash on single char messages

* Bump release and update CHANGES.rst for 8.1.1

* No error on single / and test
2017-10-15 16:59:10 +02:00
Noam Meltzer 23774383dc Bump version & update CHANGES.rst for 8.1.0 release 2017-10-15 00:04:35 +03:00
Eldinnie 28680ac1d5 edited_updates also for channel_posts (#832) 2017-10-14 23:48:06 +03:00
Jannes Höke ec9b16ac7b Fix command not recognized if it is directly followed by a newline (#869)
fixes #868
2017-10-14 21:04:02 +03:00
Eldinnie bfad2fa1f3 support 3.4 API (#865) 2017-10-14 21:03:02 +03:00
Noam Meltzer 8a8b1215c8 Fix documentation of Request object initialization (for advanced users) (#866)
fixes #676
2017-10-12 00:39:09 +03:00
Eldinnie 2aa456d637 remove Bot._message_wrapper (#822)
Moved tho code to the @message decorator instead

fixes #752
2017-10-11 23:38:00 +03:00
Jannes Höke 445bcde686 Update README.rst 2017-10-10 13:09:11 +02:00
Rahiel Kasim 6facde9534 README: show AppVeyor logo in badge (#861) 2017-10-09 15:23:59 +02:00
Rahiel Kasim 046ec28361 README: add AppVeyor badge (closes #591) (#856) 2017-10-07 18:36:31 +02:00
Eldinnie 3d00ae25df Update .codeclimate.yml
Last edit :S
2017-09-25 21:42:17 +02:00
Eldinnie aeedcfb874 Update .codeclimate.yml 2017-09-25 21:38:47 +02:00
Eldinnie 6d7a61ae72 mistake in codeclimate 2017-09-25 21:37:53 +02:00
Eldinnie d75e0f6014 Stabilize Coverage (#846)
* test_error added

* ignore unstables for coverage
2017-09-25 20:58:10 +02:00
Jeff dbb3b16edc Add Job Class to docs (#845)
* fix job class missing from docs

* fix typo
2017-09-25 20:57:53 +02:00
Eldinnie 7c3bba42cc loosen codeclimate (#833)
Codeclimate's defaults are pretty strict. Our complexity is High for objects and bot methods so I decreased the RADON-setting to be a bit more forgiving.
Secondly I removed the warning for `similar` code leaving only `duplicate` code.
2017-09-25 20:57:06 +02:00
Eldinnie 8754980448 add utils.helpers to docs (#828) 2017-09-25 20:56:28 +02:00
Jeff 5614af1847 Docstring Fix for ext/filters.py (#830) 2017-09-15 23:56:16 +02:00
Marco Túlio Zuquim e362a5b59d Small documentation fix in user.py (#829) 2017-09-15 23:55:35 +02:00
Eldinnie dd20237f55 Enable appveyor (#823)
* modify tests and appveyor.yml

* modify test_meta

* Add pytest-cov to requirements

* appveyor.yaml adjustment

* another try

* oops

* skipping jobqueue tests on appveyor

appveyor is terrible at precise timings.

* adding reasons to skips

* modify test_official

* coverage trouble

* Make it look good

* appveyor.yml

* codecov flags

* oops

* update appveyor.yml

* shorten decorators

* removed redundant quotes
2017-09-14 17:54:00 +02:00
Eldinnie c5ab8930b9 Update appveyor.yml
small update
2017-09-09 23:53:49 +02:00
Jacob Bom f5b746f1ba Switch to codecov (#819)
* Switch to codecov

We've talked about it in the dev group for a while now, finally doing it

* Update coverage in readme

[ci skip]
2017-09-09 23:38:19 +02:00
Eldinnie 4fe4eea893 only run coverage for relevant test (#818)
* fix test troubles

* another try

* testing travis build?

* trying pytestmark
2017-09-09 23:37:07 +02:00
Yii Kuo Chong e39b6abe61 Small documentation fix (#815)
Fixes #799
2017-09-07 22:55:06 +03:00
Pieter Schutz 6012fa65c1 Bump to version 8.0 2017-09-01 10:38:15 +02:00
Pieter Schutz ef9dbacf7e Fix last issues for new release 2017-09-01 10:38:04 +02:00
Eldinnie e13cd58773 Fix sticker tests (#807)
* Fix sticker tests

Added flaky, timeout and xfails to the stickerset bot method tests.

* Make sure the first stickersettest worked before modifying in test 2

* some mor changes to make the deletion more stable
2017-09-01 08:47:34 +02:00
Jannes Höke 1f5311b473 catch exceptions in error handlerfor errors that happen during polling (2) (#810)
* catch exceptions in error handlerfor errors that happen during polling

* add tests for error handlers that raise exceptions
2017-09-01 08:46:21 +02:00
Eldinnie eae139d3e9 remove de_json() (#789)
* remove de_json()

* Remove or rename obsolete tests

* fixes according to rc
2017-09-01 08:45:22 +02:00
Eldinnie 160ffed5ad Remove Botan (#776) 2017-09-01 08:45:01 +02:00
Jacob Bom 8cda3f7a71 Turn on branch coverage (#790)
* Run pre commit hook tests on all platforms TEMPORARILY

* Use os.system instead of subprocess.call

* Turn on branch coverage

* Let's try this then

* More testing!

* Update test_meta.py
2017-09-01 08:44:45 +02:00
Jacob Bom d7929a8de4 Test UserProfilePhotos (#796) 2017-09-01 08:44:26 +02:00
Noam Meltzer e018445513 Documentation update to PEP (#797) 2017-09-01 08:43:08 +02:00
Eldinnie 4601eedf0f Show status_updates in docs (#798)
* Show status_updates in docs

* Show status_updates in docs
2017-09-01 08:41:15 +02:00
Eldinnie 6cd0c14c02 Show what parameter/attribute is missing in test_offical (#808)
* Show what parameter/attribute is missing in test_offical

* Only run test_official on py3.6

Down from 3.5 and 3.6
2017-09-01 08:40:52 +02:00
Jeff b6a0853896 Add Bot API 3.3 (#806)
* Bot API 3.3

* simpler helper methods (accept only custom names now)
attempt to fix circular import (not sure if its the correct way tho)
added helper methods into User object

* fix User objects in tests to contain is_bot required value

* fix User objects in tests to contain is_bot required value

* delete extra line that caused flake8 error

* fix swapped arguments
2017-09-01 08:40:05 +02:00
Noam Meltzer 16a49ec659 Remove DispatcherHandlerContinue + more unitests for dispatcher (#792)
The idea was nice, but it really complicated things for us and for the
user.
If a user wants to run more than one handler on an update, he can put
the handlers in different groups or he can have a single handler.
If a user wants to have multiple handlers in the same group which only
one of them should run on the update, he should use check_update().

Since we haven't released this code yet, there's no problem with
backward compatability.
2017-08-12 18:57:12 +03:00
Noam Meltzer ee34d57521 Warn on small con_pool_size during custom initalization of Updater (#793)
fixes #787
2017-08-12 16:45:38 +03:00
Jacob Bom 5d7c6ad541 Switch to pytest + required fixes to code (#788)
Required fixes:
 - CallbackQuery is now comparable.
 - Message.effective_attachment, Message.photo,
   Message.new_chat_members, Message.new_chat_photo &
   Game.text_entitties semantic fixes - when they are not defined,
   return an empty list.
 - Docstring fix to Update class.
2017-08-12 00:58:41 +03:00
Noam Meltzer 915cd64140 Effective attachment (#766)
fixes #746
2017-08-08 00:25:19 +03:00
Eldinnie 56f7d18853 bot.py semantic fixes (#774)
offset for get_user_profile_photo can be zero.
edit_message_caption should raise ValueError in compliance with other methods. Also changed to snake_case
edit_message_reply_markup was even weirder
2017-08-08 00:13:32 +03:00
Eldinnie b04869f36a docstring correction (#775)
[ci skip]
2017-08-08 00:11:44 +03:00
Eldinnie 70057a67c5 Regression fix for text_html & text_markdown (#777)
`text_html` & `text_markdown` reverted to the old semantics - URLs are not converted to hyperlinks.
To get the new behaviour there are matching `text_html_urled` & `text_markdown_urled` properties.
fixes #773
2017-08-08 00:10:48 +03:00
Eldinnie 8d4b484f7b Fix Game.to_dict() (#767)
`Game.to_dict()` fails if the `optional` parameter `test_entities` has it's default value `None`
This fixes that.
2017-08-05 00:02:51 +03:00
Jacob Bom 073bed3a01 Fix get_game_high_scores (#771) 2017-08-04 20:41:27 +03:00
Noam Meltzer 5ceb6f8f36 Docs fixes (#765)
* Clean warnings during build_sphinx

* fix documentation types
2017-08-02 05:56:07 +03:00
Ihor Polyakov b6ef30a454 Authors list (#758) 2017-08-01 23:10:07 +03:00
Ihor Polyakov 1b515a16d6 CONTRIBUTING.rst: Note about git behaviour (#763) 2017-08-01 23:09:15 +03:00
Jacob Bom b3069348ca Add rich comparison to CallbackQuery (#764)
We currently have no tests for it. Noted for #756 and #757.
2017-08-01 23:07:12 +03:00
Ihor Polyakov 454ab050c0 Decrease number of requests in tests (#755) 2017-08-01 00:16:44 +03:00
Eldinnie d19ae5084a Add RESTRICTED as constant to ChatMember (#761) 2017-07-31 23:37:00 +03:00
Ihor Polyakov 6aacde189e Flow control ability in Dispatcher (#738)
fixes #666
2017-07-29 20:15:43 +03:00
Noam Meltzer 5d3f5575ab Sane defaults for tcp socket options on linux (#754)
Currently only for linux as most of our userbase is there.

fixes #535
2017-07-29 14:30:27 +03:00
Noam Meltzer 13ab6d43d3 CHANGES.rst: Added 7.0.1 release notes.
[ci skip]
2017-07-28 21:22:48 +03:00
Eldinnie 2ba7505eaf Fix TypeError exception in RegexHandler (#751)
fixes #750
2017-07-28 20:47:42 +03:00
Eldinnie 5ed06df840 callback query docstring (#749)
showed from instead of from_user and was missing the informational note about it.

[ci skip]
2017-07-28 17:41:25 +03:00
Noam Meltzer eef1238d60 Comment out unitest test_send_contact_with_contact
Caused too many "RetryAfter: Flood control exceeded" errors.

[ci skip]
2017-07-28 17:39:21 +03:00
246 changed files with 12698 additions and 9195 deletions
-5
View File
@@ -1,5 +0,0 @@
languages:
Python: true
exclude_paths:
- "telegram/emoji.py"
- "tests/*"
-8
View File
@@ -1,8 +0,0 @@
[run]
source = telegram
omit = telegram/vendor/*
[report]
omit =
tests/
telegram/vendor/*
+33 -18
View File
@@ -1,10 +1,10 @@
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.
@@ -35,7 +35,7 @@ Setting things up
$ pre-commit install
Finding something to do
###################
#######################
If you already know what you'd like to work on, you can skip this section.
@@ -44,7 +44,7 @@ If you have an idea for something to do, first check if it's already been filed
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``.
@@ -89,7 +89,7 @@ Here's how to make a one-off code change.
.. code-block::
$ nosetests -v
$ pytest -v
- To actually make the commit (this will trigger tests for yapf, lint and pep8 automatically):
@@ -129,13 +129,19 @@ Here's how to make a one-off code change.
.. code-block:: bash
$ git checkout your-branch-name
$ git fetch upstream
$ git merge upstream/master
$ ...[fix the conflicts]...
$ ...[make sure the tests pass before committing]...
$ git commit -a
$ git push origin your-branch-name
$ 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
- If after merging you see local modified files in ``telegram/vendor/`` directory, that you didn't actually touch, that means you need to update submodules with this command:
.. code-block:: bash
$ git submodule update --init --recursive
- At the end, the reviewer will merge the pull request.
@@ -149,20 +155,29 @@ Here's how to make a one-off code change.
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
######################
Assert comparison order
#######################
- assertEqual method's arguments should be in ('actual', 'expected') order.
- assert statements should compare in **actual** == **expected** order.
For example (assuming ``test_call`` is the thing being tested):
.. code-block:: python
# GOOD
assert test_call() == 5
# BAD
assert 5 == test_call()
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
@@ -180,7 +195,7 @@ 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
+1
View File
@@ -40,6 +40,7 @@ htmlcov/
.coverage
.coverage.*
.cache
.pytest_cache
nosetests.xml
coverage.xml
*,cover
+3 -3
View File
@@ -6,10 +6,10 @@
args:
- --diff
- repo: git://github.com/pre-commit/pre-commit-hooks
sha: 78818b90cd694c29333ba54d38f9e60b6359ccfc
sha: v1.2.0
hooks:
- id: flake8
files: ^telegram/.*\.py$
exclude: ^(setup.py|docs/source/conf.py)$
- repo: git://github.com/pre-commit/mirrors-pylint
sha: v1.7.1
hooks:
@@ -17,4 +17,4 @@
files: ^telegram/.*\.py$
args:
- --errors-only
- --disable=no-name-in-module,import-error
- --disable=import-error
+9 -7
View File
@@ -1,12 +1,10 @@
language: python
python:
- "2.7"
- "3.3"
- "3.4"
- "3.5"
- "3.6"
- "pypy-5.7.1"
- "pypy3.5-5.8.0"
dist: trusty
sudo: false
@@ -18,18 +16,22 @@ branches:
cache:
directories:
- $HOME/.cache/pip
- $HOME/.pre-commit
before_cache:
- rm -f $HOME/.cache/pip/log/debug.log
- rm -f $HOME/.pre-commit/pre-commit.log
install:
- pip install coveralls
- pip install -U codecov pytest-cov
- pip install -U wheel
- pip install -r requirements.txt
- pip install -r requirements-dev.txt
- pip install -U -r requirements.txt
- pip install -U -r requirements-dev.txt
- if [[ $TRAVIS_PYTHON_VERSION != 'pypy'* ]]; then pip install ujson; fi
script:
- python travis.py
- pytest -v -m nocoverage
- pytest -v -m "not nocoverage" --cov
after_success:
coveralls
- coverage combine
- codecov -F Travis
+9 -3
View File
@@ -16,12 +16,13 @@ Contributors
The following wonderful people contributed directly or indirectly to this project:
- `Alateas <https://github.com/alateas>`_
- `Avanatiker <https://github.com/Avanatiker>`_
- `Anton Tagunov <https://github.com/anton-tagunov>`_
- `Avanatiker <https://github.com/Avanatiker>`_
- `Balduro <https://github.com/Balduro>`_
- `bimmlerd <https://github.com/bimmlerd>`_
- `d-qoi <https://github.com/d-qoi>`_
- `daimajia <https://github.com/daimajia>`_
- `Daniel Reed <https://github.com/nmlorg>`_
- `Eli Gao <https://github.com/eligao>`_
- `ErgoZ Riftbit Vaper <https://github.com/ergoz>`_
- `Eugene Lisitsky <https://github.com/lisitsky>`_
@@ -29,31 +30,36 @@ The following wonderful people contributed directly or indirectly to this projec
- `evgfilim1 <https://github.com/evgfilim1>`_
- `franciscod <https://github.com/franciscod>`_
- `Hugo Damer <https://github.com/HakimusGIT>`_
- `ihoru <https://github.com/ihoru>`_
- `Jacob Bom <https://github.com/bomjacob>`_
- `JASON0916 <https://github.com/JASON0916>`_
- `jeffffc <https://github.com/jeffffc>`_
- `Jelle Besseling <https://github.com/pingiun>`_
- `jh0ker <https://github.com/jh0ker>`_
- `jlmadurga <https://github.com/jlmadurga>`_
- `John Yong <https://github.com/whipermr5>`_
- `Joscha Götzer <https://github.com/Rostgnom>`_
- `jossalgon <https://github.com/jossalgon>`_
- `JRoot3D <https://github.com/JRoot3D>`_
- `jlmadurga <https://github.com/jlmadurga>`_
- `Kjwon15 <https://github.com/kjwon15>`_
- `Li-aung Yip <https://github.com/LiaungYip>`_
- `macrojames <https://github.com/macrojames>`_
- `Michael Elovskikh <https://github.com/wronglink>`_
- `Mischa Krüger <https://github.com/Makman2>`_
- `naveenvhegde <https://github.com/naveenvhegde>`_
- `neurrone <https://github.com/neurrone>`_
- `njittam <https://github.com/njittam>`_
- `Noam Meltzer <https://github.com/tsnoam>`_
- `Oleg Shlyazhko <https://github.com/ollmer>`_
- `Oleg Sushchenko <https://github.com/feuillemorte>`_
- `overquota <https://github.com/overquota>`_
- `Patrick Hofmann <https://github.com/PH89>`_
- `Paul Larsen <https://github.com/PaulSonOfLars>`_
- `Pieter Schutz <https://github.com/eldinnie>`_
- `Rahiel Kasim <https://github.com/rahiel>`_
- `Joscha Götzer <https://github.com/Rostgnom>`_
- `Sascha <https://github.com/saschalalala>`_
- `Shelomentsev D <https://github.com/shelomentsevd>`_
- `Simon Schürrle <https://github.com/SitiSchu>`_
- `sooyhwang <https://github.com/sooyhwang>`_
- `thodnev <https://github.com/thodnev>`_
- `Valentijn <https://github.com/Faalentijn>`_
+174
View File
@@ -1,6 +1,180 @@
=======
Changes
=======
**2018-03-05**
*Released 10.0.1*
Fixes:
- Fix conversationhandler timeout (PR `#1032`_)
- Add missing docs utils (PR `#912`_)
.. _`#1032`: https://github.com/python-telegram-bot/python-telegram-bot/pull/826
.. _`#912`: https://github.com/python-telegram-bot/python-telegram-bot/pull/826
**2018-03-02**
*Released 10.0.0*
Non backward compatabile changes and changed defaults
- JobQueue: Remove deprecated prevent_autostart & put() (PR `#1012`_)
- Bot, Updater: Remove deprecated network_delay (PR `#1012`_)
- Remove deprecated Message.new_chat_member (PR `#1012`_)
- Retry bootstrap phase indefinitely (by default) on network errors (PR `#1018`_)
New Features
- Support v3.6 API (PR `#1006`_)
- User.full_name convinience property (PR `#949`_)
- Add `send_phone_number_to_provider` and `send_email_to_provider` arguments to send_invoice (PR `#986`_)
- Bot: Add shortcut methods reply_{markdown,html} (PR `#827`_)
- Bot: Add shortcut method reply_media_group (PR `#994`_)
- Added utils.helpers.effective_message_type (PR `#826`_)
- Bot.get_file now allows passing a file in addition to file_id (PR `#963`_)
- Add .get_file() to Audio, Document, PhotoSize, Sticker, Video, VideoNote and Voice (PR `#963`_)
- Add .send_*() methods to User and Chat (PR `#963`_)
- Get jobs by name (PR `#1011`_)
- Add Message caption html/markdown methods (PR `#1013`_)
- File.download_as_bytearray - new method to get a d/led file as bytearray (PR `#1019`_)
- File.download(): Now returns a meaningful return value (PR `#1019`_)
- Added conversation timeout in ConversationHandler (PR `#895`_)
Changes
- Store bot in PreCheckoutQuery (PR `#953`_)
- Updater: Issue INFO log upon received signal (PR `#951`_)
- JobQueue: Thread safety fixes (PR `#977`_)
- WebhookHandler: Fix exception thrown during error handling (PR `#985`_)
- Explicitly check update.effective_chat in ConversationHandler.check_update (PR `#959`_)
- Updater: Better handling of timeouts during get_updates (PR `#1007`_)
- Remove unnecessary to_dict() (PR `#834`_)
- CommandHandler - ignore strings in entities and "/" followed by whitespace (PR `#1020`_)
- Documentation & style fixes (PR `#942`_, PR `#956`_, PR `#962`_, PR `#980`_, PR `#983`_)
.. _`#826`: https://github.com/python-telegram-bot/python-telegram-bot/pull/826
.. _`#827`: https://github.com/python-telegram-bot/python-telegram-bot/pull/827
.. _`#834`: https://github.com/python-telegram-bot/python-telegram-bot/pull/834
.. _`#895`: https://github.com/python-telegram-bot/python-telegram-bot/pull/895
.. _`#942`: https://github.com/python-telegram-bot/python-telegram-bot/pull/942
.. _`#949`: https://github.com/python-telegram-bot/python-telegram-bot/pull/949
.. _`#951`: https://github.com/python-telegram-bot/python-telegram-bot/pull/951
.. _`#956`: https://github.com/python-telegram-bot/python-telegram-bot/pull/956
.. _`#953`: https://github.com/python-telegram-bot/python-telegram-bot/pull/953
.. _`#962`: https://github.com/python-telegram-bot/python-telegram-bot/pull/962
.. _`#959`: https://github.com/python-telegram-bot/python-telegram-bot/pull/959
.. _`#963`: https://github.com/python-telegram-bot/python-telegram-bot/pull/963
.. _`#977`: https://github.com/python-telegram-bot/python-telegram-bot/pull/977
.. _`#980`: https://github.com/python-telegram-bot/python-telegram-bot/pull/980
.. _`#983`: https://github.com/python-telegram-bot/python-telegram-bot/pull/983
.. _`#985`: https://github.com/python-telegram-bot/python-telegram-bot/pull/985
.. _`#986`: https://github.com/python-telegram-bot/python-telegram-bot/pull/986
.. _`#994`: https://github.com/python-telegram-bot/python-telegram-bot/pull/994
.. _`#1006`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1006
.. _`#1007`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1007
.. _`#1011`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1011
.. _`#1012`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1012
.. _`#1013`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1013
.. _`#1018`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1018
.. _`#1019`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1019
.. _`#1020`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1020
**2017-12-08**
*Released 9.0.0*
Breaking changes (possibly)
- Drop support for python 3.3 (PR `#930`_)
New Features
- Support Bot API 3.5 (PR `#920`_)
Changes
- Fix race condition in dispatcher start/stop (`#887`_)
- Log error trace if there is no error handler registered (`#694`_)
- Update examples with consistent string formatting (`#870`_)
- Various changes and improvements to the docs.
.. _`#920`: https://github.com/python-telegram-bot/python-telegram-bot/pull/920
.. _`#930`: https://github.com/python-telegram-bot/python-telegram-bot/pull/930
.. _`#887`: https://github.com/python-telegram-bot/python-telegram-bot/pull/887
.. _`#694`: https://github.com/python-telegram-bot/python-telegram-bot/pull/694
.. _`#870`: https://github.com/python-telegram-bot/python-telegram-bot/pull/870
**2017-10-15**
*Released 8.1.1*
- Fix Commandhandler crashing on single character messages (PR `#873`_).
.. _`#873`: https://github.com/python-telegram-bot/python-telegram-bot/pull/871
**2017-10-14**
*Released 8.1.0*
New features
- Support Bot API 3.4 (PR `#865`_).
Changes
- MessageHandler & RegexHandler now consider channel_updates.
- Fix command not recognized if it is directly followed by a newline (PR `#869`_).
- Removed Bot._message_wrapper (PR `#822`_).
- Unitests are now also running on AppVeyor (Windows VM).
- Various unitest improvements.
- Documentation fixes.
.. _`#822`: https://github.com/python-telegram-bot/python-telegram-bot/pull/822
.. _`#865`: https://github.com/python-telegram-bot/python-telegram-bot/pull/865
.. _`#869`: https://github.com/python-telegram-bot/python-telegram-bot/pull/869
**2017-09-01**
*Released 8.0.0*
New features
- Fully support Bot Api 3.3 (PR `#806`_).
- DispatcherHandlerStop (`see docs`_).
- Regression fix for text_html & text_markdown (PR `#777`_).
- Added effective_attachment to message (PR `#766`_).
Non backward compatible changes
- Removed Botan support from the library (PR `#776`_).
- Fully support Bot Api 3.3 (PR `#806`_).
- Remove de_json() (PR `#789`_).
Changes
- Sane defaults for tcp socket options on linux (PR `#754`_).
- Add RESTRICTED as constant to ChatMember (PR `#761`_).
- Add rich comparison to CallbackQuery (PR `#764`_).
- Fix get_game_high_scores (PR `#771`_).
- Warn on small con_pool_size during custom initalization of Updater (PR `#793`_).
- Catch exceptions in error handlerfor errors that happen during polling (PR `#810`_).
- For testing we switched to pytest (PR `#788`_).
- Lots of small improvements to our tests and documentation.
.. _`see docs`: http://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.dispatcher.html#telegram.ext.Dispatcher.add_handler
.. _`#777`: https://github.com/python-telegram-bot/python-telegram-bot/pull/777
.. _`#806`: https://github.com/python-telegram-bot/python-telegram-bot/pull/806
.. _`#766`: https://github.com/python-telegram-bot/python-telegram-bot/pull/766
.. _`#776`: https://github.com/python-telegram-bot/python-telegram-bot/pull/776
.. _`#789`: https://github.com/python-telegram-bot/python-telegram-bot/pull/789
.. _`#754`: https://github.com/python-telegram-bot/python-telegram-bot/pull/754
.. _`#761`: https://github.com/python-telegram-bot/python-telegram-bot/pull/761
.. _`#764`: https://github.com/python-telegram-bot/python-telegram-bot/pull/764
.. _`#771`: https://github.com/python-telegram-bot/python-telegram-bot/pull/771
.. _`#788`: https://github.com/python-telegram-bot/python-telegram-bot/pull/788
.. _`#793`: https://github.com/python-telegram-bot/python-telegram-bot/pull/793
.. _`#810`: https://github.com/python-telegram-bot/python-telegram-bot/pull/810
**2017-07-28**
*Released 7.0.1*
- Fix TypeError exception in RegexHandler (PR #751).
- Small documentation fix (PR #749).
**2017-07-25**
*Released 7.0.0*
+47
View File
@@ -0,0 +1,47 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Publication of any content supporting, justifying or otherwise affiliating with terror and/or hate towards others
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at devs@python-telegram-bot.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
+4 -4
View File
@@ -2,7 +2,7 @@
.PHONY: clean pep257 pep8 yapf lint test install
PYLINT := pylint
NOSETESTS := nosetests
PYTEST := pytest
PEP257 := pep257
PEP8 := flake8
YAPF := yapf
@@ -29,7 +29,7 @@ lint:
$(PYLINT) -E telegram --disable=no-name-in-module,import-error
test:
$(NOSETESTS) -v
$(PYTEST) -v
install:
$(PIP) install -r requirements.txt -r requirements-dev.txt
@@ -41,11 +41,11 @@ help:
@echo "- pep8 Check style with flake8"
@echo "- lint Check style with pylint"
@echo "- yapf Check style with yapf"
@echo "- test Run tests"
@echo "- test Run tests using pytest"
@echo
@echo "Available variables:"
@echo "- PYLINT default: $(PYLINT)"
@echo "- NOSETESTS default: $(NOSETESTS)"
@echo "- PYTEST default: $(PYTEST)"
@echo "- PEP257 default: $(PEP257)"
@echo "- PEP8 default: $(PEP8)"
@echo "- YAPF default: $(YAPF)"
+12 -7
View File
@@ -31,13 +31,14 @@ We have made you a wrapper you can't refuse
:target: https://travis-ci.org/python-telegram-bot/python-telegram-bot
:alt: Travis CI Status
.. image:: https://codeclimate.com/github/python-telegram-bot/python-telegram-bot/badges/gpa.svg
:target: https://codeclimate.com/github/python-telegram-bot/python-telegram-bot
:alt: Code Climate
.. image:: https://img.shields.io/appveyor/ci/Eldinnie/python-telegram-bot/master.svg?logo=appveyor
:target: https://ci.appveyor.com/project/Eldinnie/python-telegram-bot
:alt: AppVeyor CI Status
.. 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:: https://codecov.io/gh/python-telegram-bot/python-telegram-bot/branch/master/graph/badge.svg
:target: https://codecov.io/gh/python-telegram-bot/python-telegram-bot
:alt: Code coverage
.. image:: http://isitmaintained.com/badge/resolution/python-telegram-bot/python-telegram-bot.svg
:target: http://isitmaintained.com/project/python-telegram-bot/python-telegram-bot
@@ -47,6 +48,10 @@ We have made you a wrapper you can't refuse
:target: https://telegram.me/pythontelegrambotgroup
:alt: Telegram Group
.. image:: https://img.shields.io/badge/IRC-Channel-blue.svg
:target: https://webchat.freenode.net/?channels=##python-telegram-bot
:alt: IRC Bridge
=================
Table of contents
=================
@@ -88,7 +93,7 @@ make the development of bots easy and straightforward. These classes are contain
Telegram API support
====================
As of **23. July 2017**, all types and methods of the Telegram Bot API 3.2 are supported.
All types and methods of the Telegram Bot API 3.4 are supported.
==========
Installing
+18 -10
View File
@@ -7,25 +7,33 @@ environment:
# isn't covered by this document) at the time of writing.
- PYTHON: "C:\\Python27"
- PYTHON: "C:\\Python33"
- PYTHON: "C:\\Python34"
- PYTHON: "C:\\Python35"
- PYTHON: "C:\\Python36"
branches:
only:
- master
skip_branch_with_pr: true
max_jobs: 1
install:
# We need wheel installed to build wheels
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- "git submodule update --init --recursive"
- "%PYTHON%\\python.exe -m pip install -U wheel"
- "%PYTHON%\\python.exe -m pip install -r requirements.txt"
- "%PYTHON%\\python.exe -m pip install -r requirements-dev.txt"
# Check that we have the expected version and architecture for Python
- "python --version"
# We need wheel installed to build wheels
- "pip install -U codecov pytest-cov"
- "pip install -U wheel"
- "pip install -r requirements.txt"
- "pip install -r requirements-dev.txt"
build: off
cache: C:\Users\appveyor\pip\wheels
test_script:
- "%python%\\Scripts\\nosetests -v --with-flaky --no-flaky-report tests"
- "pytest -m \"not nocoverage\" --cov --cov-report xml:coverage.xml"
after_test:
# This step builds your wheels.
- "%PYTHON%\\python.exe setup.py bdist_wheel"
- "codecov -f coverage.xml -F Appveyor"
+1
View File
@@ -0,0 +1 @@
comment: false
View File
+3 -3
View File
@@ -50,7 +50,7 @@ master_doc = 'index'
# General information about the project.
project = u'Python Telegram Bot'
copyright = u'2015-2017, Leandro Toledo'
copyright = u'2015-2018, Leandro Toledo'
author = u'Leandro Toledo'
# The version info for the project you're documenting, acts as replacement for
@@ -58,9 +58,9 @@ author = u'Leandro Toledo'
# built documents.
#
# The short X.Y version.
version = '7.0' # telegram.__version__[:3]
version = '10.0' # telegram.__version__[:3]
# The full version, including alpha/beta/rc tags.
release = '7.0.0' # telegram.__version__
release = '10.0.1' # telegram.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
+4
View File
@@ -12,6 +12,10 @@ objects in the package reflect the types as defined by the `telegram bot api <ht
.. toctree::
telegram
Changelog
---------
.. include:: ../../CHANGES.rst
Indices and tables
==================
-7
View File
@@ -1,7 +0,0 @@
telegram.contrib.Botan
======================
.. autoclass:: telegram.contrib.Botan
:members:
:undoc-members:
:show-inheritance:
-18
View File
@@ -1,18 +0,0 @@
telegram.contrib package
========================
Submodules
----------
.. toctree::
telegram.contrib.botan
Module contents
---------------
.. automodule:: telegram.contrib
:members:
:undoc-members:
:show-inheritance:
:noindex:
+6
View File
@@ -0,0 +1,6 @@
telegram.ext.Job
=====================
.. autoclass:: telegram.ext.Job
:members:
:show-inheritance:
+1
View File
@@ -6,6 +6,7 @@ telegram.ext package
telegram.ext.updater
telegram.ext.dispatcher
telegram.ext.filters
telegram.ext.job
telegram.ext.jobqueue
telegram.ext.messagequeue
telegram.ext.delayqueue
+6
View File
@@ -0,0 +1,6 @@
telegram.InputMedia
===================
.. autoclass:: telegram.InputMedia
:members:
:show-inheritance:
+6
View File
@@ -0,0 +1,6 @@
telegram.InputMediaPhoto
========================
.. autoclass:: telegram.InputMediaPhoto
:members:
:show-inheritance:
+6
View File
@@ -0,0 +1,6 @@
telegram.InputMediaVideo
========================
.. autoclass:: telegram.InputMediaVideo
:members:
:show-inheritance:
+4 -1
View File
@@ -3,8 +3,8 @@ telegram package
.. toctree::
telegram.contrib
telegram.ext
telegram.utils
telegram.audio
telegram.bot
telegram.callbackquery
@@ -21,6 +21,9 @@ telegram package
telegram.inlinekeyboardbutton
telegram.inlinekeyboardmarkup
telegram.inputfile
telegram.inputmedia
telegram.inputmediaphoto
telegram.inputmediavideo
telegram.keyboardbutton
telegram.location
telegram.message
+6
View File
@@ -0,0 +1,6 @@
telegram.utils.helpers Module
=============================
.. automodule:: telegram.utils.helpers
:members:
:show-inheritance:
+6
View File
@@ -0,0 +1,6 @@
telegram.utils.promise.Promise
==============================
.. autoclass:: telegram.utils.promise.Promise
:members:
:show-inheritance:
+6
View File
@@ -0,0 +1,6 @@
telegram.utils.request.Request
==============================
.. autoclass:: telegram.utils.request.Request
:members:
:show-inheritance:
+8
View File
@@ -0,0 +1,8 @@
telegram.utils package
======================
.. toctree::
telegram.utils.helpers
telegram.utils.promise
telegram.utils.request
+4 -1
View File
@@ -1,6 +1,6 @@
# Examples
The examples in this folder are small bots meant to show you how a bot that is written with `python-telegram-bot` looks like. Some bots focus on one specific aspect of the Telegram Bot API while others focus on one of the mechanics of this library. Except for the `echobot.py` example, they all use the high-level framework this library provides with the `telegram.ext` submodule.
The examples in this folder are small bots meant to show you how a bot that is written with `python-telegram-bot` looks like. Some bots focus on one specific aspect of the Telegram Bot API while others focus on one of the mechanics of this library. Except for the [`echobot.py`](#pure-api) example, they all use the high-level framework this library provides with the [`telegram.ext`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.html) submodule.
All examples are licensed under the [CC0 License](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/LICENSE.txt) and are therefore fully dedicated to the public domain. You can use them as the base for your own bots without worrying about copyrights.
@@ -22,5 +22,8 @@ This example sheds some light on inline keyboards, callback queries and message
### [`inlinebot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinebot.py)
A basic example of an [inline bot](https://core.telegram.org/bots/inline). Don't forget to enable inline mode with [@BotFather](https://telegram.me/BotFather).
### [`paymentbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/paymentbot.py)
A basic example of a bot that can accept payments. Don't forget to enable and configure payments with [@BotFather](https://telegram.me/BotFather).
## Pure API
The [`echobot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot.py) example uses only the pure, "bare-metal" API wrapper.
+10 -9
View File
@@ -46,7 +46,7 @@ def start(bot, update):
def gender(bot, update):
user = update.message.from_user
logger.info("Gender of %s: %s" % (user.first_name, update.message.text))
logger.info("Gender of %s: %s", user.first_name, update.message.text)
update.message.reply_text('I see! Please send me a photo of yourself, '
'so I know what you look like, or send /skip if you don\'t want to.',
reply_markup=ReplyKeyboardRemove())
@@ -58,7 +58,7 @@ def photo(bot, update):
user = update.message.from_user
photo_file = bot.get_file(update.message.photo[-1].file_id)
photo_file.download('user_photo.jpg')
logger.info("Photo of %s: %s" % (user.first_name, 'user_photo.jpg'))
logger.info("Photo of %s: %s", user.first_name, 'user_photo.jpg')
update.message.reply_text('Gorgeous! Now, send me your location please, '
'or send /skip if you don\'t want to.')
@@ -67,7 +67,7 @@ def photo(bot, update):
def skip_photo(bot, update):
user = update.message.from_user
logger.info("User %s did not send a photo." % user.first_name)
logger.info("User %s did not send a photo.", user.first_name)
update.message.reply_text('I bet you look great! Now, send me your location please, '
'or send /skip.')
@@ -77,8 +77,8 @@ def skip_photo(bot, update):
def location(bot, update):
user = update.message.from_user
user_location = update.message.location
logger.info("Location of %s: %f / %f"
% (user.first_name, user_location.latitude, user_location.longitude))
logger.info("Location of %s: %f / %f", user.first_name, user_location.latitude,
user_location.longitude)
update.message.reply_text('Maybe I can visit you sometime! '
'At last, tell me something about yourself.')
@@ -87,7 +87,7 @@ def location(bot, update):
def skip_location(bot, update):
user = update.message.from_user
logger.info("User %s did not send a location." % user.first_name)
logger.info("User %s did not send a location.", user.first_name)
update.message.reply_text('You seem a bit paranoid! '
'At last, tell me something about yourself.')
@@ -96,7 +96,7 @@ def skip_location(bot, update):
def bio(bot, update):
user = update.message.from_user
logger.info("Bio of %s: %s" % (user.first_name, update.message.text))
logger.info("Bio of %s: %s", user.first_name, update.message.text)
update.message.reply_text('Thank you! I hope we can talk again some day.')
return ConversationHandler.END
@@ -104,7 +104,7 @@ def bio(bot, update):
def cancel(bot, update):
user = update.message.from_user
logger.info("User %s canceled the conversation." % user.first_name)
logger.info("User %s canceled the conversation.", user.first_name)
update.message.reply_text('Bye! I hope we can talk again some day.',
reply_markup=ReplyKeyboardRemove())
@@ -112,7 +112,8 @@ def cancel(bot, update):
def error(bot, update, error):
logger.warn('Update "%s" caused error "%s"' % (update, error))
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error)
def main():
+10 -9
View File
@@ -41,7 +41,7 @@ def facts_to_str(user_data):
facts = list()
for key, value in user_data.items():
facts.append('%s - %s' % (key, value))
facts.append('{} - {}'.format(key, value))
return "\n".join(facts).join(['\n', '\n'])
@@ -58,7 +58,8 @@ def start(bot, update):
def regular_choice(bot, update, user_data):
text = update.message.text
user_data['choice'] = text
update.message.reply_text('Your %s? Yes, I would love to hear about that!' % text.lower())
update.message.reply_text(
'Your {}? Yes, I would love to hear about that!'.format(text.lower()))
return TYPING_REPLY
@@ -77,10 +78,9 @@ def received_information(bot, update, user_data):
del user_data['choice']
update.message.reply_text("Neat! Just so you know, this is what you already told me:"
"%s"
"You can tell me more, or change your opinion on something."
% facts_to_str(user_data),
reply_markup=markup)
"{}"
"You can tell me more, or change your opinion on something.".format(
facts_to_str(user_data)), reply_markup=markup)
return CHOOSING
@@ -90,15 +90,16 @@ def done(bot, update, user_data):
del user_data['choice']
update.message.reply_text("I learned these facts about you:"
"%s"
"Until next time!" % facts_to_str(user_data))
"{}"
"Until next time!".format(facts_to_str(user_data)))
user_data.clear()
return ConversationHandler.END
def error(bot, update, error):
logger.warn('Update "%s" caused error "%s"' % (update, error))
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error)
def main():
+9 -4
View File
@@ -1,9 +1,11 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# 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.
"""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
@@ -12,7 +14,9 @@ from time import sleep
update_id = None
def main():
"""Run the bot."""
global update_id
# Telegram Bot Authorization Token
bot = telegram.Bot('TOKEN')
@@ -37,6 +41,7 @@ def main():
def echo(bot):
"""Echo the message the user sent."""
global update_id
# Request updates after the last update_id
for update in bot.get_updates(offset=update_id, timeout=10):
+11 -5
View File
@@ -1,9 +1,10 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Simple Bot to reply to Telegram messages
# This program is dedicated to the public domain under the CC0 license.
"""
"""Simple Bot to reply to Telegram messages.
This program is dedicated to the public domain under the CC0 license.
This Bot uses the Updater class to handle the bot.
First, a few handler functions are defined. Then, those functions are passed to
@@ -29,22 +30,27 @@ logger = logging.getLogger(__name__)
# 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):
"""Send a message when the command /start is issued."""
update.message.reply_text('Hi!')
def help(bot, update):
"""Send a message when the command /help is issued."""
update.message.reply_text('Help!')
def echo(bot, update):
"""Echo the user message."""
update.message.reply_text(update.message.text)
def error(bot, update, error):
logger.warn('Update "%s" caused error "%s"' % (update, error))
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error)
def main():
"""Start the bot."""
# Create the EventHandler and pass it your bot's token.
updater = Updater("TOKEN")
+29 -30
View File
@@ -1,9 +1,10 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Simple Bot to reply to Telegram messages
# This program is dedicated to the public domain under the CC0 license.
"""
"""Simple Bot to reply to Telegram messages.
This program is dedicated to the public domain under the CC0 license.
This Bot uses the Updater class to handle the bot.
First, a few handler functions are defined. Then, those functions are passed to
@@ -17,7 +18,7 @@ bot.
"""
from uuid import uuid4
import re
from telegram.utils.helpers import escape_markdown
from telegram import InlineQueryResultArticle, ParseMode, \
InputTextMessageContent
@@ -34,45 +35,43 @@ logger = logging.getLogger(__name__)
# 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):
"""Send a message when the command /start is issued."""
update.message.reply_text('Hi!')
def help(bot, update):
"""Send a message when the command /help is issued."""
update.message.reply_text('Help!')
def escape_markdown(text):
"""Helper function to escape telegram markup symbols"""
escape_chars = '\*_`\['
return re.sub(r'([%s])' % escape_chars, r'\\\1', text)
def inlinequery(bot, update):
"""Handle the inline query."""
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="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 = [
InlineQueryResultArticle(
id=uuid4(),
title="Caps",
input_message_content=InputTextMessageContent(
query.upper())),
InlineQueryResultArticle(
id=uuid4(),
title="Bold",
input_message_content=InputTextMessageContent(
"*{}*".format(escape_markdown(query)),
parse_mode=ParseMode.MARKDOWN)),
InlineQueryResultArticle(
id=uuid4(),
title="Italic",
input_message_content=InputTextMessageContent(
"_{}_".format(escape_markdown(query)),
parse_mode=ParseMode.MARKDOWN))]
update.inline_query.answer(results)
def error(bot, update, error):
logger.warning('Update "%s" caused error "%s"' % (update, error))
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error)
def main():
+23 -16
View File
@@ -1,15 +1,16 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Basic example for a bot that uses inline keyboards.
# This program is dedicated to the public domain under the CC0 license.
"""Basic example for a bot that uses inline keyboards.
# This program is dedicated to the public domain under the CC0 license.
"""
import logging
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
def start(bot, update):
@@ -26,7 +27,7 @@ def start(bot, update):
def button(bot, update):
query = update.callback_query
bot.edit_message_text(text="Selected option: %s" % query.data,
bot.edit_message_text(text="Selected option: {}".format(query.data),
chat_id=query.message.chat_id,
message_id=query.message.message_id)
@@ -36,20 +37,26 @@ def help(bot, update):
def error(bot, update, error):
logging.warning('Update "%s" caused error "%s"' % (update, error))
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error)
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN")
def main():
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN")
updater.dispatcher.add_handler(CommandHandler('start', start))
updater.dispatcher.add_handler(CallbackQueryHandler(button))
updater.dispatcher.add_handler(CommandHandler('help', help))
updater.dispatcher.add_error_handler(error)
updater.dispatcher.add_handler(CommandHandler('start', start))
updater.dispatcher.add_handler(CallbackQueryHandler(button))
updater.dispatcher.add_handler(CommandHandler('help', help))
updater.dispatcher.add_error_handler(error)
# Start the Bot
updater.start_polling()
# Start the Bot
updater.start_polling()
# Run the bot until the user presses Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT
updater.idle()
# Run the bot until the user presses Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT
updater.idle()
if __name__ == '__main__':
main()
+10 -6
View File
@@ -1,8 +1,10 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Basic example for a bot that can receive payment from user.
# This program is dedicated to the public domain under the CC0 license.
"""Basic example for a bot that can receive payment from user.
This program is dedicated to the public domain under the CC0 license.
"""
from telegram import (LabeledPrice, ShippingOption)
from telegram.ext import (Updater, CommandHandler, MessageHandler,
@@ -10,13 +12,15 @@ from telegram.ext import (Updater, CommandHandler, MessageHandler,
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__)
def error(bot, update, error):
logger.warn('Update "%s" caused error "%s"' % (update, error))
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error)
def start_callback(bot, update):
@@ -31,7 +35,7 @@ def start_with_shipping_callback(bot, update):
description = "Payment Example using python-telegram-bot"
# select a payload just for you to recognize its the donation from your bot
payload = "Custom-Payload"
# get your provider_token at @botfather, see https://core.telegram.org/bots/payments#getting-a-token
# In order to get a provider_token see https://core.telegram.org/bots/payments#getting-a-token
provider_token = "PROVIDER_TOKEN"
start_parameter = "test-payment"
currency = "USD"
@@ -55,7 +59,7 @@ def start_without_shipping_callback(bot, update):
description = "Payment Example using python-telegram-bot"
# select a payload just for you to recognize its the donation from your bot
payload = "Custom-Payload"
# get your provider_token at @botfather, see https://core.telegram.org/bots/payments#getting-a-token
# In order to get a provider_token see https://core.telegram.org/bots/payments#getting-a-token
provider_token = "PROVIDER_TOKEN"
start_parameter = "test-payment"
currency = "USD"
+14 -11
View File
@@ -1,9 +1,11 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Simple Bot to send timed Telegram messages
"""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.
@@ -17,7 +19,7 @@ Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
from telegram.ext import Updater, CommandHandler, Job
from telegram.ext import Updater, CommandHandler
import logging
# Enable logging
@@ -34,12 +36,12 @@ def start(bot, update):
def alarm(bot, job):
"""Function to send the alarm message"""
"""Send the alarm message."""
bot.send_message(job.context, text='Beep!')
def set(bot, update, args, job_queue, chat_data):
"""Adds a job to the queue"""
def set_timer(bot, update, args, job_queue, chat_data):
"""Add a job to the queue."""
chat_id = update.message.chat_id
try:
# args[0] should contain the time for the timer in seconds
@@ -59,8 +61,7 @@ def set(bot, update, args, job_queue, chat_data):
def unset(bot, update, chat_data):
"""Removes the job if the user changed their mind"""
"""Remove the job if the user changed their mind."""
if 'job' not in chat_data:
update.message.reply_text('You have no active timer')
return
@@ -73,10 +74,12 @@ def unset(bot, update, chat_data):
def error(bot, update, error):
logger.warning('Update "%s" caused error "%s"' % (update, error))
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error)
def main():
"""Run bot."""
updater = Updater("TOKEN")
# Get the dispatcher to register handlers
@@ -85,7 +88,7 @@ def main():
# on different commands - answer in Telegram
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", start))
dp.add_handler(CommandHandler("set", set,
dp.add_handler(CommandHandler("set", set_timer,
pass_args=True,
pass_job_queue=True,
pass_chat_data=True))
+3 -3
View File
@@ -1,10 +1,10 @@
flake8
nose
pep257
pylint
flaky
yapf
pre-commit
pre-commit-hooks
beautifulsoup4
rednose
pytest
pytest-catchlog
pytest-timeout
+18
View File
@@ -17,3 +17,21 @@ ignore = W503
based_on_style = google
split_before_logical_operator = True
column_limit = 99
[tool:pytest]
testpaths = tests
addopts = --no-success-flaky-report -rsxX
filterwarnings =
error
ignore::DeprecationWarning
[coverage:run]
branch = True
source = telegram
parallel = True
concurrency = thread, multiprocessing
omit =
tests/
telegram/__main__.py
telegram/vendor/*
-1
View File
@@ -53,7 +53,6 @@ with codecs.open('README.rst', 'r', 'utf-8') as fd:
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6'
+6 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -96,6 +96,9 @@ from .constants import (MAX_MESSAGE_LENGTH, MAX_CAPTION_LENGTH, SUPPORTED_WEBHOO
MAX_FILESIZE_DOWNLOAD, MAX_FILESIZE_UPLOAD,
MAX_MESSAGES_PER_SECOND_PER_CHAT, MAX_MESSAGES_PER_SECOND,
MAX_MESSAGES_PER_MINUTE_PER_GROUP)
from .files.inputmedia import InputMedia
from .files.inputmediavideo import InputMediaVideo
from .files.inputmediaphoto import InputMediaPhoto
from .version import __version__ # flake8: noqa
__author__ = 'devs@python-telegram-bot.org'
@@ -121,5 +124,6 @@ __all__ = [
'MAX_MESSAGES_PER_SECOND', 'MAX_MESSAGES_PER_MINUTE_PER_GROUP', 'WebhookInfo', 'Animation',
'Game', 'GameHighScore', 'VideoNote', 'LabeledPrice', 'SuccessfulPayment', 'ShippingOption',
'ShippingAddress', 'PreCheckoutQuery', 'OrderInfo', 'Invoice', 'ShippingQuery', 'ChatPhoto',
'StickerSet', 'MaskPosition', 'CallbackGame'
'StickerSet', 'MaskPosition', 'CallbackGame', 'InputMedia', 'InputMediaPhoto',
'InputMediaVideo'
]
+18
View File
@@ -1,3 +1,21 @@
# !/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# 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/].
import sys
import certifi
+5 -4
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -27,9 +27,7 @@ from abc import ABCMeta
class TelegramObject(object):
"""
Base class for most telegram objects.
"""
"""Base class for most telegram objects."""
__metaclass__ = ABCMeta
_id_attrs = ()
@@ -53,6 +51,7 @@ class TelegramObject(object):
"""
Returns:
:obj:`str`
"""
return json.dumps(self.to_dict())
@@ -71,6 +70,8 @@ class TelegramObject(object):
else:
data[key] = value
if data.get('from_user'):
data['from'] = data.pop('from_user', None)
return data
def __eq__(self, other):
+430 -176
View File
File diff suppressed because it is too large Load Diff
+16 -23
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -30,11 +30,12 @@ class CallbackQuery(TelegramObject):
inline mode), the field :attr:`inline_message_id` will be present.
Note:
Exactly one of the fields :attr:`data` or :attr:`game_short_name` will be present.
* In Python `from` is a reserved word, use `from_user` instead.
* Exactly one of the fields :attr:`data` or :attr:`game_short_name` will be present.
Attributes:
id (:obj:`str`): Unique identifier for this query.
from (:class:`telegram.User`): Sender.
from_user (:class:`telegram.User`): Sender.
message (:class:`telegram.Message`): Optional. Message with the callback button that
originated the query.
inline_message_id (:obj:`str`): Optional. Identifier of the message sent via the bot in
@@ -46,7 +47,7 @@ class CallbackQuery(TelegramObject):
Args:
id (:obj:`str`): Unique identifier for this query.
from (:class:`telegram.User`): Sender.
from_user (:class:`telegram.User`): Sender.
message (:class:`telegram.Message`, optional): Message with the callback button that
originated the query. Note that message content and message date will not be available
if the message is too old.
@@ -65,6 +66,7 @@ class CallbackQuery(TelegramObject):
until you call :attr:`answer`. It is, therefore, necessary to react
by calling :attr:`telegram.Bot.answer_callback_query` even if no notification to the user
is needed (e.g., without specifying any of the optional parameters).
"""
def __init__(self,
@@ -89,6 +91,8 @@ class CallbackQuery(TelegramObject):
self.bot = bot
self._id_attrs = (self.id,)
@classmethod
def de_json(cls, data, bot):
if not data:
@@ -101,28 +105,19 @@ class CallbackQuery(TelegramObject):
return cls(bot=bot, **data)
def to_dict(self):
data = super(CallbackQuery, self).to_dict()
# Required
data['from'] = data.pop('from_user', None)
return data
def answer(self, *args, **kwargs):
"""
Shortcut for::
"""Shortcut for::
bot.answer_callback_query(update.callback_query.id, *args, **kwargs)
Returns:
:obj:`bool`: On success, ``True`` is returned.
"""
"""
return self.bot.answerCallbackQuery(self.id, *args, **kwargs)
def edit_message_text(self, *args, **kwargs):
"""
Shortcut for either::
"""Shortcut for either::
bot.edit_message_text(chat_id=update.callback_query.message.chat_id,
message_id=update.callback_query.message.message_id,
@@ -136,8 +131,8 @@ class CallbackQuery(TelegramObject):
Returns:
:class:`telegram.Message`: On success, if edited message is sent by the bot, the
edited Message is returned, otherwise ``True`` is returned.
"""
"""
if self.inline_message_id:
return self.bot.edit_message_text(
inline_message_id=self.inline_message_id, *args, **kwargs)
@@ -146,8 +141,7 @@ class CallbackQuery(TelegramObject):
chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs)
def edit_message_caption(self, *args, **kwargs):
"""
Shortcut for either::
"""Shortcut for either::
bot.edit_message_caption(chat_id=update.callback_query.message.chat_id,
message_id=update.callback_query.message.message_id,
@@ -161,8 +155,8 @@ class CallbackQuery(TelegramObject):
Returns:
:class:`telegram.Message`: On success, if edited message is sent by the bot, the
edited Message is returned, otherwise ``True`` is returned.
"""
"""
if self.inline_message_id:
return self.bot.edit_message_caption(
inline_message_id=self.inline_message_id, *args, **kwargs)
@@ -171,8 +165,7 @@ class CallbackQuery(TelegramObject):
chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs)
def edit_message_reply_markup(self, *args, **kwargs):
"""
Shortcut for either::
"""Shortcut for either::
bot.edit_message_replyMarkup(chat_id=update.callback_query.message.chat_id,
message_id=update.callback_query.message.message_id,
@@ -186,8 +179,8 @@ class CallbackQuery(TelegramObject):
Returns:
:class:`telegram.Message`: On success, if edited message is sent by the bot, the
edited Message is returned, otherwise ``True`` is returned.
"""
"""
if self.inline_message_id:
return self.bot.edit_message_reply_markup(
inline_message_id=self.inline_message_id, *args, **kwargs)
+141 -24
View File
@@ -2,7 +2,7 @@
# pylint: disable=C0103,W0622
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -23,8 +23,7 @@ from telegram import TelegramObject, ChatPhoto
class Chat(TelegramObject):
"""
This object represents a chat.
"""This object represents a chat.
Attributes:
id (:obj:`int`): Unique identifier for this chat.
@@ -37,6 +36,11 @@ class Chat(TelegramObject):
photo (:class:`telegram.ChatPhoto`): Optional. Chat photo.
description (:obj:`str`): Optional. Description, for supergroups and channel chats.
invite_link (:obj:`str`): Optional. Chat invite link, for supergroups and channel chats.
pinned_message (:class:`telegram.Message`): Optional. Pinned message, for supergroups.
Returned only in get_chat.
sticker_set_name (:obj:`str`): Optional. For supergroups, name of Group sticker set.
can_set_sticker_set (:obj:`bool`): Optional. ``True``, if the bot can change group the
sticker set.
Args:
id (:obj:`int`): Unique identifier for this chat. This number may be greater than 32 bits
@@ -57,7 +61,13 @@ class Chat(TelegramObject):
Returned only in get_chat.
invite_link (:obj:`str`, optional): Chat invite link, for supergroups and channel chats.
Returned only in get_chat.
pinned_message (:class:`telegram.Message`, optional): Pinned message, for supergroups.
Returned only in get_chat.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
sticker_set_name (:obj:`str`, optional): For supergroups, name of Group sticker set.
Returned only in get_chat.
can_set_sticker_set (:obj:`bool`, optional): ``True``, if the bot can change group the
sticker set. Returned only in get_chat.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
@@ -83,6 +93,9 @@ class Chat(TelegramObject):
photo=None,
description=None,
invite_link=None,
pinned_message=None,
sticker_set_name=None,
can_set_sticker_set=None,
**kwargs):
# Required
self.id = int(id)
@@ -96,6 +109,9 @@ class Chat(TelegramObject):
self.photo = photo
self.description = description
self.invite_link = invite_link
self.pinned_message = pinned_message
self.sticker_set_name = sticker_set_name
self.can_set_sticker_set = can_set_sticker_set
self.bot = bot
self._id_attrs = (self.id,)
@@ -106,36 +122,36 @@ class Chat(TelegramObject):
return None
data['photo'] = ChatPhoto.de_json(data.get('photo'), bot)
from telegram import Message
data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot)
return cls(bot=bot, **data)
def send_action(self, *args, **kwargs):
"""
Shortcut for::
"""Shortcut for::
bot.send_chat_action(update.message.chat.id, *args, **kwargs)
Returns:
:obj:`bool`: If the action was sent successfully.
"""
return self.bot.send_chat_action(self.id, *args, **kwargs)
def leave(self, *args, **kwargs):
"""
Shortcut for::
"""Shortcut for::
bot.leave_chat(update.message.chat.id, *args, **kwargs)
Returns:
:obj:`bool` If the action was sent successfully.
"""
"""
return self.bot.leave_chat(self.id, *args, **kwargs)
def get_administrators(self, *args, **kwargs):
"""
Shortcut for::
"""Shortcut for::
bot.get_chat_administrators(update.message.chat.id, *args, **kwargs)
@@ -144,39 +160,36 @@ class Chat(TelegramObject):
:class:`telegram.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
"""
"""
return self.bot.get_chat_administrators(self.id, *args, **kwargs)
def get_members_count(self, *args, **kwargs):
"""
Shortcut for::
"""Shortcut for::
bot.get_chat_members_count(update.message.chat.id, *args, **kwargs)
Returns:
:obj:`int`
"""
"""
return self.bot.get_chat_members_count(self.id, *args, **kwargs)
def get_member(self, *args, **kwargs):
"""
Shortcut for::
"""Shortcut for::
bot.get_chat_member(update.message.chat.id, *args, **kwargs)
Returns:
:class:`telegram.ChatMember`
"""
"""
return self.bot.get_chat_member(self.id, *args, **kwargs)
def kick_member(self, *args, **kwargs):
"""
Shortcut for::
"""Shortcut for::
bot.kick_chat_member(update.message.chat.id, *args, **kwargs)
bot.kick_chat_member(update.message.chat.id, *args, **kwargs)
Returns:
:obj:`bool`: If the action was sent succesfully.
@@ -185,17 +198,121 @@ class Chat(TelegramObject):
This method will only work if the `All Members Are Admins` setting is off in the
target group. Otherwise members may only be removed by the group's creator or by the
member that added them.
"""
"""
return self.bot.kick_chat_member(self.id, *args, **kwargs)
def unban_member(self, *args, **kwargs):
"""
Shortcut for::
"""Shortcut for::
bot.unban_chat_member(update.message.chat.id, *args, **kwargs)
bot.unban_chat_member(update.message.chat.id, *args, **kwargs)
Returns:
:obj:`bool`: If the action was sent successfully.
"""
return self.bot.unban_chat_member(self.id, *args, **kwargs)
def send_message(self, *args, **kwargs):
"""Shortcut for::
bot.send_message(Chat.chat_id, *args, **kwargs)
Where Chat is the current instance.
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_message(chat_id=self.id, *args, **kwargs)
def send_photo(self, *args, **kwargs):
"""Shortcut for::
bot.send_photo(Chat.chat_id, *args, **kwargs)
Where Chat is the current instance.
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_photo(chat_id=self.id, *args, **kwargs)
def send_audio(self, *args, **kwargs):
"""Shortcut for::
bot.send_audio(Chat.chat_id, *args, **kwargs)
Where Chat is the current instance.
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_audio(chat_id=self.id, *args, **kwargs)
def send_document(self, *args, **kwargs):
"""Shortcut for::
bot.send_document(Chat.chat_id, *args, **kwargs)
Where Chat is the current instance.
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_document(chat_id=self.id, *args, **kwargs)
def send_sticker(self, *args, **kwargs):
"""Shortcut for::
bot.send_sticker(Chat.chat_id, *args, **kwargs)
Where Chat is the current instance.
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_sticker(chat_id=self.id, *args, **kwargs)
def send_video(self, *args, **kwargs):
"""Shortcut for::
bot.send_video(Chat.chat_id, *args, **kwargs)
Where Chat is the current instance.
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_video(chat_id=self.id, *args, **kwargs)
def send_video_note(self, *args, **kwargs):
"""Shortcut for::
bot.send_video_note(Chat.chat_id, *args, **kwargs)
Where Chat is the current instance.
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_video_note(chat_id=self.id, *args, **kwargs)
def send_voice(self, *args, **kwargs):
"""Shortcut for::
bot.send_voice(Chat.chat_id, *args, **kwargs)
Where Chat is the current instance.
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
"""
return self.bot.send_voice(chat_id=self.id, *args, **kwargs)
+2 -4
View File
@@ -2,7 +2,7 @@
# pylint: disable=R0903
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -21,9 +21,7 @@
class ChatAction(object):
"""
Helper class to provide constants for different chatactions
"""
"""Helper class to provide constants for different chatactions."""
FIND_LOCATION = 'find_location'
""":obj:`str`: 'find_location'"""
+6 -5
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -23,8 +23,7 @@ from telegram.utils.helpers import to_timestamp, from_timestamp
class ChatMember(TelegramObject):
"""
This object contains information about one member of the chat.
"""This object contains information about one member of the chat.
Attributes:
user (:class:`telegram.User`): Information about the user.
@@ -59,7 +58,7 @@ class ChatMember(TelegramObject):
Args:
user (:class:`telegram.User`): Information about the user.
status (:obj:`str`): The member's status in the chat. Can be 'creator', 'administrator',
'member', 'left' or 'kicked'.
'member', 'restricted', 'left' or 'kicked'.
until_date (:class:`datetime.datetime`, optional): Restricted and kicked only. Date when
restrictions will be lifted for this user.
can_be_edited (:obj:`bool`, optional): Administrators only. True, if the bot is allowed to
@@ -91,8 +90,8 @@ class ChatMember(TelegramObject):
send animations, games, stickers and use inline bots, implies can_send_media_messages.
can_add_web_page_previews (:obj:`bool`, optional): Restricted only. True, if user may add
web page previews to his messages, implies can_send_media_messages.
"""
"""
ADMINISTRATOR = 'administrator'
""":obj:`str`: 'administrator'"""
CREATOR = 'creator'
@@ -103,6 +102,8 @@ class ChatMember(TelegramObject):
""":obj:`str`: 'left'"""
MEMBER = 'member'
""":obj:`str`: 'member'"""
RESTRICTED = 'restricted'
""":obj:`str`: 'restricted'"""
def __init__(self, user, status, until_date=None, can_be_edited=None,
can_change_info=None, can_post_messages=None, can_edit_messages=None,
+2 -9
View File
@@ -2,7 +2,7 @@
# pylint: disable=R0902,R0912,R0913
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -47,6 +47,7 @@ class ChosenInlineResult(TelegramObject):
callback queries and can be used to edit the message.
query (:obj:`str`): The query that was used to obtain the result.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self,
@@ -78,11 +79,3 @@ class ChosenInlineResult(TelegramObject):
data['location'] = Location.de_json(data.get('location'), bot)
return cls(**data)
def to_dict(self):
data = super(ChosenInlineResult, self).to_dict()
# Required
data['from'] = data.pop('from_user', None)
return data
+3 -3
View File
@@ -1,5 +1,5 @@
# python-telegram-bot - a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# by the python-telegram-bot contributors <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -14,8 +14,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""
Constants in the Telegram network.
"""Constants in the Telegram network.
The following constants were extracted from the
`Telegram Bots FAQ <https://core.telegram.org/bots/faq>`_.
@@ -37,6 +36,7 @@ The following constant have been found by experimentation:
Attributes:
MAX_MESSAGE_ENTITIES (:obj:`int`): 100 (Beyond this cap telegram will simply ignore further
formatting styles)
"""
MAX_MESSAGE_LENGTH = 4096
-3
View File
@@ -1,3 +0,0 @@
from .botan import Botan
__all__ = ['Botan']
-43
View File
@@ -1,43 +0,0 @@
import logging
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(logging.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 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+4 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -18,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""Extensions over the Telegram Bot API to facilitate bot making"""
from .dispatcher import Dispatcher
from .dispatcher import Dispatcher, DispatcherHandlerStop, run_async
from .jobqueue import JobQueue, Job
from .updater import Updater
from .callbackqueryhandler import CallbackQueryHandler
@@ -42,4 +42,5 @@ __all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler',
'ChosenInlineResultHandler', 'CommandHandler', 'Handler', 'InlineQueryHandler',
'MessageHandler', 'BaseFilter', 'Filters', 'RegexHandler', 'StringCommandHandler',
'StringRegexHandler', 'TypeHandler', 'ConversationHandler',
'PreCheckoutQueryHandler', 'ShippingQueryHandler', 'MessageQueue', 'DelayQueue')
'PreCheckoutQueryHandler', 'ShippingQueryHandler', 'MessageQueue', 'DelayQueue',
'DispatcherHandlerStop', 'run_async')
+9 -10
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -16,7 +16,7 @@
#
# 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 """
"""This module contains the CallbackQueryHandler class."""
import re
@@ -27,8 +27,8 @@ from .handler import Handler
class CallbackQueryHandler(Handler):
"""
Handler class to handle Telegram callback queries. Optionally based on a regex.
"""Handler class to handle Telegram callback queries. Optionally based on a regex.
Read the documentation of the ``re`` module for more information.
Attributes:
@@ -79,6 +79,7 @@ class CallbackQueryHandler(Handler):
``user_data`` will be passed to the callback function. Default is ``False``.
pass_chat_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. Default is ``False``.
"""
def __init__(self,
@@ -105,16 +106,15 @@ class CallbackQueryHandler(Handler):
self.pass_groupdict = pass_groupdict
def check_update(self, update):
"""
Determines whether an update should be passed to this handlers :attr:`callback`.
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
Returns:
:obj:`bool`
"""
"""
if isinstance(update, Update) and update.callback_query:
if self.pattern:
if update.callback_query.data:
@@ -124,14 +124,13 @@ class CallbackQueryHandler(Handler):
return True
def handle_update(self, update, dispatcher):
"""
Send the update to the :attr:`callback`.
"""Send the update to the :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
"""
optional_args = self.collect_optional_args(dispatcher, update)
if self.pattern:
match = re.match(self.pattern, update.callback_query.data)
+8 -10
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -16,7 +16,7 @@
#
# 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 """
"""This module contains the ChosenInlineResultHandler class."""
from .handler import Handler
from telegram import Update
@@ -24,8 +24,7 @@ from telegram.utils.deprecate import deprecate
class ChosenInlineResultHandler(Handler):
"""
Handler class to handle Telegram updates that contain a chosen inline result.
"""Handler class to handle Telegram updates that contain a chosen inline result.
Attributes:
callback (:obj:`callable`): The callback function for this handler.
@@ -60,6 +59,7 @@ class ChosenInlineResultHandler(Handler):
``user_data`` will be passed to the callback function. Default is ``False``.
pass_chat_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. Default is ``False``.
"""
def __init__(self,
@@ -76,27 +76,25 @@ class ChosenInlineResultHandler(Handler):
pass_chat_data=pass_chat_data)
def check_update(self, update):
"""
Determines whether an update should be passed to this handlers :attr:`callback`.
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
Returns:
:obj:`bool`
"""
"""
return isinstance(update, Update) and update.chosen_inline_result
def handle_update(self, update, dispatcher):
"""
Send the update to the :attr:`callback`.
"""Send the update to the :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
"""
optional_args = self.collect_optional_args(dispatcher, update)
return self.callback(dispatcher.bot, update, **optional_args)
+26 -28
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -16,7 +16,7 @@
#
# 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 """
"""This module contains the CommandHandler class."""
import warnings
from future.utils import string_types
@@ -26,10 +26,10 @@ from telegram import Update
class CommandHandler(Handler):
"""
Handler class to handle Telegram commands. Commands are Telegram messages
that start with ``/``, optionally followed by an ``@`` and the bot's
name and/or some additional text.
"""Handler class to handle Telegram commands.
Commands are Telegram messages that start with ``/``, optionally followed by an ``@`` and the
bot's name and/or some additional text.
Attributes:
command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
@@ -84,6 +84,7 @@ class CommandHandler(Handler):
``user_data`` will be passed to the callback function. Default is ``False``.
pass_chat_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. Default is ``False``.
"""
def __init__(self,
@@ -119,49 +120,46 @@ class CommandHandler(Handler):
'instead. More info: https://git.io/vPTbc.')
def check_update(self, update):
"""
Determines whether an update should be passed to this handlers :attr:`callback`.
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
Returns:
:obj:`bool`
"""
"""
if (isinstance(update, Update)
and (update.message or update.edited_message and self.allow_edited)):
message = update.message or update.edited_message
if message.text:
command = message.text[1:].split(' ')[0].split('@')
command.append(
message.bot.username) # in case the command was send without a username
if message.text and message.text.startswith('/') and len(message.text) > 1:
first_word = message.text_html.split(None, 1)[0]
if len(first_word) > 1 and first_word.startswith('/'):
command = first_word[1:].split('@')
command.append(
message.bot.username) # in case the command was sent without a username
if self.filters is None:
res = True
elif isinstance(self.filters, list):
res = any(func(message) for func in self.filters)
else:
res = self.filters(message)
if self.filters is None:
res = True
elif isinstance(self.filters, list):
res = any(func(message) for func in self.filters)
else:
res = self.filters(message)
return res and (message.text.startswith('/') and command[0].lower() in self.command
and command[1].lower() == message.bot.username.lower())
else:
return False
return res and (command[0].lower() in self.command
and command[1].lower() == message.bot.username.lower())
else:
return False
return False
def handle_update(self, update, dispatcher):
"""
Send the update to the :attr:`callback`.
"""Send the update to the :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
"""
optional_args = self.collect_optional_args(dispatcher, update)
message = update.message or update.edited_message
+35 -13
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the ConversationHandler """
"""This module contains the ConversationHandler."""
import logging
@@ -55,7 +55,7 @@ class ConversationHandler(Handler):
To change the state of conversation, the callback function of a handler must return the new
state after responding to the user. If it does not return anything (returning ``None`` by
default), the state will not change. To end the conversation, the callback function must
return :attr`END` or ``-1``.
return :attr:`END` or ``-1``.
Attributes:
entry_points (List[:class:`telegram.ext.Handler`]): A list of ``Handler`` objects that can
@@ -76,6 +76,9 @@ class ConversationHandler(Handler):
per_user (:obj:`bool`): Optional. If the conversationkey should contain the User's ID.
per_message (:obj:`bool`): Optional. If the conversationkey should contain the Message's
ID.
conversation_timeout (:obj:`float`|:obj:`datetime.timedelta`): Optional. When this handler
is inactive more than this timeout (in seconds), it will be automatically ended. If
this value is 0 (default), there will be no timeout.
Args:
entry_points (List[:class:`telegram.ext.Handler`]): A list of ``Handler`` objects that can
@@ -107,11 +110,14 @@ class ConversationHandler(Handler):
Default is ``True``.
per_message (:obj:`bool`, optional): If the conversationkey should contain the Message's
ID. Default is ``False``.
conversation_timeout (:obj:`float`|:obj:`datetime.timedelta`, optional): When this handler
is inactive more than this timeout (in seconds), it will be automatically ended. If
this value is 0 or None (default), there will be no timeout.
Raises:
ValueError
"""
"""
END = -1
""":obj:`int`: Used as a constant to return when a conversation is ended."""
@@ -124,7 +130,8 @@ class ConversationHandler(Handler):
timed_out_behavior=None,
per_chat=True,
per_user=True,
per_message=False):
per_message=False,
conversation_timeout=None):
self.entry_points = entry_points
self.states = states
@@ -136,7 +143,9 @@ class ConversationHandler(Handler):
self.per_user = per_user
self.per_chat = per_chat
self.per_message = per_message
self.conversation_timeout = conversation_timeout
self.timeout_jobs = dict()
self.conversations = dict()
self.current_conversation = None
self.current_handler = None
@@ -203,13 +212,14 @@ class ConversationHandler(Handler):
Returns:
:obj:`bool`
"""
"""
# Ignore messages in channels
if (not isinstance(update, Update) or update.channel_post or self.per_chat
and (update.inline_query or update.chosen_inline_result) or self.per_message
and not update.callback_query or update.callback_query and self.per_chat
and not update.callback_query.message):
if (not isinstance(update, Update) or
update.channel_post or
self.per_chat and not update.effective_chat or
self.per_message and not update.callback_query or
update.callback_query and self.per_chat and not update.callback_query.message):
return False
key = self._get_key(update)
@@ -285,15 +295,23 @@ class ConversationHandler(Handler):
return True
def handle_update(self, update, dispatcher):
"""
Send the update to the callback for the current state and Handler
"""Send the update to the callback for the current state and Handler
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
"""
new_state = self.current_handler.handle_update(update, dispatcher)
timeout_job = self.timeout_jobs.pop(self.current_conversation, None)
if timeout_job is not None:
timeout_job.schedule_removal()
if self.conversation_timeout and new_state != self.END:
self.timeout_jobs[self.current_conversation] = dispatcher.job_queue.run_once(
self._trigger_timeout, self.conversation_timeout,
context=self.current_conversation
)
self.update_state(new_state, self.current_conversation)
@@ -309,3 +327,7 @@ class ConversationHandler(Handler):
elif new_state is not None:
self.conversations[key] = new_state
def _trigger_timeout(self, bot, job):
del self.timeout_jobs[job.context]
self.update_state(self.END, job.context)
+97 -82
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -39,15 +39,15 @@ DEFAULT_GROUP = 0
def run_async(func):
"""
Function decorator that will run the function in a new thread.
will run :attr:`telegram.ext.Dispatcher.run_async`.
"""Function decorator that will run the function in a new thread.
Will run :attr:`telegram.ext.Dispatcher.run_async`.
Using this decorator is only possible when only a single Dispatcher exist in the system.
Note:
Use this decorator to run handlers asynchronously.
"""
Note: Use this decorator to run handlers asynchronously.
"""
@wraps(func)
def async_func(*args, **kwargs):
return Dispatcher.get_instance().run_async(func, *args, **kwargs)
@@ -55,9 +55,13 @@ def run_async(func):
return async_func
class DispatcherHandlerStop(Exception):
"""Raise this in handler to prevent execution any other handler (even in different group)."""
pass
class Dispatcher(object):
"""
This class dispatches all kinds of updates to its registered handlers.
"""This class dispatches all kinds of updates to its registered handlers.
Attributes:
bot (:class:`telegram.Bot`): The bot object that should be passed to the handlers.
@@ -74,6 +78,7 @@ class Dispatcher(object):
instance to pass onto handler callbacks.
workers (:obj:`int`, optional): Number of maximum concurrent worker threads for the
``@run_async`` decorator. defaults to 4.
"""
__singleton_lock = Lock()
@@ -113,12 +118,6 @@ class Dispatcher(object):
else:
self._set_singleton(None)
@classmethod
def _reset_singleton(cls):
# NOTE: This method was added mainly for test_updater benefit and specifically pypy. Never
# call it in production code.
cls.__singleton_semaphore.release()
def _init_async_threads(self, base_name, workers):
base_name = '{}_'.format(base_name) if base_name else ''
@@ -134,16 +133,15 @@ class Dispatcher(object):
@classmethod
def get_instance(cls):
"""
Get the singleton instance of this class.
"""Get the singleton instance of this class.
Returns:
:class:`telegram.ext.Dispatcher`
Raises:
RuntimeError
"""
"""
if cls.__singleton is not None:
return cls.__singleton()
else:
@@ -162,10 +160,13 @@ class Dispatcher(object):
break
promise.run()
if isinstance(promise.exception, DispatcherHandlerStop):
self.logger.warning(
'DispatcherHandlerStop is not supported with async functions; func: %s',
promise.pooled_function.__name__)
def run_async(self, func, *args, **kwargs):
"""
Queue a function (with given args/kwargs) to be run asynchronously.
"""Queue a function (with given args/kwargs) to be run asynchronously.
Args:
func (:obj:`callable`): The function to run in the thread.
@@ -182,14 +183,20 @@ class Dispatcher(object):
self.__async_queue.put(promise)
return promise
def start(self):
"""
Thread target of thread 'dispatcher'. Runs in background and processes
the update queue.
"""
def start(self, ready=None):
"""Thread target of thread 'dispatcher'.
Runs in background and processes the update queue.
Args:
ready (:obj:`threading.Event`, optional): If specified, the event will be set once the
dispatcher is ready.
"""
if self.running:
self.logger.warning('already running')
if ready is not None:
ready.set()
return
if self.__exception_event.is_set():
@@ -201,6 +208,9 @@ class Dispatcher(object):
self.running = True
self.logger.debug('Dispatcher started')
if ready is not None:
ready.set()
while 1:
try:
# Pop update from update queue.
@@ -221,10 +231,7 @@ class Dispatcher(object):
self.logger.debug('Dispatcher thread stopped')
def stop(self):
"""
Stops the thread.
"""
"""Stops the thread."""
if self.running:
self.__stop_event.set()
while self.running:
@@ -251,64 +258,71 @@ class Dispatcher(object):
return self.running or bool(self.__async_threads)
def process_update(self, update):
"""
Processes a single update.
"""Processes a single update.
Args:
update (:obj:`str` | :class:`telegram.Update`): The update to process.
"""
update (:obj:`str` | :class:`telegram.Update` | :class:`telegram.TelegramError`):
The update to process.
"""
# An error happened while polling
if isinstance(update, TelegramError):
self.dispatch_error(None, update)
try:
self.dispatch_error(None, update)
except Exception:
self.logger.exception('An uncaught error was raised while handling the error')
return
else:
for group in self.groups:
for handler in self.handlers[group]:
try:
if handler.check_update(update):
handler.handle_update(update, self)
break
# Dispatch any errors
except TelegramError as te:
self.logger.warn('A TelegramError was raised while processing the '
'Update.')
for group in self.groups:
try:
for handler in (x for x in self.handlers[group] if x.check_update(update)):
handler.handle_update(update, self)
break
try:
self.dispatch_error(update, te)
except Exception:
self.logger.exception('An uncaught error was raised while '
'handling the error')
finally:
break
# Stop processing with any other handler.
except DispatcherHandlerStop:
self.logger.debug('Stopping further handlers due to DispatcherHandlerStop')
break
# Errors should not stop the thread
except Exception:
self.logger.exception('An uncaught error was raised while '
'processing the update')
break
# Dispatch any error.
except TelegramError as te:
self.logger.warning('A TelegramError was raised while processing the Update')
try:
self.dispatch_error(update, te)
except DispatcherHandlerStop:
self.logger.debug('Error handler stopped further handlers')
break
except Exception:
self.logger.exception('An uncaught error was raised while handling the error')
# Errors should not stop the thread.
except Exception:
self.logger.exception('An uncaught error was raised while processing the update')
def add_handler(self, handler, group=DEFAULT_GROUP):
"""
Register a handler.
"""Register a handler.
TL;DR: Order and priority counts. 0 or 1 handlers per group will be
used.
TL;DR: Order and priority counts. 0 or 1 handlers per group will be used.
A handler must be an instance of a subclass of :class:`telegram.ext.Handler`. All handlers
are organized in groups with a numeric value. The default group is 0. All groups will be
evaluated for handling an update, but only 0 or 1 handler per group will be used.
evaluated for handling an update, but only 0 or 1 handler per group will be used. If
:class:`telegram.DispatcherHandlerStop` is raised from one of the handlers, no further
handlers (regardless of the group) will be called.
The priority/order of handlers is determined as follows:
* Priority of the group (lower group number == higher priority)
* The first handler in a group which should handle an update will be
used. Other handlers from the group will not be used. The order in
which handlers were added to the group defines the priority.
* The first handler in a group which should handle an update (see
:attr:`telegram.ext.Handler.check_update`) will be used. Other handlers from the
group will not be used. The order in which handlers were added to the group defines the
priority.
Args:
handler (:class:`telegram.ext.Handler`): A Handler instance.
group (:obj:`int`, optional): The group identifier. Default is 0.
"""
if not isinstance(handler, Handler):
@@ -324,14 +338,13 @@ class Dispatcher(object):
self.handlers[group].append(handler)
def remove_handler(self, handler, group=DEFAULT_GROUP):
"""
Remove a handler from the specified group
"""Remove a handler from the specified group.
Args:
handler (:class:`telegram.ext.Handler`): A Handler instance.
group (:obj:`object`, optional): The group identifier. Default is 0.
"""
"""
if handler in self.handlers[group]:
self.handlers[group].remove(handler)
if not self.handlers[group]:
@@ -339,35 +352,37 @@ class Dispatcher(object):
self.groups.remove(group)
def add_error_handler(self, callback):
"""
Registers an error handler in the Dispatcher.
"""Registers an error handler in the Dispatcher.
Args:
handler (:obj:`callable`): A function that takes ``Bot, Update, TelegramError`` as
callback (:obj:`callable`): A function that takes ``Bot, Update, TelegramError`` as
arguments.
"""
"""
self.error_handlers.append(callback)
def remove_error_handler(self, callback):
"""
Removes an error handler.
"""Removes an error handler.
Args:
handler (:obj:`callable`): The error handler to remove.
"""
callback (:obj:`callable`): The error handler to remove.
"""
if callback in self.error_handlers:
self.error_handlers.remove(callback)
def dispatch_error(self, update, error):
"""
Dispatches an error.
"""Dispatches an error.
Args:
update (:obj:`str` | :class:`telegram.Update`): The update that caused the error
update (:obj:`str` | :class:`telegram.Update` | None): The update that caused the error
error (:class:`telegram.TelegramError`): The Telegram error that was raised.
"""
for callback in self.error_handlers:
callback(self.bot, update, error)
"""
if self.error_handlers:
for callback in self.error_handlers:
callback(self.bot, update, error)
else:
self.logger.exception(
'No error handlers are registered, logging exception...', exc_info=error)
+63 -22
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -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 the Filters for use with the MessageHandler class """
"""This module contains the Filters for use with the MessageHandler class."""
from telegram import Chat
from future.utils import string_types
class BaseFilter(object):
"""
Base class for all Message Filters
"""Base class for all Message Filters.
Subclassing from this class filters to be combined using bitwise operators:
@@ -55,6 +54,7 @@ class BaseFilter(object):
Attributes:
name (:obj:`str`): Name for this filter. Defaults to the type of filter.
"""
name = None
@@ -78,14 +78,14 @@ class BaseFilter(object):
return self.name
def filter(self, message):
"""
This method must be overwritten.
"""This method must be overwritten.
Args:
message (:class:`telegram.Message`): The message that is tested.
Returns:
:obj:`bool`
"""
raise NotImplementedError
@@ -96,6 +96,7 @@ class InvertedFilter(BaseFilter):
Args:
f: The filter to invert.
"""
def __init__(self, f):
@@ -115,6 +116,7 @@ class MergedFilter(BaseFilter):
base_filter: Filter 1 of the merged filter
and_filter: Optional filter to "and" with base_filter. Mutually exclusive with or_filter.
or_filter: Optional filter to "or" with base_filter. Mutually exclusive with and_filter.
"""
def __init__(self, base_filter, and_filter=None, or_filter=None):
@@ -134,12 +136,12 @@ class MergedFilter(BaseFilter):
class Filters(object):
"""
Predefined filters for use with the `filter` argument of :class:`telegram.ext.MessageHandler`.
"""Predefined filters for use as the `filter` argument of :class:`telegram.ext.MessageHandler`.
Examples:
Use ``MessageHandler(Filters.video, callback_method)`` to filter all video
messages. Use ``MessageHandler(Filters.contact, callback_method)`` for all contacts. etc.
"""
class _All(BaseFilter):
@@ -260,12 +262,12 @@ class Filters(object):
""":obj:`Filter`: Messages that contain :class:`telegram.Venue`."""
class _StatusUpdate(BaseFilter):
"""
Subset for messages containing a status update.
"""Subset for messages containing a status update.
Examples:
Use these filters like: ``Filters.status_update.new_chat_member`` etc. Or use just
Use these filters like: ``Filters.status_update.new_chat_members`` etc. Or use just
``Filters.status_update`` for all status update messages.
"""
class _NewChatMembers(BaseFilter):
@@ -275,7 +277,7 @@ class Filters(object):
return bool(message.new_chat_members)
new_chat_members = _NewChatMembers()
""":obj:`Filter`: Messages that contain :attr:`telegram.Message.new_chat_member`."""
""":obj:`Filter`: Messages that contain :attr:`telegram.Message.new_chat_members`."""
class _LeftChatMember(BaseFilter):
name = 'Filters.status_update.left_chat_member'
@@ -333,7 +335,7 @@ class Filters(object):
migrate = _Migrate()
""":obj:`Filter`: Messages that contain :attr:`telegram.Message.migrate_from_chat_id` or
:attr: `telegram.Message.migrate_from_chat_id`."""
:attr: `telegram.Message.migrate_to_chat_id`."""
class _PinnedMessage(BaseFilter):
name = 'Filters.status_update.pinned_message'
@@ -344,15 +346,52 @@ class Filters(object):
pinned_message = _PinnedMessage()
""":obj:`Filter`: Messages that contain :attr:`telegram.Message.pinned_message`."""
class _ConnectedWebsite(BaseFilter):
name = 'Filters.status_update.connected_website'
def filter(self, message):
return bool(message.connected_website)
connected_website = _ConnectedWebsite()
""":obj:`Filter`: Messages that contain :attr:`telegram.Message.connected_website`."""
name = 'Filters.status_update'
def filter(self, message):
return bool(self.new_chat_members(message) or self.left_chat_member(message) or
self.new_chat_title(message) or self.new_chat_photo(message) or
self.delete_chat_photo(message) or self.chat_created(message) or
self.migrate(message) or self.pinned_message(message))
self.migrate(message) or self.pinned_message(message) or
self.connected_website(message))
status_update = _StatusUpdate()
"""Subset for messages containing a status update.
Examples:
Use these filters like: ``Filters.status_update.new_chat_members`` etc. Or use just
``Filters.status_update`` for all status update messages.
Attributes:
chat_created (:obj:`Filter`): Messages that contain
:attr:`telegram.Message.group_chat_created`,
:attr:`telegram.Message.supergroup_chat_created` or
:attr:`telegram.Message.channel_chat_created`.
delete_chat_photo (:obj:`Filter`): Messages that contain
:attr:`telegram.Message.delete_chat_photo`.
left_chat_member (:obj:`Filter`): Messages that contain
:attr:`telegram.Message.left_chat_member`.
migrate (:obj:`Filter`): Messages that contain
:attr:`telegram.Message.migrate_from_chat_id` or
:attr: `telegram.Message.migrate_from_chat_id`.
new_chat_members (:obj:`Filter`): Messages that contain
:attr:`telegram.Message.new_chat_members`.
new_chat_photo (:obj:`Filter`): Messages that contain
:attr:`telegram.Message.new_chat_photo`.
new_chat_title (:obj:`Filter`): Messages that contain
:attr:`telegram.Message.new_chat_title`.
pinned_message (:obj:`Filter`): Messages that contain
:attr:`telegram.Message.pinned_message`.
"""
class _Forwarded(BaseFilter):
name = 'Filters.forwarded'
@@ -383,6 +422,7 @@ class Filters(object):
Args:
entity_type: Entity type to check for. All types can be found as constants
in :class:`telegram.MessageEntity`.
"""
def __init__(self, entity_type):
@@ -411,8 +451,7 @@ class Filters(object):
""":obj:`Filter`: Messages sent in a group chat."""
class user(BaseFilter):
"""
Filters messages to allow only those which are from specified user ID.
"""Filters messages to allow only those which are from specified user ID.
Examples:
``MessageHandler(Filters.user(1234), callback_method)``
@@ -424,6 +463,7 @@ class Filters(object):
Raises:
ValueError: If chat_id and username are both present, or neither is.
"""
def __init__(self, user_id=None, username=None):
@@ -449,8 +489,7 @@ class Filters(object):
message.from_user.username in self.usernames)
class chat(BaseFilter):
"""
Filters messages to allow only those which are from specified chat ID.
"""Filters messages to allow only those which are from specified chat ID.
Examples:
``MessageHandler(Filters.chat(-1234), callback_method)``
@@ -462,6 +501,7 @@ class Filters(object):
Raises:
ValueError: If chat_id and username are both present, or neither is.
"""
def __init__(self, chat_id=None, username=None):
@@ -504,10 +544,10 @@ class Filters(object):
""":obj:`Filter`: Messages that confirm a :class:`telegram.SuccessfulPayment`."""
class language(BaseFilter):
"""
Filters messages to only allow those which are from users with a certain language code.
Note that according to telegrams documentation, every single user does not have the
language_code attribute.
"""Filters messages to only allow those which are from users with a certain language code.
Note: According to telegrams documentation, every single user does not have the
`language_code` attribute.
Examples:
``MessageHandler(Filters.language("en"), callback_method)``
@@ -516,6 +556,7 @@ class Filters(object):
lang (:obj:`str` | List[:obj:`str`]): Which language code(s) to allow through. This
will be matched using ``.startswith`` meaning that 'en' will match both 'en_US'
and 'en_GB'.
"""
def __init__(self, lang):
+7 -9
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -16,14 +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 """
"""This module contains the base class for handlers as used by the Dispatcher."""
class Handler(object):
"""
The base class for all update handlers. You can create your own handlers by inheriting from
this class.
"""The base class for all update handlers. Create custom handlers by inheriting from it.
Attributes:
callback (:obj:`callable`): The callback function for this handler.
@@ -58,6 +55,7 @@ class Handler(object):
``user_data`` will be passed to the callback function. Default is ``False``.
pass_chat_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. Default is ``False``.
"""
def __init__(self,
@@ -82,6 +80,7 @@ class Handler(object):
Returns:
:obj:`bool`
"""
raise NotImplementedError
@@ -101,12 +100,11 @@ class Handler(object):
raise NotImplementedError
def collect_optional_args(self, dispatcher, update=None):
"""
Prepares the optional arguments that are the same for all types of
handlers.
"""Prepares the optional arguments that are the same for all types of handlers.
Args:
dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher.
"""
optional_args = dict()
+3 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -38,7 +38,7 @@ class InlineQueryHandler(Handler):
pass_job_queue (:obj:`bool`): Optional. Determines whether ``job_queue`` will be passed to
the callback function.
pattern (:obj:`str` | :obj:`Pattern`): Optional. Regex pattern to test
:attr:`telegram.CallbackQuery.data` against.
:attr:`telegram.InlineQuery.query` against.
pass_groups (:obj:`bool`): Optional. Determines whether ``groups`` will be passed to the
callback function.
pass_groupdict (:obj:`bool`): Optional. Determines whether ``groupdict``. will be passed to
@@ -67,7 +67,7 @@ class InlineQueryHandler(Handler):
:class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``.
pattern (:obj:`str` | :obj:`Pattern`, optional): Regex pattern. If not ``None``,
``re.match`` is used on :attr:`telegram.CallbackQuery.data` to determine if an update
``re.match`` is used on :attr:`telegram.InlineQuery.query` to determine if an update
should be handled by this handler.
pass_groups (:obj:`bool`, optional): If the callback should be passed the result of
``re.match(pattern, data).groups()`` as a keyword argument called ``groups``.
+46 -113
View File
@@ -1,8 +1,7 @@
#!/usr/bin/env python
# flake8: noqa E501
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -21,7 +20,6 @@
import logging
import time
import warnings
import datetime
import weakref
from numbers import Number
@@ -35,26 +33,19 @@ class Days(object):
class JobQueue(object):
"""
This class allows you to periodically perform tasks with the bot.
"""This class allows you to periodically perform tasks with the bot.
Attributes:
queue (:obj:`PriorityQueue`): The queue that holds the Jobs.
_queue (:obj:`PriorityQueue`): The queue that holds the Jobs.
bot (:class:`telegram.Bot`): Bot that's send to the handlers.
Args:
bot (:class:`telegram.Bot`): The bot instance that should be passed to the jobs.
Deprecated:
prevent_autostart (:obj:`bool`, optional): Thread does not start during initialisation.
Use `start` method instead.
"""
def __init__(self, bot, prevent_autostart=None):
if prevent_autostart is not None:
warnings.warn("prevent_autostart is being deprecated, use `start` method instead.")
self.queue = PriorityQueue()
def __init__(self, bot):
self._queue = PriorityQueue()
self.bot = bot
self.logger = logging.getLogger(self.__class__.__name__)
self.__start_lock = Lock()
@@ -64,37 +55,6 @@ class JobQueue(object):
self._next_peek = None
self._running = False
def put(self, job, next_t=None):
"""
Queue a new job.
Note:
This method is deprecated. Please use: :attr:`run_once`, :attr:`run_daily`
or :attr:`run_repeating` instead.
Args:
job (:class:`telegram.ext.Job`): The ``Job`` instance representing the new job.
next_t (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta` | :obj:`datetime.datetime` | :obj:`datetime.time`, optional):
Time in or at which the job should run for the first time. This parameter will
be interpreted depending on its type.
* :obj:`int` or :obj:`float` will be interpreted as "seconds from now" in which the
job should run.
* :obj:`datetime.timedelta` will be interpreted as "time from now" in which the
job should run.
* :obj:`datetime.datetime` will be interpreted as a specific date and time at
which the job should run.
* :obj:`datetime.time` will be interpreted as a specific time at which the job
should run. This could be either today or, if the time has already passed,
tomorrow.
"""
warnings.warn("'JobQueue.put' is being deprecated, use 'JobQueue.run_once', "
"'JobQueue.run_daily' or 'JobQueue.run_repeating' instead")
if job.job_queue is None:
job.job_queue = self
self._put(job, next_t=next_t)
def _put(self, job, next_t=None, last_t=None):
if next_t is None:
next_t = job.interval
@@ -119,21 +79,21 @@ class JobQueue(object):
self.logger.debug('Putting job %s with t=%f', job.name, next_t)
self.queue.put((next_t, job))
self._queue.put((next_t, job))
# Wake up the loop if this job should be executed next
self._set_next_peek(next_t)
def run_once(self, callback, when, context=None, name=None):
"""
Creates a new ``Job`` that runs once and adds it to the queue.
"""Creates a new ``Job`` that runs once and adds it to the queue.
Args:
callback (:obj:`callable`): The callback function that should be executed by the new
job. It should take ``bot, job`` as parameters, where ``job`` is the
:class:`telegram.ext.Job` instance. It can be used to access it's
``job.context`` or change it to a repeating job.
when (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta` | :obj:`datetime.datetime` | :obj:`datetime.time`):
when (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta` | \
:obj:`datetime.datetime` | :obj:`datetime.time`):
Time in or at which the job should run. This parameter will be interpreted
depending on its type.
@@ -155,15 +115,14 @@ class JobQueue(object):
Returns:
:class:`telegram.ext.Job`: The new ``Job`` instance that has been added to the job
queue.
"""
"""
job = Job(callback, repeat=False, context=context, name=name, job_queue=self)
self._put(job, next_t=when)
return job
def run_repeating(self, callback, interval, first=None, context=None, name=None):
"""
Creates a new ``Job`` that runs once and adds it to the queue.
"""Creates a new ``Job`` that runs once and adds it to the queue.
Args:
callback (:obj:`callable`): The callback function that should be executed by the new
@@ -173,7 +132,8 @@ class JobQueue(object):
interval (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta`): The interval in which
the job will run. If it is an :obj:`int` or a :obj:`float`, it will be interpreted
as seconds.
first (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta` | :obj:`datetime.datetime` | :obj:`datetime.time`, optional):
first (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta` | \
:obj:`datetime.datetime` | :obj:`datetime.time`, optional):
Time in or at which the job should run. This parameter will be interpreted
depending on its type.
@@ -196,8 +156,8 @@ class JobQueue(object):
Returns:
:class:`telegram.ext.Job`: The new ``Job`` instance that has been added to the job
queue.
"""
"""
job = Job(callback,
interval=interval,
repeat=True,
@@ -208,8 +168,7 @@ class JobQueue(object):
return job
def run_daily(self, callback, time, days=Days.EVERY_DAY, context=None, name=None):
"""
Creates a new ``Job`` that runs once and adds it to the queue.
"""Creates a new ``Job`` that runs once and adds it to the queue.
Args:
callback (:obj:`callable`): The callback function that should be executed by the new
@@ -227,8 +186,8 @@ class JobQueue(object):
Returns:
:class:`telegram.ext.Job`: The new ``Job`` instance that has been added to the job
queue.
"""
"""
job = Job(callback,
interval=datetime.timedelta(days=1),
repeat=True,
@@ -250,17 +209,14 @@ class JobQueue(object):
self.__tick.set()
def tick(self):
"""
Run all jobs that are due and re-enqueue them with their interval.
"""
"""Run all jobs that are due and re-enqueue them with their interval."""
now = time.time()
self.logger.debug('Ticking jobs with t=%f', now)
while True:
try:
t, job = self.queue.get(False)
t, job = self._queue.get(False)
except Empty:
break
@@ -273,7 +229,7 @@ class JobQueue(object):
# 2. At the first iteration of the loop only if `self.put()` had triggered
# `self.__tick` because `self._next_peek` wasn't set
self.logger.debug("Next task isn't due yet. Finished!")
self.queue.put((t, job))
self._queue.put((t, job))
self._set_next_peek(t)
break
@@ -288,7 +244,7 @@ class JobQueue(object):
self.logger.debug('Running job %s', job.name)
job.run(self.bot)
except:
except Exception:
self.logger.exception('An uncaught error was raised while executing job %s',
job.name)
else:
@@ -300,10 +256,7 @@ class JobQueue(object):
self.logger.debug('Dropping non-repeating or removed job %s', job.name)
def start(self):
"""
Starts the job_queue thread.
"""
"""Starts the job_queue thread."""
self.__start_lock.acquire()
if not self._running:
@@ -319,8 +272,8 @@ class JobQueue(object):
"""
Thread target of thread ``job_queue``. Runs in background and performs ticks on the job
queue.
"""
"""
while self._running:
# self._next_peek may be (re)scheduled during self.tick() or self.put()
with self.__next_peek_lock:
@@ -339,10 +292,7 @@ class JobQueue(object):
self.logger.debug('%s thread stopped', self.__class__.__name__)
def stop(self):
"""
Stops the thread.
"""
"""Stops the thread."""
with self.__start_lock:
self._running = False
@@ -351,16 +301,18 @@ class JobQueue(object):
self.__thread.join()
def jobs(self):
"""
Returns a tuple of all jobs that are currently in the ``JobQueue``
"""
"""Returns a tuple of all jobs that are currently in the ``JobQueue``."""
with self._queue.mutex:
return tuple(job[1] for job in self._queue.queue if job)
return tuple(job[1] for job in self.queue.queue if job)
def get_jobs_by_name(self, name):
"""Returns a tuple of jobs with the given name that are currently in the ``JobQueue``"""
with self._queue.mutex:
return tuple(job[1] for job in self._queue.queue if job and job[1].name == name)
class Job(object):
"""
This class encapsulates a Job
"""This class encapsulates a Job.
Attributes:
callback (:obj:`callable`): The callback function that should be executed by the new job.
@@ -383,8 +335,9 @@ class Job(object):
name (:obj:`str`, optional): The name of the new job. Defaults to ``callback.__name__``.
days (Tuple[:obj:`int`], optional): Defines on which days of the week the job should run.
Defaults to ``Days.EVERY_DAY``
job_queue (class:`telegram.ext.JobQueue`, optional): The ``JobQueue`` this job belongs to.
job_queue (:class:`telegram.ext.JobQueue`, optional): The ``JobQueue`` this job belongs to.
Only optional for backward compatibility with ``JobQueue.put()``.
"""
def __init__(self,
@@ -415,34 +368,25 @@ class Job(object):
self._enabled.set()
def run(self, bot):
"""
Executes the callback function.
"""
"""Executes the callback function."""
self.callback(bot, self)
def schedule_removal(self):
"""
Schedules this job for removal from the ``JobQueue``. It will be removed without executing
its callback function again.
"""
"""
self._remove.set()
@property
def removed(self):
"""
:obj:`bool`: Whether this job is due to be removed.
"""
""":obj:`bool`: Whether this job is due to be removed."""
return self._remove.is_set()
@property
def enabled(self):
"""
:obj:`bool`: Whether this job is enabled.
"""
""":obj:`bool`: Whether this job is enabled."""
return self._enabled.is_set()
@enabled.setter
@@ -457,8 +401,8 @@ class Job(object):
"""
:obj:`int` | :obj:`float` | :obj:`datetime.timedelta`: Optional. The interval in which the
job will run.
"""
"""
return self._interval
@interval.setter
@@ -474,21 +418,16 @@ class Job(object):
@property
def interval_seconds(self):
"""
:obj:`int`: The interval for this job in seconds.
"""
if isinstance(self.interval, datetime.timedelta):
return self.interval.total_seconds()
""":obj:`int`: The interval for this job in seconds."""
interval = self.interval
if isinstance(interval, datetime.timedelta):
return interval.total_seconds()
else:
return self.interval
return interval
@property
def repeat(self):
"""
:obj:`bool`: Optional. If this job should be periodically execute its callback function.
"""
""":obj:`bool`: Optional. If this job should periodically execute its callback function."""
return self._repeat
@repeat.setter
@@ -499,10 +438,7 @@ class Job(object):
@property
def days(self):
"""
Tuple[:obj:`int`]: Optional. Defines on which days of the week the job should run.
"""
"""Tuple[:obj:`int`]: Optional. Defines on which days of the week the job should run."""
return self._days
@days.setter
@@ -521,10 +457,7 @@ class Job(object):
@property
def job_queue(self):
"""
:class:`telegram.ext.JobQueue`: Optional. The ``JobQueue`` this job belongs to.
"""
""":class:`telegram.ext.JobQueue`: Optional. The ``JobQueue`` this job belongs to."""
return self._job_queue
@job_queue.setter
+12 -14
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -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/].
# TODO: Remove allow_edited
""" This module contains the MessageHandler class """
"""This module contains the MessageHandler class."""
import warnings
from .handler import Handler
from telegram import Update
from .handler import Handler
class MessageHandler(Handler):
"""
Handler class to handle telegram messages. They might contain text, media or status updates.
"""Handler class to handle telegram messages. They might contain text, media or status updates.
Attributes:
filters (:obj:`Filter`): Only allow updates with these Filters. See
@@ -86,6 +85,7 @@ class MessageHandler(Handler):
Raises:
ValueError
"""
def __init__(self,
@@ -125,21 +125,20 @@ class MessageHandler(Handler):
'instead. More info: https://git.io/vPTbc.')
def _is_allowed_update(self, update):
return any([(self.message_updates and update.message),
(self.edited_updates and update.edited_message),
(self.channel_post_updates and update.channel_post)])
return any([self.message_updates and update.message,
self.edited_updates and (update.edited_message or update.edited_channel_post),
self.channel_post_updates and update.channel_post])
def check_update(self, update):
"""
Determines whether an update should be passed to this handlers :attr:`callback`.
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
Returns:
:obj:`bool`
"""
"""
if isinstance(update, Update) and self._is_allowed_update(update):
if not self.filters:
@@ -158,14 +157,13 @@ class MessageHandler(Handler):
return res
def handle_update(self, update, dispatcher):
"""
Send the update to the :attr:`callback`.
"""Send the update to the :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
"""
optional_args = self.collect_optional_args(dispatcher, update)
return self.callback(dispatcher.bot, update, **optional_args)
+16 -15
View File
@@ -4,7 +4,7 @@
# Tymofii A. Khodniev (thodnev) <thodnev@mail.ru>
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -19,7 +19,7 @@
#
# 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 throughput-limiting message processor for Telegram bots"""
"""A throughput-limiting message processor for Telegram bots."""
from telegram.utils import promise
import functools
@@ -44,9 +44,7 @@ else:
class DelayQueueError(RuntimeError):
"""
Indicates processing errors.
"""
"""Indicates processing errors."""
pass
@@ -78,6 +76,7 @@ class DelayQueue(threading.Thread):
creation; if ``False``, should be started manually by `start` method. Defaults to True.
name (:obj:`str`, optional): Thread's name. Defaults to ``'DelayQueue-N'``, where N is
sequential number of object created.
"""
_instcnt = 0 # instance counter
@@ -105,7 +104,8 @@ class DelayQueue(threading.Thread):
def run(self):
"""
Do not use the method except for unthreaded testing purposes, the method normally is
automatically called by autostart argument .
automatically called by autostart argument.
"""
times = [] # used to store each callable processing time
@@ -134,14 +134,14 @@ class DelayQueue(threading.Thread):
self.exc_route(exc) # to prevent thread exit
def stop(self, timeout=None):
"""
Used to gently stop processor and shutdown its thread.
"""Used to gently stop processor and shutdown its thread.
Args:
timeout (:obj:`float`): Indicates maximum time to wait for processor to stop and its
thread to exit. If timeout exceeds and processor has not stopped, method silently
returns. :attr:`is_alive` could be used afterwards to check the actual status.
``timeout`` set to None, blocks until processor is shut down. Defaults to None.
"""
self.__exit_req = True # gently request
@@ -153,19 +153,20 @@ class DelayQueue(threading.Thread):
"""
Dummy exception handler which re-raises exception in thread. Could be possibly overwritten
by subclasses.
"""
raise exc
def __call__(self, func, *args, **kwargs):
"""
Used to process callbacks in throughput-limiting thread through queue.
"""Used to process callbacks in throughput-limiting thread through queue.
Args:
func (:obj:`callable`): The actual function (or any callable) that is processed through
queue.
*args (:obj:`list`): Variable-length `func` arguments.
**kwargs (:obj:`dict`): Arbitrary keyword-arguments to `func`.
"""
if not self.is_alive() or self.__exit_req:
@@ -202,6 +203,7 @@ class MessageQueue(object):
autostart (:obj:`bool`, optional): If True, processors are started immediately after
object's creation; if ``False``, should be started manually by :attr:`start` method.
Defaults to ``True``.
"""
def __init__(self,
@@ -224,9 +226,7 @@ class MessageQueue(object):
autostart=autostart)
def start(self):
"""
Method is used to manually start the ``MessageQueue`` processing.
"""
"""Method is used to manually start the ``MessageQueue`` processing."""
self._all_delayq.start()
self._group_delayq.start()
@@ -258,6 +258,7 @@ class MessageQueue(object):
Returns:
:obj:`callable`: Used as ``promise`` argument.
"""
if not is_group_msg: # ignore middle group delay
@@ -268,8 +269,7 @@ class MessageQueue(object):
def queuedmessage(method):
"""
A decorator to be used with :attr:`telegram.Bot` send* methods.
"""A decorator to be used with :attr:`telegram.Bot` send* methods.
Note:
As it probably wouldn't be a good idea to make this decorator a property, it has been coded
@@ -297,6 +297,7 @@ def queuedmessage(method):
Returns:
``telegram.utils.promise.Promise``: In case call is queued or original method's return
value if it's not.
"""
@functools.wraps(method)
+8 -10
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -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 PreCheckoutQueryHandler class """
"""This module contains the PreCheckoutQueryHandler class."""
from telegram import Update
from .handler import Handler
class PreCheckoutQueryHandler(Handler):
"""
Handler class to handle Telegram PreCheckout callback queries.
"""Handler class to handle Telegram PreCheckout callback queries.
Attributes:
callback (:obj:`callable`): The callback function for this handler.
@@ -59,6 +58,7 @@ class PreCheckoutQueryHandler(Handler):
``user_data`` will be passed to the callback function. Default is ``False``.
pass_chat_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. Default is ``False``.
"""
def __init__(self,
@@ -75,26 +75,24 @@ class PreCheckoutQueryHandler(Handler):
pass_chat_data=pass_chat_data)
def check_update(self, update):
"""
Determines whether an update should be passed to this handlers :attr:`callback`.
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
Returns:
:obj:`bool`
"""
"""
return isinstance(update, Update) and update.pre_checkout_query
def handle_update(self, update, dispatcher):
"""
Send the update to the :attr:`callback`.
"""Send the update to the :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
"""
optional_args = self.collect_optional_args(dispatcher, update)
return self.callback(dispatcher.bot, update, **optional_args)
+18 -16
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
# TODO: Remove allow_edited
""" This module contains the RegexHandler class """
"""This module contains the RegexHandler class."""
import re
import warnings
@@ -29,11 +29,11 @@ from .handler import Handler
class RegexHandler(Handler):
"""
Handler class to handle Telegram updates based on a regex. It uses a
regular expression to check text messages. Read the documentation of the
``re`` module for more information. The ``re.match`` function is used to
determine if an update should be handled by this handler.
"""Handler class to handle Telegram updates based on a regex.
It uses a regular expression to check text messages. Read the documentation of the ``re``
module for more information. The ``re.match`` function is used to determine if an update should
be handled by this handler.
Attributes:
pattern (:obj:`str` | :obj:`Pattern`): The regex pattern.
@@ -91,6 +91,7 @@ class RegexHandler(Handler):
Raises:
ValueError
"""
def __init__(self,
@@ -133,31 +134,32 @@ class RegexHandler(Handler):
self.edited_updates = edited_updates
def check_update(self, update):
"""
Determines whether an update should be passed to this handlers :attr:`callback`.
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
Returns:
:obj:`bool`
"""
if any([(self.message_updates and update.message),
(self.edited_updates and update.edited_message),
(self.channel_post_updates and update.channel_post)]) and (
isinstance(update, Update)):
"""
if not isinstance(update, Update) and not update.effective_message:
return False
if any([self.message_updates and update.message,
self.edited_updates and (update.edited_message or update.edited_channel_post),
self.channel_post_updates and update.channel_post]) and \
update.effective_message.text:
match = re.match(self.pattern, update.effective_message.text)
return bool(match)
return False
def handle_update(self, update, dispatcher):
"""
Send the update to the :attr:`callback`.
"""Send the update to the :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
optional_args = self.collect_optional_args(dispatcher, update)
+8 -10
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -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 ShippingQueryHandler class """
"""This module contains the ShippingQueryHandler class."""
from telegram import Update
from .handler import Handler
class ShippingQueryHandler(Handler):
"""
Handler class to handle Telegram shipping callback queries.
"""Handler class to handle Telegram shipping callback queries.
Attributes:
callback (:obj:`callable`): The callback function for this handler.
@@ -59,6 +58,7 @@ class ShippingQueryHandler(Handler):
``user_data`` will be passed to the callback function. Default is ``False``.
pass_chat_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. Default is ``False``.
"""
def __init__(self,
@@ -75,26 +75,24 @@ class ShippingQueryHandler(Handler):
pass_chat_data=pass_chat_data)
def check_update(self, update):
"""
Determines whether an update should be passed to this handlers :attr:`callback`.
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
Returns:
:obj:`bool`
"""
"""
return isinstance(update, Update) and update.shipping_query
def handle_update(self, update, dispatcher):
"""
Send the update to the :attr:`callback`.
"""Send the update to the :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
"""
optional_args = self.collect_optional_args(dispatcher, update)
return self.callback(dispatcher.bot, update, **optional_args)
+8 -9
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -16,7 +16,7 @@
#
# 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 """
"""This module contains the StringCommandHandler class."""
from future.utils import string_types
@@ -24,9 +24,7 @@ from .handler import Handler
class StringCommandHandler(Handler):
"""
Handler class to handle string commands. Commands are string updates
that start with ``/``.
"""Handler class to handle string commands. Commands are string updates that start with ``/``.
Note:
This handler is not used to handle Telegram :attr:`telegram.Update`, but strings manually
@@ -60,6 +58,7 @@ class StringCommandHandler(Handler):
``job_queue`` will be passed to the callback function. It will be a
class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``.
"""
def __init__(self,
@@ -74,26 +73,26 @@ class StringCommandHandler(Handler):
self.pass_args = pass_args
def check_update(self, update):
"""
Determines whether an update should be passed to this handlers :attr:`callback`.
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:obj:`str`): An incomming command.
Returns:
:obj:`bool`
"""
return (isinstance(update, string_types) and update.startswith('/')
and update[1:].split(' ')[0] == self.command)
def handle_update(self, update, dispatcher):
"""
Send the update to the :attr:`callback`.
"""Send the update to the :attr:`callback`.
Args:
update (:obj:`str`): An incomming command.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the command.
"""
optional_args = self.collect_optional_args(dispatcher)
+11 -13
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -16,7 +16,7 @@
#
# 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 """
"""This module contains the StringRegexHandler class."""
import re
@@ -26,11 +26,10 @@ from .handler import Handler
class StringRegexHandler(Handler):
"""
Handler class to handle string updates based on a regex. It uses a
regular expression to check update content. Read the documentation of the
``re`` module for more information. The ``re.match`` function is used to
determine if an update should be handled by this handler.
"""Handler class to handle string updates based on a regex which checks the update content.
Read the documentation of the ``re`` module for more information. The ``re.match`` function is
used to determine if an update should be handled by this handler.
Note:
This handler is not used to handle Telegram :attr:`telegram.Update`, but strings manually
@@ -67,6 +66,7 @@ class StringRegexHandler(Handler):
``job_queue`` will be passed to the callback function. It will be a
:class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``.
"""
def __init__(self,
@@ -87,27 +87,25 @@ class StringRegexHandler(Handler):
self.pass_groupdict = pass_groupdict
def check_update(self, update):
"""
Determines whether an update should be passed to this handlers :attr:`callback`.
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:obj:`str`): An incomming command.
Returns:
:obj:`bool`
"""
"""
return isinstance(update, string_types) and bool(re.match(self.pattern, update))
def handle_update(self, update, dispatcher):
"""
Send the update to the :attr:`callback`.
"""Send the update to the :attr:`callback`.
Args:
update (:obj:`str`): An incomming command.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the command.
"""
"""
optional_args = self.collect_optional_args(dispatcher)
match = re.match(self.pattern, update)
+8 -9
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -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 the TypeHandler class """
"""This module contains the TypeHandler class."""
from .handler import Handler
class TypeHandler(Handler):
"""
Handler class to handle updates of custom types.
"""Handler class to handle updates of custom types.
Attributes:
type (:obj:`type`): The ``type`` of updates this handler should process.
@@ -51,6 +50,7 @@ class TypeHandler(Handler):
``job_queue`` will be passed to the callback function. It will be a
:class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``.
"""
def __init__(self, type, callback, strict=False, pass_update_queue=False,
@@ -61,14 +61,14 @@ class TypeHandler(Handler):
self.strict = strict
def check_update(self, update):
"""
Determines whether an update should be passed to this handlers :attr:`callback`.
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
Returns:
:obj:`bool`
"""
if not self.strict:
@@ -77,14 +77,13 @@ class TypeHandler(Handler):
return type(update) is self.type
def handle_update(self, update, dispatcher):
"""
Send the update to the :attr:`callback`.
"""Send the update to the :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
"""
"""
optional_args = self.collect_optional_args(dispatcher)
return self.callback(dispatcher.bot, update, **optional_args)
+143 -94
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -16,13 +16,11 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the class Updater, which tries to make creating
Telegram bots intuitive."""
"""This module contains the class Updater, which tries to make creating Telegram bots intuitive."""
import logging
import os
import ssl
import warnings
from threading import Thread, Lock, current_thread, Event
from time import sleep
import subprocess
@@ -31,7 +29,8 @@ from queue import Queue
from telegram import Bot, TelegramError
from telegram.ext import Dispatcher, JobQueue
from telegram.error import Unauthorized, InvalidToken, RetryAfter
from telegram.error import Unauthorized, InvalidToken, RetryAfter, TimedOut
from telegram.utils.helpers import get_signal_name
from telegram.utils.request import Request
from telegram.utils.webhookhandler import (WebhookServer, WebhookHandler)
@@ -70,14 +69,17 @@ class Updater(object):
user_sig_handler (:obj:`function`, optional): Takes ``signum, frame`` as positional
arguments. This will be called when a signal is received, defaults are (SIGINT,
SIGTERM, SIGABRT) setable with :attr:`idle`.
request_kwargs (:obj:`dict`, optional): Keyword args to control the creation of a request
object (ignored if `bot` argument is used).
request_kwargs (:obj:`dict`, optional): Keyword args to control the creation of a
`telegram.utils.request.Request` object (ignored if `bot` argument is used). The
request_kwargs are very useful for the advanced users who would like to control the
default timeouts and/or control the proxy used for http communication.
Note:
You must supply either a :attr:`bot` or a :attr:`token` argument.
Raises:
ValueError: If both :attr:`token` and :attr:`bot` are passed or none of them.
"""
_request = None
@@ -95,8 +97,16 @@ class Updater(object):
if (token is not None) and (bot is not None):
raise ValueError('`token` and `bot` are mutually exclusive')
self.logger = logging.getLogger(__name__)
con_pool_size = workers + 4
if bot is not None:
self.bot = bot
if bot.request.con_pool_size < con_pool_size:
self.logger.warning(
'Connection pool of Request object is smaller than optimal value (%s)',
con_pool_size)
else:
# we need a connection pool the size of:
# * for each of the workers
@@ -107,7 +117,7 @@ class Updater(object):
if request_kwargs is None:
request_kwargs = {}
if 'con_pool_size' not in request_kwargs:
request_kwargs['con_pool_size'] = workers + 4
request_kwargs['con_pool_size'] = con_pool_size
self._request = Request(**request_kwargs)
self.bot = Bot(token, base_url, request=self._request)
self.user_sig_handler = user_sig_handler
@@ -121,7 +131,6 @@ class Updater(object):
workers=workers,
exception_event=self.__exception_event)
self.last_update_id = 0
self.logger = logging.getLogger(__name__)
self.running = False
self.is_idle = False
self.httpd = None
@@ -140,20 +149,18 @@ class Updater(object):
target(*args, **kwargs)
except Exception:
self.__exception_event.set()
self.logger.exception('unhandled exception')
self.logger.exception('unhandled exception in %s', thr_name)
raise
self.logger.debug('{0} - ended'.format(thr_name))
def start_polling(self,
poll_interval=0.0,
timeout=10,
network_delay=None,
clean=False,
bootstrap_retries=0,
bootstrap_retries=-1,
read_latency=2.,
allowed_updates=None):
"""
Starts polling updates from Telegram.
"""Starts polling updates from Telegram.
Args:
poll_interval (:obj:`float`, optional): Time to wait between polling updates from
@@ -164,8 +171,8 @@ class Updater(object):
bootstrap_retries (:obj:`int`, optional): Whether the bootstrapping phase of the
`Updater` will retry on failures on the Telegram server.
* < 0 - retry indefinitely
* 0 - no retries (default)
* < 0 - retry indefinitely (default)
* 0 - no retries
* > 0 - retry up to X times
allowed_updates (List[:obj:`str`], optional): Passed to
@@ -173,27 +180,24 @@ class Updater(object):
read_latency (:obj:`float` | :obj:`int`, optional): Grace time in seconds for receiving
the reply from server. Will be added to the `timeout` value and used as the read
timeout from server (Default: 2).
network_delay: Deprecated. Will be honoured as :attr:`read_latency` for a while but
will be removed in the future.
Returns:
:obj:`Queue`: The update queue that can be filled from the main thread.
"""
if network_delay is not None:
warnings.warn('network_delay is deprecated, use read_latency instead')
read_latency = network_delay
with self.__lock:
if not self.running:
self.running = True
# Create & start threads
self.job_queue.start()
self._init_thread(self.dispatcher.start, "dispatcher")
dispatcher_ready = Event()
self._init_thread(self.dispatcher.start, "dispatcher", ready=dispatcher_ready)
self._init_thread(self._start_polling, "updater", poll_interval, timeout,
read_latency, bootstrap_retries, clean, allowed_updates)
dispatcher_ready.wait()
# Return the update queue so the main thread can insert updates
return self.update_queue
@@ -222,11 +226,11 @@ class Updater(object):
key (:obj:`str`, optional): Path to the SSL key file.
clean (:obj:`bool`, optional): Whether to clean any pending updates on Telegram servers
before actually starting the webhook. Default is ``False``.
bootstrap_retries (Optional[int[): Whether the bootstrapping phase of the `Updater`
will retry on failures on the Telegram server.
bootstrap_retries (:obj:`int`, optional): Whether the bootstrapping phase of the
`Updater` will retry on failures on the Telegram server.
* < 0 - retry indefinitely
* 0 - no retries (default)
* < 0 - retry indefinitely (default)
* 0 - no retries
* > 0 - retry up to X times
webhook_url (:obj:`str`, optional): Explicitly specify the webhook url. Useful behind
@@ -236,8 +240,8 @@ class Updater(object):
Returns:
:obj:`Queue`: The update queue that can be filled from the main thread.
"""
"""
with self.__lock:
if not self.running:
self.running = True
@@ -252,51 +256,80 @@ class Updater(object):
return self.update_queue
def _start_polling(self, poll_interval, timeout, read_latency, bootstrap_retries, clean,
allowed_updates):
# """
allowed_updates): # pragma: no cover
# Thread target of thread 'updater'. Runs in background, pulls
# updates from Telegram and inserts them in the update queue of the
# Dispatcher.
# """
cur_interval = poll_interval
self.logger.debug('Updater thread started')
self.logger.debug('Updater thread started (polling)')
self._bootstrap(bootstrap_retries, clean=clean, webhook_url='', allowed_updates=None)
while self.running:
try:
updates = self.bot.get_updates(
self.last_update_id,
timeout=timeout,
read_latency=read_latency,
allowed_updates=allowed_updates)
except RetryAfter as e:
self.logger.info(str(e))
cur_interval = 0.5 + e.retry_after
except TelegramError as te:
self.logger.error("Error while getting Updates: {0}".format(te))
self.logger.debug('Bootstrap done')
# Put the error into the update queue and let the Dispatcher
# broadcast it
self.update_queue.put(te)
def polling_action_cb():
updates = self.bot.get_updates(
self.last_update_id, timeout=timeout, read_latency=read_latency,
allowed_updates=allowed_updates)
cur_interval = self._increase_poll_interval(cur_interval)
else:
if updates:
if not self.running:
if len(updates) > 0:
self.logger.debug('Updates ignored and will be pulled '
'again on restart.')
break
if updates:
self.logger.debug('Updates ignored and will be pulled again on restart')
else:
for update in updates:
self.update_queue.put(update)
self.last_update_id = updates[-1].update_id + 1
cur_interval = poll_interval
return True
sleep(cur_interval)
def polling_onerr_cb(exc):
# Put the error into the update queue and let the Dispatcher
# broadcast it
self.update_queue.put(exc)
self._network_loop_retry(polling_action_cb, polling_onerr_cb, 'getting Updates',
poll_interval)
def _network_loop_retry(self, action_cb, onerr_cb, description, interval):
"""Perform a loop calling `action_cb`, retrying after network errors.
Stop condition for loop: `self.running` evaluates False or return value of `action_cb`
evaluates False.
Args:
action_cb (:obj:`callable`): Network oriented callback function to call.
onerr_cb (:obj:`callable`): Callback to call when TelegramError is caught. Receives the
exception object as a parameter.
description (:obj:`str`): Description text to use for logs and exception raised.
interval (:obj:`float` | :obj:`int`): Interval to sleep between each call to
`action_cb`.
"""
self.logger.debug('Start network loop retry %s', description)
cur_interval = interval
while self.running:
try:
if not action_cb():
break
except RetryAfter as e:
self.logger.info('%s', e)
cur_interval = 0.5 + e.retry_after
except TimedOut as toe:
self.logger.debug('Timed out %s: %s', description, toe)
# If failure is due to timeout, we should retry asap.
cur_interval = 0
except InvalidToken as pex:
self.logger.error('Invalid token; aborting')
raise pex
except TelegramError as te:
self.logger.error('Error while %s: %s', description, te)
onerr_cb(te)
cur_interval = self._increase_poll_interval(cur_interval)
else:
cur_interval = interval
if cur_interval:
sleep(cur_interval)
@staticmethod
def _increase_poll_interval(current_interval):
@@ -311,7 +344,7 @@ class Updater(object):
def _start_webhook(self, listen, port, url_path, cert, key, bootstrap_retries, clean,
webhook_url, allowed_updates):
self.logger.debug('Updater thread started')
self.logger.debug('Updater thread started (webhook)')
use_ssl = cert is not None and key is not None
if not url_path.startswith('/'):
url_path = '/{0}'.format(url_path)
@@ -362,44 +395,59 @@ class Updater(object):
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, allowed_updates, cert=None):
retries = 0
while 1:
def _bootstrap(self, max_retries, clean, webhook_url, allowed_updates, cert=None,
bootstrap_interval=5):
retries = [0]
try:
if clean:
# Disable webhook for cleaning
self.bot.delete_webhook()
self._clean_updates()
sleep(1)
def bootstrap_del_webhook():
self.bot.delete_webhook()
return False
self.bot.set_webhook(
url=webhook_url, certificate=cert, allowed_updates=allowed_updates)
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
def bootstrap_clean_updates():
self.logger.debug('Cleaning updates from Telegram server')
updates = self.bot.get_updates()
while updates:
updates = self.bot.get_updates(updates[-1].update_id + 1)
return False
def bootstrap_set_webhook():
self.bot.set_webhook(
url=webhook_url, certificate=cert, allowed_updates=allowed_updates)
return False
def bootstrap_onerr_cb(exc):
if not isinstance(exc, Unauthorized) and (max_retries < 0 or retries[0] < max_retries):
retries[0] += 1
self.logger.warning('Failed bootstrap phase; try=%s max_retries=%s',
retries[0], max_retries)
else:
break
self.logger.error('Failed bootstrap phase after %s retries (%s)', retries[0], exc)
raise exc
# Cleaning pending messages is done by polling for them - so we need to delete webhook if
# one is configured.
# We also take this chance to delete pre-configured webhook if this is a polling Updater.
# NOTE: We don't know ahead if a webhook is configured, so we just delete.
if clean or not webhook_url:
self._network_loop_retry(bootstrap_del_webhook, bootstrap_onerr_cb,
'bootstrap del webhook', bootstrap_interval)
retries[0] = 0
# Clean pending messages, if requested.
if clean:
self._network_loop_retry(bootstrap_clean_updates, bootstrap_onerr_cb,
'bootstrap clean updates', bootstrap_interval)
retries[0] = 0
sleep(1)
def _clean_updates(self):
self.logger.debug('Cleaning updates from Telegram server')
updates = self.bot.get_updates()
while updates:
updates = self.bot.get_updates(updates[-1].update_id + 1)
# Restore/set webhook settings, if needed. Again, we don't know ahead if a webhook is set,
# so we set it anyhow.
if webhook_url:
self._network_loop_retry(bootstrap_set_webhook, bootstrap_onerr_cb,
'bootstrap set webhook', bootstrap_interval)
def stop(self):
"""
Stops the polling/webhook thread, the dispatcher and the job queue.
"""
"""Stops the polling/webhook thread, the dispatcher and the job queue."""
self.job_queue.stop()
with self.__lock:
@@ -438,6 +486,8 @@ class Updater(object):
def signal_handler(self, signum, frame):
self.is_idle = False
if self.running:
self.logger.info('Received signal {} ({}), stopping...'.format(
signum, get_signal_name(signum)))
self.stop()
if self.user_sig_handler:
self.user_sig_handler(signum, frame)
@@ -447,15 +497,14 @@ class Updater(object):
os._exit(1)
def idle(self, stop_signals=(SIGINT, SIGTERM, SIGABRT)):
"""
Blocks until one of the signals are received and stops the updater.
"""Blocks until one of the signals are received and stops the updater.
Args:
stop_signals (:obj:`iterable`): Iterable containing signals from the signal module that
should be subscribed to. Updater.stop() will be called on receiving one of those
signals. Defaults to (``SIGINT``, ``SIGTERM``, ``SIGABRT``).
"""
"""
for sig in stop_signals:
signal(sig, self.signal_handler)
+26 -4
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -22,8 +22,7 @@ from telegram import TelegramObject
class Audio(TelegramObject):
"""
This object represents an audio file to be treated as music by the Telegram clients.
"""This object represents an audio file to be treated as music by the Telegram clients.
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
@@ -33,6 +32,7 @@ class Audio(TelegramObject):
title (:obj:`str`): Optional. Title of the audio as defined by sender or by audio tags.
mime_type (:obj:`str`): Optional. MIME type of the file as defined by sender.
file_size (:obj:`int`): Optional. File size.
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
@@ -42,7 +42,9 @@ class Audio(TelegramObject):
title (:obj:`str`, optional): Title of the audio as defined by sender or by audio tags.
mime_type (:obj:`str`, optional): MIME type of the file as defined by sender.
file_size (:obj:`int`, optional): File size.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self,
@@ -52,6 +54,7 @@ class Audio(TelegramObject):
title=None,
mime_type=None,
file_size=None,
bot=None,
**kwargs):
# Required
self.file_id = str(file_id)
@@ -61,6 +64,7 @@ class Audio(TelegramObject):
self.title = title
self.mime_type = mime_type
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
@@ -69,4 +73,22 @@ class Audio(TelegramObject):
if not data:
return None
return cls(**data)
return cls(bot=bot, **data)
def get_file(self, timeout=None, **kwargs):
"""Convenience wrapper over :attr:`telegram.Bot.get_file`
Args:
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
the read timeout from the server (instead of the one specified during creation of
the connection pool).
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
Returns:
:class:`telegram.File`
Raises:
:class:`telegram.TelegramError`
"""
return self.bot.get_file(self.file_id, timeout=timeout, **kwargs)
+3 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -24,8 +24,7 @@ from telegram import TelegramObject
class ChatPhoto(TelegramObject):
"""
This object represents a chat photo.
"""This object represents a chat photo.
Attributes:
small_file_id (:obj:`str`): Unique file identifier of small (160x160) chat photo.
@@ -38,6 +37,7 @@ class ChatPhoto(TelegramObject):
can be used only for photo download.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self, small_file_id, big_file_id, bot=None, **kwargs):
+3 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -22,8 +22,7 @@ from telegram import TelegramObject
class Contact(TelegramObject):
"""
This object represents a phone contact.
"""This object represents a phone contact.
Attributes:
phone_number (:obj:`str`): Contact's phone number.
@@ -37,6 +36,7 @@ class Contact(TelegramObject):
last_name (:obj:`str`, optional): Contact's last name.
user_id (:obj:`int`, optional): Contact's user identifier in Telegram.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self, phone_number, first_name, last_name=None, user_id=None, **kwargs):
+26 -5
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -22,8 +22,7 @@ from telegram import PhotoSize, TelegramObject
class Document(TelegramObject):
"""
This object represents a general file (as opposed to photos, voice messages and audio files).
"""This object represents a general file (as opposed to photos, voice messages and audio files).
Attributes:
file_id (:obj:`str`): Unique file identifier.
@@ -31,6 +30,7 @@ class Document(TelegramObject):
file_name (:obj:`str`): Original filename.
mime_type (:obj:`str`): Optional. MIME type of the file.
file_size (:obj:`int`): Optional. File size.
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique file identifier
@@ -38,9 +38,10 @@ class Document(TelegramObject):
file_name (:obj:`str`, optional): Original filename as defined by sender.
mime_type (:obj:`str`, optional): MIME type of the file as defined by sender.
file_size (:obj:`int`, optional): File size.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
"""
_id_keys = ('file_id',)
def __init__(self,
@@ -49,6 +50,7 @@ class Document(TelegramObject):
file_name=None,
mime_type=None,
file_size=None,
bot=None,
**kwargs):
# Required
self.file_id = str(file_id)
@@ -57,6 +59,7 @@ class Document(TelegramObject):
self.file_name = file_name
self.mime_type = mime_type
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
@@ -69,4 +72,22 @@ class Document(TelegramObject):
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
return cls(**data)
return cls(bot=bot, **data)
def get_file(self, timeout=None, **kwargs):
"""Convenience wrapper over :attr:`telegram.Bot.get_file`
Args:
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
the read timeout from the server (instead of the one specified during creation of
the connection pool).
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
Returns:
:class:`telegram.File`
Raises:
:class:`telegram.TelegramError`
"""
return self.bot.get_file(self.file_id, timeout=timeout, **kwargs)
+38 -11
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -44,6 +44,7 @@ class File(TelegramObject):
file_path (:obj:`str`, optional): File path. Use :attr:`download` to get the file.
bot (:obj:`telegram.Bot`, optional): Bot to use with shortcut method.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self, file_id, bot=None, file_size=None, file_path=None, **kwargs):
@@ -73,32 +74,34 @@ class File(TelegramObject):
that object using the ``out.write`` method.
Note:
`custom_path` and `out` are mutually exclusive.
:attr:`custom_path` and :attr:`out` are mutually exclusive.
Args:
custom_path (:obj:`str`, optional): Custom path.
out (:obj:`object`, optional): A file-like object. Must be opened in binary mode, if
applicable.
out (:obj:`io.BufferedWriter`, optional): A file-like object. Must be opened for
writing in binary mode, if applicable.
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
the read timeout from the server (instead of the one specified during creation of
the connection pool).
Raises:
ValueError: If both ``custom_path`` and ``out`` are passed.
"""
Returns:
:obj:`str` | :obj:`io.BufferedWriter`: The same object as :attr:`out` if specified.
Otherwise, returns the filename downloaded to.
Raises:
ValueError: If both :attr:`custom_path` and :attr:`out` are passed.
"""
if custom_path is not None and out is not None:
raise ValueError('custom_path and out are mutually exclusive')
# Convert any UTF-8 char into a url encoded ASCII string.
sres = urllib_parse.urlsplit(self.file_path)
url = urllib_parse.urlunsplit(urllib_parse.SplitResult(
sres.scheme, sres.netloc, urllib_parse.quote(sres.path), sres.query, sres.fragment))
url = self._get_encoded_url()
if out:
buf = self.bot.request.retrieve(url)
out.write(buf)
return out
else:
if custom_path:
filename = custom_path
@@ -106,3 +109,27 @@ class File(TelegramObject):
filename = basename(self.file_path)
self.bot.request.download(url, filename, timeout=timeout)
return filename
def _get_encoded_url(self):
"""Convert any UTF-8 char in :obj:`File.file_path` into a url encoded ASCII string."""
sres = urllib_parse.urlsplit(self.file_path)
return urllib_parse.urlunsplit(urllib_parse.SplitResult(
sres.scheme, sres.netloc, urllib_parse.quote(sres.path), sres.query, sres.fragment))
def download_as_bytearray(self, buf=None):
"""Download this file and return it as a bytearray.
Args:
buf (:obj:`bytearray`, optional): Extend the given bytearray with the downloaded data.
Returns:
:obj:`bytearray`: The same object as :attr:`buf` if it was specified. Otherwise a newly
allocated :obj:`bytearray`.
"""
if buf is None:
buf = bytearray()
buf.extend(self.bot.request.retrieve(self._get_encoded_url()))
return buf
+11 -19
View File
@@ -2,7 +2,7 @@
# pylint: disable=W0622,E0611
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -40,8 +40,7 @@ FILE_TYPES = ('audio', 'document', 'photo', 'sticker', 'video', 'voice', 'certif
class InputFile(object):
"""
This object represents a Telegram InputFile.
"""This object represents a Telegram InputFile.
Attributes:
data (:obj:`dict`): Data containing an inputfile.
@@ -51,6 +50,7 @@ class InputFile(object):
Raises:
TelegramError
"""
def __init__(self, data):
@@ -88,28 +88,22 @@ class InputFile(object):
@property
def headers(self):
"""
:obj:`dict`: Headers.
"""
""":obj:`dict`: Headers."""
return {'User-agent': USER_AGENT, 'Content-type': self.content_type}
@property
def content_type(self):
"""
:obj:`str`: Content type
"""
""":obj:`str`: Content type"""
return 'multipart/form-data; boundary=%s' % self.boundary
def to_form(self):
"""
Transform the inputfile to multipart/form data.
"""Transform the inputfile to multipart/form data.
Returns:
:obj:`str`
"""
"""
form = []
form_boundary = '--' + self.boundary
@@ -148,16 +142,15 @@ class InputFile(object):
@staticmethod
def is_image(stream):
"""
Check if the content file is an image by analyzing its headers.
"""Check if the content file is an image by analyzing its headers.
Args:
stream (:obj:`str`): A str representing the content of a file.
Returns:
:obj:`str`: The str mime-type of an image.
"""
"""
image = imghdr.what(None, stream)
if image:
return 'image/%s' % image
@@ -166,16 +159,15 @@ class InputFile(object):
@staticmethod
def is_inputfile(data):
"""
Check if the request is a file request.
"""Check if the request is a file request.
Args:
data (Dict[:obj:`str`, :obj:`str`]): A dict of (str, str) key/value pairs.
Returns:
:obj:`bool`
"""
"""
if data:
file_type = [i for i in iter(data) if i in FILE_TYPES]
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# 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/].
"""Base class for Telegram InputMedia Objects."""
from telegram import TelegramObject
class InputMedia(TelegramObject):
"""Base class for Telegram InputMedia Objects.
See :class:`telegram.InputMediaPhoto` and :class:`telegram.InputMediaVideo` for
detailed use.
"""
pass
+65
View File
@@ -0,0 +1,65 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# 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 an object that represents a Telegram InputMediaPhoto."""
from telegram import InputMedia, PhotoSize
class InputMediaPhoto(InputMedia):
"""Represents a photo to be sent.
Attributes:
type (:obj:`str`): ``photo``.
media (:obj:`str`): File to send. Pass a file_id to send a file that exists on the
Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the
Internet. Lastly you can pass an existing :class:`telegram.PhotoSize` object to send.
caption (:obj:`str`): Optional. Caption of the photo to be sent, 0-200 characters.
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption.. See the constants
in :class:`telegram.ParseMode` for the available modes.
Args:
media (:obj:`str`): File to send. Pass a file_id to send a file that exists on the
Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the
Internet. Lastly you can pass an existing :class:`telegram.PhotoSize` object to send.
caption (:obj:`str`, optional ): Caption of the photo to be sent, 0-200 characters.
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption.. See the constants
in :class:`telegram.ParseMode` for the available modes.
Note:
At the moment using a new file is not yet supported.
"""
# TODO: Make InputMediaPhoto, InputMediaVideo and send_media_group work with new files
def __init__(self, media, caption=None, parse_mode=None):
self.type = 'photo'
if isinstance(media, PhotoSize):
self.media = media.file_id
elif hasattr(media, 'read'):
raise ValueError(
'Sending files is not supported (yet). Use file_id, url or PhotoSize')
else:
self.media = media
if caption:
self.caption = caption
if parse_mode:
self.parse_mode = parse_mode
+89
View File
@@ -0,0 +1,89 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# 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 an object that represents a Telegram InputMediaPhoto."""
from telegram import InputMedia, Video
class InputMediaVideo(InputMedia):
"""Represents a video to be sent.
Attributes:
type (:obj:`str`): ``video``.
media (:obj:`str`): File to send. Pass a file_id to send a file that exists on the Telegram
servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet.
Lastly you can pass an existing :class:`telegram.Video` object to send.
caption (:obj:`str`): Optional. Caption of the video to be sent, 0-200 characters.
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption.. See the constants
in :class:`telegram.ParseMode` for the available modes.
width (:obj:`int`): Optional. Video width.
height (:obj:`int`): Optional. Video height.
duration (:obj:`int`): Optional. Video duration.
supports_streaming (:obj:`bool`): Optional. Pass True, if the uploaded video is suitable
for streaming.
Args:
media (:obj:`str`): File to send. Pass a file_id to send a file that exists on the Telegram
servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet.
Lastly you can pass an existing :class:`telegram.Video` object to send.
caption (:obj:`str`, optional): Caption of the video to be sent, 0-200 characters.
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption.. See the constants
in :class:`telegram.ParseMode` for the available modes.
width (:obj:`int`, optional): Video width.
height (:obj:`int`, optional): Video height.
duration (:obj:`int`, optional): Video duration.
supports_streaming (:obj:`bool`, optional): Pass True, if the uploaded video is suitable
for streaming.
Note:
When using a :class:`telegram.Video` for the :attr:`media` attribute. It will take the
width, height and duration from that video, unless otherwise specified with the optional
arguments.
At the moment using a new file is not yet supported.
"""
# TODO: Make InputMediaPhoto, InputMediaVideo and send_media_group work with new files
def __init__(self, media, caption=None, width=None, height=None, duration=None,
supports_streaming=None, parse_mode=None):
self.type = 'video'
if isinstance(media, Video):
self.media = media.file_id
self.width = media.width
self.height = media.height
self.duration = media.duration
elif hasattr(media, 'read'):
raise ValueError('Sending files is not supported (yet). Use file_id, url or Video')
else:
self.media = media
if caption:
self.caption = caption
if parse_mode:
self.parse_mode = parse_mode
if width:
self.width = width
if height:
self.height = height
if duration:
self.duration = duration
if supports_streaming:
self.supports_streaming = supports_streaming
+3 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -22,8 +22,7 @@ from telegram import TelegramObject
class Location(TelegramObject):
"""
This object represents a point on the map.
"""This object represents a point on the map.
Attributes:
longitude (:obj:`float`): Longitude as defined by sender.
@@ -33,6 +32,7 @@ class Location(TelegramObject):
longitude (:obj:`float`): Longitude as defined by sender.
latitude (:obj:`float`): Latitude as defined by sender.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self, longitude, latitude, **kwargs):
+26 -5
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -22,30 +22,33 @@ from telegram import TelegramObject
class PhotoSize(TelegramObject):
"""
This object represents one size of a photo or a file/sticker thumbnail.
"""This object represents one size of a photo or a file/sticker thumbnail.
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
width (:obj:`int`): Photo width.
height (:obj:`int`): Photo height.
file_size (:obj:`int`): Optional. File size.
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
width (:obj:`int`): Photo width.
height (:obj:`int`): Photo height.
file_size (:obj:`int`, optional): File size.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self, file_id, width, height, file_size=None, **kwargs):
def __init__(self, file_id, width, height, file_size=None, bot=None, **kwargs):
# Required
self.file_id = str(file_id)
self.width = int(width)
self.height = int(height)
# Optionals
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
@@ -54,7 +57,7 @@ class PhotoSize(TelegramObject):
if not data:
return None
return cls(**data)
return cls(bot=bot, **data)
@classmethod
def de_list(cls, data, bot):
@@ -66,3 +69,21 @@ class PhotoSize(TelegramObject):
photos.append(cls.de_json(photo, bot))
return photos
def get_file(self, timeout=None, **kwargs):
"""Convenience wrapper over :attr:`telegram.Bot.get_file`
Args:
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
the read timeout from the server (instead of the one specified during creation of
the connection pool).
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
Returns:
:class:`telegram.File`
Raises:
:class:`telegram.TelegramError`
"""
return self.bot.get_file(self.file_id, timeout=timeout, **kwargs)
+31 -10
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -22,8 +22,7 @@ from telegram import PhotoSize, TelegramObject
class Sticker(TelegramObject):
"""
This object represents a sticker.
"""This object represents a sticker.
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
@@ -36,6 +35,7 @@ class Sticker(TelegramObject):
mask_position (:class:`telegram.MaskPosition`): Optional. For mask stickers, the position
where the mask should be placed.
file_size (:obj:`int`): Optional. File size.
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
@@ -49,7 +49,9 @@ class Sticker(TelegramObject):
mask_position (:class:`telegram.MaskPosition`, optional): For mask stickers, the
position where the mask should be placed.
file_size (:obj:`int`, optional): File size.
**kwargs (obj:`dict`): Arbitrary keyword arguments.
**kwargs (obj:`dict`): Arbitrary keyword arguments.7
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
"""
def __init__(self,
@@ -61,6 +63,7 @@ class Sticker(TelegramObject):
file_size=None,
set_name=None,
mask_position=None,
bot=None,
**kwargs):
# Required
self.file_id = str(file_id)
@@ -72,6 +75,7 @@ class Sticker(TelegramObject):
self.file_size = file_size
self.set_name = set_name
self.mask_position = mask_position
self.bot = bot
self._id_attrs = (self.file_id,)
@@ -85,7 +89,7 @@ class Sticker(TelegramObject):
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
data['mask_position'] = MaskPosition.de_json(data.get('mask_position'), bot)
return cls(**data)
return cls(bot=bot, **data)
@classmethod
def de_list(cls, data, bot):
@@ -94,10 +98,27 @@ class Sticker(TelegramObject):
return [cls.de_json(d, bot) for d in data]
def get_file(self, timeout=None, **kwargs):
"""Convenience wrapper over :attr:`telegram.Bot.get_file`
Args:
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
the read timeout from the server (instead of the one specified during creation of
the connection pool).
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
Returns:
:class:`telegram.File`
Raises:
:class:`telegram.TelegramError`
"""
return self.bot.get_file(self.file_id, timeout=timeout, **kwargs)
class StickerSet(TelegramObject):
"""
This object represents a sticker set.
"""This object represents a sticker set.
Attributes:
name (:obj:`str`): Sticker set name.
@@ -110,6 +131,7 @@ class StickerSet(TelegramObject):
title (:obj:`str`): Sticker set title.
contains_masks (:obj:`bool`): True, if the sticker set contains masks.
stickers (List[:class:`telegram.Sticker`]): List of all set stickers.
"""
def __init__(self, name, title, contains_masks, stickers, bot=None, **kwargs):
@@ -140,8 +162,7 @@ class StickerSet(TelegramObject):
class MaskPosition(TelegramObject):
"""
This object describes the position on faces where a mask should be placed by default.
"""This object describes the position on faces where a mask should be placed by default.
Attributes:
point (:obj:`str`): The part of the face relative to which the mask should be placed.
@@ -164,8 +185,8 @@ class MaskPosition(TelegramObject):
size, from top to bottom. For example, 1.0 will place the mask just below the default
mask position.
scale (:obj:`float`): Mask scaling coefficient. For example, 2.0 means double size.
"""
"""
FOREHEAD = 'forehead'
""":obj:`str`: 'forehead'"""
EYES = 'eyes'
+3 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -22,8 +22,7 @@ from telegram import TelegramObject, Location
class Venue(TelegramObject):
"""
This object represents a venue.
"""This object represents a venue.
Attributes:
location (:class:`telegram.Location`): Venue location.
@@ -37,6 +36,7 @@ class Venue(TelegramObject):
address (:obj:`str`): Address of the venue.
foursquare_id (:obj:`str`, optional): Foursquare identifier of the venue.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self, location, title, address, foursquare_id=None, **kwargs):
+26 -4
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -22,8 +22,7 @@ from telegram import PhotoSize, TelegramObject
class Video(TelegramObject):
"""
This object represents a video file.
"""This object represents a video file.
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
@@ -33,6 +32,7 @@ class Video(TelegramObject):
thumb (:class:`telegram.PhotoSize`): Optional. Video thumbnail.
mime_type (:obj:`str`): Optional. Mime type of a file as defined by sender.
file_size (:obj:`int`): Optional. File size.
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
@@ -42,7 +42,9 @@ class Video(TelegramObject):
thumb (:class:`telegram.PhotoSize`, optional): Video thumbnail.
mime_type (:obj:`str`, optional): Mime type of a file as defined by sender.
file_size (:obj:`int`, optional): File size.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self,
@@ -53,6 +55,7 @@ class Video(TelegramObject):
thumb=None,
mime_type=None,
file_size=None,
bot=None,
**kwargs):
# Required
self.file_id = str(file_id)
@@ -63,6 +66,7 @@ class Video(TelegramObject):
self.thumb = thumb
self.mime_type = mime_type
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
@@ -75,4 +79,22 @@ class Video(TelegramObject):
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
return cls(**data)
return cls(bot=bot, **data)
def get_file(self, timeout=None, **kwargs):
"""Convenience wrapper over :attr:`telegram.Bot.get_file`
Args:
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
the read timeout from the server (instead of the one specified during creation of
the connection pool).
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
Returns:
:class:`telegram.File`
Raises:
:class:`telegram.TelegramError`
"""
return self.bot.get_file(self.file_id, timeout=timeout, **kwargs)
+26 -5
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -22,8 +22,7 @@ from telegram import PhotoSize, TelegramObject
class VideoNote(TelegramObject):
"""
This object represents a video message (available in Telegram apps as of v.4.0).
"""This object represents a video message (available in Telegram apps as of v.4.0).
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
@@ -31,6 +30,7 @@ class VideoNote(TelegramObject):
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
thumb (:class:`telegram.PhotoSize`): Optional. Video thumbnail.
file_size (:obj:`int`): Optional. File size.
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
@@ -38,10 +38,12 @@ class VideoNote(TelegramObject):
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
thumb (:class:`telegram.PhotoSize`, optional): Video thumbnail.
file_size (:obj:`int`, optional): File size.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self, file_id, length, duration, thumb=None, file_size=None, **kwargs):
def __init__(self, file_id, length, duration, thumb=None, file_size=None, bot=None, **kwargs):
# Required
self.file_id = str(file_id)
self.length = int(length)
@@ -49,6 +51,7 @@ class VideoNote(TelegramObject):
# Optionals
self.thumb = thumb
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
@@ -61,4 +64,22 @@ class VideoNote(TelegramObject):
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
return cls(**data)
return cls(bot=bot, **data)
def get_file(self, timeout=None, **kwargs):
"""Convenience wrapper over :attr:`telegram.Bot.get_file`
Args:
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
the read timeout from the server (instead of the one specified during creation of
the connection pool).
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
Returns:
:class:`telegram.File`
Raises:
:class:`telegram.TelegramError`
"""
return self.bot.get_file(self.file_id, timeout=timeout, **kwargs)
+26 -5
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -22,30 +22,33 @@ from telegram import TelegramObject
class Voice(TelegramObject):
"""
This object represents a voice note.
"""This object represents a voice note.
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
duration (:obj:`int`): Duration of the audio in seconds as defined by sender.
mime_type (:obj:`str`): Optional. MIME type of the file as defined by sender.
file_size (:obj:`int`): Optional. File size.
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
duration (:obj:`int`, optional): Duration of the audio in seconds as defined by sender.
mime_type (:obj:`str`, optional): MIME type of the file as defined by sender.
file_size (:obj:`int`, optional): File size.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self, file_id, duration, mime_type=None, file_size=None, **kwargs):
def __init__(self, file_id, duration, mime_type=None, file_size=None, bot=None, **kwargs):
# Required
self.file_id = str(file_id)
self.duration = int(duration)
# Optionals
self.mime_type = mime_type
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
@@ -56,4 +59,22 @@ class Voice(TelegramObject):
data = super(Voice, cls).de_json(data, bot)
return cls(**data)
return cls(bot=bot, **data)
def get_file(self, timeout=None, **kwargs):
"""Convenience wrapper over :attr:`telegram.Bot.get_file`
Args:
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
the read timeout from the server (instead of the one specified during creation of
the connection pool).
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
Returns:
:class:`telegram.File`
Raises:
:class:`telegram.TelegramError`
"""
return self.bot.get_file(self.file_id, timeout=timeout, **kwargs)
+2 -8
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -41,6 +41,7 @@ class ForceReply(ReplyMarkup):
original message.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self, force_reply=True, selective=False, **kwargs):
@@ -48,10 +49,3 @@ class ForceReply(ReplyMarkup):
self.force_reply = bool(force_reply)
# Optionals
self.selective = bool(selective)
@classmethod
def de_json(cls, data, bot):
if not data:
return None
return cls(**data)
+3 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -22,8 +22,7 @@ from telegram import TelegramObject
class Animation(TelegramObject):
"""
This object represents an animation file to be displayed in the message containing a game.
"""This object represents an animation file to be displayed in the message containing a game.
Attributes:
file_id (:obj:`str`): Unique file identifier.
@@ -39,6 +38,7 @@ class Animation(TelegramObject):
file_name (:obj:`str`, optional): Original animation filename as defined by sender.
mime_type (:obj:`str`, optional): MIME type of the file as defined by sender.
file_size (:obj:`int`, optional): File size.
"""
def __init__(self,
+2 -4
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -22,6 +22,4 @@ from telegram import TelegramObject
class CallbackGame(TelegramObject):
"""
A placeholder, currently holds no information. Use BotFather to set up your game.
"""
"""A placeholder, currently holds no information. Use BotFather to set up your game."""
+8 -7
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -54,6 +54,7 @@ class Game(TelegramObject):
appear in text, such as usernames, URLs, bot commands, etc.
animation (:class:`telegram.Animation`, optional): Animation that will be displayed in the
game message in chats. Upload via BotFather.
"""
def __init__(self,
@@ -68,7 +69,7 @@ class Game(TelegramObject):
self.description = description
self.photo = photo
self.text = text
self.text_entities = text_entities
self.text_entities = text_entities or list()
self.animation = animation
@classmethod
@@ -88,13 +89,13 @@ class Game(TelegramObject):
data = super(Game, self).to_dict()
data['photo'] = [p.to_dict() for p in self.photo]
data['text_entities'] = [x.to_dict() for x in self.text_entities]
if self.text_entities:
data['text_entities'] = [x.to_dict() for x in self.text_entities]
return data
def parse_text_entity(self, entity):
"""
Returns the text from a given :class:`telegram.MessageEntity`.
"""Returns the text from a given :class:`telegram.MessageEntity`.
Note:
This method is present because Telegram calculates the offset and length in
@@ -107,8 +108,8 @@ class Game(TelegramObject):
Returns:
:obj:`str`: The text of the given entity.
"""
"""
# Is it a narrow build, if so we don't need to convert
if sys.maxunicode == 0xffff:
return self.text[entity.offset:entity.offset + entity.length]
@@ -137,8 +138,8 @@ class Game(TelegramObject):
Returns:
Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to
the text that belongs to them, calculated based on UTF-16 codepoints.
"""
"""
if types is None:
types = MessageEntity.ALL_TYPES
+3 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -22,8 +22,7 @@ from telegram import TelegramObject, User
class GameHighScore(TelegramObject):
"""
This object represents one row of the high scores table for a game.
"""This object represents one row of the high scores table for a game.
Attributes:
position (:obj:`int`): Position in high score table for the game.
@@ -34,6 +33,7 @@ class GameHighScore(TelegramObject):
position (:obj:`int`): Position in high score table for the game.
user (:class:`telegram.User`): User.
score (:obj:`int`): Score.
"""
def __init__(self, position, user, score):
+4 -25
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -16,15 +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 an object that represents a Telegram
InlineKeyboardButton"""
"""This module contains an object that represents a Telegram InlineKeyboardButton."""
from telegram import TelegramObject
class InlineKeyboardButton(TelegramObject):
"""
This object represents one button of an inline keyboard.
"""This object represents one button of an inline keyboard.
Note:
You must use exactly one of the optional fields. Mind that :attr:`callback_game` is not
@@ -67,6 +65,7 @@ class InlineKeyboardButton(TelegramObject):
pay (:obj:`bool`, optional): Specify True, to send a Pay button. This type of button must
always be the ``first`` button in the first row.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self,
@@ -88,23 +87,3 @@ class InlineKeyboardButton(TelegramObject):
self.switch_inline_query_current_chat = switch_inline_query_current_chat
self.callback_game = callback_game
self.pay = pay
@classmethod
def de_json(cls, data, bot):
data = super(InlineKeyboardButton, cls).de_json(data, bot)
if not data:
return None
return cls(**data)
@classmethod
def de_list(cls, data, bot):
if not data:
return []
inline_keyboards = list()
for inline_keyboard in data:
inline_keyboards.append(cls.de_json(inline_keyboard, bot))
return inline_keyboards
+3 -16
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -18,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram InlineKeyboardMarkup."""
from telegram import ReplyMarkup, InlineKeyboardButton
from telegram import ReplyMarkup
class InlineKeyboardMarkup(ReplyMarkup):
@@ -33,26 +33,13 @@ class InlineKeyboardMarkup(ReplyMarkup):
inline_keyboard (List[List[:class:`telegram.InlineKeyboardButton`]]): Array of button rows,
each represented by an Array of InlineKeyboardButton objects.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self, inline_keyboard, **kwargs):
# Required
self.inline_keyboard = inline_keyboard
@classmethod
def de_json(cls, data, bot):
data = super(InlineKeyboardMarkup, cls).de_json(data, bot)
if not data:
return None
data['inline_keyboard'] = [
InlineKeyboardButton.de_list(inline_keyboard, bot)
for inline_keyboard in data['inline_keyboard']
]
return cls(**data)
def to_dict(self):
data = super(InlineKeyboardMarkup, self).to_dict()
+6 -14
View File
@@ -2,7 +2,7 @@
# pylint: disable=R0902,R0912,R0913
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -17,7 +17,7 @@
#
# 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 an object that represents a Telegram InlineQuery"""
"""This module contains an object that represents a Telegram InlineQuery."""
from telegram import TelegramObject, User, Location
@@ -47,6 +47,7 @@ class InlineQuery(TelegramObject):
offset (:obj:`str`): Offset of the results to be returned, can be controlled by the bot.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self, id, from_user, query, offset, location=None, bot=None, **kwargs):
@@ -74,19 +75,10 @@ class InlineQuery(TelegramObject):
return cls(bot=bot, **data)
def to_dict(self):
data = super(InlineQuery, self).to_dict()
# Required
data['from'] = data.pop('from_user', None)
return data
def answer(self, *args, **kwargs):
"""
Shortcut for::
"""Shortcut for::
bot.answer_inline_query(update.inline_query.id, *args, **kwargs)
bot.answer_inline_query(update.inline_query.id, *args, **kwargs)
Args:
results (List[:class:`telegram.InlineQueryResult`]): A list of results for the inline
@@ -106,6 +98,6 @@ class InlineQuery(TelegramObject):
switch_pm_parameter (:obj:`str`, optional): Deep-linking parameter for the /start
message sent to the bot when user presses the switch button. 1-64 characters,
only A-Z, a-z, 0-9, _ and - are allowed.
"""
"""
return self.bot.answer_inline_query(self.id, *args, **kwargs)
+4 -4
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -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 the classes that represent Telegram InlineQueryResult"""
"""This module contains the classes that represent Telegram InlineQueryResult."""
from telegram import TelegramObject
class InlineQueryResult(TelegramObject):
"""
Baseclass for the InlineQueryResult* classes.
"""Baseclass for the InlineQueryResult* classes.
Attributes:
type (:obj:`str`): Type of the result.
@@ -33,6 +32,7 @@ class InlineQueryResult(TelegramObject):
type (:obj:`str`): Type of the result.
id (:obj:`str`): Unique identifier for this result, 1-64 Bytes.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self, type, id, **kwargs):
+4 -17
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -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 the classes that represent Telegram
InlineQueryResultArticle"""
"""This module contains the classes that represent Telegram InlineQueryResultArticle."""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
from telegram import InlineQueryResult
class InlineQueryResultArticle(InlineQueryResult):
@@ -56,6 +55,7 @@ class InlineQueryResultArticle(InlineQueryResult):
thumb_width (:obj:`int`, optional): Thumbnail width.
thumb_height (:obj:`int`, optional): Thumbnail height.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self,
@@ -91,16 +91,3 @@ class InlineQueryResultArticle(InlineQueryResult):
self.thumb_width = thumb_width
if thumb_height:
self.thumb_height = thumb_height
@classmethod
def de_json(cls, data, bot):
data = super(InlineQueryResultArticle, cls).de_json(data, bot)
if not data:
return None
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
return cls(**data)
+13 -17
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -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 the classes that represent Telegram
InlineQueryResultAudio"""
"""This module contains the classes that represent Telegram InlineQueryResultAudio."""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
from telegram import InlineQueryResult
class InlineQueryResultAudio(InlineQueryResult):
@@ -36,6 +35,9 @@ class InlineQueryResultAudio(InlineQueryResult):
performer (:obj:`str`): Optional. Caption, 0-200 characters.
audio_duration (:obj:`str`): Optional. Performer.
caption (:obj:`str`): Optional. Audio duration in seconds.
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption.. See the constants
in :class:`telegram.ParseMode` for the available modes.
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
to the message.
input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the
@@ -48,11 +50,15 @@ class InlineQueryResultAudio(InlineQueryResult):
performer (:obj:`str`, optional): Caption, 0-200 characters.
audio_duration (:obj:`str`, optional): Performer.
caption (:obj:`str`, optional): Audio duration in seconds.
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption.. See the constants
in :class:`telegram.ParseMode` for the available modes.
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
to the message.
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
message to be sent instead of the audio.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self,
@@ -64,6 +70,7 @@ class InlineQueryResultAudio(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
**kwargs):
# Required
@@ -78,20 +85,9 @@ class InlineQueryResultAudio(InlineQueryResult):
self.audio_duration = audio_duration
if caption:
self.caption = caption
if parse_mode:
self.parse_mode = parse_mode
if reply_markup:
self.reply_markup = reply_markup
if input_message_content:
self.input_message_content = input_message_content
@classmethod
def de_json(cls, data, bot):
data = super(InlineQueryResultAudio, cls).de_json(data, bot)
if not data:
return None
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
return cls(**data)
+13 -17
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Copyright (C) 2015-2018
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -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 the classes that represent Telegram
InlineQueryResultCachedAudio"""
"""This module contains the classes that represent Telegram InlineQueryResultCachedAudio."""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
from telegram import InlineQueryResult
class InlineQueryResultCachedAudio(InlineQueryResult):
@@ -33,6 +32,9 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
audio_file_id (:obj:`str`): A valid file identifier for the audio file.
caption (:obj:`str`): Optional. Caption, 0-200 characters
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption.. See the constants
in :class:`telegram.ParseMode` for the available modes.
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
to the message.
input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the
@@ -42,11 +44,15 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
audio_file_id (:obj:`str`): A valid file identifier for the audio file.
caption (:obj:`str`, optional): Caption, 0-200 characters
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption.. See the constants
in :class:`telegram.ParseMode` for the available modes.
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
to the message.
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
message to be sent instead of the audio.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self,
@@ -55,6 +61,7 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
**kwargs):
# Required
super(InlineQueryResultCachedAudio, self).__init__('audio', id)
@@ -63,20 +70,9 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
# Optionals
if caption:
self.caption = caption
if parse_mode:
self.parse_mode = parse_mode
if reply_markup:
self.reply_markup = reply_markup
if input_message_content:
self.input_message_content = input_message_content
@classmethod
def de_json(cls, data, bot):
data = super(InlineQueryResultCachedAudio, cls).de_json(data, bot)
if not data:
return None
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
return cls(**data)

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