Compare commits

...

193 Commits

Author SHA1 Message Date
Jannes Höke 218e22631c Prepare to release v3.3 2016-02-28 02:33:49 +01:00
Jannes Höke 75c6662f35 leverage timing a bit more 2016-02-28 02:11:48 +01:00
Jannes Höke cd42524fd4 Merge pull request #174 from python-telegram-bot/inlinebots
Implement Inlinebots
2016-02-28 02:10:21 +01:00
Jannes Höke b5875a3abd increase timing for repeatedly failing travis test 2016-02-28 01:53:18 +01:00
Jannes Höke 139cbc657d add inline bot handler example 2016-02-28 01:51:14 +01:00
Jannes Höke 4aca4d1d1f Merge pull request #187 from python-telegram-bot/flaky-tests
Flaky tests
2016-02-28 01:02:11 +01:00
Noam Meltzer 2e6db1330f test_botan.py: another flaky test 2016-02-27 23:39:03 +02:00
Noam Meltzer ccb24d1288 request.py: fix for python2.7 2016-02-27 23:13:29 +02:00
Noam Meltzer 26f87c4873 test_video.py: specify timeout for the sendVideo operation itself 2016-02-27 23:01:22 +02:00
Noam Meltzer fc618274ae bot.py: allow specifying timeout for sendVideo operations 2016-02-27 23:00:33 +02:00
Noam Meltzer d2623d2671 test_voice.py: adapt to latest changes in the API 2016-02-27 22:53:40 +02:00
Noam Meltzer ec8cd37345 test_video.py: adapt to latest changes in the API 2016-02-27 22:50:28 +02:00
Jannes Höke ee56ffc9a5 Merge pull request #182 from tsnoam/master
request: better parsing of URLError exception
2016-02-24 09:51:02 +01:00
Noam Meltzer ca8404adb7 travis.yml: another try to fix nosetests command line 2016-02-24 00:55:30 +02:00
Noam Meltzer d5b35e7e1d unitests: remove docstrings & prints - not needed with nosetests 2016-02-24 00:45:49 +02:00
Noam Meltzer df8e388189 travis.yml: fix nosetests command line 2016-02-23 23:32:52 +02:00
Noam Meltzer a2632b777a travis.yml: enable nosetests --with-flaky 2016-02-22 02:03:10 +02:00
Noam Meltzer e0cf0abb1b flaky tests: try up to 3 times, need to succeed once 2016-02-22 01:52:31 +02:00
Noam Meltzer ec13a36bdd unitests: use home brewed timeout for tests 2016-02-22 01:38:26 +02:00
Jannes Höke d05fa1275a use timed tests and flaky 2016-02-21 12:52:47 +01:00
Jannes Höke e189e8ad05 add -v to nosetests to debug travis tests 2016-02-21 12:08:40 +01:00
Jannes Höke efd10507d0 remove kwargs in favor of named keyword arguments, validate argument types 2016-02-21 11:33:34 +01:00
Noam Meltzer 2338377eda request: better parsing of URLError exception 2016-02-20 15:20:00 +02:00
Jannes Höke ca526fba73 improve unit tests according to comments 2016-02-20 12:14:34 +01:00
Jannes Höke e2cc8db992 Merge branch 'master' into inlinebots 2016-02-20 11:17:34 +01:00
Noam Meltzer 04c86813b3 Merge pull request #177 from tsnoam/master
join() threads for a cleaner stop procedure
2016-02-19 19:10:35 +02:00
Noam Meltzer 7d39e1bdef updater: more unitests for webookhandler 2016-02-19 18:00:57 +02:00
Noam Meltzer d415a60ebf join() threads instead of guessing if they're running
- new book keeping of dispatcher's async threads so they can be joined
   when stopping
 - updater, webhook & dispatcher threads are now kept on
   Updater.__threads so they can be joined at the end

refs #175
2016-02-19 18:00:57 +02:00
Noam Meltzer fd7baa2236 updater.py: use _init_thread for webhook as well 2016-02-19 18:00:57 +02:00
Noam Meltzer 4945d99de6 webhookhandler: better handling of invalid POST data 2016-02-19 18:00:57 +02:00
Noam Meltzer c103c05679 updater.py: fix log of thread name 2016-02-19 18:00:57 +02:00
Jannes Höke 8afea8cd69 Update group link (issue #178) 2016-02-12 08:15:06 +01:00
Noam Meltzer 9d77a3b503 Merge pull request #162 from rahiel/master
legacy echobot: simpler exception handling
2016-02-10 10:44:09 +02:00
Rahiel Kasim 3e978277b3 echobot: simplify exception handling 2016-02-09 15:28:54 +01:00
Rahiel Kasim d02e656700 raise NetworkError for HTTP 502 (Bad Gateway) 2016-02-09 15:26:59 +01:00
Jannes Höke edf4e8abbe fix docstring 2016-02-07 23:34:26 +01:00
Jannes Höke c55c5408b1 tests and corrections for inlinequery 2016-02-07 23:34:15 +01:00
Jannes Höke d1dc32d849 tests and corrections for choseninlineresult 2016-02-07 23:26:38 +01:00
Jannes Höke fc73948c82 fix to_dict tests 2016-02-07 23:25:48 +01:00
Jannes Höke 70dfb88eb5 complete tests for inline results 2016-02-07 22:56:25 +01:00
Jannes Höke 2a01292a64 Merge branch 'master' into inlinebots 2016-02-07 22:42:12 +01:00
Jannes Höke 5b9ec7ff40 catch errors in error handlers 2016-02-07 22:40:55 +01:00
Jannes Höke bec81b5c3b Merge pull request #161 from tsnoam/master
better exception handling
2016-02-07 22:34:49 +01:00
Jannes Höke f5a1e00af9 add info about logging to getting started section 2016-02-07 22:25:16 +01:00
Jannes Höke eac3d8842d remove json import 2016-02-07 15:09:41 +01:00
Jannes Höke 17ac73c3c4 fix message to json 2016-02-07 14:46:36 +01:00
Noam Meltzer 4a5001668d updater/dispatcher: on exception stop all threads 2016-02-07 00:56:30 +02:00
Noam Meltzer 7ebbc60694 Unauthorized is HTTP code 401
I am not certain where the older 403 came from, but for now going to
leave it as is
2016-02-07 00:56:30 +02:00
Noam Meltzer 9c81b810f2 be verbose when running test - easier to follow progress 2016-02-07 00:56:30 +02:00
Noam Meltzer e8363f2440 control name and location of python cmds
this is very useful for setups where python/pylint/etc. are not
installed in default names/paths
2016-02-07 00:56:30 +02:00
Noam Meltzer e82ae432f7 dispatcher is now stopped using a threading.Event 2016-02-07 00:56:30 +02:00
Noam Meltzer d4beb94059 Add specific exceptions types for important errors
maintain backward compatibility with the 'old' exception by inherting
from TelegramError and using the same message
2016-02-06 18:48:55 +02:00
Noam Meltzer 029705e0af updater: catch TelegramError instead of URLError
- URLError is now being caught by telegram.request.
- simplify the code now that we don't need to catch URLError
2016-02-06 18:48:55 +02:00
Noam Meltzer 2859a8873f utils.request: catch URLError in _try_except_req decorator 2016-02-06 18:48:55 +02:00
Jannes Höke e5df2072e4 Merge pull request #171 from tsnoam/disable_audio_unitest
test_audio: temp. disable duration unitest
2016-02-06 17:47:06 +01:00
Noam Meltzer 034d0054ea test_audio: temp. disable duration unitest
due to telegram server bug
2016-02-06 18:34:30 +02:00
Leandro Toledo 0edb2e0a0f Update README.rst
Updating joinchat link
2016-02-03 11:29:50 -02:00
Jannes Höke 2436cab2e5 tests for gif and mpeg4 2016-02-03 02:09:05 +01:00
Leandro Toledo 3e7dc705db Merge pull request #169 from trollknurr/little-readme-extend
Little document extend
2016-02-01 21:22:35 -02:00
Shtarev cabbef8780 how to send photo 2016-02-01 20:14:17 +03:00
Jannes Höke 08836c60ca tests for InlineQueryResultPhoto 2016-01-31 10:52:04 +01:00
Jannes Höke 6d2e159d75 move 'no new updates found' message to debug (#167) 2016-01-31 10:32:34 +01:00
Jannes Höke 8d94c14504 Merge branch 'master' into inlinebots 2016-01-29 23:55:34 +01:00
Jannes Höke 69183d85f7 add test for InlineQueryResultArticle 2016-01-29 23:54:49 +01:00
Jannes Höke 142cc6e6ad add test for dispatching inline queries/results 2016-01-29 23:54:49 +01:00
Jannes Höke d4370e49e5 Merge pull request #164 from jlmadurga/parse_mode-html
Parse mode for HTML added
2016-01-27 11:46:10 +01:00
Juan Madurga df1d639144 Fix doc 2016-01-27 11:28:44 +01:00
Juan Madurga c13af4d61d parse mode for HTML added 2016-01-27 11:20:32 +01:00
Leandro Toledo 516d41eac0 Merge pull request #163 from jlmadurga/replymarkup-tests
Add tests for replymarkup objects
2016-01-26 21:11:03 -02:00
Juan Madurga 702544f709 fix pep8 extra lines 2016-01-26 23:58:07 +01:00
Juan Madurga 713f0d1335 add tests for replymarkup objects 2016-01-26 23:43:16 +01:00
Jannes Hoeke a383cee558 merge master into inlinebots 2016-01-25 18:05:27 +01:00
Jannes Höke 44645d2cc6 Merge pull request #157 from jlmadurga/context-for-handlers
Add context to dispatcher
2016-01-25 17:46:14 +01:00
Jannes Höke 77f00e386c Merge pull request #159 from ollmer/botan_integration
Botan integration
2016-01-25 17:18:12 +01:00
Oleg Shlyazhko 1000a56e0d more test vor botan module, increase coverage 2016-01-24 18:32:12 +03:00
Oleg Shlyazhko c3bca9af48 remove checking for non 200 http codes, exceptions already handle it 2016-01-24 18:28:27 +03:00
Oleg Shlyazhko 9eec34edd1 pep8 fix 2016-01-23 20:13:48 +03:00
Oleg Shlyazhko 383a2d3742 fix httperror error output 2016-01-23 20:03:29 +03:00
Oleg Shlyazhko d1e4eeaf30 fix urlopen data format 2016-01-23 19:49:55 +03:00
Oleg Shlyazhko 80b53f1ab7 fix urlopen call 2016-01-23 19:44:40 +03:00
Oleg Shlyazhko 0faa38b8af fix httperror logging 2016-01-23 19:29:01 +03:00
Oleg Shlyazhko 5a2a541ae0 rename botan test correctly and move token to ENV 2016-01-23 19:09:56 +03:00
Oleg Shlyazhko 7508c2f8bf add detailed comment in class and src key to tracking url 2016-01-23 16:59:53 +03:00
Oleg Shlyazhko bb2cce56a3 insert correct appmetrica token instead of bot token in botan test 2016-01-23 16:58:44 +03:00
Oleg Shlyazhko 2c12bd6775 replace print with logging 2016-01-23 16:58:01 +03:00
Oleg Shlyazhko 5b05bcc8f2 add http error exception handling 2016-01-23 15:21:13 +03:00
Oleg Shlyazhko adb446abf6 fix pep8 styling 2016-01-23 15:14:17 +03:00
Oleg Shlyazhko 545767ea40 add myself to authors 2016-01-23 14:25:09 +03:00
Oleg Shlyazhko 7931045bf3 test for botan analytics module 2016-01-23 14:24:49 +03:00
Oleg Shlyazhko 17c2857622 add module for botan analytics 2016-01-23 14:24:34 +03:00
Juan Madurga cbcbfab45f fix pep8 2016-01-22 16:42:39 +01:00
Juan Madurga 1dc9907a70 fix code comments by jh0ker 2016-01-22 16:30:55 +01:00
Juan Madurga b8c9992eff Merge branch 'master' into context-for-handlers 2016-01-22 09:46:05 +01:00
Jannes Höke fe26c94440 Merge pull request #155 from tsnoam/master
_try_except_req(): catch httplib.HTTPException
2016-01-22 02:48:11 +01:00
Juan Madurga a6650de93d Add context parameter to dispatcher. Handlers now can updates with a context data if required 2016-01-20 19:56:41 +01:00
Noam Meltzer dc032b349c _try_except_req(): catch httplib.HTTPException
fixes #153
2016-01-19 22:02:34 +02:00
Jannes Höke 5c2458bfd8 merge master into inlinebots 2016-01-14 15:28:22 +01:00
Jannes Höke 6166e7f07a Merge pull request #152 from python-telegram-bot/docstring_pep257_checker
Add docstring style checker and fixing some
2016-01-14 15:24:44 +01:00
Leandro Toledo 2a7b03e7c2 Fix travis - file_size is irrelevant for resend test 2016-01-13 19:15:11 -02:00
Jannes Höke da97c1741c merge 'urls' into 'inline' 2016-01-13 21:58:45 +01:00
Jannes Höke 70445dc054 Merge pull request #151 from python-telegram-bot/urls
enable sending by URL for all sendX methods
2016-01-13 21:49:04 +01:00
leandrotoledo d3a02405b2 pep8 2016-01-13 14:23:15 -02:00
leandrotoledo dfc75cc64e Add docstring style checker and fixing some 2016-01-13 14:09:35 -02:00
Jannes Höke 332667c98b flake8 2016-01-12 13:16:43 +01:00
Jannes Höke f71c40e0b8 use basename and check if filename has extension 2016-01-12 12:42:07 +01:00
Leandro Toledo 9f55c15cc7 Add sendPhoto tests, fix file_id casting to str instead unicode 2016-01-11 22:25:46 -02:00
Leandro Toledo a32ae8b895 Merge branch 'master' into urls 2016-01-11 22:12:06 -02:00
Leandro Toledo a539a0c3a5 Add data object for sendPhoto tests 2016-01-11 22:11:29 -02:00
Leandro Toledo b4abf75524 Fix data objects references for tests to raw files on Githup repo 2016-01-11 21:11:10 -02:00
Leandro Toledo 12d71d5a91 Fix file_size tests for send* by URL 2016-01-11 20:33:17 -02:00
Leandro Toledo 48bf6bfab0 Adding telegram.gif for testing purposes 2016-01-11 20:20:57 -02:00
Leandro Toledo 7227ca3aab Tests for send(Audio,Voice,Video) by URL 2016-01-11 20:19:55 -02:00
Jannes Höke 307d2621d7 removed support-restriction for sending by url 2016-01-10 22:08:01 +01:00
Jannes Höke 831a0a31e5 flake8 2016-01-10 15:20:05 +01:00
Jannes Höke d10b657521 enable sending by URL for all sendX methods 2016-01-10 15:12:42 +01:00
Jannes Höke ef1012b722 enable sending by URL for all sendX methods 2016-01-10 15:11:17 +01:00
Jannes Höke 5fd7a4fe0d release v3.3b1 2016-01-09 15:50:19 +01:00
Jannes Höke 85d20ba549 Merge branch 'master' into inlinebots 2016-01-09 15:44:18 +01:00
Jannes Höke 48c1673d7c fix additional args for inline queries 2016-01-09 15:30:47 +01:00
Jannes Höke af6eb457c1 Merge branch 'inlinebots' of github.com:python-telegram-bot/python-telegram-bot into inlinebots 2016-01-09 15:12:34 +01:00
Jannes Höke 6ece89bc56 update inline query results after api change 2016-01-09 15:09:48 +01:00
Jannes Höke c69cdfd184 Initial commit for inline bot support 2016-01-09 15:09:48 +01:00
Jannes Höke 6dab7068c3 Add Telegram Group link as badge 2016-01-09 14:57:46 +01:00
Jannes Höke fd90c16265 merge master into inlinebots 2016-01-09 14:51:24 +01:00
Jannes Höke 77ca036d02 update inline query results after api change 2016-01-09 14:48:27 +01:00
Jannes Höke e2e98e86db Initial commit for inline bot support 2016-01-09 14:48:27 +01:00
Leandro Toledo 71f82d57d9 Update CONTRIBUTING.rst 2016-01-06 17:09:19 -02:00
Jannes Höke 6a3d514ed1 Merge branch 'master' into inlinebots 2016-01-06 16:28:17 +01:00
Leandro Toledo 6aa2e510a3 Merge pull request #149 from python-telegram-bot/contribute-update
Expanding more subjects on CONTRIBUTING.rst
2016-01-06 13:04:59 -02:00
leandrotoledo 099deabf4e Removing if block to install unittest2 when running tests on Py2.6 2016-01-06 12:50:42 -02:00
Jannes Höke b78ceeed10 fix race condition in dispatcher 2016-01-06 15:36:14 +01:00
leandrotoledo a8240dca1f Adding unittest2, update travis to use requirements-dev.txt 2016-01-06 12:12:01 -02:00
leandrotoledo 7e207433cc Using flake8 instead pep8 2016-01-06 12:05:09 -02:00
leandrotoledo fc6d0f4f07 Expanding more subjects 2016-01-06 11:54:22 -02:00
Jannes Höke 4fef5bcceb Merge branch 'master' into inlinebots 2016-01-05 15:17:38 +01:00
Jannes Höke fc64cc0a4a Merge pull request #148 from mr-karan/patch-1
handle case of negative values of seconds
2016-01-05 14:37:26 +01:00
Karan Sharma 14a9962296 handle case of negative values of seconds 2016-01-05 19:06:28 +05:30
Jannes Höke 4197400853 remove token once again 2016-01-05 14:29:20 +01:00
Jannes Höke aa8966832b release 3.2 and update copyright notice to 2015-2016 2016-01-05 14:12:03 +01:00
Jannes Höke e27427676d fix jobqueue creation 2016-01-05 13:40:07 +01:00
Jannes Höke 8175fd7b53 update timerbot example 2016-01-05 13:39:51 +01:00
Jannes Höke 2875bae881 Integrate JobQueue with Updater 2016-01-05 13:32:19 +01:00
Jannes Höke c756aa1c02 remove error raising in dispatchType 2016-01-05 12:49:48 +01:00
Jannes Höke 6d5c3187fc Initial commit for inline bot support 2016-01-04 17:31:06 +01:00
Jannes Höke c2f04331f3 Merge pull request #145 from python-telegram-bot/jobqueue
JobQueue by @franciscod
2016-01-04 12:34:29 +01:00
Jannes Höke 5293ec49ff Add section about JobQueue to README 2016-01-04 11:34:47 +01:00
Jannes Höke 9319903343 rename testclass 2016-01-04 11:08:43 +01:00
Jannes Höke 95e2aad9bd fix sphinx build 2016-01-04 02:36:27 +01:00
Jannes Höke 73f4f26da5 more tests 2016-01-04 02:36:15 +01:00
Jannes Höke 24b2bf335c Increase time for travis testing 2016-01-04 02:12:24 +01:00
Jannes Höke fc3fa0f515 Increase time for travis testing 2016-01-04 02:11:42 +01:00
Jannes Höke 87657bcd70 flake8 and string formatting 2016-01-04 02:05:39 +01:00
Jannes Höke 81b58c7d00 remove bot token (once again...) 2016-01-04 01:58:50 +01:00
Jannes Höke dd7d1255d1 fixes, tests and examples for job queue 2016-01-04 01:56:22 +01:00
Jannes Höke f813d4f5ec improve docstring 2016-01-04 01:32:47 +01:00
Jannes Höke 623863cee1 fix exception logging 2016-01-04 01:32:00 +01:00
Jannes Höke aa125e59fb Merge branch 'master' into jobqueue 2016-01-04 00:01:27 +01:00
Jannes Höke 68c844df0c implement starting jobqueue in thread and fix errors 2016-01-04 00:01:00 +01:00
Jannes Höke 3779d454f3 Merge pull request #136 from tsnoam/master
consistent TelegramError exceptions
2016-01-03 23:58:14 +01:00
Noam Meltzer 5d168bd479 improve unitest coverage 2016-01-04 00:34:39 +02:00
Noam Meltzer 1c8bb21790 fix python 2.6 unitests which with the backport package unittest2 2016-01-04 00:32:17 +02:00
Noam Meltzer 054c976ad6 add tsnoam to AUTHORS 2016-01-04 00:32:17 +02:00
Noam Meltzer bc2679f2ae utils.request: provide HTTP error code with unknown errors 2016-01-04 00:32:17 +02:00
Noam Meltzer 17dae68654 gitignore: files generated by unitests 2016-01-04 00:32:17 +02:00
Noam Meltzer 7734c1d6a6 fix unitests to work with new token validator 2016-01-04 00:32:13 +02:00
Noam Meltzer aeca3ca174 basic token validation
refs #134
2016-01-04 00:31:40 +02:00
Noam Meltzer 21fdaa4ff7 utils.request: decorate functions with general try... except...
refs #134
2016-01-04 00:31:40 +02:00
Noam Meltzer 79f29c4b9e properly handle utf-8 server response & identify json parsing errors
refs #134
2016-01-04 00:31:40 +02:00
Noam Meltzer a2d8ca3663 some errors are prefixed with '[Error]: ' - strip that down
use custom _lstrip_str() func instead of regex. a little performance
improvment and (IMO) a bit more readable.
2016-01-04 00:31:40 +02:00
Jannes Höke 6f76bfc2de Merge branch 'master' into jobqueue 2015-12-31 15:45:19 +01:00
Jannes Höke 97ebebefc1 Merge branch 'Tengah-master' 2015-12-31 15:41:53 +01:00
Jannes Höke 119743433d merge Tengah-master into master 2015-12-31 15:41:37 +01:00
Bob Pasker b0ad07f30f remove traceback import 2015-12-31 09:29:02 -05:00
Jannes Höke 114cb787ea Merge branch 'master' into jobqueue 2015-12-31 15:22:08 +01:00
Jannes Höke 5d1ef673a3 flake8 2015-12-31 15:04:42 +01:00
Jannes Höke 3e1cb08567 flake8 2015-12-31 15:03:40 +01:00
Jannes Höke 29a4062945 properly lock updater and dispatcher start/stop methods 2015-12-31 14:58:01 +01:00
Jannes Höke 2ff6fe9901 initial commit for JobQueue by @franciscod 2015-12-31 14:56:48 +01:00
Jannes Höke 34b91f5062 properly lock updater and dispatcher start/stop methods 2015-12-31 14:52:28 +01:00
Bob Pasker cc6c4a6eb9 Change a print statement to use logger instead 2015-12-31 08:33:42 -05:00
Jannes Höke fc9456e1a8 release v3.1.2 2015-12-29 14:46:00 +01:00
Jannes Höke 41ba57f064 don't stop the dispatcher thread on uncaught errors 2015-12-29 14:28:39 +01:00
Jannes Höke b205751aee change default value for poll_interval to 0 2015-12-29 14:28:39 +01:00
Jannes Höke c4f13bd1bb Merge pull request #138 from Gems/master
Fix file.download with custom_path
2015-12-28 12:27:26 +01:00
Eduard Gomolyako 7d7397317e Fix file.download with custom_path 2015-12-27 04:01:13 +03:00
Jannes Höke 631954e5d9 Merge pull request #133 from rahiel/master
dedicate examples to the public domain (CC0)
2015-12-25 17:56:07 +01:00
Rahiel Kasim 90ed128a61 dedicate examples to the public domain (CC0) 2015-12-25 15:19:17 +01:00
Jannes Höke 2a2b679362 improved and renamed examples 2015-12-22 13:23:59 +01:00
Jannes Höke f75e329225 fix docstring 2015-12-21 21:40:41 +01:00
Jannes Höke ea7c54ba07 Release v3.1.1, change author email everywhere 2015-12-21 21:18:53 +01:00
Jannes Höke 3c7b037eaa add additional arguments 'groups' and 'groupdict' for regex handlers 2015-12-21 20:25:31 +01:00
Jannes Höke c15a1bc151 fix for #123 2015-12-21 19:36:17 +01:00
Jannes Höke 9e7314134e fix docstring 2015-12-17 14:55:20 +01:00
88 changed files with 4202 additions and 959 deletions
+7
View File
@@ -61,3 +61,10 @@ target/
# Sublime Text 2
*.sublime*
# unitests files
telegram.mp3
telegram.mp4
telegram.ogg
telegram.png
telegram.webp
+3 -2
View File
@@ -7,10 +7,11 @@ python:
- "pypy"
- "pypy3"
install:
- pip install pylint flake8 coveralls
- pip install coveralls
- pip install -r requirements.txt
- pip install -r requirements-dev.txt
script:
- nosetests --with-coverage --cover-package telegram/
- nosetests -v --with-flaky --no-flaky-report --with-coverage --cover-package=telegram/
- flake8 telegram
- 'if [[ $TRAVIS_PYTHON_VERSION != 2.6 ]]; then pylint -E telegram --disable=no-name-in-module,import-error; fi'
after_success:
+3
View File
@@ -16,9 +16,12 @@ The following wonderful people contributed directly or indirectly to this projec
- `JASON0916 <https://github.com/JASON0916>`_
- `jh0ker <https://github.com/jh0ker>`_
- `JRoot3D <https://github.com/JRoot3D>`_
- `jlmadurga <https://github.com/jlmadurga>`_
- `macrojames <https://github.com/macrojames>`_
- `naveenvhegde <https://github.com/naveenvhegde>`_
- `njittam <https://github.com/njittam>`_
- `Noam Meltzer <https://github.com/tsnoam>`_
- `Oleg Shlyazhko <https://github.com/ollmer>`_
- `Rahiel Kasim <https://github.com/rahiel>`_
- `sooyhwang <https://github.com/sooyhwang>`_
- `wjt <https://github.com/wjt>`_
+42
View File
@@ -1,3 +1,45 @@
**2016-02-28**
*Released 3.3*
- Inline bots
- Send any file by URL
- Specialized exceptions: ``Unauthorized``, ``InvalidToken``, ``NetworkError`` and ``TimedOut``
- Integration for botan.io (thanks to @ollmer)
- HTML Parsemode (thanks to @jlmadurga)
- Bugfixes and under-the-hood improvements
**Very special thanks to Noam Meltzer (@tsnoam) for all of his work!**
**2016-01-09**
*Released 3.3b1*
- Implement inline bots (beta)
**2016-01-05**
*Released 3.2.0*
- Introducing ``JobQueue`` (original author: @franciscod)
- Streamlining all exceptions to ``TelegramError`` (Special thanks to @tsnoam)
- Proper locking of ``Updater`` and ``Dispatcher`` ``start`` and ``stop`` methods
- Small bugfixes
**2015-12-29**
*Released 3.1.2*
- Fix custom path for file downloads
- Don't stop the dispatcher thread on uncaught errors in handlers
**2015-12-21**
*Released 3.1.1*
- Fix a bug where asynchronous handlers could not have additional arguments
- Add ``groups`` and ``groupdict`` as additional arguments for regex-based handlers
**2015-12-16**
*Released 3.1.0*
+115 -24
View File
@@ -1,39 +1,130 @@
How To Contribute
=================
Every open source project lives from the generous help by contributors that sacrifice their time and ``python-telegram-bot`` is no different.
Every open source project lives from the generous help by contributors that sacrifice their time and ``python-telegram-bot`` is no different. To make participation as pleasant as possible, this project adheres to the `Code of Conduct`_ by the Python Software Foundation.
To make participation as pleasant as possible, this project adheres to the `Code of Conduct`_ by the Python Software Foundation.
Setting things up
-----------------
Here are a few guidelines to get you started:
1. Fork the ``python-telegram-bot`` repository to your GitHub account.
- Add yourself to the AUTHORS.rst_ file in an alphabetical fashion.
Every contribution is valuable and shall be credited.
- If your change is noteworthy, add an entry to the CHANGES_.
- No contribution is too small; please submit as many fixes for typos and grammar bloopers as you can!
- Dont break backward compatibility.
- *Always* add tests and docs for your code.
This is a hard rule; patches with missing tests or documentation wont be merged.
If a feature is not tested or documented, it doesnt exist.
- Obey `PEP 8`_ and `PEP 257`_.
- Follow `Google Python Style Guide`_ and `Google Python Style Docstrings`_.
- Write `good commit messages`_.
2. Clone your forked repository of ``python-telegram-bot`` to your computer:
.. note::
If you have something great but arent sure whether it adheres -- or even can adhere -- to the rules above: **please submit a pull request anyway**!
``$ git clone https://github.com/<your username>/python-telegram-bot``
In the best case, we can mold it into something, in the worst case the pull request gets politely closed.
Theres absolutely nothing to fear.
``$ cd python-telegram-bot``
Thank you for considering to contribute to ``python-telegram-bot``!
If you have any question or concerns, feel free to reach out to me.
3. Add a track to the original repository:
``$ git remote add upstream https://github.com/python-telegram-bot/python-telegram-bot``
4. Install dependencies:
``$ pip install -r requirements.txt``
``$ pip install -r requirements-dev.txt``
5. In order to run tests you need to set the following environment variables:
``$ export CHAT_ID=your-chat-id``
``$ export TOKEN=your-bot-token``
Finding something to do
-----------------------
If you already know what you'd like to work on, you can skip this section.
If you have an idea for something to do, first check if it's already been filed on the `issue tracker`_. If so, add a comment to the issue saying you'd like to work on it, and we'll help you get started! Otherwise, please file a new issue and assign yourself to it.
Another great way to start contributing is by writing tests. Tests are really important because they help prevent developers from accidentally breaking existing code, allowing them to build cool things faster. If you're interested in helping out, let the development team know by posting to the `developers' mailing list`_, and we'll help you get started.
Instructions for making a code change
-------------------------------------
The central development branch is ``master``, which should be clean and ready for release at any time. In general, all changes should be done as feature branches based off of ``master``.
Here's how to make a one-off code change.
1. **Choose a descriptive branch name.** It should be lowercase, hyphen-separated, and a noun describing the change (so, ``fuzzy-rules``, but not ``implement-fuzzy-rules``). Also, it shouldn't start with ``hotfix`` or ``release``.
2. **Create a new branch with this name, starting from** ``master``. In other words, run:
``$ git fetch upstream``
``$ git checkout master``
``$ git checkout merge upstream/master``
``$ git checkout -b your-branch-name``
3. **Make a commit to your feature branch**. Each commit should be self-contained and have a descriptive commit message that helps other developers understand why the changes were made.
- You can refer to relevant issues in the commit message by writing, e.g., "#105".
- For consistency, please conform to `Google Python Style Guide`_ and `Google Python Style Docstrings`_. In addition, code should be formatted consistently with other code around it.
- Please ensure that the code you write is well-tested.
- Dont break backward compatibility.
- Add yourself to the AUTHORS.rst_ file in an alphabetical fashion.
- Before making a commit ensure that all automated tests still pass:
``$ make test``
- To actually make the commit and push it to your GitHub fork, run:
``$ git commit -a -m "your-commit-message-here"``
``$ git push origin your-branch-name``
4. **When your feature is ready to merge, create a pull request.**
- Go to your fork on GitHub, select your branch from the dropdown menu, and click "New pull request".
- Add a descriptive comment explaining the purpose of the branch (e.g. "Add the new API feature to create inline bot queries."). This will tell the reviewer what the purpose of the branch is.
- Click "Create pull request". An admin will assign a reviewer to your commit.
5. **Address review comments until all reviewers give LGTM ('looks good to me').**
- When your reviewer has reviewed the code, you'll get an email. You'll need to respond in two ways:
- Make a new commit addressing the comments you agree with, and push it to the same branch. Ideally, the commit message would explain what the commit does (e.g. "Fix lint error"), but if there are lots of disparate review comments, it's fine to refer to the original commit message and add something like "(address review comments)".
- In addition, please reply to each comment. Each reply should be either "Done" or a response explaining why the corresponding suggestion wasn't implemented. All comments must be resolved before LGTM can be given.
- Resolve any merge conflicts that arise. To resolve conflicts between 'your-branch-name' (in your fork) and 'master' (in the ``python-telegram-bot`` repository), run:
``$ git checkout your-branch-name``
``$ git fetch upstream``
``$ git merge upstream/master``
``$ ...[fix the conflicts]...``
``$ ...[make sure the tests pass before committing]...``
``$ git commit -a``
``$ git push origin your-branch-name``
- At the end, the reviewer will merge the pull request.
6. **Tidy up!** Delete the feature branch from your both your local clone and the GitHub repository:
``$ git branch -D your-branch-name``
``$ git push origin --delete your-branch-name``
7. **Celebrate.** Congratulations, you have contributed to ``python-telegram-bot``!
.. _`PEP 8`: https://www.python.org/dev/peps/pep-0008/
.. _`PEP 257`: https://www.python.org/dev/peps/pep-0257/
.. _`good commit messages`: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
.. _`Code of Conduct`: https://www.python.org/psf/codeofconduct/
.. _`issue tracker`: https://github.com/python-telegram-bot/python-telegram-bot/issues
.. _`developers' mailing list`: mailto:devs@python-telegram-bot.org
.. _`Google Python Style Guide`: https://google-styleguide.googlecode.com/svn/trunk/pyguide.html
.. _`Google Python Style Docstrings`: http://sphinx-doc.org/latest/ext/example_google.html
.. _CHANGES: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/CHANGES
.. _AUTHORS.rst: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/AUTHORS.rst
+21 -4
View File
@@ -1,5 +1,11 @@
.PHONY: clean pep8 lint test install
PYLINT := pylint
NOSETESTS := nosetests
PEP257 := pep257
PEP8 := flake8
PIP := pip
clean:
rm -fr build
rm -fr dist
@@ -7,21 +13,32 @@ clean:
find . -name '*.pyo' -exec rm -f {} \;
find . -name '*~' -exec rm -f {} \;
pep257:
$(PEP257) telegram
pep8:
flake8 telegram
$(PEP8) telegram
lint:
pylint -E telegram
$(PYLINT) -E telegram --disable=no-name-in-module,import-error
test:
nosetests
$(NOSETESTS) -v
install:
pip install -r requirements.txt
$(PIP) install -r requirements.txt
help:
@echo "Available targets:"
@echo "- clean Clean up the source directory"
@echo "- pep257 Check docstring style with pep257"
@echo "- pep8 Check style with flake8"
@echo "- lint Check style with pylint"
@echo "- test Run tests"
@echo
@echo "Available variables:"
@echo "- PYLINT default: $(PYLINT)"
@echo "- NOSETESTS default: $(NOSETESTS)"
@echo "- PEP257 default: $(PEP257)"
@echo "- PEP8 default: $(PEP8)"
@echo "- PIP default: $(PIP)"
+97 -15
View File
@@ -35,6 +35,10 @@ A Python wrapper around the Telegram Bot API.
:target: https://coveralls.io/github/python-telegram-bot/python-telegram-bot?branch=master
:alt: Coveralls
.. image:: https://img.shields.io/badge/Telegram-Group-blue.svg
:target: https://telegram.me/joinchat/ALnA-AJQm5TcNEiy2G_4cQ
:alt: Telegram Group
=================
Table of contents
=================
@@ -54,14 +58,16 @@ Table of contents
- `Getting started`_
1. `The Updater class`_
2. `API`_
3. `Logging`_
4. `Examples`_
3. `JobQueue`_
5. `Documentation`_
4. `Logging`_
5. `Examples`_
6. `Documentation`_
- `License`_
@@ -141,6 +147,10 @@ Check out the latest development version anonymously with::
$ git clone https://github.com/python-telegram-bot/python-telegram-bot
$ cd python-telegram-bot
Install dependencies:
$ pip install -r requirements.txt
Run tests:
$ make test
@@ -155,9 +165,17 @@ _`Getting started`
View the last release API documentation at: https://core.telegram.org/bots/api
------
This library uses the `logging` module. To set up logging to standard output, put::
import logging
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
at the beginning of your script.
--------------------
_`The Updater class`
------
--------------------
The ``Updater`` class is the new way to create bots with ``python-telegram-bot``. It provides an easy-to-use interface to the ``telegram.Bot`` by caring about getting new updates from telegram and forwarding them to the ``Dispatcher`` class. We can register handler functions in the ``Dispatcher`` to make our bot react to Telegram commands, messages and even arbitrary updates.
@@ -180,7 +198,7 @@ Now, we need to define a function that should process a specific type of update:
We want this function to be called on a Telegram message that contains the ``/start`` command, so we need to register it in the dispatcher::
>>> dispatcher.addTelegramCommandHandler('start', start)
The last step is to tell the ``Updater`` to start working::
>>> updater.start_polling()
@@ -206,9 +224,23 @@ Let's add some functionality to our bot. We want to add the ``/caps`` command, t
>>> def caps(bot, update, args):
... text_caps = ' '.join(args).upper()
... bot.sendMessage(chat_id=update.message.chat_id, text=text_caps)
...
...
>>> dispatcher.addTelegramCommandHandler('caps', caps)
To enable our bot to respond to inline queries, we can add the following (you will also have to talk to BotFather)::
>>> from telegram import InlineQueryResultArticle
>>> def inline_caps(bot, update):
... # If you activated inline feedback, updates might either contain
... # inline_query or chosen_inline_result, the other one will be None
... if update.inline_query:
... query = bot.update.inline_query.query
... results = list()
... results.append(InlineQueryResultArticle(query.upper(), 'Caps', text_caps))
... bot.answerInlineQuery(update.inline_query.id, results)
...
>>> dispatcher.addTelegramInlineHandler(inline_caps)
Now it's time to stop the bot::
>>> updater.stop()
@@ -261,15 +293,23 @@ To post a text message with markdown::
>>> bot.sendMessage(chat_id=chat_id, text="*bold* _italic_ [link](http://google.com).", parse_mode=telegram.ParseMode.MARKDOWN)
To post a text message with Html style::
>>> bot.sendMessage(chat_id=chat_id, text="<b>bold</b> <i>italic</i> <a href="http://google.com">link</a>.", parse_mode=telegram.ParseMode.HTML)
To post an Emoji (special thanks to `Tim Whitlock <http://apps.timwhitlock.info/emoji/tables/unicode>`_)::
>>> bot.sendMessage(chat_id=chat_id, text=telegram.Emoji.PILE_OF_POO)
To post an image file via URL (right now only sendPhoto supports this)::
To post an image file via URL::
>>> bot.sendPhoto(chat_id=chat_id, photo='https://telegram.org/img/t_logo.png')
To post a voice file::
To post an image file from disk::
>>> bot.sendPhoto(chat_id=chat_id, photo=open('tests/test.png', 'rb'))
To post a voice file from disk::
>>> bot.sendVoice(chat_id=chat_id, voice=open('tests/telegram.ogg', 'rb'))
@@ -299,9 +339,39 @@ There are many more API methods, to read the full API documentation::
$ pydoc telegram.Bot
-----------
_`Logging`
_`JobQueue`
-----------
The ``JobQueue`` allows you to perform tasks with a delay or even periodically. The ``Updater`` will create one for you::
>>> from telegram import Updater
>>> u = Updater('TOKEN')
>>> j = u.job_queue
The job queue uses functions for tasks, so we define one and add it to the queue. Usually, when the first job is added to the queue, it wil start automatically. We can prevent this by setting ``prevent_autostart=True``::
>>> def job1(bot):
... bot.sendMessage(chat_id='@examplechannel', text='One message every minute')
>>> j.put(job1, 60, next_t=0, prevent_autostart=True)
You can also have a job that will not be executed repeatedly::
>>> def job2(bot):
... bot.sendMessage(chat_id='@examplechannel', text='A single message with 30s delay')
>>> j.put(job2, 30, repeat=False)
Now, because we didn't prevent the auto start this time, the queue will start ticking. It runs in a seperate thread, so it is non-blocking. When we stop the Updater, the related queue will be stopped as well::
>>> u.stop()
We can also stop the job queue by itself::
>>> j.stop()
----------
_`Logging`
----------
You can get logs in your main application by calling `logging` and setting the log level you want::
>>> import logging
@@ -318,14 +388,26 @@ _`Examples`
Here follows some examples to help you to get your own Bot up to speed:
- `echobot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot.py>`_ replies back messages.
- `echobot2 <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot2.py>`_ replies back messages.
- `roboed <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/roboed.py>`_ talks to `Robô Ed <http://www.ed.conpet.gov.br/br/converse.php>`_.
- `clibot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/clibot.py>`_ has a command line interface.
- `timerbot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/timerbot.py>`_ uses the ``JobQueue`` to send timed messages.
- `Welcome Bot <https://github.com/jh0ker/welcomebot>`_ greets everyone who joins a group chat.
Legacy examples (pre-3.0):
- `echobot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/legacy/echobot.py>`_ replies back messages.
- `roboed <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/legacy/roboed.py>`_ talks to `Robô Ed <http://www.ed.conpet.gov.br/br/converse.php>`_.
- `Simple-Echo-Telegram-Bot <https://github.com/sooyhwang/Simple-Echo-Telegram-Bot>`_ simple Python Telegram bot that echoes your input with Flask microframework, setWebhook method, and Google App Engine (optional) - by @sooyhwang.
- `DevOps Reaction Bot <https://github.com/leandrotoledo/gae-devops-reaction-telegram-bot>`_ sends latest or random posts from `DevOps Reaction <http://devopsreactions.tumblr.com/>`_. Running on `Google App Engine <https://cloud.google.com/appengine>`_ (billing has to be enabled for fully Socket API support).
Other notable examples:
- `TwitterForwarderBot <https://github.com/franciscod/telegram-twitter-forwarder-bot>`_ forwards you tweets from people that you have subscribed to.
================
@@ -344,7 +426,7 @@ You may copy, distribute and modify the software provided that modifications are
_`Contact`
==========
Feel free to join to our `Telegram group <https://telegram.me/joinchat/ALnA-AJQm5R7Km9hdCgyng>`_.
Feel free to join to our `Telegram group <https://telegram.me/joinchat/ALnA-AJQm5TcNEiy2G_4cQ>`_.
=======
_`TODO`
+3 -3
View File
@@ -50,7 +50,7 @@ master_doc = 'index'
# General information about the project.
project = u'Python Telegram Bot'
copyright = u'2015, Leandro Toledo'
copyright = u'2015-2016, Leandro Toledo'
author = u'Leandro Toledo'
# The version info for the project you're documenting, acts as replacement for
@@ -58,9 +58,9 @@ author = u'Leandro Toledo'
# built documents.
#
# The short X.Y version.
version = '3.1'
version = '3.3'
# The full version, including alpha/beta/rc tags.
release = '3.1.0'
release = '3.3.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -0,0 +1,7 @@
telegram.choseninlineresult module
==================================
.. automodule:: telegram.choseninlineresult
:members:
:undoc-members:
:show-inheritance:
+7
View File
@@ -0,0 +1,7 @@
telegram.inlinequery module
===========================
.. automodule:: telegram.inlinequery
:members:
:undoc-members:
:show-inheritance:
@@ -0,0 +1,7 @@
telegram.inlinequeryresult module
=================================
.. automodule:: telegram.inlinequeryresult
:members:
:undoc-members:
:show-inheritance:
+7
View File
@@ -0,0 +1,7 @@
telegram.jobqueue module
========================
.. automodule:: telegram.jobqueue
:members:
:undoc-members:
:show-inheritance:
+4
View File
@@ -11,6 +11,10 @@ Submodules
telegram.bot
telegram.updater
telegram.dispatcher
telegram.jobqueue
telegram.inlinequery
telegram.inlinequeryresult
telegram.choseninlineresult
telegram.chataction
telegram.contact
telegram.document
+116
View File
@@ -0,0 +1,116 @@
CC0 1.0 Universal
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator and
subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific
works ("Commons") that the public can reliably and without fear of later
claims of infringement build upon, modify, incorporate in other works, reuse
and redistribute as freely as possible in any form whatsoever and for any
purposes, including without limitation commercial purposes. These owners may
contribute to the Commons to promote the ideal of a free culture and the
further production of creative, cultural and scientific works, or to gain
reputation or greater distribution for their Work in part through the use and
efforts of others.
For these and/or other purposes and motivations, and without any expectation
of additional consideration or compensation, the person associating CC0 with a
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
and publicly distribute the Work under its terms, with knowledge of his or her
Copyright and Related Rights in the Work and the meaning and intended legal
effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not limited
to, the following:
i. the right to reproduce, adapt, distribute, perform, display, communicate,
and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or likeness
depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data in
a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation thereof,
including any amended or successor version of such directive); and
vii. other similar, equivalent or corresponding rights throughout the world
based on applicable law or treaty, and any national implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
and Related Rights and associated claims and causes of action, whether now
known or unknown (including existing as well as future claims and causes of
action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time
extensions), (iii) in any current or future medium and for any number of
copies, and (iv) for any purpose whatsoever, including without limitation
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
the Waiver for the benefit of each member of the public at large and to the
detriment of Affirmer's heirs and successors, fully intending that such Waiver
shall not be subject to revocation, rescission, cancellation, termination, or
any other legal or equitable action to disrupt the quiet enjoyment of the Work
by the public as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
is so judged Affirmer hereby grants to each affected person a royalty-free,
non transferable, non sublicensable, non exclusive, irrevocable and
unconditional license to exercise Affirmer's Copyright and Related Rights in
the Work (i) in all territories worldwide, (ii) for the maximum duration
provided by applicable law or treaty (including future time extensions), (iii)
in any current or future medium and for any number of copies, and (iv) for any
purpose whatsoever, including without limitation commercial, advertising or
promotional purposes (the "License"). The License shall be deemed effective as
of the date CC0 was applied by Affirmer to the Work. Should any part of the
License for any reason be judged legally invalid or ineffective under
applicable law, such partial invalidity or ineffectiveness shall not
invalidate the remainder of the License, and in such case Affirmer hereby
affirms that he or she will not (i) exercise any of his or her remaining
Copyright and Related Rights in the Work or (ii) assert any associated claims
and causes of action with respect to the Work, in either case contrary to
Affirmer's express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or warranties
of any kind concerning the Work, express, implied, statutory or otherwise,
including without limitation warranties of title, merchantability, fitness
for a particular purpose, non infringement, or the absence of latent or
other defects, accuracy, or the present or absence of errors, whether or not
discoverable, all to the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without limitation
any person's Copyright and Related Rights in the Work. Further, Affirmer
disclaims responsibility for obtaining any necessary consents, permissions
or other rights required for any use of the Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to this
CC0 or use of the Work.
For more information, please see
<http://creativecommons.org/publicdomain/zero/1.0/>
+41 -29
View File
@@ -1,4 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Example Bot to show some of the functionality of the library
# This program is dedicated to the public domain under the CC0 license.
"""
This Bot uses the Updater class to handle the bot.
@@ -9,8 +13,8 @@ Then, the bot is started and the CLI-Loop is entered, where all text inputs are
inserted into the update queue for the bot to handle.
Usage:
Basic Echobot example, repeats messages. Reply to last chat from the command
line by typing "/reply <text>"
Repeats messages with a delay.
Reply to last chat from the command line by typing "/reply <text>"
Type 'stop' on the command line to stop the bot.
"""
@@ -18,24 +22,20 @@ from telegram import Updater
from telegram.dispatcher import run_async
from time import sleep
import logging
import sys
root = logging.getLogger()
root.setLevel(logging.INFO)
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(logging.INFO)
formatter = \
logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
root.addHandler(ch)
last_chat_id = 0
# Enable Logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
# We use this var to save the last chat id, so we can reply to it
last_chat_id = 0
# Command Handlers
# Define a few (command) handlers. These usually take the two arguments bot and
# update. Error handlers also receive the raised TelegramError object in error.
def start(bot, update):
""" Answer in Telegram """
bot.sendMessage(update.message.chat_id, text='Hi!')
@@ -65,10 +65,11 @@ def unknown_command(bot, update):
@run_async
def message(bot, update):
def message(bot, update, **kwargs):
"""
Example for an asynchronous handler. It's not guaranteed that replies will
be in order when using @run_async.
be in order when using @run_async. Also, you have to include **kwargs in
your parameter list. The kwargs contain all optional parameters that are
"""
sleep(2) # IO-heavy operation here
@@ -76,15 +77,12 @@ def message(bot, update):
update.message.text)
def error(bot, update, error):
""" Print error to console """
logger.warn('Update %s caused error %s' % (update, error))
# These handlers are for updates of type str. We use them to react to inputs
# on the command line interface
def cli_reply(bot, update, args):
"""
For any update of type telegram.Update or str, you can get the argument
list by appending args to the function parameters.
For any update of type telegram.Update or str that contains a command, you
can get the argument list by appending args to the function parameters.
Here, we reply to the last active chat with the text after the command.
"""
if last_chat_id is not 0:
@@ -96,6 +94,9 @@ def cli_noncommand(bot, update, update_queue):
You can also get the update queue as an argument in any handler by
appending it to the argument list. Be careful with this though.
Here, we put the input string back into the queue, but as a command.
To learn more about those optional handler parameters, read:
http://python-telegram-bot.readthedocs.org/en/latest/telegram.dispatcher.html
"""
update_queue.put('/%s' % update)
@@ -104,28 +105,39 @@ def unknown_cli_command(bot, update):
logger.warn("Command not found: %s" % update)
def error(bot, update, error):
""" Print error to console """
logger.warn('Update %s caused error %s' % (update, error))
def main():
# Create the EventHandler and pass it your bot's token.
token = 'token'
updater = Updater(token, workers=2)
token = 'TOKEN'
updater = Updater(token, workers=10)
# Get the dispatcher to register handlers
dp = updater.dispatcher
# This is how we add handlers for Telegram messages
dp.addTelegramCommandHandler("start", start)
dp.addTelegramCommandHandler("help", help)
dp.addUnknownTelegramCommandHandler(unknown_command)
# Message handlers only receive updates that don't contain commands
dp.addTelegramMessageHandler(message)
# Regex handlers will receive all updates on which their regex matches
dp.addTelegramRegexHandler('.*', any_message)
# String handlers work pretty much the same
dp.addStringCommandHandler('reply', cli_reply)
dp.addUnknownStringCommandHandler(unknown_cli_command)
dp.addStringRegexHandler('[^/].*', cli_noncommand)
# All TelegramErrors are caught for you and delivered to the error
# handler(s). Other types of Errors are not caught.
dp.addErrorHandler(error)
# Start the Bot and store the update Queue, so we can insert updates
update_queue = updater.start_polling(poll_interval=0.1, timeout=20)
update_queue = updater.start_polling(poll_interval=0.1, timeout=10)
'''
# Alternatively, run with webhook:
@@ -157,9 +169,9 @@ def main():
updater.stop()
break
# else, put the text into the update queue
# else, put the text into the update queue to be handled by our handlers
elif len(text) > 0:
update_queue.put(text) # Put command into queue
update_queue.put(text)
if __name__ == '__main__':
main()
-80
View File
@@ -1,80 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Simple Bot to reply to Telegram messages
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import logging
import telegram
from time import sleep
try:
from urllib.error import URLError
except ImportError:
from urllib2 import URLError # python 2
def main():
# Telegram Bot Authorization Token
bot = telegram.Bot('TOKEN')
# 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
except IndexError:
update_id = None
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
while True:
try:
update_id = echo(bot, update_id)
except telegram.TelegramError as e:
# These are network problems with Telegram.
if e.message in ("Bad Gateway", "Timed out"):
sleep(1)
elif e.message == "Unauthorized":
# The user has removed or blocked the bot.
update_id += 1
else:
raise e
except URLError as e:
# These are network problems on our end.
sleep(1)
def echo(bot, update_id):
# Request updates after the last update_id
for update in bot.getUpdates(offset=update_id, timeout=10):
# chat_id is required to reply to any message
chat_id = update.message.chat_id
update_id = update.update_id + 1
message = update.message.text
if message:
# Reply to the message
bot.sendMessage(chat_id=chat_id,
text=message)
return update_id
if __name__ == '__main__':
main()
@@ -1,36 +1,35 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Simple Bot to reply to Telegram messages
# This program is dedicated to the public domain under the CC0 license.
"""
This Bot uses the Updater class to handle the bot.
First, a few handler functions are defined. Then, those functions are passed to
the Dispatcher and registered at their respective places.
Then, the bot is started and the CLI-Loop is entered.
Then, the bot is started and runs until we press Ctrl-C on the command line.
Usage:
Basic Echobot example, repeats messages.
Type 'stop' on the command line to stop the bot.
Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
from telegram import Updater
import logging
import sys
# Enable logging
root = logging.getLogger()
root.setLevel(logging.INFO)
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(logging.DEBUG)
formatter = \
logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
root.addHandler(ch)
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
# Command Handlers
# Define a few command handlers. These usually take the two arguments bot and
# update. Error handlers also receive the raised TelegramError object in error.
def start(bot, update):
bot.sendMessage(update.message.chat_id, text='Hi!')
@@ -61,14 +60,15 @@ def main():
# on noncommand i.e message - echo the message on Telegram
dp.addTelegramMessageHandler(echo)
# on error - print error to stdout
# log all errors
dp.addErrorHandler(error)
# Start the Bot
updater.start_polling(timeout=5)
updater.start_polling()
# Run the bot until the user presses Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT
# Run the bot until the you presses Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
+106
View File
@@ -0,0 +1,106 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Simple Bot to reply to Telegram messages
# This program is dedicated to the public domain under the CC0 license.
"""
This Bot uses the Updater class to handle the bot.
First, a few handler functions are defined. Then, those functions are passed to
the Dispatcher and registered at their respective places.
Then, the bot is started and runs until we press Ctrl-C on the command line.
Usage:
Basic inline bot example. Applies different text transformations.
Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
from random import getrandbits
import re
from telegram import Updater, Update, InlineQueryResultArticle, ParseMode
import logging
# Enable logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
# Define a few command handlers. These usually take the two arguments bot and
# update. Error handlers also receive the raised TelegramError object in error.
def start(bot, update):
bot.sendMessage(update.message.chat_id, text='Hi!')
def help(bot, update):
bot.sendMessage(update.message.chat_id, text='Help!')
def escape_markdown(text):
"""Helper function to escape telegram markup symbols"""
escape_chars = '\*_`\['
return re.sub(r'([%s])' % escape_chars, r'\\\1', text)
def inlinequery(bot, update):
if update.inline_query is not None and update.inline_query.query:
query = update.inline_query.query
results = list()
results.append(InlineQueryResultArticle(
id=hex(getrandbits(64))[2:],
title="Caps",
message_text=query.upper()))
results.append(InlineQueryResultArticle(
id=hex(getrandbits(64))[2:],
title="Bold",
message_text="*%s*" % escape_markdown(query),
parse_mode=ParseMode.MARKDOWN))
results.append(InlineQueryResultArticle(
id=hex(getrandbits(64))[2:],
title="Italic",
message_text="_%s_" % escape_markdown(query),
parse_mode=ParseMode.MARKDOWN))
bot.answerInlineQuery(update.inline_query.id, results=results)
def error(bot, update, error):
logger.warn('Update "%s" caused error "%s"' % (update, error))
def main():
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN")
# Get the dispatcher to register handlers
dp = updater.dispatcher
# on different commands - answer in Telegram
dp.addTelegramCommandHandler("start", start)
dp.addTelegramCommandHandler("help", help)
# on noncommand i.e message - echo the message on Telegram
dp.addTelegramInlineHandler(inlinequery)
# log all errors
dp.addErrorHandler(error)
# Start the Bot
updater.start_polling()
# Block until the user presses Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Simple Bot to reply to Telegram messages
# This program is dedicated to the public domain under the CC0 license.
import logging
import telegram
from telegram.error import NetworkError, Unauthorized
from time import sleep
def main():
# Telegram Bot Authorization Token
bot = telegram.Bot('TOKEN')
# 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
except IndexError:
update_id = None
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
while True:
try:
update_id = echo(bot, update_id)
except NetworkError:
sleep(1)
except Unauthorized:
# The user has removed or blocked the bot.
update_id += 1
def echo(bot, update_id):
# Request updates after the last update_id
for update in bot.getUpdates(offset=update_id, timeout=10):
# chat_id is required to reply to any message
chat_id = update.message.chat_id
update_id = update.update_id + 1
message = update.message.text
if message:
# Reply to the message
bot.sendMessage(chat_id=chat_id,
text=message)
return update_id
if __name__ == '__main__':
main()
@@ -2,23 +2,7 @@
# encoding: utf-8
#
# Robô Ed Telegram Bot
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
__author__ = 'leandrotoledodesouza@gmail.com'
# This program is dedicated to the public domain under the CC0 license.
import logging
import telegram
+92
View File
@@ -0,0 +1,92 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Simple Bot to send timed Telegram messages
# This program is dedicated to the public domain under the CC0 license.
"""
This Bot uses the Updater class to handle the bot and the JobQueue to send
timed messages.
First, a few handler functions are defined. Then, those functions are passed to
the Dispatcher and registered at their respective places.
Then, the bot is started and runs until we press Ctrl-C on the command line.
Usage:
Basic Alarm Bot example, sends a message after a set time.
Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
from telegram import Updater
import logging
# Enable logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
job_queue = None
# Define a few command handlers. These usually take the two arguments bot and
# update. Error handlers also receive the raised TelegramError object in error.
def start(bot, update):
bot.sendMessage(update.message.chat_id, text='Hi! Use /set <seconds> to '
'set a timer')
def set(bot, update, args):
""" Adds a job to the queue """
chat_id = update.message.chat_id
try:
# args[0] should contain the time for the timer in seconds
due = int(args[0])
if due < 0:
bot.sendMessage(chat_id,text='Sorry we can not go back to future!')
def alarm(bot):
""" Inner function to send the alarm message """
bot.sendMessage(chat_id, text='Beep!')
# Add job to queue
job_queue.put(alarm, due, repeat=False)
bot.sendMessage(chat_id, text='Timer successfully set!')
except IndexError:
bot.sendMessage(chat_id, text='Usage: /set <seconds>')
except ValueError:
bot.sendMessage(chat_id, text='Usage: /set <seconds>')
def error(bot, update, error):
logger.warn('Update "%s" caused error "%s"' % (update, error))
def main():
global job_queue
updater = Updater("TOKEN")
job_queue = updater.job_queue
# Get the dispatcher to register handlers
dp = updater.dispatcher
# on different commands - answer in Telegram
dp.addTelegramCommandHandler("start", start)
dp.addTelegramCommandHandler("help", start)
dp.addTelegramCommandHandler("set", set)
# log all errors
dp.addErrorHandler(error)
# 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.
updater.idle()
if __name__ == '__main__':
main()
+6
View File
@@ -0,0 +1,6 @@
flake8
nose
pep257
pylint
unittest2
flaky
+2 -2
View File
@@ -26,9 +26,9 @@ def requirements():
setup(
name='python-telegram-bot',
version='3.1.0',
version='3.3',
author='Leandro Toledo',
author_email='leandrotoledodesouza@gmail.com',
author_email='devs@python-telegram-bot.org',
license='LGPLv3',
url='https://github.com/python-telegram-bot/python-telegram-bot',
keywords='python telegram bot api wrapper',
+17 -6
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -18,9 +19,6 @@
"""A library that provides a Python interface to the Telegram Bot API"""
__author__ = 'leandrotoledodesouza@gmail.com'
__version__ = '3.1.0'
from .base import TelegramObject
from .user import User
from .chat import Chat
@@ -45,14 +43,27 @@ from .nullhandler import NullHandler
from .emoji import Emoji
from .parsemode import ParseMode
from .message import Message
from .inlinequery import InlineQuery
from .choseninlineresult import ChosenInlineResult
from .inlinequeryresult import InlineQueryResultArticle, InlineQueryResultGif,\
InlineQueryResultMpeg4Gif, InlineQueryResultPhoto, InlineQueryResultVideo
from .update import Update
from .bot import Bot
from .dispatcher import Dispatcher
from .jobqueue import JobQueue
from .updatequeue import UpdateQueue
from .updater import Updater
__all__ = ['Bot', 'Updater', 'Dispatcher', 'Emoji', 'TelegramError',
__author__ = 'devs@python-telegram-bot.org'
__version__ = '3.3'
__all__ = ('Bot', 'Updater', 'Dispatcher', 'Emoji', 'TelegramError',
'InputFile', 'ReplyMarkup', 'ForceReply', 'ReplyKeyboardHide',
'ReplyKeyboardMarkup', 'UserProfilePhotos', 'ChatAction',
'Location', 'Contact', 'Video', 'Sticker', 'Document', 'File',
'Audio', 'PhotoSize', 'Chat', 'Update', 'ParseMode', 'Message',
'User', 'TelegramObject', 'NullHandler', 'Voice']
'User', 'TelegramObject', 'NullHandler', 'Voice', 'JobQueue',
'InlineQuery', 'ChosenInlineResult', 'InlineQueryResultArticle',
'InlineQueryResultGif', 'InlineQueryResultPhoto',
'InlineQueryResultMpeg4Gif', 'InlineQueryResultVideo',
'UpdateQueue')
+3 -2
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -16,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Audio"""
"""This module contains a object that represents a Telegram Audio."""
from telegram import TelegramObject
+4 -3
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -16,14 +17,14 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""Base class for Telegram Objects"""
"""Base class for Telegram Objects."""
import json
from abc import ABCMeta
class TelegramObject(object):
"""Base class for most telegram objects"""
"""Base class for most telegram objects."""
__metaclass__ = ABCMeta
+104 -27
View File
@@ -2,7 +2,8 @@
# pylint: disable=E0611,E0213,E1102,C0103,E1101,W0613,R0913,R0904
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -17,14 +18,16 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Bot"""
"""This module contains a object that represents a Telegram Bot."""
import functools
import logging
from telegram import (User, Message, Update, UserProfilePhotos, File,
TelegramError, ReplyMarkup, TelegramObject, NullHandler)
from telegram.error import InvalidToken
from telegram.utils import request
from telegram.utils.validate import validate_string
H = NullHandler()
logging.getLogger(__name__).addHandler(H)
@@ -52,7 +55,7 @@ class Bot(TelegramObject):
def __init__(self,
token,
base_url=None):
self.token = token
self.token = self._valid_token(token)
if base_url is None:
self.base_url = 'https://api.telegram.org/bot%s' % self.token
@@ -140,29 +143,39 @@ class Bot(TelegramObject):
decorator
"""
url, data = func(self, *args, **kwargs)
if not data.get('chat_id'):
raise TelegramError('Invalid chat_id')
if kwargs.get('reply_to_message_id'):
reply_to_message_id = kwargs.get('reply_to_message_id')
data['reply_to_message_id'] = reply_to_message_id
if kwargs.get('reply_markup'):
reply_markup = kwargs.get('reply_markup')
if isinstance(reply_markup, ReplyMarkup):
data['reply_markup'] = reply_markup.to_json()
else:
data['reply_markup'] = reply_markup
result = request.post(url, data)
if result is True:
return result
return Message.de_json(result)
return Bot._post_message(url, data, kwargs)
return decorator
@staticmethod
def _post_message(url, data, kwargs, timeout=None, network_delay=2.):
"""Posts a message to the telegram servers.
Returns:
telegram.Message
"""
if not data.get('chat_id'):
raise TelegramError('Invalid chat_id')
if kwargs.get('reply_to_message_id'):
reply_to_message_id = kwargs.get('reply_to_message_id')
data['reply_to_message_id'] = reply_to_message_id
if kwargs.get('reply_markup'):
reply_markup = kwargs.get('reply_markup')
if isinstance(reply_markup, ReplyMarkup):
data['reply_markup'] = reply_markup.to_json()
else:
data['reply_markup'] = reply_markup
result = request.post(url, data, timeout=timeout,
network_delay=network_delay)
if result is True:
return result
return Message.de_json(result)
@log
def getMe(self):
"""A simple method for testing your bot's auth token.
@@ -428,12 +441,12 @@ class Bot(TelegramObject):
return url, data
@log
@message
def sendVideo(self,
chat_id,
video,
duration=None,
caption=None,
timeout=None,
**kwargs):
"""Use this method to send video files, Telegram clients support mp4
videos (other formats may be sent as telegram.Document).
@@ -450,6 +463,9 @@ class Bot(TelegramObject):
caption:
Video caption (may also be used when resending videos by file_id).
[Optional]
timeout:
float. If this value is specified, use it as the definitive timeout
(in seconds) for urlopen() operations. [Optional]
reply_to_message_id:
If the message is a reply, ID of the original message. [Optional]
reply_markup:
@@ -471,7 +487,7 @@ class Bot(TelegramObject):
if caption:
data['caption'] = caption
return url, data
return self._post_message(url, data, kwargs, timeout=timeout)
@log
@message
@@ -583,6 +599,59 @@ class Bot(TelegramObject):
return url, data
@log
def answerInlineQuery(self,
inline_query_id,
results,
cache_time=None,
is_personal=None,
next_offset=None):
"""Use this method to reply to an inline query.
Args:
inline_query_id (str):
Unique identifier for answered query
results (list[InlineQueryResult]):
A list of results for the inline query
Keyword Args:
cache_time (Optional[int]): The maximum amount of time the result
of the inline query may be cached on the server
is_personal (Optional[bool]): Pass True, if results may be cached
on the server side only for the user that sent the query. By
default, results may be returned to any user who sends the same
query
next_offset (Optional[str]): Pass the offset that a client should
send in the next query with the same text to receive more
results. Pass an empty string if there are no more results or
if you don't support pagination. Offset length can't exceed 64
bytes.
Returns:
A boolean if answering was successful
"""
validate_string(inline_query_id, 'inline_query_id')
validate_string(inline_query_id, 'next_offset')
url = '%s/answerInlineQuery' % self.base_url
results = [res.to_dict() for res in results]
data = {'inline_query_id': inline_query_id,
'results': results}
if cache_time is not None:
data['cache_time'] = int(cache_time)
if is_personal is not None:
data['is_personal'] = bool(is_personal)
if next_offset is not None:
data['next_offset'] = next_offset
result = request.post(url, data)
return result
@log
def getUserProfilePhotos(self,
user_id,
@@ -691,7 +760,7 @@ class Bot(TelegramObject):
self.logger.info(
'Getting updates: %s', [u['update_id'] for u in result])
else:
self.logger.info('No new updates found.')
self.logger.debug('No new updates found.')
return [Update.de_json(x) for x in result]
@@ -744,3 +813,11 @@ class Bot(TelegramObject):
def __reduce__(self):
return (self.__class__, (self.token,
self.base_url.replace(self.token, '')))
@staticmethod
def _valid_token(token):
"""a very basic validation on token"""
left, sep, _right = token.partition(':')
if (not sep) or (not left.isdigit()) or (len(left) < 3):
raise InvalidToken()
return token
+3 -2
View File
@@ -2,7 +2,8 @@
# pylint: disable=C0103,W0622
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -17,7 +18,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 a object that represents a Telegram Chat"""
"""This module contains a object that represents a Telegram Chat."""
from telegram import TelegramObject
+3 -2
View File
@@ -2,7 +2,8 @@
# pylint: disable=R0903
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -17,7 +18,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 a object that represents a Telegram ChatAction"""
"""This module contains a object that represents a Telegram ChatAction."""
class ChatAction(object):
+81
View File
@@ -0,0 +1,81 @@
#!/usr/bin/env python
# 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>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""
This module contains a object that represents a Telegram ChosenInlineResult
"""
from telegram import TelegramObject, User
class ChosenInlineResult(TelegramObject):
"""This object represents a Telegram ChosenInlineResult.
Note:
* In Python `from` is a reserved word, use `from_user` instead.
Attributes:
result_id (str):
from_user (:class:`telegram.User`):
query (str):
Args:
result_id (str):
from_user (:class:`telegram.User`):
query (str):
"""
def __init__(self,
result_id,
from_user,
query):
# Required
self.result_id = result_id
self.from_user = from_user
self.query = query
@staticmethod
def de_json(data):
"""
Args:
data (dict):
Returns:
telegram.ChosenInlineResult:
"""
if not data:
return None
data = data.copy()
data['from_user'] = User.de_json(data.pop('from'))
return ChosenInlineResult(**data)
def to_dict(self):
"""
Returns:
dict:
"""
data = super(ChosenInlineResult, self).to_dict()
# Required
data['from'] = data.pop('from_user', None)
return data
+3 -2
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -16,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Contact"""
"""This module contains a object that represents a Telegram Contact."""
from telegram import TelegramObject
+216 -110
View File
@@ -1,27 +1,48 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the Dispatcher class."""
"""
This module contains the Dispatcher class.
"""
import logging
from functools import wraps
from inspect import getargspec
from threading import Thread, BoundedSemaphore, Lock
from threading import Thread, BoundedSemaphore, Lock, Event, current_thread
from re import match
from time import sleep
from telegram import (TelegramError, Update, NullHandler)
from telegram.updatequeue import Empty
H = NullHandler()
logging.getLogger(__name__).addHandler(H)
semaphore = None
running_async = 0
async_threads = set()
""":type: set[Thread]"""
async_lock = Lock()
def run_async(func):
"""
Function decorator that will run the function in a new thread.
Function decorator that will run the function in a new thread. A function
decorated with this will have to include **kwargs in their parameter list,
which will contain all optional parameters.
Args:
func (function): The function to run in the thread.
@@ -30,28 +51,29 @@ def run_async(func):
function:
"""
# TODO: handle exception in async threads
# set a threading.Event to notify caller thread
@wraps(func)
def pooled(*args, **kwargs):
def pooled(*pargs, **kwargs):
"""
A wrapper to run a thread in a thread pool
"""
global running_async, async_lock
result = func(*args, **kwargs)
result = func(*pargs, **kwargs)
semaphore.release()
with async_lock:
running_async -= 1
async_threads.remove(current_thread())
return result
@wraps(func)
def async_func(*args, **kwargs):
def async_func(*pargs, **kwargs):
"""
A wrapper to run a function in a thread
"""
global running_async, async_lock
thread = Thread(target=pooled, args=args, kwargs=kwargs)
thread = Thread(target=pooled, args=pargs, kwargs=kwargs)
semaphore.acquire()
with async_lock:
running_async += 1
async_threads.add(thread)
thread.start()
return thread
@@ -87,19 +109,38 @@ class Dispatcher:
a list that contains the content of the message split on spaces,
except the first word (usually the command).
Example: '/add item1 item2 item3' -> ['item1', 'item2', 'item3']
For updates that contain inline queries, they will contain the
whole query split on spaces.
For other updates, args will be None
Attributes:
In some cases handlers may need some context data to process the update. To
procedure just queue in update_queue.put(update, context=context) or
processUpdate(update,context=context).
context:
Extra data for handling updates.
For regex-based handlers, you can also request information about the match.
For all other handlers, these will be None
groups:
A tuple that contains the result of
re.match(matcher, ...).groups()
groupdict:
A dictionary that contains the result of
re.match(matcher, ...).groupdict()
Args:
bot (telegram.Bot): The bot object that should be passed to the
handlers update_queue (queue.Queue): The synchronized queue that will
contain the updates.
handlers
update_queue (telegram.UpdateQueue): The synchronized queue that will
contain the updates.
"""
def __init__(self, bot, update_queue, workers=4):
def __init__(self, bot, update_queue, workers=4, exception_event=None):
self.bot = bot
self.update_queue = update_queue
self.telegram_message_handlers = []
self.telegram_inline_handlers = []
self.telegram_command_handlers = {}
self.telegram_regex_handlers = {}
self.string_regex_handlers = {}
@@ -110,6 +151,8 @@ class Dispatcher:
self.error_handlers = []
self.logger = logging.getLogger(__name__)
self.running = False
self.__stop_event = Event()
self.__exception_event = exception_event or Event()
global semaphore
if not semaphore:
@@ -117,41 +160,60 @@ class Dispatcher:
else:
self.logger.info("Semaphore already initialized, skipping.")
class _Stop(object):
"""
A class which objects can be passed into the update queue to stop the
thread
"""
pass
def start(self):
"""
Thread target of thread 'dispatcher'. Runs in background and processes
the update queue.
"""
if self.running:
self.logger.warning('already running')
return
if self.__exception_event.is_set():
msg = 'reusing dispatcher after exception event is forbidden'
self.logger.error(msg)
raise TelegramError(msg)
self.running = True
self.logger.info('Dispatcher thread started')
while True:
update = None
self.logger.info('Dispatcher started')
while 1:
try:
# Pop update from update queue.
# Blocks if no updates are available.
update = self.update_queue.get()
if type(update) is self._Stop:
update, context = self.update_queue.get(True, 1, True)
except Empty:
if self.__stop_event.is_set():
self.logger.info('orderly stopping')
break
elif self.__stop_event.is_set():
self.logger.critical(
'stopping due to exception in another thread')
break
continue
self.processUpdate(update)
self.logger.debug('Processed Update: %s' % update)
try:
self.processUpdate(update, context)
self.logger.debug('Processed Update: %s with context %s'
% (update, context))
# Dispatch any errors
except TelegramError as te:
self.logger.warn("Error was raised while processing Update.")
self.dispatchError(update, te)
try:
self.dispatchError(update, te)
# Log errors in error handlers
except:
self.logger.exception("An uncaught error was raised while "
"handling the error")
# All other errors should not stop the thread, just print them
except:
self.logger.exception("An uncaught error was raised while "
"processing an update")
self.running = False
self.logger.info('Dispatcher thread stopped')
def stop(self):
@@ -159,10 +221,12 @@ class Dispatcher:
Stops the thread
"""
if self.running:
self.running = False
self.update_queue.put(self._Stop())
self.__stop_event.set()
while self.running:
sleep(0.1)
self.__stop_event.clear()
def processUpdate(self, update):
def processUpdate(self, update, context=None):
"""
Processes a single update.
@@ -175,15 +239,15 @@ class Dispatcher:
# Custom type handlers
for t in self.type_handlers:
if isinstance(update, t):
self.dispatchType(update)
self.dispatchType(update, context)
handled = True
# string update
if type(update) is str and update.startswith('/'):
self.dispatchStringCommand(update)
self.dispatchStringCommand(update, context)
handled = True
elif type(update) is str:
self.dispatchStringRegex(update)
self.dispatchRegex(update, context)
handled = True
# An error happened while polling
@@ -192,21 +256,23 @@ class Dispatcher:
handled = True
# Telegram update (regex)
if isinstance(update, Update):
self.dispatchTelegramRegex(update)
if isinstance(update, Update) and update.message is not None:
self.dispatchRegex(update, context)
handled = True
# Telegram update (command)
if isinstance(update, Update) \
and update.message.text.startswith('/'):
self.dispatchTelegramCommand(update)
handled = True
# Telegram update (message)
elif isinstance(update, Update):
self.dispatchTelegramMessage(update)
handled = True
# Telegram update (command)
if update.message.text.startswith('/'):
self.dispatchTelegramCommand(update, context)
# Telegram update (message)
else:
self.dispatchTelegramMessage(update, context)
handled = True
elif isinstance(update, Update) and \
(update.inline_query is not None or
update.chosen_inline_result is not None):
self.dispatchTelegramInline(update, context)
handled = True
# Update not recognized
if not handled:
self.dispatchError(update, TelegramError(
@@ -224,6 +290,17 @@ class Dispatcher:
self.telegram_message_handlers.append(handler)
def addTelegramInlineHandler(self, handler):
"""
Registers an inline query handler in the Dispatcher.
Args:
handler (function): A function that takes (Bot, Update, *args) as
arguments.
"""
self.telegram_inline_handlers.append(handler)
def addTelegramCommandHandler(self, command, handler):
"""
Registers a command handler in the Dispatcher.
@@ -353,6 +430,17 @@ class Dispatcher:
if handler in self.telegram_message_handlers:
self.telegram_message_handlers.remove(handler)
def removeTelegramInlineHandler(self, handler):
"""
De-registers an inline query handler.
Args:
handler (any):
"""
if handler in self.telegram_inline_handlers:
self.telegram_inline_handlers.remove(handler)
def removeTelegramCommandHandler(self, command, handler):
"""
De-registers a command handler.
@@ -450,7 +538,7 @@ class Dispatcher:
and handler in self.type_handlers[the_type]:
self.type_handlers[the_type].remove(handler)
def dispatchTelegramCommand(self, update):
def dispatchTelegramCommand(self, update, context=None):
"""
Dispatches an update that contains a command.
@@ -463,31 +551,39 @@ class Dispatcher:
command = update.message.text.split(' ')[0][1:].split('@')[0]
if command in self.telegram_command_handlers:
self.dispatchTo(self.telegram_command_handlers[command], update)
self.dispatchTo(self.telegram_command_handlers[command], update,
context=context)
else:
self.dispatchTo(self.unknown_telegram_command_handlers, update)
self.dispatchTo(self.unknown_telegram_command_handlers, update,
context=context)
def dispatchTelegramRegex(self, update):
def dispatchRegex(self, update, context=None):
"""
Dispatches an update to all regex handlers that match the message
string.
Dispatches an update to all string or telegram regex handlers that
match the string/message content.
Args:
command (str): The command keyword
update (telegram.Update): The Telegram update that contains the
command
update (str, Update): The update that should be checked for matches
"""
matching_handlers = []
if isinstance(update, Update):
handlers = self.telegram_regex_handlers
to_match = update.message.text
elif isinstance(update, str):
handlers = self.string_regex_handlers
to_match = update
for matcher in self.telegram_regex_handlers:
if match(matcher, update.message.text):
for handler in self.telegram_regex_handlers[matcher]:
matching_handlers.append(handler)
for matcher in handlers:
m = match(matcher, to_match)
if m:
for handler in handlers[matcher]:
self.call_handler(handler,
update,
groups=m.groups(),
groupdict=m.groupdict(),
context=context)
self.dispatchTo(matching_handlers, update)
def dispatchStringCommand(self, update):
def dispatchStringCommand(self, update, context=None):
"""
Dispatches a string-update that contains a command.
@@ -498,31 +594,13 @@ class Dispatcher:
command = update.split(' ')[0][1:]
if command in self.string_command_handlers:
self.dispatchTo(self.string_command_handlers[command], update)
self.dispatchTo(self.string_command_handlers[command], update,
context=context)
else:
self.dispatchTo(self.unknown_string_command_handlers, update)
self.dispatchTo(self.unknown_string_command_handlers, update,
context=context)
def dispatchStringRegex(self, update):
"""
Dispatches an update to all string regex handlers that match the
string.
Args:
command (str): The command keyword
update (telegram.Update): The Telegram update that contains the
command
"""
matching_handlers = []
for matcher in self.string_regex_handlers:
if match(matcher, update):
for handler in self.string_regex_handlers[matcher]:
matching_handlers.append(handler)
self.dispatchTo(matching_handlers, update)
def dispatchType(self, update):
def dispatchType(self, update, context=None):
"""
Dispatches an update of any type.
@@ -532,12 +610,9 @@ class Dispatcher:
for t in self.type_handlers:
if isinstance(update, t):
self.dispatchTo(self.type_handlers[t], update)
else:
self.dispatchError(update, TelegramError(
"Received update of unknown type %s" % type(update)))
self.dispatchTo(self.type_handlers[t], update, context=context)
def dispatchTelegramMessage(self, update):
def dispatchTelegramMessage(self, update, context=None):
"""
Dispatches an update that contains a regular message.
@@ -546,7 +621,19 @@ class Dispatcher:
message.
"""
self.dispatchTo(self.telegram_message_handlers, update)
self.dispatchTo(self.telegram_message_handlers, update,
context=context)
def dispatchTelegramInline(self, update, context=None):
"""
Dispatches an update that contains an inline update.
Args:
update (telegram.Update): The Telegram update that contains the
message.
"""
self.dispatchTo(self.telegram_inline_handlers, update, context=None)
def dispatchError(self, update, error):
"""
@@ -560,7 +647,7 @@ class Dispatcher:
for handler in self.error_handlers:
handler(self.bot, update, error)
def dispatchTo(self, handlers, update):
def dispatchTo(self, handlers, update, **kwargs):
"""
Dispatches an update to a list of handlers.
@@ -570,9 +657,9 @@ class Dispatcher:
"""
for handler in handlers:
self.call_handler(handler, update)
self.call_handler(handler, update, **kwargs)
def call_handler(self, handler, update):
def call_handler(self, handler, update, **kwargs):
"""
Calls an update handler. Checks the handler for keyword arguments and
fills them, if possible.
@@ -581,20 +668,39 @@ class Dispatcher:
handler (function): An update handler function
update (any): An update
"""
kwargs = {}
target_kwargs = {}
fargs = getargspec(handler).args
if 'update_queue' in fargs:
kwargs['update_queue'] = self.update_queue
'''
async handlers will receive all optional arguments, since we can't
their argument list.
'''
if 'args' in fargs:
if isinstance(update, Update):
is_async = 'pargs' == getargspec(handler).varargs
if is_async or 'update_queue' in fargs:
target_kwargs['update_queue'] = self.update_queue
if is_async or 'args' in fargs:
if isinstance(update, Update) and update.message:
args = update.message.text.split(' ')[1:]
elif isinstance(update, Update) and update.inline_query:
args = update.inline_query.query.split(' ')
elif isinstance(update, str):
args = update.split(' ')[1:]
else:
args = None
kwargs['args'] = args
target_kwargs['args'] = args
handler(self.bot, update, **kwargs)
if is_async or 'groups' in fargs:
target_kwargs['groups'] = kwargs.get('groups', None)
if is_async or 'groupdict' in fargs:
target_kwargs['groupdict'] = kwargs.get('groupdict', None)
if is_async or 'context' in fargs:
target_kwargs['context'] = kwargs.get('context', None)
handler(self.bot, update, **target_kwargs)
+3 -2
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -16,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Document"""
"""This module contains a object that represents a Telegram Document."""
from telegram import PhotoSize, TelegramObject
+3 -2
View File
@@ -3,7 +3,8 @@
# pylint: disable=C0103,C0301,R0903
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -18,7 +19,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents an Emoji"""
"""This module contains a object that represents an Emoji."""
from future.utils import bytes_to_native_str as n
+51 -9
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -16,9 +17,24 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Error"""
"""This module contains a object that represents a Telegram Error."""
import re
def _lstrip_str(in_s, lstr):
"""
Args:
in_s (str): in string
lstr (str): substr to strip from left side
Returns:
str:
"""
if in_s.startswith(lstr):
res = in_s[len(lstr):]
else:
res = in_s
return res
class TelegramError(Exception):
@@ -26,16 +42,42 @@ class TelegramError(Exception):
def __init__(self, message):
"""
Args:
message (str):
Returns:
str:
"""
super(TelegramError, self).__init__()
api_error = re.match(r'^Error: (?P<message>.*)', message)
if api_error:
self.message = api_error.group('message').capitalize()
else:
self.message = message
msg = _lstrip_str(message, 'Error: ')
msg = _lstrip_str(msg, '[Error]: ')
if msg != message:
# api_error - capitalize the msg...
msg = msg.capitalize()
self.message = msg
def __str__(self):
return '%s' % (self.message)
class Unauthorized(TelegramError):
def __init__(self):
super(Unauthorized, self).__init__('Unauthorized')
class InvalidToken(TelegramError):
def __init__(self):
super(InvalidToken, self).__init__('Invalid token')
class NetworkError(TelegramError):
pass
class TimedOut(NetworkError):
def __init__(self):
super(TimedOut, self).__init__('Timed out')
+4 -3
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -16,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram File"""
"""This module contains a object that represents a Telegram File."""
from os.path import basename
@@ -74,7 +75,7 @@ class File(TelegramObject):
url = self.file_path
if custom_path:
filename = basename(custom_path)
filename = custom_path
else:
filename = basename(url)
+3 -2
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -16,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram ForceReply"""
"""This module contains a object that represents a Telegram ForceReply."""
from telegram import ReplyMarkup
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env python
# 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>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram InlineQuery"""
from telegram import TelegramObject, User
class InlineQuery(TelegramObject):
"""This object represents a Telegram InlineQuery.
Note:
* In Python `from` is a reserved word, use `from_user` instead.
Attributes:
id (str):
from_user (:class:`telegram.User`):
query (str):
offset (str):
Args:
id (int):
from_user (:class:`telegram.User`):
query (str):
offset (str):
"""
def __init__(self,
id,
from_user,
query,
offset):
# Required
self.id = id
self.from_user = from_user
self.query = query
self.offset = offset
@staticmethod
def de_json(data):
"""
Args:
data (dict):
Returns:
telegram.InlineQuery:
"""
if not data:
return None
data = data.copy()
data['from_user'] = User.de_json(data.pop('from'))
return InlineQuery(**data)
def to_dict(self):
"""
Returns:
dict:
"""
data = super(InlineQuery, self).to_dict()
# Required
data['from'] = data.pop('from_user', None)
return data
+499
View File
@@ -0,0 +1,499 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""
This module contains the classes that represent Telegram InlineQueryResults
https://core.telegram.org/bots/api#inline-mode
"""
from telegram import TelegramObject
from telegram.utils.validate import validate_string
class InlineQueryResult(TelegramObject):
"""This object represents a Telegram InlineQueryResult.
Attributes:
type (str):
id (str):
Args:
type (str):
id (str): Unique identifier for this result, 1-64 Bytes
"""
def __init__(self,
type,
id):
# Required
self.type = str(type)
self.id = str(id)
@staticmethod
def de_json(data):
"""
Args:
data (dict):
Returns:
telegram.InlineQueryResult:
"""
if not data:
return None
return InlineQueryResult(**data)
class InlineQueryResultArticle(InlineQueryResult):
"""This object represents a Telegram InlineQueryResultArticle.
Attributes:
id (str):
title (str):
message_text (str):
parse_mode (str):
disable_web_page_preview (bool):
url (str):
hide_url (bool):
description (str):
thumb_url (str):
thumb_width (int):
thumb_height (int):
Args:
id (str): Unique identifier for this result, 1-64 Bytes
title (str):
message_text (str):
Keyword Args:
parse_mode (Optional[str]):
disable_web_page_preview (Optional[bool]):
url (Optional[str]):
hide_url (Optional[bool]):
description (Optional[str]):
thumb_url (Optional[str]):
thumb_width (Optional[int]):
thumb_height (Optional[int]):
"""
def __init__(self,
id,
title,
message_text,
parse_mode=None,
disable_web_page_preview=None,
url=None,
hide_url=None,
description=None,
thumb_url=None,
thumb_width=None,
thumb_height=None):
validate_string(title, 'title')
validate_string(message_text, 'message_text')
validate_string(url, 'url')
validate_string(description, 'description')
validate_string(thumb_url, 'thumb_url')
validate_string(parse_mode, 'parse_mode')
# Required
super(InlineQueryResultArticle, self).__init__('article', id)
self.title = title
self.message_text = message_text
# Optional
self.parse_mode = parse_mode
self.disable_web_page_preview = bool(disable_web_page_preview)
self.url = url
self.hide_url = bool(hide_url)
self.description = description
self.thumb_url = thumb_url
if thumb_width is not None:
self.thumb_width = int(thumb_width)
if thumb_height is not None:
self.thumb_height = int(thumb_height)
@staticmethod
def de_json(data):
"""
Args:
data (dict):
Returns:
telegram.InlineQueryResultArticle:
"""
if not data:
return None
data = data.copy()
data.pop('type', None)
return InlineQueryResultArticle(**data)
class InlineQueryResultPhoto(InlineQueryResult):
"""This object represents a Telegram InlineQueryResultPhoto.
Attributes:
id (str):
photo_url (str):
mime_type (str):
photo_width (int):
photo_height (int):
thumb_url (str):
title (str):
description (str):
caption (str):
message_text (str):
parse_mode (str):
disable_web_page_preview (bool):
Args:
id (str): Unique identifier for this result, 1-64 Bytes
photo_url (str):
thumb_url (str):
Keyword Args:
mime_type (Optional[str]):
photo_width (Optional[int]):
photo_height (Optional[int]):
title (Optional[str]):
description (Optional[str]):
caption (Optional[str]):
message_text (Optional[str]):
parse_mode (Optional[str]):
disable_web_page_preview (Optional[bool]):
"""
def __init__(self,
id,
photo_url,
thumb_url,
mime_type=None,
photo_width=None,
photo_height=None,
title=None,
description=None,
caption=None,
message_text=None,
parse_mode=None,
disable_web_page_preview=None):
validate_string(photo_url, 'photo_url')
validate_string(thumb_url, 'thumb_url')
validate_string(mime_type, 'mime_type')
validate_string(title, 'title')
validate_string(description, 'description')
validate_string(caption, 'caption')
validate_string(message_text, 'message_text')
validate_string(parse_mode, 'parse_mode')
# Required
super(InlineQueryResultPhoto, self).__init__('photo', id)
self.photo_url = photo_url
self.thumb_url = thumb_url
# Optional
self.mime_type = mime_type
if photo_width is not None:
self.photo_width = int(photo_width)
if photo_height is not None:
self.photo_height = int(photo_height)
self.title = title
self.description = description
self.caption = caption
self.message_text = message_text
self.parse_mode = parse_mode
self.disable_web_page_preview = bool(disable_web_page_preview)
@staticmethod
def de_json(data):
"""
Args:
data (dict):
Returns:
telegram.InlineQueryResultPhoto:
"""
if not data:
return None
data = data.copy()
data.pop('type', None)
return InlineQueryResultPhoto(**data)
class InlineQueryResultGif(InlineQueryResult):
"""This object represents a Telegram InlineQueryResultGif.
Attributes:
id (str):
gif_url (str):
gif_width (int):
gif_height (int):
thumb_url (str):
title (str):
caption (str):
message_text (str):
parse_mode (str):
disable_web_page_preview (bool):
Args:
id (str): Unique identifier for this result, 1-64 Bytes
gif_url (str):
thumb_url (str):
Keyword Args:
gif_width (Optional[int]):
gif_height (Optional[int]):
title (Optional[str]):
caption (Optional[str]):
message_text (Optional[str]):
parse_mode (Optional[str]):
disable_web_page_preview (Optional[bool]):
"""
def __init__(self,
id,
gif_url,
thumb_url,
gif_width=None,
gif_height=None,
title=None,
caption=None,
message_text=None,
parse_mode=None,
disable_web_page_preview=None):
validate_string(gif_url, 'gif_url')
validate_string(thumb_url, 'thumb_url')
validate_string(title, 'title')
validate_string(caption, 'caption')
validate_string(message_text, 'message_text')
validate_string(parse_mode, 'parse_mode')
# Required
super(InlineQueryResultGif, self).__init__('gif', id)
self.gif_url = gif_url
self.thumb_url = thumb_url
# Optional
if gif_width is not None:
self.gif_width = int(gif_width)
if gif_height is not None:
self.gif_height = int(gif_height)
self.title = title
self.caption = caption
self.message_text = message_text
self.parse_mode = parse_mode
self.disable_web_page_preview = bool(disable_web_page_preview)
@staticmethod
def de_json(data):
"""
Args:
data (dict):
Returns:
telegram.InlineQueryResultGif:
"""
if not data:
return None
data = data.copy()
data.pop('type', None)
return InlineQueryResultGif(**data)
class InlineQueryResultMpeg4Gif(InlineQueryResult):
"""This object represents a Telegram InlineQueryResultMpeg4Gif.
Attributes:
id (str):
mpeg4_url (str):
mpeg4_width (int):
mpeg4_height (int):
thumb_url (str):
title (str):
caption (str):
message_text (str):
parse_mode (str):
disable_web_page_preview (bool):
Args:
id (str): Unique identifier for this result, 1-64 Bytes
mpeg4_url (str):
thumb_url (str):
Keyword Args:
mpeg4_width (Optional[int]):
mpeg4_height (Optional[int]):
title (Optional[str]):
caption (Optional[str]):
message_text (Optional[str]):
parse_mode (Optional[str]):
disable_web_page_preview (Optional[bool]):
"""
def __init__(self,
id,
mpeg4_url,
thumb_url,
mpeg4_width=None,
mpeg4_height=None,
title=None,
caption=None,
message_text=None,
parse_mode=None,
disable_web_page_preview=None):
validate_string(mpeg4_url, 'mpeg4_url')
validate_string(thumb_url, 'thumb_url')
validate_string(title, 'title')
validate_string(caption, 'caption')
validate_string(message_text, 'message_text')
validate_string(parse_mode, 'parse_mode')
# Required
super(InlineQueryResultMpeg4Gif, self).__init__('mpeg4_gif', id)
self.mpeg4_url = mpeg4_url
self.thumb_url = thumb_url
# Optional
if mpeg4_width is not None:
self.mpeg4_width = int(mpeg4_width)
if mpeg4_height is not None:
self.mpeg4_height = int(mpeg4_height)
self.title = title
self.caption = caption
self.message_text = message_text
self.parse_mode = parse_mode
self.disable_web_page_preview = bool(disable_web_page_preview)
@staticmethod
def de_json(data):
"""
Args:
data (dict):
Returns:
telegram.InlineQueryResultMpeg4Gif:
"""
if not data:
return None
data = data.copy()
data.pop('type', None)
return InlineQueryResultMpeg4Gif(**data)
class InlineQueryResultVideo(InlineQueryResult):
"""This object represents a Telegram InlineQueryResultVideo.
Attributes:
id (str):
video_url (str):
mime_type (str):
video_width (int):
video_height (int):
video_duration (int):
thumb_url (str):
title (str):
description (str):
caption (str):
message_text (str):
parse_mode (str):
disable_web_page_preview (bool):
Args:
id (str): Unique identifier for this result, 1-64 Bytes
video_url (str):
mime_type (str):
thumb_url (str):
title (str):
message_text (str):
Keyword Args:
video_width (Optional[int]):
video_height (Optional[int]):
video_duration (Optional[int]):
description (Optional[str]):
caption (Optional[str]):
parse_mode (Optional[str]):
disable_web_page_preview (Optional[bool]):
"""
def __init__(self,
id,
video_url,
mime_type,
thumb_url,
title,
message_text,
video_width=None,
video_height=None,
video_duration=None,
description=None,
caption=None,
parse_mode=None,
disable_web_page_preview=None):
validate_string(video_url, 'video_url')
validate_string(mime_type, 'mime_type')
validate_string(thumb_url, 'thumb_url')
validate_string(title, 'title')
validate_string(message_text, 'message_text')
validate_string(description, 'description')
validate_string(caption, 'caption')
validate_string(parse_mode, 'parse_mode')
# Required
super(InlineQueryResultVideo, self).__init__('video', id)
self.video_url = video_url
self.mime_type = mime_type
self.thumb_url = thumb_url
self.title = title
self.message_text = message_text
# Optional
if video_width is not None:
self.video_width = int(video_width)
if video_height is not None:
self.video_height = int(video_height)
if video_duration is not None:
self.video_duration = int(video_duration)
self.description = description
self.caption = caption
self.parse_mode = parse_mode
self.disable_web_page_preview = bool(disable_web_page_preview)
@staticmethod
def de_json(data):
"""
Args:
data (dict):
Returns:
telegram.InlineQueryResultVideo:
"""
if not data:
return None
data = data.copy()
data.pop('type', None)
return InlineQueryResultVideo(**data)
+25 -15
View File
@@ -2,7 +2,8 @@
# pylint: disable=W0622,E0611
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -17,7 +18,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 a object that represents a Telegram InputFile"""
"""This module contains a object that represents a Telegram InputFile."""
try:
from email.generator import _make_boundary as choose_boundary
@@ -26,6 +27,7 @@ try:
except ImportError:
from mimetools import choose_boundary
from urllib2 import urlopen
import mimetypes
import os
import sys
@@ -68,19 +70,30 @@ class InputFile(object):
self.input_name = 'certificate'
self.input_file = data.pop('certificate')
if isinstance(self.input_file, file):
if str(self.input_file).startswith('http'):
from_url = True
self.input_file = urlopen(self.input_file)
else:
from_url = False
if isinstance(self.input_file, file) or from_url:
self.filename = None
self.input_file_content = self.input_file.read()
if 'filename' in data:
self.filename = self.data.pop('filename')
else:
elif isinstance(self.input_file, file):
self.filename = os.path.basename(self.input_file.name)
self.mimetype = mimetypes.guess_type(self.filename)[0] or \
DEFAULT_MIME_TYPE
elif from_url:
self.filename = os.path.basename(self.input_file.url)\
.split('?')[0].split('&')[0]
if 'http' in self.input_file:
self.input_file_content = urlopen(self.input_file).read()
self.mimetype = InputFile.is_image(self.input_file_content)
self.filename = self.mimetype.replace('/', '.')
try:
self.mimetype = InputFile.is_image(self.input_file_content)
if not self.filename or '.' not in self.filename:
self.filename = self.mimetype.replace('/', '.')
except TelegramError:
self.mimetype = mimetypes.guess_type(self.filename)[0] or \
DEFAULT_MIME_TYPE
@property
def headers(self):
@@ -184,10 +197,7 @@ class InputFile(object):
if file_type:
file_content = data[file_type[0]]
if file_type[0] == 'photo' or file_type[0] == 'document':
return isinstance(file_content, file) or \
str(file_content).startswith('http')
return isinstance(file_content, file)
return isinstance(file_content, file) or \
str(file_content).startswith('http')
return False
+166
View File
@@ -0,0 +1,166 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the class JobQueue."""
import logging
import time
from threading import Thread, Lock
try:
from queue import PriorityQueue
except ImportError:
from Queue import PriorityQueue
class JobQueue(object):
"""
This class allows you to periodically perform tasks with the bot.
Attributes:
tick_interval (float):
queue (PriorityQueue):
bot (Bot):
running (bool):
Args:
bot (Bot): The bot instance that should be passed to the jobs
Keyword Args:
tick_interval (Optional[float]): The interval this queue should check
the newest task in seconds. Defaults to 1.0
"""
def __init__(self, bot, tick_interval=1.0):
self.tick_interval = tick_interval
self.queue = PriorityQueue()
self.bot = bot
self.logger = logging.getLogger(__name__)
self.__lock = Lock()
self.running = False
def put(self,
run,
interval,
repeat=True,
next_t=None,
prevent_autostart=False):
"""
Queue a new job. If the JobQueue is not running, it will be started.
Args:
run (function): A function that takes the parameter `bot`
interval (float): The interval in seconds in which `run` should be
executed
repeat (Optional[bool]): If `False`, job will only be executed once
next_t (Optional[float]): Time in seconds in which run should be
executed first. Defaults to `interval`
prevent_autostart (Optional[bool]): If `True`, the job queue will
not be started automatically if it is not running.
"""
name = run.__name__
job = JobQueue.Job()
job.run = run
job.interval = interval
job.name = name
job.repeat = repeat
if next_t is None:
next_t = interval
next_t += time.time()
self.logger.debug("Putting a %s with t=%f" % (job.name, next_t))
self.queue.put((next_t, job))
if not self.running and not prevent_autostart:
self.logger.info("Auto-starting JobQueue")
self.start()
def tick(self):
"""
Run all jobs that are due and re-enqueue them with their interval
"""
now = time.time()
self.logger.debug("Ticking jobs with t=%f" % now)
while not self.queue.empty():
t, j = self.queue.queue[0]
self.logger.debug("Peeked at %s with t=%f" % (j.name, t))
if t < now:
self.queue.get()
self.logger.info("Running job %s" % j.name)
try:
j.run(self.bot)
except:
self.logger.exception("An uncaught error was raised while "
"executing job %s" % j.name)
if j.repeat:
self.put(j.run, j.interval)
continue
self.logger.debug("Next task isn't due yet. Finished!")
break
def start(self):
"""
Starts the job_queue thread.
"""
self.__lock.acquire()
if not self.running:
self.running = True
self.__lock.release()
job_queue_thread = Thread(target=self._start,
name="job_queue")
job_queue_thread.start()
self.logger.info('Job Queue thread started')
else:
self.__lock.release()
def _start(self):
"""
Thread target of thread 'job_queue'. Runs in background and performs
ticks on the job queue.
"""
while self.running:
self.tick()
time.sleep(self.tick_interval)
self.logger.info('Job Queue thread stopped')
def stop(self):
"""
Stops the thread
"""
with self.__lock:
self.running = False
class Job(object):
""" Inner class that represents a job """
interval = None
name = None
repeat = None
def run(self):
pass
def __lt__(self, other):
return False
+3 -2
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -16,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Location"""
"""This module contains a object that represents a Telegram Location."""
from telegram import TelegramObject
+4 -3
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 <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -17,7 +18,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 a object that represents a Telegram Message"""
"""This module contains a object that represents a Telegram Message."""
from datetime import datetime
from time import mktime
@@ -60,7 +61,7 @@ class Message(TelegramObject):
message_id (int):
from_user (:class:`telegram.User`):
date (:class:`datetime.datetime`):
chat (:class:`telegram.User` or :class:`telegram.Chat`):
chat (:class:`telegram.Chat`):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
+3 -2
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -16,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a logging NullHandler"""
"""This module contains a object that represents a logging NullHandler."""
import logging
+4 -2
View File
@@ -2,7 +2,8 @@
# pylint: disable=R0903
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -18,10 +19,11 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
Message Parse Modes"""
Message Parse Modes."""
class ParseMode(object):
"""This object represents a Telegram Message Parse Modes."""
MARKDOWN = 'Markdown'
HTML = 'HTML'
+12 -3
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -16,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram PhotoSize"""
"""This module contains a object that represents a Telegram PhotoSize."""
from telegram import TelegramObject
@@ -46,12 +47,20 @@ class PhotoSize(TelegramObject):
height,
**kwargs):
# Required
self.file_id = file_id
self.file_id = str(file_id)
self.width = int(width)
self.height = int(height)
# Optionals
self.file_size = int(kwargs.get('file_size', 0))
def __eq__(self, other):
if not isinstance(other, self.__class__):
return False
return (self.file_id == other.file_id and
self.width == other.width and
self.height == other.height and
self.file_size == other.file_size)
@staticmethod
def de_json(data):
"""
+3 -2
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -17,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
ReplyKeyboardHide"""
ReplyKeyboardHide."""
from telegram import ReplyMarkup
+3 -2
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -17,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
ReplyKeyboardMarkup"""
ReplyKeyboardMarkup."""
from telegram import ReplyMarkup
+3 -2
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -16,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""Base class for Telegram ReplyMarkup Objects"""
"""Base class for Telegram ReplyMarkup Objects."""
from telegram import TelegramObject
+3 -2
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -16,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Sticker"""
"""This module contains a object that represents a Telegram Sticker."""
from telegram import PhotoSize, TelegramObject
+15 -5
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -16,9 +17,9 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Update"""
"""This module contains a object that represents a Telegram Update."""
from telegram import Message, TelegramObject
from telegram import Message, TelegramObject, InlineQuery, ChosenInlineResult
class Update(TelegramObject):
@@ -27,6 +28,8 @@ class Update(TelegramObject):
Attributes:
update_id (int):
message (:class:`telegram.Message`):
inline_query (:class:`telegram.InlineQuery`):
chosen_inline_result (:class:`telegram.ChosenInlineResult`):
Args:
update_id (int):
@@ -34,6 +37,8 @@ class Update(TelegramObject):
Keyword Args:
message (Optional[:class:`telegram.Message`]):
inline_query (Optional[:class:`telegram.InlineQuery`]):
chosen_inline_result (Optional[:class:`telegram.ChosenInlineResult`])
"""
def __init__(self,
update_id,
@@ -42,12 +47,14 @@ class Update(TelegramObject):
self.update_id = int(update_id)
# Optionals
self.message = kwargs.get('message')
self.inline_query = kwargs.get('inline_query')
self.chosen_inline_result = kwargs.get('chosen_inline_result')
@staticmethod
def de_json(data):
"""
Args:
data (str):
data (dict):
Returns:
telegram.Update:
@@ -55,6 +62,9 @@ class Update(TelegramObject):
if not data:
return None
data['message'] = Message.de_json(data['message'])
data['message'] = Message.de_json(data.get('message'))
data['inline_query'] = InlineQuery.de_json(data.get('inline_query'))
data['chosen_inline_result'] = \
ChosenInlineResult.de_json(data.get('chosen_inline_result'))
return Update(**data)
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the class UpdateQueue to override standard
Queue."""
# Adjust for differences in Python versions
try:
# loading Empty here so it can be imported by users of updatequeue
from queue import Queue, Empty # flake8: noqa
except ImportError:
from Queue import Queue, Empty # flake8: noqa
class UpdateQueue(Queue):
"""
This class overrides standard Queues. Allows you to de/queue context
data apart from the handled `update`
"""
def put(self, item, block=True, timeout=None, context=None):
"""
Put an item into the queue with context data if provided as a
tuple (item, context). Overrides standard Queue.put method.
Args:
update (any): handled by the dispatcher
context (any): extra data to use in handlers
"""
Queue.put(self, (item, context), block, timeout)
def get(self, block=True, timeout=None, context=False):
"""
Remove and return an item from the queue. A tuple of
(update, context) if requested. Overrides standard Queue.get
method.
Args:
context (boolean): set true to get (update, context)
"""
if not context:
return Queue.get(self, block, timeout)[0]
return Queue.get(self, block, timeout)
+154 -82
View File
@@ -1,31 +1,37 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the class Updater, which tries to make creating
Telegram bots intuitive."""
"""
This module contains the class Updater, which tries to make creating Telegram
Bots intuitive!
"""
import logging
import os
import ssl
from threading import Thread
from threading import Thread, Lock, current_thread, Event
from time import sleep
import subprocess
from signal import signal, SIGINT, SIGTERM, SIGABRT
from telegram import (Bot, TelegramError, dispatcher, Dispatcher,
NullHandler)
NullHandler, JobQueue, UpdateQueue)
from telegram.utils.webhookhandler import (WebhookServer, WebhookHandler)
# Adjust for differences in Python versions
try:
from Queue import Queue
except ImportError:
from queue import Queue
try:
from urllib2 import URLError
except ImportError:
from urllib.error import URLError
H = NullHandler()
logging.getLogger(__name__).addHandler(H)
@@ -47,31 +53,52 @@ class Updater:
Attributes:
Args:
token (str): The bots token given by the @BotFather
token (Optional[str]): The bot's token given by the @BotFather
base_url (Optional[str]):
workers (Optional[int]): Amount of threads in the thread pool for
functions decorated with @run_async
bot (Optional[Bot]):
Raises:
ValueError: If both `token` and `bot` are passed or none of them.
"""
def __init__(self, token, base_url=None, workers=4):
def __init__(self,
token=None,
base_url=None,
workers=4,
bot=None,
job_queue_tick_interval=1.0):
if (token is None) and (bot is None):
raise ValueError('`token` or `bot` must be passed')
if (token is not None) and (bot is not None):
raise ValueError('`token` and `bot` are mutually exclusive')
self.bot = Bot(token, base_url)
self.update_queue = Queue()
self.dispatcher = Dispatcher(self.bot, self.update_queue,
workers=workers)
if bot is not None:
self.bot = bot
else:
self.bot = Bot(token, base_url)
self.update_queue = UpdateQueue()
self.job_queue = JobQueue(self.bot, job_queue_tick_interval)
self.__exception_event = Event()
self.dispatcher = Dispatcher(self.bot, self.update_queue, workers,
self.__exception_event)
self.last_update_id = 0
self.logger = logging.getLogger(__name__)
self.running = False
self.is_idle = False
self.httpd = None
self.__lock = Lock()
self.__threads = []
""":type: list[Thread]"""
def start_polling(self, poll_interval=1.0, timeout=10, network_delay=2):
def start_polling(self, poll_interval=0.0, timeout=10, network_delay=2):
"""
Starts polling updates from Telegram.
Args:
poll_interval (Optional[float]): Time to wait between polling
updates from Telegram in seconds. Default is 1.0
updates from Telegram in seconds. Default is 0.0
timeout (Optional[float]): Passed to Bot.getUpdates
network_delay (Optional[float]): Passed to Bot.getUpdates
@@ -79,22 +106,34 @@ class Updater:
Queue: The update queue that can be filled from the main thread
"""
# Create Thread objects
dispatcher_thread = Thread(target=self.dispatcher.start,
name="dispatcher")
event_handler_thread = Thread(target=self._start_polling,
name="updater",
args=(poll_interval, timeout,
network_delay))
with self.__lock:
if not self.running:
self.running = True
self.running = True
# Create & start threads
self._init_thread(self.dispatcher.start, "dispatcher")
self._init_thread(self._start_polling, "updater",
poll_interval, timeout, network_delay)
# Start threads
dispatcher_thread.start()
event_handler_thread.start()
# Return the update queue so the main thread can insert updates
return self.update_queue
# Return the update queue so the main thread can insert updates
return self.update_queue
def _init_thread(self, target, name, *args, **kwargs):
thr = Thread(target=self._thread_wrapper, name=name,
args=(target,) + args, kwargs=kwargs)
thr.start()
self.__threads.append(thr)
def _thread_wrapper(self, target, *args, **kwargs):
thr_name = current_thread().name
self.logger.debug('{0} - started'.format(thr_name))
try:
target(*args, **kwargs)
except Exception:
self.__exception_event.set()
self.logger.exception('unhandled exception')
raise
self.logger.debug('{0} - ended'.format(thr_name))
def start_webhook(self,
listen='127.0.0.1',
@@ -120,21 +159,17 @@ class Updater:
Queue: The update queue that can be filled from the main thread
"""
# Create Thread objects
dispatcher_thread = Thread(target=self.dispatcher.start,
name="dispatcher")
event_handler_thread = Thread(target=self._start_webhook,
name="updater",
args=(listen, port, url_path, cert, key))
with self.__lock:
if not self.running:
self.running = True
self.running = True
# Create & start threads
self._init_thread(self.dispatcher.start, "dispatcher"),
self._init_thread(self._start_webhook, "updater", listen,
port, url_path, cert, key)
# Start threads
dispatcher_thread.start()
event_handler_thread.start()
# Return the update queue so the main thread can insert updates
return self.update_queue
# Return the update queue so the main thread can insert updates
return self.update_queue
def _start_polling(self, poll_interval, timeout, network_delay):
"""
@@ -143,7 +178,7 @@ class Updater:
Dispatcher.
"""
current_interval = poll_interval
cur_interval = poll_interval
self.logger.info('Updater thread started')
# Remove webhook
@@ -154,33 +189,41 @@ class Updater:
updates = self.bot.getUpdates(self.last_update_id,
timeout=timeout,
network_delay=network_delay)
except TelegramError as te:
self.logger.error(
"Error while getting Updates: {0}".format(te))
# Put the error into the update queue and let the Dispatcher
# broadcast it
self.update_queue.put(te)
cur_interval = self._increase_poll_interval(cur_interval)
else:
if not self.running:
if len(updates) > 0:
self.logger.info('Updates ignored and will be pulled '
'again on restart.')
break
for update in updates:
self.update_queue.put(update)
self.last_update_id = update.update_id + 1
current_interval = poll_interval
if updates:
for update in updates:
self.update_queue.put(update)
self.last_update_id = updates[-1].update_id + 1
sleep(current_interval)
except TelegramError as te:
# Put the error into the update queue and let the Dispatcher
# broadcast it
self.update_queue.put(te)
sleep(current_interval)
cur_interval = poll_interval
except URLError as e:
self.logger.error("Error while getting Updates: %s" % e)
# increase waiting times on subsequent errors up to 30secs
if current_interval < 30:
current_interval += current_interval / 2
if current_interval > 30:
current_interval = 30
sleep(cur_interval)
self.logger.info('Updater thread stopped')
@staticmethod
def _increase_poll_interval(current_interval):
# increase waiting times on subsequent errors up to 30secs
if current_interval == 0:
current_interval = 1
elif current_interval < 30:
current_interval += current_interval / 2
elif current_interval > 30:
current_interval = 30
return current_interval
def _start_webhook(self, listen, port, url_path, cert, key):
self.logger.info('Updater thread started')
@@ -208,35 +251,64 @@ class Updater:
keyfile=key,
server_side=True)
except ssl.SSLError as error:
self.logger.exception('failed to init SSL socket')
raise TelegramError(str(error))
else:
raise TelegramError('SSL Certificate invalid')
self.httpd.serve_forever(poll_interval=1)
self.logger.info('Updater thread stopped')
def stop(self):
"""
Stops the polling/webhook thread and the dispatcher
Stops the polling/webhook thread, the dispatcher and the job queue
"""
self.logger.info('Stopping Updater and Dispatcher...')
self.logger.debug('This might take a long time if you set a high value'
' as polling timeout.')
self.running = False
self.job_queue.stop()
with self.__lock:
if self.running:
self.logger.info('Stopping Updater and Dispatcher...')
self.running = False
self._stop_httpd()
self._stop_dispatcher()
self._join_threads()
# async threads must be join()ed only after the dispatcher
# thread was joined, otherwise we can still have new async
# threads dispatched
self._join_async_threads()
def _stop_httpd(self):
if self.httpd:
self.logger.info(
'Waiting for current webhook connection to be closed... '
'Send a Telegram message to the bot to exit immediately.')
'Waiting for current webhook connection to be '
'closed... Send a Telegram message to the bot to exit '
'immediately.')
self.httpd.shutdown()
self.httpd = None
def _stop_dispatcher(self):
self.logger.debug("Requesting Dispatcher to stop...")
self.dispatcher.stop()
while dispatcher.running_async > 0:
sleep(1)
self.logger.debug("Dispatcher stopped.")
def _join_async_threads(self):
with dispatcher.async_lock:
threads = list(dispatcher.async_threads)
total = len(threads)
for i, thr in enumerate(threads):
self.logger.info(
'Waiting for async thread {0}/{1} to end'.format(i, total))
thr.join()
self.logger.debug(
'async thread {0}/{1} has ended'.format(i, total))
def _join_threads(self):
for thr in self.__threads:
self.logger.info(
'Waiting for {0} thread to end'.format(thr.name))
thr.join()
self.logger.debug('{0} thread has ended'.format(thr.name))
self.__threads = []
def signal_handler(self, signum, frame):
self.is_idle = False
@@ -244,7 +316,7 @@ class Updater:
def idle(self, stop_signals=(SIGINT, SIGTERM, SIGABRT)):
"""
Waits for the user to press Ctrl-C and stops the updater
Blocks until one of the signals are received and stops the updater
Args:
stop_signals: Iterable containing signals from the signal module
+3 -2
View File
@@ -2,7 +2,8 @@
# pylint: disable=C0103,W0622
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -17,7 +18,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 a object that represents a Telegram User"""
"""This module contains a object that represents a Telegram User."""
from telegram import TelegramObject
+3 -2
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -17,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
UserProfilePhotos"""
UserProfilePhotos."""
from telegram import PhotoSize, TelegramObject
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env python
import logging
from telegram import NullHandler
try:
from urllib.request import urlopen, Request
from urllib.parse import quote
from urllib.error import URLError, HTTPError
except ImportError:
from urllib2 import urlopen, Request
from urllib import quote
from urllib2 import URLError, HTTPError
H = NullHandler()
logging.getLogger(__name__).addHandler(H)
class Botan(object):
"""This class helps to send incoming events in your botan analytics account.
See more: https://github.com/botanio/sdk#botan-sdk"""
token = ''
url_template = 'https://api.botan.io/track?token={token}' \
'&uid={uid}&name={name}&src=python-telegram-bot'
def __init__(self, token):
self.token = token
self.logger = logging.getLogger(__name__)
def track(self, message, event_name='event'):
try:
uid = message.chat_id
except AttributeError:
self.logger.warn('No chat_id in message')
return False
data = message.to_json()
try:
url = self.url_template.format(token=str(self.token),
uid=str(uid),
name=quote(event_name))
request = Request(url,
data=data.encode(),
headers={'Content-Type': 'application/json'})
urlopen(request)
return True
except HTTPError as error:
self.logger.warn('Botan track error ' +
str(error.code) +
':' + error.read().decode('utf-8'))
return False
except URLError as error:
self.logger.warn('Botan track error ' + str(error.reason))
return False
+93 -42
View File
@@ -2,7 +2,8 @@
# pylint: disable=no-name-in-module,unused-import
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -19,19 +20,30 @@
"""This module contains methods to make POST and GET requests"""
import functools
import json
import socket
from ssl import SSLError
try:
from urllib.request import urlopen, urlretrieve, Request
from urllib.error import HTTPError
# python2
from httplib import HTTPException
except ImportError:
# python3
from http.client import HTTPException
try:
# python3
from urllib.request import urlopen, urlretrieve, Request
from urllib.error import HTTPError, URLError
except ImportError:
# python2
from urllib import urlretrieve
from urllib2 import urlopen, Request
from urllib2 import urlopen, Request, URLError
from urllib2 import HTTPError
from telegram import (InputFile, TelegramError)
from telegram.error import Unauthorized, NetworkError, TimedOut
def _parse(json_data):
@@ -45,7 +57,11 @@ def _parse(json_data):
Returns:
A JSON parsed as Python dict with results.
"""
data = json.loads(json_data.decode())
decoded_s = json_data.decode('utf-8')
try:
data = json.loads(decoded_s)
except ValueError:
raise TelegramError('Invalid server response')
if not data.get('ok') and data.get('description'):
return data['description']
@@ -53,6 +69,47 @@ def _parse(json_data):
return data['result']
def _try_except_req(func):
"""Decorator for requests to handle known exceptions"""
@functools.wraps(func)
def decorator(*args, **kwargs):
try:
return func(*args, **kwargs)
except HTTPError as error:
# `HTTPError` inherits from `URLError` so `HTTPError` handling must
# come first.
errcode = error.getcode()
if errcode in (401, 403):
raise Unauthorized()
if errcode == 502:
raise NetworkError('Bad Gateway')
try:
message = _parse(error.read())
except ValueError:
message = 'Unknown HTTPError {0}'.format(error.getcode())
raise NetworkError('{0} ({1})'.format(message, errcode))
except URLError as error:
raise NetworkError('URLError: {0}'.format(error.reason))
except (SSLError, socket.timeout) as error:
err_s = str(error)
if "operation timed out" in err_s:
raise TimedOut()
raise NetworkError(err_s)
except HTTPException as error:
raise NetworkError('HTTPException: {0!r}'.format(error))
return decorator
@_try_except_req
def get(url):
"""Request an URL.
Args:
@@ -67,8 +124,10 @@ def get(url):
return _parse(result)
@_try_except_req
def post(url,
data,
timeout=None,
network_delay=2.):
"""Request an URL.
Args:
@@ -76,54 +135,46 @@ def post(url,
The web location we want to retrieve.
data:
A dict of (str, unicode) key/value pairs.
timeout:
float. If this value is specified, use it as the definitive timeout (in
seconds) for urlopen() operations. [Optional]
network_delay:
Additional timeout in seconds to allow the response from Telegram to
take some time.
float. If using the timeout specified in `data` (which is a timeout for
the Telegram servers operation), then `network_delay` as an extra delay
(in seconds) to compensate for network latency.
default: 2 [Optional]
Notes:
If neither `timeout` nor `data['timeout']` is specified. The underlying
defaults are used.
Returns:
A JSON object.
"""
urlopen_kwargs = {}
# Add time to the timeout of urlopen to allow data to be transferred over
# the network.
if 'timeout' in data:
timeout = data['timeout'] + network_delay
if timeout is not None:
urlopen_kwargs['timeout'] = timeout
elif 'timeout' in data:
urlopen_kwargs['timeout'] = data['timeout'] + network_delay
if InputFile.is_inputfile(data):
data = InputFile(data)
request = Request(url,
data=data.to_form(),
headers=data.headers)
else:
timeout = None
data = json.dumps(data)
request = Request(url,
data=data.encode(),
headers={'Content-Type': 'application/json'})
try:
if InputFile.is_inputfile(data):
data = InputFile(data)
request = Request(url,
data=data.to_form(),
headers=data.headers)
else:
data = json.dumps(data)
request = Request(url,
data=data.encode(),
headers={'Content-Type': 'application/json'})
result = urlopen(request, timeout=timeout).read()
except HTTPError as error:
if error.getcode() == 403:
raise TelegramError('Unauthorized')
if error.getcode() == 502:
raise TelegramError('Bad Gateway')
try:
message = _parse(error.read())
except ValueError:
message = 'Unknown HTTPError'
raise TelegramError(message)
except (SSLError, socket.timeout) as error:
if "operation timed out" in str(error):
raise TelegramError("Timed out")
raise TelegramError(str(error))
result = urlopen(request, **urlopen_kwargs).read()
return _parse(result)
@_try_except_req
def download(url,
filename):
"""Download a file by its URL.
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains functions to validate function arguments"""
try:
type(basestring)
except NameError:
basestring = str
def validate_string(arg, name):
"""
Validate a string argument. Raises a ValueError if `arg` is neither an
instance of basestring (Python 2) or str (Python 3) nor None.
Args:
arg (basestring): The value to be tested
name (str): The name of the argument, for the error message
"""
if not isinstance(arg, basestring) and arg is not None:
raise ValueError(name + " is not a string")
+34 -10
View File
@@ -1,7 +1,7 @@
import logging
from telegram import Update, NullHandler
from future.utils import bytes_to_native_str as n
from future.utils import bytes_to_native_str
from threading import Lock
import json
try:
@@ -14,6 +14,13 @@ H = NullHandler()
logging.getLogger(__name__).addHandler(H)
class _InvalidPost(Exception):
def __init__(self, http_code):
self.http_code = http_code
super(_InvalidPost, self).__init__()
class WebhookServer(BaseHTTPServer.HTTPServer, object):
def __init__(self, server_address, RequestHandlerClass, update_queue,
webhook_path):
@@ -63,12 +70,15 @@ class WebhookHandler(BaseHTTPServer.BaseHTTPRequestHandler, object):
def do_POST(self):
self.logger.debug("Webhook triggered")
if self.path == self.server.webhook_path and \
'content-type' in self.headers and \
'content-length' in self.headers and \
self.headers['content-type'] == 'application/json':
json_string = \
n(self.rfile.read(int(self.headers['content-length'])))
try:
self._validate_post()
clen = self._get_content_len()
except _InvalidPost as e:
self.send_error(e.http_code)
self.end_headers()
else:
buf = self.rfile.read(clen)
json_string = bytes_to_native_str(buf)
self.send_response(200)
self.end_headers()
@@ -80,6 +90,20 @@ class WebhookHandler(BaseHTTPServer.BaseHTTPRequestHandler, object):
update.update_id)
self.server.update_queue.put(update)
else:
self.send_error(403)
self.end_headers()
def _validate_post(self):
if not (self.path == self.server.webhook_path and
'content-type' in self.headers and
self.headers['content-type'] == 'application/json'):
raise _InvalidPost(403)
def _get_content_len(self):
clen = self.headers.get('content-length')
if clen is None:
raise _InvalidPost(411)
try:
clen = int(clen)
except ValueError:
raise _InvalidPost(403)
if clen < 0:
raise _InvalidPost(403)
return clen
+3 -2
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -16,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Video"""
"""This module contains a object that represents a Telegram Video."""
from telegram import PhotoSize, TelegramObject
+3 -2
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
@@ -16,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Voice"""
"""This module contains a object that represents a Telegram Voice."""
from telegram import TelegramObject
+36 -1
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,6 +21,11 @@
import os
import sys
import signal
import traceback
from nose.tools import make_decorator
sys.path.append('.')
import json
@@ -53,3 +59,32 @@ class BaseTest(object):
return True
return False
class TestTimedOut(AssertionError):
def __init__(self, time_limit, frame):
super(TestTimedOut, self).__init__('time_limit={0}'.format(time_limit))
self.time_limit = time_limit
self.frame = frame
def timeout(time_limit):
def decorator(func):
def timed_out(_signum, frame):
raise TestTimedOut(time_limit, frame)
def newfunc(*args, **kwargs):
orig_handler = signal.signal(signal.SIGALRM, timed_out)
signal.alarm(time_limit)
try:
rc = func(*args, **kwargs)
finally:
signal.alarm(0)
signal.signal(signal.SIGALRM, orig_handler)
return rc
newfunc = make_decorator(func)(newfunc)
return newfunc
return decorator
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+42 -33
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,10 +22,12 @@
import os
import unittest
import sys
from flaky import flaky
sys.path.append('.')
import telegram
from tests.base import BaseTest
from tests.base import BaseTest, timeout
class AudioTest(BaseTest, unittest.TestCase):
@@ -33,6 +36,7 @@ class AudioTest(BaseTest, unittest.TestCase):
def setUp(self):
self.audio_file = open('tests/data/telegram.mp3', 'rb')
self.audio_file_id = 'BQADAQADDwADHyP1B6PSPq2HjX8kAg'
self.audio_file_url = 'https://raw.githubusercontent.com/python-telegram-bot/python-telegram-bot/master/tests/data/telegram.mp3'
self.duration = 4
self.performer = 'Leandro Toledo'
self.title = 'Teste'
@@ -48,10 +52,9 @@ class AudioTest(BaseTest, unittest.TestCase):
'file_size': self.file_size
}
@flaky(3, 1)
@timeout(10)
def test_send_audio_required_args_only(self):
"""Test telegram.Bot sendAudio method"""
print('Testing bot.sendAudio - With required arguments only')
message = self._bot.sendAudio(self._chat_id,
self.audio_file)
@@ -59,16 +62,15 @@ class AudioTest(BaseTest, unittest.TestCase):
self.assertTrue(isinstance(audio.file_id, str))
self.assertNotEqual(audio.file_id, '')
self.assertEqual(audio.duration, 4)
# self.assertEqual(audio.duration, 4)
self.assertEqual(audio.performer, '')
self.assertEqual(audio.title, '')
self.assertEqual(audio.mime_type, self.mime_type)
self.assertEqual(audio.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_audio_all_args(self):
"""Test telegram.Bot sendAudio method"""
print('Testing bot.sendAudio - With all arguments')
message = self._bot.sendAudio(self._chat_id,
self.audio_file,
duration=self.duration,
@@ -87,10 +89,9 @@ class AudioTest(BaseTest, unittest.TestCase):
self.assertEqual(audio.mime_type, self.mime_type)
self.assertEqual(audio.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_audio_mp3_file(self):
"""Test telegram.Bot sendAudio method"""
print('Testing bot.sendAudio - MP3 File')
message = self._bot.sendAudio(chat_id=self._chat_id,
audio=self.audio_file,
duration=self.duration,
@@ -107,10 +108,9 @@ class AudioTest(BaseTest, unittest.TestCase):
self.assertEqual(audio.mime_type, self.mime_type)
self.assertEqual(audio.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_audio_mp3_file_custom_filename(self):
"""Test telegram.Bot sendAudio method"""
print('Testing bot.sendAudio - MP3 File with custom filename')
message = self._bot.sendAudio(chat_id=self._chat_id,
audio=self.audio_file,
duration=self.duration,
@@ -128,10 +128,28 @@ class AudioTest(BaseTest, unittest.TestCase):
self.assertEqual(audio.mime_type, self.mime_type)
self.assertEqual(audio.file_size, self.file_size)
def test_send_audio_resend(self):
"""Test telegram.Bot sendAudio method"""
print('Testing bot.sendAudio - Resend by file_id')
@flaky(3, 1)
@timeout(10)
def test_send_audio_mp3_url_file(self):
message = self._bot.sendAudio(chat_id=self._chat_id,
audio=self.audio_file_url,
duration=self.duration,
performer=self.performer,
title=self.title)
audio = message.audio
self.assertTrue(isinstance(audio.file_id, str))
self.assertNotEqual(audio.file_id, '')
self.assertEqual(audio.duration, self.duration)
self.assertEqual(audio.performer, self.performer)
self.assertEqual(audio.title, self.title)
self.assertEqual(audio.mime_type, self.mime_type)
self.assertEqual(audio.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_audio_resend(self):
message = self._bot.sendAudio(chat_id=self._chat_id,
audio=self.audio_file_id,
duration=self.duration,
@@ -147,9 +165,6 @@ class AudioTest(BaseTest, unittest.TestCase):
self.assertEqual(audio.mime_type, self.mime_type)
def test_audio_de_json(self):
"""Test Audio.de_json() method"""
print('Testing Audio.de_json()')
audio = telegram.Audio.de_json(self.json_dict)
self.assertEqual(audio.file_id, self.audio_file_id)
@@ -160,17 +175,11 @@ class AudioTest(BaseTest, unittest.TestCase):
self.assertEqual(audio.file_size, self.file_size)
def test_audio_to_json(self):
"""Test Audio.to_json() method"""
print('Testing Audio.to_json()')
audio = telegram.Audio.de_json(self.json_dict)
self.assertTrue(self.is_json(audio.to_json()))
def test_audio_to_dict(self):
"""Test Audio.to_dict() method"""
print('Testing Audio.to_dict()')
audio = telegram.Audio.de_json(self.json_dict)
self.assertTrue(self.is_dict(audio.to_dict()))
@@ -181,9 +190,9 @@ class AudioTest(BaseTest, unittest.TestCase):
self.assertEqual(audio['mime_type'], self.mime_type)
self.assertEqual(audio['file_size'], self.file_size)
@flaky(3, 1)
@timeout(10)
def test_error_send_audio_empty_file(self):
print('Testing bot.sendAudio - Null file')
json_dict = self.json_dict
del(json_dict['file_id'])
@@ -193,9 +202,9 @@ class AudioTest(BaseTest, unittest.TestCase):
lambda: self._bot.sendAudio(chat_id=self._chat_id,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_error_send_audio_empty_file_id(self):
print('Testing bot.sendAudio - Empty file_id')
json_dict = self.json_dict
del(json_dict['file_id'])
@@ -205,9 +214,9 @@ class AudioTest(BaseTest, unittest.TestCase):
lambda: self._bot.sendAudio(chat_id=self._chat_id,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_error_audio_without_required_args(self):
print('Testing bot.sendAudio - Without required arguments')
json_dict = self.json_dict
del(json_dict['file_id'])
+57 -26
View File
@@ -2,7 +2,8 @@
# encoding: utf-8
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,21 +21,27 @@
"""This module contains a object that represents Tests for Telegram Bot"""
import os
import unittest
from datetime import datetime
import sys
from flaky import flaky
if sys.version_info[0:2] == (2, 6):
import unittest2 as unittest
else:
import unittest
sys.path.append('.')
import telegram
from tests.base import BaseTest
from tests.base import BaseTest, timeout
class BotTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram Bot."""
@flaky(3, 1)
@timeout(10)
def testGetMe(self):
'''Test the telegram.Bot getMe method'''
print('Testing getMe')
bot = self._bot.getMe()
self.assertTrue(self.is_json(bot.to_json()))
@@ -44,9 +51,9 @@ class BotTest(BaseTest, unittest.TestCase):
self.assertEqual(bot.username, 'PythonTelegramBot')
self.assertEqual(bot.name, '@PythonTelegramBot')
@flaky(3, 1)
@timeout(10)
def testSendMessage(self):
'''Test the telegram.Bot sendMessage method'''
print('Testing sendMessage')
message = self._bot.sendMessage(chat_id=self._chat_id,
text='Моё судно на воздушной подушке полно угрей')
@@ -54,18 +61,18 @@ class BotTest(BaseTest, unittest.TestCase):
self.assertEqual(message.text, u'Моё судно на воздушной подушке полно угрей')
self.assertTrue(isinstance(message.date, datetime))
@flaky(3, 1)
@timeout(10)
def testGetUpdates(self):
'''Test the telegram.Bot getUpdates method'''
print('Testing getUpdates')
updates = self._bot.getUpdates()
if updates:
self.assertTrue(self.is_json(updates[0].to_json()))
self.assertTrue(isinstance(updates[0], telegram.Update))
@flaky(3, 1)
@timeout(10)
def testForwardMessage(self):
'''Test the telegram.Bot forwardMessage method'''
print('Testing forwardMessage')
message = self._bot.forwardMessage(chat_id=self._chat_id,
from_chat_id=self._chat_id,
message_id=2398)
@@ -75,9 +82,9 @@ class BotTest(BaseTest, unittest.TestCase):
self.assertEqual(message.forward_from.username, 'leandrotoledo')
self.assertTrue(isinstance(message.forward_date, datetime))
@flaky(3, 1)
@timeout(10)
def testSendPhoto(self):
'''Test the telegram.Bot sendPhoto method'''
print('Testing sendPhoto - File')
message = self._bot.sendPhoto(photo=open('tests/data/telegram.png', 'rb'),
caption='testSendPhoto',
chat_id=self._chat_id)
@@ -86,56 +93,80 @@ class BotTest(BaseTest, unittest.TestCase):
self.assertEqual(message.photo[0].file_size, 1451)
self.assertEqual(message.caption, 'testSendPhoto')
@flaky(3, 1)
@timeout(10)
def testResendPhoto(self):
'''Test the telegram.Bot sendPhoto method'''
print('Testing sendPhoto - Resend')
message = self._bot.sendPhoto(photo='AgADAQADyKcxGx8j9Qdp6d-gpUsw4Gja1i8ABEVJsVqQk8LfJ3wAAgI',
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.photo[0].file_id, 'AgADAQADyKcxGx8j9Qdp6d-gpUsw4Gja1i8ABEVJsVqQk8LfJ3wAAgI')
@flaky(3, 1)
@timeout(10)
def testSendJPGURLPhoto(self):
'''Test the telegram.Bot sendPhoto method'''
print('Testing testSendJPGURLPhoto - URL')
message = self._bot.sendPhoto(photo='http://dummyimage.com/600x400/000/fff.jpg&text=telegram',
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.photo[0].file_size, 822)
@flaky(3, 1)
@timeout(10)
def testSendPNGURLPhoto(self):
'''Test the telegram.Bot sendPhoto method'''
print('Testing testSendPNGURLPhoto - URL')
message = self._bot.sendPhoto(photo='http://dummyimage.com/600x400/000/fff.png&text=telegram',
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.photo[0].file_size, 684)
@flaky(3, 1)
@timeout(10)
def testSendGIFURLPhoto(self):
'''Test the telegram.Bot sendPhoto method'''
print('Testing testSendGIFURLPhoto - URL')
message = self._bot.sendPhoto(photo='http://dummyimage.com/600x400/000/fff.gif&text=telegram',
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.photo[0].file_size, 684)
@flaky(3, 1)
@timeout(10)
def testSendChatAction(self):
'''Test the telegram.Bot sendChatAction method'''
print('Testing sendChatAction - ChatAction.TYPING')
self._bot.sendChatAction(action=telegram.ChatAction.TYPING,
chat_id=self._chat_id)
@flaky(3, 1)
@timeout(10)
def testGetUserProfilePhotos(self):
'''Test the telegram.Bot getUserProfilePhotos method'''
print('Testing getUserProfilePhotos')
upf = self._bot.getUserProfilePhotos(user_id=self._chat_id)
self.assertTrue(self.is_json(upf.to_json()))
self.assertEqual(upf.photos[0][0].file_size, 12421)
def _test_invalid_token(self, token):
self.assertRaisesRegexp(telegram.error.InvalidToken, 'Invalid token', telegram.Bot, token)
def testInvalidToken1(self):
self._test_invalid_token('123')
def testInvalidToken2(self):
self._test_invalid_token('12a:')
def testInvalidToken3(self):
self._test_invalid_token('12:')
def testUnauthToken(self):
with self.assertRaisesRegexp(telegram.error.Unauthorized, 'Unauthorized'):
bot = telegram.Bot('1234:abcd1234')
bot.getMe()
def testInvalidSrvResp(self):
with self.assertRaisesRegexp(telegram.TelegramError, 'Invalid server response'):
# bypass the valid token check
bot_cls = type('bot_cls', (telegram.Bot, ), {'_valid_token': lambda self, token: token})
bot = bot_cls('12')
bot.getMe()
if __name__ == '__main__':
unittest.main()
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env python
"""This module contains a object that represents Tests for Botan analytics integration"""
import os
import unittest
import sys
from flaky import flaky
sys.path.append('.')
from telegram.utils.botan import Botan
from tests.base import BaseTest
class MessageMock(object):
chat_id = None
def __init__(self, chat_id):
self.chat_id = chat_id
def to_json(self):
return "{}"
@flaky(3, 1)
class BotanTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Botan analytics integration."""
token = os.environ.get('BOTAN_TOKEN')
def test_track(self):
botan = Botan(self.token)
message = MessageMock(self._chat_id)
result = botan.track(message, 'named event')
self.assertTrue(result)
def test_track_fail(self):
botan = Botan(self.token)
botan.url_template = 'https://api.botan.io/traccc?token={token}&uid={uid}&name={name}'
message = MessageMock(self._chat_id)
result = botan.track(message, 'named event')
self.assertFalse(result)
def test_wrong_message(self):
botan = Botan(self.token)
message = MessageMock(self._chat_id)
message = delattr(message, 'chat_id')
result = botan.track(message, 'named event')
self.assertFalse(result)
def test_wrong_endpoint(self):
botan = Botan(self.token)
botan.url_template = 'https://api.botaaaaan.io/traccc?token={token}&uid={uid}&name={name}'
message = MessageMock(self._chat_id)
result = botan.track(message, 'named event')
self.assertFalse(result)
if __name__ == '__main__':
unittest.main()
+2 -13
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -42,17 +43,11 @@ class ChatTest(BaseTest, unittest.TestCase):
}
def test_group_chat_de_json_empty_json(self):
"""Test Chat.de_json() method"""
print('Testing Chat.de_json() - Empty JSON')
group_chat = telegram.Chat.de_json({})
self.assertEqual(group_chat, None)
def test_group_chat_de_json(self):
"""Test Chat.de_json() method"""
print('Testing Chat.de_json()')
group_chat = telegram.Chat.de_json(self.json_dict)
self.assertEqual(group_chat.id, self.id)
@@ -60,17 +55,11 @@ class ChatTest(BaseTest, unittest.TestCase):
self.assertEqual(group_chat.type, self.type)
def test_group_chat_to_json(self):
"""Test Chat.to_json() method"""
print('Testing Chat.to_json()')
group_chat = telegram.Chat.de_json(self.json_dict)
self.assertTrue(self.is_json(group_chat.to_json()))
def test_group_chat_to_dict(self):
"""Test Chat.to_dict() method"""
print('Testing Chat.to_dict()')
group_chat = telegram.Chat.de_json(self.json_dict)
self.assertTrue(self.is_dict(group_chat.to_dict()))
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents Tests for Telegram
ChosenInlineResult"""
import sys
if sys.version_info[0:2] == (2, 6):
import unittest2 as unittest
else:
import unittest
sys.path.append('.')
import telegram
from tests.base import BaseTest
class ChosenInlineResultTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram ChosenInlineResult."""
def setUp(self):
user = telegram.User(1, 'First name')
self.result_id = 'result id'
self.from_user = user
self.query = 'query text'
self.json_dict = {
'result_id': self.result_id,
'from': self.from_user.to_dict(),
'query': self.query
}
def test_choseninlineresult_de_json(self):
result = telegram.ChosenInlineResult.de_json(self.json_dict)
self.assertEqual(result.result_id, self.result_id)
self.assertDictEqual(result.from_user.to_dict(),
self.from_user.to_dict())
self.assertEqual(result.query, self.query)
def test_choseninlineresult_to_json(self):
result = telegram.ChosenInlineResult.de_json(self.json_dict)
self.assertTrue(self.is_json(result.to_json()))
def test_choseninlineresult_to_dict(self):
result = telegram.ChosenInlineResult.de_json(self.json_dict).to_dict()
self.assertTrue(self.is_dict(result))
self.assertEqual(result['result_id'], self.result_id)
self.assertEqual(result['from'], self.from_user.to_dict())
self.assertEqual(result['query'], self.query)
if __name__ == '__main__':
unittest.main()
+2 -10
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -44,9 +45,6 @@ class ContactTest(BaseTest, unittest.TestCase):
}
def test_contact_de_json(self):
"""Test Contact.de_json() method"""
print('Testing Contact.de_json()')
contact = telegram.Contact.de_json(self.json_dict)
self.assertEqual(contact.phone_number, self.phone_number)
@@ -55,17 +53,11 @@ class ContactTest(BaseTest, unittest.TestCase):
self.assertEqual(contact.user_id, self.user_id)
def test_contact_to_json(self):
"""Test Contact.to_json() method"""
print('Testing Contact.to_json()')
contact = telegram.Contact.de_json(self.json_dict)
self.assertTrue(self.is_json(contact.to_json()))
def test_contact_to_dict(self):
"""Test Contact.to_dict() method"""
print('Testing Contact.to_dict()')
contact = telegram.Contact.de_json(self.json_dict)
self.assertTrue(self.is_dict(contact.to_dict()))
+21 -31
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,10 +22,12 @@
import os
import unittest
import sys
from flaky import flaky
sys.path.append('.')
import telegram
from tests.base import BaseTest
from tests.base import BaseTest, timeout
class DocumentTest(BaseTest, unittest.TestCase):
@@ -33,7 +36,7 @@ class DocumentTest(BaseTest, unittest.TestCase):
def setUp(self):
self.document_file = open('tests/data/telegram.png', 'rb')
self.document_file_id = 'BQADAQADpAADHyP1B04ipZxJTe2BAg'
self.document_file_url = 'http://dummyimage.com/600x400/000/fff.gif&text=telegram'
self.document_file_url = 'https://raw.githubusercontent.com/python-telegram-bot/python-telegram-bot/master/tests/data/telegram.gif'
self.thumb = {'width': 90,
'height': 90,
'file_id': 'BQADAQADoQADHyP1B0mzJMVyzcB0Ag',
@@ -50,10 +53,9 @@ class DocumentTest(BaseTest, unittest.TestCase):
'file_size': self.file_size
}
@flaky(3, 1)
@timeout(10)
def test_send_document_png_file(self):
"""Test telegram.Bot sendDocument method"""
print('Testing bot.sendDocument - PNG File')
message = self._bot.sendDocument(self._chat_id,
self.document_file)
@@ -66,10 +68,9 @@ class DocumentTest(BaseTest, unittest.TestCase):
self.assertEqual(document.mime_type, self.mime_type)
self.assertEqual(document.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_document_png_file_with_custom_file_name(self):
"""Test telegram.Bot sendDocument method"""
print('Testing bot.sendDocument - PNG File with custom filename')
message = self._bot.sendDocument(self._chat_id,
self.document_file,
filename='telegram_custom.png')
@@ -83,10 +84,9 @@ class DocumentTest(BaseTest, unittest.TestCase):
self.assertEqual(document.mime_type, self.mime_type)
self.assertEqual(document.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_document_url_gif_file(self):
"""Test telegram.Bot sendDocument method"""
print('Testing bot.sendDocument - GIF File by URL')
message = self._bot.sendDocument(self._chat_id,
self.document_file_url)
@@ -95,14 +95,13 @@ class DocumentTest(BaseTest, unittest.TestCase):
self.assertTrue(isinstance(document.file_id, str))
self.assertNotEqual(document.file_id, '')
self.assertTrue(isinstance(document.thumb, telegram.PhotoSize))
self.assertEqual(document.file_name, 'image.gif')
self.assertEqual(document.file_name, 'telegram.gif')
self.assertEqual(document.mime_type, 'image/gif')
self.assertEqual(document.file_size, 3878)
@flaky(3, 1)
@timeout(10)
def test_send_document_resend(self):
"""Test telegram.Bot sendDocument method"""
print('Testing bot.sendDocument - Resend by file_id')
message = self._bot.sendDocument(chat_id=self._chat_id,
document=self.document_file_id)
@@ -114,9 +113,6 @@ class DocumentTest(BaseTest, unittest.TestCase):
self.assertEqual(document.mime_type, self.mime_type)
def test_document_de_json(self):
"""Test Document.de_json() method"""
print('Testing Document.de_json()')
document = telegram.Document.de_json(self.json_dict)
self.assertEqual(document.file_id, self.document_file_id)
@@ -126,17 +122,11 @@ class DocumentTest(BaseTest, unittest.TestCase):
self.assertEqual(document.file_size, self.file_size)
def test_document_to_json(self):
"""Test Document.to_json() method"""
print('Testing Document.to_json()')
document = telegram.Document.de_json(self.json_dict)
self.assertTrue(self.is_json(document.to_json()))
def test_document_to_dict(self):
"""Test Document.to_dict() method"""
print('Testing Document.to_dict()')
document = telegram.Document.de_json(self.json_dict)
self.assertTrue(self.is_dict(document.to_dict()))
@@ -146,9 +136,9 @@ class DocumentTest(BaseTest, unittest.TestCase):
self.assertEqual(document['mime_type'], self.mime_type)
self.assertEqual(document['file_size'], self.file_size)
@flaky(3, 1)
@timeout(10)
def test_error_send_document_empty_file(self):
print('Testing bot.sendDocument - Null file')
json_dict = self.json_dict
del(json_dict['file_id'])
@@ -158,9 +148,9 @@ class DocumentTest(BaseTest, unittest.TestCase):
lambda: self._bot.sendDocument(chat_id=self._chat_id,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_error_send_document_empty_file_id(self):
print('Testing bot.sendDocument - Empty file_id')
json_dict = self.json_dict
del(json_dict['file_id'])
@@ -170,9 +160,9 @@ class DocumentTest(BaseTest, unittest.TestCase):
lambda: self._bot.sendDocument(chat_id=self._chat_id,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_error_document_without_required_args(self):
print('Testing bot.sendDocument - Without required arguments')
json_dict = self.json_dict
del(json_dict['file_id'])
+2 -4
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -32,9 +33,6 @@ class EmojiTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram Emoji."""
def test_emoji(self):
"""Test Emoji class"""
print('Testing Emoji class')
for attr in dir(Emoji):
if attr[0] != '_': # TODO: dirty way to filter out functions
self.assertTrue(type(getattr(Emoji, attr)) is str)
+2 -30
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -45,9 +46,6 @@ class FileTest(BaseTest, unittest.TestCase):
}
def test_get_and_download_file_audio(self):
"""Test telegram.Bot getFile method - Audio"""
print('Testing bot.getFile - With Audio.file_id')
newFile = self._bot.getFile(self.audio_file_id)
self.assertEqual(newFile.file_size, 28232)
@@ -59,9 +57,6 @@ class FileTest(BaseTest, unittest.TestCase):
self.assertTrue(os.path.isfile('telegram.mp3'))
def test_get_and_download_file_document(self):
"""Test telegram.Bot getFile method - Document"""
print('Testing bot.getFile - With Document.file_id')
newFile = self._bot.getFile(self.document_file_id)
self.assertEqual(newFile.file_size, 12948)
@@ -73,9 +68,6 @@ class FileTest(BaseTest, unittest.TestCase):
self.assertTrue(os.path.isfile('telegram.png'))
def test_get_and_download_file_sticker(self):
"""Test telegram.Bot getFile method - Sticker"""
print('Testing bot.getFile - With Sticker.file_id')
newFile = self._bot.getFile(self.sticker_file_id)
self.assertEqual(newFile.file_size, 39518)
@@ -87,9 +79,6 @@ class FileTest(BaseTest, unittest.TestCase):
self.assertTrue(os.path.isfile('telegram.webp'))
def test_get_and_download_file_video(self):
"""Test telegram.Bot getFile method - Video"""
print('Testing bot.getFile - With Video.file_id')
newFile = self._bot.getFile(self.video_file_id)
self.assertEqual(newFile.file_size, 326534)
@@ -101,9 +90,6 @@ class FileTest(BaseTest, unittest.TestCase):
self.assertTrue(os.path.isfile('telegram.mp4'))
def test_get_and_download_file_voice(self):
"""Test telegram.Bot getFile method - Voice"""
print('Testing bot.getFile - With Voice.file_id')
newFile = self._bot.getFile(self.voice_file_id)
self.assertEqual(newFile.file_size, 9199)
@@ -115,9 +101,6 @@ class FileTest(BaseTest, unittest.TestCase):
self.assertTrue(os.path.isfile('telegram.ogg'))
def test_file_de_json(self):
"""Test File.de_json() method"""
print('Testing File.de_json()')
newFile = telegram.File.de_json(self.json_dict)
self.assertEqual(newFile.file_id, self.json_dict['file_id'])
@@ -125,17 +108,11 @@ class FileTest(BaseTest, unittest.TestCase):
self.assertEqual(newFile.file_size, self.json_dict['file_size'])
def test_file_to_json(self):
"""Test File.to_json() method"""
print('Testing File.to_json()')
newFile = telegram.File.de_json(self.json_dict)
self.assertTrue(self.is_json(newFile.to_json()))
def test_file_to_dict(self):
"""Test File.to_dict() method"""
print('Testing File.to_dict()')
newFile = telegram.File.de_json(self.json_dict)
self.assertTrue(self.is_dict(newFile.to_dict()))
@@ -144,10 +121,7 @@ class FileTest(BaseTest, unittest.TestCase):
self.assertEqual(newFile['file_size'], self.json_dict['file_size'])
def test_error_get_empty_file_id(self):
print('Testing bot.getFile - Null file_id')
json_dict = self.json_dict
json_dict['file_id'] = ''
del(json_dict['file_path'])
del(json_dict['file_size'])
@@ -156,8 +130,6 @@ class FileTest(BaseTest, unittest.TestCase):
lambda: self._bot.getFile(**json_dict))
def test_error_file_without_required_args(self):
print('Testing bot.getFile - Without required arguments')
json_dict = self.json_dict
del(json_dict['file_id'])
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env python
# encoding: utf-8
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents Tests for Telegram ForceReply"""
import unittest
import sys
sys.path.append('.')
import telegram
from tests.base import BaseTest
class ForceReplyTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram ForceReply."""
def setUp(self):
self.force_reply = True
self.selective = True
self.json_dict = {
'force_reply': self.force_reply,
'selective': self.selective,
}
def test_send_message_with_force_reply(self):
message = self._bot.sendMessage(self._chat_id,
'Моё судно на воздушной подушке полно угрей',
reply_markup=telegram.ForceReply.de_json(self.json_dict))
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.text, u'Моё судно на воздушной подушке полно угрей')
def test_force_reply_de_json(self):
force_reply = telegram.ForceReply.de_json(self.json_dict)
self.assertEqual(force_reply.force_reply, self.force_reply)
self.assertEqual(force_reply.selective, self.selective)
def test_force_reply_to_json(self):
force_reply = telegram.ForceReply.de_json(self.json_dict)
self.assertTrue(self.is_json(force_reply.to_json()))
def test_force_reply_to_dict(self):
force_reply = telegram.ForceReply.de_json(self.json_dict)
self.assertEqual(force_reply['force_reply'], self.force_reply)
self.assertEqual(force_reply['selective'], self.selective)
if __name__ == '__main__':
unittest.main()
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents Tests for Telegram
InlineQuery"""
import sys
if sys.version_info[0:2] == (2, 6):
import unittest2 as unittest
else:
import unittest
sys.path.append('.')
import telegram
from tests.base import BaseTest
class InlineQueryTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram InlineQuery."""
def setUp(self):
user = telegram.User(1, 'First name')
self.id = 'id'
self.from_user = user
self.query = 'query text'
self.offset = 'offset'
self.json_dict = {
'id': self.id,
'from': self.from_user.to_dict(),
'query': self.query,
'offset': self.offset
}
def test_inlinequery_de_json(self):
inlinequery = telegram.InlineQuery.de_json(self.json_dict)
self.assertEqual(inlinequery.id, self.id)
self.assertDictEqual(inlinequery.from_user.to_dict(),
self.from_user.to_dict())
self.assertEqual(inlinequery.query, self.query)
self.assertEqual(inlinequery.offset, self.offset)
def test_inlinequery_to_json(self):
inlinequery = telegram.InlineQuery.de_json(self.json_dict)
self.assertTrue(self.is_json(inlinequery.to_json()))
def test_inlinequery_to_dict(self):
inlinequery = telegram.InlineQuery.de_json(self.json_dict).to_dict()
self.assertTrue(self.is_dict(inlinequery))
self.assertDictEqual(inlinequery, self.json_dict)
if __name__ == '__main__':
unittest.main()
+346
View File
@@ -0,0 +1,346 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents Tests for Telegram
InlineResults"""
import sys
if sys.version_info[0:2] == (2, 6):
import unittest2 as unittest
else:
import unittest
sys.path.append('.')
import telegram
from tests.base import BaseTest
class InlineQueryResultArticleTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram InlineQueryResultArticle."""
def setUp(self):
self.id = 'id'
self.type = 'article'
self.title = 'title'
self.message_text = 'message text'
self.parse_mode = 'HTML'
self.disable_web_page_preview = True
self.url = 'url'
self.hide_url = True
self.description = 'description'
self.thumb_url = 'thumb url'
self.thumb_height = 10
self.thumb_width = 15
self.json_dict = {
'type': self.type,
'id': self.id,
'title': self.title,
'message_text': self.message_text,
'parse_mode': self.parse_mode,
'disable_web_page_preview': self.disable_web_page_preview,
'url': self.url,
'hide_url': self.hide_url,
'description': self.description,
'thumb_url': self.thumb_url,
'thumb_height': self.thumb_height,
'thumb_width': self.thumb_width
}
def test_article_de_json(self):
article = telegram.InlineQueryResultArticle.de_json(self.json_dict)
self.assertEqual(article.type, self.type)
self.assertEqual(article.id, self.id)
self.assertEqual(article.title, self.title)
self.assertEqual(article.message_text, self.message_text)
self.assertEqual(article.parse_mode, self.parse_mode)
self.assertEqual(article.disable_web_page_preview,
self.disable_web_page_preview)
self.assertEqual(article.url, self.url)
self.assertEqual(article.hide_url, self.hide_url)
self.assertEqual(article.description, self.description)
self.assertEqual(article.thumb_url, self.thumb_url)
self.assertEqual(article.thumb_height, self.thumb_height)
self.assertEqual(article.thumb_width, self.thumb_width)
def test_article_to_json(self):
article = telegram.InlineQueryResultArticle.de_json(self.json_dict)
self.assertTrue(self.is_json(article.to_json()))
def test_article_to_dict(self):
article = \
telegram.InlineQueryResultArticle.de_json(self.json_dict).to_dict()
self.assertTrue(self.is_dict(article))
self.assertDictEqual(self.json_dict, article)
class InlineQueryResultPhotoTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram InlineQueryResultPhoto."""
def setUp(self):
self.id = 'id'
self.type = 'photo'
self.photo_url = 'photo url'
self.mime_type = 'mime type'
self.photo_width = 10
self.photo_height = 15
self.thumb_url = 'thumb url'
self.title = 'title'
self.caption = 'caption'
self.message_text = 'message text'
self.parse_mode = 'parse mode'
self.disable_web_page_preview = True
self.json_dict = {
'type': self.type,
'id': self.id,
'photo_url': self.photo_url,
'mime_type': self.mime_type,
'photo_width': self.photo_width,
'photo_height': self.photo_height,
'thumb_url': self.thumb_url,
'title': self.title,
'caption': self.caption,
'message_text': self.message_text,
'parse_mode': self.parse_mode,
'disable_web_page_preview': self.disable_web_page_preview
}
def test_photo_de_json(self):
photo = telegram.InlineQueryResultPhoto.de_json(self.json_dict)
self.assertEqual(photo.type, self.type)
self.assertEqual(photo.id, self.id)
self.assertEqual(photo.photo_url, self.photo_url)
self.assertEqual(photo.mime_type, self.mime_type)
self.assertEqual(photo.photo_width, self.photo_width)
self.assertEqual(photo.photo_height, self.photo_height)
self.assertEqual(photo.thumb_url, self.thumb_url)
self.assertEqual(photo.title, self.title)
self.assertEqual(photo.caption, self.caption)
self.assertEqual(photo.message_text, self.message_text)
self.assertEqual(photo.parse_mode, self.parse_mode)
self.assertEqual(photo.disable_web_page_preview,
self.disable_web_page_preview)
def test_photo_to_json(self):
photo = telegram.InlineQueryResultPhoto.de_json(self.json_dict)
self.assertTrue(self.is_json(photo.to_json()))
def test_photo_to_dict(self):
photo = \
telegram.InlineQueryResultPhoto.de_json(self.json_dict).to_dict()
self.assertTrue(self.is_dict(photo))
self.assertDictEqual(self.json_dict, photo)
class InlineQueryResultGifTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram InlineQueryResultGif."""
def setUp(self):
self.id = 'id'
self.type = 'gif'
self.gif_url = 'gif url'
self.gif_width = 10
self.gif_height = 15
self.thumb_url = 'thumb url'
self.title = 'title'
self.caption = 'caption'
self.message_text = 'message text'
self.parse_mode = 'parse mode'
self.disable_web_page_preview = True
self.json_dict = {
'type': self.type,
'id': self.id,
'gif_url': self.gif_url,
'gif_width': self.gif_width,
'gif_height': self.gif_height,
'thumb_url': self.thumb_url,
'title': self.title,
'caption': self.caption,
'message_text': self.message_text,
'parse_mode': self.parse_mode,
'disable_web_page_preview': self.disable_web_page_preview
}
def test_gif_de_json(self):
gif = telegram.InlineQueryResultGif.de_json(self.json_dict)
self.assertEqual(gif.type, self.type)
self.assertEqual(gif.id, self.id)
self.assertEqual(gif.gif_url, self.gif_url)
self.assertEqual(gif.gif_width, self.gif_width)
self.assertEqual(gif.gif_height, self.gif_height)
self.assertEqual(gif.thumb_url, self.thumb_url)
self.assertEqual(gif.title, self.title)
self.assertEqual(gif.caption, self.caption)
self.assertEqual(gif.message_text, self.message_text)
self.assertEqual(gif.parse_mode, self.parse_mode)
self.assertEqual(gif.disable_web_page_preview,
self.disable_web_page_preview)
def test_gif_to_json(self):
gif = telegram.InlineQueryResultGif.de_json(self.json_dict)
self.assertTrue(self.is_json(gif.to_json()))
def test_gif_to_dict(self):
gif = telegram.InlineQueryResultGif.de_json(self.json_dict).to_dict()
self.assertTrue(self.is_dict(gif))
self.assertDictEqual(self.json_dict, gif)
class InlineQueryResultMpeg4GifTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram InlineQueryResultMpeg4Gif."""
def setUp(self):
self.id = 'id'
self.type = 'mpeg4_gif'
self.mpeg4_url = 'mpeg4 url'
self.mpeg4_width = 10
self.mpeg4_height = 15
self.thumb_url = 'thumb url'
self.title = 'title'
self.caption = 'caption'
self.message_text = 'message text'
self.parse_mode = 'parse mode'
self.disable_web_page_preview = True
self.json_dict = {
'type': self.type,
'id': self.id,
'mpeg4_url': self.mpeg4_url,
'mpeg4_width': self.mpeg4_width,
'mpeg4_height': self.mpeg4_height,
'thumb_url': self.thumb_url,
'title': self.title,
'caption': self.caption,
'message_text': self.message_text,
'parse_mode': self.parse_mode,
'disable_web_page_preview': self.disable_web_page_preview
}
def test_mpeg4_de_json(self):
mpeg4 = telegram.InlineQueryResultMpeg4Gif.de_json(self.json_dict)
self.assertEqual(mpeg4.type, self.type)
self.assertEqual(mpeg4.id, self.id)
self.assertEqual(mpeg4.mpeg4_url, self.mpeg4_url)
self.assertEqual(mpeg4.mpeg4_width, self.mpeg4_width)
self.assertEqual(mpeg4.mpeg4_height, self.mpeg4_height)
self.assertEqual(mpeg4.thumb_url, self.thumb_url)
self.assertEqual(mpeg4.title, self.title)
self.assertEqual(mpeg4.caption, self.caption)
self.assertEqual(mpeg4.message_text, self.message_text)
self.assertEqual(mpeg4.parse_mode, self.parse_mode)
self.assertEqual(mpeg4.disable_web_page_preview,
self.disable_web_page_preview)
def test_mpeg4_to_json(self):
mpeg4 = telegram.InlineQueryResultMpeg4Gif.de_json(self.json_dict)
self.assertTrue(self.is_json(mpeg4.to_json()))
def test_mpeg4_to_dict(self):
mpeg4 = \
telegram.InlineQueryResultMpeg4Gif.de_json(self.json_dict).to_dict()
self.assertTrue(self.is_dict(mpeg4))
self.assertDictEqual(self.json_dict, mpeg4)
class InlineQueryResultVideoTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram InlineQueryResultVideo."""
def setUp(self):
self.id = 'id'
self.type = 'video'
self.video_url = 'video url'
self.mime_type = 'mime type'
self.video_width = 10
self.video_height = 15
self.video_duration = 15
self.thumb_url = 'thumb url'
self.title = 'title'
self.caption = 'caption'
self.description = 'description'
self.message_text = 'message text'
self.parse_mode = 'parse mode'
self.disable_web_page_preview = True
self.json_dict = {
'type': self.type,
'id': self.id,
'video_url': self.video_url,
'mime_type': self.mime_type,
'video_width': self.video_width,
'video_height': self.video_height,
'video_duration': self.video_duration,
'thumb_url': self.thumb_url,
'title': self.title,
'caption': self.caption,
'description': self.description,
'message_text': self.message_text,
'parse_mode': self.parse_mode,
'disable_web_page_preview': self.disable_web_page_preview
}
def test_video_de_json(self):
video = telegram.InlineQueryResultVideo.de_json(self.json_dict)
self.assertEqual(video.type, self.type)
self.assertEqual(video.id, self.id)
self.assertEqual(video.video_url, self.video_url)
self.assertEqual(video.mime_type, self.mime_type)
self.assertEqual(video.video_width, self.video_width)
self.assertEqual(video.video_height, self.video_height)
self.assertEqual(video.video_duration, self.video_duration)
self.assertEqual(video.thumb_url, self.thumb_url)
self.assertEqual(video.title, self.title)
self.assertEqual(video.description, self.description)
self.assertEqual(video.caption, self.caption)
self.assertEqual(video.message_text, self.message_text)
self.assertEqual(video.parse_mode, self.parse_mode)
self.assertEqual(video.disable_web_page_preview,
self.disable_web_page_preview)
def test_video_to_json(self):
video = telegram.InlineQueryResultVideo.de_json(self.json_dict)
self.assertTrue(self.is_json(video.to_json()))
def test_video_to_dict(self):
video = \
telegram.InlineQueryResultVideo.de_json(self.json_dict).to_dict()
self.assertTrue(self.is_dict(video))
self.assertDictEqual(self.json_dict, video)
if __name__ == '__main__':
unittest.main()
+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/env python
# encoding: utf-8
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""
This module contains a object that represents Tests for JobQueue
"""
import logging
import sys
from time import sleep
if sys.version_info[0:2] == (2, 6):
import unittest2 as unittest
else:
import unittest
try:
from urllib2 import urlopen, Request
except ImportError:
from urllib.request import Request, urlopen
sys.path.append('.')
from telegram import JobQueue, Updater
from tests.base import BaseTest
# Enable logging
root = logging.getLogger()
root.setLevel(logging.INFO)
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(logging.WARN)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
root.addHandler(ch)
class JobQueueTest(BaseTest, unittest.TestCase):
"""
This object represents Tests for Updater, Dispatcher, WebhookServer and
WebhookHandler
"""
def setUp(self):
self.jq = JobQueue("Bot", tick_interval=0.005)
self.result = 0
def tearDown(self):
if self.jq is not None:
self.jq.stop()
def job1(self, bot):
self.result += 1
def job2(self, bot):
raise Exception("Test Error")
def test_basic(self):
self.jq.put(self.job1, 0.1)
sleep(1.5)
self.assertGreaterEqual(self.result, 10)
def test_noRepeat(self):
self.jq.put(self.job1, 0.1, repeat=False)
sleep(0.5)
self.assertEqual(1, self.result)
def test_nextT(self):
self.jq.put(self.job1, 0.1, next_t=0.5)
sleep(0.45)
self.assertEqual(0, self.result)
sleep(0.1)
self.assertEqual(1, self.result)
def test_multiple(self):
self.jq.put(self.job1, 0.1, repeat=False)
self.jq.put(self.job1, 0.2, repeat=False)
self.jq.put(self.job1, 0.4)
sleep(1)
self.assertEqual(4, self.result)
def test_error(self):
self.jq.put(self.job2, 0.1)
self.jq.put(self.job1, 0.2)
self.jq.start()
sleep(0.4)
self.assertEqual(1, self.result)
def test_inUpdater(self):
u = Updater(bot="MockBot", job_queue_tick_interval=0.005)
u.job_queue.put(self.job1, 0.5)
sleep(0.75)
self.assertEqual(1, self.result)
u.stop()
sleep(2)
self.assertEqual(1, self.result)
if __name__ == '__main__':
unittest.main()
+2 -20
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -40,9 +41,6 @@ class LocationTest(BaseTest, unittest.TestCase):
}
def test_send_location_implicit_args(self):
"""Test telegram.Bot sendLocation method"""
print('Testing bot.sendLocation - Implicit arguments')
message = self._bot.sendLocation(self._chat_id,
self.latitude,
self.longitude)
@@ -53,9 +51,6 @@ class LocationTest(BaseTest, unittest.TestCase):
self.assertEqual(location.longitude, self.longitude)
def test_send_location_explicit_args(self):
"""Test telegram.Bot sendLocation method"""
print('Testing bot.sendLocation - Explicit arguments')
message = self._bot.sendLocation(chat_id=self._chat_id,
latitude=self.latitude,
longitude=self.longitude)
@@ -66,34 +61,23 @@ class LocationTest(BaseTest, unittest.TestCase):
self.assertEqual(location.longitude, self.longitude)
def test_location_de_json(self):
"""Test Location.de_json() method"""
print('Testing Location.de_json()')
location = telegram.Location.de_json(self.json_dict)
self.assertEqual(location.latitude, self.latitude)
self.assertEqual(location.longitude, self.longitude)
def test_location_to_json(self):
"""Test Location.to_json() method"""
print('Testing Location.to_json()')
location = telegram.Location.de_json(self.json_dict)
self.assertTrue(self.is_json(location.to_json()))
def test_location_to_dict(self):
"""Test Location.to_dict() method"""
print('Testing Location.to_dict()')
location = telegram.Location.de_json(self.json_dict)
self.assertEqual(location['latitude'], self.latitude)
self.assertEqual(location['longitude'], self.longitude)
def test_error_send_location_empty_args(self):
print('Testing bot.sendLocation - Empty arguments')
json_dict = self.json_dict
json_dict['latitude'] = ''
@@ -104,8 +88,6 @@ class LocationTest(BaseTest, unittest.TestCase):
**json_dict))
def test_error_location_without_required_args(self):
print('Testing bot.sendLocation - Without required arguments')
json_dict = self.json_dict
del(json_dict['latitude'])
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env python
# encoding: utf-8
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents Tests for Telegram ParseMode"""
import unittest
import sys
sys.path.append('.')
import telegram
from tests.base import BaseTest
class ParseMode(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram ParseMode."""
def setUp(self):
self.markdown_text = "*bold* _italic_ [link](http://google.com)."
self.html_text = '<b>bold</b> <i>italic</i> <a href="http://google.com">link</a>.'
self.formatted_text_formatted = u'bold italic link.'
def test_send_message_with_parse_mode_markdown(self):
message = self._bot.sendMessage(chat_id=self._chat_id,
text=self.markdown_text,
parse_mode=telegram.ParseMode.MARKDOWN)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.text, self.formatted_text_formatted)
def test_send_message_with_parse_mode_html(self):
message = self._bot.sendMessage(chat_id=self._chat_id,
text=self.html_text,
parse_mode=telegram.ParseMode.HTML)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.text, self.formatted_text_formatted)
if __name__ == '__main__':
unittest.main()
+207
View File
@@ -0,0 +1,207 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents Tests for Telegram Photo"""
import os
import unittest
import sys
from flaky import flaky
sys.path.append('.')
import telegram
from tests.base import BaseTest, timeout
class PhotoTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram Photo."""
def setUp(self):
self.photo_file = open('tests/data/telegram.jpg', 'rb')
self.photo_file_id = 'AgADAQADvb8xGx8j9QcpZDKxYoFK3bfX1i8ABFX_dgMWoKDuQugAAgI'
self.photo_file_url = 'https://raw.githubusercontent.com/python-telegram-bot/python-telegram-bot/master/tests/data/telegram.jpg'
self.width = 300
self.height = 300
self.thumb = {'width': 90,
'height': 90,
'file_id': 'AgADAQADvb8xGx8j9QcpZDKxYoFK3bfX1i8ABBxRLXFhLnhIQ-gAAgI',
'file_size': 1478}
self.file_size = 10209
# caption is part of sendPhoto method but not Photo object
self.caption = u'PhotoTest - Caption'
self.json_dict = {
'file_id': self.photo_file_id,
'width': self.width,
'height': self.height,
'file_size': self.file_size
}
@flaky(3, 1)
@timeout(10)
def test_sendphotoo_all_args(self):
message = self._bot.sendPhoto(self._chat_id,
self.photo_file,
caption=self.caption)
thumb, photo = message.photo
self.assertTrue(isinstance(thumb.file_id, str))
self.assertNotEqual(thumb.file_id, '')
self.assertTrue(isinstance(thumb, telegram.PhotoSize))
self.assertEqual(thumb.width, self.thumb['width'])
self.assertEqual(thumb.height, self.thumb['height'])
self.assertEqual(thumb.file_size, self.thumb['file_size'])
self.assertTrue(isinstance(photo.file_id, str))
self.assertNotEqual(photo.file_id, '')
self.assertTrue(isinstance(photo, telegram.PhotoSize))
self.assertEqual(photo.width, self.width)
self.assertEqual(photo.height, self.height)
self.assertEqual(photo.file_size, self.file_size)
self.assertEqual(message.caption, self.caption)
@flaky(3, 1)
@timeout(10)
def test_send_photo_jpg_file(self):
message = self._bot.sendPhoto(self._chat_id,
self.photo_file)
thumb, photo = message.photo
self.assertTrue(isinstance(thumb.file_id, str))
self.assertNotEqual(thumb.file_id, '')
self.assertTrue(isinstance(thumb, telegram.PhotoSize))
self.assertEqual(thumb.width, self.thumb['width'])
self.assertEqual(thumb.height, self.thumb['height'])
self.assertEqual(thumb.file_size, self.thumb['file_size'])
self.assertTrue(isinstance(photo.file_id, str))
self.assertNotEqual(photo.file_id, '')
self.assertTrue(isinstance(photo, telegram.PhotoSize))
self.assertEqual(photo.width, self.width)
self.assertEqual(photo.height, self.height)
self.assertEqual(photo.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_photo_url_jpg_file(self):
message = self._bot.sendPhoto(self._chat_id,
self.photo_file_url)
thumb, photo = message.photo
self.assertTrue(isinstance(thumb.file_id, str))
self.assertNotEqual(thumb.file_id, '')
self.assertTrue(isinstance(thumb, telegram.PhotoSize))
self.assertEqual(thumb.width, self.thumb['width'])
self.assertEqual(thumb.height, self.thumb['height'])
self.assertEqual(thumb.file_size, self.thumb['file_size'])
self.assertTrue(isinstance(photo.file_id, str))
self.assertNotEqual(photo.file_id, '')
self.assertTrue(isinstance(photo, telegram.PhotoSize))
self.assertEqual(photo.width, self.width)
self.assertEqual(photo.height, self.height)
self.assertEqual(photo.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_photo_resend(self):
message = self._bot.sendPhoto(chat_id=self._chat_id,
photo=self.photo_file_id)
thumb, photo = message.photo
self.assertEqual(thumb.file_id, self.thumb['file_id'])
self.assertTrue(isinstance(thumb, telegram.PhotoSize))
self.assertEqual(thumb.width, self.thumb['width'])
self.assertEqual(thumb.height, self.thumb['height'])
self.assertEqual(photo.file_id, self.photo_file_id)
self.assertTrue(isinstance(photo, telegram.PhotoSize))
self.assertEqual(photo.width, self.width)
self.assertEqual(photo.height, self.height)
def test_photo_de_json(self):
photo = telegram.PhotoSize.de_json(self.json_dict)
self.assertEqual(photo.file_id, self.photo_file_id)
self.assertTrue(isinstance(photo, telegram.PhotoSize))
self.assertEqual(photo.width, self.width)
self.assertEqual(photo.height, self.height)
self.assertEqual(photo.file_size, self.file_size)
def test_photo_to_json(self):
photo = telegram.PhotoSize.de_json(self.json_dict)
self.assertTrue(self.is_json(photo.to_json()))
def test_photo_to_dict(self):
photo = telegram.PhotoSize.de_json(self.json_dict)
self.assertTrue(self.is_dict(photo.to_dict()))
self.assertEqual(photo['file_id'], self.photo_file_id)
self.assertTrue(isinstance(photo, telegram.PhotoSize))
self.assertEqual(photo['width'], self.width)
self.assertEqual(photo['height'], self.height)
self.assertEqual(photo['file_size'], self.file_size)
@flaky(3, 1)
@timeout(10)
def test_error_send_photo_empty_file(self):
json_dict = self.json_dict
del(json_dict['file_id'])
json_dict['photo'] = open(os.devnull, 'rb')
self.assertRaises(telegram.TelegramError,
lambda: self._bot.sendPhoto(chat_id=self._chat_id,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_error_send_photo_empty_file_id(self):
json_dict = self.json_dict
del(json_dict['file_id'])
json_dict['photo'] = ''
self.assertRaises(telegram.TelegramError,
lambda: self._bot.sendPhoto(chat_id=self._chat_id,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_error_photo_without_required_args(self):
json_dict = self.json_dict
del(json_dict['file_id'])
del(json_dict['width'])
del(json_dict['height'])
self.assertRaises(TypeError,
lambda: self._bot.sendPhoto(chat_id=self._chat_id,
**json_dict))
if __name__ == '__main__':
unittest.main()
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env python
# encoding: utf-8
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents Tests for Telegram ReplyKeyboardHide"""
import unittest
import sys
sys.path.append('.')
import telegram
from tests.base import BaseTest
class ReplyKeyboardHideTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram ReplyKeyboardHide."""
def setUp(self):
self.hide_keyboard = True
self.selective = True
self.json_dict = {
'hide_keyboard': self.hide_keyboard,
'selective': self.selective,
}
def test_send_message_with_reply_keyboard_hide(self):
message = self._bot.sendMessage(self._chat_id,
'Моё судно на воздушной подушке полно угрей',
reply_markup=telegram.ReplyKeyboardHide.de_json(self.json_dict))
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.text, u'Моё судно на воздушной подушке полно угрей')
def test_reply_keyboard_hide_de_json(self):
reply_keyboard_hide = telegram.ReplyKeyboardHide.de_json(self.json_dict)
self.assertEqual(reply_keyboard_hide.hide_keyboard, self.hide_keyboard)
self.assertEqual(reply_keyboard_hide.selective, self.selective)
def test_reply_keyboard_hide_to_json(self):
reply_keyboard_hide = telegram.ReplyKeyboardHide.de_json(self.json_dict)
self.assertTrue(self.is_json(reply_keyboard_hide.to_json()))
def test_reply_keyboard_hide_to_dict(self):
reply_keyboard_hide = telegram.ReplyKeyboardHide.de_json(self.json_dict)
self.assertEqual(reply_keyboard_hide['hide_keyboard'], self.hide_keyboard)
self.assertEqual(reply_keyboard_hide['selective'], self.selective)
if __name__ == '__main__':
unittest.main()
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/env python
# encoding: utf-8
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents Tests for Telegram ReplyKeyboardMarkup"""
import unittest
import sys
sys.path.append('.')
import telegram
from tests.base import BaseTest
class ReplyKeyboardMarkupTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram ReplyKeyboardMarkup."""
def setUp(self):
self.keyboard = [['button1', 'button2']]
self.resize_keyboard = True
self.one_time_keyboard = True
self.selective = True
self.json_dict = {
'keyboard': self.keyboard,
'resize_keyboard': self.resize_keyboard,
'one_time_keyboard': self.one_time_keyboard,
'selective': self.selective,
}
def test_send_message_with_reply_keyboard_markup(self):
message = self._bot.sendMessage(self._chat_id,
'Моё судно на воздушной подушке полно угрей',
reply_markup=telegram.ReplyKeyboardMarkup.de_json(self.json_dict))
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.text, u'Моё судно на воздушной подушке полно угрей')
def test_reply_keyboard_markup_de_json(self):
reply_keyboard_markup = telegram.ReplyKeyboardMarkup.de_json(self.json_dict)
self.assertEqual(reply_keyboard_markup.keyboard, self.keyboard)
self.assertEqual(reply_keyboard_markup.resize_keyboard, self.resize_keyboard)
self.assertEqual(reply_keyboard_markup.one_time_keyboard, self.one_time_keyboard)
self.assertEqual(reply_keyboard_markup.selective, self.selective)
def test_reply_keyboard_markup_to_json(self):
reply_keyboard_markup = telegram.ReplyKeyboardMarkup.de_json(self.json_dict)
self.assertTrue(self.is_json(reply_keyboard_markup.to_json()))
def test_reply_keyboard_markup_to_dict(self):
reply_keyboard_markup = telegram.ReplyKeyboardMarkup.de_json(self.json_dict)
self.assertEqual(reply_keyboard_markup['keyboard'], self.keyboard)
self.assertEqual(reply_keyboard_markup['resize_keyboard'], self.resize_keyboard)
self.assertEqual(reply_keyboard_markup['one_time_keyboard'], self.one_time_keyboard)
self.assertEqual(reply_keyboard_markup['selective'], self.selective)
if __name__ == '__main__':
unittest.main()
+15 -20
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,10 +22,12 @@
import os
import unittest
import sys
from flaky import flaky
sys.path.append('.')
import telegram
from tests.base import BaseTest
from tests.base import BaseTest, timeout
class StickerTest(BaseTest, unittest.TestCase):
@@ -48,13 +51,14 @@ class StickerTest(BaseTest, unittest.TestCase):
'file_size': self.file_size
}
@flaky(3, 1)
@timeout(10)
def test_send_sticker_file(self):
pass
@flaky(3, 1)
@timeout(10)
def test_send_sticker_resend(self):
"""Test telegram.Bot sendSticker method"""
print('Testing bot.sendSticker - Resend by file_id')
message = self._bot.sendSticker(chat_id=self._chat_id,
sticker=self.sticker_file_id)
@@ -67,9 +71,6 @@ class StickerTest(BaseTest, unittest.TestCase):
self.assertEqual(sticker.file_size, self.file_size)
def test_sticker_de_json(self):
"""Test Sticker.de_json() method"""
print('Testing Sticker.de_json()')
sticker = telegram.Sticker.de_json(self.json_dict)
self.assertEqual(sticker.file_id, self.sticker_file_id)
@@ -79,17 +80,11 @@ class StickerTest(BaseTest, unittest.TestCase):
self.assertEqual(sticker.file_size, self.file_size)
def test_sticker_to_json(self):
"""Test Sticker.to_json() method"""
print('Testing Sticker.to_json()')
sticker = telegram.Sticker.de_json(self.json_dict)
self.assertTrue(self.is_json(sticker.to_json()))
def test_sticker_to_dict(self):
"""Test Sticker.to_dict() method"""
print('Testing Sticker.to_dict()')
sticker = telegram.Sticker.de_json(self.json_dict)
self.assertEqual(sticker['file_id'], self.sticker_file_id)
@@ -98,9 +93,9 @@ class StickerTest(BaseTest, unittest.TestCase):
self.assertTrue(isinstance(sticker['thumb'], telegram.PhotoSize))
self.assertEqual(sticker['file_size'], self.file_size)
@flaky(3, 1)
@timeout(10)
def test_error_send_sticker_empty_file(self):
print('Testing bot.sendSticker - Null file')
json_dict = self.json_dict
del(json_dict['file_id'])
@@ -110,9 +105,9 @@ class StickerTest(BaseTest, unittest.TestCase):
lambda: self._bot.sendSticker(chat_id=self._chat_id,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_error_send_sticker_empty_file_id(self):
print('Testing bot.sendSticker - Empty file_id')
json_dict = self.json_dict
del(json_dict['file_id'])
@@ -122,9 +117,9 @@ class StickerTest(BaseTest, unittest.TestCase):
lambda: self._bot.sendSticker(chat_id=self._chat_id,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_error_sticker_without_required_args(self):
print('Testing bot.sendSticker - Without required arguments')
json_dict = self.json_dict
del(json_dict['file_id'])
+2 -10
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -51,26 +52,17 @@ class UpdateTest(BaseTest, unittest.TestCase):
}
def test_update_de_json(self):
"""Test Update.de_json() method"""
print('Testing Update.de_json()')
update = telegram.Update.de_json(self.json_dict)
self.assertEqual(update.update_id, self.update_id)
self.assertTrue(isinstance(update.message, telegram.Message))
def test_update_to_json(self):
"""Test Update.to_json() method"""
print('Testing Update.to_json()')
update = telegram.Update.de_json(self.json_dict)
self.assertTrue(self.is_json(update.to_json()))
def test_update_to_dict(self):
"""Test Update.to_dict() method"""
print('Testing Update.to_dict()')
update = telegram.Update.de_json(self.json_dict)
self.assertTrue(self.is_dict(update.to_dict()))
+217 -104
View File
@@ -2,7 +2,8 @@
# encoding: utf-8
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,7 +23,6 @@ This module contains a object that represents Tests for Updater, Dispatcher,
WebhookServer and WebhookHandler
"""
import logging
import unittest
import sys
import re
import os
@@ -30,15 +30,24 @@ import signal
from random import randrange
from time import sleep
from datetime import datetime
from future.builtins import bytes
if sys.version_info[0:2] == (2, 6):
import unittest2 as unittest
else:
import unittest
try:
from urllib2 import urlopen, Request
# python2
from urllib2 import urlopen, Request, HTTPError
except ImportError:
# python3
from urllib.request import Request, urlopen
from urllib.error import HTTPError
sys.path.append('.')
from telegram import Update, Message, TelegramError, User, Chat, Updater
from telegram import Update, Message, TelegramError, User, Chat, Updater, Bot
from telegram.dispatcher import run_async
from tests.base import BaseTest
from threading import Lock, Thread
@@ -61,14 +70,18 @@ class UpdaterTest(BaseTest, unittest.TestCase):
"""
def setUp(self):
self.updater = Updater('', workers=2)
self.updater = None
self.received_message = None
self.message_count = 0
self.lock = Lock()
def _setup_updater(self, *args, **kwargs):
bot = MockBot(*args, **kwargs)
self.updater = Updater(workers=2, bot=bot)
def tearDown(self):
self.updater.stop()
if self.updater is not None:
self.updater.stop()
def reset(self):
self.message_count = 0
@@ -78,8 +91,13 @@ class UpdaterTest(BaseTest, unittest.TestCase):
self.received_message = update.message.text
self.message_count += 1
def telegramInlineHandlerTest(self, bot, update):
self.received_message = (update.inline_query,
update.chosen_inline_result)
self.message_count += 1
@run_async
def asyncHandlerTest(self, bot, update):
def asyncHandlerTest(self, bot, update, **kwargs):
sleep(1)
with self.lock:
self.received_message = update.message.text
@@ -89,6 +107,10 @@ class UpdaterTest(BaseTest, unittest.TestCase):
self.received_message = update
self.message_count += 1
def regexGroupHandlerTest(self, bot, update, groups=None, groupdict=None):
self.received_message = (groups, groupdict)
self.message_count += 1
def additionalArgsTest(self, bot, update, update_queue, args):
self.received_message = update
self.message_count += 1
@@ -96,6 +118,20 @@ class UpdaterTest(BaseTest, unittest.TestCase):
update_queue.put('/test5 noresend')
elif args[0] == 'noresend':
pass
def contextTest(self, bot, update, context):
self.received_message = update
self.message_count += 1
self.context = context
@run_async
def asyncAdditionalHandlerTest(self, bot, update, update_queue=None,
**kwargs):
sleep(1)
with self.lock:
if update_queue is not None:
self.received_message = update.message.text
self.message_count += 1
def errorRaisingHandlerTest(self, bot, update):
raise TelegramError(update)
@@ -105,9 +141,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
self.message_count += 1
def test_addRemoveTelegramMessageHandler(self):
print('Testing add/removeTelegramMessageHandler')
bot = MockBot('Test')
self.updater.bot = bot
self._setup_updater('Test')
d = self.updater.dispatcher
d.addTelegramMessageHandler(
self.telegramHandlerTest)
@@ -119,13 +153,12 @@ class UpdaterTest(BaseTest, unittest.TestCase):
d.removeTelegramMessageHandler(self.telegramHandlerTest)
self.reset()
bot.send_messages = 1
self.updater.bot.send_messages = 1
sleep(.1)
self.assertTrue(None is self.received_message)
def test_addTelegramMessageHandlerMultipleMessages(self):
print('Testing addTelegramMessageHandler and send 100 messages...')
self.updater.bot = MockBot('Multiple', 100)
self._setup_updater('Multiple', 100)
self.updater.dispatcher.addTelegramMessageHandler(
self.telegramHandlerTest)
self.updater.start_polling(0.0)
@@ -134,9 +167,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
self.assertEqual(self.message_count, 100)
def test_addRemoveTelegramRegexHandler(self):
print('Testing add/removeStringRegexHandler')
bot = MockBot('Test2')
self.updater.bot = bot
self._setup_updater('Test2')
d = self.updater.dispatcher
regobj = re.compile('Te.*')
self.updater.dispatcher.addTelegramRegexHandler(regobj,
@@ -149,14 +180,12 @@ class UpdaterTest(BaseTest, unittest.TestCase):
d.removeTelegramRegexHandler(regobj, self.telegramHandlerTest)
self.reset()
bot.send_messages = 1
self.updater.bot.send_messages = 1
sleep(.1)
self.assertTrue(None is self.received_message)
def test_addRemoveTelegramCommandHandler(self):
print('Testing add/removeTelegramCommandHandler')
bot = MockBot('/test')
self.updater.bot = bot
self._setup_updater('/test')
d = self.updater.dispatcher
self.updater.dispatcher.addTelegramCommandHandler(
'test', self.telegramHandlerTest)
@@ -168,14 +197,12 @@ class UpdaterTest(BaseTest, unittest.TestCase):
d.removeTelegramCommandHandler('test', self.telegramHandlerTest)
self.reset()
bot.send_messages = 1
self.updater.bot.send_messages = 1
sleep(.1)
self.assertTrue(None is self.received_message)
def test_addRemoveUnknownTelegramCommandHandler(self):
print('Testing add/removeUnknownTelegramCommandHandler')
bot = MockBot('/test2')
self.updater.bot = bot
self._setup_updater('/test2')
d = self.updater.dispatcher
self.updater.dispatcher.addUnknownTelegramCommandHandler(
self.telegramHandlerTest)
@@ -187,14 +214,12 @@ class UpdaterTest(BaseTest, unittest.TestCase):
d.removeUnknownTelegramCommandHandler(self.telegramHandlerTest)
self.reset()
bot.send_messages = 1
self.updater.bot.send_messages = 1
sleep(.1)
self.assertTrue(None is self.received_message)
def test_addRemoveStringRegexHandler(self):
print('Testing add/removeStringRegexHandler')
bot = MockBot('', messages=0)
self.updater.bot = bot
self._setup_updater('', messages=0)
d = self.updater.dispatcher
d.addStringRegexHandler('Te.*', self.stringHandlerTest)
queue = self.updater.start_polling(0.01)
@@ -211,9 +236,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
self.assertTrue(None is self.received_message)
def test_addRemoveStringCommandHandler(self):
print('Testing add/removeStringCommandHandler')
bot = MockBot('', messages=0)
self.updater.bot = bot
self._setup_updater('', messages=0)
d = self.updater.dispatcher
d.addStringCommandHandler(
'test3', self.stringHandlerTest)
@@ -232,9 +255,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
self.assertTrue(None is self.received_message)
def test_addRemoveUnknownStringCommandHandler(self):
print('Testing add/removeUnknownStringCommandHandler')
bot = MockBot('/test')
self.updater.bot = bot
self._setup_updater('/test')
d = self.updater.dispatcher
d.addUnknownStringCommandHandler(
self.stringHandlerTest)
@@ -247,14 +268,12 @@ class UpdaterTest(BaseTest, unittest.TestCase):
d.removeUnknownStringCommandHandler(self.stringHandlerTest)
self.reset()
bot.send_messages = 1
self.updater.bot.send_messages = 1
sleep(.1)
self.assertTrue(None is self.received_message)
def test_addRemoveErrorHandler(self):
print('Testing add/removeErrorHandler')
bot = MockBot('', messages=0)
self.updater.bot = bot
self._setup_updater('', messages=0)
d = self.updater.dispatcher
d.addErrorHandler(self.errorHandlerTest)
queue = self.updater.start_polling(0.01)
@@ -272,12 +291,9 @@ class UpdaterTest(BaseTest, unittest.TestCase):
self.assertTrue(None is self.received_message)
def test_errorInHandler(self):
print('Testing error in Handler')
bot = MockBot('', messages=0)
self.updater.bot = bot
self._setup_updater('', messages=0)
d = self.updater.dispatcher
d.addStringRegexHandler('.*',
self.errorRaisingHandlerTest)
d.addStringRegexHandler('.*', self.errorRaisingHandlerTest)
self.updater.dispatcher.addErrorHandler(self.errorHandlerTest)
queue = self.updater.start_polling(0.01)
@@ -286,9 +302,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
self.assertEqual(self.received_message, 'Test Error 1')
def test_errorOnGetUpdates(self):
print('Testing error on getUpdates')
bot = MockBot('', raise_error=True)
self.updater.bot = bot
self._setup_updater('', raise_error=True)
d = self.updater.dispatcher
d.addErrorHandler(self.errorHandlerTest)
self.updater.start_polling(0.01)
@@ -296,9 +310,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
self.assertEqual(self.received_message, "Test Error 2")
def test_addRemoveTypeHandler(self):
print('Testing add/removeTypeHandler')
bot = MockBot('', messages=0)
self.updater.bot = bot
self._setup_updater('', messages=0)
d = self.updater.dispatcher
d.addTypeHandler(dict, self.stringHandlerTest)
queue = self.updater.start_polling(0.01)
@@ -315,10 +327,32 @@ class UpdaterTest(BaseTest, unittest.TestCase):
sleep(.1)
self.assertTrue(None is self.received_message)
def test_addRemoveInlineHandlerQuery(self):
print('Testing add/removeInlineHandler')
self._setup_updater('', messages=0)
d = self.updater.dispatcher
d.addTelegramInlineHandler(self.telegramInlineHandlerTest)
queue = self.updater.start_polling(0.01)
update = Update(update_id=0, inline_query="testquery")
update2 = Update(update_id=0, chosen_inline_result="testresult")
queue.put(update)
sleep(.1)
self.assertEqual(self.received_message[0], "testquery")
queue.put(update2)
sleep(.1)
self.assertEqual(self.received_message[1], "testresult")
# Remove handler
d.removeTelegramInlineHandler(self.telegramInlineHandlerTest)
self.reset()
queue.put(update)
sleep(.1)
self.assertTrue(None is self.received_message)
def test_runAsync(self):
print('Testing @run_async')
bot = MockBot('Test5', messages=2)
self.updater.bot = bot
self._setup_updater('Test5', messages=2)
d = self.updater.dispatcher
d.addTelegramMessageHandler(
self.asyncHandlerTest)
@@ -328,8 +362,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
self.assertEqual(self.message_count, 2)
def test_additionalArgs(self):
print('Testing additional arguments for handlers')
self.updater.bot = MockBot('', messages=0)
self._setup_updater('', messages=0)
self.updater.dispatcher.addStringCommandHandler(
'test5', self.additionalArgsTest)
@@ -338,18 +371,51 @@ class UpdaterTest(BaseTest, unittest.TestCase):
sleep(.1)
self.assertEqual(self.received_message, '/test5 noresend')
self.assertEqual(self.message_count, 2)
def test_context(self):
context = "context_data"
self._setup_updater('', messages=0)
self.updater.dispatcher.addStringCommandHandler(
'test_context', self.contextTest)
queue = self.updater.start_polling(0.01)
queue.put('/test_context', context=context)
sleep(.5)
self.assertEqual(self.received_message, '/test_context')
self.assertEqual(self.message_count, 1)
self.assertEqual(self.context, context)
def test_regexGroupHandler(self):
self._setup_updater('', messages=0)
d = self.updater.dispatcher
d.addStringRegexHandler('^(This).*?(?P<testgroup>regex group).*',
self.regexGroupHandlerTest)
queue = self.updater.start_polling(0.01)
queue.put('This is a test message for regex group matching.')
sleep(.1)
self.assertEqual(self.received_message, (('This', 'regex group'),
{'testgroup': 'regex group'}))
def test_runAsyncWithAdditionalArgs(self):
self._setup_updater('Test6', messages=2)
d = self.updater.dispatcher
d.addTelegramMessageHandler(
self.asyncAdditionalHandlerTest)
self.updater.start_polling(0.01)
sleep(1.2)
self.assertEqual(self.received_message, 'Test6')
self.assertEqual(self.message_count, 2)
def test_webhook(self):
print('Testing Webhook')
bot = MockBot('', messages=0)
self.updater.bot = bot
self._setup_updater('', messages=0)
d = self.updater.dispatcher
d.addTelegramMessageHandler(
self.telegramHandlerTest)
# Select random port for travis
port = randrange(1024, 49152)
self.updater.start_webhook('127.0.0.1', port,
ip = '127.0.0.1'
port = randrange(1024, 49152) # Select random port for travis
self.updater.start_webhook(ip, port,
url_path='TOKEN',
cert='./tests/test_updater.py',
key='./tests/test_updater.py')
@@ -365,34 +431,19 @@ class UpdaterTest(BaseTest, unittest.TestCase):
update = Update(1)
update.message = message
try:
payload = bytes(update.to_json(), encoding='utf-8')
except TypeError:
payload = bytes(update.to_json())
header = {
'content-type': 'application/json',
'content-length': str(len(payload))
}
r = Request('http://127.0.0.1:%d/TOKEN' % port,
data=payload,
headers=header)
urlopen(r)
self._send_webhook_msg(ip, port, update.to_json(), 'TOKEN')
sleep(1)
self.assertEqual(self.received_message, 'Webhook Test')
print("Test other webhook server functionalities...")
request = Request('http://localhost:%d/webookhandler.py' % port)
response = urlopen(request)
response = self._send_webhook_msg(ip, port, None, 'webookhandler.py')
self.assertEqual(b'', response.read())
self.assertEqual(200, response.code)
request.get_method = lambda: 'HEAD'
response = self._send_webhook_msg(ip, port, None, 'webookhandler.py',
get_method=lambda: 'HEAD')
response = urlopen(request)
self.assertEqual(b'', response.read())
self.assertEqual(200, response.code)
@@ -402,16 +453,14 @@ class UpdaterTest(BaseTest, unittest.TestCase):
self.assertTrue(True)
def test_webhook_no_ssl(self):
print('Testing Webhook without SSL')
bot = MockBot('', messages=0)
self.updater.bot = bot
self._setup_updater('', messages=0)
d = self.updater.dispatcher
d.addTelegramMessageHandler(
self.telegramHandlerTest)
# Select random port for travis
port = randrange(1024, 49152)
self.updater.start_webhook('127.0.0.1', port)
ip = '127.0.0.1'
port = randrange(1024, 49152) # Select random port for travis
self.updater.start_webhook(ip, port)
sleep(0.5)
# Now, we send an update to the server via urlopen
@@ -422,37 +471,101 @@ class UpdaterTest(BaseTest, unittest.TestCase):
update = Update(1)
update.message = message
try:
payload = bytes(update.to_json(), encoding='utf-8')
except TypeError:
payload = bytes(update.to_json())
header = {
'content-type': 'application/json',
'content-length': str(len(payload))
}
r = Request('http://127.0.0.1:%d/' % port,
data=payload,
headers=header)
urlopen(r)
self._send_webhook_msg(ip, port, update.to_json())
sleep(1)
self.assertEqual(self.received_message, 'Webhook Test 2')
def test_webhook_invalid_posts(self):
self._setup_updater('', messages=0)
ip = '127.0.0.1'
port = randrange(1024, 49152) # select random port for travis
thr = Thread(target=self.updater._start_webhook,
args=(ip, port, '', None, None))
thr.start()
sleep(0.5)
try:
with self.assertRaises(HTTPError) as ctx:
self._send_webhook_msg(ip, port,
'<root><bla>data</bla></root>',
content_type='application/xml')
self.assertEqual(ctx.exception.code, 403)
with self.assertRaises(HTTPError) as ctx:
self._send_webhook_msg(ip, port, 'dummy-payload',
content_len=-2)
self.assertEqual(ctx.exception.code, 403)
# TODO: prevent urllib or the underlying from adding content-length
# with self.assertRaises(HTTPError) as ctx:
# self._send_webhook_msg(ip, port, 'dummy-payload',
# content_len=None)
# self.assertEqual(ctx.exception.code, 411)
with self.assertRaises(HTTPError) as ctx:
self._send_webhook_msg(ip, port, 'dummy-payload',
content_len='not-a-number')
self.assertEqual(ctx.exception.code, 403)
finally:
self.updater._stop_httpd()
thr.join()
def _send_webhook_msg(self, ip, port, payload_str, url_path='',
content_len=-1, content_type='application/json',
get_method=None):
headers = {
'content-type': content_type,
}
if not payload_str:
content_len = None
payload = None
else:
payload = bytes(payload_str, encoding='utf-8')
if content_len == -1:
content_len = len(payload)
if content_len is not None:
headers['content-length'] = str(content_len)
url = 'http://{ip}:{port}/{path}'.format(ip=ip, port=port,
path=url_path)
req = Request(url, data=payload, headers=headers)
if get_method is not None:
req.get_method = get_method
return urlopen(req)
def signalsender(self):
sleep(0.5)
os.kill(os.getpid(), signal.SIGTERM)
def test_idle(self):
print('Testing idle')
self.updater.bot = MockBot('Test6', messages=0)
self._setup_updater('Test6', messages=0)
self.updater.start_polling(poll_interval=0.01)
Thread(target=self.signalsender).start()
self.updater.idle()
# If we get this far, idle() ran through
sleep(1)
self.updater.running = False
self.assertFalse(self.updater.running)
def test_createBot(self):
updater = Updater('123:abcd')
self.assertIsNotNone(updater.bot)
def test_mutualExclusiveTokenBot(self):
bot = Bot('123:zyxw')
self.assertRaises(ValueError, Updater, token='123:abcd', bot=bot)
def test_noTokenOrBot(self):
self.assertRaises(ValueError, Updater)
class MockBot:
+2 -16
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -46,9 +47,6 @@ class UserTest(BaseTest, unittest.TestCase):
}
def test_user_de_json(self):
"""Test User.de_json() method"""
print('Testing User.de_json()')
user = telegram.User.de_json(self.json_dict)
self.assertEqual(user.id, self.id)
@@ -60,9 +58,6 @@ class UserTest(BaseTest, unittest.TestCase):
self.assertEqual(user.name, '@leandrotoledo')
def test_user_de_json_without_username(self):
"""Test User.de_json() method"""
print('Testing User.de_json() - Without username')
json_dict = self.json_dict
del(json_dict['username'])
@@ -78,9 +73,6 @@ class UserTest(BaseTest, unittest.TestCase):
def test_user_de_json_without_username_and_lastname(self):
"""Test User.de_json() method"""
print('Testing User.de_json() - Without username and last_name')
json_dict = self.json_dict
del(json_dict['username'])
@@ -94,17 +86,11 @@ class UserTest(BaseTest, unittest.TestCase):
self.assertEqual(user.name, self.first_name)
def test_user_to_json(self):
"""Test User.to_json() method"""
print('Testing User.to_json()')
user = telegram.User.de_json(self.json_dict)
self.assertTrue(self.is_json(user.to_json()))
def test_user_to_dict(self):
"""Test User.to_dict() method"""
print('Testing User.to_dict()')
user = telegram.User.de_json(self.json_dict)
self.assertTrue(self.is_dict(user.to_dict()))
+83 -60
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,10 +22,12 @@
import os
import unittest
import sys
from flaky import flaky
sys.path.append('.')
import telegram
from tests.base import BaseTest
from tests.base import BaseTest, timeout
class VideoTest(BaseTest, unittest.TestCase):
@@ -33,10 +36,15 @@ class VideoTest(BaseTest, unittest.TestCase):
def setUp(self):
self.video_file = open('tests/data/telegram.mp4', 'rb')
self.video_file_id = 'BAADAQADXwADHyP1BwJFTcmY2RYCAg'
self.video_file_url = 'https://raw.githubusercontent.com/python-telegram-bot/python-telegram-bot/master/tests/data/telegram.mp4'
self.width = 360
self.height = 640
self.duration = 4
self.thumb = telegram.PhotoSize.de_json({})
self.duration = 5
self.thumb = telegram.PhotoSize.de_json(
{'file_id': 'AAQBABOMsecvAAQqqoY1Pee_MqcyAAIC',
'file_size': 645,
'height': 90,
'width': 51})
self.mime_type = 'video/mp4'
self.file_size = 326534
@@ -48,35 +56,35 @@ class VideoTest(BaseTest, unittest.TestCase):
'width': self.width,
'height': self.height,
'duration': self.duration,
'thumb': self.thumb,
'thumb': self.thumb.to_dict(),
'mime_type': self.mime_type,
'file_size': self.file_size
}
@flaky(3, 1)
@timeout(10)
def test_send_video_required_args_only(self):
"""Test telegram.Bot sendVideo method"""
print('Testing bot.sendVideo - With required arguments only')
message = self._bot.sendVideo(self._chat_id,
self.video_file)
video = message.video
self.assertTrue(isinstance(video.file_id, str))
self.assertNotEqual(video.file_id, '')
self.assertEqual(video.width, 0)
self.assertEqual(video.height, 0)
self.assertEqual(video.duration, 0)
self.assertEqual(video.thumb, None)
self.assertEqual(video.mime_type, '')
self.assertEqual(video.file_size, self.file_size)
def test_send_video_all_args(self):
"""Test telegram.Bot sendAudio method"""
print('Testing bot.sendVideo - With all arguments')
message = self._bot.sendVideo(self._chat_id,
self.video_file,
timeout=10)
video = message.video
self.assertTrue(isinstance(video.file_id, str))
self.assertNotEqual(video.file_id, '')
self.assertEqual(video.width, self.width)
self.assertEqual(video.height, self.height)
self.assertEqual(video.duration, self.duration)
self.assertEqual(video.thumb, self.thumb)
self.assertEqual(video.mime_type, '')
self.assertEqual(video.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_video_all_args(self):
message = self._bot.sendVideo(self._chat_id,
self.video_file,
timeout=10,
duration=self.duration,
caption=self.caption)
@@ -84,21 +92,21 @@ class VideoTest(BaseTest, unittest.TestCase):
self.assertTrue(isinstance(video.file_id, str))
self.assertNotEqual(video.file_id, '')
self.assertEqual(video.width, 0)
self.assertEqual(video.height, 0)
self.assertEqual(video.width, self.width)
self.assertEqual(video.height, self.height)
self.assertEqual(video.duration, self.duration)
self.assertEqual(video.thumb, None)
self.assertEqual(video.thumb, self.thumb)
self.assertEqual(video.mime_type, '')
self.assertEqual(video.file_size, self.file_size)
self.assertEqual(message.caption, self.caption)
@flaky(3, 1)
@timeout(10)
def test_send_video_mp4_file(self):
"""Test telegram.Bot sendVideo method"""
print('Testing bot.sendVideo - MP4 File')
message = self._bot.sendVideo(chat_id=self._chat_id,
video=self.video_file,
timeout=10,
duration=self.duration,
caption=self.caption)
@@ -106,21 +114,21 @@ class VideoTest(BaseTest, unittest.TestCase):
self.assertTrue(isinstance(video.file_id, str))
self.assertNotEqual(video.file_id, '')
self.assertEqual(video.width, 0)
self.assertEqual(video.height, 0)
self.assertEqual(video.width, self.width)
self.assertEqual(video.height, self.height)
self.assertEqual(video.duration, self.duration)
self.assertEqual(video.thumb, None)
self.assertEqual(video.thumb, self.thumb)
self.assertEqual(video.mime_type, '')
self.assertEqual(video.file_size, self.file_size)
self.assertEqual(message.caption, self.caption)
@flaky(3, 1)
@timeout(10)
def test_send_video_mp4_file_with_custom_filename(self):
"""Test telegram.Bot sendVideo method"""
print('Testing bot.sendVideo - MP4 File with custom filename')
message = self._bot.sendVideo(chat_id=self._chat_id,
video=self.video_file,
timeout=10,
duration=self.duration,
caption=self.caption,
filename='telegram_custom.mp4')
@@ -129,21 +137,42 @@ class VideoTest(BaseTest, unittest.TestCase):
self.assertTrue(isinstance(video.file_id, str))
self.assertNotEqual(video.file_id, '')
self.assertEqual(video.width, 0)
self.assertEqual(video.height, 0)
self.assertEqual(video.width, self.width)
self.assertEqual(video.height, self.height)
self.assertEqual(video.duration, self.duration)
self.assertEqual(video.thumb, None)
self.assertEqual(video.thumb, self.thumb)
self.assertEqual(video.mime_type, '')
self.assertEqual(video.file_size, self.file_size)
self.assertEqual(message.caption, self.caption)
def test_send_video_resend(self):
"""Test telegram.Bot sendVideo method"""
print('Testing bot.sendVideo - Resend by file_id')
@flaky(3, 1)
@timeout(10)
def test_send_video_mp4_file_url(self):
message = self._bot.sendVideo(chat_id=self._chat_id,
video=self.video_file_url,
timeout=10,
duration=self.duration,
caption=self.caption)
video = message.video
self.assertTrue(isinstance(video.file_id, str))
self.assertNotEqual(video.file_id, '')
self.assertEqual(video.height, self.height)
self.assertEqual(video.duration, self.duration)
self.assertEqual(video.thumb, self.thumb)
self.assertEqual(video.mime_type, '')
self.assertEqual(video.file_size, self.file_size)
self.assertEqual(message.caption, self.caption)
@flaky(3, 1)
@timeout(10)
def test_send_video_resend(self):
message = self._bot.sendVideo(chat_id=self._chat_id,
video=self.video_file_id,
timeout=10,
duration=self.duration,
caption=self.caption)
@@ -157,31 +186,22 @@ class VideoTest(BaseTest, unittest.TestCase):
self.assertEqual(message.caption, self.caption)
def test_video_de_json(self):
"""Test Video.de_json() method"""
print('Testing Video.de_json()')
video = telegram.Video.de_json(self.json_dict)
self.assertEqual(video.file_id, self.video_file_id)
self.assertEqual(video.width, self.width)
self.assertEqual(video.height, self.height)
self.assertEqual(video.duration, self.duration)
self.assertEqual(video.thumb, None)
self.assertEqual(video.thumb, self.thumb)
self.assertEqual(video.mime_type, self.mime_type)
self.assertEqual(video.file_size, self.file_size)
def test_video_to_json(self):
"""Test Video.to_json() method"""
print('Testing Video.to_json()')
video = telegram.Video.de_json(self.json_dict)
self.assertTrue(self.is_json(video.to_json()))
def test_video_to_dict(self):
"""Test Video.to_dict() method"""
print('Testing Video.to_dict()')
video = telegram.Video.de_json(self.json_dict)
self.assertTrue(self.is_dict(video.to_dict()))
@@ -192,9 +212,9 @@ class VideoTest(BaseTest, unittest.TestCase):
self.assertEqual(video['mime_type'], self.mime_type)
self.assertEqual(video['file_size'], self.file_size)
@flaky(3, 1)
@timeout(10)
def test_error_send_video_empty_file(self):
print('Testing bot.sendVideo - Null file')
json_dict = self.json_dict
del(json_dict['file_id'])
@@ -202,11 +222,12 @@ class VideoTest(BaseTest, unittest.TestCase):
self.assertRaises(telegram.TelegramError,
lambda: self._bot.sendVideo(chat_id=self._chat_id,
timeout=10,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_error_send_video_empty_file_id(self):
print('Testing bot.sendVideo - Empty file_id')
json_dict = self.json_dict
del(json_dict['file_id'])
@@ -214,11 +235,12 @@ class VideoTest(BaseTest, unittest.TestCase):
self.assertRaises(telegram.TelegramError,
lambda: self._bot.sendVideo(chat_id=self._chat_id,
timeout=10,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_error_video_without_required_args(self):
print('Testing bot.sendVideo - Without required arguments')
json_dict = self.json_dict
del(json_dict['file_id'])
@@ -226,6 +248,7 @@ class VideoTest(BaseTest, unittest.TestCase):
self.assertRaises(TypeError,
lambda: self._bot.sendVideo(chat_id=self._chat_id,
timeout=10,
**json_dict))
if __name__ == '__main__':
+39 -34
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,10 +22,12 @@
import os
import unittest
import sys
from flaky import flaky
sys.path.append('.')
import telegram
from tests.base import BaseTest
from tests.base import BaseTest, timeout
class VoiceTest(BaseTest, unittest.TestCase):
@@ -33,7 +36,8 @@ class VoiceTest(BaseTest, unittest.TestCase):
def setUp(self):
self.voice_file = open('tests/data/telegram.ogg', 'rb')
self.voice_file_id = 'AwADAQADTgADHyP1B_mbw34svXPHAg'
self.duration = 0
self.voice_file_url = 'https://raw.githubusercontent.com/python-telegram-bot/python-telegram-bot/master/tests/data/telegram.ogg'
self.duration = 3
self.mime_type = 'audio/ogg'
self.file_size = 9199
@@ -44,10 +48,9 @@ class VoiceTest(BaseTest, unittest.TestCase):
'file_size': self.file_size
}
@flaky(3, 1)
@timeout(10)
def test_send_voice_required_args_only(self):
"""Test telegram.Bot sendVoice method"""
print('Testing bot.sendVoice - With required arguments only')
message = self._bot.sendVoice(self._chat_id,
self.voice_file)
@@ -59,10 +62,9 @@ class VoiceTest(BaseTest, unittest.TestCase):
self.assertEqual(voice.mime_type, self.mime_type)
self.assertEqual(voice.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_voice_all_args(self):
"""Test telegram.Bot sendAudio method"""
print('Testing bot.sendVoice - With all arguments')
message = self._bot.sendVoice(self._chat_id,
self.voice_file,
self.duration,
@@ -77,10 +79,9 @@ class VoiceTest(BaseTest, unittest.TestCase):
self.assertEqual(voice.mime_type, self.mime_type)
self.assertEqual(voice.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_voice_ogg_file(self):
"""Test telegram.Bot sendVoice method"""
print('Testing bot.sendVoice - Ogg File')
message = self._bot.sendVoice(chat_id=self._chat_id,
voice=self.voice_file,
duration=self.duration)
@@ -93,10 +94,9 @@ class VoiceTest(BaseTest, unittest.TestCase):
self.assertEqual(voice.mime_type, self.mime_type)
self.assertEqual(voice.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_voice_ogg_file_with_custom_filename(self):
"""Test telegram.Bot sendVoice method"""
print('Testing bot.sendVoice - Ogg File with custom filename')
message = self._bot.sendVoice(chat_id=self._chat_id,
voice=self.voice_file,
duration=self.duration,
@@ -110,10 +110,24 @@ class VoiceTest(BaseTest, unittest.TestCase):
self.assertEqual(voice.mime_type, self.mime_type)
self.assertEqual(voice.file_size, self.file_size)
def test_send_voice_resend(self):
"""Test telegram.Bot sendVoice method"""
print('Testing bot.sendVoice - Resend by file_id')
@flaky(3, 1)
@timeout(10)
def test_send_voice_ogg_url_file(self):
message = self._bot.sendVoice(chat_id=self._chat_id,
voice=self.voice_file_url,
duration=self.duration)
voice = message.voice
self.assertTrue(isinstance(voice.file_id, str))
self.assertNotEqual(voice.file_id, '')
self.assertEqual(voice.duration, self.duration)
self.assertEqual(voice.mime_type, self.mime_type)
self.assertEqual(voice.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_voice_resend(self):
message = self._bot.sendVoice(chat_id=self._chat_id,
voice=self.voice_file_id,
duration=self.duration)
@@ -121,13 +135,10 @@ class VoiceTest(BaseTest, unittest.TestCase):
voice = message.voice
self.assertEqual(voice.file_id, self.voice_file_id)
self.assertEqual(voice.duration, self.duration)
self.assertEqual(voice.duration, 0)
self.assertEqual(voice.mime_type, self.mime_type)
def test_voice_de_json(self):
"""Test Voice.de_json() method"""
print('Testing Voice.de_json()')
voice = telegram.Voice.de_json(self.json_dict)
self.assertEqual(voice.file_id, self.voice_file_id)
@@ -136,17 +147,11 @@ class VoiceTest(BaseTest, unittest.TestCase):
self.assertEqual(voice.file_size, self.file_size)
def test_voice_to_json(self):
"""Test Voice.to_json() method"""
print('Testing Voice.to_json()')
voice = telegram.Voice.de_json(self.json_dict)
self.assertTrue(self.is_json(voice.to_json()))
def test_voice_to_dict(self):
"""Test Voice.to_dict() method"""
print('Testing Voice.to_dict()')
voice = telegram.Voice.de_json(self.json_dict)
self.assertTrue(self.is_dict(voice.to_dict()))
@@ -155,9 +160,9 @@ class VoiceTest(BaseTest, unittest.TestCase):
self.assertEqual(voice['mime_type'], self.mime_type)
self.assertEqual(voice['file_size'], self.file_size)
@flaky(3, 1)
@timeout(10)
def test_error_send_voice_empty_file(self):
print('Testing bot.sendVoice - Null file')
json_dict = self.json_dict
del(json_dict['file_id'])
@@ -167,9 +172,9 @@ class VoiceTest(BaseTest, unittest.TestCase):
lambda: self._bot.sendVoice(chat_id=self._chat_id,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_error_send_voice_empty_file_id(self):
print('Testing bot.sendVoice - Empty file_id')
json_dict = self.json_dict
del(json_dict['file_id'])
@@ -179,9 +184,9 @@ class VoiceTest(BaseTest, unittest.TestCase):
lambda: self._bot.sendVoice(chat_id=self._chat_id,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_error_voice_without_required_args(self):
print('Testing bot.sendVoice - Without required arguments')
json_dict = self.json_dict
del(json_dict['file_id'])