Compare commits

...

124 Commits

Author SHA1 Message Date
Jannes Höke 6479e15578 Bump version to v6.0.0 2017-05-19 21:49:01 +02:00
Jannes Höke 5dd3a660e3 forgot some escaping 2017-05-19 20:57:52 +02:00
Eldinnie e2a651afc8 Allow edited as seperate input (#608)
* Allow edited as seperate input

In short made it possible to tune messagehandler more to your wishes. and choose exactly what updates to receive. messages, edited_message or channel_post or a combination.

- Added the edited_updates argument to MessageHandler
- Added DepricationWarning when using allow_edited
- replaced _is_allowed_message and _is_allowed_channel_post with _is_allowed_update
- Modified tests to reflect new way

* oops

Spelled deprecation wrong
made an error in the _is_allowed_update.

* Python 2 does not have assertWarns.

* remove unneeded statements
2017-05-19 20:26:10 +02:00
Evgen 33512ffd2e Improved CommandHandler (#613)
* Improved CommandHandler

Now you can pass list of commands instead of one command

* Added tests for list of commands

* Return backward compatibility

Renamed `commands` to `command` in CommandHandler

* Added test for a command not in the list

* Fixed py2 unicode command support in `CommandHandler`
2017-05-19 20:21:37 +02:00
Evgen c2c5452829 Updated docstring in deleteMessage methods (#618)
* Removed "undocumented" notice

* Updated docstring according to Bot API documentation

* Removed extra line
2017-05-19 20:21:20 +02:00
Jannes Höke 9aa5522694 sanitize html and markdown in Message.text_html and text_markdown (#621)
* sanitize html and markdown in Message.text_html and text_markdown

* add import for escape_html
2017-05-19 19:11:40 +02:00
Noam Meltzer 9720f59d7e Fix docs (#614)
* Spelling fixes in `README.rst`

Fixes #609 and #610

* Fixed and added some type hints in docstrings
2017-05-17 22:46:06 +03:00
evgfilim1 578627feca Fixed and added some type hints in docstrings 2017-05-17 21:41:08 +05:00
evgfilim1 257b8321f8 Spelling fixes in README.rst
Fixes #609 and #610
2017-05-16 22:59:02 +05:00
Eldinnie 4f2ea5b0f1 Update readme to reflect changed install procedure (#610)
Fixes #609
2017-05-16 10:32:36 +03:00
Noam Meltzer 9a2d5e0410 Update copyright notice to include 2017 - missing files from another PR 2017-05-15 00:37:40 +03:00
Jacob Bom 1b7f83625c Update copyright notice to include 2017
Not strictly needed, but it helps show that the project is being actively
developed which I find important.
2017-05-15 00:36:49 +03:00
Jacob Bom da01601ff9 Add equality rich comparision operators to telegram objects (#604)
fixes #587
2017-05-15 00:29:31 +03:00
Evgen 752b64769d Fix for #601 (#603) 2017-05-12 17:42:02 +02:00
Evgen f3b75d95c4 Implemented undocumented 'deleteMessage' method (#602)
* Implemented `Bot.delete_message()` and `Message.delete()` methods

* Added tests for `Bot.delete_message()` and `Messsage.delete()`

* Added myself to `AUTHORS.rst`

* Using `assertRaisesRegexp` instead of `assertRaisesRegex` in tests

Now tests can run on Python 2.7 without raising 'AttributeError'

* Fix docstring

Added a warning to the docstring describing that this method is undocumented and not guaranteed to work
2017-05-12 17:40:57 +02:00
Eldinnie 36b98b0ab4 fix mimetype errors in video tests (#600) 2017-05-12 17:39:28 +02:00
Eldinnie 05b85d4334 Skip test_idle() and test_UserSignal on Windows (#599) 2017-05-12 17:39:14 +02:00
Eldinnie f27807552f refactor self.id -> self._id to let pycharms testrunner run (#598) 2017-05-12 17:38:51 +02:00
Eldinnie f3aca42e69 pass errormessage to Unauthorized (#597) 2017-05-12 17:38:36 +02:00
Noam Meltzer ed49bdb19c Merge pull request #592 from python-telegram-bot/snake_case
prefer snake_case for telegram.Bot methods
2017-05-08 00:30:05 +03:00
Rahiel Kasim 9f3afa5fa2 prefer snake_case for telegram.Bot methods 2017-05-07 16:09:58 +02:00
Joscha Götzer cdf36a20b7 Fixed deprecation warning (#586) 2017-04-29 19:56:27 +02:00
manorom c5598b96bc Fix Bug #571 (second try) (#574)
* Fix Bug #571
ConversationHandler will not process CallbackQuery if per_chat=True and
the CallbackQuery has no message attached to it (as is the case with
buttons on inline results)

* Adds test case for CallbackQuery without Chat
2017-04-29 15:51:48 +02:00
Noam Meltzer ca4351079f Merge pull request #542 from python-telegram-bot/urllib3-vendor-beta
Vendor (embed) urllib3 with our package.
2017-04-29 16:38:44 +03:00
Noam Meltzer 34059c951d Don't run coveralls on the vendor subdir 2017-04-29 15:52:31 +03:00
Jannes Höke c7dbdce3dc Allow CallbackQueryHandler in ConversationHandler with per_mess… (#561)
* 🐛 Allow CallbackQueryHandler in ConversationHandler with per_message=False

but show a warning #556

*  warning logs instead of ValueErrors

 #556
2017-04-29 15:15:17 +03:00
Noam Meltzer fe5ae8ed84 request.py: Fix warning on stderr irrelevant for most users
Attempting to import urllib3.contrib.socks may fail if PySocks is not
installed. Most users won't care for that.
Only import that module if the user requested to use a socks proxy.
2017-04-29 14:42:36 +03:00
Noam Meltzer a6b28b022a Merge remote-tracking branch 'origin/master' into urllib3-vendor-beta 2017-04-29 14:09:45 +03:00
Noam Meltzer 284f16b87b Merge pull request #583 from whipermr5/gae-urllib3-support
Compatibility with GAE via urllib3.contrib package
2017-04-29 12:29:51 +03:00
John Yong 785245a57e Add myself to authors list 2017-04-27 17:00:31 +08:00
John Yong 1d905d567c GAE support via urllib3.contrib.appengine module #334 2017-04-27 16:59:35 +08:00
Jannes Höke 4541476143 enforce urllib3==1.20 (#580)
* 🚑 enforce urllib3==1.20

 #579

* 🚑 Bump version to v5.3.1

 #579
2017-04-25 17:26:54 +02:00
Dickson Tan 11a3de67ea Remove deprecated code #408 part 1 (#564)
* Remove non-pep8 dispatcher methods

* callbackqueryhandler: Remove non-pep8 compliant methods

* commandhandler: Remove non-pep8 compliant methods

* handler: Remove non-pep8 compliant methods

* messagehandler: Remove non-pep8 compliant methods

* stringcommandhandler: Remove non-pep8 compliant methods

* stringregexhandler: Remove non-pep8 compliant methods

* test_updater: fix usage of deprecated methods

* dispatcher: fix accidental misalignment of comments by pre-commit hook

* Add myself to list of contributors
2017-04-25 10:39:02 +02:00
Eldinnie 90bf26c09b Issue 566 (#577)
* stripping token of whitespaces before starting bot

* Line feed

* - Case insensitivity for commandhandler
- Ignore pylint case on windows.
2017-04-25 10:37:06 +02:00
Eldinnie b5b09884b1 Group filters (#575)
* stripping token of whitespaces before starting bot

* Line feed

* show exception that's caused (fixes flake8 failing)

* Add private/group filters

Add filters:
 - Filters.private (for messages in private chats)
 - Filters.group   (for messages in group chats)

* use constants
2017-04-23 23:22:05 +02:00
Rahiel Kasim 35132271af contributing: how to run tests without make; AUTHORS: fix formatting (#567) 2017-04-17 14:49:42 +03:00
Iulian Onofrei 44d7bad11c Update constants.py (#553)
Add maximum inline query results constant
2017-03-29 17:31:26 +02:00
Jacob Bom ad5f009ce7 Add the ability to invert (not) filters (#552)
* Add InvertedFilter and use it from __invert__

* Add docstrings and __str__ for inverting filters

* Tests for inverted filters
2017-03-28 18:38:44 +02:00
Jannes Höke 8fe6e13ff2 Update handlers (#532)
* 🔨 Refactor `Update.extract_` methods to `Update.effective_` properties
 #507

*  Update RegexHandler to work with edited messages and channel posts

* 🔨 messagehandler.py: refactor channel_posts_updates -> channel_post_updates

* 🔨 handler.py: use effective_ properties
2017-03-26 14:36:45 +02:00
Jannes Höke ff39e2436e Refactor Update.extract_ methods to Update.effective_ properties (#531)
* 🔨 Refactor `Update.extract_` methods to `Update.effective_` properties
 #507

* 🔨 handler.py: use effective_ properties
2017-03-26 14:36:34 +02:00
Alex Hirschfeld 5b14b134dc Added user defined function for updater's signalHandler (#512)
* Added user defined function for updater's signalHandler

* Added test_userSignal to test_updater

* Added test_userSignal to test_updater
Fixing paren
2017-03-26 14:36:13 +02:00
Jannes Höke 5897affa07 add missing allowed_updates to start_webhook (#549)
* 🐛 add missing allowed_updates to start_webhook
 #548

* 🔨 fix webhook-related tests
 #548
2017-03-25 12:46:37 +01:00
Jannes Höke 9982f3c908 🐛 fix message date parsing for messages without a timestamp (#550)
#546
2017-03-25 12:15:37 +01:00
thodnev 22142e7cbd Introduce MessageQueue (#537)
* Introduce MessageQueue

* minor documentation and terminology fixes according to the review

* minor documentation and terminology fixes according to the review

* minor documentation and terminology fixes according to the review

* pep8 fix
2017-03-15 17:35:33 +01:00
Noam Meltzer 4c8d1c9a5e Fix Bot.sendDocument 2017-03-11 23:55:26 +02:00
Noam Meltzer bef6651da0 travis: when running nosetests run only the telegram tests (not urllib3) 2017-03-11 23:50:30 +02:00
Noam Meltzer 8c2ee0b439 Remove urllib3 from requirements.txt 2017-03-11 23:50:30 +02:00
Noam Meltzer bfb99a688a AUTHORS.rst: Add self to maintainers and add license info about urllib3 2017-03-11 23:50:30 +02:00
Noam Meltzer d239f4ee62 setup.py: vendor the urllib3 package 2017-03-11 23:50:30 +02:00
Noam Meltzer 2b930c221d Sane default for sending files timeout 2017-03-11 16:23:43 +02:00
Jannes Höke 2a1d40bd28 🚧 manipulate sys.path to prefer vendor urllib3
#533
2017-03-09 23:29:15 +01:00
Jannes Höke 1d4464ddbd add git submodule to vendor urllib3
#533
2017-03-09 23:16:58 +01:00
Jannes Höke 85b9236641 Update AUTHORS.rst 2017-02-28 15:55:38 +01:00
Jose Luis Salazar Gonzalez e78d11a99b Add filters to commandHandler (#536)
* Add filters to commandHandler

* Add commandHandler tests with filters

* Add myself to authors
2017-02-28 15:44:55 +01:00
Jannes Höke cc73469dab Issue 502 (#530)
* conversationhandler.py: add per_chat, per_user and per_message

* test_conversationhandler.py: test case per_user=False

* test_conversationhandler.py: add test for callbackqueryhandlers

* ✏️ Fix accidental typo in logging format
2017-02-27 17:52:58 +01:00
Jannes Höke 853d823964 🚨 promise.py: remove pylint warning
#529
2017-02-27 16:23:04 +01:00
Jannes Höke 69bfb85298 Fix tests (missed one)
#494
2017-02-27 15:35:57 +01:00
Jannes Höke f267646828 Fix tests
#494
2017-02-27 15:23:48 +01:00
Jannes Höke 6b7144bbab 🚨 fix yapf 2017-02-27 14:53:28 +01:00
Jannes Höke f8b13440c1 🔀 Merge thodnev/ptb into ptb/promises-with-exceptions
#529
2017-02-27 14:45:12 +01:00
Jannes Höke a1ade408b0 🔀 Merge master into dec04
#483
2017-02-27 14:39:18 +01:00
Jannes Höke 2954ca2bad 🔀 Merge master into fix-460
#494
2017-02-27 14:33:58 +01:00
Eldinnie 34ebb7fe5a Issue 520 (#521)
* stripping token of whitespaces before starting bot

* Line feed

* fixed chat parameter all_members_are_administrators
2017-02-27 14:27:06 +01:00
Eldinnie 78094b796d Fix commandhandler (#515)
* stripping token of whitespaces before starting bot

* Line feed

* CommandHandler checks if message is for this bot

* CommandHandler checks if message is for this bot
- Added tests

* Fixed tests in test_conversationHandler to work with new commandhandler structure

* type in conversationhandler test
2017-02-27 14:26:53 +01:00
thodnev fb378775a4 Changed promises to handle exceptions 2017-02-26 23:27:03 +02:00
代码家 264b9bd08c add myself to authors (#527) 2017-02-26 13:57:50 +01:00
Noam Meltzer d714da4b36 Merge pull request #500 from python-telegram-bot/bug470
Fix bad formatting of BadRequest exception message

Fixes #470
2017-02-25 22:40:49 +02:00
代码家 e39afad321 Add support for Socks5 proxy. (#518) 2017-02-25 20:47:56 +02:00
Rahiel Kasim 0507378509 fix typo's (#523) and comment out failing sticker test 2017-02-18 16:03:50 +01:00
Rahiel Kasim 659ac52d92 fix tests (telegram cache file_id's) 2017-02-05 23:10:48 +01:00
Rahiel Kasim 924c241680 declare support for Python 3.6 2017-02-05 20:40:16 +01:00
Joscha Götzer ac59f2f37c Added methods to generate the original markdown/html string from entities contained in Message (#507)
* Added methods to generate the original markdown/html formatted string from the entities contained in an update

* Added

* Moved the html/markdown parsing methods to `Message`

* Moved extract_* methods from helpers to the appropriate location

* Refactored text_markdown and text_html

* Refactored text_markdown and text_html for efficiency

* Fixed method call in conversationhandler

* Fixed method call in handler

* Fixed `make test` command on windows systems

* Improved method documentation
2017-01-20 20:13:58 +01:00
Jannes Höke 86c8ebbfb7 fix tests 2017-01-16 00:28:47 +01:00
Jannes Höke cd38bdbed5 bot.py: add delete_webhook alias 2017-01-16 00:28:26 +01:00
Jannes Höke 975d193441 Merge branch 'master' into dec04 2017-01-16 00:15:09 +01:00
Jannes Höke 728ffa432d updater.py: add allowed_updates parameter to start_polling 2017-01-16 00:07:46 +01:00
Jannes Höke 8ac66698b5 test_bot.py: unit test setWebhook and deleteWebhook 2017-01-16 00:01:32 +01:00
Jannes Höke c2c93f5d51 webhookinfo.py: add new parameters 2017-01-16 00:00:54 +01:00
Jannes Höke fc9f36d4db use deleteWebhook in Updater._bootstrap 2017-01-16 00:00:27 +01:00
Jacob Bom e69e99ce23 Make everything default to None
This effectively removes most type checking from all optional variables... I'm not really sure that's what we want...
2017-01-11 19:41:39 +01:00
Jacob Bom 27b2fd64b7 More IDE friendliness 2017-01-09 19:29:05 +01:00
Jacob Bom 4dee785fba Last of the classes I think 2017-01-09 19:16:28 +01:00
Noam Meltzer 4c7cc3a05b Fix test_set_game_score3 - telegram changed something in their responses 2017-01-07 23:37:23 +02:00
Noam Meltzer 1bb5dd224b Fix bad formatting of BadRequest exception message
fixes #470
2017-01-07 23:09:06 +02:00
Noam Meltzer bd96771a7a Fix typo 2017-01-07 23:05:58 +02:00
Noam Meltzer 191e442e59 test_jobqueue.py: Fix time delta calculations on several tests 2017-01-07 20:13:08 +02:00
Noam Meltzer 58dddfd9c3 Control the read timeout from telegram servers
refs #495
refs #364
2017-01-07 20:13:08 +02:00
Noam Meltzer 68a7d9fa1b Make sure to hint Telegram servers that we reuse connections
refs #495
refs #364
2017-01-07 20:13:08 +02:00
Jacob Bom 38f2064639 Tricky empty string in __init__ 2016-12-30 14:03:49 +01:00
Jacob Bom 651119fd69 Fix instances of empty strings in __init__ in example contributing code 2016-12-30 13:58:57 +01:00
Jacob Bom 232a0b0286 Fix instances of empty strings in __init__
Should partially fix #460
2016-12-30 13:57:59 +01:00
Anton Tagunov 48bcc3129a set timeout for post message with file object (#486) 2016-12-29 12:01:58 +02:00
Joscha Götzer 6a01164897 ConversationHandler breaks when bot is also used in Channels (#487)
* ConversationHandler now ignores channel posts
2016-12-25 22:36:01 +02:00
Noam Meltzer 7f6b017ce2 Merge pull request #484 from python-telegram-bot/jobqueue-absolute-simple
JobQueue: Simpler API for adding jobs
2016-12-21 00:04:01 +02:00
Jannes Höke 09cb33f52d rename Job.is_removed to removed 2016-12-20 22:37:36 +01:00
Noam Meltzer 423251f66c Change Job.is_removed to be a property instead of a method 2016-12-20 00:14:03 +02:00
Noam Meltzer ed1785981d disable test_send_contact 2016-12-20 00:12:57 +02:00
Hugo Hakim Damer a8fecc527d Prevented modifications to the request object's original data (#454)
fixes #357
2016-12-20 00:07:35 +02:00
Yan c3984e1bf1 Download changed (#459)
* DownBytes added

* File.downbyte changed

* Changed file.download();Remove downbyte()

* Fixed typo

* add docstring, make custom_path and out mutually exclusive, rename downbytes to retrieve

* remove trailing whitespace

* run pre-commit hooks
2016-12-18 03:05:00 +01:00
Noam Meltzer 93bf21a0a4 jobqueue.py: stability improvments
- Job.job_queue is now weakref.proxy reducing the risk of cyclic
   pointers preventing Job object from being deleted.
 - JobQueue._put(): raise if both next_t and job.interval are None
 - Don't put repeating job back to queue if user had disabled it was
   disabled during the time of execution.
 - New method: Job.is_removed() - promising a consistent API (instead of
   access to private member Job._remove)
 - Documentation fixes.
2016-12-15 00:17:57 +02:00
Jannes Höke cbf93e1046 switch to run_x naming scheme 2016-12-14 18:01:44 +01:00
Jannes Höke a1a8628c75 bot.py: fix argument order, setWebhook: make 'url' and 'webhook_url' mutually exclusive 2016-12-14 17:38:06 +01:00
Jannes Höke d5ce32c672 removed Job.run_immediately and related code 2016-12-14 17:15:52 +01:00
Jannes Höke 09ddc1b1a8 DAAAANGER ZOOOONE 2016-12-14 16:27:45 +01:00
Jannes Höke c7cd379016 jobqueue.py: move the check for job.interval types into Job.__init__ 2016-12-14 06:30:18 +01:00
Jannes Höke 84f3bc0c79 README.rst: update api support date 2016-12-14 00:51:43 +01:00
Jannes Höke 92dc9b81ce bot.py: implement changes of december 04 (bot api 2.3.1) 2016-12-14 00:50:34 +01:00
Jannes Höke 8ead72e3ef jobqueue: add support for specifying next_t in datetime.datetime or datetime.time 2016-12-13 23:38:13 +01:00
Jannes Höke a37add39f4 [ci skip] update timerbot.py to use chat_data 2016-12-13 21:57:37 +01:00
Jannes Höke 71530f404d Bump version to v5.3.0 2016-12-11 23:51:51 +01:00
Jannes Höke 0cd7ecab50 fix test_filters_reply 2016-12-11 23:16:10 +01:00
Jeong Arm c5f9e53d44 Add "reply" filter (#465)
* Add "reply" filter

This filter will filter messages that reply to other's message.

* Add test for "reply" filter

* Add "Kjwon15" to AUTHORS.rst
2016-12-11 22:45:51 +01:00
Noam Meltzer acf1541395 Botapi2.3 (#471)
* New fields channel_post and edited_channel_post for Update

refs #468

* setGameScore() changes

 - Changed behaviour: messages with high scores will be update with new
   high scores by default. (documentation fix)
 - Use (new) disable_edit_message in setGameScore to disable the above new
   behaviour.
 - The edit_message parameter from setGameScore is no longer in use. For
   backward compatibility, it will be taken into account for a while,
   unless disable_edit_message is passed explicitly.

refs #468

* New field forward_from_message_id for Message.

refs #468

* New parameter cache_time for answerCallbackQuery

refs #468

* replykeyboardhide renamed to replykeyboardremove

refs #468

* Unitests for updated setGameScore semantics

refs #468

* Backward compatibility for ReplyKeyboardHide

refs #468

* Fix docstrings of wrapper methods in Message

* Unitest new field forward_from_message_id of Message

refs #468

* Fix testMaxCaptionLength

Telegram servers changed their behaviour - now they truncate a long
caption instead of returning an error.

* MessageHandler: Added support for channel posts

* Fix flake8 complaints in a manner which yapf will like it too.

* fix rst markup
2016-12-11 22:44:52 +01:00
Jacob Bom 906a1b8d7d message.edit_reply_markup now correctly edits reply_markup (#473) 2016-12-08 13:23:51 +01:00
lisitsky a2fddbe85c Fix telegram API change, returning '404 Not found' (#461)
* Fix telegram API change, returning '404 Not found' with raising own TelegramError rather native exception

* Change exception to InvalidToken in test and request util

* Added myself to AUTHORS. Thx for appreciation :)
2016-11-09 14:36:42 +01:00
Wesley Gahr 68e87db909 Job queue time units (#452)
* Adding timeunit and day support to the jobqueue

* Adding tests

* Changed the file permission back to 644.

* Changed AssertEqual argument order to (actual, expectd).

* Removed the TimeUnit enum and unit param, instead use datetime.time for interval.

* Removing the TimeUnits enum and unit param in favour of optionally using a datetime.time as the interval.

* Removing the TimeUnits enumeration, forgot the remove it in the last one.

* Removed some old docstrings refering to the TimeUnits enum.

* Removed the old TimeUnits import.

* Adding some error handling for the 'days' argument (only a 'tuple' with 'Days')

* Writing the error message directly in the exception.

* Moving a debug statement wrongfully saying a job would be running on days it wouldn't.

* Writing error messages directly in the exceptions instead of making an extra variable.

* Replacing datetime.time in favour of datetime.timedelta because of the get_seconds() method.

* Adding error handling for the method .

* Splitting the tests up in multiple ones, no float test because I haven't found a reliable way to test it.

* Excluding .exrc file.

* Removing \ at EOF of ValueError.

* Replacing Enums with plain new-style classes.

* Using numbers.number to check for ints/floats instead of seperate int/float checks.

* Fixing typo, number -> Number.

* Changed lower_case Days attributes to UPPER_CASE.

* Different formatting for Days class, removed the get_days function in favour of a tuple.

* Removed redundant function get_days.

* Edited the docstring for next_t to also take datetime.timedelta.

* Removed for-loop in favour of any().

* Changed docstring for interval.

* Removed debug print.

* Changing some docstrings.

* Changing some docstrings (again).
2016-11-08 23:39:25 +01:00
Blue a7bfb0c3a1 hide inline keyboard in conversationbot example (#439) 2016-11-08 22:14:11 +01:00
Jannes Höke e76ee6bb57 [ci skip] docs: add even more missing classes (wtf) 2016-11-01 09:37:03 +01:00
Jannes Höke 65061f8a99 [ci skip] docs: add all missing classes 2016-11-01 09:01:36 +01:00
Jannes Höke dabbbcafcb [ci skip] docs: remove superfluous text in index.rst 2016-11-01 08:15:51 +01:00
Jannes Höke 1eb4b40dbf [ci skip] docs: add logo to html and pdf builds 2016-11-01 08:09:25 +01:00
Jannes Höke 7167936692 [ci skip] pdf docs: use a4paper, update motto 2016-11-01 07:54:04 +01:00
Jannes Höke aa3ca38837 Sphinx restructuring: Now builds PDF and better HTML docs (#449)
* update sphinx source files to properly build latexpdf and improve html build

* fix docstrings and sphinx sources to get rid of warnings

* add telegram.contrib.rst
2016-11-01 06:53:51 +01:00
Jannes Höke b22c3cc5c0 [ci skip] add state diagrams for conversation handler examples 2016-10-28 08:26:02 +02:00
223 changed files with 4490 additions and 1218 deletions
+4 -1
View File
@@ -1,5 +1,8 @@
[run]
source = telegram
omit = telegram/vendor/*
[report]
omit = tests/
omit =
tests/
telegram/vendor/*
+8 -2
View File
@@ -85,6 +85,12 @@ Here's how to make a one-off code change.
$ make test
If you don't have ``make``, do:
.. code-block::
$ nosetests -v
- To actually make the commit (this will trigger tests for yapf, lint and pep8 automatically):
.. code-block:: bash
@@ -184,11 +190,11 @@ break the API classes. For example:
.. code-block:: python
# GOOD
def __init__(self, id, name, last_name='', **kwargs):
def __init__(self, id, name, last_name=None, **kwargs):
self.last_name = last_name
# BAD
def __init__(self, id, name, last_name=''):
def __init__(self, id, name, last_name=None):
self.last_name = last_name
+3
View File
@@ -71,3 +71,6 @@ telegram.webp
# original files from merges
*.orig
# Exclude .exrc file for Vim
.exrc
+4
View File
@@ -0,0 +1,4 @@
[submodule "telegram/vendor/urllib3"]
path = telegram/vendor/urllib3
url = https://github.com/python-telegram-bot/urllib3.git
branch = ptb
+2 -1
View File
@@ -4,6 +4,7 @@ python:
- "3.3"
- "3.4"
- "3.5"
- "3.6"
- "pypy"
- "pypy3"
branches:
@@ -15,7 +16,7 @@ install:
- pip install -r requirements-dev.txt
- if [[ $TRAVIS_PYTHON_VERSION != 'pypy'* ]]; then pip install ujson; fi
script:
- nosetests -v --with-flaky --no-flaky-report --with-coverage --cover-package=telegram/
- nosetests -v --with-flaky --no-flaky-report --with-coverage --cover-package=telegram/ tests
- if [[ $TRAVIS_PYTHON_VERSION == 3.5 ]]; then pre-commit run --all-files; fi
after_success:
coveralls
+22 -1
View File
@@ -1,7 +1,14 @@
Credits
=======
``python-telegram-bot`` is written and maintained by `Leandro Toledo <https://github.com/leandrotoledo>`_.
``python-telegram-bot`` was originally created by
`Leandro Toledo <https://github.com/leandrotoledo>`_ and is now maintained by
`Jannes Höke <https://github.com/jh0ker>`_ (`@jh0ker <https://t.me/jh0ker>`_ on Telegram) and
`Noam Meltzer <https://github.com/tsnoam>`_.
We're vendoring urllib3 as part of ``python-telegram-bot`` which is distributed under the MIT
license. For more info, full credits & license terms, the sources can be found here:
`https://github.com/python-telegram-bot/urllib3`.
Contributors
------------
@@ -9,29 +16,43 @@ Contributors
The following wonderful people contributed directly or indirectly to this project:
- `Avanatiker <https://github.com/Avanatiker>`_
- `Anton Tagunov <https://github.com/anton-tagunov>`_
- `Balduro <https://github.com/Balduro>`_
- `bimmlerd <https://github.com/bimmlerd>`_
- `d-qoi <https://github.com/d-qoi>`_
- `daimajia <https://github.com/daimajia>`_
- `Eli Gao <https://github.com/eligao>`_
- `ErgoZ Riftbit Vaper <https://github.com/ergoz>`_
- `Eugene Lisitsky <https://github.com/lisitsky>`_
- `evgfilim1 <https://github.com/evgfilim1>`_
- `franciscod <https://github.com/franciscod>`_
- `Hugo Damer <https://github.com/HakimusGIT>`_
- `Jacob Bom <https://github.com/bomjacob>`_
- `JASON0916 <https://github.com/JASON0916>`_
- `jh0ker <https://github.com/jh0ker>`_
- `John Yong <https://github.com/whipermr5>`_
- `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>`_
- `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>`_
- `overquota <https://github.com/overquota>`_
- `Patrick Hofmann <https://github.com/PH89>`_
- `Pieter Schutz <https://github.com/eldinnie>`_
- `Rahiel Kasim <https://github.com/rahiel>`_
- `Joscha Götzer <https://github.com/Rostgnom>`_
- `Shelomentsev D <https://github.com/shelomentsevd>`_
- `sooyhwang <https://github.com/sooyhwang>`_
- `thodnev <https://github.com/thodnev>`_
- `Valentijn <https://github.com/Faalentijn>`_
- `voider1 <https://github.com/voider1>`_
- `wjt <https://github.com/wjt>`_
Please add yourself here alphabetically when you submit your first pull request.
+38
View File
@@ -2,6 +2,44 @@
Changes
=======
**2017-05-19**
*Released 6.0.0*
- Add support for Bot API 2.3.1
- Add support for ``deleteMessage`` API method
- New, simpler API for ``JobQueue`` - https://github.com/python-telegram-bot/python-telegram-bot/pull/484
- Download files into file-like objects - https://github.com/python-telegram-bot/python-telegram-bot/pull/459
- Use vendor ``urllib3`` to address issues with timeouts
- The default timeout for messages is now 5 seconds. For sending media, the default timeout is now 20 seconds.
- String attributes that are not set are now ``None`` by default, instead of empty strings
- Add ``text_markdown`` and ``text_html`` properties to ``Message`` - https://github.com/python-telegram-bot/python-telegram-bot/pull/507
- Add support for Socks5 proxy - https://github.com/python-telegram-bot/python-telegram-bot/pull/518
- Add support for filters in ``CommandHandler`` - https://github.com/python-telegram-bot/python-telegram-bot/pull/536
- Add the ability to invert (not) filters - https://github.com/python-telegram-bot/python-telegram-bot/pull/552
- Add ``Filters.group`` and ``Filters.private``
- Compatibility with GAE via ``urllib3.contrib`` package - https://github.com/python-telegram-bot/python-telegram-bot/pull/583
- Add equality rich comparision operators to telegram objects - https://github.com/python-telegram-bot/python-telegram-bot/pull/604
- Several bugfixes and other improvements
- Remove some deprecated code
**2017-04-17**
*Released 5.3.1*
- Hotfix release due to bug introduced by urllib3 version 1.21
**2016-12-11**
*Released 5.3*
- Implement API changes of November 21st (Bot API 2.3)
- ``JobQueue`` now supports ``datetime.timedelta`` in addition to seconds
- ``JobQueue`` now supports running jobs only on certain days
- New ``Filters.reply`` filter
- Bugfix for ``Message.edit_reply_markup``
- Other bugfixes
**2016-10-25**
*Released 5.2*
+8 -2
View File
@@ -84,7 +84,7 @@ make the development of bots easy and straightforward. These classes are contain
Telegram API support
====================
As of **3. Oct 2016**, all types and methods of the Telegram Bot API are supported.
As of **4. Dec 2016**, all types and methods of the Telegram Bot API are supported.
==========
Installing
@@ -100,9 +100,15 @@ Or you can install from source with:
.. code:: shell
$ git clone https://github.com/python-telegram-bot/python-telegram-bot
$ git clone https://github.com/python-telegram-bot/python-telegram-bot --recursive
$ cd python-telegram-bot
$ python setup.py install
In case you have a previously cloned local repository already, you should initialize the added urllib3 submodule before installing with:
.. code:: shell
$ git submodule update --init --recursive
===============
Getting started
+12 -9
View File
@@ -51,7 +51,7 @@ master_doc = 'index'
# General information about the project.
project = u'Python Telegram Bot'
copyright = u'2015-2016, Leandro Toledo'
copyright = u'2015-2017, Leandro Toledo'
author = u'Leandro Toledo'
# The version info for the project you're documenting, acts as replacement for
@@ -59,9 +59,9 @@ author = u'Leandro Toledo'
# built documents.
#
# The short X.Y version.
version = '5.2' # telegram.__version__[:3]
version = '6.0' # telegram.__version__[:3]
# The full version, including alpha/beta/rc tags.
release = '5.2.0' # telegram.__version__
release = '6.0.0' # telegram.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -131,7 +131,7 @@ html_theme = 'sphinx_rtd_theme'
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
html_logo = 'ptb-logo-orange.png'
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
@@ -209,14 +209,17 @@ htmlhelp_basename = 'PythonTelegramBotdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
'papersize': 'a4paper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
'preamble': r'''\setcounter{tocdepth}{2}
\usepackage{enumitem}
\setlistdepth{99}''',
# Latex figure (float) alignment
#'figure_align': 'htbp',
@@ -227,12 +230,12 @@ latex_elements = {
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'PythonTelegramBot.tex', u'Python Telegram Bot Documentation',
u'Leandro Toledo', 'manual'),
author, 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
latex_logo = 'ptb-logo_1024.png'
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
@@ -271,7 +274,7 @@ man_pages = [
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'PythonTelegramBot', u'Python Telegram Bot Documentation',
author, 'PythonTelegramBot', 'Not just a Python wrapper around the Telegram Bot API',
author, 'PythonTelegramBot', "We have made you a wrapper you can't refuse",
'Miscellaneous'),
]
+1 -5
View File
@@ -6,12 +6,8 @@
Welcome to Python Telegram Bot's documentation!
===============================================
Contents:
telegram
.. toctree::
:maxdepth: 2
telegram
Indices and tables
-7
View File
@@ -1,7 +0,0 @@
telegram
========
.. toctree::
:maxdepth: 4
telegram
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

+7
View File
@@ -0,0 +1,7 @@
telegram.animation module
=========================
.. automodule:: telegram.animation
:members:
:undoc-members:
:show-inheritance:
+7
View File
@@ -0,0 +1,7 @@
telegram.callbackgame module
============================
.. automodule:: telegram.callbackgame
:members:
:undoc-members:
:show-inheritance:
+7
View File
@@ -0,0 +1,7 @@
telegram.callbackquery module
=============================
.. automodule:: telegram.callbackquery
:members:
:undoc-members:
:show-inheritance:
+7
View File
@@ -0,0 +1,7 @@
telegram.chatmember module
==========================
.. automodule:: telegram.chatmember
:members:
:undoc-members:
:show-inheritance:
+17
View File
@@ -0,0 +1,17 @@
telegram.contrib package
========================
Submodules
----------
.. toctree::
telegram.contrib.botan
Module contents
---------------
.. automodule:: telegram.contrib
:members:
:undoc-members:
:show-inheritance:
@@ -0,0 +1,7 @@
telegram.ext.messagequeue module
================================
.. automodule:: telegram.ext.messagequeue
:members:
:undoc-members:
:show-inheritance:
+2
View File
@@ -10,11 +10,13 @@ Submodules
telegram.ext.dispatcher
telegram.ext.jobqueue
telegram.ext.handler
telegram.ext.callbackqueryhandler
telegram.ext.choseninlineresulthandler
telegram.ext.conversationhandler
telegram.ext.commandhandler
telegram.ext.inlinequeryhandler
telegram.ext.messagehandler
telegram.ext.messagequeue
telegram.ext.filters
telegram.ext.regexhandler
telegram.ext.stringcommandhandler
+7
View File
@@ -0,0 +1,7 @@
telegram.file module
====================
.. automodule:: telegram.file
:members:
:undoc-members:
:show-inheritance:
+7
View File
@@ -0,0 +1,7 @@
telegram.game module
====================
.. automodule:: telegram.game
:members:
:undoc-members:
:show-inheritance:
+7
View File
@@ -0,0 +1,7 @@
telegram.gamehighscore module
=============================
.. automodule:: telegram.gamehighscore
:members:
:undoc-members:
:show-inheritance:
@@ -1,5 +1,5 @@
telegram.inlinekeyboardbutton module
===========================
====================================
.. automodule:: telegram.inlinekeyboardbutton
:members:
@@ -1,5 +1,5 @@
telegram.inlinekeyboardmarkup module
==========================
====================================
.. automodule:: telegram.inlinekeyboardmarkup
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultarticle module
=================================
========================================
.. automodule:: telegram.inlinequeryresultarticle
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultaudio module
=================================
======================================
.. automodule:: telegram.inlinequeryresultaudio
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultcachedaudio module
=================================
============================================
.. automodule:: telegram.inlinequeryresultcachedaudio
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultcacheddocument module
=================================
===============================================
.. automodule:: telegram.inlinequeryresultcacheddocument
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultcachedgif module
=================================
==========================================
.. automodule:: telegram.inlinequeryresultcachedgif
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultcachedmpeg4gif module
=================================
===============================================
.. automodule:: telegram.inlinequeryresultcachedmpeg4gif
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultcachedphoto module
=================================
============================================
.. automodule:: telegram.inlinequeryresultcachedphoto
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultcachedsticker module
=================================
==============================================
.. automodule:: telegram.inlinequeryresultcachedsticker
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultcachedvideo module
=================================
============================================
.. automodule:: telegram.inlinequeryresultcachedvideo
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultcachedvoice module
=================================
============================================
.. automodule:: telegram.inlinequeryresultcachedvoice
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultcontact module
=================================
========================================
.. automodule:: telegram.inlinequeryresultcontact
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultdocument module
=================================
=========================================
.. automodule:: telegram.inlinequeryresultdocument
:members:
@@ -0,0 +1,7 @@
telegram.inlinequeryresultgame module
=====================================
.. automodule:: telegram.inlinequeryresultgame
:members:
:undoc-members:
:show-inheritance:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultgif module
=================================
====================================
.. automodule:: telegram.inlinequeryresultgif
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultlocation module
=================================
=========================================
.. automodule:: telegram.inlinequeryresultlocation
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultmpeg4gif module
=================================
=========================================
.. automodule:: telegram.inlinequeryresultmpeg4gif
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultphoto module
=================================
======================================
.. automodule:: telegram.inlinequeryresultphoto
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultvenue module
=================================
======================================
.. automodule:: telegram.inlinequeryresultvenue
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultvideo module
=================================
======================================
.. automodule:: telegram.inlinequeryresultvideo
:members:
@@ -1,5 +1,5 @@
telegram.inlinequeryresultvoice module
=================================
======================================
.. automodule:: telegram.inlinequeryresultvoice
:members:
@@ -0,0 +1,7 @@
telegram.inputcontactmessagecontent module
==========================================
.. automodule:: telegram.inputcontactmessagecontent
:members:
:undoc-members:
:show-inheritance:
@@ -0,0 +1,7 @@
telegram.inputlocationmessagecontent module
===========================================
.. automodule:: telegram.inputlocationmessagecontent
:members:
:undoc-members:
:show-inheritance:
@@ -0,0 +1,7 @@
telegram.inputmessagecontent module
===================================
.. automodule:: telegram.inputmessagecontent
:members:
:undoc-members:
:show-inheritance:
@@ -0,0 +1,7 @@
telegram.inputtextmessagecontent module
=======================================
.. automodule:: telegram.inputtextmessagecontent
:members:
:undoc-members:
:show-inheritance:
@@ -0,0 +1,7 @@
telegram.inputvenuemessagecontent module
========================================
.. automodule:: telegram.inputvenuemessagecontent
:members:
:undoc-members:
:show-inheritance:
+7
View File
@@ -0,0 +1,7 @@
telegram.keyboardbutton module
==============================
.. automodule:: telegram.keyboardbutton
:members:
:undoc-members:
:show-inheritance:
+7
View File
@@ -0,0 +1,7 @@
telegram.messageentity module
=============================
.. automodule:: telegram.messageentity
:members:
:undoc-members:
:show-inheritance:
+7
View File
@@ -0,0 +1,7 @@
telegram.parsemode module
=========================
.. automodule:: telegram.parsemode
:members:
:undoc-members:
:show-inheritance:
@@ -1,7 +0,0 @@
telegram.replykeyboardhide module
=================================
.. automodule:: telegram.replykeyboardhide
:members:
:undoc-members:
:show-inheritance:
@@ -0,0 +1,7 @@
telegram.replykeyboardremove module
===================================
.. automodule:: telegram.replykeyboardremove
:members:
:undoc-members:
:show-inheritance:
+31 -13
View File
@@ -6,14 +6,30 @@ Submodules
.. toctree::
telegram.contrib
telegram.ext
telegram.animation
telegram.audio
telegram.base
telegram.bot
telegram.ext
telegram.inlinequery
telegram.inlinequeryresult
telegram.callbackgame
telegram.callbackquery
telegram.chat
telegram.chataction
telegram.chatmember
telegram.choseninlineresult
telegram.constants
telegram.contact
telegram.document
telegram.error
telegram.file
telegram.forcereply
telegram.game
telegram.gamehighscore
telegram.inlinekeyboardbutton
telegram.inlinekeyboardmarkup
telegram.inlinequery
telegram.inlinequeryresult
telegram.inlinequeryresultarticle
telegram.inlinequeryresultaudio
telegram.inlinequeryresultcachedaudio
@@ -26,6 +42,7 @@ Submodules
telegram.inlinequeryresultcachedvoice
telegram.inlinequeryresultcontact
telegram.inlinequeryresultdocument
telegram.inlinequeryresultgame
telegram.inlinequeryresultgif
telegram.inlinequeryresultlocation
telegram.inlinequeryresultmpeg4gif
@@ -33,28 +50,29 @@ Submodules
telegram.inlinequeryresultvenue
telegram.inlinequeryresultvideo
telegram.inlinequeryresultvoice
telegram.choseninlineresult
telegram.chataction
telegram.contact
telegram.document
telegram.emoji
telegram.error
telegram.forcereply
telegram.chat
telegram.inputcontactmessagecontent
telegram.inputfile
telegram.inputlocationmessagecontent
telegram.inputmessagecontent
telegram.inputtextmessagecontent
telegram.inputvenuemessagecontent
telegram.keyboardbutton
telegram.location
telegram.message
telegram.nullhandler
telegram.messageentity
telegram.parsemode
telegram.photosize
telegram.replykeyboardhide
telegram.replykeyboardremove
telegram.replykeyboardmarkup
telegram.replymarkup
telegram.sticker
telegram.update
telegram.user
telegram.userprofilephotos
telegram.venue
telegram.video
telegram.voice
telegram.webhookinfo
Module contents
---------------
+7
View File
@@ -0,0 +1,7 @@
telegram.venue module
=====================
.. automodule:: telegram.venue
:members:
:undoc-members:
:show-inheritance:
@@ -1,7 +1,7 @@
telegram.nullhandler module
telegram.webhookinfo module
===========================
.. automodule:: telegram.nullhandler
.. automodule:: telegram.webhookinfo
:members:
:undoc-members:
:show-inheritance:
+2 -2
View File
@@ -11,10 +11,10 @@ This is probably the base for most of the bots made with `python-telegram-bot`.
This bot uses the [`JobQueue`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.jobqueue.html) class to send timed messages. The user sets a timer by using `/set` command with a specific time, for example `/set 30`. The bot then sets up a job to send a message to that user after 30 seconds. The user can also cancel the timer by sending `/unset`. To learn more about the `JobQueue`, read [this wiki article](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-JobQueue).
### [`conversationbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/conversationbot.py)
A common task for a bot is to ask information from the user. In v5.0 of this library, we introduced the [`ConversationHandler`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.conversationhandler.html) for that exact purpose. This example uses it to retrieve user-information in a conversation-like style.
A common task for a bot is to ask information from the user. In v5.0 of this library, we introduced the [`ConversationHandler`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.conversationhandler.html) for that exact purpose. This example uses it to retrieve user-information in a conversation-like style. To get a better understanding, take a look at the [state diagram](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/conversationbot.png).
### [`conversationbot2.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/conversationbot2.py)
A more complex example of a bot that uses the `ConversationHandler`
A more complex example of a bot that uses the `ConversationHandler`. It is also more confusing. Good thing there is a [fancy state diagram](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/conversationbot2.png) for this one, too!
### [`inlinekeyboard.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinekeyboard.py)
This example sheds some light on inline keyboards, callback queries and message editing.
Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

+7 -5
View File
@@ -17,7 +17,7 @@ Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
from telegram import (ReplyKeyboardMarkup)
from telegram import (ReplyKeyboardMarkup, ReplyKeyboardRemove)
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, RegexHandler,
ConversationHandler)
@@ -48,14 +48,15 @@ def gender(bot, update):
user = update.message.from_user
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.')
'so I know what you look like, or send /skip if you don\'t want to.',
reply_markup=ReplyKeyboardRemove())
return PHOTO
def photo(bot, update):
user = update.message.from_user
photo_file = bot.getFile(update.message.photo[-1].file_id)
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'))
update.message.reply_text('Gorgeous! Now, send me your location please, '
@@ -104,7 +105,8 @@ def bio(bot, update):
def cancel(bot, update):
user = update.message.from_user
logger.info("User %s canceled the conversation." % user.first_name)
update.message.reply_text('Bye! I hope we can talk again some day.')
update.message.reply_text('Bye! I hope we can talk again some day.',
reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
@@ -147,7 +149,7 @@ def main():
# Start the Bot
updater.start_polling()
# Run the bot until the you presses Ctrl-C or the process receives SIGINT,
# Run the bot until you press Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

+1 -1
View File
@@ -142,7 +142,7 @@ def main():
# Start the Bot
updater.start_polling()
# Run the bot until the you presses Ctrl-C or the process receives SIGINT,
# Run the bot until you press Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
+2 -4
View File
@@ -20,7 +20,7 @@ def main():
# get the first pending update_id, this is so we can skip over it in case
# we get an "Unauthorized" exception.
try:
update_id = bot.getUpdates()[0].update_id
update_id = bot.get_updates()[0].update_id
except IndexError:
update_id = None
@@ -39,9 +39,7 @@ def main():
def echo(bot):
global update_id
# Request updates after the last update_id
for update in bot.getUpdates(offset=update_id, timeout=10):
# chat_id is required to reply to any message
chat_id = update.message.chat_id
for update in bot.get_updates(offset=update_id, timeout=10):
update_id = update.update_id + 1
if update.message: # your bot can receive updates without messages
+1 -1
View File
@@ -64,7 +64,7 @@ def main():
# Start the Bot
updater.start_polling()
# Run the bot until the you presses Ctrl-C or the process receives SIGINT,
# Run the bot until you press Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
+1 -1
View File
@@ -72,7 +72,7 @@ def inlinequery(bot, update):
def error(bot, update, error):
logger.warn('Update "%s" caused error "%s"' % (update, error))
logger.warning('Update "%s" caused error "%s"' % (update, error))
def main():
+3 -3
View File
@@ -26,9 +26,9 @@ def start(bot, update):
def button(bot, update):
query = update.callback_query
bot.editMessageText(text="Selected option: %s" % query.data,
chat_id=query.message.chat_id,
message_id=query.message.message_id)
bot.edit_message_text(text="Selected option: %s" % query.data,
chat_id=query.message.chat_id,
message_id=query.message.message_id)
def help(bot, update):
+18 -18
View File
@@ -22,10 +22,9 @@ import logging
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.DEBUG)
level=logging.INFO)
logger = logging.getLogger(__name__)
timers = dict()
# Define a few command handlers. These usually take the two arguments bot and
@@ -36,10 +35,10 @@ def start(bot, update):
def alarm(bot, job):
"""Function to send the alarm message"""
bot.sendMessage(job.context, text='Beep!')
bot.send_message(job.context, text='Beep!')
def set(bot, update, args, job_queue):
def set(bot, update, args, job_queue, chat_data):
"""Adds a job to the queue"""
chat_id = update.message.chat_id
try:
@@ -50,9 +49,8 @@ def set(bot, update, args, job_queue):
return
# Add job to queue
job = Job(alarm, due, repeat=False, context=chat_id)
timers[chat_id] = job
job_queue.put(job)
job = job_queue.run_once(alarm, due, context=chat_id)
chat_data['job'] = job
update.message.reply_text('Timer successfully set!')
@@ -60,23 +58,22 @@ def set(bot, update, args, job_queue):
update.message.reply_text('Usage: /set <seconds>')
def unset(bot, update):
def unset(bot, update, chat_data):
"""Removes the job if the user changed their mind"""
chat_id = update.message.chat_id
if chat_id not in timers:
if 'job' not in chat_data:
update.message.reply_text('You have no active timer')
return
job = timers[chat_id]
job = chat_data['job']
job.schedule_removal()
del timers[chat_id]
del chat_data['job']
update.message.reply_text('Timer successfully unset!')
def error(bot, update, error):
logger.warn('Update "%s" caused error "%s"' % (update, error))
logger.warning('Update "%s" caused error "%s"' % (update, error))
def main():
@@ -88,8 +85,11 @@ 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, pass_args=True, pass_job_queue=True))
dp.add_handler(CommandHandler("unset", unset))
dp.add_handler(CommandHandler("set", set,
pass_args=True,
pass_job_queue=True,
pass_chat_data=True))
dp.add_handler(CommandHandler("unset", unset, pass_chat_data=True))
# log all errors
dp.add_error_handler(error)
@@ -97,9 +97,9 @@ def main():
# Start the Bot
updater.start_polling()
# Block until the you presses Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
# Block until you press Ctrl-C or the process receives SIGINT, SIGTERM or
# SIGABRT. This should be used most of the time, since start_polling() is
# non-blocking and will stop the bot gracefully.
updater.idle()
-1
View File
@@ -1,3 +1,2 @@
future>=0.15.2
urllib3>=1.10
certifi
+11 -2
View File
@@ -17,6 +17,13 @@ def requirements():
return requirements_list
packages = find_packages(exclude=['tests*'])
packages.extend(['telegram.vendor.urllib3.urllib3',
'telegram.vendor.urllib3.urllib3.packages', 'telegram.vendor.urllib3.urllib3.packages.ssl_match_hostname',
'telegram.vendor.urllib3.urllib3.packages.backports', 'telegram.vendor.urllib3.urllib3.contrib',
'telegram.vendor.urllib3.urllib3.util',
])
with codecs.open('README.rst', 'r', 'utf-8') as fd:
fn = os.path.join('telegram', 'version.py')
with open(fn) as fh:
@@ -30,12 +37,13 @@ with codecs.open('README.rst', 'r', 'utf-8') as fd:
license='LGPLv3',
url='https://python-telegram-bot.org/',
keywords='python telegram bot api wrapper',
description='Not just a Python wrapper around the Telegram Bot API',
description="We have made you a wrapper you can't refuse",
long_description=fd.read(),
packages=find_packages(exclude=['tests*']),
packages=packages,
install_requires=requirements(),
extras_require={
'json': 'ujson',
'socks': 'PySocks'
},
include_package_data=True,
classifiers=[
@@ -53,4 +61,5 @@ with codecs.open('README.rst', 'r', 'utf-8') as fd:
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6'
],)
+9 -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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -19,6 +19,10 @@
"""A library that provides a Python interface to the Telegram Bot API"""
from sys import version_info
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'vendor', 'urllib3'))
from .base import TelegramObject
from .user import User
@@ -38,7 +42,7 @@ from .userprofilephotos import UserProfilePhotos
from .keyboardbutton import KeyboardButton
from .replymarkup import ReplyMarkup
from .replykeyboardmarkup import ReplyKeyboardMarkup
from .replykeyboardhide import ReplyKeyboardHide
from .replykeyboardremove import ReplyKeyboardRemove, ReplyKeyboardHide
from .forcereply import ForceReply
from .error import TelegramError
from .inputfile import InputFile
@@ -106,9 +110,9 @@ __all__ = [
'InlineQueryResultVoice', 'InlineQueryResultGame', 'InputContactMessageContent', 'InputFile',
'InputLocationMessageContent', 'InputMessageContent', 'InputTextMessageContent',
'InputVenueMessageContent', 'KeyboardButton', 'Location', 'Message', 'MessageEntity',
'ParseMode', 'PhotoSize', 'ReplyKeyboardHide', 'ReplyKeyboardMarkup', 'ReplyMarkup', 'Sticker',
'TelegramError', 'TelegramObject', 'Update', 'User', 'UserProfilePhotos', 'Venue', 'Video',
'Voice', 'MAX_MESSAGE_LENGTH', 'MAX_CAPTION_LENGTH', 'SUPPORTED_WEBHOOK_PORTS',
'ParseMode', 'PhotoSize', 'ReplyKeyboardRemove', 'ReplyKeyboardMarkup', 'ReplyMarkup',
'Sticker', 'TelegramError', 'TelegramObject', 'Update', 'User', 'UserProfilePhotos', 'Venue',
'Video', 'Voice', 'MAX_MESSAGE_LENGTH', 'MAX_CAPTION_LENGTH', 'SUPPORTED_WEBHOOK_PORTS',
'MAX_FILESIZE_DOWNLOAD', 'MAX_FILESIZE_UPLOAD', 'MAX_MESSAGES_PER_SECOND_PER_CHAT',
'MAX_MESSAGES_PER_SECOND', 'MAX_MESSAGES_PER_MINUTE_PER_GROUP', 'WebhookInfo', 'Animation',
'Game', 'GameHighScore'
+7 -2
View File
@@ -14,5 +14,10 @@ def print_ver_info():
print('future {0}'.format(future.__version__))
print('Python {0}'.format(sys.version.replace('\n', ' ')))
# main
print_ver_info()
def main():
print_ver_info()
if __name__ == '__main__':
main()
+16 -6
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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -35,12 +35,20 @@ class Animation(TelegramObject):
"""
def __init__(self, file_id, **kwargs):
def __init__(self,
file_id,
thumb=None,
file_name=None,
mime_type=None,
file_size=None,
**kwargs):
self.file_id = file_id
self.thumb = kwargs.get('thumb')
self.file_name = kwargs.get('file_name')
self.mime_type = kwargs.get('mime_type')
self.file_size = kwargs.get('file_size')
self.thumb = thumb
self.file_name = file_name
self.mime_type = mime_type
self.file_size = file_size
self._id_attrs = (self.file_id,)
@staticmethod
def de_json(data, bot):
@@ -55,6 +63,8 @@ class Animation(TelegramObject):
if not data:
return None
data = super(Animation, Animation).de_json(data, bot)
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
return Animation(**data)
+9 -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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -46,10 +46,10 @@ class Audio(TelegramObject):
def __init__(self,
file_id,
duration,
performer='',
title='',
mime_type='',
file_size=0,
performer=None,
title=None,
mime_type=None,
file_size=None,
**kwargs):
# Required
self.file_id = str(file_id)
@@ -57,8 +57,10 @@ class Audio(TelegramObject):
# Optionals
self.performer = performer
self.title = title
self.mime_type = str(mime_type)
self.file_size = int(file_size)
self.mime_type = mime_type
self.file_size = file_size
self._id_attrs = (self.file_id,)
@staticmethod
def de_json(data, bot):
+13 -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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -28,8 +28,8 @@ from abc import ABCMeta
class TelegramObject(object):
"""Base class for most telegram objects."""
__metaclass__ = ABCMeta
_id_attrs = ()
def __str__(self):
return str(self.to_dict())
@@ -69,7 +69,7 @@ class TelegramObject(object):
data = dict()
for key in iter(self.__dict__):
if key == 'bot':
if key in ('bot', '_id_attrs'):
continue
value = self.__dict__[key]
@@ -80,3 +80,13 @@ class TelegramObject(object):
data[key] = value
return data
def __eq__(self, other):
if isinstance(other, self.__class__):
return self._id_attrs == other._id_attrs
return super(TelegramObject, self).__eq__(other)
def __hash__(self):
if self._id_attrs:
return hash((self.__class__, self._id_attrs))
return super(TelegramObject, self).__hash__()
+664 -393
View File
File diff suppressed because it is too large Load Diff
+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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+3 -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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -60,6 +60,8 @@ class CallbackQuery(TelegramObject):
if not data:
return None
data = super(CallbackQuery, CallbackQuery).de_json(data, bot)
data['from_user'] = User.de_json(data.get('from'), bot)
data['message'] = Message.de_json(data.get('message'), bot)
+9 -8
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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -32,7 +32,7 @@ class Chat(TelegramObject):
username (str): Username, for private chats and channels if available
first_name (str): First name of the other party in a private chat
last_name (str): Last name of the other party in a private chat
all_members_are_admins (bool): True if a group has 'All Members Are Admins' enabled.
all_members_are_administrators (bool): True if group has 'All Members Are Administrators'
Args:
id (int):
@@ -53,11 +53,11 @@ class Chat(TelegramObject):
def __init__(self,
id,
type,
title='',
username='',
first_name='',
last_name='',
all_members_are_admins=False,
title=None,
username=None,
first_name=None,
last_name=None,
all_members_are_administrators=None,
bot=None,
**kwargs):
# Required
@@ -68,9 +68,10 @@ class Chat(TelegramObject):
self.username = username
self.first_name = first_name
self.last_name = last_name
self.all_members_are_admins = all_members_are_admins
self.all_members_are_administrators = all_members_are_administrators
self.bot = bot
self._id_attrs = (self.id,)
@staticmethod
def de_json(data, bot):
+1 -1
View File
@@ -2,7 +2,7 @@
# pylint: disable=R0903
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+5 -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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -46,6 +46,8 @@ class ChatMember(TelegramObject):
self.user = user
self.status = status
self._id_attrs = (self.user, self.status)
@staticmethod
def de_json(data, bot):
"""
@@ -59,6 +61,8 @@ class ChatMember(TelegramObject):
if not data:
return None
data = super(ChatMember, ChatMember).de_json(data, bot)
data['user'] = User.de_json(data.get('user'), bot)
return ChatMember(**data)
+6 -2
View File
@@ -2,7 +2,8 @@
# pylint: disable=R0902,R0912,R0913
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <devs@python-telegram-bot.org>
# Copyright (C) 2015-2017
# 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
@@ -61,6 +62,8 @@ class ChosenInlineResult(TelegramObject):
self.location = location
self.inline_message_id = inline_message_id
self._id_attrs = (self.result_id,)
@staticmethod
def de_json(data, bot):
"""
@@ -74,7 +77,8 @@ class ChosenInlineResult(TelegramObject):
if not data:
return None
# Required
data = super(ChosenInlineResult, ChosenInlineResult).de_json(data, bot)
# Required
data['from_user'] = User.de_json(data.pop('from'), bot)
# Optionals
data['location'] = Location.de_json(data.get('location'), bot)
+3 -1
View File
@@ -1,5 +1,5 @@
# python-telegram-bot - a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Copyright (C) 2015-2017
# by the python-telegram-bot contributors <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -32,6 +32,7 @@ Attributes:
limit, but eventually you'll begin receiving 429 errors.
MAX_MESSAGES_PER_SECOND (int)
MAX_MESSAGES_PER_MINUTE_PER_GROUP (int)
MAX_INLINE_QUERY_RESULTS (int)
The following constant have been found by experimentation:
@@ -52,3 +53,4 @@ MAX_MESSAGES_PER_SECOND_PER_CHAT = 1
MAX_MESSAGES_PER_SECOND = 30
MAX_MESSAGES_PER_MINUTE_PER_GROUP = 20
MAX_MESSAGE_ENTITIES = 100
MAX_INLINE_QUERY_RESULTS = 50
+5 -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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -39,13 +39,15 @@ class Contact(TelegramObject):
"""
def __init__(self, phone_number, first_name, last_name='', user_id=0, **kwargs):
def __init__(self, phone_number, first_name, last_name=None, user_id=None, **kwargs):
# Required
self.phone_number = str(phone_number)
self.first_name = first_name
# Optionals
self.last_name = last_name
self.user_id = int(user_id)
self.user_id = user_id
self._id_attrs = (self.phone_number,)
@staticmethod
def de_json(data, bot):
+16 -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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -41,14 +41,24 @@ class Document(TelegramObject):
"""
def __init__(self, file_id, thumb=None, file_name='', mime_type='', file_size=0, **kwargs):
_id_keys = ('file_id',)
def __init__(self,
file_id,
thumb=None,
file_name=None,
mime_type=None,
file_size=None,
**kwargs):
# Required
self.file_id = str(file_id)
# Optionals
self.thumb = thumb
self.file_name = file_name
self.mime_type = str(mime_type)
self.file_size = int(file_size)
self.mime_type = mime_type
self.file_size = file_size
self._id_attrs = (self.file_id,)
@staticmethod
def de_json(data, bot):
@@ -63,6 +73,8 @@ class Document(TelegramObject):
if not data:
return None
data = super(Document, Document).de_json(data, bot)
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
return Document(**data)
+1 -1
View File
@@ -3,7 +3,7 @@
# pylint: disable=C0103,R0903,E0213
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -62,9 +62,7 @@ class TelegramError(Exception):
class Unauthorized(TelegramError):
def __init__(self):
super(Unauthorized, self).__init__('Unauthorized')
pass
class InvalidToken(TelegramError):
+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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -23,7 +23,6 @@ import re
from future.utils import string_types
from telegram import Update
from telegram.utils.deprecate import deprecate
from .handler import Handler
@@ -39,7 +38,7 @@ class CallbackQueryHandler(Handler):
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
be used to insert updates. Default is ``False``.
be used to insert updates. Default is ``False``.
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
@@ -105,8 +104,3 @@ class CallbackQueryHandler(Handler):
optional_args['groupdict'] = match.groupdict()
return self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.CallbackQueryHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+2 -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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -35,7 +35,7 @@ class ChosenInlineResultHandler(Handler):
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
be used to insert updates. Default is ``False``.
be used to insert updates. Default is ``False``.
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
+43 -12
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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -17,10 +17,10 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the CommandHandler class """
import warnings
from .handler import Handler
from telegram import Update
from telegram.utils.deprecate import deprecate
class CommandHandler(Handler):
@@ -30,10 +30,15 @@ class CommandHandler(Handler):
name and/or some additional text.
Args:
command (str): The name of the command this handler should listen for.
command (str|list): The name of the command or list of command this handler should
listen for.
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
filters (telegram.ext.BaseFilter): A filter inheriting from
:class:`telegram.ext.filters.BaseFilter`. Standard filters can be found in
:class:`telegram.ext.filters.Filters`. Filters can be combined using bitwise
operators (& for and, | for or).
allow_edited (Optional[bool]): If the handler should also accept edited messages.
Default is ``False``
pass_args (optional[bool]): If the handler should be passed the
@@ -44,7 +49,7 @@ class CommandHandler(Handler):
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
be used to insert updates. Default is ``False``.
be used to insert updates. Default is ``False``.
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
@@ -62,6 +67,7 @@ class CommandHandler(Handler):
def __init__(self,
command,
callback,
filters=None,
allow_edited=False,
pass_args=False,
pass_update_queue=False,
@@ -74,17 +80,47 @@ class CommandHandler(Handler):
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
self.command = command
try:
_str = basestring # Python 2
except NameError:
_str = str # Python 3
if isinstance(command, _str):
self.command = [command]
else:
self.command = command
self.filters = filters
self.allow_edited = allow_edited
self.pass_args = pass_args
# We put this up here instead of with the rest of checking code
# in check_update since we don't wanna spam a ton
if isinstance(self.filters, list):
warnings.warn('Using a list of filters in MessageHandler is getting '
'deprecated, please use bitwise operators (& and |) '
'instead. More info: https://git.io/vPTbc.')
def check_update(self, update):
if (isinstance(update, Update)
and (update.message or update.edited_message and self.allow_edited)):
message = update.message or update.edited_message
return (message.text and message.text.startswith('/')
and message.text[1:].split(' ')[0].split('@')[0] == self.command)
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 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] in self.command
and command[1].lower() == message.bot.username.lower())
else:
return False
else:
return False
@@ -98,8 +134,3 @@ class CommandHandler(Handler):
optional_args['args'] = message.text.split()[1:]
return self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.CommandHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+79 -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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -21,14 +21,16 @@
import logging
from telegram import Update
from telegram.ext import Handler
from telegram.utils.helpers import extract_chat_and_user
from telegram.ext import (Handler, CallbackQueryHandler, InlineQueryHandler,
ChosenInlineResultHandler)
from telegram.utils.promise import Promise
class ConversationHandler(Handler):
"""
A handler to hold a conversation with a user by managing four collections of other handlers.
A handler to hold a conversation with a single user by managing four collections of other
handlers. Note that neither posts in Telegram Channels, nor group interactions with multiple
users are managed by instances of this class.
The first collection, a ``list`` named ``entry_points``, is used to initiate the conversation,
for example with a ``CommandHandler`` or ``RegexHandler``.
@@ -86,7 +88,10 @@ class ConversationHandler(Handler):
fallbacks,
allow_reentry=False,
run_async_timeout=None,
timed_out_behavior=None):
timed_out_behavior=None,
per_chat=True,
per_user=True,
per_message=False):
self.entry_points = entry_points
""":type: list[telegram.ext.Handler]"""
@@ -104,21 +109,76 @@ class ConversationHandler(Handler):
""":type: list[telegram.ext.Handler]"""
self.conversations = dict()
""":type: dict[(int, int): str]"""
self.per_user = per_user
self.per_chat = per_chat
self.per_message = per_message
""":type: dict[tuple: object]"""
self.current_conversation = None
self.current_handler = None
self.logger = logging.getLogger(__name__)
if not any((self.per_user, self.per_chat, self.per_message)):
raise ValueError("'per_user', 'per_chat' and 'per_message' can't all be 'False'")
if self.per_message and not self.per_chat:
logging.warning("If 'per_message=True' is used, 'per_chat=True' should also be used, "
"since message IDs are not globally unique.")
all_handlers = list()
all_handlers.extend(entry_points)
all_handlers.extend(fallbacks)
for state_handlers in states.values():
all_handlers.extend(state_handlers)
if self.per_message:
for handler in all_handlers:
if not isinstance(handler, CallbackQueryHandler):
logging.warning("If 'per_message=True', all entry points and state handlers"
" must be 'CallbackQueryHandler', since no other handlers "
"have a message context.")
else:
for handler in all_handlers:
if isinstance(handler, CallbackQueryHandler):
logging.warning("If 'per_message=False', 'CallbackQueryHandler' will not be "
"tracked for every message.")
if self.per_chat:
for handler in all_handlers:
if isinstance(handler, (InlineQueryHandler, ChosenInlineResultHandler)):
logging.warning("If 'per_chat=True', 'InlineQueryHandler' can not be used, "
"since inline queries have no chat context.")
def _get_key(self, update):
chat = update.effective_chat
user = update.effective_user
key = list()
if self.per_chat:
key.append(chat.id)
if self.per_user:
key.append(user.id)
if self.per_message:
key.append(update.callback_query.inline_message_id
or update.callback_query.message.message_id)
return tuple(key)
def check_update(self, update):
if not isinstance(update, Update):
# 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):
return False
chat, user = extract_chat_and_user(update)
key = (chat.id, user.id) if chat else (None, user.id)
key = self._get_key(update)
state = self.conversations.get(key)
# Resolve promises
@@ -126,10 +186,16 @@ class ConversationHandler(Handler):
self.logger.debug('waiting for promise...')
old_state, new_state = state
new_state.result(timeout=self.run_async_timeout)
error = False
try:
res = new_state.result(timeout=self.run_async_timeout)
except Exception as exc:
self.logger.exception("Promise function raised exception")
self.logger.exception("{}".format(exc))
error = True
if new_state.done.is_set():
self.update_state(new_state.result(), key)
if not error and new_state.done.is_set():
self.update_state(res, key)
state = self.conversations.get(key)
else:
+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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -32,7 +32,6 @@ from future.builtins import range
from telegram import TelegramError
from telegram.ext.handler import Handler
from telegram.utils.deprecate import deprecate
from telegram.utils.promise import Promise
logging.getLogger(__name__).addHandler(logging.NullHandler())
@@ -159,11 +158,7 @@ class Dispatcher(object):
len(self.__async_threads))
break
try:
promise.run()
except:
self.logger.exception("run_async function raised exception")
promise.run()
def run_async(self, func, *args, **kwargs):
"""Queue a function (with given args/kwargs) to be run asynchronously.
@@ -309,7 +304,7 @@ class Dispatcher(object):
which handlers were added to the group defines the priority.
Args:
handler (Handler): A Handler instance
handler (telegram.ext.Handler): A Handler instance
group (Optional[int]): The group identifier. Default is 0
"""
@@ -330,7 +325,7 @@ class Dispatcher(object):
Remove a handler from the specified group
Args:
handler (Handler): A Handler instance
handler (telegram.ext.Handler): A Handler instance
group (optional[object]): The group identifier. Default is 0
"""
if handler in self.handlers[group]:
@@ -372,11 +367,3 @@ class Dispatcher(object):
for callback in self.error_handlers:
callback(self.bot, update, error)
# old non-PEP8 Dispatcher methods
m = "telegram.dispatcher."
addHandler = deprecate(add_handler, m + "AddHandler", m + "add_handler")
removeHandler = deprecate(remove_handler, m + "removeHandler", m + "remove_handler")
addErrorHandler = deprecate(add_error_handler, m + "addErrorHandler", m + "add_error_handler")
removeErrorHandler = deprecate(remove_error_handler, m + "removeErrorHandler",
m + "remove_error_handler")
+50 -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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -17,6 +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 the Filters for use with the MessageHandler class """
from telegram import Chat
class BaseFilter(object):
@@ -32,9 +33,14 @@ class BaseFilter(object):
>>> (Filters.audio | Filters.video)
Not:
>>> ~ Filters.command
Also works with more than two filters:
>>> (Filters.text & (Filters.entity(URL) | Filters.entity(TEXT_LINK)))
>>> Filters.text & (~ Filters.forwarded)
If you want to create your own filters create a class inheriting from this class and implement
a `filter` method that returns a boolean: `True` if the message should be handled, `False`
@@ -51,10 +57,32 @@ class BaseFilter(object):
def __or__(self, other):
return MergedFilter(self, or_filter=other)
def __invert__(self):
return InvertedFilter(self)
def filter(self, message):
raise NotImplementedError
class InvertedFilter(BaseFilter):
"""Represents a filter that has been inverted.
Args:
f: The filter to invert
"""
def __init__(self, f):
self.f = f
def filter(self, message):
return not self.f(message)
def __str__(self):
return "<telegram.ext.filters.InvertedFilter inverting {}>".format(self.f)
__repr__ = __str__
class MergedFilter(BaseFilter):
"""Represents a filter consisting of two other filters.
@@ -107,6 +135,13 @@ class Filters(object):
def filter(self, message):
return bool(message.text and message.text.startswith('/'))
class _Reply(BaseFilter):
def filter(self, message):
return bool(message.reply_to_message)
reply = _Reply()
command = _Command()
class _Audio(BaseFilter):
@@ -214,3 +249,17 @@ class Filters(object):
def filter(self, message):
return any([entity.type == self.entity_type for entity in message.entities])
class _Private(BaseFilter):
def filter(self, message):
return message.chat.type == Chat.PRIVATE
private = _Private()
class _Group(BaseFilter):
def filter(self, message):
return message.chat.type in [Chat.GROUP, Chat.SUPERGROUP]
group = _Group()
+7 -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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -19,9 +19,6 @@
""" This module contains the base class for handlers as used by the
Dispatcher """
from telegram.utils.deprecate import deprecate
from telegram.utils.helpers import extract_chat_and_user
class Handler(object):
"""
@@ -35,7 +32,7 @@ class Handler(object):
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
be used to insert updates. Default is ``False``.
be used to insert updates. Default is ``False``.
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
@@ -85,7 +82,7 @@ class Handler(object):
Args:
update (object): The update to be handled
dispatcher (Dispatcher): The dispatcher to collect optional args
dispatcher (telegram.ext.Dispatcher): The dispatcher to collect optional args
"""
raise NotImplementedError
@@ -96,7 +93,7 @@ class Handler(object):
handlers
Args:
dispatcher (Dispatcher):
dispatcher (telegram.ext.Dispatcher):
"""
optional_args = dict()
@@ -105,19 +102,13 @@ class Handler(object):
if self.pass_job_queue:
optional_args['job_queue'] = dispatcher.job_queue
if self.pass_user_data or self.pass_chat_data:
chat, user = extract_chat_and_user(update)
chat = update.effective_chat
user = update.effective_user
if self.pass_user_data:
optional_args['user_data'] = dispatcher.user_data[user.id]
optional_args['user_data'] = dispatcher.user_data[user.id if user else None]
if self.pass_chat_data:
optional_args['chat_data'] = dispatcher.chat_data[chat.id if chat else None]
return optional_args
# old non-PEP8 Handler methods
m = "telegram.Handler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
collectOptionalArgs = deprecate(collect_optional_args, m + "collectOptionalArgs",
m + "collect_optional_args")
+2 -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-2016
# Copyright (C) 2015-2017
# 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_update_queue (optional[bool]): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
be used to insert updates. Default is ``False``.
be used to insert updates. Default is ``False``.
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
+290 -37
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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -21,19 +21,27 @@
import logging
import time
import warnings
import datetime
import weakref
from numbers import Number
from threading import Thread, Lock, Event
from queue import PriorityQueue, Empty
class Days(object):
MON, TUE, WED, THU, FRI, SAT, SUN = range(7)
EVERY_DAY = tuple(range(7))
class JobQueue(object):
"""This class allows you to periodically perform tasks with the bot.
Attributes:
queue (PriorityQueue):
bot (Bot):
bot (telegram.Bot):
Args:
bot (Bot): The bot instance that should be passed to the jobs
bot (telegram.Bot): The bot instance that should be passed to the jobs
Deprecated: 5.2
prevent_autostart (Optional[bool]): Thread does not start during initialisation.
@@ -60,30 +68,183 @@ class JobQueue(object):
"""Queue a new job.
Args:
job (Job): The ``Job`` instance representing the new job
next_t (Optional[float]): Time in seconds in which the job should be executed first.
Defaults to ``job.interval``
job (telegram.ext.Job): The ``Job`` instance representing the new job
next_t (Optional[int, float, datetime.timedelta, datetime.datetime, datetime.time]):
Time in or at which the job should run for the first time. This parameter will be
interpreted depending on its type.
``int`` or ``float`` will be interpreted as "seconds from now" in which the job
should run.
``datetime.timedelta`` will be interpreted as "time from now" in which the job
should run.
``datetime.datetime`` will be interpreted as a specific date and time at which the
job should run.
``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):
"""Queue a new job.
Args:
job (telegram.ext.Job): The ``Job`` instance representing the new job
next_t (Optional[int, float, datetime.timedelta, datetime.datetime, datetime.time]):
Time in or at which the job should run for the first time. This parameter will be
interpreted depending on its type.
* ``int`` or ``float`` will be interpreted as "seconds from now" in which the job
should run.
* ``datetime.timedelta`` will be interpreted as "time from now" in which the job
should run.
* ``datetime.datetime`` will be interpreted as a specific date and time at which
the job should run.
* ``datetime.time`` will be interpreted as a specific time of day at which the job
should run. This could be either today or, if the time has already passed,
tomorrow.
last_t (Optional[float]): Timestamp of the time when ``job`` was scheduled for in the
last ``put`` call. If provided, it will be used to calculate the next timestamp
more accurately by accounting for the execution time of the job (and possibly
others). If None, `now` will be assumed.
"""
job.job_queue = self
if next_t is None:
next_t = job.interval
if next_t is None:
raise ValueError('next_t is None')
now = time.time()
next_t += now
if isinstance(next_t, datetime.datetime):
next_t = (next_t - datetime.datetime.now()).total_seconds()
elif isinstance(next_t, datetime.time):
next_datetime = datetime.datetime.combine(datetime.date.today(), next_t)
if datetime.datetime.now().time() > next_t:
next_datetime += datetime.timedelta(days=1)
next_t = (next_datetime - datetime.datetime.now()).total_seconds()
elif isinstance(next_t, datetime.timedelta):
next_t = next_t.total_seconds()
next_t += last_t or time.time()
self.logger.debug('Putting job %s with t=%f', job.name, next_t)
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.
Args:
callback (function): The callback function that should be executed by the new job. It
should take two parameters ``bot`` and ``job``, where ``job`` is the ``Job``
instance. It can be used to access it's ``context`` or change it to a repeating
job.
when (int, float, datetime.timedelta, datetime.datetime, datetime.time):
Time in or at which the job should run. This parameter will be interpreted
depending on its type.
* ``int`` or ``float`` will be interpreted as "seconds from now" in which the job
should run.
* ``datetime.timedelta`` will be interpreted as "time from now" in which the job
should run.
* ``datetime.datetime`` will be interpreted as a specific date and time at which
the job should run.
* ``datetime.time`` will be interpreted as a specific time of day at which the job
should run. This could be either today or, if the time has already passed,
tomorrow.
context (Optional[object]): Additional data needed for the callback function. Can be
accessed through ``job.context`` in the callback. Defaults to ``None``
name (Optional[str]): The name of the new job. Defaults to ``callback.__name__``
Returns:
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.
Args:
callback (function): The callback function that should be executed by the new job. It
should take two parameters ``bot`` and ``job``, where ``job`` is the ``Job``
instance. It can be used to access it's ``context``, terminate the job or change
its interval.
interval (int, float, datetime.timedelta): The interval in which the job will run.
If it is an ``int`` or a ``float``, it will be interpreted as seconds.
first (int, float, datetime.timedelta, datetime.datetime, datetime.time):
* ``int`` or ``float`` will be interpreted as "seconds from now" in which the job
should run.
* ``datetime.timedelta`` will be interpreted as "time from now" in which the job
should run.
* ``datetime.datetime`` will be interpreted as a specific date and time at which
the job should run.
* ``datetime.time`` will be interpreted as a specific time of day at which the job
should run. This could be either today or, if the time has already passed,
tomorrow.
Defaults to ``interval``
context (Optional[object]): Additional data needed for the callback function. Can be
accessed through ``job.context`` in the callback. Defaults to ``None``
name (Optional[str]): The name of the new job. Defaults to ``callback.__name__``
Returns:
Job: The new ``Job`` instance that has been added to the job queue.
"""
job = Job(callback,
interval=interval,
repeat=True,
context=context,
name=name,
job_queue=self)
self._put(job, next_t=first)
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.
Args:
callback (function): The callback function that should be executed by the new job. It
should take two parameters ``bot`` and ``job``, where ``job`` is the ``Job``
instance. It can be used to access it's ``context`` or terminate the job.
time (datetime.time): Time of day at which the job should run.
days (Optional[tuple[int]]): Defines on which days of the week the job should run.
Defaults to ``Days.EVERY_DAY``
context (Optional[object]): Additional data needed for the callback function. Can be
accessed through ``job.context`` in the callback. Defaults to ``None``
name (Optional[str]): The name of the new job. Defaults to ``callback.__name__``
Returns:
Job: The new ``Job`` instance that has been added to the job queue.
"""
job = Job(callback,
interval=datetime.timedelta(days=1),
repeat=True,
days=days,
context=context,
name=name,
job_queue=self)
self._put(job, next_t=time)
return job
def _set_next_peek(self, t):
"""
Set next peek if not defined or `t` is before next peek.
In case the next peek was set, also trigger the `self.__tick` event.
"""
with self.__next_peek_lock:
if not self._next_peek or self._next_peek > t:
@@ -108,9 +269,9 @@ class JobQueue(object):
self.logger.debug('Peeked at %s with t=%f', job.name, t)
if t > now:
# we can get here in two conditions:
# 1. At the second or later pass of the while loop, after we've already processed
# the job(s) we were supposed to at this time.
# We can get here in two conditions:
# 1. At the second or later pass of the while loop, after we've already
# processed the job(s) we were supposed to at this time.
# 2. At the first iteration of the loop only if `self.put()` had triggered
# `self.__tick` because `self._next_peek` wasn't set
self.logger.debug("Next task isn't due yet. Finished!")
@@ -118,25 +279,27 @@ class JobQueue(object):
self._set_next_peek(t)
break
if job._remove.is_set():
if job.removed:
self.logger.debug('Removing job %s', job.name)
continue
if job.enabled:
self.logger.debug('Running job %s', job.name)
try:
job.run(self.bot)
current_week_day = datetime.datetime.now().weekday()
if any(day == current_week_day for day in job.days):
self.logger.debug('Running job %s', job.name)
job.run(self.bot)
except:
self.logger.exception('An uncaught error was raised while executing job %s',
job.name)
else:
self.logger.debug('Skipping disabled job %s', job.name)
if job.repeat:
self.put(job)
if job.repeat and not job.removed:
self._put(job, last_t=t)
else:
self.logger.debug('Dropping non-repeating or removed job %s', job.name)
def start(self):
"""
@@ -151,7 +314,6 @@ class JobQueue(object):
self.__thread = Thread(target=self._main_loop, name="job_queue")
self.__thread.start()
self.logger.debug('%s thread started', self.__class__.__name__)
else:
self.__start_lock.release()
@@ -164,7 +326,7 @@ class JobQueue(object):
while self._running:
# self._next_peek may be (re)scheduled during self.tick() or self.put()
with self.__next_peek_lock:
tmout = self._next_peek and self._next_peek - time.time()
tmout = self._next_peek - time.time() if self._next_peek else None
self._next_peek = None
self.__tick.clear()
@@ -198,33 +360,58 @@ class Job(object):
"""This class encapsulates a Job
Attributes:
callback (function):
interval (float):
repeat (bool):
name (str):
callback (function): The function that the job executes when it's due
interval (int, float, datetime.timedelta): The interval in which the job runs
days (tuple[int]): A tuple of ``int`` values that determine on which days of the week the
job runs
repeat (bool): If the job runs periodically or only once
name (str): The name of this job
job_queue (JobQueue): The ``JobQueue`` this job belongs to
enabled (bool): Boolean property that decides if this job is currently active
Args:
callback (function): The callback function that should be executed by the Job. It should
take two parameters ``bot`` and ``job``, where ``job`` is the ``Job`` instance. It
can be used to terminate the job or modify its interval.
interval (float): The interval in which this job should execute its callback function in
seconds.
interval (Optional[int, float, datetime.timedelta]): The interval in which the job will
execute its callback function. ``int`` and ``float`` will be interpreted as seconds.
If you don't set this value, you must set ``repeat=False`` and specify ``next_t`` when
you put the job into the job queue.
repeat (Optional[bool]): If this job should be periodically execute its callback function
(``True``) or only once (``False``). Defaults to ``True``
context (Optional[object]): Additional data needed for the callback function. Can be
accessed through ``job.context`` in the callback. Defaults to ``None``
days (Optional[tuple[int]]): Defines on which days of the week the job should run.
Defaults to ``Days.EVERY_DAY``
name (Optional[str]): The name of this job. Defaults to ``callback.__name__``
job_queue (Optional[class:`telegram.ext.JobQueue`]): The ``JobQueue`` this job belongs to.
Only optional for backward compatibility with ``JobQueue.put()``.
"""
job_queue = None
def __init__(self, callback, interval, repeat=True, context=None):
def __init__(self,
callback,
interval=None,
repeat=True,
context=None,
days=Days.EVERY_DAY,
name=None,
job_queue=None):
self.callback = callback
self.context = context
self.name = name or callback.__name__
self._repeat = repeat
self._interval = None
self.interval = interval
self.repeat = repeat
self.context = context
self.name = callback.__name__
self._days = None
self.days = days
self._job_queue = weakref.proxy(job_queue) if job_queue is not None else None
self._remove = Event()
self._enabled = Event()
self._enabled.set()
@@ -237,20 +424,86 @@ class Job(object):
"""
Schedules this job for removal from the ``JobQueue``. It will be removed without executing
its callback function again.
"""
self._remove.set()
def is_enabled(self):
@property
def removed(self):
return self._remove.is_set()
@property
def enabled(self):
return self._enabled.is_set()
def set_enabled(self, status):
@enabled.setter
def enabled(self, status):
if status:
self._enabled.set()
else:
self._enabled.clear()
enabled = property(is_enabled, set_enabled)
@property
def interval(self):
return self._interval
@interval.setter
def interval(self, interval):
if interval is None and self.repeat:
raise ValueError("The 'interval' can not be 'None' when 'repeat' is set to 'True'")
if not (interval is None or isinstance(interval, (Number, datetime.timedelta))):
raise ValueError("The 'interval' must be of type 'datetime.timedelta',"
" 'int' or 'float'")
self._interval = interval
@property
def interval_seconds(self):
if isinstance(self.interval, datetime.timedelta):
return self.interval.total_seconds()
else:
return self.interval
@property
def repeat(self):
return self._repeat
@repeat.setter
def repeat(self, repeat):
if self.interval is None and repeat:
raise ValueError("'repeat' can not be set to 'True' when no 'interval' is set")
self._repeat = repeat
@property
def days(self):
return self._days
@days.setter
def days(self, days):
if not isinstance(days, tuple):
raise ValueError("The 'days' argument should be of type 'tuple'")
if not all(isinstance(day, int) for day in days):
raise ValueError("The elements of the 'days' argument should be of type 'int'")
if not all(0 <= day <= 6 for day in days):
raise ValueError("The elements of the 'days' argument should be from 0 up to and "
"including 6")
self._days = days
@property
def job_queue(self):
""" :rtype: JobQueue """
return self._job_queue
@job_queue.setter
def job_queue(self, job_queue):
# Property setter for backward compatibility with JobQueue.put()
if not self._job_queue:
self._job_queue = weakref.proxy(job_queue)
else:
raise RuntimeError("The 'job_queue' attribute can only be set once.")
def __lt__(self, other):
return False
+33 -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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -21,7 +21,6 @@ import warnings
from .handler import Handler
from telegram import Update
from telegram.utils.deprecate import deprecate
class MessageHandler(Handler):
@@ -32,14 +31,12 @@ class MessageHandler(Handler):
Args:
filters (telegram.ext.BaseFilter): A filter inheriting from
:class:`telegram.filters.BaseFilter`. Standard filters can be found in
:class:`telegram.filters.Filters`. Filters can be combined using bitwise
:class:`telegram.ext.filters.BaseFilter`. Standard filters can be found in
:class:`telegram.ext.filters.Filters`. Filters can be combined using bitwise
operators (& for and, | for or).
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
allow_edited (Optional[bool]): If the handler should also accept edited messages.
Default is ``False``
pass_update_queue (optional[bool]): If the handler should be passed the
update queue as a keyword argument called ``update_queue``. It can
be used to insert updates. Default is ``False``
@@ -51,6 +48,15 @@ class MessageHandler(Handler):
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
message_updates (Optional[bool]): Should "normal" message updates be handled? Default is
``True``.
allow_edited (Optional[bool]): If the handler should also accept edited messages.
Default is ``False`` - Deprecated. use edited updates instead.
channel_post_updates (Optional[bool]): Should channel posts updates be handled? Default is
``True``.
edited_updates (Optional[bool]): Should "edited" message updates be handled? Default is
``False``.
"""
def __init__(self,
@@ -60,7 +66,17 @@ class MessageHandler(Handler):
pass_update_queue=False,
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
pass_chat_data=False,
message_updates=True,
channel_post_updates=True,
edited_updates=False):
if not message_updates and not channel_post_updates and not edited_updates:
raise ValueError(
'message_updates, channel_post_updates and edited_updates are all False')
if allow_edited:
warnings.warn('allow_edited is getting deprecated, please use edited_updates instead')
edited_updates = allow_edited
super(MessageHandler, self).__init__(
callback,
pass_update_queue=pass_update_queue,
@@ -68,7 +84,9 @@ class MessageHandler(Handler):
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
self.filters = filters
self.allow_edited = allow_edited
self.message_updates = message_updates
self.channel_post_updates = channel_post_updates
self.edited_updates = edited_updates
# We put this up here instead of with the rest of checking code
# in check_update since we don't wanna spam a ton
@@ -77,15 +95,19 @@ class MessageHandler(Handler):
'deprecated, please use bitwise operators (& and |) '
'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)])
def check_update(self, update):
if (isinstance(update, Update)
and (update.message or update.edited_message and self.allow_edited)):
if isinstance(update, Update) and self._is_allowed_update(update):
if not self.filters:
res = True
else:
message = update.message or update.edited_message
message = update.effective_message
if isinstance(self.filters, list):
res = any(func(message) for func in self.filters)
else:
@@ -100,8 +122,3 @@ class MessageHandler(Handler):
optional_args = self.collect_optional_args(dispatcher, update)
return self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.MessageHandler."
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
+330
View File
@@ -0,0 +1,330 @@
#!/usr/bin/env python
#
# Module author:
# Tymofii A. Khodniev (thodnev) <thodnev@mail.ru>
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# 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/]
'''A throughput-limiting message processor for Telegram bots'''
from telegram.utils import promise
import functools
import sys
import time
import threading
if sys.version_info.major > 2:
import queue as q
else:
import Queue as q
# We need to count < 1s intervals, so the most accurate timer is needed
# Starting from Python 3.3 we have time.perf_counter which is the clock
# with the highest resolution available to the system, so let's use it there.
# In Python 2.7, there's no perf_counter yet, so fallback on what we have:
# on Windows, the best available is time.clock while time.time is on
# another platforms (M. Lutz, "Learning Python," 4ed, p.630-634)
if sys.version_info.major == 3 and sys.version_info.minor >= 3:
curtime = time.perf_counter # pylint: disable=E1101
else:
curtime = time.clock if sys.platform[:3] == 'win' else time.time
class DelayQueueError(RuntimeError):
'''Indicates processing errors'''
pass
class DelayQueue(threading.Thread):
'''Processes callbacks from queue with specified throughput limits.
Creates a separate thread to process callbacks with delays.
Args:
queue (:obj:`queue.Queue`, optional): used to pass callbacks to
thread.
Creates `queue.Queue` implicitly if not provided.
burst_limit (:obj:`int`, optional): number of maximum callbacks to
process per time-window defined by `time_limit_ms`.
Defaults to 30.
time_limit_ms (:obj:`int`, optional): defines width of time-window
used when each processing limit is calculated.
Defaults to 1000.
exc_route (:obj:`callable`, optional): a callable, accepting 1
positional argument; used to route exceptions from processor
thread to main thread; is called on `Exception` subclass
exceptions.
If not provided, exceptions are routed through dummy handler,
which re-raises them.
autostart (:obj:`bool`, optional): if True, processor is started
immediately after object's 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
def __init__(self,
queue=None,
burst_limit=30,
time_limit_ms=1000,
exc_route=None,
autostart=True,
name=None):
self._queue = queue if queue is not None else q.Queue()
self.burst_limit = burst_limit
self.time_limit = time_limit_ms / 1000
self.exc_route = (exc_route if exc_route is not None else self._default_exception_handler)
self.__exit_req = False # flag to gently exit thread
self.__class__._instcnt += 1
if name is None:
name = '%s-%s' % (self.__class__.__name__, self.__class__._instcnt)
super(DelayQueue, self).__init__(name=name)
self.daemon = False
if autostart: # immediately start processing
super(DelayQueue, self).start()
def run(self):
'''Do not use the method except for unthreaded testing purposes,
the method normally is automatically called by `start` method.
'''
times = [] # used to store each callable processing time
while True:
item = self._queue.get()
if self.__exit_req:
return # shutdown thread
# delay routine
now = curtime()
t_delta = now - self.time_limit # calculate early to improve perf.
if times and t_delta > times[-1]:
# if last call was before the limit time-window
# used to impr. perf. in long-interval calls case
times = [now]
else:
# collect last in current limit time-window
times = [t for t in times if t >= t_delta]
times.append(now)
if len(times) >= self.burst_limit: # if throughput limit was hit
time.sleep(times[1] - t_delta)
# finally process one
try:
func, args, kwargs = item
func(*args, **kwargs)
except Exception as exc: # re-route any exceptions
self.exc_route(exc) # to prevent thread exit
def stop(self, timeout=None):
'''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. `is_alive` could be used afterwards
to check the actual status. If `timeout` set to None, blocks
until processor is shut down.
Defaults to None.
Returns:
None
'''
self.__exit_req = True # gently request
self._queue.put(None) # put something to unfreeze if frozen
super(DelayQueue, self).join(timeout=timeout)
@staticmethod
def _default_exception_handler(exc):
'''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.
Args:
func (:obj:`callable`): the actual function (or any callable) that
is processed through queue.
*args: variable-length `func` arguments.
**kwargs: arbitrary keyword-arguments to `func`.
Returns:
None
'''
if not self.is_alive() or self.__exit_req:
raise DelayQueueError('Could not process callback in stopped thread')
self._queue.put((func, args, kwargs))
# The most straightforward way to implement this is to use 2 sequenital delay
# queues, like on classic delay chain schematics in electronics.
# So, message path is:
# msg --> group delay if group msg, else no delay --> normal msg delay --> out
# This way OS threading scheduler cares of timings accuracy.
# (see time.time, time.clock, time.perf_counter, time.sleep @ docs.python.org)
class MessageQueue(object):
'''Implements callback processing with proper delays to avoid hitting
Telegram's message limits.
Contains two `DelayQueue`s, for group and for all messages, interconnected
in delay chain. Callables are processed through *group* `DelayQueue`, then
through *all* `DelayQueue` for group-type messages. For non-group messages,
only the *all* `DelayQueue` is used.
Args:
all_burst_limit (:obj:`int`, optional): numer of maximum *all-type*
callbacks to process per time-window defined by
`all_time_limit_ms`.
Defaults to 30.
all_time_limit_ms (:obj:`int`, optional): defines width of *all-type*
time-window used when each processing limit is calculated.
Defaults to 1000 ms.
group_burst_limit (:obj:`int`, optional): numer of maximum *group-type*
callbacks to process per time-window defined by
`group_time_limit_ms`.
Defaults to 20.
group_time_limit_ms (:obj:`int`, optional): defines width of
*group-type* time-window used when each processing limit is
calculated.
Defaults to 60000 ms.
exc_route (:obj:`callable`, optional): a callable, accepting one
positional argument; used to route exceptions from processor
threads to main thread; is called on `Exception` subclass
exceptions.
If not provided, exceptions are routed through dummy handler,
which re-raises them.
autostart (:obj:`bool`, optional): if True, processors are started
immediately after object's creation; if False, should be
started manually by `start` method.
Defaults to True.
Attributes:
_all_delayq (:obj:`telegram.ext.messagequeue.DelayQueue`): actual
`DelayQueue` used for *all-type* callback processing
_group_delayq (:obj:`telegram.ext.messagequeue.DelayQueue`): actual
`DelayQueue` used for *group-type* callback processing
'''
def __init__(self,
all_burst_limit=30,
all_time_limit_ms=1000,
group_burst_limit=20,
group_time_limit_ms=60000,
exc_route=None,
autostart=True):
# create accoring delay queues, use composition
self._all_delayq = DelayQueue(
burst_limit=all_burst_limit,
time_limit_ms=all_time_limit_ms,
exc_route=exc_route,
autostart=autostart)
self._group_delayq = DelayQueue(
burst_limit=group_burst_limit,
time_limit_ms=group_time_limit_ms,
exc_route=exc_route,
autostart=autostart)
def start(self):
'''Method is used to manually start the `MessageQueue` processing
Returns:
None
'''
self._all_delayq.start()
self._group_delayq.start()
def stop(self, timeout=None):
self._group_delayq.stop(timeout=timeout)
self._all_delayq.stop(timeout=timeout)
stop.__doc__ = DelayQueue.stop.__doc__ or '' # reuse docsting if any
def __call__(self, promise, is_group_msg=False):
'''Processes callables in troughput-limiting queues to avoid
hitting limits (specified with \*_burst_limit and *\_time_limit_ms).
Args:
promise (:obj:`callable`): mainly the
:obj:`telegram.utils.promise.Promise` (see Notes for other
callables), that is processed in delay queues
is_group_msg (:obj:`bool`, optional): defines whether `promise`
would be processed in *group*+*all* `DelayQueue`s
(if set to ``True``), or only through *all* `DelayQueue`
(if set to ``False``), resulting in needed delays to avoid
hitting specified limits.
Defaults to ``True``.
Notes:
Method is designed to accept :obj:`telegram.utils.promise.Promise`
as `promise` argument, but other callables could be used too.
For example, lambdas or simple functions could be used to wrap
original func to be called with needed args.
In that case, be sure that either wrapper func does not raise
outside exceptions or the proper `exc_route` handler is provided.
Returns:
:obj:`callable` used as `promise` argument.
'''
if not is_group_msg: # ignore middle group delay
self._all_delayq(promise)
else: # use middle group delay
self._group_delayq(self._all_delayq, promise)
return promise
def queuedmessage(method):
'''A decorator to be used with `telegram.bot.Bot` send* methods.
Note:
As it probably wouldn't be a good idea to make this decorator a
property, it had been coded as decorator function, so it implies that
**first positional argument to wrapped MUST be self**\.
The next object attributes are used by decorator:
Attributes:
self._is_messages_queued_default (:obj:`bool`): Value to provide
class-defaults to `queued` kwarg if not provided during wrapped
method call.
self._msg_queue (:obj:`telegram.ext.messagequeue.MessageQueue`):
The actual `MessageQueue` used to delay outbond messages according
to specified time-limits.
Wrapped method starts accepting the next kwargs:
Args:
queued (:obj:`bool`, optional): if set to ``True``, the `MessageQueue`
is used to process output messages.
Defaults to `self._is_queued_out`.
isgroup (:obj:`bool`, optional): if set to ``True``, the message is
meant to be group-type (as there's no obvious way to determine its
type in other way at the moment). Group-type messages could have
additional processing delay according to limits set in
`self._out_queue`.
Defaults to ``False``.
Returns:
Either :obj:`telegram.utils.promise.Promise` in case call is queued,
or original method's return value if it's not.
'''
@functools.wraps(method)
def wrapped(self, *args, **kwargs):
queued = kwargs.pop('queued', self._is_messages_queued_default)
isgroup = kwargs.pop('isgroup', False)
if queued:
prom = promise.Promise(method, args, kwargs)
return self._msg_queue(prom, isgroup)
return method(self, *args, **kwargs)
return wrapped
+22 -6
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-2016
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -48,7 +48,7 @@ class RegexHandler(Handler):
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
be used to insert updates. Default is ``False``.
be used to insert updates. Default is ``False``.
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
@@ -71,7 +71,10 @@ class RegexHandler(Handler):
pass_update_queue=False,
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
pass_chat_data=False,
allow_edited=False,
message_updates=True,
channel_post_updates=False):
super(RegexHandler, self).__init__(
callback,
pass_update_queue=pass_update_queue,
@@ -85,17 +88,30 @@ class RegexHandler(Handler):
self.pattern = pattern
self.pass_groups = pass_groups
self.pass_groupdict = pass_groupdict
self.allow_edited = allow_edited
self.message_updates = message_updates
self.channel_post_updates = channel_post_updates
def _is_allowed_message(self, update):
return (self.message_updates
and (update.message or (update.edited_message and self.allow_edited)))
def _is_allowed_channel_post(self, update):
return (self.channel_post_updates
and (update.channel_post or (update.edited_channel_post and self.allow_edited)))
def check_update(self, update):
if isinstance(update, Update) and update.message and update.message.text:
match = re.match(self.pattern, update.message.text)
if (isinstance(update, Update)
and (self._is_allowed_message(update) or self._is_allowed_channel_post(update))
and update.effective_message.text):
match = re.match(self.pattern, update.effective_message.text)
return bool(match)
else:
return False
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher, update)
match = re.match(self.pattern, update.message.text)
match = re.match(self.pattern, update.effective_message.text)
if self.pass_groups:
optional_args['groups'] = match.groups()

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